Skip to content

Commit ec89a26

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 ec89a26

File tree

23 files changed

+419
-42
lines changed

23 files changed

+419
-42
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: 37 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,37 @@ 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+
* We also encode the tree before pickling it TASTy. Therefore we still load quote patterns as `unapply`s
745+
* as we did before the existence of `QuotePattern`.
746+
*
747+
* The `bindings` contain the list of quote pattern type variable definitions (`Bind`s) in the oreder in
748+
* which they are defined in the source.
749+
*
750+
* @param bindings Type variable definitions (`Bind` tree)
751+
* @param body Quoted pattern (without type variable definitions)
752+
* @param quotes A reference to the given `Quotes` instance in scope
753+
*/
754+
case class QuotePattern[+T <: Untyped] private[ast] (bindings: List[Tree[T]], body: Tree[T], quotes: Tree[T])(implicit @constructorOnly src: SourceFile)
755+
extends PatternTree[T] {
756+
type ThisTree[+T <: Untyped] = QuotePattern[T]
757+
758+
/** Type of the quoted pattern */
759+
def bodyType(using Context): Type =
760+
val quoteType = typeOpt // `Expr[T]` or `Type[T]`
761+
quoteType.argInfos.head // T
762+
}
763+
740764
/** A tree representing a pattern splice `${ pattern }`, `$ident` or `$ident(args*)` in a quote pattern.
741765
*
742766
* Parser will only create `${ pattern }` and `$ident`, hence they will not have args.
743767
* While typing, the `$ident(args*)` the args are identified and desugared into a `SplicePattern`
744768
* containing them.
745769
*
746-
* SplicePattern are removed after typing the pattern and are not present in TASTy.
770+
* `SplicePattern` can only be contained within a `QuotePattern`.
747771
*
748772
* @param body The tree that was spliced
749773
* @param args The arguments of the splice (the HOAS arguments)
@@ -1163,6 +1187,7 @@ object Trees {
11631187
type Inlined = Trees.Inlined[T]
11641188
type Quote = Trees.Quote[T]
11651189
type Splice = Trees.Splice[T]
1190+
type QuotePattern = Trees.QuotePattern[T]
11661191
type SplicePattern = Trees.SplicePattern[T]
11671192
type TypeTree = Trees.TypeTree[T]
11681193
type InferredTypeTree = Trees.InferredTypeTree[T]
@@ -1342,6 +1367,10 @@ object Trees {
13421367
case tree: Splice if (expr eq tree.expr) => tree
13431368
case _ => finalize(tree, untpd.Splice(expr)(sourceFile(tree)))
13441369
}
1370+
def QuotePattern(tree: Tree)(bindings: List[Tree], body: Tree, quotes: Tree)(using Context): QuotePattern = tree match {
1371+
case tree: QuotePattern if (bindings eq tree.bindings) && (body eq tree.body) && (quotes eq tree.quotes) => tree
1372+
case _ => finalize(tree, untpd.QuotePattern(bindings, body, quotes)(sourceFile(tree)))
1373+
}
13451374
def SplicePattern(tree: Tree)(body: Tree, args: List[Tree])(using Context): SplicePattern = tree match {
13461375
case tree: SplicePattern if (body eq tree.body) && (args eq tree.args) => tree
13471376
case _ => finalize(tree, untpd.SplicePattern(body, args)(sourceFile(tree)))
@@ -1587,6 +1616,8 @@ object Trees {
15871616
cpy.Quote(tree)(transform(body)(using quoteContext), transform(tags))
15881617
case tree @ Splice(expr) =>
15891618
cpy.Splice(tree)(transform(expr)(using spliceContext))
1619+
case tree @ QuotePattern(bindings, body, quotes) =>
1620+
cpy.QuotePattern(tree)(transform(bindings), transform(body)(using quoteContext), transform(quotes))
15901621
case tree @ SplicePattern(body, args) =>
15911622
cpy.SplicePattern(tree)(transform(body)(using spliceContext), transform(args))
15921623
case tree @ Hole(isTerm, idx, args, content) =>
@@ -1734,6 +1765,8 @@ object Trees {
17341765
this(this(x, body)(using quoteContext), tags)
17351766
case Splice(expr) =>
17361767
this(x, expr)(using spliceContext)
1768+
case QuotePattern(bindings, body, quotes) =>
1769+
this(this(this(x, bindings), body)(using quoteContext), quotes)
17371770
case SplicePattern(body, args) =>
17381771
this(this(x, body)(using spliceContext), args)
17391772
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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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/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)