1
1
package scala .quoted
2
2
package runtime .impl
3
3
4
-
5
4
import dotty .tools .dotc .ast .tpd
6
5
import dotty .tools .dotc .core .Contexts .*
7
6
import dotty .tools .dotc .core .Flags .*
8
7
import dotty .tools .dotc .core .Names .*
9
8
import dotty .tools .dotc .core .Types .*
10
9
import dotty .tools .dotc .core .StdNames .nme
11
10
import dotty .tools .dotc .core .Symbols .*
11
+ import dotty .tools .dotc .util .optional
12
12
13
13
/** Matches a quoted tree against a quoted pattern tree.
14
14
* A quoted pattern tree may have type and term holes in addition to normal terms.
@@ -103,12 +103,13 @@ import dotty.tools.dotc.core.Symbols.*
103
103
object QuoteMatcher {
104
104
import tpd .*
105
105
106
- // TODO improve performance
107
-
108
106
// TODO use flag from Context. Maybe -debug or add -debug-macros
109
107
private inline val debug = false
110
108
111
- import Matching ._
109
+ /** Sequence of matched expressions.
110
+ * These expressions are part of the scrutinee and will be bound to the quote pattern term splices.
111
+ */
112
+ type MatchingExprs = Seq [MatchResult ]
112
113
113
114
/** A map relating equivalent symbols from the scrutinee and the pattern
114
115
* For example in
@@ -121,32 +122,34 @@ object QuoteMatcher {
121
122
122
123
private def withEnv [T ](env : Env )(body : Env ?=> T ): T = body(using env)
123
124
124
- def treeMatch (scrutineeTree : Tree , patternTree : Tree )(using Context ): Option [Tuple ] =
125
+ def treeMatch (scrutineeTree : Tree , patternTree : Tree )(using Context ): Option [MatchingExprs ] =
125
126
given Env = Map .empty
126
- scrutineeTree =?= patternTree
127
+ optional :
128
+ scrutineeTree =?= patternTree
127
129
128
130
/** Check that all trees match with `mtch` and concatenate the results with &&& */
129
- private def matchLists [T ](l1 : List [T ], l2 : List [T ])(mtch : (T , T ) => Matching ): Matching = (l1, l2) match {
131
+ private def matchLists [T ](l1 : List [T ], l2 : List [T ])(mtch : (T , T ) => MatchingExprs ): optional[ MatchingExprs ] = (l1, l2) match {
130
132
case (x :: xs, y :: ys) => mtch(x, y) &&& matchLists(xs, ys)(mtch)
131
133
case (Nil , Nil ) => matched
132
134
case _ => notMatched
133
135
}
134
136
135
137
extension (scrutinees : List [Tree ])
136
- private def =?= (patterns : List [Tree ])(using Env , Context ): Matching =
138
+ private def =?= (patterns : List [Tree ])(using Env , Context ): optional[ MatchingExprs ] =
137
139
matchLists(scrutinees, patterns)(_ =?= _)
138
140
139
141
extension (scrutinee0 : Tree )
140
142
141
143
/** Check that the trees match and return the contents from the pattern holes.
142
- * Return None if the trees do not match otherwise return Some of a tuple containing all the contents in the holes.
144
+ * Return a sequence containing all the contents in the holes.
145
+ * If it does not match, continues to the `optional` with `None`.
143
146
*
144
147
* @param scrutinee The tree being matched
145
148
* @param pattern The pattern tree that the scrutinee should match. Contains `patternHole` holes.
146
149
* @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`.
147
- * @return `None` if it did not match or `Some(tup: Tuple)` if it matched where `tup` contains the contents of the holes .
150
+ * @return The sequence with the contents of the holes of the matched expression .
148
151
*/
149
- private def =?= (pattern0 : Tree )(using Env , Context ): Matching =
152
+ private def =?= (pattern0 : Tree )(using Env , Context ): optional[ MatchingExprs ] =
150
153
151
154
/* Match block flattening */ // TODO move to cases
152
155
/** Normalize the tree */
@@ -203,31 +206,12 @@ object QuoteMatcher {
203
206
// Matches an open term and wraps it into a lambda that provides the free variables
204
207
case Apply (TypeApply (Ident (_), List (TypeTree ())), SeqLiteral (args, _) :: Nil )
205
208
if pattern.symbol.eq(defn.QuotedRuntimePatterns_higherOrderHole ) =>
206
- def hoasClosure = {
207
- val names : List [TermName ] = args.map {
208
- case Block (List (DefDef (nme.ANON_FUN , _, _, Apply (Ident (name), _))), _) => name.asTermName
209
- case arg => arg.symbol.name.asTermName
210
- }
211
- val argTypes = args.map(x => x.tpe.widenTermRefExpr)
212
- val methTpe = MethodType (names)(_ => argTypes, _ => pattern.tpe)
213
- val meth = newAnonFun(ctx.owner, methTpe)
214
- def bodyFn (lambdaArgss : List [List [Tree ]]): Tree = {
215
- val argsMap = args.map(_.symbol).zip(lambdaArgss.head).toMap
216
- val body = new TreeMap {
217
- override def transform (tree : Tree )(using Context ): Tree =
218
- tree match
219
- case tree : Ident => summon[Env ].get(tree.symbol).flatMap(argsMap.get).getOrElse(tree)
220
- case tree => super .transform(tree)
221
- }.transform(scrutinee)
222
- TreeOps (body).changeNonLocalOwners(meth)
223
- }
224
- Closure (meth, bodyFn)
225
- }
209
+ val env = summon[Env ]
226
210
val capturedArgs = args.map(_.symbol)
227
- val captureEnv = summon[ Env ] .filter((k, v) => ! capturedArgs.contains(v))
211
+ val captureEnv = env .filter((k, v) => ! capturedArgs.contains(v))
228
212
withEnv(captureEnv) {
229
213
scrutinee match
230
- case ClosedPatternTerm (scrutinee) => matched(hoasClosure )
214
+ case ClosedPatternTerm (scrutinee) => matchedOpen(scrutinee, pattern.tpe, args, env )
231
215
case _ => notMatched
232
216
}
233
217
@@ -431,7 +415,6 @@ object QuoteMatcher {
431
415
case _ => scrutinee
432
416
val pattern = patternTree.symbol
433
417
434
-
435
418
devirtualizedScrutinee == pattern
436
419
|| summon[Env ].get(devirtualizedScrutinee).contains(pattern)
437
420
|| devirtualizedScrutinee.allOverriddenSymbols.contains(pattern)
@@ -452,32 +435,67 @@ object QuoteMatcher {
452
435
accumulator.apply(Set .empty, term)
453
436
}
454
437
455
- /** Result of matching a part of an expression */
456
- private type Matching = Option [Tuple ]
457
-
458
- private object Matching {
459
-
460
- def notMatched : Matching = None
461
-
462
- val matched : Matching = Some (Tuple ())
463
-
464
- def matched (tree : Tree )(using Context ): Matching =
465
- Some (Tuple1 (new ExprImpl (tree, SpliceScope .getCurrent)))
466
-
467
- extension (self : Matching )
468
- def asOptionOfTuple : Option [Tuple ] = self
469
-
470
- /** Concatenates the contents of two successful matchings or return a `notMatched` */
471
- def &&& (that : => Matching ): Matching = self match {
472
- case Some (x) =>
473
- that match {
474
- case Some (y) => Some (x ++ y)
475
- case _ => None
476
- }
477
- case _ => None
478
- }
479
- end extension
480
-
481
- }
438
+ enum MatchResult :
439
+ /** Closed pattern extracted value
440
+ * @param tree Scrutinee sub-tree that matched
441
+ */
442
+ case ClosedTree (tree : Tree )
443
+ /** HOAS pattern extracted value
444
+ *
445
+ * @param tree Scrutinee sub-tree that matched
446
+ * @param patternTpe Type of the pattern hole (from the pattern)
447
+ * @param args HOAS arguments (from the pattern)
448
+ * @param env Mapping between scrutinee and pattern variables
449
+ */
450
+ case OpenTree (tree : Tree , patternTpe : Type , args : List [Tree ], env : Env )
451
+
452
+ /** Return the expression that was extracted from a hole.
453
+ *
454
+ * If it was a closed expression it returns that expression. Otherwise,
455
+ * if it is a HOAS pattern, the surrounding lambda is generated using
456
+ * `mapTypeHoles` to create the signature of the lambda.
457
+ *
458
+ * This expression is assumed to be a valid expression in the given splice scope.
459
+ */
460
+ def toExpr (mapTypeHoles : TypeMap , spliceScope : Scope )(using Context ): Expr [Any ] = this match
461
+ case MatchResult .ClosedTree (tree) =>
462
+ new ExprImpl (tree, spliceScope)
463
+ case MatchResult .OpenTree (tree, patternTpe, args, env) =>
464
+ val names : List [TermName ] = args.map {
465
+ case Block (List (DefDef (nme.ANON_FUN , _, _, Apply (Ident (name), _))), _) => name.asTermName
466
+ case arg => arg.symbol.name.asTermName
467
+ }
468
+ val paramTypes = args.map(x => mapTypeHoles(x.tpe.widenTermRefExpr))
469
+ val methTpe = MethodType (names)(_ => paramTypes, _ => mapTypeHoles(patternTpe))
470
+ val meth = newAnonFun(ctx.owner, methTpe)
471
+ def bodyFn (lambdaArgss : List [List [Tree ]]): Tree = {
472
+ val argsMap = args.view.map(_.symbol).zip(lambdaArgss.head).toMap
473
+ val body = new TreeMap {
474
+ override def transform (tree : Tree )(using Context ): Tree =
475
+ tree match
476
+ case tree : Ident => env.get(tree.symbol).flatMap(argsMap.get).getOrElse(tree)
477
+ case tree => super .transform(tree)
478
+ }.transform(tree)
479
+ TreeOps (body).changeNonLocalOwners(meth)
480
+ }
481
+ val hoasClosure = Closure (meth, bodyFn)
482
+ new ExprImpl (hoasClosure, spliceScope)
483
+
484
+ private inline def notMatched : optional[MatchingExprs ] =
485
+ optional.break()
486
+
487
+ private inline def matched : MatchingExprs =
488
+ Seq .empty
489
+
490
+ private inline def matched (tree : Tree )(using Context ): MatchingExprs =
491
+ Seq (MatchResult .ClosedTree (tree))
492
+
493
+ private def matchedOpen (tree : Tree , patternTpe : Type , args : List [Tree ], env : Env )(using Context ): MatchingExprs =
494
+ Seq (MatchResult .OpenTree (tree, patternTpe, args, env))
495
+
496
+ extension (self : MatchingExprs )
497
+ /** Concatenates the contents of two successful matchings */
498
+ def &&& (that : MatchingExprs ): MatchingExprs = self ++ that
499
+ end extension
482
500
483
501
}
0 commit comments