Skip to content

Commit d59048c

Browse files
committed
Add QuotePattern AST node
We add the definition of the `QuotePattern` AST. In this first step, we keep the typing of quote patterns untouched and decode the resulting unapply into a `QuotePattern`. This AST is used to preform all transformations until the `staging` phase, where it is encoded into an `unapply` and a `Quote` AST. We also encode the AST into an `unapply` when we pickle the tree to keep the same pickled representation as before.
1 parent cd91bc8 commit d59048c

File tree

24 files changed

+429
-46
lines changed

24 files changed

+429
-46
lines changed

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -810,12 +810,19 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
810810

811811
/** The variables defined by a pattern, in reverse order of their appearance. */
812812
def patVars(tree: Tree)(using Context): List[Symbol] = {
813-
val acc = new TreeAccumulator[List[Symbol]] {
813+
val acc = new TreeAccumulator[List[Symbol]] { outer =>
814814
def apply(syms: List[Symbol], tree: Tree)(using Context) = tree match {
815815
case Bind(_, body) => apply(tree.symbol :: syms, body)
816816
case Annotated(tree, id @ Ident(tpnme.BOUNDTYPE_ANNOT)) => apply(id.symbol :: syms, tree)
817+
case QuotePattern(bindings, body, _) => quotePatVars(bindings.map(_.symbol) ::: syms, body)
817818
case _ => foldOver(syms, tree)
818819
}
820+
private object quotePatVars extends TreeAccumulator[List[Symbol]] {
821+
def apply(syms: List[Symbol], tree: Tree)(using Context) = tree match {
822+
case SplicePattern(pat, _) => outer.apply(syms, pat)
823+
case _ => foldOver(syms, tree)
824+
}
825+
}
819826
}
820827
acc(Nil, tree)
821828
}

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -686,9 +686,9 @@ object Trees {
686686
* phases. After `pickleQuotes` phase, the only quotes that exist are in `inline`
687687
* methods. These are dropped when we remove the inline method implementations.
688688
*
689-
* Type quotes `'[body]` from the parser are desugared into quote patterns (using a `Type.of[T]]`)
690-
* when type checking. TASTy files will not contain type quotes. Type quotes are used again
691-
* in the `staging` phase to represent the reification of `Type.of[T]]`.
689+
* Type quotes `'[body]` from the parser are typed into `QuotePattern`s when type checking.
690+
* TASTy files will not contain type quotes. Type quotes are used again in the `staging`
691+
* phase to represent the reification of `Type.of[T]]`.
692692
*
693693
* Type tags `tags` are always empty before the `staging` phase. Tags for stage inconsistent
694694
* types are added in the `staging` phase to level 0 quotes. Tags for types that refer to
@@ -737,13 +737,35 @@ object Trees {
737737
type ThisTree[+T <: Untyped] = Splice[T]
738738
}
739739

740+
/** A tree representing a quote pattern `'{ type binding1; ...; body }` or `'[ type binding1; ...; body ]`.
741+
* `QuotePattern`s are created the type checker when typing an `untpd.Quote` in a pattern context.
742+
*
743+
* `QuotePattern`s are checked are encoded into `unapply`s in the `staging` phase.
744+
*
745+
* The `bindings` contain the list of quote pattern type variable definitions (`Bind`s) in the oreder in
746+
* which they are defined in the source.
747+
*
748+
* @param bindings Type variable definitions (`Bind` tree)
749+
* @param body Quoted pattern (without type variable definitions)
750+
* @param quotes A reference to the given `Quotes` instance in scope
751+
*/
752+
case class QuotePattern[+T <: Untyped] private[ast] (bindings: List[Tree[T]], body: Tree[T], quotes: Tree[T])(implicit @constructorOnly src: SourceFile)
753+
extends PatternTree[T] {
754+
type ThisTree[+T <: Untyped] = QuotePattern[T]
755+
756+
/** Type of the quoted pattern */
757+
def bodyType(using Context): Type =
758+
val quoteType = typeOpt // `Expr[T]` or `Type[T]`
759+
quoteType.argInfos.head // T
760+
}
761+
740762
/** A tree representing a pattern splice `${ pattern }`, `$ident` or `$ident(args*)` in a quote pattern.
741763
*
742764
* Parser will only create `${ pattern }` and `$ident`, hence they will not have args.
743765
* While typing, the `$ident(args*)` the args are identified and desugared into a `SplicePattern`
744766
* containing them.
745767
*
746-
* SplicePattern are removed after typing the pattern and are not present in TASTy.
768+
* `SplicePattern` can only be contained within a `QuotePattern`.
747769
*
748770
* @param body The tree that was spliced
749771
* @param args The arguments of the splice (the HOAS arguments)
@@ -1163,6 +1185,7 @@ object Trees {
11631185
type Inlined = Trees.Inlined[T]
11641186
type Quote = Trees.Quote[T]
11651187
type Splice = Trees.Splice[T]
1188+
type QuotePattern = Trees.QuotePattern[T]
11661189
type SplicePattern = Trees.SplicePattern[T]
11671190
type TypeTree = Trees.TypeTree[T]
11681191
type InferredTypeTree = Trees.InferredTypeTree[T]
@@ -1342,6 +1365,10 @@ object Trees {
13421365
case tree: Splice if (expr eq tree.expr) => tree
13431366
case _ => finalize(tree, untpd.Splice(expr)(sourceFile(tree)))
13441367
}
1368+
def QuotePattern(tree: Tree)(bindings: List[Tree], body: Tree, quotes: Tree)(using Context): QuotePattern = tree match {
1369+
case tree: QuotePattern if (bindings eq tree.bindings) && (body eq tree.body) && (quotes eq tree.quotes) => tree
1370+
case _ => finalize(tree, untpd.QuotePattern(bindings, body, quotes)(sourceFile(tree)))
1371+
}
13451372
def SplicePattern(tree: Tree)(body: Tree, args: List[Tree])(using Context): SplicePattern = tree match {
13461373
case tree: SplicePattern if (body eq tree.body) && (args eq tree.args) => tree
13471374
case _ => finalize(tree, untpd.SplicePattern(body, args)(sourceFile(tree)))
@@ -1587,6 +1614,8 @@ object Trees {
15871614
cpy.Quote(tree)(transform(body)(using quoteContext), transform(tags))
15881615
case tree @ Splice(expr) =>
15891616
cpy.Splice(tree)(transform(expr)(using spliceContext))
1617+
case tree @ QuotePattern(bindings, body, quotes) =>
1618+
cpy.QuotePattern(tree)(transform(bindings), transform(body)(using quoteContext), transform(quotes))
15901619
case tree @ SplicePattern(body, args) =>
15911620
cpy.SplicePattern(tree)(transform(body)(using spliceContext), transform(args))
15921621
case tree @ Hole(isTerm, idx, args, content) =>
@@ -1734,6 +1763,8 @@ object Trees {
17341763
this(this(x, body)(using quoteContext), tags)
17351764
case Splice(expr) =>
17361765
this(x, expr)(using spliceContext)
1766+
case QuotePattern(bindings, body, quotes) =>
1767+
this(this(this(x, bindings), body)(using quoteContext), quotes)
17371768
case SplicePattern(body, args) =>
17381769
this(this(x, body)(using spliceContext), args)
17391770
case Hole(_, _, args, content) =>

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
173173
def Quote(body: Tree, tags: List[Tree])(using Context): Quote =
174174
untpd.Quote(body, tags).withBodyType(body.tpe)
175175

176+
def QuotePattern(bindings: List[Tree], body: Tree, quotes: Tree, proto: Type)(using Context): QuotePattern =
177+
ta.assignType(untpd.QuotePattern(bindings, body, quotes), proto)
178+
176179
def Splice(expr: Tree, tpe: Type)(using Context): Splice =
177180
untpd.Splice(expr).withType(tpe)
178181

compiler/src/dotty/tools/dotc/ast/untpd.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
399399
def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion)
400400
def Quote(body: Tree, tags: List[Tree])(implicit src: SourceFile): Quote = new Quote(body, tags)
401401
def Splice(expr: Tree)(implicit src: SourceFile): Splice = new Splice(expr)
402+
def QuotePattern(bindings: List[Tree], body: Tree, quotes: Tree)(implicit src: SourceFile): QuotePattern = new QuotePattern(bindings, body, quotes)
402403
def SplicePattern(body: Tree, args: List[Tree])(implicit src: SourceFile): SplicePattern = new SplicePattern(body, args)
403404
def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree()
404405
def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree()

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -853,9 +853,9 @@ class Definitions {
853853

854854
@tu lazy val QuoteMatchingClass: ClassSymbol = requiredClass("scala.quoted.runtime.QuoteMatching")
855855
@tu lazy val QuoteMatching_ExprMatch: Symbol = QuoteMatchingClass.requiredMethod("ExprMatch")
856-
@tu lazy val QuoteMatching_ExprMatchModule: Symbol = QuoteMatchingClass.requiredClass("ExprMatchModule")
856+
@tu lazy val QuoteMatching_ExprMatch_unapply: Symbol = QuoteMatchingClass.requiredClass("ExprMatchModule").requiredMethod(nme.unapply)
857857
@tu lazy val QuoteMatching_TypeMatch: Symbol = QuoteMatchingClass.requiredMethod("TypeMatch")
858-
@tu lazy val QuoteMatching_TypeMatchModule: Symbol = QuoteMatchingClass.requiredClass("TypeMatchModule")
858+
@tu lazy val QuoteMatching_TypeMatch_unapply: Symbol = QuoteMatchingClass.requiredClass("TypeMatchModule").requiredMethod(nme.unapply)
859859
@tu lazy val QuoteMatchingModule: Symbol = requiredModule("scala.quoted.runtime.QuoteMatching")
860860
@tu lazy val QuoteMatching_KNil: Symbol = QuoteMatchingModule.requiredType("KNil")
861861
@tu lazy val QuoteMatching_KCons: Symbol = QuoteMatchingModule.requiredType("KCons")
@@ -942,6 +942,7 @@ class Definitions {
942942
def TupleXXLModule(using Context): Symbol = TupleXXLClass.companionModule
943943

944944
def TupleXXL_fromIterator(using Context): Symbol = TupleXXLModule.requiredMethod("fromIterator")
945+
def TupleXXL_unapplySeq(using Context): Symbol = TupleXXLModule.requiredMethod(nme.unapplySeq)
945946

946947
@tu lazy val RuntimeTupleMirrorTypeRef: TypeRef = requiredClassRef("scala.runtime.TupleMirror")
947948

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import config.Config
1919
import collection.mutable
2020
import reporting.{Profile, NoProfile}
2121
import dotty.tools.tasty.TastyFormat.ASTsSection
22+
import quoted.QuotePatterns
2223

2324
object TreePickler:
2425
class StackSizeExceeded(val mdef: tpd.MemberDef) extends Exception
@@ -685,6 +686,9 @@ class TreePickler(pickler: TastyPickler) {
685686
.appliedTo(expr)
686687
.withSpan(tree.span)
687688
)
689+
case tree: QuotePattern =>
690+
// TODO: Add QUOTEPATTERN tag to TASTy
691+
pickleTree(QuotePatterns.encode(tree))
688692
case Hole(_, idx, args, _) =>
689693
writeByte(HOLE)
690694
withLength {

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import Trees._
3333
import Decorators._
3434
import transform.SymUtils._
3535
import cc.{adaptFunctionTypeUnderPureFuns, adaptByNameArgUnderPureFuns}
36+
import dotty.tools.dotc.quoted.QuotePatterns
3637

3738
import dotty.tools.tasty.{TastyBuffer, TastyReader}
3839
import TastyBuffer._
@@ -1419,7 +1420,11 @@ class TreeUnpickler(reader: TastyReader,
14191420
}
14201421
val patType = readType()
14211422
val argPats = until(end)(readTree())
1422-
UnApply(fn, implicitArgs, argPats, patType)
1423+
val unapply = UnApply(fn, implicitArgs, argPats, patType)
1424+
if fn.symbol == defn.QuoteMatching_ExprMatch_unapply
1425+
|| fn.symbol == defn.QuoteMatching_TypeMatch_unapply
1426+
then QuotePatterns.decode(unapply)
1427+
else unapply
14231428
case REFINEDtpt =>
14241429
val refineCls = symAtAddr.getOrElse(start,
14251430
newRefinedClassSymbol(coordAt(start))).asClass

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,14 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
741741
case Splice(expr) =>
742742
val spliceTypeText = (keywordStr("[") ~ toTextGlobal(tree.typeOpt) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists)
743743
keywordStr("$") ~ spliceTypeText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}")
744+
case tree @ QuotePattern(bindings, body, quotes) =>
745+
val quotesText = (keywordStr("<") ~ toText(quotes) ~ keywordStr(">")).provided(printDebug)
746+
val bindingsText = bindings.map(binding => {
747+
keywordStr("type ") ~ toText(binding.symbol.name) ~ toText(binding.symbol.info) ~ "; "
748+
}).reduceLeft(_ ~~ _).provided(bindings.nonEmpty)
749+
val open = if (body.isTerm) keywordStr("{") else keywordStr("[")
750+
val close = if (body.isTerm) keywordStr("}") else keywordStr("]")
751+
keywordStr("'") ~ quotesText ~ open ~ bindingsText ~ toTextGlobal(body) ~ close
744752
case SplicePattern(pattern, args) =>
745753
val spliceTypeText = (keywordStr("[") ~ toTextGlobal(tree.typeOpt) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists)
746754
keywordStr("$") ~ spliceTypeText ~ {

0 commit comments

Comments
 (0)