diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 51f133c972b4..04d8d7bc51a0 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -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 diff --git a/tests/pos-macros/i19191/Lib_1.scala b/tests/pos-macros/i19191/Lib_1.scala new file mode 100644 index 000000000000..dd77aec377b2 --- /dev/null +++ b/tests/pos-macros/i19191/Lib_1.scala @@ -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) } diff --git a/tests/pos-macros/i19191/Macro_1.scala b/tests/pos-macros/i19191/Macro_1.scala new file mode 100644 index 000000000000..b5ba403d3faa --- /dev/null +++ b/tests/pos-macros/i19191/Macro_1.scala @@ -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 } +} diff --git a/tests/pos-macros/i19191/Test_2.scala b/tests/pos-macros/i19191/Test_2.scala new file mode 100644 index 000000000000..c7f40d996ed9 --- /dev/null +++ b/tests/pos-macros/i19191/Test_2.scala @@ -0,0 +1,6 @@ +def Test = { + val vars: Vars[Int] = ??? + + val works = vars.foreach { v => () } + val fails = for (v <- vars) () +}