Skip to content

Commit 40ddbb5

Browse files
authored
Backport "Move treeMatch into QuoteMatcher" to LTS (#18937)
Backports #17236 to the LTS branch. PR submitted by the release tooling.
2 parents 721e7c8 + c351484 commit 40ddbb5

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 =
@@ -3155,65 +3155,14 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
31553155
def unapply[TypeBindings, Tup <: Tuple](scrutinee: scala.quoted.Expr[Any])(using pattern: scala.quoted.Expr[Any]): Option[Tup] =
31563156
val scrutineeTree = reflect.asTerm(scrutinee)
31573157
val patternTree = reflect.asTerm(pattern)
3158-
treeMatch(scrutineeTree, patternTree).asInstanceOf[Option[Tup]]
3158+
QuoteMatcher.treeMatch(scrutineeTree, patternTree).asInstanceOf[Option[Tup]]
31593159
end ExprMatch
31603160

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

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

0 commit comments

Comments
 (0)