diff --git a/compiler/src/dotty/tools/dotc/util/optional.scala b/compiler/src/dotty/tools/dotc/util/optional.scala new file mode 100644 index 000000000000..cb62315d3c98 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/util/optional.scala @@ -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) diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index 7c952dbbe142..9a77cba97400 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -1,7 +1,6 @@ package scala.quoted package runtime.impl - import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.core.Flags.* @@ -9,6 +8,7 @@ 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. @@ -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 @@ -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) @@ -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 */ @@ -431,7 +433,6 @@ object QuoteMatcher { case _ => scrutinee val pattern = patternTree.symbol - devirtualizedScrutinee == pattern || summon[Env].get(devirtualizedScrutinee).contains(pattern) || devirtualizedScrutinee.allOverriddenSymbols.contains(pattern) @@ -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 } diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 965a3e907878..378f3f6a5c40 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -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]]) } }