@@ -124,63 +124,62 @@ object QuoteMatcher {
124124
125125 private def withEnv [T ](env : Env )(body : Env ?=> T ): T = body(using env)
126126
127- /** Evaluate the the result of pattern matching against a quote pattern.
128- * Implementation of the runtime of `QuoteMatching.{ExprMatch,TypeMatch}.unapply`.
127+ /** Evaluate the result of pattern matching against a quote pattern.
128+ * Implementation of the runtime of `QuoteMatching.{ExprMatch, TypeMatch}.unapply`.
129129 */
130130 def treeMatch (scrutinee : Tree , pattern : Tree )(using Context ): Option [Tuple ] = {
131- def isTypeHoleDef (tree : Tree ): Boolean =
132- tree match
133- case tree : TypeDef =>
134- tree.symbol.hasAnnotation(defn.QuotedRuntimePatterns_patternTypeAnnot )
135- case _ => false
136-
137- def extractTypeHoles (pat : Tree ): (Tree , List [Symbol ]) =
138- pat match
139- case tpd.Inlined (_, Nil , pat2) => extractTypeHoles(pat2)
140- case tpd.Block (stats @ ((typeHole : TypeDef ) :: _), expr) if isTypeHoleDef(typeHole) =>
141- val holes = stats.takeWhile(isTypeHoleDef).map(_.symbol)
142- val otherStats = stats.dropWhile(isTypeHoleDef)
143- (tpd.cpy.Block (pat)(otherStats, expr), holes)
144- case _ =>
145- (pat, Nil )
146-
147- val (pat1, typeHoles) = extractTypeHoles(pattern)
148-
149- val ctx1 =
150- if typeHoles.isEmpty then ctx
151- else
152- val ctx1 = ctx.fresh.setFreshGADTBounds.addMode(GadtConstraintInference )
153- ctx1.gadtState.addToConstraint(typeHoles)
154- ctx1
155-
156- // After matching and doing all subtype checks, we have to approximate all the type bindings
157- // that we have found, seal them in a quoted.Type and add them to the result
158- def typeHoleApproximation (sym : Symbol ) =
159- val fromAboveAnnot = sym.hasAnnotation(defn.QuotedRuntimePatterns_fromAboveAnnot )
160- val fullBounds = ctx1.gadt.fullBounds(sym)
161- if fromAboveAnnot then fullBounds.nn.hi else fullBounds.nn.lo
162-
163- optional {
164- given Context = ctx1
165- given Env = Map .empty
166- scrutinee =?= pat1
167- }.map { matchings =>
168- import QuoteMatcher .MatchResult .*
169- lazy val spliceScope = SpliceScope .getCurrent
170- val typeHoleApproximations = typeHoles.map(typeHoleApproximation)
171- val typeHoleMapping = Map (typeHoles.zip(typeHoleApproximations)* )
172- val typeHoleMap = new TypeMap {
173- def apply (tp : Type ): Type = tp match
174- case TypeRef (NoPrefix , _) => typeHoleMapping.getOrElse(tp.typeSymbol, tp)
175- case _ => mapOver(tp)
131+ val (pat1, typeHoles, ctx1) = instrumentTypeHoles(pattern)
132+ inContext(ctx1) {
133+ optional {
134+ given Env = Map .empty
135+ scrutinee =?= pat1
136+ }.map { matchings =>
137+ import QuoteMatcher .MatchResult .*
138+ lazy val spliceScope = SpliceScope .getCurrent
139+ // After matching and doing all subtype checks, we have to approximate all the type bindings
140+ // that we have found, seal them in a quoted.Type and add them to the result
141+ val typeHoleApproximations = typeHoles.map(typeHoleApproximation)
142+ val matchedTypes = typeHoleApproximations.map(tpe => new TypeImpl (TypeTree (tpe), spliceScope))
143+ val matchedExprs =
144+ val typeHoleMap : Type => Type =
145+ if typeHoles.isEmpty then identity
146+ else new TypeMap {
147+ private val typeHoleMapping = Map (typeHoles.zip(typeHoleApproximations)* )
148+ def apply (tp : Type ): Type = tp match
149+ case TypeRef (NoPrefix , _) => typeHoleMapping.getOrElse(tp.typeSymbol, tp)
150+ case _ => mapOver(tp)
151+ }
152+ if matchings.isEmpty then Nil
153+ else matchings.map(_.toExpr(typeHoleMap, spliceScope))
154+ val results = matchedTypes ++ matchedExprs
155+ Tuple .fromIArray(IArray .unsafeFromArray(results.toArray))
176156 }
177- val matchedExprs = matchings.map(_.toExpr(typeHoleMap, spliceScope))
178- val matchedTypes = typeHoleApproximations.map(tpe => new TypeImpl (TypeTree (tpe), spliceScope))
179- val results = matchedTypes ++ matchedExprs
180- Tuple .fromIArray(IArray .unsafeFromArray(results.toArray))
181157 }
182158 }
183159
160+ def instrumentTypeHoles (pat : Tree )(using Context ): (Tree , List [Symbol ], Context ) =
161+ def isTypeHoleDef (tree : Tree ): Boolean = tree match
162+ case tree : TypeDef => tree.symbol.hasAnnotation(defn.QuotedRuntimePatterns_patternTypeAnnot )
163+ case _ => false
164+ pat match
165+ case tpd.Inlined (_, Nil , pat2) => instrumentTypeHoles(pat2)
166+ case tpd.Block (stats @ ((typeHole : TypeDef ) :: _), expr) if isTypeHoleDef(typeHole) =>
167+ val (holeDefs, otherStats) = stats.span(isTypeHoleDef)
168+ val holeSyms = holeDefs.map(_.symbol)
169+ val ctx1 = ctx.fresh.setFreshGADTBounds.addMode(GadtConstraintInference )
170+ ctx1.gadtState.addToConstraint(holeSyms)
171+ (tpd.cpy.Block (pat)(otherStats, expr), holeSyms, ctx1)
172+ case _ =>
173+ (pat, Nil , ctx)
174+
175+ /** Type approximation of a quote pattern type variable.
176+ * Should only be approximated after matching the tree.
177+ */
178+ def typeHoleApproximation (sym : Symbol )(using Context ): Type =
179+ val fromAboveAnnot = sym.hasAnnotation(defn.QuotedRuntimePatterns_fromAboveAnnot )
180+ val fullBounds = ctx.gadt.fullBounds(sym)
181+ if fromAboveAnnot then fullBounds.nn.hi else fullBounds.nn.lo
182+
184183 /** Check that all trees match with `mtch` and concatenate the results with &&& */
185184 private def matchLists [T ](l1 : List [T ], l2 : List [T ])(mtch : (T , T ) => MatchingExprs ): optional[MatchingExprs ] = (l1, l2) match {
186185 case (x :: xs, y :: ys) => mtch(x, y) &&& matchLists(xs, ys)(mtch)
@@ -511,7 +510,7 @@ object QuoteMatcher {
511510 *
512511 * This expression is assumed to be a valid expression in the given splice scope.
513512 */
514- def toExpr (mapTypeHoles : TypeMap , spliceScope : Scope )(using Context ): Expr [Any ] = this match
513+ def toExpr (mapTypeHoles : Type => Type , spliceScope : Scope )(using Context ): Expr [Any ] = this match
515514 case MatchResult .ClosedTree (tree) =>
516515 new ExprImpl (tree, spliceScope)
517516 case MatchResult .OpenTree (tree, patternTpe, args, env) =>
0 commit comments