Skip to content

Simplify QuoteMatcher.&&& using optional #16953

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions compiler/src/dotty/tools/dotc/util/optional.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package dotty.tools.dotc.util

import scala.util.boundary

/** Return type that indicates that the method returns a T or aborts to the enclosing boundary with a `None` */
type optional[T] = boundary.Label[None.type] ?=> T

/** A prompt for `Option`, which establishes a boundary which `_.?` on `Option` can return */
object optional:
inline def apply[T](inline body: optional[T]): Option[T] =
boundary(Some(body))

extension [T](r: Option[T])
inline def ? (using label: boundary.Label[None.type]): T = r match
case Some(x) => x
case None => boundary.break(None)

inline def break()(using label: boundary.Label[None.type]): Nothing =
boundary.break(None)
55 changes: 20 additions & 35 deletions compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package scala.quoted
package runtime.impl


import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core.Contexts.*
import dotty.tools.dotc.core.Flags.*
import dotty.tools.dotc.core.Names.*
import dotty.tools.dotc.core.Types.*
import dotty.tools.dotc.core.StdNames.nme
import dotty.tools.dotc.core.Symbols.*
import dotty.tools.dotc.util.optional

/** Matches a quoted tree against a quoted pattern tree.
* A quoted pattern tree may have type and term holes in addition to normal terms.
Expand Down Expand Up @@ -103,12 +103,13 @@ import dotty.tools.dotc.core.Symbols.*
object QuoteMatcher {
import tpd.*

// TODO improve performance

// TODO use flag from Context. Maybe -debug or add -debug-macros
private inline val debug = false

import Matching._
/** Sequence of matched expressions.
* These expressions are part of the scrutinee and will be bound to the quote pattern term splices.
*/
type MatchingExprs = Seq[Expr[Any]]

/** A map relating equivalent symbols from the scrutinee and the pattern
* For example in
Expand All @@ -121,19 +122,20 @@ object QuoteMatcher {

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

def treeMatch(scrutineeTree: Tree, patternTree: Tree)(using Context): Option[Tuple] =
def treeMatch(scrutineeTree: Tree, patternTree: Tree)(using Context): Option[MatchingExprs] =
given Env = Map.empty
scrutineeTree =?= patternTree
optional:
scrutineeTree =?= patternTree

/** Check that all trees match with `mtch` and concatenate the results with &&& */
private def matchLists[T](l1: List[T], l2: List[T])(mtch: (T, T) => Matching): Matching = (l1, l2) match {
private def matchLists[T](l1: List[T], l2: List[T])(mtch: (T, T) => MatchingExprs): optional[MatchingExprs] = (l1, l2) match {
case (x :: xs, y :: ys) => mtch(x, y) &&& matchLists(xs, ys)(mtch)
case (Nil, Nil) => matched
case _ => notMatched
}

extension (scrutinees: List[Tree])
private def =?= (patterns: List[Tree])(using Env, Context): Matching =
private def =?= (patterns: List[Tree])(using Env, Context): optional[MatchingExprs] =
matchLists(scrutinees, patterns)(_ =?= _)

extension (scrutinee0: Tree)
Expand All @@ -144,9 +146,9 @@ object QuoteMatcher {
* @param scrutinee The tree being matched
* @param pattern The pattern tree that the scrutinee should match. Contains `patternHole` holes.
* @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`.
* @return `None` if it did not match or `Some(tup: Tuple)` if it matched where `tup` contains the contents of the holes.
* @return `None` if it did not match or `Some(tup: MatchingExprs)` if it matched where `tup` contains the contents of the holes.
*/
private def =?= (pattern0: Tree)(using Env, Context): Matching =
private def =?= (pattern0: Tree)(using Env, Context): optional[MatchingExprs] =

/* Match block flattening */ // TODO move to cases
/** Normalize the tree */
Expand Down Expand Up @@ -431,7 +433,6 @@ object QuoteMatcher {
case _ => scrutinee
val pattern = patternTree.symbol


devirtualizedScrutinee == pattern
|| summon[Env].get(devirtualizedScrutinee).contains(pattern)
|| devirtualizedScrutinee.allOverriddenSymbols.contains(pattern)
Expand All @@ -452,32 +453,16 @@ object QuoteMatcher {
accumulator.apply(Set.empty, term)
}

/** Result of matching a part of an expression */
private type Matching = Option[Tuple]

private object Matching {

def notMatched: Matching = None
private inline def notMatched: optional[MatchingExprs] =
optional.break()

val matched: Matching = Some(Tuple())
private inline def matched: MatchingExprs =
Seq.empty

def matched(tree: Tree)(using Context): Matching =
Some(Tuple1(new ExprImpl(tree, SpliceScope.getCurrent)))
private inline def matched(tree: Tree)(using Context): MatchingExprs =
Seq(new ExprImpl(tree, SpliceScope.getCurrent))

extension (self: Matching)
def asOptionOfTuple: Option[Tuple] = self

/** Concatenates the contents of two successful matchings or return a `notMatched` */
def &&& (that: => Matching): Matching = self match {
case Some(x) =>
that match {
case Some(y) => Some(x ++ y)
case _ => None
}
case _ => None
}
end extension

}
extension (self: MatchingExprs)
private inline def &&& (that: MatchingExprs): MatchingExprs = self ++ that

}
22 changes: 10 additions & 12 deletions compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3139,18 +3139,16 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler

val matchings = QuoteMatcher.treeMatch(scrutinee, pat1)(using ctx1)

if typeHoles.isEmpty then matchings
else {
// After matching and doing all subtype checks, we have to approximate all the type bindings
// that we have found, seal them in a quoted.Type and add them to the result
def typeHoleApproximation(sym: Symbol) =
val fromAboveAnnot = sym.hasAnnotation(dotc.core.Symbols.defn.QuotedRuntimePatterns_fromAboveAnnot)
val fullBounds = ctx1.gadt.fullBounds(sym)
val tp = if fromAboveAnnot then fullBounds.hi else fullBounds.lo
reflect.TypeReprMethods.asType(tp)
matchings.map { tup =>
Tuple.fromIArray(typeHoles.map(typeHoleApproximation).toArray.asInstanceOf[IArray[Object]]) ++ tup
}
// After matching and doing all subtype checks, we have to approximate all the type bindings
// that we have found, seal them in a quoted.Type and add them to the result
def typeHoleApproximation(sym: Symbol) =
val fromAboveAnnot = sym.hasAnnotation(dotc.core.Symbols.defn.QuotedRuntimePatterns_fromAboveAnnot)
val fullBounds = ctx1.gadt.fullBounds(sym)
val tp = if fromAboveAnnot then fullBounds.hi else fullBounds.lo
reflect.TypeReprMethods.asType(tp)
matchings.map { tup =>
val results = typeHoles.map(typeHoleApproximation) ++ tup
Tuple.fromIArray(results.toArray.asInstanceOf[IArray[Object]])
}
}

Expand Down