diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index 06a064860ac4..fb4fd2b8a9fb 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -203,24 +203,33 @@ object QuoteMatcher { // Matches an open term and wraps it into a lambda that provides the free variables case Apply(TypeApply(Ident(_), List(TypeTree())), SeqLiteral(args, _) :: Nil) if pattern.symbol.eq(defn.QuotedRuntimePatterns_higherOrderHole) => - val names: List[TermName] = args.map { - case Block(List(DefDef(nme.ANON_FUN, _, _, Apply(Ident(name), _))), _) => name.asTermName - case arg => arg.symbol.name.asTermName + def hoasClosure = { + val names: List[TermName] = args.map { + case Block(List(DefDef(nme.ANON_FUN, _, _, Apply(Ident(name), _))), _) => name.asTermName + case arg => arg.symbol.name.asTermName + } + val argTypes = args.map(x => x.tpe.widenTermRefExpr) + val methTpe = MethodType(names)(_ => argTypes, _ => pattern.tpe) + val meth = newAnonFun(ctx.owner, methTpe) + def bodyFn(lambdaArgss: List[List[Tree]]): Tree = { + val argsMap = args.map(_.symbol).zip(lambdaArgss.head).toMap + val body = new TreeMap { + override def transform(tree: Tree)(using Context): Tree = + tree match + case tree: Ident => summon[Env].get(tree.symbol).flatMap(argsMap.get).getOrElse(tree) + case tree => super.transform(tree) + }.transform(scrutinee) + TreeOps(body).changeNonLocalOwners(meth) + } + Closure(meth, bodyFn) } - val argTypes = args.map(x => x.tpe.widenTermRefExpr) - val methTpe = MethodType(names)(_ => argTypes, _ => pattern.tpe) - val meth = newAnonFun(ctx.owner, methTpe) - def bodyFn(lambdaArgss: List[List[Tree]]): Tree = { - val argsMap = args.map(_.symbol).zip(lambdaArgss.head).toMap - val body = new TreeMap { - override def transform(tree: Tree)(using Context): Tree = - tree match - case tree: Ident => summon[Env].get(tree.symbol).flatMap(argsMap.get).getOrElse(tree) - case tree => super.transform(tree) - }.transform(scrutinee) - TreeOps(body).changeNonLocalOwners(meth) + val capturedArgs = args.map(_.symbol) + val captureEnv = summon[Env].filter((k, v) => !capturedArgs.contains(v)) + withEnv(captureEnv) { + scrutinee match + case ClosedPatternTerm(scrutinee) => matched(hoasClosure) + case _ => notMatched } - matched(Closure(meth, bodyFn)) /* Match type ascription (b) */ case Typed(expr2, _) => diff --git a/tests/run-macros/pattern-scope-extrusion.check b/tests/run-macros/pattern-scope-extrusion.check new file mode 100644 index 000000000000..22dbc280172d --- /dev/null +++ b/tests/run-macros/pattern-scope-extrusion.check @@ -0,0 +1,7 @@ +(42: scala.Int) +((x: scala.Int) => x.+(x)).apply(1) + +(42: scala.Int) +((x: scala.Int) => x.+(x)).apply(1) +((y: scala.Int) => y.+(y)).apply(2) +((x: scala.Int, y: scala.Int) => x.+(y)).apply(1, 2) diff --git a/tests/run-macros/pattern-scope-extrusion/quoted_1.scala b/tests/run-macros/pattern-scope-extrusion/quoted_1.scala new file mode 100644 index 000000000000..8494a0ed7a1d --- /dev/null +++ b/tests/run-macros/pattern-scope-extrusion/quoted_1.scala @@ -0,0 +1,18 @@ +import scala.quoted.* + +inline def test1(inline x: Int): String = ${ test1Expr('x) } +inline def test2(inline x: Int): String = ${ test2Expr('x) } + +private def test1Expr(x: Expr[Int])(using Quotes) : Expr[String] = + x match + case '{ val x: Int = 1; $z: Int } => Expr(z.show) + case '{ val x: Int = 1; $z(x): Int } => Expr('{$z(1)}.show) + case _ => '{"No match"} + +private def test2Expr(x: Expr[Int])(using Quotes) : Expr[String] = + x match + case '{ val x: Int = 1; val y: Int = 2; $z: Int } => Expr(z.show) + case '{ val x: Int = 1; val y: Int = 2; $z(x): Int } => Expr('{$z(1)}.show) + case '{ val x: Int = 1; val y: Int = 2; $z(y): Int } => Expr('{$z(2)}.show) + case '{ val x: Int = 1; val y: Int = 2; $z(x,y): Int } => Expr('{$z(1, 2)}.show) + case _ => '{"No match"} diff --git a/tests/run-macros/pattern-scope-extrusion/quoted_2.scala b/tests/run-macros/pattern-scope-extrusion/quoted_2.scala new file mode 100644 index 000000000000..614605e75938 --- /dev/null +++ b/tests/run-macros/pattern-scope-extrusion/quoted_2.scala @@ -0,0 +1,34 @@ +@main def Test = + println(test1 { + val a: Int = 1 + 42: Int + }) + println(test1 { + val a: Int = 1 + a + a + }) + + println() + + println(test2 { + val a: Int = 1 + val b: Int = 2 + 42: Int + }) + println(test2 { + val a: Int = 1 + val b: Int = 2 + a + a + }) + + println(test2 { + val a: Int = 1 + val b: Int = 2 + b + b + }) + + println(test2 { + val a: Int = 1 + val b: Int = 2 + a + b + })