Skip to content

Commit c078225

Browse files
committed
Split treeMatch into exprMatch and typeMatch
1 parent 7d3f3e4 commit c078225

File tree

2 files changed

+66
-53
lines changed

2 files changed

+66
-53
lines changed

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

Lines changed: 63 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -125,62 +125,75 @@ object QuoteMatcher {
125125
private def withEnv[T](env: Env)(body: Env ?=> T): T = body(using env)
126126

127127
/** Evaluate the the result of pattern matching against a quote pattern.
128-
* Implementation of the runtime of `QuoteMatching.{ExprMatch,TypeMatch}.unapply`.
128+
* Implementation of the runtime of `QuoteMatching.ExprMatch.unapply`.
129129
*/
130-
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)
130+
def exprMatch(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 typeHoleMapping = Map(typeHoles.zip(typeHoleApproximations)*)
143+
val typeHoleMap = new TypeMap {
144+
def apply(tp: Type): Type = tp match
145+
case TypeRef(NoPrefix, _) => typeHoleMapping.getOrElse(tp.typeSymbol, tp)
146+
case _ => mapOver(tp)
147+
}
148+
val matchedExprs = matchings.map(_.toExpr(typeHoleMap, spliceScope))
149+
val matchedTypes = typeHoleApproximations.map(tpe => new TypeImpl(TypeTree(tpe), spliceScope))
150+
val results = matchedTypes ++ matchedExprs
151+
Tuple.fromIArray(IArray.unsafeFromArray(results.toArray))
152+
}
153+
}
154+
}
148155

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)
156+
/** Evaluate the the result of pattern matching against a quote pattern.
157+
* Implementation of the runtime of `QuoteMatching.TypeMatch.unapply`.
158+
*/
159+
def typeMatch(scrutinee: Tree, pattern: Tree)(using Context): Option[Tuple] = {
160+
val (pat1, typeHoles, ctx1) = instrumentTypeHoles(pattern)
161+
inContext(ctx1) {
162+
optional {
163+
given Env = Map.empty
164+
scrutinee =?= pat1
165+
}.map { matchings =>
166+
assert(matchings.isEmpty)
167+
lazy val spliceScope = SpliceScope.getCurrent
168+
val matchedTypes = typeHoles.map(tpe => new TypeImpl(TypeTree(typeHoleApproximation(tpe)), spliceScope))
169+
Tuple.fromIArray(IArray.unsafeFromArray(matchedTypes.toArray))
176170
}
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))
181171
}
182172
}
183173

174+
def instrumentTypeHoles(pat: Tree)(using Context): (Tree, List[Symbol], Context) =
175+
def isTypeHoleDef(tree: Tree): Boolean = tree match
176+
case tree: TypeDef => tree.symbol.hasAnnotation(defn.QuotedRuntimePatterns_patternTypeAnnot)
177+
case _ => false
178+
pat match
179+
case tpd.Inlined(_, Nil, pat2) => instrumentTypeHoles(pat2)
180+
case tpd.Block(stats @ ((typeHole: TypeDef) :: _), expr) if isTypeHoleDef(typeHole) =>
181+
val holes = stats.takeWhile(isTypeHoleDef).map(_.symbol)
182+
val otherStats = stats.dropWhile(isTypeHoleDef)
183+
val ctx1 = ctx.fresh.setFreshGADTBounds.addMode(GadtConstraintInference)
184+
ctx1.gadtState.addToConstraint(holes)
185+
(tpd.cpy.Block(pat)(otherStats, expr), holes, ctx1)
186+
case _ =>
187+
(pat, Nil, ctx)
188+
189+
/** Type approximation of a quote pattern type variable.
190+
* Should only be approximated after matching the tree.
191+
*/
192+
def typeHoleApproximation(sym: Symbol)(using Context): Type =
193+
val fromAboveAnnot = sym.hasAnnotation(defn.QuotedRuntimePatterns_fromAboveAnnot)
194+
val fullBounds = ctx.gadt.fullBounds(sym)
195+
if fromAboveAnnot then fullBounds.nn.hi else fullBounds.nn.lo
196+
184197
/** Check that all trees match with `mtch` and concatenate the results with &&& */
185198
private def matchLists[T](l1: List[T], l2: List[T])(mtch: (T, T) => MatchingExprs): optional[MatchingExprs] = (l1, l2) match {
186199
case (x :: xs, y :: ys) => mtch(x, y) &&& matchLists(xs, ys)(mtch)

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

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

4848
def matches(that: scala.quoted.Expr[Any]): Boolean =
49-
QuoteMatcher.treeMatch(reflect.asTerm(self), reflect.asTerm(that)).nonEmpty
49+
QuoteMatcher.exprMatch(reflect.asTerm(self), reflect.asTerm(that)).nonEmpty
5050

5151
def valueOrAbort(using fromExpr: FromExpr[T]): T =
5252
def reportError =
@@ -3110,14 +3110,14 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
31103110
def unapply[TypeBindings, Tup <: Tuple](scrutinee: scala.quoted.Expr[Any])(using pattern: scala.quoted.Expr[Any]): Option[Tup] =
31113111
val scrutineeTree = reflect.asTerm(scrutinee)
31123112
val patternTree = reflect.asTerm(pattern)
3113-
QuoteMatcher.treeMatch(scrutineeTree, patternTree).asInstanceOf[Option[Tup]]
3113+
QuoteMatcher.exprMatch(scrutineeTree, patternTree).asInstanceOf[Option[Tup]]
31143114
end ExprMatch
31153115

31163116
object TypeMatch extends TypeMatchModule:
31173117
def unapply[TypeBindings, Tup <: Tuple](scrutinee: scala.quoted.Type[?])(using pattern: scala.quoted.Type[?]): Option[Tup] =
31183118
val scrutineeTree = reflect.TypeTree.of(using scrutinee)
31193119
val patternTree = reflect.TypeTree.of(using pattern)
3120-
QuoteMatcher.treeMatch(scrutineeTree, patternTree).asInstanceOf[Option[Tup]]
3120+
QuoteMatcher.typeMatch(scrutineeTree, patternTree).asInstanceOf[Option[Tup]]
31213121
end TypeMatch
31223122

31233123
end QuotesImpl

0 commit comments

Comments
 (0)