From cd2efa8f0a7d0c501b36ebc2a2c1d086140ee464 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 31 Oct 2025 10:21:53 -0700 Subject: [PATCH 1/6] Use correct patvars --- compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index e4a17e04a7d1..e5dd15d694cb 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -463,7 +463,7 @@ class QuoteMatcher(debug: Boolean) { if sclo.tpe.isNothingType && schi.tpe.isAny => pttypedef match case TypeDef(_, TypeBoundsTree(ptlo, pthi, EmptyTree)) - if sclo.tpe.isNothingType && schi.tpe.isAny => + if ptlo.tpe.isNothingType && pthi.tpe.isAny => matched case _ => notMatched case _ => notMatched From cf4c07b0474a00a4d79b08c6c16c492d35ef8e98 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 31 Oct 2025 10:23:59 -0700 Subject: [PATCH 2/6] Try aligned cases for reading --- .../scala/quoted/runtime/impl/QuoteMatcher.scala | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index e5dd15d694cb..6ebd03bcf363 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -458,14 +458,15 @@ class QuoteMatcher(debug: Boolean) { * Implementation restriction: The current implementation matches type parameters * only when they have empty bounds (>: Nothing <: Any) */ - def matchTypeDef(sctypedef: TypeDef, pttypedef: TypeDef): MatchingExprs = sctypedef match + def matchTypeDef(sctypedef: TypeDef, pttypedef: TypeDef): MatchingExprs = + sctypedef match case TypeDef(_, TypeBoundsTree(sclo, schi, EmptyTree)) - if sclo.tpe.isNothingType && schi.tpe.isAny => + if sclo.tpe.isNothingType && schi.tpe.isAny => pttypedef match - case TypeDef(_, TypeBoundsTree(ptlo, pthi, EmptyTree)) - if ptlo.tpe.isNothingType && pthi.tpe.isAny => - matched - case _ => notMatched + case TypeDef(_, TypeBoundsTree(ptlo, pthi, EmptyTree)) + if ptlo.tpe.isNothingType && pthi.tpe.isAny => + matched + case _ => notMatched case _ => notMatched def matchParamss(scparamss: List[ParamClause], ptparamss: List[ParamClause])(using Env): optional[(Env, MatchingExprs)] = From f75130d210745a2ec57f6405ffc9b991d5ba9ce9 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 31 Oct 2025 18:23:33 -0700 Subject: [PATCH 3/6] Try unrolled inline loop for safety --- .../quoted/runtime/impl/QuoteMatcher.scala | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index 6ebd03bcf363..1f2d10aa22e0 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -454,20 +454,20 @@ class QuoteMatcher(debug: Boolean) { notMatched case _ => matched - /** - * Implementation restriction: The current implementation matches type parameters - * only when they have empty bounds (>: Nothing <: Any) - */ + /** Implementation restriction: The current implementation matches type parameters + * only when they have empty bounds (>: Nothing <: Any) + */ def matchTypeDef(sctypedef: TypeDef, pttypedef: TypeDef): MatchingExprs = - sctypedef match - case TypeDef(_, TypeBoundsTree(sclo, schi, EmptyTree)) - if sclo.tpe.isNothingType && schi.tpe.isAny => - pttypedef match - case TypeDef(_, TypeBoundsTree(ptlo, pthi, EmptyTree)) - if ptlo.tpe.isNothingType && pthi.tpe.isAny => + inline def recur(tds: List[TypeDef], unwind: Int): MatchingExprs = + if unwind == 0 then matched - case _ => notMatched - case _ => notMatched + else + tds.head match + case TypeDef(_, TypeBoundsTree(lo, hi, EmptyTree)) + if lo.tpe.isNothingType && hi.tpe.isAny + => recur(tds.tail, unwind - 1) + case _ => notMatched + recur(sctypedef :: pttypedef :: Nil, unwind = 2) def matchParamss(scparamss: List[ParamClause], ptparamss: List[ParamClause])(using Env): optional[(Env, MatchingExprs)] = (scparamss, ptparamss) match { From 0d6cde1b0613c367da29523eeb8879f544440e68 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 31 Oct 2025 20:10:13 -0700 Subject: [PATCH 4/6] Try array for simplicity --- .../scala/quoted/runtime/impl/QuoteMatcher.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index 1f2d10aa22e0..11d1ff5a35ed 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -458,16 +458,16 @@ class QuoteMatcher(debug: Boolean) { * only when they have empty bounds (>: Nothing <: Any) */ def matchTypeDef(sctypedef: TypeDef, pttypedef: TypeDef): MatchingExprs = - inline def recur(tds: List[TypeDef], unwind: Int): MatchingExprs = - if unwind == 0 then + inline def recur(tds: Array[TypeDef], i: Int): MatchingExprs = + if i == 2 then matched else - tds.head match - case TypeDef(_, TypeBoundsTree(lo, hi, EmptyTree)) - if lo.tpe.isNothingType && hi.tpe.isAny - => recur(tds.tail, unwind - 1) + tds(i).rhs match + case tbt: TypeBoundsTree + if tbt.lo.tpe.isNothingType && tbt.hi.tpe.isAny && tbt.alias.isEmpty + => recur(tds, i + 1) case _ => notMatched - recur(sctypedef :: pttypedef :: Nil, unwind = 2) + recur(Array(sctypedef, pttypedef), i = 0) def matchParamss(scparamss: List[ParamClause], ptparamss: List[ParamClause])(using Env): optional[(Env, MatchingExprs)] = (scparamss, ptparamss) match { From 12d87dfa26bd2e09d0690d07f369eb8943ec1ae3 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 31 Oct 2025 20:13:45 -0700 Subject: [PATCH 5/6] Try picking the arg for simplicity --- .../src/scala/quoted/runtime/impl/QuoteMatcher.scala | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index 11d1ff5a35ed..61b972844779 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -458,16 +458,17 @@ class QuoteMatcher(debug: Boolean) { * only when they have empty bounds (>: Nothing <: Any) */ def matchTypeDef(sctypedef: TypeDef, pttypedef: TypeDef): MatchingExprs = - inline def recur(tds: Array[TypeDef], i: Int): MatchingExprs = + inline def recur(i: Int): MatchingExprs = if i == 2 then matched else - tds(i).rhs match + val td = if i == 0 then sctypedef else pttypedef + td.rhs match case tbt: TypeBoundsTree if tbt.lo.tpe.isNothingType && tbt.hi.tpe.isAny && tbt.alias.isEmpty - => recur(tds, i + 1) + => recur(i + 1) case _ => notMatched - recur(Array(sctypedef, pttypedef), i = 0) + recur(i = 0) def matchParamss(scparamss: List[ParamClause], ptparamss: List[ParamClause])(using Env): optional[(Env, MatchingExprs)] = (scparamss, ptparamss) match { From 9c218d051c08dc3bc904a64f8745db85d226e15d Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 31 Oct 2025 22:22:08 -0700 Subject: [PATCH 6/6] Realign stars munged by cut-paste --- .../quoted/runtime/impl/QuoteMatcher.scala | 73 +++++++++---------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index 61b972844779..8c37bdada2b1 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -116,13 +116,13 @@ class QuoteMatcher(debug: Boolean) { private type MatchingExprs = Seq[MatchResult] /** TODO-18271: update - * A map relating equivalent symbols from the scrutinee and the pattern - * For example in - * ``` - * '{val a = 4; a * a} match case '{ val x = 4; x * x } - * ``` - * when matching `a * a` with `x * x` the environment will contain `Map(a -> x)`. - */ + * A map relating equivalent symbols from the scrutinee and the pattern + * For example in + * ``` + * '{val a = 4; a * a} match case '{ val x = 4; x * x } + * ``` + * when matching `a * a` with `x * x` the environment will contain `Map(a -> x)`. + */ private case class Env(val termEnv: Map[Symbol, Symbol], val typeEnv: Map[Symbol, Symbol]) private def withEnv[T](env: Env)(body: Env ?=> T): T = body(using env) @@ -198,14 +198,14 @@ class QuoteMatcher(debug: Boolean) { extension (scrutinee0: Tree) /** Check that the trees match and return the contents from the pattern holes. - * Return a sequence containing all the contents in the holes. - * If it does not match, continues to the `optional` with `None`. - * - * @param scrutinee The tree being matched - * @param pattern The pattern tree that the scrutinee should match. Contains `patternHole` holes. - * @param `summon[Env]` Set of tuples containing pairs of symbols (s, p) where s defines a symbol in `scrutinee` which corresponds to symbol p in `pattern`. - * @return The sequence with the contents of the holes of the matched expression. - */ + * Return a sequence containing all the contents in the holes. + * If it does not match, continues to the `optional` with `None`. + * + * @param scrutinee The tree being matched + * @param pattern The pattern tree that the scrutinee should match. Contains `patternHole` holes. + * @param `summon[Env]` Set of tuples containing pairs of symbols (s, p) where s defines a symbol in `scrutinee` which corresponds to symbol p in `pattern`. + * @return The sequence with the contents of the holes of the matched expression. + */ private def =?= (pattern0: Tree)(using Env, Context): optional[MatchingExprs] = /* Match block flattening */ // TODO move to cases @@ -238,19 +238,19 @@ class QuoteMatcher(debug: Boolean) { case _ => None end TypeTreeTypeTest - /* Some of method symbols in arguments of higher-order term hole are eta-expanded. - * e.g. - * g: (Int) => Int - * => { - * def $anonfun(y: Int): Int = g(y) - * closure($anonfun) - * } - * - * f: (using Int) => Int - * => f(using x) - * This function restores the symbol of the original method from - * the eta-expanded function. - */ + /** Some of method symbols in arguments of higher-order term hole are eta-expanded. + * e.g. + * g: (Int) => Int + * => { + * def $anonfun(y: Int): Int = g(y) + * closure($anonfun) + * } + * + * f: (using Int) => Int + * => f(using x) + * This function restores the symbol of the original method from + * the eta-expanded function. + */ def getCapturedIdent(arg: Tree)(using Context): Ident = arg match case id: Ident => id @@ -552,10 +552,10 @@ class QuoteMatcher(debug: Boolean) { end extension /** Does the scrutinee symbol match the pattern symbol? It matches if: - * - They are the same symbol - * - The scrutinee has is in the environment and they are equivalent - * - The scrutinee overrides the symbol of the pattern - */ + * - They are the same symbol + * - The scrutinee is in the environment and they are equivalent + * - The scrutinee overrides the symbol of the pattern + */ private def symbolMatch(scrutineeTree: Tree, patternTree: Tree)(using Env, Context): Boolean = val scrutinee = scrutineeTree.symbol @@ -675,11 +675,10 @@ class QuoteMatcher(debug: Boolean) { treeMap = new TreeMap { override def transform(tree: Tree)(using Context): Tree = tree match - /* - * When matching a method call `f(0)` against a HOAS pattern `p(g)` where - * f has a method type `(x: Int): Int` and `f` maps to `g`, `p` should hold - * `g.apply(0)` because the type of `g` is `Int => Int` due to eta expansion. - */ + /* When matching a method call `f(0)` against a HOAS pattern `p(g)` where + * f has a method type `(x: Int): Int` and `f` maps to `g`, `p` should hold + * `g.apply(0)` because the type of `g` is `Int => Int` due to eta expansion. + */ case Apply(fun, args) if termEnv.contains(tree.symbol) => transform(fun).select(nme.apply).appliedToArgs(args.map(transform)) case tree: Ident => termEnv.get(tree.symbol).flatMap(argsMap.get).getOrElse(tree) case tree => super.transform(tree)