Skip to content

Commit 8f8fc36

Browse files
authored
Move treeMatch into QuoteMatcher (#17236)
2 parents aa74ac4 + aad8475 commit 8f8fc36

File tree

2 files changed

+61
-59
lines changed

2 files changed

+61
-59
lines changed

compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ package runtime.impl
33

44
import dotty.tools.dotc.ast.tpd
55
import dotty.tools.dotc.core.Contexts.*
6+
import dotty.tools.dotc.core.Decorators.*
67
import dotty.tools.dotc.core.Flags.*
78
import dotty.tools.dotc.core.Names.*
9+
import dotty.tools.dotc.core.Mode.GadtConstraintInference
810
import dotty.tools.dotc.core.Types.*
911
import dotty.tools.dotc.core.StdNames.nme
1012
import dotty.tools.dotc.core.Symbols.*
@@ -122,10 +124,61 @@ object QuoteMatcher {
122124

123125
private def withEnv[T](env: Env)(body: Env ?=> T): T = body(using env)
124126

125-
def treeMatch(scrutineeTree: Tree, patternTree: Tree)(using Context): Option[MatchingExprs] =
126-
given Env = Map.empty
127-
optional:
128-
scrutineeTree =?= patternTree
127+
/** Evaluate the result of pattern matching against a quote pattern.
128+
* Implementation of the runtime of `QuoteMatching.{ExprMatch, TypeMatch}.unapply`.
129+
*/
130+
def treeMatch(scrutinee: Tree, pattern: Tree)(using Context): Option[Tuple] = {
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))
156+
}
157+
}
158+
}
159+
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
129182

130183
/** Check that all trees match with `mtch` and concatenate the results with &&& */
131184
private def matchLists[T](l1: List[T], l2: List[T])(mtch: (T, T) => MatchingExprs): optional[MatchingExprs] = (l1, l2) match {
@@ -457,7 +510,7 @@ object QuoteMatcher {
457510
*
458511
* This expression is assumed to be a valid expression in the given splice scope.
459512
*/
460-
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
461514
case MatchResult.ClosedTree(tree) =>
462515
new ExprImpl(tree, spliceScope)
463516
case MatchResult.OpenTree(tree, patternTpe, args, env) =>

compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

Lines changed: 3 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
4545
reflect.Printer.TreeCode.show(reflect.asTerm(self))
4646

4747
def matches(that: scala.quoted.Expr[Any]): Boolean =
48-
treeMatch(reflect.asTerm(self), reflect.asTerm(that)).nonEmpty
48+
QuoteMatcher.treeMatch(reflect.asTerm(self), reflect.asTerm(that)).nonEmpty
4949

5050
def valueOrAbort(using fromExpr: FromExpr[T]): T =
5151
def reportError =
@@ -3159,65 +3159,14 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
31593159
def unapply[TypeBindings, Tup <: Tuple](scrutinee: scala.quoted.Expr[Any])(using pattern: scala.quoted.Expr[Any]): Option[Tup] =
31603160
val scrutineeTree = reflect.asTerm(scrutinee)
31613161
val patternTree = reflect.asTerm(pattern)
3162-
treeMatch(scrutineeTree, patternTree).asInstanceOf[Option[Tup]]
3162+
QuoteMatcher.treeMatch(scrutineeTree, patternTree).asInstanceOf[Option[Tup]]
31633163
end ExprMatch
31643164

31653165
object TypeMatch extends TypeMatchModule:
31663166
def unapply[TypeBindings, Tup <: Tuple](scrutinee: scala.quoted.Type[?])(using pattern: scala.quoted.Type[?]): Option[Tup] =
31673167
val scrutineeTree = reflect.TypeTree.of(using scrutinee)
31683168
val patternTree = reflect.TypeTree.of(using pattern)
3169-
treeMatch(scrutineeTree, patternTree).asInstanceOf[Option[Tup]]
3169+
QuoteMatcher.treeMatch(scrutineeTree, patternTree).asInstanceOf[Option[Tup]]
31703170
end TypeMatch
31713171

3172-
private def treeMatch(scrutinee: reflect.Tree, pattern: reflect.Tree): Option[Tuple] = {
3173-
import reflect._
3174-
def isTypeHoleDef(tree: Tree): Boolean =
3175-
tree match
3176-
case tree: TypeDef =>
3177-
tree.symbol.hasAnnotation(dotc.core.Symbols.defn.QuotedRuntimePatterns_patternTypeAnnot)
3178-
case _ => false
3179-
3180-
def extractTypeHoles(pat: Term): (Term, List[Symbol]) =
3181-
pat match
3182-
case tpd.Inlined(_, Nil, pat2) => extractTypeHoles(pat2)
3183-
case tpd.Block(stats @ ((typeHole: TypeDef) :: _), expr) if isTypeHoleDef(typeHole) =>
3184-
val holes = stats.takeWhile(isTypeHoleDef).map(_.symbol)
3185-
val otherStats = stats.dropWhile(isTypeHoleDef)
3186-
(tpd.cpy.Block(pat)(otherStats, expr), holes)
3187-
case _ =>
3188-
(pat, Nil)
3189-
3190-
val (pat1, typeHoles) = extractTypeHoles(pattern)
3191-
3192-
val ctx1 =
3193-
if typeHoles.isEmpty then ctx
3194-
else
3195-
val ctx1 = ctx.fresh.setFreshGADTBounds.addMode(dotc.core.Mode.GadtConstraintInference)
3196-
ctx1.gadtState.addToConstraint(typeHoles)
3197-
ctx1
3198-
3199-
// After matching and doing all subtype checks, we have to approximate all the type bindings
3200-
// that we have found, seal them in a quoted.Type and add them to the result
3201-
def typeHoleApproximation(sym: Symbol) =
3202-
val fromAboveAnnot = sym.hasAnnotation(dotc.core.Symbols.defn.QuotedRuntimePatterns_fromAboveAnnot)
3203-
val fullBounds = ctx1.gadt.fullBounds(sym)
3204-
if fromAboveAnnot then fullBounds.hi else fullBounds.lo
3205-
3206-
QuoteMatcher.treeMatch(scrutinee, pat1)(using ctx1).map { matchings =>
3207-
import QuoteMatcher.MatchResult.*
3208-
lazy val spliceScope = SpliceScope.getCurrent
3209-
val typeHoleApproximations = typeHoles.map(typeHoleApproximation)
3210-
val typeHoleMapping = Map(typeHoles.zip(typeHoleApproximations)*)
3211-
val typeHoleMap = new Types.TypeMap {
3212-
def apply(tp: Types.Type): Types.Type = tp match
3213-
case Types.TypeRef(Types.NoPrefix, _) => typeHoleMapping.getOrElse(tp.typeSymbol, tp)
3214-
case _ => mapOver(tp)
3215-
}
3216-
val matchedExprs = matchings.map(_.toExpr(typeHoleMap, spliceScope))
3217-
val matchedTypes = typeHoleApproximations.map(reflect.TypeReprMethods.asType)
3218-
val results = matchedTypes ++ matchedExprs
3219-
Tuple.fromIArray(IArray.unsafeFromArray(results.toArray))
3220-
}
3221-
}
3222-
32233172
end QuotesImpl

0 commit comments

Comments
 (0)