Skip to content

Commit

Permalink
Set the correct type when copying reflect Inlined trees (#19409)
Browse files Browse the repository at this point in the history
Fixes #19191
  • Loading branch information
nicolasstucki authored Jan 12, 2024
2 parents b2ba6dc + 2930d2e commit 70ec709
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 1 deletion.
6 changes: 5 additions & 1 deletion compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1030,7 +1030,11 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
def apply(call: Option[Tree], bindings: List[Definition], expansion: Term): Inlined =
withDefaultPos(tpd.Inlined(call.getOrElse(tpd.EmptyTree), bindings.map { case b: tpd.MemberDef => b }, xCheckMacroValidExpr(expansion)))
def copy(original: Tree)(call: Option[Tree], bindings: List[Definition], expansion: Term): Inlined =
tpd.Inlined(call.getOrElse(tpd.EmptyTree), bindings.asInstanceOf[List[tpd.MemberDef]], xCheckMacroValidExpr(expansion)).withSpan(original.span).withType(original.tpe)
original match
case original: Inlined =>
tpd.cpy.Inlined(original)(call.getOrElse(tpd.EmptyTree), bindings.asInstanceOf[List[tpd.MemberDef]], xCheckMacroValidExpr(expansion))
case original =>
throw new IllegalArgumentException(i"Expected argument `original` to be an `Inlined` but was: ${original.show}")
def unapply(x: Inlined): (Option[Tree /* Term | TypeTree */], List[Definition], Term) =
(optional(x.call), x.bindings, x.body)
end Inlined
Expand Down
15 changes: 15 additions & 0 deletions tests/pos-macros/i19191/Lib_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
trait Binding[+A]:
def value: A = ???
object Binding:
inline def apply[A](inline a: A): Binding[A] = ???
final case class Constant[+A](override val value: A) extends Binding[A]

extension [A](inline binding: Binding[A])
transparent inline def bind: A = null.asInstanceOf[A]

trait Vars[A] extends BindingSeq[A]
trait BindingSeq[+A]:
def foreachBinding[U](f: A => Binding[U]): Binding[Unit] = ???

extension [A](inline bindingSeq: BindingSeq[A])
transparent inline def foreach[U](inline f: A => U): Unit = ${ Macros.foreach('bindingSeq, 'f) }
54 changes: 54 additions & 0 deletions tests/pos-macros/i19191/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import scala.compiletime._
import scala.deriving._
import scala.quoted._
import scala.annotation.meta.field

object Macros{
private def bindingFunctionBody[A: quoted.Type, B: quoted.Type](f: quoted.Expr[A => B])(using Quotes) =
import quoted.quotes.reflect.*
f.asTerm match
case inlined @ Inlined(
call,
bindings,
block @ Block(
List(
defDef @ DefDef(
name,
paramss @ List(TermParamClause(List(param @ ValDef(paramName, paramTpt, _)))),
tpt,
Some(rhs)
)
),
closureIdent
)
) =>
Inlined
.copy(inlined)(
call,
bindings,
'{ (a: A) =>
${
Block(
List(
ValDef
.copy(param)(paramName, paramTpt, Some('a.asTerm))
.changeOwner(Symbol.spliceOwner)
),
'{
Binding(${ rhs.changeOwner(Symbol.spliceOwner).asExprOf[B] })
}.asTerm.changeOwner(Symbol.spliceOwner)
)
.asExprOf[Binding[B]]
}: Binding[B]
}.asTerm
)
.asExprOf[A => Binding[B]]
case _ =>
'{ (a: A) => Binding.Constant($f(a)): Binding[B] }
end match
end bindingFunctionBody

def foreach[A: quoted.Type, U: quoted.Type](self: quoted.Expr[BindingSeq[A]], f: quoted.Expr[A => U])(using
qctx: Quotes
): quoted.Expr[Unit] = '{ $self.foreachBinding(${ bindingFunctionBody(f) }).bind }
}
6 changes: 6 additions & 0 deletions tests/pos-macros/i19191/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
def Test = {
val vars: Vars[Int] = ???

val works = vars.foreach { v => () }
val fails = for (v <- vars) ()
}

0 comments on commit 70ec709

Please sign in to comment.