From 9a2a1eec1e9958e0e209bbb962f1b73a2b49164c Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 13:35:56 +0200 Subject: [PATCH 01/21] Use `defn` directly --- .../src/scala/quoted/runtime/impl/Matcher.scala | 14 +++++++------- .../src/scala/quoted/runtime/impl/QuotesImpl.scala | 5 +---- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/Matcher.scala b/compiler/src/scala/quoted/runtime/impl/Matcher.scala index c6176cbcf830..e5dd8449a511 100644 --- a/compiler/src/scala/quoted/runtime/impl/Matcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/Matcher.scala @@ -4,6 +4,9 @@ package runtime.impl import scala.annotation.internal.sharable import scala.annotation.{Annotation, compileTimeOnly} +import dotty.tools.dotc +import dotty.tools.dotc.core.Contexts._ + /** Matches a quoted tree against a quoted pattern tree. * A quoted pattern tree may have type and term holes in addition to normal terms. * @@ -96,7 +99,7 @@ import scala.annotation.{Annotation, compileTimeOnly} */ object Matcher { - abstract class QuoteMatcher[QCtx <: Quotes & Singleton](val qctx: QCtx) { + class QuoteMatcher[QCtx <: Quotes & Singleton](val qctx: QCtx)(using Context) { // TODO improve performance @@ -106,9 +109,6 @@ object Matcher { import qctx.reflect._ import Matching._ - def patternHoleSymbol: Symbol - def higherOrderHoleSymbol: Symbol - /** A map relating equivalent symbols from the scrutinee and the pattern * For example in * ``` @@ -179,7 +179,7 @@ object Matcher { /* Term hole */ // Match a scala.internal.Quoted.patternHole typed as a repeated argument and return the scrutinee tree case (scrutinee @ Typed(s, tpt1), Typed(TypeApply(patternHole, tpt :: Nil), tpt2)) - if patternHole.symbol == patternHoleSymbol && + if patternHole.symbol.eq(dotc.core.Symbols.defn.QuotedRuntimePatterns_patternHole) && s.tpe <:< tpt.tpe && tpt2.tpe.derivesFrom(defn.RepeatedParamClass) => matched(scrutinee.asExpr) @@ -187,14 +187,14 @@ object Matcher { /* Term hole */ // Match a scala.internal.Quoted.patternHole and return the scrutinee tree case (ClosedPatternTerm(scrutinee), TypeApply(patternHole, tpt :: Nil)) - if patternHole.symbol == patternHoleSymbol && + if patternHole.symbol.eq(dotc.core.Symbols.defn.QuotedRuntimePatterns_patternHole) && scrutinee.tpe <:< tpt.tpe => matched(scrutinee.asExpr) /* Higher order term hole */ // Matches an open term and wraps it into a lambda that provides the free variables case (scrutinee, pattern @ Apply(TypeApply(Ident("higherOrderHole"), List(Inferred())), Repeated(args, _) :: Nil)) - if pattern.symbol == higherOrderHoleSymbol => + if pattern.symbol.eq(dotc.core.Symbols.defn.QuotedRuntimePatterns_higherOrderHole) => def bodyFn(lambdaArgs: List[Tree]): Tree = { val argsMap = args.map(_.symbol).zip(lambdaArgs.asInstanceOf[List[Term]]).toMap diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index fed05b4492fd..a3b53b08e16b 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2937,10 +2937,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler val qctx1 = QuotesImpl()(using ctx1) - val matcher = new Matcher.QuoteMatcher[qctx1.type](qctx1) { - def patternHoleSymbol: qctx1.reflect.Symbol = dotc.core.Symbols.defn.QuotedRuntimePatterns_patternHole.asInstanceOf - def higherOrderHoleSymbol: qctx1.reflect.Symbol = dotc.core.Symbols.defn.QuotedRuntimePatterns_higherOrderHole.asInstanceOf - } + val matcher = new Matcher.QuoteMatcher[qctx1.type](qctx1)(using ctx1) val matchings = if pat1.isType then matcher.termMatch(scrutinee.asInstanceOf[matcher.qctx.reflect.Term], pat1.asInstanceOf[matcher.qctx.reflect.Term]) From a523bcabe37c1dbf86dfc259fb83dea6863375de Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 13:50:32 +0200 Subject: [PATCH 02/21] Remove IdentArgs --- .../src/scala/quoted/runtime/impl/Matcher.scala | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/Matcher.scala b/compiler/src/scala/quoted/runtime/impl/Matcher.scala index e5dd8449a511..fa2ea668470f 100644 --- a/compiler/src/scala/quoted/runtime/impl/Matcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/Matcher.scala @@ -6,6 +6,8 @@ import scala.annotation.{Annotation, compileTimeOnly} import dotty.tools.dotc import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.StdNames.nme /** Matches a quoted tree against a quoted pattern tree. * A quoted pattern tree may have type and term holes in addition to normal terms. @@ -389,17 +391,6 @@ object Matcher { accumulator.foldTree(Set.empty, term)(Symbol.spliceOwner) } - private object IdentArgs { - def unapply(args: List[Term]): Option[List[Ident]] = - args.foldRight(Option(List.empty[Ident])) { - case (id: Ident, Some(acc)) => Some(id :: acc) - case (Block(List(DefDef("$anonfun", TermParamClause(params) :: Nil, Inferred(), Some(Apply(id: Ident, args)))), Closure(Ident("$anonfun"), None)), Some(acc)) - if params.zip(args).forall(_.symbol == _.symbol) => - Some(id :: acc) - case _ => None - } - } - private def treeOptMatches(scrutinee: Option[Tree], pattern: Option[Tree])(using Env): Matching = { (scrutinee, pattern) match { case (Some(x), Some(y)) => x =?= y From 02b3896ae3ce3cf57f7d8176473b838cf772a329 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 14:01:26 +0200 Subject: [PATCH 03/21] Port freePatternVars --- .../src/scala/quoted/runtime/impl/Matcher.scala | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/Matcher.scala b/compiler/src/scala/quoted/runtime/impl/Matcher.scala index fa2ea668470f..9134abbedb3a 100644 --- a/compiler/src/scala/quoted/runtime/impl/Matcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/Matcher.scala @@ -6,7 +6,6 @@ import scala.annotation.{Annotation, compileTimeOnly} import dotty.tools.dotc import dotty.tools.dotc.core.Contexts._ -import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.StdNames.nme /** Matches a quoted tree against a quoted pattern tree. @@ -378,17 +377,19 @@ object Matcher { private object ClosedPatternTerm { /** Matches a term that does not contain free variables defined in the pattern (i.e. not defined in `Env`) */ def unapply(term: Term)(using Env): Option[term.type] = - if freePatternVars(term).isEmpty then Some(term) else None + if freePatternVars(term.asInstanceOf).isEmpty then Some(term) else None /** Return all free variables of the term defined in the pattern (i.e. defined in `Env`) */ - def freePatternVars(term: Term)(using env: Env): Set[Symbol] = + def freePatternVars(term: dotc.ast.tpd.Tree)(using env: Env): Set[dotc.core.Symbols.Symbol] = + import dotc.ast.tpd.* // TODO remove + import dotc.core.Symbols.* // TODO remove val accumulator = new TreeAccumulator[Set[Symbol]] { - def foldTree(x: Set[Symbol], tree: Tree)(owner: Symbol): Set[Symbol] = + def apply(x: Set[Symbol], tree: Tree)(using Context): Set[Symbol] = tree match - case tree: Ident if env.contains(tree.symbol) => foldOverTree(x + tree.symbol, tree)(owner) - case _ => foldOverTree(x, tree)(owner) + case tree: Ident if env.contains(tree.symbol.asInstanceOf) => foldOver(x + tree.symbol, tree) + case _ => foldOver(x, tree) } - accumulator.foldTree(Set.empty, term)(Symbol.spliceOwner) + accumulator.apply(Set.empty, term) } private def treeOptMatches(scrutinee: Option[Tree], pattern: Option[Tree])(using Env): Matching = { From 77208d3152b619fb876f4bcc4855f6bdf2657f61 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 14:09:10 +0200 Subject: [PATCH 04/21] Port symbolMatch --- .../src/scala/quoted/runtime/impl/Matcher.scala | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/Matcher.scala b/compiler/src/scala/quoted/runtime/impl/Matcher.scala index 9134abbedb3a..ff83ce809288 100644 --- a/compiler/src/scala/quoted/runtime/impl/Matcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/Matcher.scala @@ -232,7 +232,7 @@ object Matcher { scrutinee =?= expr2 /* Match selection */ - case (ref: Ref, Select(qual2, _)) if symbolMatch(scrutinee, pattern) => + case (ref: Ref, Select(qual2, _)) if symbolMatch(scrutinee.asInstanceOf, pattern.asInstanceOf) => ref match case Select(qual1, _) => qual1 =?= qual2 case ref: Ident => @@ -241,7 +241,7 @@ object Matcher { case _ => matched /* Match reference */ - case (_: Ref, _: Ident) if symbolMatch(scrutinee, pattern) => + case (_: Ref, _: Ident) if symbolMatch(scrutinee.asInstanceOf, pattern.asInstanceOf) => matched /* Match application */ @@ -360,18 +360,25 @@ object Matcher { * - The scrutinee has 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): Boolean = + private def symbolMatch(scrutineeTree: dotc.ast.tpd.Tree, patternTree: dotc.ast.tpd.Tree)(using Env): Boolean = + import dotc.ast.tpd.* // TODO remove val scrutinee = scrutineeTree.symbol + + def overridingSymbol(ofclazz: dotc.core.Symbols.Symbol): dotc.core.Symbols.Symbol = + if ofclazz.isClass then scrutinee.denot.overridingSymbol(ofclazz.asClass) + else dotc.core.Symbols.NoSymbol + val devirtualizedScrutinee = scrutineeTree match case Select(qual, _) => - val sym = scrutinee.overridingSymbol(qual.tpe.typeSymbol) + val sym = overridingSymbol(qual.tpe.typeSymbol) if sym.exists then sym else scrutinee case _ => scrutinee val pattern = patternTree.symbol + devirtualizedScrutinee == pattern - || summon[Env].get(devirtualizedScrutinee).contains(pattern) + || summon[Env].get(devirtualizedScrutinee.asInstanceOf).contains(pattern) || devirtualizedScrutinee.allOverriddenSymbols.contains(pattern) private object ClosedPatternTerm { From b751d13d6df2e128ac90f544619d88750f9127c8 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 14:27:04 +0200 Subject: [PATCH 05/21] Port Env --- .../scala/quoted/runtime/impl/Matcher.scala | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/Matcher.scala b/compiler/src/scala/quoted/runtime/impl/Matcher.scala index ff83ce809288..449d8b2610ce 100644 --- a/compiler/src/scala/quoted/runtime/impl/Matcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/Matcher.scala @@ -5,6 +5,7 @@ import scala.annotation.internal.sharable import scala.annotation.{Annotation, compileTimeOnly} import dotty.tools.dotc +import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.StdNames.nme @@ -99,6 +100,7 @@ import dotty.tools.dotc.core.StdNames.nme * ``` */ object Matcher { + import tpd.* class QuoteMatcher[QCtx <: Quotes & Singleton](val qctx: QCtx)(using Context) { @@ -117,7 +119,7 @@ object Matcher { * ``` * when matching `a * a` with `x * x` the environment will contain `Map(a -> x)`. */ - private type Env = Map[Symbol, Symbol] + private type Env = Map[dotc.core.Symbols.Symbol, dotc.core.Symbols.Symbol] inline private def withEnv[T](env: Env)(inline body: Env ?=> T): T = body(using env) @@ -202,7 +204,7 @@ object Matcher { new TreeMap { override def transformTerm(tree: Term)(owner: Symbol): Term = tree match - case tree: Ident => summon[Env].get(tree.symbol).flatMap(argsMap.get).getOrElse(tree) + case tree: Ident => summon[Env].get(tree.symbol.asInstanceOf).asInstanceOf[Option[Symbol]].flatMap(argsMap.get).getOrElse(tree) case tree => super.transformTerm(tree)(owner) }.transformTree(scrutinee)(Symbol.spliceOwner) } @@ -256,7 +258,7 @@ object Matcher { case (Block(stat1 :: stats1, expr1), Block(stat2 :: stats2, expr2)) => val newEnv = (stat1, stat2) match { case (stat1: Definition, stat2: Definition) => - summon[Env] + (stat1.symbol -> stat2.symbol) + summon[Env] + (stat1.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> stat2.symbol.asInstanceOf[dotc.core.Symbols.Symbol]) case _ => summon[Env] } @@ -299,20 +301,20 @@ object Matcher { /* Match val */ case (ValDef(_, tpt1, rhs1), ValDef(_, tpt2, rhs2)) if checkValFlags() => - def rhsEnv = summon[Env] + (scrutinee.symbol -> pattern.symbol) + def rhsEnv = summon[Env] + (scrutinee.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> pattern.symbol.asInstanceOf[dotc.core.Symbols.Symbol]) tpt1 =?= tpt2 &&& treeOptMatches(rhs1, rhs2)(using rhsEnv) /* Match def */ case (DefDef(_, paramss1, tpt1, Some(rhs1)), DefDef(_, paramss2, tpt2, Some(rhs2))) => - def rhsEnv = - val paramSyms: List[(Symbol, Symbol)] = + def rhsEnv: Env = + val paramSyms: List[(dotc.core.Symbols.Symbol, dotc.core.Symbols.Symbol)] = for (clause1, clause2) <- paramss1.zip(paramss2) (param1, param2) <- clause1.params.zip(clause2.params) yield - param1.symbol -> param2.symbol + param1.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> param2.symbol.asInstanceOf[dotc.core.Symbols.Symbol] val oldEnv: Env = summon[Env] - val newEnv: List[(Symbol, Symbol)] = (scrutinee.symbol -> pattern.symbol) :: paramSyms + val newEnv: List[(dotc.core.Symbols.Symbol, dotc.core.Symbols.Symbol)] = (scrutinee.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> pattern.symbol.asInstanceOf[dotc.core.Symbols.Symbol]) :: paramSyms oldEnv ++ newEnv matchLists(paramss1, paramss2)(_ =?= _) From cbe825f4d1438ae79f02058f525bbde7e053fde0 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 15:10:57 +0200 Subject: [PATCH 06/21] =?UTF-8?q?Partially=20port=20Tree=20=3D=3F=3D?= --- .../scala/quoted/runtime/impl/Matcher.scala | 125 ++++++++++-------- 1 file changed, 73 insertions(+), 52 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/Matcher.scala b/compiler/src/scala/quoted/runtime/impl/Matcher.scala index 449d8b2610ce..b1a564a356dc 100644 --- a/compiler/src/scala/quoted/runtime/impl/Matcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/Matcher.scala @@ -143,6 +143,13 @@ object Matcher { private def =?= (patterns: List[Tree])(using Env): Matching = matchLists(scrutinees, patterns)(_ =?= _) + extension (scrutinee: tpd.Tree) + private def =?= (pattern: tpd.Tree)(using Env): Matching = + scrutinee.asInstanceOf[Tree] =?= pattern.asInstanceOf[Tree] + extension (scrutinees: List[tpd.Tree]) + private def =?= (patterns: List[tpd.Tree])(using Env)(using DummyImplicit): Matching = + matchLists(scrutinees, patterns)(_ =?= _) + extension (scrutinee0: Tree) /** Check that the trees match and return the contents from the pattern holes. * Return None if the trees do not match otherwise return Some of a tuple containing all the contents in the holes. @@ -169,14 +176,6 @@ object Matcher { val scrutinee = normalize(scrutinee0) val pattern = normalize(pattern0) - /** Check that both are `val` or both are `lazy val` or both are `var` **/ - def checkValFlags(): Boolean = { - import Flags._ - val sFlags = scrutinee.symbol.flags - val pFlags = pattern.symbol.flags - sFlags.is(Lazy) == pFlags.is(Lazy) && sFlags.is(Mutable) == pFlags.is(Mutable) - } - (scrutinee, pattern) match { /* Term hole */ @@ -299,56 +298,78 @@ object Matcher { case (scrutinee: TypeTree, pattern: TypeTree) if scrutinee.tpe <:< pattern.tpe => matched - /* Match val */ - case (ValDef(_, tpt1, rhs1), ValDef(_, tpt2, rhs2)) if checkValFlags() => - def rhsEnv = summon[Env] + (scrutinee.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> pattern.symbol.asInstanceOf[dotc.core.Symbols.Symbol]) - tpt1 =?= tpt2 &&& treeOptMatches(rhs1, rhs2)(using rhsEnv) - - /* Match def */ - case (DefDef(_, paramss1, tpt1, Some(rhs1)), DefDef(_, paramss2, tpt2, Some(rhs2))) => - def rhsEnv: Env = - val paramSyms: List[(dotc.core.Symbols.Symbol, dotc.core.Symbols.Symbol)] = - for - (clause1, clause2) <- paramss1.zip(paramss2) - (param1, param2) <- clause1.params.zip(clause2.params) - yield - param1.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> param2.symbol.asInstanceOf[dotc.core.Symbols.Symbol] - val oldEnv: Env = summon[Env] - val newEnv: List[(dotc.core.Symbols.Symbol, dotc.core.Symbols.Symbol)] = (scrutinee.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> pattern.symbol.asInstanceOf[dotc.core.Symbols.Symbol]) :: paramSyms - oldEnv ++ newEnv - - matchLists(paramss1, paramss2)(_ =?= _) - &&& tpt1 =?= tpt2 - &&& withEnv(rhsEnv)(rhs1 =?= rhs2) - - case (Closure(_, tpt1), Closure(_, tpt2)) => - // TODO match tpt1 with tpt2? - matched - - case (NamedArg(name1, arg1), NamedArg(name2, arg2)) if name1 == name2 => - arg1 =?= arg2 // No Match case _ => - if (debug) - println( - s""">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - |Scrutinee - | ${scrutinee.show} - |did not match pattern - | ${pattern.show} - | - |with environment: ${summon[Env]} - | - |Scrutinee: ${scrutinee.show(using Printer.TreeStructure)} - |Pattern: ${pattern.show(using Printer.TreeStructure)} - | - |""".stripMargin) - notMatched + otherCases(scrutinee.asInstanceOf, pattern.asInstanceOf) } } end extension + def otherCases(scrutinee: tpd.Tree, pattern: tpd.Tree)(using Env): Matching = + import tpd.* // TODO remove + import dotc.core.Flags.* // TODO remove + + /** Check that both are `val` or both are `lazy val` or both are `var` **/ + def checkValFlags(): Boolean = { + val sFlags = scrutinee.symbol.flags + val pFlags = pattern.symbol.flags + sFlags.is(Lazy) == pFlags.is(Lazy) && sFlags.is(Mutable) == pFlags.is(Mutable) + } + + (scrutinee, pattern) match + + /* Match val */ + case (scrutinee @ ValDef(_, tpt1, _), pattern @ ValDef(_, tpt2, _)) if checkValFlags() => + def rhsEnv = summon[Env] + (scrutinee.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> pattern.symbol.asInstanceOf[dotc.core.Symbols.Symbol]) + tpt1 =?= tpt2 &&& withEnv(rhsEnv)(scrutinee.rhs =?= pattern.rhs) + + /* Match def */ + case (scrutinee @ DefDef(_, paramss1, tpt1, _), pattern @ DefDef(_, paramss2, tpt2, _)) => + def rhsEnv: Env = + val paramSyms: List[(dotc.core.Symbols.Symbol, dotc.core.Symbols.Symbol)] = + for + (clause1, clause2) <- paramss1.zip(paramss2) + (param1, param2) <- clause1.zip(clause2) + yield + param1.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> param2.symbol.asInstanceOf[dotc.core.Symbols.Symbol] + val oldEnv: Env = summon[Env] + val newEnv: List[(dotc.core.Symbols.Symbol, dotc.core.Symbols.Symbol)] = (scrutinee.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> pattern.symbol.asInstanceOf[dotc.core.Symbols.Symbol]) :: paramSyms + oldEnv ++ newEnv + + matchLists(paramss1, paramss2)(_ =?= _) + &&& tpt1 =?= tpt2 + &&& withEnv(rhsEnv)(scrutinee.rhs =?= pattern.rhs) + + case (Closure(_, _, tpt1), Closure(_, _, tpt2)) => + // TODO match tpt1 with tpt2? + matched + + case (NamedArg(name1, arg1), NamedArg(name2, arg2)) if name1 == name2 => + arg1 =?= arg2 + + case (EmptyTree, EmptyTree) => + matched + + // No Match + case _ => + if (debug) + println( + s""">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + |Scrutinee + | ${scrutinee.show} + |did not match pattern + | ${pattern.show} + | + |with environment: ${summon[Env]} + | + |Scrutinee: ${Printer.TreeStructure.show(scrutinee.asInstanceOf)} + |Pattern: ${Printer.TreeStructure.show(pattern.asInstanceOf)} + | + |""".stripMargin) + notMatched + + extension (scrutinee: ParamClause) /** Check that all parameters in the clauses clauses match with =?= and concatenate the results with &&& */ private def =?= (pattern: ParamClause)(using Env)(using DummyImplicit): Matching = @@ -401,7 +422,7 @@ object Matcher { accumulator.apply(Set.empty, term) } - private def treeOptMatches(scrutinee: Option[Tree], pattern: Option[Tree])(using Env): Matching = { + private def treeOptMatches(scrutinee: Option[Tree], pattern: Option[Tree])(using Env)(using DummyImplicit): Matching = { (scrutinee, pattern) match { case (Some(x), Some(y)) => x =?= y case (None, None) => matched From b370837f615bf24e3a82c02cf4d873271e0643c6 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 15:39:21 +0200 Subject: [PATCH 07/21] =?UTF-8?q?Port=20more=20of=20Tree=20=3D=3F=3D?= --- .../scala/quoted/runtime/impl/Matcher.scala | 101 ++++++++++-------- 1 file changed, 54 insertions(+), 47 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/Matcher.scala b/compiler/src/scala/quoted/runtime/impl/Matcher.scala index b1a564a356dc..a3497d7e9822 100644 --- a/compiler/src/scala/quoted/runtime/impl/Matcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/Matcher.scala @@ -253,52 +253,6 @@ object Matcher { case (TypeApply(fn1, args1), TypeApply(fn2, args2)) => fn1 =?= fn2 &&& args1 =?= args2 - /* Match block */ - case (Block(stat1 :: stats1, expr1), Block(stat2 :: stats2, expr2)) => - val newEnv = (stat1, stat2) match { - case (stat1: Definition, stat2: Definition) => - summon[Env] + (stat1.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> stat2.symbol.asInstanceOf[dotc.core.Symbols.Symbol]) - case _ => - summon[Env] - } - withEnv(newEnv) { - stat1 =?= stat2 &&& Block(stats1, expr1) =?= Block(stats2, expr2) - } - - /* Match if */ - case (If(cond1, thenp1, elsep1), If(cond2, thenp2, elsep2)) => - cond1 =?= cond2 &&& thenp1 =?= thenp2 &&& elsep1 =?= elsep2 - - /* Match while */ - case (While(cond1, body1), While(cond2, body2)) => - cond1 =?= cond2 &&& body1 =?= body2 - - /* Match assign */ - case (Assign(lhs1, rhs1), Assign(lhs2, rhs2)) => - lhs1 =?= lhs2 &&& rhs1 =?= rhs2 - - /* Match new */ - case (New(tpt1), New(tpt2)) if tpt1.tpe.typeSymbol == tpt2.tpe.typeSymbol => - matched - - /* Match this */ - case (This(_), This(_)) if scrutinee.symbol == pattern.symbol => - matched - - /* Match super */ - case (Super(qual1, mix1), Super(qual2, mix2)) if mix1 == mix2 => - qual1 =?= qual2 - - /* Match varargs */ - case (Repeated(elems1, _), Repeated(elems2, _)) if elems1.size == elems2.size => - elems1 =?= elems2 - - /* Match type */ - // TODO remove this? - case (scrutinee: TypeTree, pattern: TypeTree) if scrutinee.tpe <:< pattern.tpe => - matched - - // No Match case _ => otherCases(scrutinee.asInstanceOf, pattern.asInstanceOf) @@ -317,9 +271,62 @@ object Matcher { sFlags.is(Lazy) == pFlags.is(Lazy) && sFlags.is(Mutable) == pFlags.is(Mutable) } + // TODO remove + object TypeTreeTypeTest: + def unapply(x: Tree): Option[Tree & x.type] = x match + case x: (tpd.TypeBoundsTree & x.type) => None + case x: (tpd.Tree & x.type) if x.isType => Some(x) + case _ => None + end TypeTreeTypeTest + (scrutinee, pattern) match - /* Match val */ + /* Match block */ + case (Block(stat1 :: stats1, expr1), Block(stat2 :: stats2, expr2)) => + val newEnv = (stat1, stat2) match { + case (stat1: MemberDef, stat2: MemberDef) => + summon[Env] + (stat1.symbol -> stat2.symbol) + case _ => + summon[Env] + } + withEnv(newEnv) { + stat1 =?= stat2 &&& Block(stats1, expr1) =?= Block(stats2, expr2) + } + + /* Match if */ + case (If(cond1, thenp1, elsep1), If(cond2, thenp2, elsep2)) => + cond1 =?= cond2 &&& thenp1 =?= thenp2 &&& elsep1 =?= elsep2 + + /* Match while */ + case (WhileDo(cond1, body1), WhileDo(cond2, body2)) => + cond1 =?= cond2 &&& body1 =?= body2 + + /* Match assign */ + case (Assign(lhs1, rhs1), Assign(lhs2, rhs2)) => + lhs1 =?= lhs2 &&& rhs1 =?= rhs2 + + /* Match new */ + case (New(tpt1), New(tpt2)) if tpt1.tpe.typeSymbol == tpt2.tpe.typeSymbol => + matched + + /* Match this */ + case (This(_), This(_)) if scrutinee.symbol == pattern.symbol => + matched + + /* Match super */ + case (Super(qual1, mix1), Super(qual2, mix2)) if mix1 == mix2 => + qual1 =?= qual2 + + /* Match varargs */ + case (SeqLiteral(elems1, _), SeqLiteral(elems2, _)) if elems1.size == elems2.size => + elems1 =?= elems2 + + /* Match type */ + // TODO remove this? + case (TypeTreeTypeTest(scrutinee), TypeTreeTypeTest(pattern)) if scrutinee.tpe <:< pattern.tpe => + matched + + /* Match val */ case (scrutinee @ ValDef(_, tpt1, _), pattern @ ValDef(_, tpt2, _)) if checkValFlags() => def rhsEnv = summon[Env] + (scrutinee.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> pattern.symbol.asInstanceOf[dotc.core.Symbols.Symbol]) tpt1 =?= tpt2 &&& withEnv(rhsEnv)(scrutinee.rhs =?= pattern.rhs) From 3d39fec38428eed0ad66f5c2b8ac0cc79dc3dd53 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 15:50:08 +0200 Subject: [PATCH 08/21] =?UTF-8?q?Port=20more=20of=20Tree=20=3D=3F=3D=20(pa?= =?UTF-8?q?rt=202)?= --- .../scala/quoted/runtime/impl/Matcher.scala | 84 ++++++++++--------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/Matcher.scala b/compiler/src/scala/quoted/runtime/impl/Matcher.scala index a3497d7e9822..a1c44f0ec419 100644 --- a/compiler/src/scala/quoted/runtime/impl/Matcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/Matcher.scala @@ -20,20 +20,20 @@ import dotty.tools.dotc.core.StdNames.nme * * Operations: * - `s =?= p` checks if a scrutinee `s` matches the pattern `p` while accumulating extracted parts of the code. - * - `isColosedUnder(x1, .., xn)('{e})` returns true if and only if all the references in `e` to names defined in the patttern are contained in the set `{x1, ... xn}`. + * - `isClosedUnder(x1, .., xn)('{e})` returns true if and only if all the references in `e` to names defined in the pattern are contained in the set `{x1, ... xn}`. * - `lift(x1, .., xn)('{e})` returns `(y1, ..., yn) => [xi = $yi]'{e}` where `yi` is an `Expr` of the type of `xi`. - * - `withEnv(x1 -> y1, ..., xn -> yn)(matching)` evaluates mathing recording that `xi` is equivalent to `yi`. - * - `matched` denotes that the the match succedded and `matched('{e})` denotes that a matech succeded and extracts `'{e}` + * - `withEnv(x1 -> y1, ..., xn -> yn)(matching)` evaluates matching recording that `xi` is equivalent to `yi`. + * - `matched` denotes that the the match succeeded and `matched('{e})` denotes that a match succeeded and extracts `'{e}` * - `&&&` matches if both sides match. Concatenates the extracted expressions of both sides. * * Note: that not all quoted terms bellow are valid expressions * * ```scala * /* Term hole */ - * '{ e } =?= '{ hole[T] } && typeOf('{e}) <:< T && isColosedUnder()('{e}) ===> matched('{e}) + * '{ e } =?= '{ hole[T] } && typeOf('{e}) <:< T && isClosedUnder()('{e}) ===> matched('{e}) * * /* Higher order term hole */ - * '{ e } =?= '{ hole[(T1, ..., Tn) => T](x1, ..., xn) } && isColosedUnder(x1, ... xn)('{e}) ===> matched(lift(x1, ..., xn)('{e})) + * '{ e } =?= '{ hole[(T1, ..., Tn) => T](x1, ..., xn) } && isClosedUnder(x1, ... xn)('{e}) ===> matched(lift(x1, ..., xn)('{e})) * * /* Match literal */ * '{ lit } =?= '{ lit } ===> matched @@ -216,42 +216,6 @@ object Matcher { val res = Lambda(Symbol.spliceOwner, MethodType(names)(_ => argTypes, _ => resType), (meth, x) => bodyFn(x).changeOwner(meth)) matched(res.asExpr) - // - // Match two equivalent trees - // - - /* Match literal */ - case (Literal(constant1), Literal(constant2)) if constant1 == constant2 => - matched - - /* Match type ascription (a) */ - case (Typed(expr1, _), pattern) => - expr1 =?= pattern - - /* Match type ascription (b) */ - case (scrutinee, Typed(expr2, _)) => - scrutinee =?= expr2 - - /* Match selection */ - case (ref: Ref, Select(qual2, _)) if symbolMatch(scrutinee.asInstanceOf, pattern.asInstanceOf) => - ref match - case Select(qual1, _) => qual1 =?= qual2 - case ref: Ident => - ref.tpe match - case TermRef(qual: TermRef, _) => Ref.term(qual) =?= qual2 - case _ => matched - - /* Match reference */ - case (_: Ref, _: Ident) if symbolMatch(scrutinee.asInstanceOf, pattern.asInstanceOf) => - matched - - /* Match application */ - case (Apply(fn1, args1), Apply(fn2, args2)) => - fn1 =?= fn2 &&& args1 =?= args2 - - /* Match type application */ - case (TypeApply(fn1, args1), TypeApply(fn2, args2)) => - fn1 =?= fn2 &&& args1 =?= args2 // No Match case _ => @@ -263,6 +227,7 @@ object Matcher { def otherCases(scrutinee: tpd.Tree, pattern: tpd.Tree)(using Env): Matching = import tpd.* // TODO remove import dotc.core.Flags.* // TODO remove + import dotc.core.Types.* // TODO remove /** Check that both are `val` or both are `lazy val` or both are `var` **/ def checkValFlags(): Boolean = { @@ -281,6 +246,43 @@ object Matcher { (scrutinee, pattern) match + // + // Match two equivalent trees + // + + /* Match literal */ + case (Literal(constant1), Literal(constant2)) if constant1 == constant2 => + matched + + /* Match type ascription (a) */ + case (Typed(expr1, _), pattern) => + expr1 =?= pattern + + /* Match type ascription (b) */ + case (scrutinee, Typed(expr2, _)) => + scrutinee =?= expr2 + + /* Match selection */ + case (ref: RefTree, Select(qual2, _)) if symbolMatch(scrutinee, pattern) => + ref match + case Select(qual1, _) => qual1 =?= qual2 + case ref: Ident => + ref.tpe match + case TermRef(qual: TermRef, _) => tpd.ref(qual) =?= qual2 + case _ => matched + + /* Match reference */ + case (_: RefTree, _: Ident) if symbolMatch(scrutinee, pattern) => + matched + + /* Match application */ + case (Apply(fn1, args1), Apply(fn2, args2)) => + fn1 =?= fn2 &&& args1 =?= args2 + + /* Match type application */ + case (TypeApply(fn1, args1), TypeApply(fn2, args2)) => + fn1 =?= fn2 &&& args1 =?= args2 + /* Match block */ case (Block(stat1 :: stats1, expr1), Block(stat2 :: stats2, expr2)) => val newEnv = (stat1, stat2) match { From 44295503a909535b85574c75958be487cb21a468 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 16:10:47 +0200 Subject: [PATCH 09/21] =?UTF-8?q?Port=20higherOrderHole=20logic=20in=20=3D?= =?UTF-8?q?=3F=3D?= --- .../scala/quoted/runtime/impl/Matcher.scala | 59 +++++++++++-------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/Matcher.scala b/compiler/src/scala/quoted/runtime/impl/Matcher.scala index a1c44f0ec419..88c8c410c7d8 100644 --- a/compiler/src/scala/quoted/runtime/impl/Matcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/Matcher.scala @@ -7,6 +7,7 @@ import scala.annotation.{Annotation, compileTimeOnly} import dotty.tools.dotc import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Names.* import dotty.tools.dotc.core.StdNames.nme /** Matches a quoted tree against a quoted pattern tree. @@ -193,29 +194,6 @@ object Matcher { scrutinee.tpe <:< tpt.tpe => matched(scrutinee.asExpr) - /* Higher order term hole */ - // Matches an open term and wraps it into a lambda that provides the free variables - case (scrutinee, pattern @ Apply(TypeApply(Ident("higherOrderHole"), List(Inferred())), Repeated(args, _) :: Nil)) - if pattern.symbol.eq(dotc.core.Symbols.defn.QuotedRuntimePatterns_higherOrderHole) => - - def bodyFn(lambdaArgs: List[Tree]): Tree = { - val argsMap = args.map(_.symbol).zip(lambdaArgs.asInstanceOf[List[Term]]).toMap - new TreeMap { - override def transformTerm(tree: Term)(owner: Symbol): Term = - tree match - case tree: Ident => summon[Env].get(tree.symbol.asInstanceOf).asInstanceOf[Option[Symbol]].flatMap(argsMap.get).getOrElse(tree) - case tree => super.transformTerm(tree)(owner) - }.transformTree(scrutinee)(Symbol.spliceOwner) - } - val names = args.map { - case Block(List(DefDef("$anonfun", _, _, Some(Apply(Ident(name), _)))), _) => name - case arg => arg.symbol.name - } - val argTypes = args.map(x => x.tpe.widenTermRefByName) - val resType = pattern.tpe - val res = Lambda(Symbol.spliceOwner, MethodType(names)(_ => argTypes, _ => resType), (meth, x) => bodyFn(x).changeOwner(meth)) - matched(res.asExpr) - // No Match case _ => @@ -228,6 +206,7 @@ object Matcher { import tpd.* // TODO remove import dotc.core.Flags.* // TODO remove import dotc.core.Types.* // TODO remove + import dotc.core.Symbols.* // TODO remove /** Check that both are `val` or both are `lazy val` or both are `var` **/ def checkValFlags(): Boolean = { @@ -244,8 +223,42 @@ object Matcher { case _ => None end TypeTreeTypeTest + object Lambda: + def apply(owner: Symbol, tpe: MethodType, rhsFn: (Symbol, List[Tree]) => Tree): Block = + val meth = dotc.core.Symbols.newSymbol(owner, nme.ANON_FUN, Synthetic | Method, tpe) + tpd.Closure(meth, tss => rhsFn(meth, tss.head)) + end Lambda + (scrutinee, pattern) match + /* Higher order term hole */ + // Matches an open term and wraps it into a lambda that provides the free variables + case (scrutinee, pattern @ Apply(TypeApply(Ident(_), List(TypeTree())), SeqLiteral(args, _) :: Nil)) + if pattern.symbol.eq(dotc.core.Symbols.defn.QuotedRuntimePatterns_higherOrderHole) => + + def bodyFn(lambdaArgs: List[Tree]): Tree = { + val argsMap = args.map(_.symbol).zip(lambdaArgs.asInstanceOf[List[Tree]]).toMap + 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) + } + 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 resType = pattern.tpe + val res = + Lambda( + ctx.owner, + MethodType(names)( + _ => argTypes, _ => resType), + (meth, x) => tpd.TreeOps(bodyFn(x)).changeNonLocalOwners(meth.asInstanceOf)) + matched(qctx.reflect.TreeMethods.asExpr(res.asInstanceOf[qctx.reflect.Tree])) + // // Match two equivalent trees // From aeaa9b7eab45f470e7061548a0d5e7c25548d721 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 16:14:49 +0200 Subject: [PATCH 10/21] =?UTF-8?q?Port=20pattern=20hole=20logic=20in=20=3D?= =?UTF-8?q?=3F=3D?= --- .../scala/quoted/runtime/impl/Matcher.scala | 43 ++++++++----------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/Matcher.scala b/compiler/src/scala/quoted/runtime/impl/Matcher.scala index 88c8c410c7d8..0e2849d8852e 100644 --- a/compiler/src/scala/quoted/runtime/impl/Matcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/Matcher.scala @@ -177,28 +177,8 @@ object Matcher { val scrutinee = normalize(scrutinee0) val pattern = normalize(pattern0) - (scrutinee, pattern) match { - - /* Term hole */ - // Match a scala.internal.Quoted.patternHole typed as a repeated argument and return the scrutinee tree - case (scrutinee @ Typed(s, tpt1), Typed(TypeApply(patternHole, tpt :: Nil), tpt2)) - if patternHole.symbol.eq(dotc.core.Symbols.defn.QuotedRuntimePatterns_patternHole) && - s.tpe <:< tpt.tpe && - tpt2.tpe.derivesFrom(defn.RepeatedParamClass) => - matched(scrutinee.asExpr) - - /* Term hole */ - // Match a scala.internal.Quoted.patternHole and return the scrutinee tree - case (ClosedPatternTerm(scrutinee), TypeApply(patternHole, tpt :: Nil)) - if patternHole.symbol.eq(dotc.core.Symbols.defn.QuotedRuntimePatterns_patternHole) && - scrutinee.tpe <:< tpt.tpe => - matched(scrutinee.asExpr) - - - // No Match - case _ => - otherCases(scrutinee.asInstanceOf, pattern.asInstanceOf) - } + otherCases(scrutinee.asInstanceOf, pattern.asInstanceOf) + } end extension @@ -231,6 +211,21 @@ object Matcher { (scrutinee, pattern) match + /* Term hole */ + // Match a scala.internal.Quoted.patternHole typed as a repeated argument and return the scrutinee tree + case (scrutinee @ Typed(s, tpt1), Typed(TypeApply(patternHole, tpt :: Nil), tpt2)) + if patternHole.symbol.eq(dotc.core.Symbols.defn.QuotedRuntimePatterns_patternHole) && + s.tpe <:< tpt.tpe && + tpt2.tpe.derivesFrom(defn.RepeatedParamClass) => + matched(qctx.reflect.TreeMethods.asExpr(scrutinee.asInstanceOf[qctx.reflect.Tree])) + + /* Term hole */ + // Match a scala.internal.Quoted.patternHole and return the scrutinee tree + case (ClosedPatternTerm(scrutinee), TypeApply(patternHole, tpt :: Nil)) + if patternHole.symbol.eq(dotc.core.Symbols.defn.QuotedRuntimePatterns_patternHole) && + scrutinee.tpe <:< tpt.tpe => + matched(qctx.reflect.TreeMethods.asExpr(scrutinee.asInstanceOf[qctx.reflect.Tree])) + /* Higher order term hole */ // Matches an open term and wraps it into a lambda that provides the free variables case (scrutinee, pattern @ Apply(TypeApply(Ident(_), List(TypeTree())), SeqLiteral(args, _) :: Nil)) @@ -428,8 +423,8 @@ object Matcher { private object ClosedPatternTerm { /** Matches a term that does not contain free variables defined in the pattern (i.e. not defined in `Env`) */ - def unapply(term: Term)(using Env): Option[term.type] = - if freePatternVars(term.asInstanceOf).isEmpty then Some(term) else None + def unapply(term: tpd.Tree)(using Env): Option[term.type] = + if freePatternVars(term).isEmpty then Some(term) else None /** Return all free variables of the term defined in the pattern (i.e. defined in `Env`) */ def freePatternVars(term: dotc.ast.tpd.Tree)(using env: Env): Set[dotc.core.Symbols.Symbol] = From ab060b2b335c24bdfcdd80a1b2b0bbe4b752af9c Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 16:21:53 +0200 Subject: [PATCH 11/21] =?UTF-8?q?Port=20rest=20of=20Tree=20=3D=3F=3D?= --- .../scala/quoted/runtime/impl/Matcher.scala | 413 +++++++++--------- 1 file changed, 204 insertions(+), 209 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/Matcher.scala b/compiler/src/scala/quoted/runtime/impl/Matcher.scala index 0e2849d8852e..6a41fe14772b 100644 --- a/compiler/src/scala/quoted/runtime/impl/Matcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/Matcher.scala @@ -144,14 +144,15 @@ object Matcher { private def =?= (patterns: List[Tree])(using Env): Matching = matchLists(scrutinees, patterns)(_ =?= _) - extension (scrutinee: tpd.Tree) - private def =?= (pattern: tpd.Tree)(using Env): Matching = - scrutinee.asInstanceOf[Tree] =?= pattern.asInstanceOf[Tree] + extension (scrutinee: Tree) + private def =?= (pattern: Tree)(using Env): Matching = + scrutinee.asInstanceOf[tpd.Tree] =?= pattern.asInstanceOf[tpd.Tree] extension (scrutinees: List[tpd.Tree]) private def =?= (patterns: List[tpd.Tree])(using Env)(using DummyImplicit): Matching = matchLists(scrutinees, patterns)(_ =?= _) - extension (scrutinee0: Tree) + extension (scrutinee0: tpd.Tree) + /** Check that the trees match and return the contents from the pattern holes. * Return None if the trees do not match otherwise return Some of a tuple containing all the contents in the holes. * @@ -160,7 +161,11 @@ object Matcher { * @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 `None` if it did not match or `Some(tup: Tuple)` if it matched where `tup` contains the contents of the holes. */ - private def =?= (pattern0: Tree)(using Env): Matching = { + private def =?= (pattern0: tpd.Tree)(using Env): Matching = + import tpd.* // TODO remove + import dotc.core.Flags.* // TODO remove + import dotc.core.Types.* // TODO remove + import dotc.core.Symbols.* // TODO remove /* Match block flattening */ // TODO move to cases /** Normalize the tree */ @@ -177,215 +182,205 @@ object Matcher { val scrutinee = normalize(scrutinee0) val pattern = normalize(pattern0) - otherCases(scrutinee.asInstanceOf, pattern.asInstanceOf) + /** Check that both are `val` or both are `lazy val` or both are `var` **/ + def checkValFlags(): Boolean = { + val sFlags = scrutinee.symbol.flags + val pFlags = pattern.symbol.flags + sFlags.is(Lazy) == pFlags.is(Lazy) && sFlags.is(Mutable) == pFlags.is(Mutable) + } - } - end extension + // TODO remove + object TypeTreeTypeTest: + def unapply(x: Tree): Option[Tree & x.type] = x match + case x: (tpd.TypeBoundsTree & x.type) => None + case x: (tpd.Tree & x.type) if x.isType => Some(x) + case _ => None + end TypeTreeTypeTest - def otherCases(scrutinee: tpd.Tree, pattern: tpd.Tree)(using Env): Matching = - import tpd.* // TODO remove - import dotc.core.Flags.* // TODO remove - import dotc.core.Types.* // TODO remove - import dotc.core.Symbols.* // TODO remove - - /** Check that both are `val` or both are `lazy val` or both are `var` **/ - def checkValFlags(): Boolean = { - val sFlags = scrutinee.symbol.flags - val pFlags = pattern.symbol.flags - sFlags.is(Lazy) == pFlags.is(Lazy) && sFlags.is(Mutable) == pFlags.is(Mutable) - } + object Lambda: + def apply(owner: Symbol, tpe: MethodType, rhsFn: (Symbol, List[Tree]) => Tree): Block = + val meth = dotc.core.Symbols.newSymbol(owner, nme.ANON_FUN, Synthetic | Method, tpe) + tpd.Closure(meth, tss => rhsFn(meth, tss.head)) + end Lambda - // TODO remove - object TypeTreeTypeTest: - def unapply(x: Tree): Option[Tree & x.type] = x match - case x: (tpd.TypeBoundsTree & x.type) => None - case x: (tpd.Tree & x.type) if x.isType => Some(x) - case _ => None - end TypeTreeTypeTest - - object Lambda: - def apply(owner: Symbol, tpe: MethodType, rhsFn: (Symbol, List[Tree]) => Tree): Block = - val meth = dotc.core.Symbols.newSymbol(owner, nme.ANON_FUN, Synthetic | Method, tpe) - tpd.Closure(meth, tss => rhsFn(meth, tss.head)) - end Lambda - - (scrutinee, pattern) match - - /* Term hole */ - // Match a scala.internal.Quoted.patternHole typed as a repeated argument and return the scrutinee tree - case (scrutinee @ Typed(s, tpt1), Typed(TypeApply(patternHole, tpt :: Nil), tpt2)) - if patternHole.symbol.eq(dotc.core.Symbols.defn.QuotedRuntimePatterns_patternHole) && - s.tpe <:< tpt.tpe && - tpt2.tpe.derivesFrom(defn.RepeatedParamClass) => - matched(qctx.reflect.TreeMethods.asExpr(scrutinee.asInstanceOf[qctx.reflect.Tree])) - - /* Term hole */ - // Match a scala.internal.Quoted.patternHole and return the scrutinee tree - case (ClosedPatternTerm(scrutinee), TypeApply(patternHole, tpt :: Nil)) - if patternHole.symbol.eq(dotc.core.Symbols.defn.QuotedRuntimePatterns_patternHole) && - scrutinee.tpe <:< tpt.tpe => - matched(qctx.reflect.TreeMethods.asExpr(scrutinee.asInstanceOf[qctx.reflect.Tree])) - - /* Higher order term hole */ - // Matches an open term and wraps it into a lambda that provides the free variables - case (scrutinee, pattern @ Apply(TypeApply(Ident(_), List(TypeTree())), SeqLiteral(args, _) :: Nil)) - if pattern.symbol.eq(dotc.core.Symbols.defn.QuotedRuntimePatterns_higherOrderHole) => - - def bodyFn(lambdaArgs: List[Tree]): Tree = { - val argsMap = args.map(_.symbol).zip(lambdaArgs.asInstanceOf[List[Tree]]).toMap - 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) - } - 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 resType = pattern.tpe - val res = - Lambda( - ctx.owner, - MethodType(names)( - _ => argTypes, _ => resType), - (meth, x) => tpd.TreeOps(bodyFn(x)).changeNonLocalOwners(meth.asInstanceOf)) - matched(qctx.reflect.TreeMethods.asExpr(res.asInstanceOf[qctx.reflect.Tree])) - - // - // Match two equivalent trees - // - - /* Match literal */ - case (Literal(constant1), Literal(constant2)) if constant1 == constant2 => - matched - - /* Match type ascription (a) */ - case (Typed(expr1, _), pattern) => - expr1 =?= pattern - - /* Match type ascription (b) */ - case (scrutinee, Typed(expr2, _)) => - scrutinee =?= expr2 - - /* Match selection */ - case (ref: RefTree, Select(qual2, _)) if symbolMatch(scrutinee, pattern) => - ref match - case Select(qual1, _) => qual1 =?= qual2 - case ref: Ident => - ref.tpe match - case TermRef(qual: TermRef, _) => tpd.ref(qual) =?= qual2 - case _ => matched - - /* Match reference */ - case (_: RefTree, _: Ident) if symbolMatch(scrutinee, pattern) => - matched - - /* Match application */ - case (Apply(fn1, args1), Apply(fn2, args2)) => - fn1 =?= fn2 &&& args1 =?= args2 - - /* Match type application */ - case (TypeApply(fn1, args1), TypeApply(fn2, args2)) => - fn1 =?= fn2 &&& args1 =?= args2 - - /* Match block */ - case (Block(stat1 :: stats1, expr1), Block(stat2 :: stats2, expr2)) => - val newEnv = (stat1, stat2) match { - case (stat1: MemberDef, stat2: MemberDef) => - summon[Env] + (stat1.symbol -> stat2.symbol) - case _ => - summon[Env] - } - withEnv(newEnv) { - stat1 =?= stat2 &&& Block(stats1, expr1) =?= Block(stats2, expr2) - } + (scrutinee, pattern) match - /* Match if */ - case (If(cond1, thenp1, elsep1), If(cond2, thenp2, elsep2)) => - cond1 =?= cond2 &&& thenp1 =?= thenp2 &&& elsep1 =?= elsep2 - - /* Match while */ - case (WhileDo(cond1, body1), WhileDo(cond2, body2)) => - cond1 =?= cond2 &&& body1 =?= body2 - - /* Match assign */ - case (Assign(lhs1, rhs1), Assign(lhs2, rhs2)) => - lhs1 =?= lhs2 &&& rhs1 =?= rhs2 - - /* Match new */ - case (New(tpt1), New(tpt2)) if tpt1.tpe.typeSymbol == tpt2.tpe.typeSymbol => - matched - - /* Match this */ - case (This(_), This(_)) if scrutinee.symbol == pattern.symbol => - matched - - /* Match super */ - case (Super(qual1, mix1), Super(qual2, mix2)) if mix1 == mix2 => - qual1 =?= qual2 - - /* Match varargs */ - case (SeqLiteral(elems1, _), SeqLiteral(elems2, _)) if elems1.size == elems2.size => - elems1 =?= elems2 - - /* Match type */ - // TODO remove this? - case (TypeTreeTypeTest(scrutinee), TypeTreeTypeTest(pattern)) if scrutinee.tpe <:< pattern.tpe => - matched - - /* Match val */ - case (scrutinee @ ValDef(_, tpt1, _), pattern @ ValDef(_, tpt2, _)) if checkValFlags() => - def rhsEnv = summon[Env] + (scrutinee.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> pattern.symbol.asInstanceOf[dotc.core.Symbols.Symbol]) - tpt1 =?= tpt2 &&& withEnv(rhsEnv)(scrutinee.rhs =?= pattern.rhs) - - /* Match def */ - case (scrutinee @ DefDef(_, paramss1, tpt1, _), pattern @ DefDef(_, paramss2, tpt2, _)) => - def rhsEnv: Env = - val paramSyms: List[(dotc.core.Symbols.Symbol, dotc.core.Symbols.Symbol)] = - for - (clause1, clause2) <- paramss1.zip(paramss2) - (param1, param2) <- clause1.zip(clause2) - yield - param1.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> param2.symbol.asInstanceOf[dotc.core.Symbols.Symbol] - val oldEnv: Env = summon[Env] - val newEnv: List[(dotc.core.Symbols.Symbol, dotc.core.Symbols.Symbol)] = (scrutinee.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> pattern.symbol.asInstanceOf[dotc.core.Symbols.Symbol]) :: paramSyms - oldEnv ++ newEnv - - matchLists(paramss1, paramss2)(_ =?= _) - &&& tpt1 =?= tpt2 - &&& withEnv(rhsEnv)(scrutinee.rhs =?= pattern.rhs) - - case (Closure(_, _, tpt1), Closure(_, _, tpt2)) => - // TODO match tpt1 with tpt2? - matched - - case (NamedArg(name1, arg1), NamedArg(name2, arg2)) if name1 == name2 => - arg1 =?= arg2 - - case (EmptyTree, EmptyTree) => - matched - - // No Match - case _ => - if (debug) - println( - s""">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - |Scrutinee - | ${scrutinee.show} - |did not match pattern - | ${pattern.show} - | - |with environment: ${summon[Env]} - | - |Scrutinee: ${Printer.TreeStructure.show(scrutinee.asInstanceOf)} - |Pattern: ${Printer.TreeStructure.show(pattern.asInstanceOf)} - | - |""".stripMargin) - notMatched + /* Term hole */ + // Match a scala.internal.Quoted.patternHole typed as a repeated argument and return the scrutinee tree + case (scrutinee @ Typed(s, tpt1), Typed(TypeApply(patternHole, tpt :: Nil), tpt2)) + if patternHole.symbol.eq(dotc.core.Symbols.defn.QuotedRuntimePatterns_patternHole) && + s.tpe <:< tpt.tpe && + tpt2.tpe.derivesFrom(defn.RepeatedParamClass) => + matched(qctx.reflect.TreeMethods.asExpr(scrutinee.asInstanceOf[qctx.reflect.Tree])) + + /* Term hole */ + // Match a scala.internal.Quoted.patternHole and return the scrutinee tree + case (ClosedPatternTerm(scrutinee), TypeApply(patternHole, tpt :: Nil)) + if patternHole.symbol.eq(dotc.core.Symbols.defn.QuotedRuntimePatterns_patternHole) && + scrutinee.tpe <:< tpt.tpe => + matched(qctx.reflect.TreeMethods.asExpr(scrutinee.asInstanceOf[qctx.reflect.Tree])) + + /* Higher order term hole */ + // Matches an open term and wraps it into a lambda that provides the free variables + case (scrutinee, pattern @ Apply(TypeApply(Ident(_), List(TypeTree())), SeqLiteral(args, _) :: Nil)) + if pattern.symbol.eq(dotc.core.Symbols.defn.QuotedRuntimePatterns_higherOrderHole) => + + def bodyFn(lambdaArgs: List[Tree]): Tree = { + val argsMap = args.map(_.symbol).zip(lambdaArgs.asInstanceOf[List[Tree]]).toMap + 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) + } + 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 resType = pattern.tpe + val res = + Lambda( + ctx.owner, + MethodType(names)( + _ => argTypes, _ => resType), + (meth, x) => tpd.TreeOps(bodyFn(x)).changeNonLocalOwners(meth.asInstanceOf)) + matched(qctx.reflect.TreeMethods.asExpr(res.asInstanceOf[qctx.reflect.Tree])) + + // + // Match two equivalent trees + // + + /* Match literal */ + case (Literal(constant1), Literal(constant2)) if constant1 == constant2 => + matched + + /* Match type ascription (a) */ + case (Typed(expr1, _), pattern) => + expr1 =?= pattern + + /* Match type ascription (b) */ + case (scrutinee, Typed(expr2, _)) => + scrutinee =?= expr2 + + /* Match selection */ + case (ref: RefTree, Select(qual2, _)) if symbolMatch(scrutinee, pattern) => + ref match + case Select(qual1, _) => qual1 =?= qual2 + case ref: Ident => + ref.tpe match + case TermRef(qual: TermRef, _) => tpd.ref(qual) =?= qual2 + case _ => matched + + /* Match reference */ + case (_: RefTree, _: Ident) if symbolMatch(scrutinee, pattern) => + matched + + /* Match application */ + case (Apply(fn1, args1), Apply(fn2, args2)) => + fn1 =?= fn2 &&& args1 =?= args2 + + /* Match type application */ + case (TypeApply(fn1, args1), TypeApply(fn2, args2)) => + fn1 =?= fn2 &&& args1 =?= args2 + + /* Match block */ + case (Block(stat1 :: stats1, expr1), Block(stat2 :: stats2, expr2)) => + val newEnv = (stat1, stat2) match { + case (stat1: MemberDef, stat2: MemberDef) => + summon[Env] + (stat1.symbol -> stat2.symbol) + case _ => + summon[Env] + } + withEnv(newEnv) { + stat1 =?= stat2 &&& Block(stats1, expr1) =?= Block(stats2, expr2) + } + + /* Match if */ + case (If(cond1, thenp1, elsep1), If(cond2, thenp2, elsep2)) => + cond1 =?= cond2 &&& thenp1 =?= thenp2 &&& elsep1 =?= elsep2 + + /* Match while */ + case (WhileDo(cond1, body1), WhileDo(cond2, body2)) => + cond1 =?= cond2 &&& body1 =?= body2 + + /* Match assign */ + case (Assign(lhs1, rhs1), Assign(lhs2, rhs2)) => + lhs1 =?= lhs2 &&& rhs1 =?= rhs2 + + /* Match new */ + case (New(tpt1), New(tpt2)) if tpt1.tpe.typeSymbol == tpt2.tpe.typeSymbol => + matched + + /* Match this */ + case (This(_), This(_)) if scrutinee.symbol == pattern.symbol => + matched + + /* Match super */ + case (Super(qual1, mix1), Super(qual2, mix2)) if mix1 == mix2 => + qual1 =?= qual2 + + /* Match varargs */ + case (SeqLiteral(elems1, _), SeqLiteral(elems2, _)) if elems1.size == elems2.size => + elems1 =?= elems2 + + /* Match type */ + // TODO remove this? + case (TypeTreeTypeTest(scrutinee), TypeTreeTypeTest(pattern)) if scrutinee.tpe <:< pattern.tpe => + matched + + /* Match val */ + case (scrutinee @ ValDef(_, tpt1, _), pattern @ ValDef(_, tpt2, _)) if checkValFlags() => + def rhsEnv = summon[Env] + (scrutinee.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> pattern.symbol.asInstanceOf[dotc.core.Symbols.Symbol]) + tpt1 =?= tpt2 &&& withEnv(rhsEnv)(scrutinee.rhs =?= pattern.rhs) + + /* Match def */ + case (scrutinee @ DefDef(_, paramss1, tpt1, _), pattern @ DefDef(_, paramss2, tpt2, _)) => + def rhsEnv: Env = + val paramSyms: List[(dotc.core.Symbols.Symbol, dotc.core.Symbols.Symbol)] = + for + (clause1, clause2) <- paramss1.zip(paramss2) + (param1, param2) <- clause1.zip(clause2) + yield + param1.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> param2.symbol.asInstanceOf[dotc.core.Symbols.Symbol] + val oldEnv: Env = summon[Env] + val newEnv: List[(dotc.core.Symbols.Symbol, dotc.core.Symbols.Symbol)] = (scrutinee.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> pattern.symbol.asInstanceOf[dotc.core.Symbols.Symbol]) :: paramSyms + oldEnv ++ newEnv + + matchLists(paramss1, paramss2)(_ =?= _) + &&& tpt1 =?= tpt2 + &&& withEnv(rhsEnv)(scrutinee.rhs =?= pattern.rhs) + + case (Closure(_, _, tpt1), Closure(_, _, tpt2)) => + // TODO match tpt1 with tpt2? + matched + + case (NamedArg(name1, arg1), NamedArg(name2, arg2)) if name1 == name2 => + arg1 =?= arg2 + + case (EmptyTree, EmptyTree) => + matched + + // No Match + case _ => + if (debug) + println( + s""">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + |Scrutinee + | ${scrutinee.show} + |did not match pattern + | ${pattern.show} + | + |with environment: ${summon[Env]} + | + |Scrutinee: ${Printer.TreeStructure.show(scrutinee.asInstanceOf)} + |Pattern: ${Printer.TreeStructure.show(pattern.asInstanceOf)} + | + |""".stripMargin) + notMatched + end extension extension (scrutinee: ParamClause) /** Check that all parameters in the clauses clauses match with =?= and concatenate the results with &&& */ From 78dfd290dc4ddf70837a878f50d3acadc635b66e Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 16:27:59 +0200 Subject: [PATCH 12/21] Remove deadcode --- .../scala/quoted/runtime/impl/Matcher.scala | 31 +++---------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/Matcher.scala b/compiler/src/scala/quoted/runtime/impl/Matcher.scala index 6a41fe14772b..2f068c7faf84 100644 --- a/compiler/src/scala/quoted/runtime/impl/Matcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/Matcher.scala @@ -139,11 +139,6 @@ object Matcher { case _ => notMatched } - extension (scrutinees: List[Tree]) - /** Check that all trees match with =?= and concatenate the results with &&& */ - private def =?= (patterns: List[Tree])(using Env): Matching = - matchLists(scrutinees, patterns)(_ =?= _) - extension (scrutinee: Tree) private def =?= (pattern: Tree)(using Env): Matching = scrutinee.asInstanceOf[tpd.Tree] =?= pattern.asInstanceOf[tpd.Tree] @@ -156,7 +151,7 @@ object Matcher { /** Check that the trees match and return the contents from the pattern holes. * Return None if the trees do not match otherwise return Some of a tuple containing all the contents in the holes. * - * @param scrutinee The tree beeing matched + * @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 `None` if it did not match or `Some(tup: Tuple)` if it matched where `tup` contains the contents of the holes. @@ -382,21 +377,13 @@ object Matcher { end extension - extension (scrutinee: ParamClause) - /** Check that all parameters in the clauses clauses match with =?= and concatenate the results with &&& */ - private def =?= (pattern: ParamClause)(using Env)(using DummyImplicit): Matching = - (scrutinee, pattern) match - case (TermParamClause(params1), TermParamClause(params2)) => matchLists(params1, params2)(_ =?= _) - case (TypeParamClause(params1), TypeParamClause(params2)) => matchLists(params1, params2)(_ =?= _) - case _ => notMatched - /** Does the scrutenne 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 */ - private def symbolMatch(scrutineeTree: dotc.ast.tpd.Tree, patternTree: dotc.ast.tpd.Tree)(using Env): Boolean = - import dotc.ast.tpd.* // TODO remove + private def symbolMatch(scrutineeTree: tpd.Tree, patternTree: tpd.Tree)(using Env): Boolean = + import tpd.* // TODO remove val scrutinee = scrutineeTree.symbol def overridingSymbol(ofclazz: dotc.core.Symbols.Symbol): dotc.core.Symbols.Symbol = @@ -413,7 +400,7 @@ object Matcher { devirtualizedScrutinee == pattern - || summon[Env].get(devirtualizedScrutinee.asInstanceOf).contains(pattern) + || summon[Env].get(devirtualizedScrutinee).contains(pattern) || devirtualizedScrutinee.allOverriddenSymbols.contains(pattern) private object ClosedPatternTerm { @@ -428,20 +415,12 @@ object Matcher { val accumulator = new TreeAccumulator[Set[Symbol]] { def apply(x: Set[Symbol], tree: Tree)(using Context): Set[Symbol] = tree match - case tree: Ident if env.contains(tree.symbol.asInstanceOf) => foldOver(x + tree.symbol, tree) + case tree: Ident if env.contains(tree.symbol) => foldOver(x + tree.symbol, tree) case _ => foldOver(x, tree) } accumulator.apply(Set.empty, term) } - private def treeOptMatches(scrutinee: Option[Tree], pattern: Option[Tree])(using Env)(using DummyImplicit): Matching = { - (scrutinee, pattern) match { - case (Some(x), Some(y)) => x =?= y - case (None, None) => matched - case _ => notMatched - } - } - } /** Result of matching a part of an expression */ From ba3e667f9a865dc7696f94a59d8a997b7f3dd756 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 16:40:56 +0200 Subject: [PATCH 13/21] Clenup QuoteMatcher intterface --- .../scala/quoted/runtime/impl/Matcher.scala | 22 ++++++++----------- .../quoted/runtime/impl/QuotesImpl.scala | 6 ++--- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/Matcher.scala b/compiler/src/scala/quoted/runtime/impl/Matcher.scala index 2f068c7faf84..94be04dd134e 100644 --- a/compiler/src/scala/quoted/runtime/impl/Matcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/Matcher.scala @@ -103,14 +103,13 @@ import dotty.tools.dotc.core.StdNames.nme object Matcher { import tpd.* - class QuoteMatcher[QCtx <: Quotes & Singleton](val qctx: QCtx)(using Context) { + class QuoteMatcher(val quotes: Quotes)(using Context) { // TODO improve performance - // TODO use flag from qctx.reflect. Maybe -debug or add -debug-macros + // TODO use flag from Context. Maybe -debug or add -debug-macros private inline val debug = false - import qctx.reflect._ import Matching._ /** A map relating equivalent symbols from the scrutinee and the pattern @@ -124,11 +123,11 @@ object Matcher { inline private def withEnv[T](env: Env)(inline body: Env ?=> T): T = body(using env) - def termMatch(scrutineeTerm: Term, patternTerm: Term): Option[Tuple] = + def termMatch(scrutineeTerm: tpd.Tree, patternTerm: tpd.Tree): Option[Tuple] = given Env = Map.empty scrutineeTerm =?= patternTerm - def typeTreeMatch(scrutineeTypeTree: TypeTree, patternTypeTree: TypeTree): Option[Tuple] = + def typeTreeMatch(scrutineeTypeTree: tpd.Tree, patternTypeTree: tpd.Tree): Option[Tuple] = given Env = Map.empty scrutineeTypeTree =?= patternTypeTree @@ -139,9 +138,6 @@ object Matcher { case _ => notMatched } - extension (scrutinee: Tree) - private def =?= (pattern: Tree)(using Env): Matching = - scrutinee.asInstanceOf[tpd.Tree] =?= pattern.asInstanceOf[tpd.Tree] extension (scrutinees: List[tpd.Tree]) private def =?= (patterns: List[tpd.Tree])(using Env)(using DummyImplicit): Matching = matchLists(scrutinees, patterns)(_ =?= _) @@ -206,14 +202,14 @@ object Matcher { if patternHole.symbol.eq(dotc.core.Symbols.defn.QuotedRuntimePatterns_patternHole) && s.tpe <:< tpt.tpe && tpt2.tpe.derivesFrom(defn.RepeatedParamClass) => - matched(qctx.reflect.TreeMethods.asExpr(scrutinee.asInstanceOf[qctx.reflect.Tree])) + matched(quotes.reflect.TreeMethods.asExpr(scrutinee.asInstanceOf[quotes.reflect.Tree])) /* Term hole */ // Match a scala.internal.Quoted.patternHole and return the scrutinee tree case (ClosedPatternTerm(scrutinee), TypeApply(patternHole, tpt :: Nil)) if patternHole.symbol.eq(dotc.core.Symbols.defn.QuotedRuntimePatterns_patternHole) && scrutinee.tpe <:< tpt.tpe => - matched(qctx.reflect.TreeMethods.asExpr(scrutinee.asInstanceOf[qctx.reflect.Tree])) + matched(quotes.reflect.TreeMethods.asExpr(scrutinee.asInstanceOf[quotes.reflect.Tree])) /* Higher order term hole */ // Matches an open term and wraps it into a lambda that provides the free variables @@ -241,7 +237,7 @@ object Matcher { MethodType(names)( _ => argTypes, _ => resType), (meth, x) => tpd.TreeOps(bodyFn(x)).changeNonLocalOwners(meth.asInstanceOf)) - matched(qctx.reflect.TreeMethods.asExpr(res.asInstanceOf[qctx.reflect.Tree])) + matched(quotes.reflect.TreeMethods.asExpr(res.asInstanceOf[quotes.reflect.Tree])) // // Match two equivalent trees @@ -369,8 +365,8 @@ object Matcher { | |with environment: ${summon[Env]} | - |Scrutinee: ${Printer.TreeStructure.show(scrutinee.asInstanceOf)} - |Pattern: ${Printer.TreeStructure.show(pattern.asInstanceOf)} + |Scrutinee: ${quotes.reflect.Printer.TreeStructure.show(scrutinee.asInstanceOf)} + |Pattern: ${quotes.reflect.Printer.TreeStructure.show(pattern.asInstanceOf)} | |""".stripMargin) notMatched diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index a3b53b08e16b..3049430419f1 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2937,11 +2937,11 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler val qctx1 = QuotesImpl()(using ctx1) - val matcher = new Matcher.QuoteMatcher[qctx1.type](qctx1)(using ctx1) + val matcher = new Matcher.QuoteMatcher(qctx1)(using ctx1) val matchings = - if pat1.isType then matcher.termMatch(scrutinee.asInstanceOf[matcher.qctx.reflect.Term], pat1.asInstanceOf[matcher.qctx.reflect.Term]) - else matcher.termMatch(scrutinee.asInstanceOf[matcher.qctx.reflect.Term], pat1.asInstanceOf[matcher.qctx.reflect.Term]) + if pat1.isType then matcher.termMatch(scrutinee, pat1) + else matcher.termMatch(scrutinee, pat1) // val matchings = matcher.termMatch(scrutinee, pattern) if typeHoles.isEmpty then matchings From 9b0c172ec5e7a9216f804e715202a31ddf0cb69d Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 16:42:45 +0200 Subject: [PATCH 14/21] Move QuoteMatcher to QuoteMatcher.scala --- .../quoted/runtime/impl/{Matcher.scala => QuoteMatcher.scala} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename compiler/src/scala/quoted/runtime/impl/{Matcher.scala => QuoteMatcher.scala} (100%) diff --git a/compiler/src/scala/quoted/runtime/impl/Matcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala similarity index 100% rename from compiler/src/scala/quoted/runtime/impl/Matcher.scala rename to compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala From 9219ec203a46d14170bee01e4715347bf90281ea Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 16:48:31 +0200 Subject: [PATCH 15/21] Cleanup imports --- .../quoted/runtime/impl/QuoteMatcher.scala | 62 +++++++++---------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index 94be04dd134e..98fedf943d13 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -6,9 +6,12 @@ import scala.annotation.{Annotation, compileTimeOnly} import dotty.tools.dotc import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.core.Flags.* import dotty.tools.dotc.core.Names.* +import dotty.tools.dotc.core.Types.* import dotty.tools.dotc.core.StdNames.nme +import dotty.tools.dotc.core.Symbols.* /** Matches a quoted tree against a quoted pattern tree. * A quoted pattern tree may have type and term holes in addition to normal terms. @@ -119,15 +122,15 @@ object Matcher { * ``` * when matching `a * a` with `x * x` the environment will contain `Map(a -> x)`. */ - private type Env = Map[dotc.core.Symbols.Symbol, dotc.core.Symbols.Symbol] + private type Env = Map[Symbol, Symbol] inline private def withEnv[T](env: Env)(inline body: Env ?=> T): T = body(using env) - def termMatch(scrutineeTerm: tpd.Tree, patternTerm: tpd.Tree): Option[Tuple] = + def termMatch(scrutineeTerm: Tree, patternTerm: Tree): Option[Tuple] = given Env = Map.empty scrutineeTerm =?= patternTerm - def typeTreeMatch(scrutineeTypeTree: tpd.Tree, patternTypeTree: tpd.Tree): Option[Tuple] = + def typeTreeMatch(scrutineeTypeTree: Tree, patternTypeTree: Tree): Option[Tuple] = given Env = Map.empty scrutineeTypeTree =?= patternTypeTree @@ -138,11 +141,11 @@ object Matcher { case _ => notMatched } - extension (scrutinees: List[tpd.Tree]) - private def =?= (patterns: List[tpd.Tree])(using Env)(using DummyImplicit): Matching = + extension (scrutinees: List[Tree]) + private def =?= (patterns: List[Tree])(using Env)(using DummyImplicit): Matching = matchLists(scrutinees, patterns)(_ =?= _) - extension (scrutinee0: tpd.Tree) + extension (scrutinee0: Tree) /** Check that the trees match and return the contents from the pattern holes. * Return None if the trees do not match otherwise return Some of a tuple containing all the contents in the holes. @@ -152,11 +155,7 @@ object Matcher { * @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 `None` if it did not match or `Some(tup: Tuple)` if it matched where `tup` contains the contents of the holes. */ - private def =?= (pattern0: tpd.Tree)(using Env): Matching = - import tpd.* // TODO remove - import dotc.core.Flags.* // TODO remove - import dotc.core.Types.* // TODO remove - import dotc.core.Symbols.* // TODO remove + private def =?= (pattern0: Tree)(using Env): Matching = /* Match block flattening */ // TODO move to cases /** Normalize the tree */ @@ -183,15 +182,15 @@ object Matcher { // TODO remove object TypeTreeTypeTest: def unapply(x: Tree): Option[Tree & x.type] = x match - case x: (tpd.TypeBoundsTree & x.type) => None - case x: (tpd.Tree & x.type) if x.isType => Some(x) + case x: (TypeBoundsTree & x.type) => None + case x: (Tree & x.type) if x.isType => Some(x) case _ => None end TypeTreeTypeTest object Lambda: def apply(owner: Symbol, tpe: MethodType, rhsFn: (Symbol, List[Tree]) => Tree): Block = - val meth = dotc.core.Symbols.newSymbol(owner, nme.ANON_FUN, Synthetic | Method, tpe) - tpd.Closure(meth, tss => rhsFn(meth, tss.head)) + val meth = newSymbol(owner, nme.ANON_FUN, Synthetic | Method, tpe) + Closure(meth, tss => rhsFn(meth, tss.head)) end Lambda (scrutinee, pattern) match @@ -199,7 +198,7 @@ object Matcher { /* Term hole */ // Match a scala.internal.Quoted.patternHole typed as a repeated argument and return the scrutinee tree case (scrutinee @ Typed(s, tpt1), Typed(TypeApply(patternHole, tpt :: Nil), tpt2)) - if patternHole.symbol.eq(dotc.core.Symbols.defn.QuotedRuntimePatterns_patternHole) && + if patternHole.symbol.eq(defn.QuotedRuntimePatterns_patternHole) && s.tpe <:< tpt.tpe && tpt2.tpe.derivesFrom(defn.RepeatedParamClass) => matched(quotes.reflect.TreeMethods.asExpr(scrutinee.asInstanceOf[quotes.reflect.Tree])) @@ -207,14 +206,14 @@ object Matcher { /* Term hole */ // Match a scala.internal.Quoted.patternHole and return the scrutinee tree case (ClosedPatternTerm(scrutinee), TypeApply(patternHole, tpt :: Nil)) - if patternHole.symbol.eq(dotc.core.Symbols.defn.QuotedRuntimePatterns_patternHole) && + if patternHole.symbol.eq(defn.QuotedRuntimePatterns_patternHole) && scrutinee.tpe <:< tpt.tpe => matched(quotes.reflect.TreeMethods.asExpr(scrutinee.asInstanceOf[quotes.reflect.Tree])) /* Higher order term hole */ // Matches an open term and wraps it into a lambda that provides the free variables case (scrutinee, pattern @ Apply(TypeApply(Ident(_), List(TypeTree())), SeqLiteral(args, _) :: Nil)) - if pattern.symbol.eq(dotc.core.Symbols.defn.QuotedRuntimePatterns_higherOrderHole) => + if pattern.symbol.eq(defn.QuotedRuntimePatterns_higherOrderHole) => def bodyFn(lambdaArgs: List[Tree]): Tree = { val argsMap = args.map(_.symbol).zip(lambdaArgs.asInstanceOf[List[Tree]]).toMap @@ -236,7 +235,7 @@ object Matcher { ctx.owner, MethodType(names)( _ => argTypes, _ => resType), - (meth, x) => tpd.TreeOps(bodyFn(x)).changeNonLocalOwners(meth.asInstanceOf)) + (meth, x) => TreeOps(bodyFn(x)).changeNonLocalOwners(meth.asInstanceOf)) matched(quotes.reflect.TreeMethods.asExpr(res.asInstanceOf[quotes.reflect.Tree])) // @@ -323,20 +322,20 @@ object Matcher { /* Match val */ case (scrutinee @ ValDef(_, tpt1, _), pattern @ ValDef(_, tpt2, _)) if checkValFlags() => - def rhsEnv = summon[Env] + (scrutinee.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> pattern.symbol.asInstanceOf[dotc.core.Symbols.Symbol]) + def rhsEnv = summon[Env] + (scrutinee.symbol.asInstanceOf[Symbol] -> pattern.symbol.asInstanceOf[Symbol]) tpt1 =?= tpt2 &&& withEnv(rhsEnv)(scrutinee.rhs =?= pattern.rhs) /* Match def */ case (scrutinee @ DefDef(_, paramss1, tpt1, _), pattern @ DefDef(_, paramss2, tpt2, _)) => def rhsEnv: Env = - val paramSyms: List[(dotc.core.Symbols.Symbol, dotc.core.Symbols.Symbol)] = + val paramSyms: List[(Symbol, Symbol)] = for (clause1, clause2) <- paramss1.zip(paramss2) (param1, param2) <- clause1.zip(clause2) yield - param1.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> param2.symbol.asInstanceOf[dotc.core.Symbols.Symbol] + param1.symbol.asInstanceOf[Symbol] -> param2.symbol.asInstanceOf[Symbol] val oldEnv: Env = summon[Env] - val newEnv: List[(dotc.core.Symbols.Symbol, dotc.core.Symbols.Symbol)] = (scrutinee.symbol.asInstanceOf[dotc.core.Symbols.Symbol] -> pattern.symbol.asInstanceOf[dotc.core.Symbols.Symbol]) :: paramSyms + val newEnv: List[(Symbol, Symbol)] = (scrutinee.symbol.asInstanceOf[Symbol] -> pattern.symbol.asInstanceOf[Symbol]) :: paramSyms oldEnv ++ newEnv matchLists(paramss1, paramss2)(_ =?= _) @@ -373,18 +372,17 @@ object Matcher { end extension - /** Does the scrutenne symbol match the pattern symbol? It matches if: + /** 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 */ - private def symbolMatch(scrutineeTree: tpd.Tree, patternTree: tpd.Tree)(using Env): Boolean = - import tpd.* // TODO remove + private def symbolMatch(scrutineeTree: Tree, patternTree: Tree)(using Env): Boolean = val scrutinee = scrutineeTree.symbol - def overridingSymbol(ofclazz: dotc.core.Symbols.Symbol): dotc.core.Symbols.Symbol = + def overridingSymbol(ofclazz: Symbol): Symbol = if ofclazz.isClass then scrutinee.denot.overridingSymbol(ofclazz.asClass) - else dotc.core.Symbols.NoSymbol + else NoSymbol val devirtualizedScrutinee = scrutineeTree match case Select(qual, _) => @@ -401,13 +399,11 @@ object Matcher { private object ClosedPatternTerm { /** Matches a term that does not contain free variables defined in the pattern (i.e. not defined in `Env`) */ - def unapply(term: tpd.Tree)(using Env): Option[term.type] = + def unapply(term: Tree)(using Env): Option[term.type] = if freePatternVars(term).isEmpty then Some(term) else None /** Return all free variables of the term defined in the pattern (i.e. defined in `Env`) */ - def freePatternVars(term: dotc.ast.tpd.Tree)(using env: Env): Set[dotc.core.Symbols.Symbol] = - import dotc.ast.tpd.* // TODO remove - import dotc.core.Symbols.* // TODO remove + def freePatternVars(term: Tree)(using env: Env): Set[Symbol] = val accumulator = new TreeAccumulator[Set[Symbol]] { def apply(x: Set[Symbol], tree: Tree)(using Context): Set[Symbol] = tree match From 2362c8f38b2b37a862d9e68932a514a68034c7c9 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 16:56:55 +0200 Subject: [PATCH 16/21] Improve Matching.matched --- .../scala/quoted/runtime/impl/QuoteMatcher.scala | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index 98fedf943d13..a1883da362f8 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -201,14 +201,14 @@ object Matcher { if patternHole.symbol.eq(defn.QuotedRuntimePatterns_patternHole) && s.tpe <:< tpt.tpe && tpt2.tpe.derivesFrom(defn.RepeatedParamClass) => - matched(quotes.reflect.TreeMethods.asExpr(scrutinee.asInstanceOf[quotes.reflect.Tree])) + matched(scrutinee) /* Term hole */ // Match a scala.internal.Quoted.patternHole and return the scrutinee tree case (ClosedPatternTerm(scrutinee), TypeApply(patternHole, tpt :: Nil)) if patternHole.symbol.eq(defn.QuotedRuntimePatterns_patternHole) && scrutinee.tpe <:< tpt.tpe => - matched(quotes.reflect.TreeMethods.asExpr(scrutinee.asInstanceOf[quotes.reflect.Tree])) + matched(scrutinee) /* Higher order term hole */ // Matches an open term and wraps it into a lambda that provides the free variables @@ -235,8 +235,8 @@ object Matcher { ctx.owner, MethodType(names)( _ => argTypes, _ => resType), - (meth, x) => TreeOps(bodyFn(x)).changeNonLocalOwners(meth.asInstanceOf)) - matched(quotes.reflect.TreeMethods.asExpr(res.asInstanceOf[quotes.reflect.Tree])) + (meth, x) => TreeOps(bodyFn(x)).changeNonLocalOwners(meth)) + matched(res) // // Match two equivalent trees @@ -421,8 +421,11 @@ object Matcher { private object Matching { def notMatched: Matching = None + val matched: Matching = Some(Tuple()) - def matched(x: Any): Matching = Some(Tuple1(x)) + + def matched(tree: Tree)(using Context): Matching = + Some(Tuple1(new ExprImpl(tree, SpliceScope.getCurrent))) extension (self: Matching) def asOptionOfTuple: Option[Tuple] = self From 60066a590037febd4cba16a2a2fe8d8b96a382a4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 16:58:14 +0200 Subject: [PATCH 17/21] Remove asInstanceOf --- compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index a1883da362f8..68eebef88bcc 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -216,7 +216,7 @@ object Matcher { if pattern.symbol.eq(defn.QuotedRuntimePatterns_higherOrderHole) => def bodyFn(lambdaArgs: List[Tree]): Tree = { - val argsMap = args.map(_.symbol).zip(lambdaArgs.asInstanceOf[List[Tree]]).toMap + val argsMap = args.map(_.symbol).zip(lambdaArgs).toMap new TreeMap { override def transform(tree: Tree)(using Context): Tree = tree match @@ -322,7 +322,7 @@ object Matcher { /* Match val */ case (scrutinee @ ValDef(_, tpt1, _), pattern @ ValDef(_, tpt2, _)) if checkValFlags() => - def rhsEnv = summon[Env] + (scrutinee.symbol.asInstanceOf[Symbol] -> pattern.symbol.asInstanceOf[Symbol]) + def rhsEnv = summon[Env] + (scrutinee.symbol -> pattern.symbol) tpt1 =?= tpt2 &&& withEnv(rhsEnv)(scrutinee.rhs =?= pattern.rhs) /* Match def */ @@ -333,9 +333,9 @@ object Matcher { (clause1, clause2) <- paramss1.zip(paramss2) (param1, param2) <- clause1.zip(clause2) yield - param1.symbol.asInstanceOf[Symbol] -> param2.symbol.asInstanceOf[Symbol] + param1.symbol -> param2.symbol val oldEnv: Env = summon[Env] - val newEnv: List[(Symbol, Symbol)] = (scrutinee.symbol.asInstanceOf[Symbol] -> pattern.symbol.asInstanceOf[Symbol]) :: paramSyms + val newEnv: List[(Symbol, Symbol)] = (scrutinee.symbol -> pattern.symbol) :: paramSyms oldEnv ++ newEnv matchLists(paramss1, paramss2)(_ =?= _) From 5b0935cfbb70d76f5c404eaa10d57632370b30c3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 17:06:47 +0200 Subject: [PATCH 18/21] Remove extra QuoteMatcher --- compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala | 3 ++- compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index 68eebef88bcc..eac72578ec9a 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -106,7 +106,7 @@ import dotty.tools.dotc.core.Symbols.* object Matcher { import tpd.* - class QuoteMatcher(val quotes: Quotes)(using Context) { + class QuoteMatcher(using Context) { // TODO improve performance @@ -355,6 +355,7 @@ object Matcher { // No Match case _ => if (debug) + val quotes = QuotesImpl() println( s""">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |Scrutinee diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 3049430419f1..e2e6daea795e 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2935,9 +2935,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler ctx1.gadt.addToConstraint(typeHoles) ctx1 - val qctx1 = QuotesImpl()(using ctx1) - - val matcher = new Matcher.QuoteMatcher(qctx1)(using ctx1) + val matcher = new Matcher.QuoteMatcher(using ctx1) val matchings = if pat1.isType then matcher.termMatch(scrutinee, pat1) @@ -2949,7 +2947,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler // After matching and doing all subtype checks, we have to approximate all the type bindings // that we have found, seal them in a quoted.Type and add them to the result def typeHoleApproximation(sym: Symbol) = - ctx1.gadt.approximation(sym, !sym.hasAnnotation(dotc.core.Symbols.defn.QuotedRuntimePatterns_fromAboveAnnot)).asInstanceOf[qctx1.reflect.TypeRepr].asType + val fromAboveAnnot = sym.hasAnnotation(dotc.core.Symbols.defn.QuotedRuntimePatterns_fromAboveAnnot) + val approx = ctx1.gadt.approximation(sym, !fromAboveAnnot) + reflect.TypeReprMethods.asType(approx) matchings.map { tup => Tuple.fromIArray(typeHoles.map(typeHoleApproximation).toArray.asInstanceOf[IArray[Object]]) ++ tup } From 63f07ae3873282bb92a9aec3e9b652182d0e9d85 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 11 May 2021 12:06:59 +0200 Subject: [PATCH 19/21] Inline Lambda.apply --- .../quoted/runtime/impl/QuoteMatcher.scala | 35 +++++++------------ 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index eac72578ec9a..587929313b14 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -187,12 +187,6 @@ object Matcher { case _ => None end TypeTreeTypeTest - object Lambda: - def apply(owner: Symbol, tpe: MethodType, rhsFn: (Symbol, List[Tree]) => Tree): Block = - val meth = newSymbol(owner, nme.ANON_FUN, Synthetic | Method, tpe) - Closure(meth, tss => rhsFn(meth, tss.head)) - end Lambda - (scrutinee, pattern) match /* Term hole */ @@ -214,29 +208,24 @@ object Matcher { // Matches an open term and wraps it into a lambda that provides the free variables case (scrutinee, pattern @ Apply(TypeApply(Ident(_), List(TypeTree())), SeqLiteral(args, _) :: Nil)) if pattern.symbol.eq(defn.QuotedRuntimePatterns_higherOrderHole) => - - def bodyFn(lambdaArgs: List[Tree]): Tree = { - val argsMap = args.map(_.symbol).zip(lambdaArgs).toMap - new TreeMap { + 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 = newSymbol(ctx.owner, nme.ANON_FUN, Synthetic | Method, 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 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 resType = pattern.tpe - val res = - Lambda( - ctx.owner, - MethodType(names)( - _ => argTypes, _ => resType), - (meth, x) => TreeOps(bodyFn(x)).changeNonLocalOwners(meth)) - matched(res) + matched(Closure(meth, bodyFn)) // // Match two equivalent trees From d3b61dddcfeb757b197ee80c45b82ee9331d7e01 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 11 May 2021 13:34:37 +0200 Subject: [PATCH 20/21] Move QuoteMatcher logic to object --- .../quoted/runtime/impl/QuoteMatcher.scala | 586 +++++++++--------- .../quoted/runtime/impl/QuotesImpl.scala | 6 +- 2 files changed, 293 insertions(+), 299 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index 587929313b14..8be94ba2f1cd 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -103,306 +103,302 @@ import dotty.tools.dotc.core.Symbols.* * * ``` */ -object Matcher { +object QuoteMatcher { import tpd.* - class QuoteMatcher(using Context) { - - // TODO improve performance - - // TODO use flag from Context. Maybe -debug or add -debug-macros - private inline val debug = false - - import Matching._ - - /** 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 type Env = Map[Symbol, Symbol] - - inline private def withEnv[T](env: Env)(inline body: Env ?=> T): T = body(using env) - - def termMatch(scrutineeTerm: Tree, patternTerm: Tree): Option[Tuple] = - given Env = Map.empty - scrutineeTerm =?= patternTerm - - def typeTreeMatch(scrutineeTypeTree: Tree, patternTypeTree: Tree): Option[Tuple] = - given Env = Map.empty - scrutineeTypeTree =?= patternTypeTree - - /** Check that all trees match with `mtch` and concatenate the results with &&& */ - private def matchLists[T](l1: List[T], l2: List[T])(mtch: (T, T) => Matching): Matching = (l1, l2) match { - case (x :: xs, y :: ys) => mtch(x, y) &&& matchLists(xs, ys)(mtch) - case (Nil, Nil) => matched - case _ => notMatched - } - - extension (scrutinees: List[Tree]) - private def =?= (patterns: List[Tree])(using Env)(using DummyImplicit): Matching = - matchLists(scrutinees, patterns)(_ =?= _) - - extension (scrutinee0: Tree) - - /** Check that the trees match and return the contents from the pattern holes. - * Return None if the trees do not match otherwise return Some of a tuple containing all the contents in the holes. - * - * @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 `None` if it did not match or `Some(tup: Tuple)` if it matched where `tup` contains the contents of the holes. - */ - private def =?= (pattern0: Tree)(using Env): Matching = - - /* Match block flattening */ // TODO move to cases - /** Normalize the tree */ - def normalize(tree: Tree): Tree = tree match { - case Block(Nil, expr) => normalize(expr) - case Block(stats1, Block(stats2, expr)) => - expr match - case _: Closure => tree - case _ => normalize(Block(stats1 ::: stats2, expr)) - case Inlined(_, Nil, expr) => normalize(expr) - case _ => tree - } - - val scrutinee = normalize(scrutinee0) - val pattern = normalize(pattern0) - - /** Check that both are `val` or both are `lazy val` or both are `var` **/ - def checkValFlags(): Boolean = { - val sFlags = scrutinee.symbol.flags - val pFlags = pattern.symbol.flags - sFlags.is(Lazy) == pFlags.is(Lazy) && sFlags.is(Mutable) == pFlags.is(Mutable) - } - - // TODO remove - object TypeTreeTypeTest: - def unapply(x: Tree): Option[Tree & x.type] = x match - case x: (TypeBoundsTree & x.type) => None - case x: (Tree & x.type) if x.isType => Some(x) - case _ => None - end TypeTreeTypeTest - - (scrutinee, pattern) match - - /* Term hole */ - // Match a scala.internal.Quoted.patternHole typed as a repeated argument and return the scrutinee tree - case (scrutinee @ Typed(s, tpt1), Typed(TypeApply(patternHole, tpt :: Nil), tpt2)) - if patternHole.symbol.eq(defn.QuotedRuntimePatterns_patternHole) && - s.tpe <:< tpt.tpe && - tpt2.tpe.derivesFrom(defn.RepeatedParamClass) => - matched(scrutinee) - - /* Term hole */ - // Match a scala.internal.Quoted.patternHole and return the scrutinee tree - case (ClosedPatternTerm(scrutinee), TypeApply(patternHole, tpt :: Nil)) - if patternHole.symbol.eq(defn.QuotedRuntimePatterns_patternHole) && - scrutinee.tpe <:< tpt.tpe => - matched(scrutinee) - - /* Higher order term hole */ - // Matches an open term and wraps it into a lambda that provides the free variables - case (scrutinee, pattern @ 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 - } - val argTypes = args.map(x => x.tpe.widenTermRefExpr) - val methTpe = MethodType(names)(_ => argTypes, _ => pattern.tpe) - val meth = newSymbol(ctx.owner, nme.ANON_FUN, Synthetic | Method, 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) - } - matched(Closure(meth, bodyFn)) - - // - // Match two equivalent trees - // - - /* Match literal */ - case (Literal(constant1), Literal(constant2)) if constant1 == constant2 => - matched - - /* Match type ascription (a) */ - case (Typed(expr1, _), pattern) => - expr1 =?= pattern - - /* Match type ascription (b) */ - case (scrutinee, Typed(expr2, _)) => - scrutinee =?= expr2 - - /* Match selection */ - case (ref: RefTree, Select(qual2, _)) if symbolMatch(scrutinee, pattern) => - ref match - case Select(qual1, _) => qual1 =?= qual2 - case ref: Ident => - ref.tpe match - case TermRef(qual: TermRef, _) => tpd.ref(qual) =?= qual2 - case _ => matched - - /* Match reference */ - case (_: RefTree, _: Ident) if symbolMatch(scrutinee, pattern) => - matched - - /* Match application */ - case (Apply(fn1, args1), Apply(fn2, args2)) => - fn1 =?= fn2 &&& args1 =?= args2 - - /* Match type application */ - case (TypeApply(fn1, args1), TypeApply(fn2, args2)) => - fn1 =?= fn2 &&& args1 =?= args2 - - /* Match block */ - case (Block(stat1 :: stats1, expr1), Block(stat2 :: stats2, expr2)) => - val newEnv = (stat1, stat2) match { - case (stat1: MemberDef, stat2: MemberDef) => - summon[Env] + (stat1.symbol -> stat2.symbol) - case _ => - summon[Env] - } - withEnv(newEnv) { - stat1 =?= stat2 &&& Block(stats1, expr1) =?= Block(stats2, expr2) - } - - /* Match if */ - case (If(cond1, thenp1, elsep1), If(cond2, thenp2, elsep2)) => - cond1 =?= cond2 &&& thenp1 =?= thenp2 &&& elsep1 =?= elsep2 - - /* Match while */ - case (WhileDo(cond1, body1), WhileDo(cond2, body2)) => - cond1 =?= cond2 &&& body1 =?= body2 - - /* Match assign */ - case (Assign(lhs1, rhs1), Assign(lhs2, rhs2)) => - lhs1 =?= lhs2 &&& rhs1 =?= rhs2 - - /* Match new */ - case (New(tpt1), New(tpt2)) if tpt1.tpe.typeSymbol == tpt2.tpe.typeSymbol => - matched - - /* Match this */ - case (This(_), This(_)) if scrutinee.symbol == pattern.symbol => - matched - - /* Match super */ - case (Super(qual1, mix1), Super(qual2, mix2)) if mix1 == mix2 => - qual1 =?= qual2 - - /* Match varargs */ - case (SeqLiteral(elems1, _), SeqLiteral(elems2, _)) if elems1.size == elems2.size => - elems1 =?= elems2 - - /* Match type */ - // TODO remove this? - case (TypeTreeTypeTest(scrutinee), TypeTreeTypeTest(pattern)) if scrutinee.tpe <:< pattern.tpe => - matched - - /* Match val */ - case (scrutinee @ ValDef(_, tpt1, _), pattern @ ValDef(_, tpt2, _)) if checkValFlags() => - def rhsEnv = summon[Env] + (scrutinee.symbol -> pattern.symbol) - tpt1 =?= tpt2 &&& withEnv(rhsEnv)(scrutinee.rhs =?= pattern.rhs) - - /* Match def */ - case (scrutinee @ DefDef(_, paramss1, tpt1, _), pattern @ DefDef(_, paramss2, tpt2, _)) => - def rhsEnv: Env = - val paramSyms: List[(Symbol, Symbol)] = - for - (clause1, clause2) <- paramss1.zip(paramss2) - (param1, param2) <- clause1.zip(clause2) - yield - param1.symbol -> param2.symbol - val oldEnv: Env = summon[Env] - val newEnv: List[(Symbol, Symbol)] = (scrutinee.symbol -> pattern.symbol) :: paramSyms - oldEnv ++ newEnv - - matchLists(paramss1, paramss2)(_ =?= _) - &&& tpt1 =?= tpt2 - &&& withEnv(rhsEnv)(scrutinee.rhs =?= pattern.rhs) - - case (Closure(_, _, tpt1), Closure(_, _, tpt2)) => - // TODO match tpt1 with tpt2? - matched - - case (NamedArg(name1, arg1), NamedArg(name2, arg2)) if name1 == name2 => - arg1 =?= arg2 - - case (EmptyTree, EmptyTree) => - matched - - // No Match - case _ => - if (debug) - val quotes = QuotesImpl() - println( - s""">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - |Scrutinee - | ${scrutinee.show} - |did not match pattern - | ${pattern.show} - | - |with environment: ${summon[Env]} - | - |Scrutinee: ${quotes.reflect.Printer.TreeStructure.show(scrutinee.asInstanceOf)} - |Pattern: ${quotes.reflect.Printer.TreeStructure.show(pattern.asInstanceOf)} - | - |""".stripMargin) - notMatched + // TODO improve performance - end extension + // TODO use flag from Context. Maybe -debug or add -debug-macros + private inline val debug = false + + import Matching._ + + /** 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 type Env = Map[Symbol, Symbol] + + private def withEnv[T](env: Env)(body: Env ?=> T): T = body(using env) - /** 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 - */ - private def symbolMatch(scrutineeTree: Tree, patternTree: Tree)(using Env): Boolean = - val scrutinee = scrutineeTree.symbol - - def overridingSymbol(ofclazz: Symbol): Symbol = - if ofclazz.isClass then scrutinee.denot.overridingSymbol(ofclazz.asClass) - else NoSymbol - - val devirtualizedScrutinee = scrutineeTree match - case Select(qual, _) => - val sym = overridingSymbol(qual.tpe.typeSymbol) - if sym.exists then sym - else scrutinee - case _ => scrutinee - val pattern = patternTree.symbol - - - devirtualizedScrutinee == pattern - || summon[Env].get(devirtualizedScrutinee).contains(pattern) - || devirtualizedScrutinee.allOverriddenSymbols.contains(pattern) - - private object ClosedPatternTerm { - /** Matches a term that does not contain free variables defined in the pattern (i.e. not defined in `Env`) */ - def unapply(term: Tree)(using Env): Option[term.type] = - if freePatternVars(term).isEmpty then Some(term) else None - - /** Return all free variables of the term defined in the pattern (i.e. defined in `Env`) */ - def freePatternVars(term: Tree)(using env: Env): Set[Symbol] = - val accumulator = new TreeAccumulator[Set[Symbol]] { - def apply(x: Set[Symbol], tree: Tree)(using Context): Set[Symbol] = - tree match - case tree: Ident if env.contains(tree.symbol) => foldOver(x + tree.symbol, tree) - case _ => foldOver(x, tree) - } - accumulator.apply(Set.empty, term) - } + def termMatch(scrutineeTerm: Tree, patternTerm: Tree)(using Context): Option[Tuple] = + given Env = Map.empty + scrutineeTerm =?= patternTerm + + def typeTreeMatch(scrutineeTypeTree: Tree, patternTypeTree: Tree)(using Context): Option[Tuple] = + given Env = Map.empty + scrutineeTypeTree =?= patternTypeTree + + /** Check that all trees match with `mtch` and concatenate the results with &&& */ + private def matchLists[T](l1: List[T], l2: List[T])(mtch: (T, T) => Matching): Matching = (l1, l2) match { + case (x :: xs, y :: ys) => mtch(x, y) &&& matchLists(xs, ys)(mtch) + case (Nil, Nil) => matched + case _ => notMatched + } + + extension (scrutinees: List[Tree]) + private def =?= (patterns: List[Tree])(using Env, Context): Matching = + matchLists(scrutinees, patterns)(_ =?= _) + + extension (scrutinee0: Tree) + + /** Check that the trees match and return the contents from the pattern holes. + * Return None if the trees do not match otherwise return Some of a tuple containing all the contents in the holes. + * + * @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 `None` if it did not match or `Some(tup: Tuple)` if it matched where `tup` contains the contents of the holes. + */ + private def =?= (pattern0: Tree)(using Env, Context): Matching = + + /* Match block flattening */ // TODO move to cases + /** Normalize the tree */ + def normalize(tree: Tree): Tree = tree match { + case Block(Nil, expr) => normalize(expr) + case Block(stats1, Block(stats2, expr)) => + expr match + case _: Closure => tree + case _ => normalize(Block(stats1 ::: stats2, expr)) + case Inlined(_, Nil, expr) => normalize(expr) + case _ => tree + } + val scrutinee = normalize(scrutinee0) + val pattern = normalize(pattern0) + + /** Check that both are `val` or both are `lazy val` or both are `var` **/ + def checkValFlags(): Boolean = { + val sFlags = scrutinee.symbol.flags + val pFlags = pattern.symbol.flags + sFlags.is(Lazy) == pFlags.is(Lazy) && sFlags.is(Mutable) == pFlags.is(Mutable) + } + + // TODO remove + object TypeTreeTypeTest: + def unapply(x: Tree): Option[Tree & x.type] = x match + case x: (TypeBoundsTree & x.type) => None + case x: (Tree & x.type) if x.isType => Some(x) + case _ => None + end TypeTreeTypeTest + + (scrutinee, pattern) match + + /* Term hole */ + // Match a scala.internal.Quoted.patternHole typed as a repeated argument and return the scrutinee tree + case (scrutinee @ Typed(s, tpt1), Typed(TypeApply(patternHole, tpt :: Nil), tpt2)) + if patternHole.symbol.eq(defn.QuotedRuntimePatterns_patternHole) && + s.tpe <:< tpt.tpe && + tpt2.tpe.derivesFrom(defn.RepeatedParamClass) => + matched(scrutinee) + + /* Term hole */ + // Match a scala.internal.Quoted.patternHole and return the scrutinee tree + case (ClosedPatternTerm(scrutinee), TypeApply(patternHole, tpt :: Nil)) + if patternHole.symbol.eq(defn.QuotedRuntimePatterns_patternHole) && + scrutinee.tpe <:< tpt.tpe => + matched(scrutinee) + + /* Higher order term hole */ + // Matches an open term and wraps it into a lambda that provides the free variables + case (scrutinee, pattern @ 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 + } + val argTypes = args.map(x => x.tpe.widenTermRefExpr) + val methTpe = MethodType(names)(_ => argTypes, _ => pattern.tpe) + val meth = newSymbol(ctx.owner, nme.ANON_FUN, Synthetic | Method, 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) + } + matched(Closure(meth, bodyFn)) + + // + // Match two equivalent trees + // + + /* Match literal */ + case (Literal(constant1), Literal(constant2)) if constant1 == constant2 => + matched + + /* Match type ascription (a) */ + case (Typed(expr1, _), pattern) => + expr1 =?= pattern + + /* Match type ascription (b) */ + case (scrutinee, Typed(expr2, _)) => + scrutinee =?= expr2 + + /* Match selection */ + case (ref: RefTree, Select(qual2, _)) if symbolMatch(scrutinee, pattern) => + ref match + case Select(qual1, _) => qual1 =?= qual2 + case ref: Ident => + ref.tpe match + case TermRef(qual: TermRef, _) => tpd.ref(qual) =?= qual2 + case _ => matched + + /* Match reference */ + case (_: RefTree, _: Ident) if symbolMatch(scrutinee, pattern) => + matched + + /* Match application */ + case (Apply(fn1, args1), Apply(fn2, args2)) => + fn1 =?= fn2 &&& args1 =?= args2 + + /* Match type application */ + case (TypeApply(fn1, args1), TypeApply(fn2, args2)) => + fn1 =?= fn2 &&& args1 =?= args2 + + /* Match block */ + case (Block(stat1 :: stats1, expr1), Block(stat2 :: stats2, expr2)) => + val newEnv = (stat1, stat2) match { + case (stat1: MemberDef, stat2: MemberDef) => + summon[Env] + (stat1.symbol -> stat2.symbol) + case _ => + summon[Env] + } + withEnv(newEnv) { + stat1 =?= stat2 &&& Block(stats1, expr1) =?= Block(stats2, expr2) + } + + /* Match if */ + case (If(cond1, thenp1, elsep1), If(cond2, thenp2, elsep2)) => + cond1 =?= cond2 &&& thenp1 =?= thenp2 &&& elsep1 =?= elsep2 + + /* Match while */ + case (WhileDo(cond1, body1), WhileDo(cond2, body2)) => + cond1 =?= cond2 &&& body1 =?= body2 + + /* Match assign */ + case (Assign(lhs1, rhs1), Assign(lhs2, rhs2)) => + lhs1 =?= lhs2 &&& rhs1 =?= rhs2 + + /* Match new */ + case (New(tpt1), New(tpt2)) if tpt1.tpe.typeSymbol == tpt2.tpe.typeSymbol => + matched + + /* Match this */ + case (This(_), This(_)) if scrutinee.symbol == pattern.symbol => + matched + + /* Match super */ + case (Super(qual1, mix1), Super(qual2, mix2)) if mix1 == mix2 => + qual1 =?= qual2 + + /* Match varargs */ + case (SeqLiteral(elems1, _), SeqLiteral(elems2, _)) if elems1.size == elems2.size => + elems1 =?= elems2 + + /* Match type */ + // TODO remove this? + case (TypeTreeTypeTest(scrutinee), TypeTreeTypeTest(pattern)) if scrutinee.tpe <:< pattern.tpe => + matched + + /* Match val */ + case (scrutinee @ ValDef(_, tpt1, _), pattern @ ValDef(_, tpt2, _)) if checkValFlags() => + def rhsEnv = summon[Env] + (scrutinee.symbol -> pattern.symbol) + tpt1 =?= tpt2 &&& withEnv(rhsEnv)(scrutinee.rhs =?= pattern.rhs) + + /* Match def */ + case (scrutinee @ DefDef(_, paramss1, tpt1, _), pattern @ DefDef(_, paramss2, tpt2, _)) => + def rhsEnv: Env = + val paramSyms: List[(Symbol, Symbol)] = + for + (clause1, clause2) <- paramss1.zip(paramss2) + (param1, param2) <- clause1.zip(clause2) + yield + param1.symbol -> param2.symbol + val oldEnv: Env = summon[Env] + val newEnv: List[(Symbol, Symbol)] = (scrutinee.symbol -> pattern.symbol) :: paramSyms + oldEnv ++ newEnv + + matchLists(paramss1, paramss2)(_ =?= _) + &&& tpt1 =?= tpt2 + &&& withEnv(rhsEnv)(scrutinee.rhs =?= pattern.rhs) + + case (Closure(_, _, tpt1), Closure(_, _, tpt2)) => + // TODO match tpt1 with tpt2? + matched + + case (NamedArg(name1, arg1), NamedArg(name2, arg2)) if name1 == name2 => + arg1 =?= arg2 + + case (EmptyTree, EmptyTree) => + matched + + // No Match + case _ => + if (debug) + val quotes = QuotesImpl() + println( + s""">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + |Scrutinee + | ${scrutinee.show} + |did not match pattern + | ${pattern.show} + | + |with environment: ${summon[Env]} + | + |Scrutinee: ${quotes.reflect.Printer.TreeStructure.show(scrutinee.asInstanceOf)} + |Pattern: ${quotes.reflect.Printer.TreeStructure.show(pattern.asInstanceOf)} + | + |""".stripMargin) + notMatched + + 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 + */ + private def symbolMatch(scrutineeTree: Tree, patternTree: Tree)(using Env, Context): Boolean = + val scrutinee = scrutineeTree.symbol + + def overridingSymbol(ofclazz: Symbol): Symbol = + if ofclazz.isClass then scrutinee.denot.overridingSymbol(ofclazz.asClass) + else NoSymbol + + val devirtualizedScrutinee = scrutineeTree match + case Select(qual, _) => + val sym = overridingSymbol(qual.tpe.typeSymbol) + if sym.exists then sym + else scrutinee + case _ => scrutinee + val pattern = patternTree.symbol + + + devirtualizedScrutinee == pattern + || summon[Env].get(devirtualizedScrutinee).contains(pattern) + || devirtualizedScrutinee.allOverriddenSymbols.contains(pattern) + + private object ClosedPatternTerm { + /** Matches a term that does not contain free variables defined in the pattern (i.e. not defined in `Env`) */ + def unapply(term: Tree)(using Env, Context): Option[term.type] = + if freePatternVars(term).isEmpty then Some(term) else None + + /** Return all free variables of the term defined in the pattern (i.e. defined in `Env`) */ + def freePatternVars(term: Tree)(using Env, Context): Set[Symbol] = + val accumulator = new TreeAccumulator[Set[Symbol]] { + def apply(x: Set[Symbol], tree: Tree)(using Context): Set[Symbol] = + tree match + case tree: Ident if summon[Env].contains(tree.symbol) => foldOver(x + tree.symbol, tree) + case _ => foldOver(x, tree) + } + accumulator.apply(Set.empty, term) } /** Result of matching a part of an expression */ diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index e2e6daea795e..449bc4b0af32 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2935,11 +2935,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler ctx1.gadt.addToConstraint(typeHoles) ctx1 - val matcher = new Matcher.QuoteMatcher(using ctx1) - val matchings = - if pat1.isType then matcher.termMatch(scrutinee, pat1) - else matcher.termMatch(scrutinee, pat1) + if pat1.isType then QuoteMatcher.termMatch(scrutinee, pat1)(using ctx1) + else QuoteMatcher.termMatch(scrutinee, pat1)(using ctx1) // val matchings = matcher.termMatch(scrutinee, pattern) if typeHoles.isEmpty then matchings From 7c5deddd58f204ca117673745509bce18fcb3234 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 17 May 2021 14:47:33 +0200 Subject: [PATCH 21/21] Remove duplicated code --- compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala | 6 +----- compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala | 5 +---- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index 8be94ba2f1cd..72c0f289c456 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -124,14 +124,10 @@ object QuoteMatcher { private def withEnv[T](env: Env)(body: Env ?=> T): T = body(using env) - def termMatch(scrutineeTerm: Tree, patternTerm: Tree)(using Context): Option[Tuple] = + def treeMatch(scrutineeTerm: Tree, patternTerm: Tree)(using Context): Option[Tuple] = given Env = Map.empty scrutineeTerm =?= patternTerm - def typeTreeMatch(scrutineeTypeTree: Tree, patternTypeTree: Tree)(using Context): Option[Tuple] = - given Env = Map.empty - scrutineeTypeTree =?= patternTypeTree - /** Check that all trees match with `mtch` and concatenate the results with &&& */ private def matchLists[T](l1: List[T], l2: List[T])(mtch: (T, T) => Matching): Matching = (l1, l2) match { case (x :: xs, y :: ys) => mtch(x, y) &&& matchLists(xs, ys)(mtch) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 449bc4b0af32..7a984bbc3492 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2935,11 +2935,8 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler ctx1.gadt.addToConstraint(typeHoles) ctx1 - val matchings = - if pat1.isType then QuoteMatcher.termMatch(scrutinee, pat1)(using ctx1) - else QuoteMatcher.termMatch(scrutinee, pat1)(using ctx1) + val matchings = QuoteMatcher.treeMatch(scrutinee, pat1)(using ctx1) - // val matchings = matcher.termMatch(scrutinee, pattern) if typeHoles.isEmpty then matchings else { // After matching and doing all subtype checks, we have to approximate all the type bindings