From c2f63626fc3102945c73db8e4b19d7777aa81332 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 21 Apr 2023 09:39:21 +0200 Subject: [PATCH 01/46] Rename `Spliced` to `SplicedExpr` --- compiler/src/dotty/tools/dotc/ast/TreeInfo.scala | 2 +- compiler/src/dotty/tools/dotc/inlines/Inliner.scala | 4 ++-- compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala | 2 +- compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala | 4 ++-- compiler/src/dotty/tools/dotc/transform/Splicer.scala | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index c2147b6af2d3..77b34335eeb8 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -1044,7 +1044,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => } /** Extractors for splices */ - object Spliced { + object SplicedExpr { /** Extracts the content of a spliced expression tree. * The result can be the contents of a term splice, which * will return a term tree. diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index f4612e49ccac..ba6300d2e3d3 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -817,7 +817,7 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedApply(tree: untpd.Apply, pt: Type)(using Context): Tree = def cancelQuotes(tree: Tree): Tree = tree match - case Quoted(Spliced(inner)) => inner + case Quoted(SplicedExpr(inner)) => inner case _ => tree val locked = ctx.typerState.ownedVars val res = cancelQuotes(constToLiteral(BetaReduce(super.typedApply(tree, pt)))) match { @@ -1071,7 +1071,7 @@ class Inliner(val call: tpd.Tree)(using Context): level += 1 try apply(syms, body) finally level -= 1 - case Spliced(body) => + case SplicedExpr(body) => level -= 1 try apply(syms, body) finally level += 1 diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index 33bc5a7ef10e..c3aa98e6ef30 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -290,7 +290,7 @@ object PrepareInlineable { if (inlined.is(Macro) && !ctx.isAfterTyper) { def checkMacro(tree: Tree): Unit = tree match { - case Spliced(code) => + case SplicedExpr(code) => if (code.symbol.flags.is(Inline)) report.error("Macro cannot be implemented with an `inline` method", code.srcPos) Splicer.checkValidMacroBody(code) diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index adaacafa764a..49da11afd9b3 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -61,7 +61,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(quotedTree) match { - case Spliced(t) => + case SplicedExpr(t) => // Optimization: `'{ $x }` --> `x` // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]` transform(t).asInstance(tree.tpe) @@ -69,7 +69,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { } finally inQuoteOrSplice = old - case tree @ Spliced(splicedTree) => + case tree @ SplicedExpr(splicedTree) => val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(splicedTree) match { diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index bc4119ad0cff..cd41fb358d30 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -158,7 +158,7 @@ object Splicer { case Apply(Select(Apply(fn, quoted :: Nil), nme.apply), _) if fn.symbol == defn.QuotedRuntime_exprQuote => val noSpliceChecker = new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match - case Spliced(_) => + case SplicedExpr(_) => report.error("Quoted argument of macros may not have splices", tree.srcPos) case _ => traverseChildren(tree) From 938d3ae5f72192d03b084cc84e37bb677d5dc29a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 21 Apr 2023 09:50:10 +0200 Subject: [PATCH 02/46] Split `Quoted` extractor into `QuotedExpr` and `QuotedTypeOf` --- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 13 ++++++++++-- .../dotty/tools/dotc/inlines/Inliner.scala | 8 ++++++-- .../dotc/staging/TreeMapWithStages.scala | 20 ++++++++++--------- .../dotty/tools/dotc/transform/Splicer.scala | 4 ++-- .../tools/dotc/transform/patmat/Space.scala | 2 +- 5 files changed, 31 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 77b34335eeb8..f2a49254678f 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -1027,7 +1027,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => } /** Extractors for quotes */ - object Quoted { + object QuotedExpr { /** Extracts the content of a quoted tree. * The result can be the contents of a term or type quote, which * will return a term or type tree respectively. @@ -1036,7 +1036,16 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => if tree.symbol == defn.QuotedRuntime_exprQuote then // quoted.runtime.Expr.quote[T]() Some(tree.args.head) - else if tree.symbol == defn.QuotedTypeModule_of then + else None + } + + object QuotedTypeOf { + /** Extracts the content of a quoted tree. + * The result can be the contents of a term or type quote, which + * will return a term or type tree respectively. + */ + def unapply(tree: tpd.Apply)(using Context): Option[tpd.Tree] = + if tree.symbol == defn.QuotedTypeModule_of then // quoted.Type.of[](quotes) val TypeApply(_, body :: _) = tree.fun: @unchecked Some(body) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index ba6300d2e3d3..50cd933532ae 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -817,7 +817,7 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedApply(tree: untpd.Apply, pt: Type)(using Context): Tree = def cancelQuotes(tree: Tree): Tree = tree match - case Quoted(SplicedExpr(inner)) => inner + case QuotedExpr(SplicedExpr(inner)) => inner case _ => tree val locked = ctx.typerState.ownedVars val res = cancelQuotes(constToLiteral(BetaReduce(super.typedApply(tree, pt)))) match { @@ -1067,7 +1067,11 @@ class Inliner(val call: tpd.Tree)(using Context): else tree match { case tree: RefTree if tree.isTerm && tree.symbol.isDefinedInCurrentRun && !tree.symbol.isLocal => foldOver(tree.symbol :: syms, tree) - case Quoted(body) => + case QuotedExpr(body) => + level += 1 + try apply(syms, body) + finally level -= 1 + case QuotedTypeOf(body) => level += 1 try apply(syms, body) finally level -= 1 diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index 49da11afd9b3..096454de9a7f 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -49,15 +49,17 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { } tree match { - case Apply(Select(Quoted(quotedTree), _), _) if quotedTree.isType => - dropEmptyBlocks(quotedTree) match - case SplicedType(t) => - // Optimization: `quoted.Type.of[x.Underlying]` --> `x` - transform(t) - case _ => - super.transform(tree) + case Apply(Select(QuotedTypeOf(SplicedType(t)), _), _) => + // Optimization: `quoted.Type.of[x.Underlying]` --> `x` + transform(t) + + case tree @ QuotedTypeOf(quotedTree) => + val old = inQuoteOrSplice + inQuoteOrSplice = true + try transformQuotation(quotedTree, tree) + finally inQuoteOrSplice = old - case tree @ Quoted(quotedTree) => + case tree @ QuotedExpr(quotedTree) => val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(quotedTree) match { @@ -73,7 +75,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(splicedTree) match { - case Quoted(t) => + case QuotedExpr(t) => // Optimization: `${ 'x }` --> `x` transform(t) case _ => transformSplice(splicedTree, tree) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index cd41fb358d30..11e88585f144 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -44,7 +44,7 @@ object Splicer { * See: `Staging` */ def splice(tree: Tree, splicePos: SrcPos, spliceExpansionPos: SrcPos, classLoader: ClassLoader)(using Context): Tree = tree match { - case Quoted(quotedTree) => quotedTree + case QuotedExpr(quotedTree) => quotedTree case _ => val macroOwner = newSymbol(ctx.owner, nme.MACROkw, Macro | Synthetic, defn.AnyType, coord = tree.span) try @@ -136,7 +136,7 @@ object Splicer { * See: `Staging` */ def checkValidMacroBody(tree: Tree)(using Context): Unit = tree match { - case Quoted(_) => // ok + case QuotedExpr(_) => // ok case _ => type Env = Set[Symbol] diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 3e05310d7249..5db7c61aebc0 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -312,7 +312,7 @@ object SpaceEngine { def isIrrefutableQuotedPattern(unapp: tpd.Tree, implicits: List[tpd.Tree], pt: Type)(using Context): Boolean = { implicits.headOption match // pattern '{ $x: T } - case Some(tpd.Apply(tpd.Select(tpd.Quoted(tpd.TypeApply(fn, List(tpt))), nme.apply), _)) + case Some(tpd.Apply(tpd.Select(tpd.QuotedExpr(tpd.TypeApply(fn, List(tpt))), nme.apply), _)) if unapp.symbol.owner.eq(defn.QuoteMatching_ExprMatchModule) && fn.symbol.eq(defn.QuotedRuntimePatterns_patternHole) => pt <:< defn.QuotedExprClass.typeRef.appliedTo(tpt.tpe) From b7ca9b1e051c5f3dbca6eb07b226b31f5492ba69 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 21 Apr 2023 10:28:01 +0200 Subject: [PATCH 03/46] Split `transformQuotation` into `transformQuotedExpr` and `transformQuotedType` --- .../tools/dotc/staging/CrossStageSafety.scala | 67 ++++++++++--------- .../dotc/staging/TreeMapWithStages.scala | 20 +++--- 2 files changed, 46 insertions(+), 41 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 400a846194e7..9dc12d553abf 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -98,46 +98,47 @@ class CrossStageSafety extends TreeMapWithStages { } /** Transform quoted trees while maintaining level correctness */ - override protected def transformQuotation(body: Tree, quote: Apply)(using Context): Tree = { - val taggedTypes = new QuoteTypeTags(quote.span) - + override protected def transformQuotedExpr(body: Tree, quote: Apply)(using Context): Tree = { if (ctx.property(InAnnotation).isDefined) report.error("Cannot have a quote in an annotation", quote.srcPos) val stripAnnotsDeep: TypeMap = new TypeMap: def apply(tp: Type): Type = mapOver(tp.stripAnnots) + val transformedBody = transformQuoteBody(body, quote) + // `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T2]()` + val TypeApply(fun, targs) = quote.fun: @unchecked + val targs2 = targs.map(targ => TypeTree(healType(quote.fun.srcPos)(stripAnnotsDeep(targ.tpe)))) + cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, targs2), transformedBody :: Nil) + } - def transformBody() = - val contextWithQuote = - if level == 0 then contextWithQuoteTypeTags(taggedTypes)(using quoteContext) - else quoteContext - val transformedBody = transform(body)(using contextWithQuote) - taggedTypes.getTypeTags match - case Nil => transformedBody - case tags => tpd.Block(tags, transformedBody).withSpan(body.span) - - if body.isTerm then - val transformedBody = transformBody() - // `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T2]()` - val TypeApply(fun, targs) = quote.fun: @unchecked - val targs2 = targs.map(targ => TypeTree(healType(quote.fun.srcPos)(stripAnnotsDeep(targ.tpe)))) - cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, targs2), transformedBody :: Nil) - else - body.tpe match - case DirectTypeOf(termRef) => - // Optimization: `quoted.Type.of[x.Underlying](quotes)` --> `x` - ref(termRef).withSpan(quote.span) - case _ => - transformBody() match - case DirectTypeOf.Healed(termRef) => - // Optimization: `quoted.Type.of[@SplicedType type T = x.Underlying; T](quotes)` --> `x` - ref(termRef).withSpan(quote.span) - case transformedBody => - val quotes = quote.args.mapConserve(transform) - // `quoted.Type.of[](quotes)` --> `quoted.Type.of[](quotes)` - val TypeApply(fun, _) = quote.fun: @unchecked - cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, transformedBody :: Nil), quotes) + override protected def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = { + if (ctx.property(InAnnotation).isDefined) + report.error("Cannot have a quote in an annotation", quote.srcPos) + body.tpe match + case DirectTypeOf(termRef) => + // Optimization: `quoted.Type.of[x.Underlying](quotes)` --> `x` + ref(termRef).withSpan(quote.span) + case _ => + transformQuoteBody(body, quote) match + case DirectTypeOf.Healed(termRef) => + // Optimization: `quoted.Type.of[@SplicedType type T = x.Underlying; T](quotes)` --> `x` + ref(termRef).withSpan(quote.span) + case transformedBody => + val quotes = quote.args.mapConserve(transform) + // `quoted.Type.of[](quotes)` --> `quoted.Type.of[](quotes)` + val TypeApply(fun, _) = quote.fun: @unchecked + cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, transformedBody :: Nil), quotes) + } + private def transformQuoteBody(body: Tree, quote: Apply)(using Context): Tree = { + val taggedTypes = new QuoteTypeTags(quote.span) + val contextWithQuote = + if level == 0 then contextWithQuoteTypeTags(taggedTypes)(using quoteContext) + else quoteContext + val transformedBody = transform(body)(using contextWithQuote) + taggedTypes.getTypeTags match + case Nil => transformedBody + case tags => tpd.Block(tags, transformedBody).withSpan(body.span) } /** Transform splice diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index 096454de9a7f..c88a6aafebe6 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -24,14 +24,17 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { /** Transform the quote `quote` which contains the quoted `body`. * * - `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T]()` + */ + protected def transformQuotedExpr(body: Tree, quote: Apply)(using Context): Tree = + cpy.Apply(quote)(quote.fun, body :: Nil) + + /** Transform the quote `quote` which contains the quoted `body`. + * * - `quoted.Type.of[](quotes)` --> `quoted.Type.of[](quotes)` */ - protected def transformQuotation(body: Tree, quote: Apply)(using Context): Tree = - if body.isTerm then - cpy.Apply(quote)(quote.fun, body :: Nil) - else - val TypeApply(fun, _) = quote.fun: @unchecked - cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, body :: Nil), quote.args) + protected def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = + val TypeApply(fun, _) = quote.fun: @unchecked + cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, body :: Nil), quote.args) /** Transform the expression splice `splice` which contains the spliced `body`. */ protected def transformSplice(body: Tree, splice: Apply)(using Context): Tree @@ -56,7 +59,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { case tree @ QuotedTypeOf(quotedTree) => val old = inQuoteOrSplice inQuoteOrSplice = true - try transformQuotation(quotedTree, tree) + try transformQuotedType(quotedTree, tree) finally inQuoteOrSplice = old case tree @ QuotedExpr(quotedTree) => @@ -67,7 +70,8 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { // Optimization: `'{ $x }` --> `x` // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]` transform(t).asInstance(tree.tpe) - case _ => transformQuotation(quotedTree, tree) + case _ => + transformQuotedExpr(quotedTree, tree) } finally inQuoteOrSplice = old From deb03a7191f5523b8fc787edb8c0cec5022da21a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 21 Apr 2023 13:52:40 +0200 Subject: [PATCH 04/46] Add `QuotedExpr` to encode quotes directly in the AST --- .../dotty/tools/dotc/CompilationUnit.scala | 6 ++-- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 13 --------- compiler/src/dotty/tools/dotc/ast/Trees.scala | 14 ++++++++++ compiler/src/dotty/tools/dotc/ast/tpd.scala | 3 ++ compiler/src/dotty/tools/dotc/ast/untpd.scala | 1 + .../tools/dotc/core/tasty/TreePickler.scala | 8 ++++++ .../tools/dotc/core/tasty/TreeUnpickler.scala | 5 ++++ .../dotty/tools/dotc/inlines/Inliner.scala | 10 +++++-- .../dotc/inlines/PrepareInlineable.scala | 3 +- .../tools/dotc/printing/RefinedPrinter.scala | 5 ++-- .../tools/dotc/staging/CrossStageSafety.scala | 17 +++++------ .../dotc/staging/TreeMapWithStages.scala | 8 +++--- .../dotty/tools/dotc/transform/Inlining.scala | 8 ++++-- .../tools/dotc/transform/PickleQuotes.scala | 9 +++--- .../tools/dotc/transform/PostTyper.scala | 5 +++- .../dotty/tools/dotc/transform/Splicer.scala | 24 ++++++++-------- .../dotty/tools/dotc/transform/Splicing.scala | 28 ++++++++----------- .../dotty/tools/dotc/transform/SymUtils.scala | 4 --- .../tools/dotc/transform/patmat/Space.scala | 2 +- .../tools/dotc/typer/QuotesAndSplices.scala | 7 +++-- .../dotty/tools/dotc/typer/TypeAssigner.scala | 6 ++++ .../src/dotty/tools/dotc/typer/Typer.scala | 7 +++++ 22 files changed, 115 insertions(+), 78 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index 046b649941b1..897befdda4a5 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -154,11 +154,13 @@ object CompilationUnit { var containsCaptureChecking = false var containsMacroAnnotation = false def traverse(tree: Tree)(using Context): Unit = { - if (tree.symbol.isQuote) - containsQuote = true if tree.symbol.is(Flags.Inline) then containsInline = true tree match + case tpd.QuotedExpr(_, _) => + containsQuote = true + case tree: tpd.Apply if tree.symbol == defn.QuotedTypeModule_of => + containsQuote = true case Import(qual, selectors) => tpd.languageImport(qual) match case Some(prefix) => diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index f2a49254678f..96f189425180 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -1026,19 +1026,6 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => case t => assert(t.span.exists, i"$t") } - /** Extractors for quotes */ - object QuotedExpr { - /** Extracts the content of a quoted tree. - * The result can be the contents of a term or type quote, which - * will return a term or type tree respectively. - */ - def unapply(tree: tpd.Apply)(using Context): Option[tpd.Tree] = - if tree.symbol == defn.QuotedRuntime_exprQuote then - // quoted.runtime.Expr.quote[T]() - Some(tree.args.head) - else None - } - object QuotedTypeOf { /** Extracts the content of a quoted tree. * The result can be the contents of a term or type quote, which diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 1c1e80922c05..0028cf21c244 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -677,6 +677,11 @@ object Trees { override def isType = expansion.isType } + case class QuotedExpr[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) + extends TermTree[T] { + type ThisTree[+T <: Untyped] = QuotedExpr[T] + } + /** A type tree that represents an existing or inferred type */ case class TypeTree[+T <: Untyped]()(implicit @constructorOnly src: SourceFile) extends DenotingTree[T] with TypTree[T] { @@ -1087,6 +1092,7 @@ object Trees { type SeqLiteral = Trees.SeqLiteral[T] type JavaSeqLiteral = Trees.JavaSeqLiteral[T] type Inlined = Trees.Inlined[T] + type QuotedExpr = Trees.QuotedExpr[T] type TypeTree = Trees.TypeTree[T] type InferredTypeTree = Trees.InferredTypeTree[T] type SingletonTypeTree = Trees.SingletonTypeTree[T] @@ -1257,6 +1263,10 @@ object Trees { case tree: Inlined if (call eq tree.call) && (bindings eq tree.bindings) && (expansion eq tree.expansion) => tree case _ => finalize(tree, untpd.Inlined(call, bindings, expansion)(sourceFile(tree))) } + def QuotedExpr(tree: Tree)(expr: Tree, tpt: Tree)(using Context): QuotedExpr = tree match { + case tree: QuotedExpr if (expr eq tree.expr) && (tpt eq tree.tpt) => tree + case _ => finalize(tree, untpd.QuotedExpr(expr, tpt)(sourceFile(tree))) + } def SingletonTypeTree(tree: Tree)(ref: Tree)(using Context): SingletonTypeTree = tree match { case tree: SingletonTypeTree if (ref eq tree.ref) => tree case _ => finalize(tree, untpd.SingletonTypeTree(ref)(sourceFile(tree))) @@ -1494,6 +1504,8 @@ object Trees { case Thicket(trees) => val trees1 = transform(trees) if (trees1 eq trees) tree else Thicket(trees1) + case tree @ QuotedExpr(expr, tpt) => + cpy.QuotedExpr(tree)(transform(expr), transform(tpt)) case tree @ Hole(_, _, args, content, tpt) => cpy.Hole(tree)(args = transform(args), content = transform(content), tpt = transform(tpt)) case _ => @@ -1635,6 +1647,8 @@ object Trees { this(this(x, arg), annot) case Thicket(ts) => this(x, ts) + case QuotedExpr(expr, tpt) => + this(this(x, expr), tpt) case Hole(_, _, args, content, tpt) => this(this(this(x, args), content), tpt) case _ => diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index c6e0bc581b59..88a822876043 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -170,6 +170,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Inlined(call: Tree, bindings: List[MemberDef], expansion: Tree)(using Context): Inlined = ta.assignType(untpd.Inlined(call, bindings, expansion), bindings, expansion) + def QuotedExpr(expr: Tree, tpt: Tree)(using Context): QuotedExpr = + ta.assignType(untpd.QuotedExpr(expr, tpt), tpt) + def TypeTree(tp: Type, inferred: Boolean = false)(using Context): TypeTree = (if inferred then untpd.InferredTypeTree() else untpd.TypeTree()).withType(tp) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index a262c3658399..7088739ddef3 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -401,6 +401,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def SeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): SeqLiteral = new SeqLiteral(elems, elemtpt) def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt) def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion) + def QuotedExpr(expr: Tree, tpt: Tree)(implicit src: SourceFile): QuotedExpr = new QuotedExpr(expr, tpt) def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree() def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree() def SingletonTypeTree(ref: Tree)(implicit src: SourceFile): SingletonTypeTree = new SingletonTypeTree(ref) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 8a396921f32b..d704c6eac771 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -665,6 +665,14 @@ class TreePickler(pickler: TastyPickler) { pickleTree(hi) pickleTree(alias) } + case QuotedExpr(expr, tpt) => + pickleTree( + // scala.quoted.runtime.Expr.quoted[]() + ref(defn.QuotedRuntime_exprQuote) + .appliedToTypeTree(tpt) + .appliedTo(expr) + .withSpan(tree.span) + ) case Hole(_, idx, args, _, tpt) => writeByte(HOLE) withLength { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 428047d96e0c..a982e389f468 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1267,6 +1267,10 @@ class TreeUnpickler(reader: TastyReader, res.withAttachment(SuppressedApplyToNone, ()) else res + def quotedExpr(fn: Tree, args: List[Tree]): Tree = + val TypeApply(_, targs) = fn: @unchecked + QuotedExpr(args.head, targs.head) + def simplifyLub(tree: Tree): Tree = tree.overwriteType(tree.tpe.simplified) tree @@ -1283,6 +1287,7 @@ class TreeUnpickler(reader: TastyReader, val fn = readTree() val args = until(end)(readTree()) if fn.symbol.isConstructor then constructorApply(fn, args) + else if fn.symbol == defn.QuotedRuntime_exprQuote then quotedExpr(fn, args) else tpd.Apply(fn, args) case TYPEAPPLY => tpd.TypeApply(readTree(), until(end)(readTpt())) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 50cd933532ae..e4c503206abf 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -817,7 +817,7 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedApply(tree: untpd.Apply, pt: Type)(using Context): Tree = def cancelQuotes(tree: Tree): Tree = tree match - case QuotedExpr(SplicedExpr(inner)) => inner + case QuotedExpr(SplicedExpr(inner), _) => inner case _ => tree val locked = ctx.typerState.ownedVars val res = cancelQuotes(constToLiteral(BetaReduce(super.typedApply(tree, pt)))) match { @@ -835,10 +835,14 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedTypeApply(tree: untpd.TypeApply, pt: Type)(using Context): Tree = val locked = ctx.typerState.ownedVars val tree1 = inlineIfNeeded(constToLiteral(BetaReduce(super.typedTypeApply(tree, pt))), pt, locked) - if tree1.symbol.isQuote then + if tree1.symbol == defn.QuotedTypeModule_of then ctx.compilationUnit.needsStaging = true tree1 + override def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = + ctx.compilationUnit.needsStaging = true + super.typedQuotedExpr(tree, pt) + override def typedMatch(tree: untpd.Match, pt: Type)(using Context): Tree = val tree1 = if tree.isInline then @@ -1067,7 +1071,7 @@ class Inliner(val call: tpd.Tree)(using Context): else tree match { case tree: RefTree if tree.isTerm && tree.symbol.isDefinedInCurrentRun && !tree.symbol.isLocal => foldOver(tree.symbol :: syms, tree) - case QuotedExpr(body) => + case QuotedExpr(body, _) => level += 1 try apply(syms, body) finally level -= 1 diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index c3aa98e6ef30..50c6a72a5b12 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -91,7 +91,8 @@ object PrepareInlineable { } private def stagingContext(tree: Tree)(using Context): Context = tree match - case tree: Apply if tree.symbol.isQuote => StagingLevel.quoteContext + case tree: QuotedExpr => StagingLevel.quoteContext + case tree: Apply if tree.symbol eq defn.QuotedTypeModule_of => StagingLevel.quoteContext case tree: Apply if tree.symbol.isExprSplice => StagingLevel.spliceContext case _ => ctx } diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 014e5ddf0d66..2f354259b33c 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -432,8 +432,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { changePrec (GlobalPrec) { keywordStr("throw ") ~ toText(args.head) } - else if (!printDebug && fun.hasType && fun.symbol == defn.QuotedRuntime_exprQuote) - keywordStr("'{") ~ toTextGlobal(args, ", ") ~ keywordStr("}") else if (!printDebug && fun.hasType && fun.symbol.isExprSplice) keywordStr("${") ~ toTextGlobal(args, ", ") ~ keywordStr("}") else @@ -724,6 +722,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { "Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}" case MacroTree(call) => keywordStr("macro ") ~ toTextGlobal(call) + case QuotedExpr(expr, tpt) => + val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug) + keywordStr("'") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") case Hole(isTermHole, idx, args, content, tpt) => val (prefix, postfix) = if isTermHole then ("{{{", "}}}") else ("[[[", "]]]") val argsText = toTextGlobal(args, ", ") diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 9dc12d553abf..e432baf9068c 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -98,17 +98,14 @@ class CrossStageSafety extends TreeMapWithStages { } /** Transform quoted trees while maintaining level correctness */ - override protected def transformQuotedExpr(body: Tree, quote: Apply)(using Context): Tree = { + override protected def transformQuotedExpr(body: Tree, quote: QuotedExpr)(using Context): Tree = { if (ctx.property(InAnnotation).isDefined) report.error("Cannot have a quote in an annotation", quote.srcPos) - + val transformedBody = transformQuoteBody(body, quote.span) val stripAnnotsDeep: TypeMap = new TypeMap: def apply(tp: Type): Type = mapOver(tp.stripAnnots) - val transformedBody = transformQuoteBody(body, quote) - // `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T2]()` - val TypeApply(fun, targs) = quote.fun: @unchecked - val targs2 = targs.map(targ => TypeTree(healType(quote.fun.srcPos)(stripAnnotsDeep(targ.tpe)))) - cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, targs2), transformedBody :: Nil) + val tpt1 = TypeTree(healType(quote.tpt.srcPos)(stripAnnotsDeep(quote.tpt.tpe))) + cpy.QuotedExpr(quote)(transformedBody, tpt1) } override protected def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = { @@ -119,7 +116,7 @@ class CrossStageSafety extends TreeMapWithStages { // Optimization: `quoted.Type.of[x.Underlying](quotes)` --> `x` ref(termRef).withSpan(quote.span) case _ => - transformQuoteBody(body, quote) match + transformQuoteBody(body, quote.span) match case DirectTypeOf.Healed(termRef) => // Optimization: `quoted.Type.of[@SplicedType type T = x.Underlying; T](quotes)` --> `x` ref(termRef).withSpan(quote.span) @@ -130,8 +127,8 @@ class CrossStageSafety extends TreeMapWithStages { cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, transformedBody :: Nil), quotes) } - private def transformQuoteBody(body: Tree, quote: Apply)(using Context): Tree = { - val taggedTypes = new QuoteTypeTags(quote.span) + private def transformQuoteBody(body: Tree, span: Span)(using Context): Tree = { + val taggedTypes = new QuoteTypeTags(span) val contextWithQuote = if level == 0 then contextWithQuoteTypeTags(taggedTypes)(using quoteContext) else quoteContext diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index c88a6aafebe6..a35fa363aa6b 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -25,8 +25,8 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { * * - `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T]()` */ - protected def transformQuotedExpr(body: Tree, quote: Apply)(using Context): Tree = - cpy.Apply(quote)(quote.fun, body :: Nil) + protected def transformQuotedExpr(body: Tree, quote: QuotedExpr)(using Context): Tree = + cpy.QuotedExpr(quote)(body, quote.tpt) /** Transform the quote `quote` which contains the quoted `body`. * @@ -62,7 +62,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { try transformQuotedType(quotedTree, tree) finally inQuoteOrSplice = old - case tree @ QuotedExpr(quotedTree) => + case tree @ QuotedExpr(quotedTree, _) => val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(quotedTree) match { @@ -79,7 +79,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(splicedTree) match { - case QuotedExpr(t) => + case QuotedExpr(t, _) => // Optimization: `${ 'x }` --> `x` transform(t) case _ => transformSplice(splicedTree, tree) diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index d6b7f3141b96..a7644fa2a528 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -46,7 +46,9 @@ class Inlining extends MacroTransform { new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match - case _: GenericApply if tree.symbol.isQuote => + case _: QuotedExpr => + traverseChildren(tree)(using StagingLevel.quoteContext) + case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of => traverseChildren(tree)(using StagingLevel.quoteContext) case _: GenericApply if tree.symbol.isExprSplice => traverseChildren(tree)(using StagingLevel.spliceContext) @@ -98,7 +100,9 @@ class Inlining extends MacroTransform { val tree1 = super.transform(tree) if tree1.tpe.isError then tree1 else Inlines.inlineCall(tree1) - case _: GenericApply if tree.symbol.isQuote => + case _: QuotedExpr => + super.transform(tree)(using StagingLevel.quoteContext) + case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of => super.transform(tree)(using StagingLevel.quoteContext) case _: GenericApply if tree.symbol.isExprSplice => super.transform(tree)(using StagingLevel.spliceContext) diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 62174c806f09..6507bed179c3 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -83,8 +83,10 @@ class PickleQuotes extends MacroTransform { override def checkPostCondition(tree: Tree)(using Context): Unit = tree match + case tree: QuotedExpr => + assert(Inlines.inInlineMethod) case tree: RefTree if !Inlines.inInlineMethod => - assert(!tree.symbol.isQuote) + assert(tree.symbol != defn.QuotedTypeModule_of) assert(!tree.symbol.isExprSplice) case _ : TypeDef if !Inlines.inInlineMethod => assert(!tree.symbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot), @@ -97,9 +99,8 @@ class PickleQuotes extends MacroTransform { protected def newTransformer(using Context): Transformer = new Transformer { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match - case Apply(Select(Apply(TypeApply(fn, List(tpt)), List(code)),nme.apply), List(quotes)) - if fn.symbol == defn.QuotedRuntime_exprQuote => - val (contents, codeWithHoles) = makeHoles(code) + case Apply(Select(QuotedExpr(expr, tpt), nme.apply), List(quotes)) => + val (contents, codeWithHoles) = makeHoles(expr) val sourceRef = Inlines.inlineCallTrace(ctx.owner, tree.sourcePos) val codeWithHoles2 = Inlined(sourceRef, Nil, codeWithHoles) val pickled = PickleQuotes(quotes, codeWithHoles2, contents, tpt.tpe, false) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 7f3e47c14732..5cfa74fd5610 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -342,7 +342,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase val patterns1 = transform(patterns) cpy.UnApply(tree)(transform(fun), transform(implicits), patterns1) case tree: TypeApply => - if tree.symbol.isQuote then + if tree.symbol == defn.QuotedTypeModule_of then ctx.compilationUnit.needsStaging = true if tree.symbol.is(Inline) then ctx.compilationUnit.needsInlining = true @@ -485,6 +485,9 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase ) case Block(_, Closure(_, _, tpt)) if ExpandSAMs.needsWrapperClass(tpt.tpe) => superAcc.withInvalidCurrentClass(super.transform(tree)) + case QuotedExpr(expr, _) => + ctx.compilationUnit.needsStaging = true + super.transform(tree) case tree => super.transform(tree) } diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 11e88585f144..2981bdafc958 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -44,7 +44,7 @@ object Splicer { * See: `Staging` */ def splice(tree: Tree, splicePos: SrcPos, spliceExpansionPos: SrcPos, classLoader: ClassLoader)(using Context): Tree = tree match { - case QuotedExpr(quotedTree) => quotedTree + case QuotedExpr(quotedTree, _) => quotedTree case _ => val macroOwner = newSymbol(ctx.owner, nme.MACROkw, Macro | Synthetic, defn.AnyType, coord = tree.span) try @@ -136,7 +136,7 @@ object Splicer { * See: `Staging` */ def checkValidMacroBody(tree: Tree)(using Context): Unit = tree match { - case QuotedExpr(_) => // ok + case QuotedExpr(_, _) => // ok case _ => type Env = Set[Symbol] @@ -155,7 +155,7 @@ object Splicer { case Block(Nil, expr) => checkIfValidArgument(expr) case Typed(expr, _) => checkIfValidArgument(expr) - case Apply(Select(Apply(fn, quoted :: Nil), nme.apply), _) if fn.symbol == defn.QuotedRuntime_exprQuote => + case Apply(Select(QuotedExpr(expr, tpt), nme.apply), _) => val noSpliceChecker = new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match case SplicedExpr(_) => @@ -163,7 +163,7 @@ object Splicer { case _ => traverseChildren(tree) } - noSpliceChecker.traverse(quoted) + noSpliceChecker.traverse(expr) case Apply(TypeApply(fn, List(quoted)), _)if fn.symbol == defn.QuotedTypeModule_of => // OK @@ -203,7 +203,7 @@ object Splicer { case Typed(expr, _) => checkIfValidStaticCall(expr) - case Apply(Select(Apply(fn, quoted :: Nil), nme.apply), _) if fn.symbol == defn.QuotedRuntime_exprQuote => + case Apply(Select(QuotedExpr(quoted, tpt), nme.apply), _) => // OK, canceled and warning emitted case Call(fn, args) @@ -240,15 +240,15 @@ object Splicer { override protected def interpretTree(tree: Tree)(implicit env: Env): Object = tree match { // Interpret level -1 quoted code `'{...}` (assumed without level 0 splices) - case Apply(Select(Apply(TypeApply(fn, _), quoted :: Nil), nme.apply), _) if fn.symbol == defn.QuotedRuntime_exprQuote => - val quoted1 = quoted match { - case quoted: Ident if quoted.symbol.isAllOf(InlineByNameProxy) => + case Apply(Select(QuotedExpr(expr, _), nme.apply), _) => + val expr1 = expr match { + case expr: Ident if expr.symbol.isAllOf(InlineByNameProxy) => // inline proxy for by-name parameter - quoted.symbol.defTree.asInstanceOf[DefDef].rhs - case Inlined(EmptyTree, _, quoted) => quoted - case _ => quoted + expr.symbol.defTree.asInstanceOf[DefDef].rhs + case Inlined(EmptyTree, _, expr1) => expr1 + case _ => expr } - new ExprImpl(Inlined(EmptyTree, Nil, QuoteUtils.changeOwnerOfTree(quoted1, ctx.owner)).withSpan(quoted1.span), SpliceScope.getCurrent) + new ExprImpl(Inlined(EmptyTree, Nil, QuoteUtils.changeOwnerOfTree(expr1, ctx.owner)).withSpan(expr1.span), SpliceScope.getCurrent) // Interpret level -1 `Type.of[T]` case Apply(TypeApply(fn, quoted :: Nil), _) if fn.symbol == defn.QuotedTypeModule_of => diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index e959f5d1451d..f5e536d85a55 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -87,8 +87,7 @@ class Splicing extends MacroTransform: override def transform(tree: tpd.Tree)(using Context): tpd.Tree = assert(level == 0) tree match - case Apply(Select(Apply(TypeApply(fn,_), List(code)),nme.apply),List(quotes)) - if fn.symbol == defn.QuotedRuntime_exprQuote => + case Apply(Select(QuotedExpr(expr, _), nme.apply),List(quotes)) => QuoteTransformer().transform(tree) case TypeApply(_, _) if tree.symbol == defn.QuotedTypeModule_of => QuoteTransformer().transform(tree) @@ -136,8 +135,7 @@ class Splicing extends MacroTransform: typeHoles.put(qual, hole) hole cpy.TypeDef(tree)(rhs = hole) - case Apply(Select(Apply(TypeApply(fn,_), List(code)),nme.apply),List(quotes)) - if fn.symbol == defn.QuotedRuntime_exprQuote => + case Apply(Select(QuotedExpr(expr, tpt), nme.apply),List(quotes)) => super.transform(tree)(using quoteContext) case _: Template => for sym <- tree.symbol.owner.info.decls do @@ -240,17 +238,16 @@ class Splicing extends MacroTransform: case Apply(fn, args) if fn.symbol == defn.QuotedRuntime_exprNestedSplice => val newArgs = args.mapConserve(arg => transform(arg)(using spliceContext)) cpy.Apply(tree)(fn, newArgs) - case Apply(sel @ Select(app @ Apply(fn, args),nme.apply), quotesArgs) - if fn.symbol == defn.QuotedRuntime_exprQuote => - args match - case List(tree: RefTree) if isCaptured(tree.symbol) => - capturedTerm(tree) + case Apply(sel @ Select(app @ QuotedExpr(expr, tpt), nme.apply), quotesArgs) => + expr match + case expr: RefTree if isCaptured(expr.symbol) => + capturedTerm(expr) case _ => - val newArgs = withCurrentQuote(quotesArgs.head) { - if level > 1 then args.mapConserve(arg => transform(arg)(using quoteContext)) - else args.mapConserve(arg => transformLevel0QuoteContent(arg)(using quoteContext)) + val newExpr = withCurrentQuote(quotesArgs.head) { + if level > 1 then transform(expr)(using quoteContext) + else transformLevel0QuoteContent(expr)(using quoteContext) } - cpy.Apply(tree)(cpy.Select(sel)(cpy.Apply(app)(fn, newArgs), nme.apply), quotesArgs) + cpy.Apply(tree)(cpy.Select(sel)(cpy.QuotedExpr(app)(newExpr, tpt), nme.apply), quotesArgs) case Apply(TypeApply(typeof, List(tpt)), List(quotes)) if tree.symbol == defn.QuotedTypeModule_of && containsCapturedType(tpt.tpe) => val newContent = capturedPartTypes(tpt) @@ -419,10 +416,7 @@ class Splicing extends MacroTransform: .appliedTo(closure) private def quoted(expr: Tree)(using Context): Tree = - val tpe = expr.tpe.widenTermRefExpr - ref(defn.QuotedRuntime_exprQuote) - .appliedToType(tpe) - .appliedTo(expr) + QuotedExpr(expr, TypeTree(expr.tpe.widenTermRefExpr)) .select(nme.apply) .appliedTo(quotes.nn) diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index b945f5820523..a5844dc0b538 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -315,10 +315,6 @@ object SymUtils: def reachableRawTypeRef(using Context) = self.reachableTypeRef.appliedTo(self.typeParams.map(_ => TypeBounds.emptyPolyKind)) - /** Is symbol a quote operation? */ - def isQuote(using Context): Boolean = - self == defn.QuotedRuntime_exprQuote || self == defn.QuotedTypeModule_of - /** Is symbol a term splice operation? */ def isExprSplice(using Context): Boolean = self == defn.QuotedRuntime_exprSplice || self == defn.QuotedRuntime_exprNestedSplice diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 5db7c61aebc0..f7518ec8b9f5 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -312,7 +312,7 @@ object SpaceEngine { def isIrrefutableQuotedPattern(unapp: tpd.Tree, implicits: List[tpd.Tree], pt: Type)(using Context): Boolean = { implicits.headOption match // pattern '{ $x: T } - case Some(tpd.Apply(tpd.Select(tpd.QuotedExpr(tpd.TypeApply(fn, List(tpt))), nme.apply), _)) + case Some(tpd.Apply(tpd.Select(tpd.QuotedExpr(tpd.TypeApply(fn, List(tpt)), _), nme.apply), _)) if unapp.symbol.owner.eq(defn.QuoteMatching_ExprMatchModule) && fn.symbol.eq(defn.QuotedRuntimePatterns_patternHole) => pt <:< defn.QuotedExprClass.typeRef.appliedTo(tpt.tpe) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 8473bd168bc5..e8e0ad7cf4a2 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -60,7 +60,9 @@ trait QuotesAndSplices { EmptyTree else val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.quoted) - makeInlineable(typedApply(exprQuoteTree, pt)(using pushQuotes(qctx)).select(nme.apply).appliedTo(qctx).withSpan(tree.span)) + val quotedExpr = typedApply(exprQuoteTree, pt)(using pushQuotes(qctx)) match + case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => QuotedExpr(quotedExpr, tpt) + makeInlineable(quotedExpr.select(nme.apply).appliedTo(qctx).withSpan(tree.span)) } private def makeInlineable(tree: Tree)(using Context): Tree = @@ -75,6 +77,7 @@ trait QuotesAndSplices { tree.expr match { case untpd.Quote(innerExpr) if innerExpr.isTerm => report.warning("Canceled quote directly inside a splice. ${ '{ XYZ } } is equivalent to XYZ.", tree.srcPos) + return typed(innerExpr, pt) case _ => } if (ctx.mode.is(Mode.QuotedPattern)) @@ -441,7 +444,7 @@ trait QuotesAndSplices { val quoteClass = if (tree.quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass val quotedPattern = - if (tree.quoted.isTerm) ref(defn.QuotedRuntime_exprQuote.termRef).appliedToType(defn.AnyType).appliedTo(shape).select(nme.apply).appliedTo(qctx) + if (tree.quoted.isTerm) tpd.QuotedExpr(shape, TypeTree(defn.AnyType)).select(nme.apply).appliedTo(qctx) else ref(defn.QuotedTypeModule_of.termRef).appliedToTypeTree(shape).appliedTo(qctx) val matchModule = if tree.quoted.isTerm then defn.QuoteMatching_ExprMatch else defn.QuoteMatching_TypeMatch diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 6ac45cbcf04d..2d921f4dbcb1 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -392,6 +392,12 @@ trait TypeAssigner { def assignType(tree: untpd.Inlined, bindings: List[Tree], expansion: Tree)(using Context): Inlined = tree.withType(avoidingType(expansion, bindings)) + def assignType(tree: untpd.QuotedExpr, tpt: Tree)(using Context): QuotedExpr = + val lambdaType = // Quotes ?=> Expr[T] + defn.FunctionType(1, isContextual = true) + .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt.tpe)) + tree.withType(lambdaType) + def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(using Context): If = tree.withType(thenp.tpe | elsep.tpe) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 4bc012b5b226..ddf16f213676 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2045,6 +2045,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer bindings1, expansion1) } + def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = + val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) + val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem) + assignType(cpy.QuotedExpr(tree)(expr1, tpt1), tpt1) + .withNotNullInfo(expr1.notNullInfo) + def completeTypeTree(tree: untpd.TypeTree, pt: Type, original: untpd.Tree)(using Context): TypeTree = tree.withSpan(original.span).withAttachmentsFrom(original) .withType( @@ -3076,6 +3082,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case tree @ untpd.PostfixOp(qual, Ident(nme.WILDCARD)) => typedAsFunction(tree, pt) case untpd.EmptyTree => tpd.EmptyTree case tree: untpd.Quote => typedQuote(tree, pt) + case tree: untpd.QuotedExpr => typedQuotedExpr(tree, pt) case tree: untpd.Splice => typedSplice(tree, pt) case tree: untpd.MacroTree => report.error("Unexpected macro", tree.srcPos); tpd.nullLiteral // ill-formed code may reach here case tree: untpd.Hole => typedHole(tree, pt) From b7a7227e55558de2c483731d34b829d2d4018203 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 21 Apr 2023 17:28:25 +0200 Subject: [PATCH 05/46] Add `SplicedExpr` to encode splices directly in the AST --- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 10 ------ compiler/src/dotty/tools/dotc/ast/Trees.scala | 16 +++++++++ compiler/src/dotty/tools/dotc/ast/tpd.scala | 3 ++ compiler/src/dotty/tools/dotc/ast/untpd.scala | 1 + .../tools/dotc/core/tasty/TreePickler.scala | 17 +++++++++ .../tools/dotc/core/tasty/TreeUnpickler.scala | 13 +++++++ .../dotty/tools/dotc/inlines/Inliner.scala | 35 +++++++++---------- .../dotc/inlines/PrepareInlineable.scala | 6 ++-- .../tools/dotc/printing/RefinedPrinter.scala | 6 ++-- .../tools/dotc/staging/CrossStageSafety.scala | 20 +++++------ .../dotc/staging/TreeMapWithStages.scala | 9 ++--- .../tools/dotc/transform/ElimByName.scala | 6 ++++ .../tools/dotc/transform/FirstTransform.scala | 3 ++ .../dotty/tools/dotc/transform/Inlining.scala | 4 +-- .../tools/dotc/transform/PickleQuotes.scala | 3 +- .../dotty/tools/dotc/transform/Splicer.scala | 2 +- .../dotty/tools/dotc/transform/Splicing.scala | 19 +++++----- .../dotty/tools/dotc/transform/SymUtils.scala | 4 --- .../tools/dotc/typer/QuotesAndSplices.scala | 18 ++++++---- .../dotty/tools/dotc/typer/TypeAssigner.scala | 3 ++ .../src/dotty/tools/dotc/typer/Typer.scala | 11 +++++- 21 files changed, 134 insertions(+), 75 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 96f189425180..5b409b6fec9a 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -1039,16 +1039,6 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => else None } - /** Extractors for splices */ - object SplicedExpr { - /** Extracts the content of a spliced expression tree. - * The result can be the contents of a term splice, which - * will return a term tree. - */ - def unapply(tree: tpd.Apply)(using Context): Option[tpd.Tree] = - if tree.symbol.isExprSplice then Some(tree.args.head) else None - } - /** Extractors for type splices */ object SplicedType { /** Extracts the content of a spliced type tree. diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 0028cf21c244..63e4ab859899 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -682,6 +682,11 @@ object Trees { type ThisTree[+T <: Untyped] = QuotedExpr[T] } + case class SplicedExpr[+T <: Untyped] private[ast] (spliced: Tree[T], tpt: Tree[T], outerQuotes: Tree[T])(implicit @constructorOnly src: SourceFile) + extends TermTree[T] { + type ThisTree[+T <: Untyped] = SplicedExpr[T] + } + /** A type tree that represents an existing or inferred type */ case class TypeTree[+T <: Untyped]()(implicit @constructorOnly src: SourceFile) extends DenotingTree[T] with TypTree[T] { @@ -1093,6 +1098,7 @@ object Trees { type JavaSeqLiteral = Trees.JavaSeqLiteral[T] type Inlined = Trees.Inlined[T] type QuotedExpr = Trees.QuotedExpr[T] + type SplicedExpr = Trees.SplicedExpr[T] type TypeTree = Trees.TypeTree[T] type InferredTypeTree = Trees.InferredTypeTree[T] type SingletonTypeTree = Trees.SingletonTypeTree[T] @@ -1267,6 +1273,10 @@ object Trees { case tree: QuotedExpr if (expr eq tree.expr) && (tpt eq tree.tpt) => tree case _ => finalize(tree, untpd.QuotedExpr(expr, tpt)(sourceFile(tree))) } + def SplicedExpr(tree: Tree)(spliced: Tree, tpt: Tree, outerQuotes: Tree)(using Context): SplicedExpr = tree match { + case tree: SplicedExpr if (spliced eq tree.spliced) && (tpt eq tree.tpt) && (outerQuotes eq tree.outerQuotes) => tree + case _ => finalize(tree, untpd.SplicedExpr(spliced, tpt, outerQuotes)(sourceFile(tree))) + } def SingletonTypeTree(tree: Tree)(ref: Tree)(using Context): SingletonTypeTree = tree match { case tree: SingletonTypeTree if (ref eq tree.ref) => tree case _ => finalize(tree, untpd.SingletonTypeTree(ref)(sourceFile(tree))) @@ -1372,6 +1382,8 @@ object Trees { TypeDef(tree: Tree)(name, rhs) def Template(tree: Template)(using Context)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, derived: List[untpd.Tree] = tree.derived, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody): Template = Template(tree: Tree)(constr, parents, derived, self, body) + def SplicedExpr(tree: SplicedExpr)(spliced: Tree = tree.spliced, tpt: Tree = tree.tpt, outerQuotes: Tree = tree.outerQuotes)(using Context): SplicedExpr = + SplicedExpr(tree: Tree)(spliced, tpt, outerQuotes) def Hole(tree: Hole)(isTerm: Boolean = tree.isTerm, idx: Int = tree.idx, args: List[Tree] = tree.args, content: Tree = tree.content, tpt: Tree = tree.tpt)(using Context): Hole = Hole(tree: Tree)(isTerm, idx, args, content, tpt) @@ -1506,6 +1518,8 @@ object Trees { if (trees1 eq trees) tree else Thicket(trees1) case tree @ QuotedExpr(expr, tpt) => cpy.QuotedExpr(tree)(transform(expr), transform(tpt)) + case tree @ SplicedExpr(spliced, tpt, outerQuotes) => + cpy.SplicedExpr(tree)(transform(spliced), transform(tpt), transform(outerQuotes)) case tree @ Hole(_, _, args, content, tpt) => cpy.Hole(tree)(args = transform(args), content = transform(content), tpt = transform(tpt)) case _ => @@ -1649,6 +1663,8 @@ object Trees { this(x, ts) case QuotedExpr(expr, tpt) => this(this(x, expr), tpt) + case SplicedExpr(spliced, tpt, outerQuotes) => + this(this(this(x, spliced), tpt), outerQuotes) case Hole(_, _, args, content, tpt) => this(this(this(x, args), content), tpt) case _ => diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 88a822876043..e7c3ba93f72c 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -173,6 +173,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def QuotedExpr(expr: Tree, tpt: Tree)(using Context): QuotedExpr = ta.assignType(untpd.QuotedExpr(expr, tpt), tpt) + def SplicedExpr(spliced: Tree, tpt: Tree, outerQuotes: Tree)(using Context): SplicedExpr = + ta.assignType(untpd.SplicedExpr(spliced, tpt, outerQuotes), tpt) + def TypeTree(tp: Type, inferred: Boolean = false)(using Context): TypeTree = (if inferred then untpd.InferredTypeTree() else untpd.TypeTree()).withType(tp) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 7088739ddef3..b63df5f6999b 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -402,6 +402,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt) def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion) def QuotedExpr(expr: Tree, tpt: Tree)(implicit src: SourceFile): QuotedExpr = new QuotedExpr(expr, tpt) + def SplicedExpr(spliced: Tree, tpt: Tree, outerQuotes: Tree)(implicit src: SourceFile): SplicedExpr = new SplicedExpr(spliced, tpt, outerQuotes) def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree() def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree() def SingletonTypeTree(ref: Tree)(implicit src: SourceFile): SingletonTypeTree = new SingletonTypeTree(ref) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index d704c6eac771..a57ce2c00a5d 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -673,6 +673,23 @@ class TreePickler(pickler: TastyPickler) { .appliedTo(expr) .withSpan(tree.span) ) + case SplicedExpr(spliced, tpt, EmptyTree) => + pickleTree( + // scala.quoted.runtime.Expr.splice[]() + ref(defn.QuotedRuntime_exprSplice) + .appliedToTypeTree(tpt) + .appliedTo(spliced) + .withSpan(tree.span) + ) + case SplicedExpr(spliced, tpt, quotes) => + pickleTree( + // scala.quoted.runtime.Expr.nestedSplice[]()() + ref(defn.QuotedRuntime_exprNestedSplice) + .appliedToTypeTree(tpt) + .appliedTo(quotes) + .appliedTo(spliced) + .withSpan(tree.span) + ) case Hole(_, idx, args, _, tpt) => writeByte(HOLE) withLength { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index a982e389f468..eb0dbdbc8bbb 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1271,6 +1271,17 @@ class TreeUnpickler(reader: TastyReader, val TypeApply(_, targs) = fn: @unchecked QuotedExpr(args.head, targs.head) + def splicedExpr(fn: Tree, args: List[Tree]): Tree = + val TypeApply(_, targs) = fn: @unchecked + SplicedExpr(args.head, targs.head, EmptyTree) + + def nestedSpliceExpr(fn: Tree, args: List[Tree]): Tree = + fn match + case Apply(TypeApply(_, targs), outerQuotes :: Nil) => + SplicedExpr(args.head, targs.head, outerQuotes) + case _ => // nestedSplice[T](quotes) + tpd.Apply(fn, args) + def simplifyLub(tree: Tree): Tree = tree.overwriteType(tree.tpe.simplified) tree @@ -1288,6 +1299,8 @@ class TreeUnpickler(reader: TastyReader, val args = until(end)(readTree()) if fn.symbol.isConstructor then constructorApply(fn, args) else if fn.symbol == defn.QuotedRuntime_exprQuote then quotedExpr(fn, args) + else if fn.symbol == defn.QuotedRuntime_exprSplice then splicedExpr(fn, args) + else if fn.symbol == defn.QuotedRuntime_exprNestedSplice then nestedSpliceExpr(fn, args) else tpd.Apply(fn, args) case TYPEAPPLY => tpd.TypeApply(readTree(), until(end)(readTpt())) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index e4c503206abf..bb16cf7f84b3 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -815,22 +815,8 @@ class Inliner(val call: tpd.Tree)(using Context): super.typedValDef(vdef1, sym) override def typedApply(tree: untpd.Apply, pt: Type)(using Context): Tree = - def cancelQuotes(tree: Tree): Tree = - tree match - case QuotedExpr(SplicedExpr(inner), _) => inner - case _ => tree val locked = ctx.typerState.ownedVars - val res = cancelQuotes(constToLiteral(BetaReduce(super.typedApply(tree, pt)))) match { - case res: Apply if res.symbol == defn.QuotedRuntime_exprSplice - && StagingLevel.level == 0 - && !hasInliningErrors => - val expanded = expandMacro(res.args.head, tree.srcPos) - transform.TreeChecker.checkMacroGeneratedTree(res, expanded) - typedExpr(expanded) // Inline calls and constant fold code generated by the macro - case res => - specializeEq(inlineIfNeeded(res, pt, locked)) - } - res + specializeEq(inlineIfNeeded(constToLiteral(BetaReduce(super.typedApply(tree, pt))), pt, locked)) override def typedTypeApply(tree: untpd.TypeApply, pt: Type)(using Context): Tree = val locked = ctx.typerState.ownedVars @@ -840,8 +826,21 @@ class Inliner(val call: tpd.Tree)(using Context): tree1 override def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = - ctx.compilationUnit.needsStaging = true - super.typedQuotedExpr(tree, pt) + super.typedQuotedExpr(tree, pt) match + case QuotedExpr(SplicedExpr(inner, _, _), _) => inner + case tree1 => + ctx.compilationUnit.needsStaging = true + tree1 + + override def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = + super.typedSplicedExpr(tree, pt) match + case tree1 @ SplicedExpr(spliced, tpt, EmptyTree) + if StagingLevel.level == 0 + && !hasInliningErrors => + val expanded = expandMacro(spliced, tree1.srcPos) + transform.TreeChecker.checkMacroGeneratedTree(tree1, expanded) + typedExpr(expanded) // Inline calls and constant fold code generated by the macro + case tree1 => tree1 override def typedMatch(tree: untpd.Match, pt: Type)(using Context): Tree = val tree1 = @@ -1079,7 +1078,7 @@ class Inliner(val call: tpd.Tree)(using Context): level += 1 try apply(syms, body) finally level -= 1 - case SplicedExpr(body) => + case SplicedExpr(body, _, _) => level -= 1 try apply(syms, body) finally level += 1 diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index 50c6a72a5b12..c26b9bd21362 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -93,7 +93,7 @@ object PrepareInlineable { private def stagingContext(tree: Tree)(using Context): Context = tree match case tree: QuotedExpr => StagingLevel.quoteContext case tree: Apply if tree.symbol eq defn.QuotedTypeModule_of => StagingLevel.quoteContext - case tree: Apply if tree.symbol.isExprSplice => StagingLevel.spliceContext + case tree: SplicedExpr => StagingLevel.spliceContext case _ => ctx } @@ -155,7 +155,7 @@ object PrepareInlineable { val qual = qualifier(refPart) inlining.println(i"adding receiver passing inline accessor for $tree/$refPart -> (${qual.tpe}, $refPart: ${refPart.getClass}, $argss%, %") - // Need to dealias in order to cagtch all possible references to abstracted over types in + // Need to dealias in order to catch all possible references to abstracted over types in // substitutions val dealiasMap = new TypeMap { def apply(t: Type) = mapOver(t.dealias) @@ -291,7 +291,7 @@ object PrepareInlineable { if (inlined.is(Macro) && !ctx.isAfterTyper) { def checkMacro(tree: Tree): Unit = tree match { - case SplicedExpr(code) => + case SplicedExpr(code, _, _) => if (code.symbol.flags.is(Inline)) report.error("Macro cannot be implemented with an `inline` method", code.srcPos) Splicer.checkValidMacroBody(code) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 2f354259b33c..42ce138c52c7 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -432,8 +432,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { changePrec (GlobalPrec) { keywordStr("throw ") ~ toText(args.head) } - else if (!printDebug && fun.hasType && fun.symbol.isExprSplice) - keywordStr("${") ~ toTextGlobal(args, ", ") ~ keywordStr("}") else toTextLocal(fun) ~ "(" @@ -725,6 +723,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case QuotedExpr(expr, tpt) => val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug) keywordStr("'") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") + case SplicedExpr(spliced, tpt, quotes) => + val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug) + val quotesText = (keywordStr("(using ") ~ toTextGlobal(quotes) ~ keywordStr(")")).provided(printDebug) + keywordStr("$") ~ tptText ~ keywordStr("{") ~ toTextGlobal(spliced) ~ keywordStr("}") ~ quotesText case Hole(isTermHole, idx, args, content, tpt) => val (prefix, postfix) = if isTermHole then ("{{{", "}}}") else ("[[[", "]]]") val argsText = toTextGlobal(args, ", ") diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index e432baf9068c..e514043d515b 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -143,20 +143,16 @@ class CrossStageSafety extends TreeMapWithStages { * - If inside inlined code, expand the macro code. * - If inside of a macro definition, check the validity of the macro. */ - protected def transformSplice(body: Tree, splice: Apply)(using Context): Tree = { + protected def transformSplice(body: Tree, splice: SplicedExpr)(using Context): Tree = { val body1 = transform(body)(using spliceContext) - splice.fun match { - case fun @ TypeApply(_, _ :: Nil) => - // Type of the splice itself must also be healed - // `quoted.runtime.Expr.quote[F[T]](... T ...)` --> `internal.Quoted.expr[F[$t]](... T ...)` + val tpt1 = + if level == 0 then + transform(splice.tpt) + else val tp = healType(splice.srcPos)(splice.tpe.widenTermRefExpr) - cpy.Apply(splice)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), body1 :: Nil) - case f @ Apply(fun @ TypeApply(_, _), quotes :: Nil) => - // Type of the splice itself must also be healed - // `quoted.runtime.Expr.quote[F[T]](... T ...)` --> `internal.Quoted.expr[F[$t]](... T ...)` - val tp = healType(splice.srcPos)(splice.tpe.widenTermRefExpr) - cpy.Apply(splice)(cpy.Apply(f)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), quotes :: Nil), body1 :: Nil) - } + TypeTree(tp).withSpan(splice.tpt.span) + val outerQuotes1 = splice.outerQuotes + cpy.SplicedExpr(splice)(body1, tpt1, outerQuotes1) } protected def transformSpliceType(body: Tree, splice: Select)(using Context): Tree = { diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index a35fa363aa6b..ac750677c6b2 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -37,7 +37,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, body :: Nil), quote.args) /** Transform the expression splice `splice` which contains the spliced `body`. */ - protected def transformSplice(body: Tree, splice: Apply)(using Context): Tree + protected def transformSplice(body: Tree, splice: SplicedExpr)(using Context): Tree /** Transform the type splice `splice` which contains the spliced `body`. */ protected def transformSpliceType(body: Tree, splice: Select)(using Context): Tree @@ -66,7 +66,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(quotedTree) match { - case SplicedExpr(t) => + case SplicedExpr(t, _, _) => // Optimization: `'{ $x }` --> `x` // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]` transform(t).asInstance(tree.tpe) @@ -75,14 +75,15 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { } finally inQuoteOrSplice = old - case tree @ SplicedExpr(splicedTree) => + case tree @ SplicedExpr(splicedTree, _, _) => val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(splicedTree) match { case QuotedExpr(t, _) => // Optimization: `${ 'x }` --> `x` transform(t) - case _ => transformSplice(splicedTree, tree) + case _ => + transformSplice(splicedTree, tree) } finally inQuoteOrSplice = old diff --git a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala index 151e841f0e48..34be121070ea 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala @@ -159,6 +159,12 @@ class ElimByName extends MiniPhase, InfoTransformer: else tree } + override def transformOther(tree: Tree)(using Context): Tree = tree match + case tree @ SplicedExpr(spliced, tpt, outerQuotes) => + assert(dotty.tools.dotc.inlines.Inlines.inInlineMethod) + cpy.SplicedExpr(tree)(transformAllDeep(spliced), tpt, outerQuotes) + case tree => tree + object ElimByName: val name: String = "elimByName" val description: String = "map by-name parameters to functions" diff --git a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala index a7e0795ce195..1094c3c0bd0a 100644 --- a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -153,6 +153,9 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase => override def transformOther(tree: Tree)(using Context): Tree = tree match { case tree: Export => EmptyTree case tree: NamedArg => transformAllDeep(tree.arg) + case tree @ SplicedExpr(spliced, tpt, outerQuotes) => + assert(dotty.tools.dotc.inlines.Inlines.inInlineMethod) + cpy.SplicedExpr(tree)(transformAllDeep(spliced), transformAllDeep(tpt), outerQuotes) case tree => if (tree.isType) toTypeTree(tree) else tree } diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index a7644fa2a528..1fd719251421 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -50,7 +50,7 @@ class Inlining extends MacroTransform { traverseChildren(tree)(using StagingLevel.quoteContext) case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of => traverseChildren(tree)(using StagingLevel.quoteContext) - case _: GenericApply if tree.symbol.isExprSplice => + case _: SplicedExpr => traverseChildren(tree)(using StagingLevel.spliceContext) case tree: RefTree if !Inlines.inInlineMethod && StagingLevel.level == 0 => assert(!tree.symbol.isInlineMethod, tree.show) @@ -104,7 +104,7 @@ class Inlining extends MacroTransform { super.transform(tree)(using StagingLevel.quoteContext) case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of => super.transform(tree)(using StagingLevel.quoteContext) - case _: GenericApply if tree.symbol.isExprSplice => + case _: SplicedExpr => super.transform(tree)(using StagingLevel.spliceContext) case _: PackageDef => super.transform(tree) match diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 6507bed179c3..c020a53e7fdf 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -85,9 +85,10 @@ class PickleQuotes extends MacroTransform { tree match case tree: QuotedExpr => assert(Inlines.inInlineMethod) + case tree: SplicedExpr => + assert(Inlines.inInlineMethod) case tree: RefTree if !Inlines.inInlineMethod => assert(tree.symbol != defn.QuotedTypeModule_of) - assert(!tree.symbol.isExprSplice) case _ : TypeDef if !Inlines.inInlineMethod => assert(!tree.symbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot), s"${tree.symbol} should have been removed by PickledQuotes because it has a @quoteTypeTag") diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 2981bdafc958..c448cb20900b 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -158,7 +158,7 @@ object Splicer { case Apply(Select(QuotedExpr(expr, tpt), nme.apply), _) => val noSpliceChecker = new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match - case SplicedExpr(_) => + case SplicedExpr(_, _, _) => report.error("Quoted argument of macros may not have splices", tree.srcPos) case _ => traverseChildren(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index f5e536d85a55..d25c1e9a9895 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -112,15 +112,15 @@ class Splicing extends MacroTransform: override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match - case Apply(fn, List(splicedCode)) if fn.symbol == defn.QuotedRuntime_exprNestedSplice => + case tree: SplicedExpr => if level > 1 then - val splicedCode1 = super.transform(splicedCode)(using spliceContext) - cpy.Apply(tree)(fn, List(splicedCode1)) + val spliced1 = super.transform(tree.spliced)(using spliceContext) + cpy.SplicedExpr(tree)(spliced1, tree.tpt, tree.outerQuotes) else val holeIdx = numHoles numHoles += 1 val splicer = SpliceTransformer(ctx.owner, quotedDefs.contains) - val newSplicedCode1 = splicer.transformSplice(splicedCode, tree.tpe, holeIdx)(using spliceContext) + val newSplicedCode1 = splicer.transformSplice(tree.spliced, tree.tpe, holeIdx)(using spliceContext) val newSplicedCode2 = Level0QuoteTransformer.transform(newSplicedCode1)(using spliceContext) newSplicedCode2 case tree: TypeDef if tree.symbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => @@ -235,9 +235,9 @@ class Splicing extends MacroTransform: case tree @ Assign(lhs: RefTree, rhs) => if isCaptured(lhs.symbol) then transformSplicedAssign(tree) else super.transform(tree) - case Apply(fn, args) if fn.symbol == defn.QuotedRuntime_exprNestedSplice => - val newArgs = args.mapConserve(arg => transform(arg)(using spliceContext)) - cpy.Apply(tree)(fn, newArgs) + case SplicedExpr(spliced, tpt, outerQuotes) => + val spliced1 = transform(spliced)(using spliceContext) + cpy.SplicedExpr(tree)(spliced1, tpt, outerQuotes) case Apply(sel @ Select(app @ QuotedExpr(expr, tpt), nme.apply), quotesArgs) => expr match case expr: RefTree if isCaptured(expr.symbol) => @@ -410,10 +410,7 @@ class Splicing extends MacroTransform: body(using ctx.withOwner(meth)).changeOwner(ctx.owner, meth) } }) - ref(defn.QuotedRuntime_exprNestedSplice) - .appliedToType(tpe) - .appliedTo(Literal(Constant(null))) // Dropped when creating the Hole that contains it - .appliedTo(closure) + SplicedExpr(closure, TypeTree(tpe), Literal(Constant(null))) private def quoted(expr: Tree)(using Context): Tree = QuotedExpr(expr, TypeTree(expr.tpe.widenTermRefExpr)) diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index a5844dc0b538..feb841ba5c6c 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -315,10 +315,6 @@ object SymUtils: def reachableRawTypeRef(using Context) = self.reachableTypeRef.appliedTo(self.typeParams.map(_ => TypeBounds.emptyPolyKind)) - /** Is symbol a term splice operation? */ - def isExprSplice(using Context): Boolean = - self == defn.QuotedRuntime_exprSplice || self == defn.QuotedRuntime_exprNestedSplice - /** Is symbol a type splice operation? */ def isTypeSplice(using Context): Boolean = self == defn.QuotedType_splice diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index e8e0ad7cf4a2..e562470aea5e 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -88,7 +88,7 @@ trait QuotesAndSplices { using spliceContext.retractMode(Mode.QuotedPattern).addMode(Mode.Pattern).withOwner(spliceOwner(ctx))) val baseType = pat.tpe.baseType(defn.QuotedExprClass) val argType = if baseType != NoType then baseType.argTypesHi.head else defn.NothingType - ref(defn.QuotedRuntime_exprSplice).appliedToType(argType).appliedTo(pat) + SplicedExpr(pat, TypeTree(argType), EmptyTree).withSpan(tree.span) } else { report.error(em"Type must be fully defined.\nConsider annotating the splice using a type ascription:\n ($tree: XYZ).", tree.expr.srcPos) @@ -108,11 +108,17 @@ trait QuotesAndSplices { val (outerQctx, ctx1) = popQuotes() val internalSplice = + untpd.Apply(untpd.ref(defn.QuotedRuntime_exprSplice.termRef), tree.expr) outerQctx match case Some(qctxRef) => untpd.Apply(untpd.Apply(untpd.ref(defn.QuotedRuntime_exprNestedSplice.termRef), qctxRef), tree.expr) case _ => untpd.Apply(untpd.ref(defn.QuotedRuntime_exprSplice.termRef), tree.expr) - typedApply(internalSplice, pt)(using ctx1).withSpan(tree.span) + typedApply(internalSplice, pt)(using ctx1).withSpan(tree.span) match + case tree @ Apply(TypeApply(_, tpt :: Nil), spliced :: Nil) if tree.symbol == defn.QuotedRuntime_exprSplice => + cpy.SplicedExpr(tree)(spliced, tpt, EmptyTree) + case tree @ Apply(Apply(TypeApply(_, tpt :: Nil), quotes :: Nil), spliced :: Nil) if tree.symbol == defn.QuotedRuntime_exprNestedSplice => + cpy.SplicedExpr(tree)(spliced, tpt, quotes) + case tree => tree } } @@ -228,12 +234,12 @@ trait QuotesAndSplices { val freshTypeBindingsBuff = new mutable.ListBuffer[Tree] val typePatBuf = new mutable.ListBuffer[Tree] override def transform(tree: Tree)(using Context) = tree match { - case Typed(Apply(fn, pat :: Nil), tpt) if fn.symbol.isExprSplice && !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => + case Typed(SplicedExpr(pat, _, outerQuotes), tpt) if !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => val tpt1 = transform(tpt) // Transform type bindings val exprTpt = AppliedTypeTree(TypeTree(defn.QuotedExprClass.typeRef), tpt1 :: Nil) - val newSplice = ref(defn.QuotedRuntime_exprSplice).appliedToType(tpt1.tpe).appliedTo(Typed(pat, exprTpt)) + val newSplice = cpy.SplicedExpr(tree)(pat, tpt1, outerQuotes) transform(newSplice) - case Apply(TypeApply(fn, targs), Apply(sp, pat :: Nil) :: args :: Nil) if fn.symbol == defn.QuotedRuntimePatterns_patternHigherOrderHole => + case Apply(TypeApply(fn, targs), SplicedExpr(pat, _, _) :: args :: Nil) if fn.symbol == defn.QuotedRuntimePatterns_patternHigherOrderHole => args match // TODO support these patterns. Possibly using scala.quoted.util.Var case SeqLiteral(args, _) => for arg <- args; if arg.symbol.is(Mutable) do @@ -245,7 +251,7 @@ trait QuotesAndSplices { val pat1 = if (patType eq patType1) pat else pat.withType(patType1) patBuf += pat1 } - case Apply(fn, pat :: Nil) if fn.symbol.isExprSplice => + case SplicedExpr(pat, _, _) => try ref(defn.QuotedRuntimePatterns_patternHole.termRef).appliedToType(tree.tpe).withSpan(tree.span) finally { val patType = pat.tpe.widen diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 2d921f4dbcb1..9fe679a0583b 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -398,6 +398,9 @@ trait TypeAssigner { .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt.tpe)) tree.withType(lambdaType) + def assignType(tree: untpd.SplicedExpr, tpt: Tree)(using Context): SplicedExpr = + tree.withType(tpt.tpe) + def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(using Context): If = tree.withType(thenp.tpe | elsep.tpe) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index ddf16f213676..822f8e253932 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2049,7 +2049,15 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem) assignType(cpy.QuotedExpr(tree)(expr1, tpt1), tpt1) - .withNotNullInfo(expr1.notNullInfo) + + def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = + val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) + val splicedType = // Quotes ?=> Expr[T] + defn.FunctionType(1, isContextual = true) + .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) + val spliced1 = typed(tree.spliced, splicedType) + val outerQuotes1 = typed(tree.outerQuotes, defn.QuotesClass.typeRef) + assignType(cpy.SplicedExpr(tree)(spliced1, tpt1, outerQuotes1), tpt1) def completeTypeTree(tree: untpd.TypeTree, pt: Type, original: untpd.Tree)(using Context): TypeTree = tree.withSpan(original.span).withAttachmentsFrom(original) @@ -3084,6 +3092,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case tree: untpd.Quote => typedQuote(tree, pt) case tree: untpd.QuotedExpr => typedQuotedExpr(tree, pt) case tree: untpd.Splice => typedSplice(tree, pt) + case tree: untpd.SplicedExpr => typedSplicedExpr(tree, pt) case tree: untpd.MacroTree => report.error("Unexpected macro", tree.srcPos); tpd.nullLiteral // ill-formed code may reach here case tree: untpd.Hole => typedHole(tree, pt) case _ => typedUnadapted(desugar(tree, pt), pt, locked) From fe64a3e6fc3c45acaf88ad40d73471a152ba5713 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 25 Apr 2023 11:19:35 +0200 Subject: [PATCH 06/46] Remove splice outer quotes context This field is no longer used and can be removed. Removing it is backwards compatible with 3.0 and later releases. --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 20 +++++----- compiler/src/dotty/tools/dotc/ast/tpd.scala | 4 +- compiler/src/dotty/tools/dotc/ast/untpd.scala | 2 +- .../tools/dotc/core/tasty/TreePickler.scala | 11 +----- .../tools/dotc/core/tasty/TreeUnpickler.scala | 6 +-- .../dotty/tools/dotc/inlines/Inliner.scala | 6 +-- .../dotc/inlines/PrepareInlineable.scala | 2 +- .../tools/dotc/printing/RefinedPrinter.scala | 5 +-- .../dotty/tools/dotc/quoted/Interpreter.scala | 1 - .../tools/dotc/staging/CrossStageSafety.scala | 4 +- .../dotty/tools/dotc/staging/HealType.scala | 1 - .../tools/dotc/staging/QuoteContext.scala | 34 ----------------- .../dotc/staging/TreeMapWithStages.scala | 4 +- .../tools/dotc/transform/ElimByName.scala | 4 +- .../tools/dotc/transform/FirstTransform.scala | 4 +- .../dotty/tools/dotc/transform/Inlining.scala | 1 - .../dotty/tools/dotc/transform/Splicer.scala | 2 +- .../dotty/tools/dotc/transform/Splicing.scala | 9 ++--- .../dotty/tools/dotc/transform/Staging.scala | 1 - .../tools/dotc/typer/QuotesAndSplices.scala | 38 ++++++++----------- .../src/dotty/tools/dotc/typer/Typer.scala | 7 ++-- 21 files changed, 53 insertions(+), 113 deletions(-) delete mode 100644 compiler/src/dotty/tools/dotc/staging/QuoteContext.scala diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 63e4ab859899..dc37b1ef7180 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -682,7 +682,7 @@ object Trees { type ThisTree[+T <: Untyped] = QuotedExpr[T] } - case class SplicedExpr[+T <: Untyped] private[ast] (spliced: Tree[T], tpt: Tree[T], outerQuotes: Tree[T])(implicit @constructorOnly src: SourceFile) + case class SplicedExpr[+T <: Untyped] private[ast] (spliced: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = SplicedExpr[T] } @@ -1273,9 +1273,9 @@ object Trees { case tree: QuotedExpr if (expr eq tree.expr) && (tpt eq tree.tpt) => tree case _ => finalize(tree, untpd.QuotedExpr(expr, tpt)(sourceFile(tree))) } - def SplicedExpr(tree: Tree)(spliced: Tree, tpt: Tree, outerQuotes: Tree)(using Context): SplicedExpr = tree match { - case tree: SplicedExpr if (spliced eq tree.spliced) && (tpt eq tree.tpt) && (outerQuotes eq tree.outerQuotes) => tree - case _ => finalize(tree, untpd.SplicedExpr(spliced, tpt, outerQuotes)(sourceFile(tree))) + def SplicedExpr(tree: Tree)(spliced: Tree, tpt: Tree)(using Context): SplicedExpr = tree match { + case tree: SplicedExpr if (spliced eq tree.spliced) && (tpt eq tree.tpt) => tree + case _ => finalize(tree, untpd.SplicedExpr(spliced, tpt)(sourceFile(tree))) } def SingletonTypeTree(tree: Tree)(ref: Tree)(using Context): SingletonTypeTree = tree match { case tree: SingletonTypeTree if (ref eq tree.ref) => tree @@ -1382,8 +1382,8 @@ object Trees { TypeDef(tree: Tree)(name, rhs) def Template(tree: Template)(using Context)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, derived: List[untpd.Tree] = tree.derived, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody): Template = Template(tree: Tree)(constr, parents, derived, self, body) - def SplicedExpr(tree: SplicedExpr)(spliced: Tree = tree.spliced, tpt: Tree = tree.tpt, outerQuotes: Tree = tree.outerQuotes)(using Context): SplicedExpr = - SplicedExpr(tree: Tree)(spliced, tpt, outerQuotes) + def SplicedExpr(tree: SplicedExpr)(spliced: Tree = tree.spliced, tpt: Tree = tree.tpt)(using Context): SplicedExpr = + SplicedExpr(tree: Tree)(spliced, tpt) def Hole(tree: Hole)(isTerm: Boolean = tree.isTerm, idx: Int = tree.idx, args: List[Tree] = tree.args, content: Tree = tree.content, tpt: Tree = tree.tpt)(using Context): Hole = Hole(tree: Tree)(isTerm, idx, args, content, tpt) @@ -1518,8 +1518,8 @@ object Trees { if (trees1 eq trees) tree else Thicket(trees1) case tree @ QuotedExpr(expr, tpt) => cpy.QuotedExpr(tree)(transform(expr), transform(tpt)) - case tree @ SplicedExpr(spliced, tpt, outerQuotes) => - cpy.SplicedExpr(tree)(transform(spliced), transform(tpt), transform(outerQuotes)) + case tree @ SplicedExpr(spliced, tpt) => + cpy.SplicedExpr(tree)(transform(spliced), transform(tpt)) case tree @ Hole(_, _, args, content, tpt) => cpy.Hole(tree)(args = transform(args), content = transform(content), tpt = transform(tpt)) case _ => @@ -1663,8 +1663,8 @@ object Trees { this(x, ts) case QuotedExpr(expr, tpt) => this(this(x, expr), tpt) - case SplicedExpr(spliced, tpt, outerQuotes) => - this(this(this(x, spliced), tpt), outerQuotes) + case SplicedExpr(spliced, tpt) => + this(this(x, spliced), tpt) case Hole(_, _, args, content, tpt) => this(this(this(x, args), content), tpt) case _ => diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index e7c3ba93f72c..bc2498e7ca9b 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -173,8 +173,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def QuotedExpr(expr: Tree, tpt: Tree)(using Context): QuotedExpr = ta.assignType(untpd.QuotedExpr(expr, tpt), tpt) - def SplicedExpr(spliced: Tree, tpt: Tree, outerQuotes: Tree)(using Context): SplicedExpr = - ta.assignType(untpd.SplicedExpr(spliced, tpt, outerQuotes), tpt) + def SplicedExpr(spliced: Tree, tpt: Tree)(using Context): SplicedExpr = + ta.assignType(untpd.SplicedExpr(spliced, tpt), tpt) def TypeTree(tp: Type, inferred: Boolean = false)(using Context): TypeTree = (if inferred then untpd.InferredTypeTree() else untpd.TypeTree()).withType(tp) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index b63df5f6999b..c8d083ef792d 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -402,7 +402,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt) def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion) def QuotedExpr(expr: Tree, tpt: Tree)(implicit src: SourceFile): QuotedExpr = new QuotedExpr(expr, tpt) - def SplicedExpr(spliced: Tree, tpt: Tree, outerQuotes: Tree)(implicit src: SourceFile): SplicedExpr = new SplicedExpr(spliced, tpt, outerQuotes) + def SplicedExpr(spliced: Tree, tpt: Tree)(implicit src: SourceFile): SplicedExpr = new SplicedExpr(spliced, tpt) def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree() def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree() def SingletonTypeTree(ref: Tree)(implicit src: SourceFile): SingletonTypeTree = new SingletonTypeTree(ref) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index a57ce2c00a5d..0762ea476021 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -673,7 +673,7 @@ class TreePickler(pickler: TastyPickler) { .appliedTo(expr) .withSpan(tree.span) ) - case SplicedExpr(spliced, tpt, EmptyTree) => + case SplicedExpr(spliced, tpt) => pickleTree( // scala.quoted.runtime.Expr.splice[]() ref(defn.QuotedRuntime_exprSplice) @@ -681,15 +681,6 @@ class TreePickler(pickler: TastyPickler) { .appliedTo(spliced) .withSpan(tree.span) ) - case SplicedExpr(spliced, tpt, quotes) => - pickleTree( - // scala.quoted.runtime.Expr.nestedSplice[]()() - ref(defn.QuotedRuntime_exprNestedSplice) - .appliedToTypeTree(tpt) - .appliedTo(quotes) - .appliedTo(spliced) - .withSpan(tree.span) - ) case Hole(_, idx, args, _, tpt) => writeByte(HOLE) withLength { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index eb0dbdbc8bbb..153fc7b3922d 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1273,12 +1273,12 @@ class TreeUnpickler(reader: TastyReader, def splicedExpr(fn: Tree, args: List[Tree]): Tree = val TypeApply(_, targs) = fn: @unchecked - SplicedExpr(args.head, targs.head, EmptyTree) + SplicedExpr(args.head, targs.head) def nestedSpliceExpr(fn: Tree, args: List[Tree]): Tree = fn match - case Apply(TypeApply(_, targs), outerQuotes :: Nil) => - SplicedExpr(args.head, targs.head, outerQuotes) + case Apply(TypeApply(_, targs), _ :: Nil) => // nestedSplice[T](quotes)(expr) + SplicedExpr(args.head, targs.head) case _ => // nestedSplice[T](quotes) tpd.Apply(fn, args) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index bb16cf7f84b3..02e1bbdeacce 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -827,14 +827,14 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = super.typedQuotedExpr(tree, pt) match - case QuotedExpr(SplicedExpr(inner, _, _), _) => inner + case QuotedExpr(SplicedExpr(inner, _), _) => inner case tree1 => ctx.compilationUnit.needsStaging = true tree1 override def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = super.typedSplicedExpr(tree, pt) match - case tree1 @ SplicedExpr(spliced, tpt, EmptyTree) + case tree1 @ SplicedExpr(spliced, tpt) if StagingLevel.level == 0 && !hasInliningErrors => val expanded = expandMacro(spliced, tree1.srcPos) @@ -1078,7 +1078,7 @@ class Inliner(val call: tpd.Tree)(using Context): level += 1 try apply(syms, body) finally level -= 1 - case SplicedExpr(body, _, _) => + case SplicedExpr(body, _) => level -= 1 try apply(syms, body) finally level += 1 diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index c26b9bd21362..b94b76316b1b 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -291,7 +291,7 @@ object PrepareInlineable { if (inlined.is(Macro) && !ctx.isAfterTyper) { def checkMacro(tree: Tree): Unit = tree match { - case SplicedExpr(code, _, _) => + case SplicedExpr(code, _) => if (code.symbol.flags.is(Inline)) report.error("Macro cannot be implemented with an `inline` method", code.srcPos) Splicer.checkValidMacroBody(code) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 42ce138c52c7..0d899b8b5909 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -723,10 +723,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case QuotedExpr(expr, tpt) => val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug) keywordStr("'") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") - case SplicedExpr(spliced, tpt, quotes) => + case SplicedExpr(spliced, tpt) => val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug) - val quotesText = (keywordStr("(using ") ~ toTextGlobal(quotes) ~ keywordStr(")")).provided(printDebug) - keywordStr("$") ~ tptText ~ keywordStr("{") ~ toTextGlobal(spliced) ~ keywordStr("}") ~ quotesText + keywordStr("$") ~ tptText ~ keywordStr("{") ~ toTextGlobal(spliced) ~ keywordStr("}") case Hole(isTermHole, idx, args, content, tpt) => val (prefix, postfix) = if isTermHole then ("{{{", "}}}") else ("[[[", "]]]") val argsText = toTextGlobal(args, ", ") diff --git a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala index a98284f4078d..c9a77dbfa151 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala @@ -24,7 +24,6 @@ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.TypeErasure import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.quoted._ -import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.typer.ImportInfo.withRootImports import dotty.tools.dotc.util.SrcPos import dotty.tools.dotc.reporting.Message diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index e514043d515b..512e5ab6ab4c 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -10,7 +10,6 @@ import dotty.tools.dotc.core.NameKinds._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ -import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.staging.QuoteTypeTags.* import dotty.tools.dotc.util.Property @@ -151,8 +150,7 @@ class CrossStageSafety extends TreeMapWithStages { else val tp = healType(splice.srcPos)(splice.tpe.widenTermRefExpr) TypeTree(tp).withSpan(splice.tpt.span) - val outerQuotes1 = splice.outerQuotes - cpy.SplicedExpr(splice)(body1, tpt1, outerQuotes1) + cpy.SplicedExpr(splice)(body1, tpt1) } protected def transformSpliceType(body: Tree, splice: Select)(using Context): Tree = { diff --git a/compiler/src/dotty/tools/dotc/staging/HealType.scala b/compiler/src/dotty/tools/dotc/staging/HealType.scala index 50c2584b2fd0..337c7e8c9b3b 100644 --- a/compiler/src/dotty/tools/dotc/staging/HealType.scala +++ b/compiler/src/dotty/tools/dotc/staging/HealType.scala @@ -7,7 +7,6 @@ import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ -import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.staging.QuoteTypeTags.* import dotty.tools.dotc.transform.SymUtils._ diff --git a/compiler/src/dotty/tools/dotc/staging/QuoteContext.scala b/compiler/src/dotty/tools/dotc/staging/QuoteContext.scala deleted file mode 100644 index 8e25bba7110c..000000000000 --- a/compiler/src/dotty/tools/dotc/staging/QuoteContext.scala +++ /dev/null @@ -1,34 +0,0 @@ -package dotty.tools.dotc.staging - -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Contexts._ -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.util.Property -import dotty.tools.dotc.staging.StagingLevel.* - -object QuoteContext { - - /** A key to be used in a context property that tracks the quotation stack. - * Stack containing the Quotes references received by the surrounding quotes. - */ - private val QuotesStack = new Property.Key[List[tpd.Tree]] - - /** Context with an incremented quotation level and pushes a reference to a Quotes on the quote context stack */ - def pushQuotes(quotes: tpd.Tree)(using Context): Context = - val old = ctx.property(QuotesStack).getOrElse(List.empty) - quoteContext.setProperty(QuotesStack, quotes :: old) - - /** Context with a decremented quotation level and pops the Some of top of the quote context stack or None if the stack is empty. - * The quotation stack could be empty if we are in a top level splice or an erroneous splice directly within a top level splice. - */ - def popQuotes()(using Context): (Option[tpd.Tree], Context) = - val ctx1 = spliceContext - val head = - ctx.property(QuotesStack) match - case Some(x :: xs) => - ctx1.setProperty(QuotesStack, xs) - Some(x) - case _ => - None // Splice at level 0 or lower - (head, ctx1) -} diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index ac750677c6b2..1ed6d4c37e19 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -66,7 +66,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(quotedTree) match { - case SplicedExpr(t, _, _) => + case SplicedExpr(t, _) => // Optimization: `'{ $x }` --> `x` // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]` transform(t).asInstance(tree.tpe) @@ -75,7 +75,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { } finally inQuoteOrSplice = old - case tree @ SplicedExpr(splicedTree, _, _) => + case tree @ SplicedExpr(splicedTree, _) => val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(splicedTree) match { diff --git a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala index 34be121070ea..6e264385fd3e 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala @@ -160,9 +160,9 @@ class ElimByName extends MiniPhase, InfoTransformer: } override def transformOther(tree: Tree)(using Context): Tree = tree match - case tree @ SplicedExpr(spliced, tpt, outerQuotes) => + case tree @ SplicedExpr(spliced, tpt) => assert(dotty.tools.dotc.inlines.Inlines.inInlineMethod) - cpy.SplicedExpr(tree)(transformAllDeep(spliced), tpt, outerQuotes) + cpy.SplicedExpr(tree)(transformAllDeep(spliced), tpt) case tree => tree object ElimByName: diff --git a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala index 1094c3c0bd0a..7a9e3cc324d3 100644 --- a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -153,9 +153,9 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase => override def transformOther(tree: Tree)(using Context): Tree = tree match { case tree: Export => EmptyTree case tree: NamedArg => transformAllDeep(tree.arg) - case tree @ SplicedExpr(spliced, tpt, outerQuotes) => + case tree @ SplicedExpr(spliced, tpt) => assert(dotty.tools.dotc.inlines.Inlines.inInlineMethod) - cpy.SplicedExpr(tree)(transformAllDeep(spliced), transformAllDeep(tpt), outerQuotes) + cpy.SplicedExpr(tree)(transformAllDeep(spliced), transformAllDeep(tpt)) case tree => if (tree.isType) toTypeTree(tree) else tree } diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 1fd719251421..341633e30960 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -12,7 +12,6 @@ import dotty.tools.dotc.quoted._ import dotty.tools.dotc.inlines.Inlines import dotty.tools.dotc.ast.TreeMapWithImplicits import dotty.tools.dotc.core.DenotTransformers.IdentityDenotTransformer -import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.staging.StagingLevel import scala.collection.mutable.ListBuffer diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index c448cb20900b..2e6549bc8ec8 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -158,7 +158,7 @@ object Splicer { case Apply(Select(QuotedExpr(expr, tpt), nme.apply), _) => val noSpliceChecker = new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match - case SplicedExpr(_, _, _) => + case SplicedExpr(_, _) => report.error("Quoted argument of macros may not have splices", tree.srcPos) case _ => traverseChildren(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index d25c1e9a9895..ed271f20c5bf 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -21,7 +21,6 @@ import dotty.tools.dotc.core.Names._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.quoted._ import dotty.tools.dotc.config.ScalaRelease.* -import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.staging.QuoteTypeTags @@ -115,7 +114,7 @@ class Splicing extends MacroTransform: case tree: SplicedExpr => if level > 1 then val spliced1 = super.transform(tree.spliced)(using spliceContext) - cpy.SplicedExpr(tree)(spliced1, tree.tpt, tree.outerQuotes) + cpy.SplicedExpr(tree)(spliced1, tree.tpt) else val holeIdx = numHoles numHoles += 1 @@ -235,9 +234,9 @@ class Splicing extends MacroTransform: case tree @ Assign(lhs: RefTree, rhs) => if isCaptured(lhs.symbol) then transformSplicedAssign(tree) else super.transform(tree) - case SplicedExpr(spliced, tpt, outerQuotes) => + case SplicedExpr(spliced, tpt) => val spliced1 = transform(spliced)(using spliceContext) - cpy.SplicedExpr(tree)(spliced1, tpt, outerQuotes) + cpy.SplicedExpr(tree)(spliced1, tpt) case Apply(sel @ Select(app @ QuotedExpr(expr, tpt), nme.apply), quotesArgs) => expr match case expr: RefTree if isCaptured(expr.symbol) => @@ -410,7 +409,7 @@ class Splicing extends MacroTransform: body(using ctx.withOwner(meth)).changeOwner(ctx.owner, meth) } }) - SplicedExpr(closure, TypeTree(tpe), Literal(Constant(null))) + SplicedExpr(closure, TypeTree(tpe)) private def quoted(expr: Tree)(using Context): Tree = QuotedExpr(expr, TypeTree(expr.tpe.widenTermRefExpr)) diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala index 4fb5f5910440..a025f86a1db6 100644 --- a/compiler/src/dotty/tools/dotc/transform/Staging.scala +++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala @@ -10,7 +10,6 @@ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.util.SrcPos import dotty.tools.dotc.transform.SymUtils._ -import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.staging.CrossStageSafety import dotty.tools.dotc.staging.HealType diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index e562470aea5e..1f3b5c758324 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -15,7 +15,6 @@ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.inlines.PrepareInlineable -import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.transform.SymUtils._ import dotty.tools.dotc.typer.Implicits._ @@ -42,15 +41,15 @@ trait QuotesAndSplices { report.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.srcPos) case _ => } - val qctx = inferImplicitArg(defn.QuotesClass.typeRef, tree.span) + val quotes = inferImplicitArg(defn.QuotesClass.typeRef, tree.span) - if qctx.tpe.isInstanceOf[SearchFailureType] then - report.error(missingArgMsg(qctx, defn.QuotesClass.typeRef, ""), ctx.source.atSpan(tree.span)) - else if !qctx.tpe.isStable then - report.error(em"Quotes require stable Quotes, but found non stable $qctx", qctx.srcPos) + if quotes.tpe.isInstanceOf[SearchFailureType] then + report.error(missingArgMsg(quotes, defn.QuotesClass.typeRef, ""), ctx.source.atSpan(tree.span)) + else if !quotes.tpe.isStable then + report.error(em"Quotes require stable Quotes, but found non stable $quotes", quotes.srcPos) if ctx.mode.is(Mode.Pattern) then - typedQuotePattern(tree, pt, qctx).withSpan(tree.span) + typedQuotePattern(tree, pt, quotes).withSpan(tree.span) else if tree.quoted.isType then val msg = em"""Quoted types `'[..]` can only be used in patterns. | @@ -60,9 +59,9 @@ trait QuotesAndSplices { EmptyTree else val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.quoted) - val quotedExpr = typedApply(exprQuoteTree, pt)(using pushQuotes(qctx)) match + val quotedExpr = typedApply(exprQuoteTree, pt)(using quoteContext) match case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => QuotedExpr(quotedExpr, tpt) - makeInlineable(quotedExpr.select(nme.apply).appliedTo(qctx).withSpan(tree.span)) + makeInlineable(quotedExpr.select(nme.apply).appliedTo(quotes).withSpan(tree.span)) } private def makeInlineable(tree: Tree)(using Context): Tree = @@ -88,7 +87,7 @@ trait QuotesAndSplices { using spliceContext.retractMode(Mode.QuotedPattern).addMode(Mode.Pattern).withOwner(spliceOwner(ctx))) val baseType = pat.tpe.baseType(defn.QuotedExprClass) val argType = if baseType != NoType then baseType.argTypesHi.head else defn.NothingType - SplicedExpr(pat, TypeTree(argType), EmptyTree).withSpan(tree.span) + SplicedExpr(pat, TypeTree(argType)).withSpan(tree.span) } else { report.error(em"Type must be fully defined.\nConsider annotating the splice using a type ascription:\n ($tree: XYZ).", tree.expr.srcPos) @@ -105,19 +104,12 @@ trait QuotesAndSplices { markAsMacro(ctx) } - val (outerQctx, ctx1) = popQuotes() - val internalSplice = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprSplice.termRef), tree.expr) - outerQctx match - case Some(qctxRef) => untpd.Apply(untpd.Apply(untpd.ref(defn.QuotedRuntime_exprNestedSplice.termRef), qctxRef), tree.expr) - case _ => untpd.Apply(untpd.ref(defn.QuotedRuntime_exprSplice.termRef), tree.expr) - typedApply(internalSplice, pt)(using ctx1).withSpan(tree.span) match + typedApply(internalSplice, pt)(using spliceContext).withSpan(tree.span) match case tree @ Apply(TypeApply(_, tpt :: Nil), spliced :: Nil) if tree.symbol == defn.QuotedRuntime_exprSplice => - cpy.SplicedExpr(tree)(spliced, tpt, EmptyTree) - case tree @ Apply(Apply(TypeApply(_, tpt :: Nil), quotes :: Nil), spliced :: Nil) if tree.symbol == defn.QuotedRuntime_exprNestedSplice => - cpy.SplicedExpr(tree)(spliced, tpt, quotes) + cpy.SplicedExpr(tree)(spliced, tpt) case tree => tree } } @@ -234,12 +226,12 @@ trait QuotesAndSplices { val freshTypeBindingsBuff = new mutable.ListBuffer[Tree] val typePatBuf = new mutable.ListBuffer[Tree] override def transform(tree: Tree)(using Context) = tree match { - case Typed(SplicedExpr(pat, _, outerQuotes), tpt) if !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => + case Typed(SplicedExpr(pat, _), tpt) if !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => val tpt1 = transform(tpt) // Transform type bindings val exprTpt = AppliedTypeTree(TypeTree(defn.QuotedExprClass.typeRef), tpt1 :: Nil) - val newSplice = cpy.SplicedExpr(tree)(pat, tpt1, outerQuotes) + val newSplice = cpy.SplicedExpr(tree)(pat, tpt1) transform(newSplice) - case Apply(TypeApply(fn, targs), SplicedExpr(pat, _, _) :: args :: Nil) if fn.symbol == defn.QuotedRuntimePatterns_patternHigherOrderHole => + case Apply(TypeApply(fn, targs), SplicedExpr(pat, _) :: args :: Nil) if fn.symbol == defn.QuotedRuntimePatterns_patternHigherOrderHole => args match // TODO support these patterns. Possibly using scala.quoted.util.Var case SeqLiteral(args, _) => for arg <- args; if arg.symbol.is(Mutable) do @@ -251,7 +243,7 @@ trait QuotesAndSplices { val pat1 = if (patType eq patType1) pat else pat.withType(patType1) patBuf += pat1 } - case SplicedExpr(pat, _, _) => + case SplicedExpr(pat, _) => try ref(defn.QuotedRuntimePatterns_patternHole.termRef).appliedToType(tree.tpe).withSpan(tree.span) finally { val patType = pat.tpe.widen diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 822f8e253932..b1d281f35976 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2047,7 +2047,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) - val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem) + val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem)(using StagingLevel.quoteContext) assignType(cpy.QuotedExpr(tree)(expr1, tpt1), tpt1) def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = @@ -2055,9 +2055,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val splicedType = // Quotes ?=> Expr[T] defn.FunctionType(1, isContextual = true) .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) - val spliced1 = typed(tree.spliced, splicedType) - val outerQuotes1 = typed(tree.outerQuotes, defn.QuotesClass.typeRef) - assignType(cpy.SplicedExpr(tree)(spliced1, tpt1, outerQuotes1), tpt1) + val spliced1 = typed(tree.spliced, splicedType)(using StagingLevel.spliceContext) + assignType(cpy.SplicedExpr(tree)(spliced1, tpt1), tpt1) def completeTypeTree(tree: untpd.TypeTree, pt: Type, original: untpd.Tree)(using Context): TypeTree = tree.withSpan(original.span).withAttachmentsFrom(original) From 161ef49f6be3a8a7adb1cdf835ae78abd9f95393 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 25 Apr 2023 11:34:27 +0200 Subject: [PATCH 07/46] Rename `SplicedExpr.{spliced=>expr}` --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 20 +++++++++---------- compiler/src/dotty/tools/dotc/ast/tpd.scala | 4 ++-- compiler/src/dotty/tools/dotc/ast/untpd.scala | 2 +- .../tools/dotc/core/tasty/TreePickler.scala | 4 ++-- .../dotty/tools/dotc/inlines/Inliner.scala | 4 ++-- .../tools/dotc/printing/RefinedPrinter.scala | 4 ++-- .../tools/dotc/transform/FirstTransform.scala | 4 ++-- .../dotty/tools/dotc/transform/Splicing.scala | 12 +++++------ .../src/dotty/tools/dotc/typer/Typer.scala | 4 ++-- 9 files changed, 29 insertions(+), 29 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index dc37b1ef7180..b95e2a41ff09 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -682,7 +682,7 @@ object Trees { type ThisTree[+T <: Untyped] = QuotedExpr[T] } - case class SplicedExpr[+T <: Untyped] private[ast] (spliced: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) + case class SplicedExpr[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = SplicedExpr[T] } @@ -1273,9 +1273,9 @@ object Trees { case tree: QuotedExpr if (expr eq tree.expr) && (tpt eq tree.tpt) => tree case _ => finalize(tree, untpd.QuotedExpr(expr, tpt)(sourceFile(tree))) } - def SplicedExpr(tree: Tree)(spliced: Tree, tpt: Tree)(using Context): SplicedExpr = tree match { - case tree: SplicedExpr if (spliced eq tree.spliced) && (tpt eq tree.tpt) => tree - case _ => finalize(tree, untpd.SplicedExpr(spliced, tpt)(sourceFile(tree))) + def SplicedExpr(tree: Tree)(expr: Tree, tpt: Tree)(using Context): SplicedExpr = tree match { + case tree: SplicedExpr if (expr eq tree.expr) && (tpt eq tree.tpt) => tree + case _ => finalize(tree, untpd.SplicedExpr(expr, tpt)(sourceFile(tree))) } def SingletonTypeTree(tree: Tree)(ref: Tree)(using Context): SingletonTypeTree = tree match { case tree: SingletonTypeTree if (ref eq tree.ref) => tree @@ -1382,8 +1382,8 @@ object Trees { TypeDef(tree: Tree)(name, rhs) def Template(tree: Template)(using Context)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, derived: List[untpd.Tree] = tree.derived, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody): Template = Template(tree: Tree)(constr, parents, derived, self, body) - def SplicedExpr(tree: SplicedExpr)(spliced: Tree = tree.spliced, tpt: Tree = tree.tpt)(using Context): SplicedExpr = - SplicedExpr(tree: Tree)(spliced, tpt) + def SplicedExpr(tree: SplicedExpr)(expr: Tree = tree.expr, tpt: Tree = tree.tpt)(using Context): SplicedExpr = + SplicedExpr(tree: Tree)(expr, tpt) def Hole(tree: Hole)(isTerm: Boolean = tree.isTerm, idx: Int = tree.idx, args: List[Tree] = tree.args, content: Tree = tree.content, tpt: Tree = tree.tpt)(using Context): Hole = Hole(tree: Tree)(isTerm, idx, args, content, tpt) @@ -1518,8 +1518,8 @@ object Trees { if (trees1 eq trees) tree else Thicket(trees1) case tree @ QuotedExpr(expr, tpt) => cpy.QuotedExpr(tree)(transform(expr), transform(tpt)) - case tree @ SplicedExpr(spliced, tpt) => - cpy.SplicedExpr(tree)(transform(spliced), transform(tpt)) + case tree @ SplicedExpr(expr, tpt) => + cpy.SplicedExpr(tree)(transform(expr), transform(tpt)) case tree @ Hole(_, _, args, content, tpt) => cpy.Hole(tree)(args = transform(args), content = transform(content), tpt = transform(tpt)) case _ => @@ -1663,8 +1663,8 @@ object Trees { this(x, ts) case QuotedExpr(expr, tpt) => this(this(x, expr), tpt) - case SplicedExpr(spliced, tpt) => - this(this(x, spliced), tpt) + case SplicedExpr(expr, tpt) => + this(this(x, expr), tpt) case Hole(_, _, args, content, tpt) => this(this(this(x, args), content), tpt) case _ => diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index bc2498e7ca9b..50b288f6ddc1 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -173,8 +173,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def QuotedExpr(expr: Tree, tpt: Tree)(using Context): QuotedExpr = ta.assignType(untpd.QuotedExpr(expr, tpt), tpt) - def SplicedExpr(spliced: Tree, tpt: Tree)(using Context): SplicedExpr = - ta.assignType(untpd.SplicedExpr(spliced, tpt), tpt) + def SplicedExpr(expr: Tree, tpt: Tree)(using Context): SplicedExpr = + ta.assignType(untpd.SplicedExpr(expr, tpt), tpt) def TypeTree(tp: Type, inferred: Boolean = false)(using Context): TypeTree = (if inferred then untpd.InferredTypeTree() else untpd.TypeTree()).withType(tp) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index c8d083ef792d..151851d89a43 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -402,7 +402,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt) def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion) def QuotedExpr(expr: Tree, tpt: Tree)(implicit src: SourceFile): QuotedExpr = new QuotedExpr(expr, tpt) - def SplicedExpr(spliced: Tree, tpt: Tree)(implicit src: SourceFile): SplicedExpr = new SplicedExpr(spliced, tpt) + def SplicedExpr(expr: Tree, tpt: Tree)(implicit src: SourceFile): SplicedExpr = new SplicedExpr(expr, tpt) def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree() def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree() def SingletonTypeTree(ref: Tree)(implicit src: SourceFile): SingletonTypeTree = new SingletonTypeTree(ref) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 0762ea476021..f41d3b57f8f0 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -673,12 +673,12 @@ class TreePickler(pickler: TastyPickler) { .appliedTo(expr) .withSpan(tree.span) ) - case SplicedExpr(spliced, tpt) => + case SplicedExpr(expr, tpt) => pickleTree( // scala.quoted.runtime.Expr.splice[]() ref(defn.QuotedRuntime_exprSplice) .appliedToTypeTree(tpt) - .appliedTo(spliced) + .appliedTo(expr) .withSpan(tree.span) ) case Hole(_, idx, args, _, tpt) => diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 02e1bbdeacce..c03e86970d90 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -834,10 +834,10 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = super.typedSplicedExpr(tree, pt) match - case tree1 @ SplicedExpr(spliced, tpt) + case tree1 @ SplicedExpr(expr, tpt) if StagingLevel.level == 0 && !hasInliningErrors => - val expanded = expandMacro(spliced, tree1.srcPos) + val expanded = expandMacro(expr, tree1.srcPos) transform.TreeChecker.checkMacroGeneratedTree(tree1, expanded) typedExpr(expanded) // Inline calls and constant fold code generated by the macro case tree1 => tree1 diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 0d899b8b5909..be407ded2f82 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -723,9 +723,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case QuotedExpr(expr, tpt) => val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug) keywordStr("'") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") - case SplicedExpr(spliced, tpt) => + case SplicedExpr(expr, tpt) => val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug) - keywordStr("$") ~ tptText ~ keywordStr("{") ~ toTextGlobal(spliced) ~ keywordStr("}") + keywordStr("$") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") case Hole(isTermHole, idx, args, content, tpt) => val (prefix, postfix) = if isTermHole then ("{{{", "}}}") else ("[[[", "]]]") val argsText = toTextGlobal(args, ", ") diff --git a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala index 7a9e3cc324d3..cad54982d10d 100644 --- a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -153,9 +153,9 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase => override def transformOther(tree: Tree)(using Context): Tree = tree match { case tree: Export => EmptyTree case tree: NamedArg => transformAllDeep(tree.arg) - case tree @ SplicedExpr(spliced, tpt) => + case tree @ SplicedExpr(expr, tpt) => assert(dotty.tools.dotc.inlines.Inlines.inInlineMethod) - cpy.SplicedExpr(tree)(transformAllDeep(spliced), transformAllDeep(tpt)) + cpy.SplicedExpr(tree)(transformAllDeep(expr), transformAllDeep(tpt)) case tree => if (tree.isType) toTypeTree(tree) else tree } diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index ed271f20c5bf..ca3acb6b8fac 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -113,13 +113,13 @@ class Splicing extends MacroTransform: tree match case tree: SplicedExpr => if level > 1 then - val spliced1 = super.transform(tree.spliced)(using spliceContext) - cpy.SplicedExpr(tree)(spliced1, tree.tpt) + val expr1 = super.transform(tree.expr)(using spliceContext) + cpy.SplicedExpr(tree)(expr1, tree.tpt) else val holeIdx = numHoles numHoles += 1 val splicer = SpliceTransformer(ctx.owner, quotedDefs.contains) - val newSplicedCode1 = splicer.transformSplice(tree.spliced, tree.tpe, holeIdx)(using spliceContext) + val newSplicedCode1 = splicer.transformSplice(tree.expr, tree.tpe, holeIdx)(using spliceContext) val newSplicedCode2 = Level0QuoteTransformer.transform(newSplicedCode1)(using spliceContext) newSplicedCode2 case tree: TypeDef if tree.symbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => @@ -234,9 +234,9 @@ class Splicing extends MacroTransform: case tree @ Assign(lhs: RefTree, rhs) => if isCaptured(lhs.symbol) then transformSplicedAssign(tree) else super.transform(tree) - case SplicedExpr(spliced, tpt) => - val spliced1 = transform(spliced)(using spliceContext) - cpy.SplicedExpr(tree)(spliced1, tpt) + case SplicedExpr(expr, tpt) => + val expr1 = transform(expr)(using spliceContext) + cpy.SplicedExpr(tree)(expr1, tpt) case Apply(sel @ Select(app @ QuotedExpr(expr, tpt), nme.apply), quotesArgs) => expr match case expr: RefTree if isCaptured(expr.symbol) => diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index b1d281f35976..b13bc5ec0bea 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2055,8 +2055,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val splicedType = // Quotes ?=> Expr[T] defn.FunctionType(1, isContextual = true) .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) - val spliced1 = typed(tree.spliced, splicedType)(using StagingLevel.spliceContext) - assignType(cpy.SplicedExpr(tree)(spliced1, tpt1), tpt1) + val expr1 = typed(tree.expr, splicedType)(using StagingLevel.spliceContext) + assignType(cpy.SplicedExpr(tree)(expr1, tpt1), tpt1) def completeTypeTree(tree: untpd.TypeTree, pt: Type, original: untpd.Tree)(using Context): TypeTree = tree.withSpan(original.span).withAttachmentsFrom(original) From cf30d30cc60bfc1d5a8e2e87b040ebbd3ffd98e3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 25 Apr 2023 11:51:41 +0200 Subject: [PATCH 08/46] Use QuotedExpr instead of Quote trees --- .../src/dotty/tools/dotc/ast/Desugar.scala | 2 +- compiler/src/dotty/tools/dotc/ast/untpd.scala | 9 ------- .../dotty/tools/dotc/parsing/Parsers.scala | 6 ++--- .../tools/dotc/printing/RefinedPrinter.scala | 4 +--- .../tools/dotc/typer/QuotesAndSplices.scala | 24 +++++++++---------- .../src/dotty/tools/dotc/typer/Typer.scala | 10 ++++---- 6 files changed, 23 insertions(+), 32 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index c1dd78451bae..bf24702e4036 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1986,7 +1986,7 @@ object desugar { trees foreach collect case Block(Nil, expr) => collect(expr) - case Quote(expr) => + case QuotedExpr(expr, _) => new UntypedTreeTraverser { def traverse(tree: untpd.Tree)(using Context): Unit = tree match { case Splice(expr) => collect(expr) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 151851d89a43..e2f0a44cea99 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -111,7 +111,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { override def isType: Boolean = !isTerm } case class Throw(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree - case class Quote(quoted: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree case class Splice(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree { def isInBraces: Boolean = span.end != expr.span.end } @@ -624,10 +623,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case tree: Throw if expr eq tree.expr => tree case _ => finalize(tree, untpd.Throw(expr)(tree.source)) } - def Quote(tree: Tree)(quoted: Tree)(using Context): Tree = tree match { - case tree: Quote if quoted eq tree.quoted => tree - case _ => finalize(tree, untpd.Quote(quoted)(tree.source)) - } def Splice(tree: Tree)(expr: Tree)(using Context): Tree = tree match { case tree: Splice if expr eq tree.expr => tree case _ => finalize(tree, untpd.Splice(expr)(tree.source)) @@ -713,8 +708,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { cpy.Tuple(tree)(transform(trees)) case Throw(expr) => cpy.Throw(tree)(transform(expr)) - case Quote(t) => - cpy.Quote(tree)(transform(t)) case Splice(expr) => cpy.Splice(tree)(transform(expr)) case ForYield(enums, expr) => @@ -774,8 +767,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { this(x, trees) case Throw(expr) => this(x, expr) - case Quote(t) => - this(x, t) case Splice(expr) => this(x, expr) case ForYield(enums, expr) => diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 15a639743c15..12e038f560d3 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1262,7 +1262,7 @@ object Parsers { } } in.nextToken() - Quote(t) + QuotedExpr(t, EmptyTree) } else if !in.featureEnabled(Feature.symbolLiterals) then @@ -2494,10 +2494,10 @@ object Parsers { case QUOTE => atSpan(in.skipToken()) { withinStaged(StageKind.Quoted | (if (location.inPattern) StageKind.QuotedPattern else 0)) { - Quote { + val expr = if (in.token == LBRACKET) inBrackets(typ()) else stagedBlock() - } + QuotedExpr(expr, EmptyTree) } } case NEW => diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index be407ded2f82..9910f471756e 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -712,8 +712,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } case Number(digits, kind) => digits - case Quote(tree) if tree.isTerm => - keywordStr("'{") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}") case Splice(tree) => keywordStr("${") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}") case Thicket(trees) => @@ -721,7 +719,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case MacroTree(call) => keywordStr("macro ") ~ toTextGlobal(call) case QuotedExpr(expr, tpt) => - val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug) + val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(!tpt.isEmpty && printDebug) keywordStr("'") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") case SplicedExpr(expr, tpt) => val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 1f3b5c758324..5e9279dbc819 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -34,9 +34,9 @@ trait QuotesAndSplices { /** Translate `'{ e }` into `scala.quoted.Expr.apply(e)` and `'[T]` into `scala.quoted.Type.apply[T]` * while tracking the quotation level in the context. */ - def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = { + def typedQuote(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = { record("typedQuote") - tree.quoted match { + tree.expr match { case untpd.Splice(innerExpr) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => report.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.srcPos) case _ => @@ -50,7 +50,7 @@ trait QuotesAndSplices { if ctx.mode.is(Mode.Pattern) then typedQuotePattern(tree, pt, quotes).withSpan(tree.span) - else if tree.quoted.isType then + else if tree.expr.isType then val msg = em"""Quoted types `'[..]` can only be used in patterns. | |Hint: To get a scala.quoted.Type[T] use scala.quoted.Type.of[T] instead. @@ -58,7 +58,7 @@ trait QuotesAndSplices { report.error(msg, tree.srcPos) EmptyTree else - val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.quoted) + val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.expr) val quotedExpr = typedApply(exprQuoteTree, pt)(using quoteContext) match case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => QuotedExpr(quotedExpr, tpt) makeInlineable(quotedExpr.select(nme.apply).appliedTo(quotes).withSpan(tree.span)) @@ -74,7 +74,7 @@ trait QuotesAndSplices { record("typedSplice") checkSpliceOutsideQuote(tree) tree.expr match { - case untpd.Quote(innerExpr) if innerExpr.isTerm => + case untpd.QuotedExpr(innerExpr, _) if innerExpr.isTerm => report.warning("Canceled quote directly inside a splice. ${ '{ XYZ } } is equivalent to XYZ.", tree.srcPos) return typed(innerExpr, pt) case _ => @@ -382,13 +382,13 @@ trait QuotesAndSplices { * ) => ... * ``` */ - private def typedQuotePattern(tree: untpd.Quote, pt: Type, qctx: Tree)(using Context): Tree = { - if tree.quoted.isTerm && !pt.derivesFrom(defn.QuotedExprClass) then + private def typedQuotePattern(tree: untpd.QuotedExpr, pt: Type, qctx: Tree)(using Context): Tree = { + val quoted = tree.expr + if quoted.isTerm && !pt.derivesFrom(defn.QuotedExprClass) then report.error("Quote pattern can only match scrutinees of type scala.quoted.Expr", tree.srcPos) - else if tree.quoted.isType && !pt.derivesFrom(defn.QuotedTypeClass) then + else if quoted.isType && !pt.derivesFrom(defn.QuotedTypeClass) then report.error("Quote pattern can only match scrutinees of type scala.quoted.Type", tree.srcPos) - val quoted = tree.quoted val exprPt = pt.baseType(if quoted.isType then defn.QuotedTypeClass else defn.QuotedExprClass) val quotedPt = exprPt.argInfos.headOption match { case Some(argPt: ValueType) => argPt // excludes TypeBounds @@ -440,12 +440,12 @@ trait QuotesAndSplices { if splices.isEmpty then ref(defn.EmptyTupleModule.termRef) else typed(untpd.Tuple(splices.map(x => untpd.TypedSplice(replaceBindingsInTree.transform(x)))).withSpan(quoted.span), patType) - val quoteClass = if (tree.quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass + val quoteClass = if (quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass val quotedPattern = - if (tree.quoted.isTerm) tpd.QuotedExpr(shape, TypeTree(defn.AnyType)).select(nme.apply).appliedTo(qctx) + if (quoted.isTerm) tpd.QuotedExpr(shape, TypeTree(defn.AnyType)).select(nme.apply).appliedTo(qctx) else ref(defn.QuotedTypeModule_of.termRef).appliedToTypeTree(shape).appliedTo(qctx) - val matchModule = if tree.quoted.isTerm then defn.QuoteMatching_ExprMatch else defn.QuoteMatching_TypeMatch + val matchModule = if quoted.isTerm then defn.QuoteMatching_ExprMatch else defn.QuoteMatching_TypeMatch val unapplyFun = qctx.asInstance(defn.QuoteMatchingClass.typeRef).select(matchModule).select(nme.unapply) UnApply( diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index b13bc5ec0bea..cf239ab4fc33 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2046,9 +2046,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer } def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = - val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) - val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem)(using StagingLevel.quoteContext) - assignType(cpy.QuotedExpr(tree)(expr1, tpt1), tpt1) + if tree.tpt.isEmpty then + typedQuote(tree, pt) + else + val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) + val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem)(using StagingLevel.quoteContext) + assignType(cpy.QuotedExpr(tree)(expr1, tpt1), tpt1) def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) @@ -3088,7 +3091,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case tree: untpd.ParsedTry => typedTry(tree, pt) case tree @ untpd.PostfixOp(qual, Ident(nme.WILDCARD)) => typedAsFunction(tree, pt) case untpd.EmptyTree => tpd.EmptyTree - case tree: untpd.Quote => typedQuote(tree, pt) case tree: untpd.QuotedExpr => typedQuotedExpr(tree, pt) case tree: untpd.Splice => typedSplice(tree, pt) case tree: untpd.SplicedExpr => typedSplicedExpr(tree, pt) From f0d758b2620106273eeb6387fdd6fc95d025e348 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 25 Apr 2023 12:54:35 +0200 Subject: [PATCH 09/46] Use SplicedExpr instead of Splice trees --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 6 +++--- compiler/src/dotty/tools/dotc/ast/Trees.scala | 1 + compiler/src/dotty/tools/dotc/ast/untpd.scala | 11 ----------- .../src/dotty/tools/dotc/parsing/Parsers.scala | 2 +- .../tools/dotc/printing/RefinedPrinter.scala | 4 +--- .../dotty/tools/dotc/typer/Applications.scala | 2 +- .../tools/dotc/typer/QuotesAndSplices.scala | 6 +++--- compiler/src/dotty/tools/dotc/typer/Typer.scala | 16 +++++++++------- 8 files changed, 19 insertions(+), 29 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index bf24702e4036..1c519a602d5a 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -338,9 +338,9 @@ object desugar { def quotedPattern(tree: untpd.Tree, expectedTpt: untpd.Tree)(using Context): untpd.Tree = { def adaptToExpectedTpt(tree: untpd.Tree): untpd.Tree = tree match { // Add the expected type as an ascription - case _: untpd.Splice => + case _: untpd.SplicedExpr => untpd.Typed(tree, expectedTpt).withSpan(tree.span) - case Typed(expr: untpd.Splice, tpt) => + case Typed(expr: untpd.SplicedExpr, tpt) => cpy.Typed(tree)(expr, untpd.makeAndType(tpt, expectedTpt).withSpan(tpt.span)) // Propagate down the expected type to the leafs of the expression @@ -1989,7 +1989,7 @@ object desugar { case QuotedExpr(expr, _) => new UntypedTreeTraverser { def traverse(tree: untpd.Tree)(using Context): Unit = tree match { - case Splice(expr) => collect(expr) + case SplicedExpr(expr, _) => collect(expr) case _ => traverseChildren(tree) } }.traverse(expr) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index b95e2a41ff09..9d487c46ff88 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -685,6 +685,7 @@ object Trees { case class SplicedExpr[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = SplicedExpr[T] + def isInBraces: Boolean = span.end != expr.span.end } /** A type tree that represents an existing or inferred type */ diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index e2f0a44cea99..b9f5bb751862 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -111,9 +111,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { override def isType: Boolean = !isTerm } case class Throw(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree - case class Splice(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree { - def isInBraces: Boolean = span.end != expr.span.end - } case class ForYield(enums: List[Tree], expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree case class ForDo(enums: List[Tree], body: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree case class GenFrom(pat: Tree, expr: Tree, checkMode: GenCheckMode)(implicit @constructorOnly src: SourceFile) extends Tree @@ -623,10 +620,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case tree: Throw if expr eq tree.expr => tree case _ => finalize(tree, untpd.Throw(expr)(tree.source)) } - def Splice(tree: Tree)(expr: Tree)(using Context): Tree = tree match { - case tree: Splice if expr eq tree.expr => tree - case _ => finalize(tree, untpd.Splice(expr)(tree.source)) - } def ForYield(tree: Tree)(enums: List[Tree], expr: Tree)(using Context): TermTree = tree match { case tree: ForYield if (enums eq tree.enums) && (expr eq tree.expr) => tree case _ => finalize(tree, untpd.ForYield(enums, expr)(tree.source)) @@ -708,8 +701,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { cpy.Tuple(tree)(transform(trees)) case Throw(expr) => cpy.Throw(tree)(transform(expr)) - case Splice(expr) => - cpy.Splice(tree)(transform(expr)) case ForYield(enums, expr) => cpy.ForYield(tree)(transform(enums), transform(expr)) case ForDo(enums, body) => @@ -767,8 +758,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { this(x, trees) case Throw(expr) => this(x, expr) - case Splice(expr) => - this(x, expr) case ForYield(enums, expr) => this(this(x, enums), expr) case ForDo(enums, body) => diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 12e038f560d3..8fa5fe54bc28 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1764,7 +1764,7 @@ object Parsers { syntaxError(em"$msg\n\nHint: $hint", Span(start, in.lastOffset)) Ident(nme.ERROR.toTypeName) else - Splice(expr) + SplicedExpr(expr, EmptyTree) } /** SimpleType ::= SimpleLiteral diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 9910f471756e..5b8b367e21be 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -712,8 +712,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } case Number(digits, kind) => digits - case Splice(tree) => - keywordStr("${") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}") case Thicket(trees) => "Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}" case MacroTree(call) => @@ -722,7 +720,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(!tpt.isEmpty && printDebug) keywordStr("'") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") case SplicedExpr(expr, tpt) => - val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug) + val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(!tpt.isEmpty && printDebug) keywordStr("$") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") case Hole(isTermHole, idx, args, content, tpt) => val (prefix, postfix) = if isTermHole then ("{{{", "}}}") else ("[[[", "]]]") diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 79d6501ccb2d..77c9eb519db1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1097,7 +1097,7 @@ trait Applications extends Compatibility { } else { val app = tree.fun match - case _: untpd.Splice if ctx.mode.is(Mode.QuotedPattern) => typedAppliedSplice(tree, pt) + case _: untpd.SplicedExpr if ctx.mode.is(Mode.QuotedPattern) => typedAppliedSplice(tree, pt) case _ => realApply app match { case Apply(fn @ Select(left, _), right :: Nil) if fn.hasType => diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 5e9279dbc819..a61798fab4b7 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -37,7 +37,7 @@ trait QuotesAndSplices { def typedQuote(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = { record("typedQuote") tree.expr match { - case untpd.Splice(innerExpr) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => + case untpd.SplicedExpr(innerExpr, _) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => report.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.srcPos) case _ => } @@ -70,7 +70,7 @@ trait QuotesAndSplices { } /** Translate `${ t: Expr[T] }` into expression `t.splice` while tracking the quotation level in the context */ - def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = { + def typedSplice(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = { record("typedSplice") checkSpliceOutsideQuote(tree) tree.expr match { @@ -123,7 +123,7 @@ trait QuotesAndSplices { */ def typedAppliedSplice(tree: untpd.Apply, pt: Type)(using Context): Tree = { assert(ctx.mode.is(Mode.QuotedPattern)) - val untpd.Apply(splice: untpd.Splice, args) = tree: @unchecked + val untpd.Apply(splice: untpd.SplicedExpr, args) = tree: @unchecked if !isFullyDefined(pt, ForceDegree.flipBottom) then report.error(em"Type must be fully defined.", splice.srcPos) tree.withType(UnspecifiedErrorType) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index cf239ab4fc33..0a20335bc1d1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2054,12 +2054,15 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer assignType(cpy.QuotedExpr(tree)(expr1, tpt1), tpt1) def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = - val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) - val splicedType = // Quotes ?=> Expr[T] - defn.FunctionType(1, isContextual = true) - .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) - val expr1 = typed(tree.expr, splicedType)(using StagingLevel.spliceContext) - assignType(cpy.SplicedExpr(tree)(expr1, tpt1), tpt1) + if tree.tpt.isEmpty then + typedSplice(tree, pt) + else + val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) + val splicedType = // Quotes ?=> Expr[T] + defn.FunctionType(1, isContextual = true) + .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) + val expr1 = typed(tree.expr, splicedType)(using StagingLevel.spliceContext) + assignType(cpy.SplicedExpr(tree)(expr1, tpt1), tpt1) def completeTypeTree(tree: untpd.TypeTree, pt: Type, original: untpd.Tree)(using Context): TypeTree = tree.withSpan(original.span).withAttachmentsFrom(original) @@ -3092,7 +3095,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case tree @ untpd.PostfixOp(qual, Ident(nme.WILDCARD)) => typedAsFunction(tree, pt) case untpd.EmptyTree => tpd.EmptyTree case tree: untpd.QuotedExpr => typedQuotedExpr(tree, pt) - case tree: untpd.Splice => typedSplice(tree, pt) case tree: untpd.SplicedExpr => typedSplicedExpr(tree, pt) case tree: untpd.MacroTree => report.error("Unexpected macro", tree.srcPos); tpd.nullLiteral // ill-formed code may reach here case tree: untpd.Hole => typedHole(tree, pt) From c6adab8e942d5cb0d428e9a12690e02cb9c2ed33 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 25 Apr 2023 13:03:33 +0200 Subject: [PATCH 10/46] Move typedQuotedExpr/typedSplicedExpr to QuotesAndSplices --- .../tools/dotc/typer/QuotesAndSplices.scala | 35 ++++++++++++++----- .../src/dotty/tools/dotc/typer/Typer.scala | 19 ---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index a61798fab4b7..d47c7abf3fc3 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -31,10 +31,33 @@ trait QuotesAndSplices { import tpd._ + def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = + if tree.tpt.isEmpty then + typedQuoteSyntactic(tree, pt) + else + val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) + val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem)(using quoteContext) + assignType(untpd.cpy.QuotedExpr(tree)(expr1, tpt1), tpt1) + + def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = + if tree.tpt.isEmpty then + typedSpliceSyntactic(tree, pt) + else + val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) + val splicedType = // Quotes ?=> Expr[T] + defn.FunctionType(1, isContextual = true) + .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) + val expr1 = typed(tree.expr, splicedType)(using spliceContext) + assignType(untpd.cpy.SplicedExpr(tree)(expr1, tpt1), tpt1) + + def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree = + val tpt = typedType(tree.tpt) + assignType(tree, tpt) + /** Translate `'{ e }` into `scala.quoted.Expr.apply(e)` and `'[T]` into `scala.quoted.Type.apply[T]` * while tracking the quotation level in the context. */ - def typedQuote(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = { + private def typedQuoteSyntactic(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = { record("typedQuote") tree.expr match { case untpd.SplicedExpr(innerExpr, _) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => @@ -70,7 +93,7 @@ trait QuotesAndSplices { } /** Translate `${ t: Expr[T] }` into expression `t.splice` while tracking the quotation level in the context */ - def typedSplice(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = { + private def typedSpliceSyntactic(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = { record("typedSplice") checkSpliceOutsideQuote(tree) tree.expr match { @@ -130,7 +153,7 @@ trait QuotesAndSplices { else if splice.isInBraces then // ${x}(...) match an application val typedArgs = args.map(arg => typedExpr(arg)) val argTypes = typedArgs.map(_.tpe.widenTermRefExpr) - val splice1 = typedSplice(splice, defn.FunctionOf(argTypes, pt)) + val splice1 = typedSpliceSyntactic(splice, defn.FunctionOf(argTypes, pt)) Apply(splice1.select(nme.apply), typedArgs).withType(pt).withSpan(tree.span) else // $x(...) higher-order quasipattern val typedArgs = args.map { @@ -143,7 +166,7 @@ trait QuotesAndSplices { if args.isEmpty then report.error("Missing arguments for open pattern", tree.srcPos) val argTypes = typedArgs.map(_.tpe.widenTermRefExpr) - val typedPat = typedSplice(splice, defn.FunctionOf(argTypes, pt)) + val typedPat = typedSpliceSyntactic(splice, defn.FunctionOf(argTypes, pt)) ref(defn.QuotedRuntimePatterns_patternHigherOrderHole).appliedToType(pt).appliedTo(typedPat, SeqLiteral(typedArgs, TypeTree(defn.AnyType))) } @@ -165,10 +188,6 @@ trait QuotesAndSplices { using spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx))) pat.select(tpnme.Underlying) - def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree = - val tpt = typedType(tree.tpt) - assignType(tree, tpt) - private def checkSpliceOutsideQuote(tree: untpd.Tree)(using Context): Unit = if (level == 0 && !ctx.owner.ownersIterator.exists(_.isInlineMethod)) report.error("Splice ${...} outside quotes '{...} or inline method", tree.srcPos) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 0a20335bc1d1..aa0243146241 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2045,25 +2045,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer bindings1, expansion1) } - def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = - if tree.tpt.isEmpty then - typedQuote(tree, pt) - else - val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) - val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem)(using StagingLevel.quoteContext) - assignType(cpy.QuotedExpr(tree)(expr1, tpt1), tpt1) - - def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = - if tree.tpt.isEmpty then - typedSplice(tree, pt) - else - val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) - val splicedType = // Quotes ?=> Expr[T] - defn.FunctionType(1, isContextual = true) - .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) - val expr1 = typed(tree.expr, splicedType)(using StagingLevel.spliceContext) - assignType(cpy.SplicedExpr(tree)(expr1, tpt1), tpt1) - def completeTypeTree(tree: untpd.TypeTree, pt: Type, original: untpd.Tree)(using Context): TypeTree = tree.withSpan(original.span).withAttachmentsFrom(original) .withType( From 3a045c4f6a63f7b5bcd04ae0a8c6163eef4800ce Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 25 Apr 2023 13:18:56 +0200 Subject: [PATCH 11/46] Rename QuotedExpr to Quote and SplicedExpr to Splice --- .../dotty/tools/dotc/CompilationUnit.scala | 2 +- .../src/dotty/tools/dotc/ast/Desugar.scala | 8 ++-- compiler/src/dotty/tools/dotc/ast/Trees.scala | 40 +++++++++---------- compiler/src/dotty/tools/dotc/ast/tpd.scala | 8 ++-- compiler/src/dotty/tools/dotc/ast/untpd.scala | 4 +- .../tools/dotc/core/tasty/TreePickler.scala | 4 +- .../tools/dotc/core/tasty/TreeUnpickler.scala | 6 +-- .../dotty/tools/dotc/inlines/Inliner.scala | 16 ++++---- .../dotc/inlines/PrepareInlineable.scala | 6 +-- .../dotty/tools/dotc/parsing/Parsers.scala | 6 +-- .../tools/dotc/printing/RefinedPrinter.scala | 4 +- .../tools/dotc/staging/CrossStageSafety.scala | 8 ++-- .../dotc/staging/TreeMapWithStages.scala | 16 ++++---- .../tools/dotc/transform/ElimByName.scala | 4 +- .../tools/dotc/transform/FirstTransform.scala | 4 +- .../dotty/tools/dotc/transform/Inlining.scala | 8 ++-- .../tools/dotc/transform/PickleQuotes.scala | 6 +-- .../tools/dotc/transform/PostTyper.scala | 2 +- .../dotty/tools/dotc/transform/Splicer.scala | 12 +++--- .../dotty/tools/dotc/transform/Splicing.scala | 20 +++++----- .../tools/dotc/transform/patmat/Space.scala | 2 +- .../dotty/tools/dotc/typer/Applications.scala | 2 +- .../tools/dotc/typer/QuotesAndSplices.scala | 36 ++++++++--------- .../dotty/tools/dotc/typer/TypeAssigner.scala | 4 +- .../src/dotty/tools/dotc/typer/Typer.scala | 4 +- 25 files changed, 116 insertions(+), 116 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index 897befdda4a5..9a41f685f6c3 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -157,7 +157,7 @@ object CompilationUnit { if tree.symbol.is(Flags.Inline) then containsInline = true tree match - case tpd.QuotedExpr(_, _) => + case tpd.Quote(_, _) => containsQuote = true case tree: tpd.Apply if tree.symbol == defn.QuotedTypeModule_of => containsQuote = true diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 1c519a602d5a..5c2410d3fbdc 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -338,9 +338,9 @@ object desugar { def quotedPattern(tree: untpd.Tree, expectedTpt: untpd.Tree)(using Context): untpd.Tree = { def adaptToExpectedTpt(tree: untpd.Tree): untpd.Tree = tree match { // Add the expected type as an ascription - case _: untpd.SplicedExpr => + case _: untpd.Splice => untpd.Typed(tree, expectedTpt).withSpan(tree.span) - case Typed(expr: untpd.SplicedExpr, tpt) => + case Typed(expr: untpd.Splice, tpt) => cpy.Typed(tree)(expr, untpd.makeAndType(tpt, expectedTpt).withSpan(tpt.span)) // Propagate down the expected type to the leafs of the expression @@ -1986,10 +1986,10 @@ object desugar { trees foreach collect case Block(Nil, expr) => collect(expr) - case QuotedExpr(expr, _) => + case Quote(expr, _) => new UntypedTreeTraverser { def traverse(tree: untpd.Tree)(using Context): Unit = tree match { - case SplicedExpr(expr, _) => collect(expr) + case Splice(expr, _) => collect(expr) case _ => traverseChildren(tree) } }.traverse(expr) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 9d487c46ff88..65a0cd6ba710 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -677,14 +677,14 @@ object Trees { override def isType = expansion.isType } - case class QuotedExpr[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) + case class Quote[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { - type ThisTree[+T <: Untyped] = QuotedExpr[T] + type ThisTree[+T <: Untyped] = Quote[T] } - case class SplicedExpr[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) + case class Splice[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { - type ThisTree[+T <: Untyped] = SplicedExpr[T] + type ThisTree[+T <: Untyped] = Splice[T] def isInBraces: Boolean = span.end != expr.span.end } @@ -1098,8 +1098,8 @@ object Trees { type SeqLiteral = Trees.SeqLiteral[T] type JavaSeqLiteral = Trees.JavaSeqLiteral[T] type Inlined = Trees.Inlined[T] - type QuotedExpr = Trees.QuotedExpr[T] - type SplicedExpr = Trees.SplicedExpr[T] + type Quote = Trees.Quote[T] + type Splice = Trees.Splice[T] type TypeTree = Trees.TypeTree[T] type InferredTypeTree = Trees.InferredTypeTree[T] type SingletonTypeTree = Trees.SingletonTypeTree[T] @@ -1270,13 +1270,13 @@ object Trees { case tree: Inlined if (call eq tree.call) && (bindings eq tree.bindings) && (expansion eq tree.expansion) => tree case _ => finalize(tree, untpd.Inlined(call, bindings, expansion)(sourceFile(tree))) } - def QuotedExpr(tree: Tree)(expr: Tree, tpt: Tree)(using Context): QuotedExpr = tree match { - case tree: QuotedExpr if (expr eq tree.expr) && (tpt eq tree.tpt) => tree - case _ => finalize(tree, untpd.QuotedExpr(expr, tpt)(sourceFile(tree))) + def Quote(tree: Tree)(expr: Tree, tpt: Tree)(using Context): Quote = tree match { + case tree: Quote if (expr eq tree.expr) && (tpt eq tree.tpt) => tree + case _ => finalize(tree, untpd.Quote(expr, tpt)(sourceFile(tree))) } - def SplicedExpr(tree: Tree)(expr: Tree, tpt: Tree)(using Context): SplicedExpr = tree match { - case tree: SplicedExpr if (expr eq tree.expr) && (tpt eq tree.tpt) => tree - case _ => finalize(tree, untpd.SplicedExpr(expr, tpt)(sourceFile(tree))) + def Splice(tree: Tree)(expr: Tree, tpt: Tree)(using Context): Splice = tree match { + case tree: Splice if (expr eq tree.expr) && (tpt eq tree.tpt) => tree + case _ => finalize(tree, untpd.Splice(expr, tpt)(sourceFile(tree))) } def SingletonTypeTree(tree: Tree)(ref: Tree)(using Context): SingletonTypeTree = tree match { case tree: SingletonTypeTree if (ref eq tree.ref) => tree @@ -1383,8 +1383,8 @@ object Trees { TypeDef(tree: Tree)(name, rhs) def Template(tree: Template)(using Context)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, derived: List[untpd.Tree] = tree.derived, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody): Template = Template(tree: Tree)(constr, parents, derived, self, body) - def SplicedExpr(tree: SplicedExpr)(expr: Tree = tree.expr, tpt: Tree = tree.tpt)(using Context): SplicedExpr = - SplicedExpr(tree: Tree)(expr, tpt) + def Splice(tree: Splice)(expr: Tree = tree.expr, tpt: Tree = tree.tpt)(using Context): Splice = + Splice(tree: Tree)(expr, tpt) def Hole(tree: Hole)(isTerm: Boolean = tree.isTerm, idx: Int = tree.idx, args: List[Tree] = tree.args, content: Tree = tree.content, tpt: Tree = tree.tpt)(using Context): Hole = Hole(tree: Tree)(isTerm, idx, args, content, tpt) @@ -1517,10 +1517,10 @@ object Trees { case Thicket(trees) => val trees1 = transform(trees) if (trees1 eq trees) tree else Thicket(trees1) - case tree @ QuotedExpr(expr, tpt) => - cpy.QuotedExpr(tree)(transform(expr), transform(tpt)) - case tree @ SplicedExpr(expr, tpt) => - cpy.SplicedExpr(tree)(transform(expr), transform(tpt)) + case tree @ Quote(expr, tpt) => + cpy.Quote(tree)(transform(expr), transform(tpt)) + case tree @ Splice(expr, tpt) => + cpy.Splice(tree)(transform(expr), transform(tpt)) case tree @ Hole(_, _, args, content, tpt) => cpy.Hole(tree)(args = transform(args), content = transform(content), tpt = transform(tpt)) case _ => @@ -1662,9 +1662,9 @@ object Trees { this(this(x, arg), annot) case Thicket(ts) => this(x, ts) - case QuotedExpr(expr, tpt) => + case Quote(expr, tpt) => this(this(x, expr), tpt) - case SplicedExpr(expr, tpt) => + case Splice(expr, tpt) => this(this(x, expr), tpt) case Hole(_, _, args, content, tpt) => this(this(this(x, args), content), tpt) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 50b288f6ddc1..66c0bedc12c3 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -170,11 +170,11 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Inlined(call: Tree, bindings: List[MemberDef], expansion: Tree)(using Context): Inlined = ta.assignType(untpd.Inlined(call, bindings, expansion), bindings, expansion) - def QuotedExpr(expr: Tree, tpt: Tree)(using Context): QuotedExpr = - ta.assignType(untpd.QuotedExpr(expr, tpt), tpt) + def Quote(expr: Tree, tpt: Tree)(using Context): Quote = + ta.assignType(untpd.Quote(expr, tpt), tpt) - def SplicedExpr(expr: Tree, tpt: Tree)(using Context): SplicedExpr = - ta.assignType(untpd.SplicedExpr(expr, tpt), tpt) + def Splice(expr: Tree, tpt: Tree)(using Context): Splice = + ta.assignType(untpd.Splice(expr, tpt), tpt) def TypeTree(tp: Type, inferred: Boolean = false)(using Context): TypeTree = (if inferred then untpd.InferredTypeTree() else untpd.TypeTree()).withType(tp) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index b9f5bb751862..45b2fcd49595 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -397,8 +397,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def SeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): SeqLiteral = new SeqLiteral(elems, elemtpt) def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt) def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion) - def QuotedExpr(expr: Tree, tpt: Tree)(implicit src: SourceFile): QuotedExpr = new QuotedExpr(expr, tpt) - def SplicedExpr(expr: Tree, tpt: Tree)(implicit src: SourceFile): SplicedExpr = new SplicedExpr(expr, tpt) + def Quote(expr: Tree, tpt: Tree)(implicit src: SourceFile): Quote = new Quote(expr, tpt) + def Splice(expr: Tree, tpt: Tree)(implicit src: SourceFile): Splice = new Splice(expr, tpt) def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree() def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree() def SingletonTypeTree(ref: Tree)(implicit src: SourceFile): SingletonTypeTree = new SingletonTypeTree(ref) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index f41d3b57f8f0..864004a3e5ec 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -665,7 +665,7 @@ class TreePickler(pickler: TastyPickler) { pickleTree(hi) pickleTree(alias) } - case QuotedExpr(expr, tpt) => + case Quote(expr, tpt) => pickleTree( // scala.quoted.runtime.Expr.quoted[]() ref(defn.QuotedRuntime_exprQuote) @@ -673,7 +673,7 @@ class TreePickler(pickler: TastyPickler) { .appliedTo(expr) .withSpan(tree.span) ) - case SplicedExpr(expr, tpt) => + case Splice(expr, tpt) => pickleTree( // scala.quoted.runtime.Expr.splice[]() ref(defn.QuotedRuntime_exprSplice) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 153fc7b3922d..79d2008eb698 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1269,16 +1269,16 @@ class TreeUnpickler(reader: TastyReader, def quotedExpr(fn: Tree, args: List[Tree]): Tree = val TypeApply(_, targs) = fn: @unchecked - QuotedExpr(args.head, targs.head) + Quote(args.head, targs.head) def splicedExpr(fn: Tree, args: List[Tree]): Tree = val TypeApply(_, targs) = fn: @unchecked - SplicedExpr(args.head, targs.head) + Splice(args.head, targs.head) def nestedSpliceExpr(fn: Tree, args: List[Tree]): Tree = fn match case Apply(TypeApply(_, targs), _ :: Nil) => // nestedSplice[T](quotes)(expr) - SplicedExpr(args.head, targs.head) + Splice(args.head, targs.head) case _ => // nestedSplice[T](quotes) tpd.Apply(fn, args) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index c03e86970d90..e2fd4c6688c1 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -825,16 +825,16 @@ class Inliner(val call: tpd.Tree)(using Context): ctx.compilationUnit.needsStaging = true tree1 - override def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = - super.typedQuotedExpr(tree, pt) match - case QuotedExpr(SplicedExpr(inner, _), _) => inner + override def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = + super.typedQuote(tree, pt) match + case Quote(Splice(inner, _), _) => inner case tree1 => ctx.compilationUnit.needsStaging = true tree1 - override def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = - super.typedSplicedExpr(tree, pt) match - case tree1 @ SplicedExpr(expr, tpt) + override def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = + super.typedSplice(tree, pt) match + case tree1 @ Splice(expr, tpt) if StagingLevel.level == 0 && !hasInliningErrors => val expanded = expandMacro(expr, tree1.srcPos) @@ -1070,7 +1070,7 @@ class Inliner(val call: tpd.Tree)(using Context): else tree match { case tree: RefTree if tree.isTerm && tree.symbol.isDefinedInCurrentRun && !tree.symbol.isLocal => foldOver(tree.symbol :: syms, tree) - case QuotedExpr(body, _) => + case Quote(body, _) => level += 1 try apply(syms, body) finally level -= 1 @@ -1078,7 +1078,7 @@ class Inliner(val call: tpd.Tree)(using Context): level += 1 try apply(syms, body) finally level -= 1 - case SplicedExpr(body, _) => + case Splice(body, _) => level -= 1 try apply(syms, body) finally level += 1 diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index b94b76316b1b..11d4d9881230 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -91,9 +91,9 @@ object PrepareInlineable { } private def stagingContext(tree: Tree)(using Context): Context = tree match - case tree: QuotedExpr => StagingLevel.quoteContext + case tree: Quote => StagingLevel.quoteContext case tree: Apply if tree.symbol eq defn.QuotedTypeModule_of => StagingLevel.quoteContext - case tree: SplicedExpr => StagingLevel.spliceContext + case tree: Splice => StagingLevel.spliceContext case _ => ctx } @@ -291,7 +291,7 @@ object PrepareInlineable { if (inlined.is(Macro) && !ctx.isAfterTyper) { def checkMacro(tree: Tree): Unit = tree match { - case SplicedExpr(code, _) => + case Splice(code, _) => if (code.symbol.flags.is(Inline)) report.error("Macro cannot be implemented with an `inline` method", code.srcPos) Splicer.checkValidMacroBody(code) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 8fa5fe54bc28..c2e2e4466d98 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1262,7 +1262,7 @@ object Parsers { } } in.nextToken() - QuotedExpr(t, EmptyTree) + Quote(t, EmptyTree) } else if !in.featureEnabled(Feature.symbolLiterals) then @@ -1764,7 +1764,7 @@ object Parsers { syntaxError(em"$msg\n\nHint: $hint", Span(start, in.lastOffset)) Ident(nme.ERROR.toTypeName) else - SplicedExpr(expr, EmptyTree) + Splice(expr, EmptyTree) } /** SimpleType ::= SimpleLiteral @@ -2497,7 +2497,7 @@ object Parsers { val expr = if (in.token == LBRACKET) inBrackets(typ()) else stagedBlock() - QuotedExpr(expr, EmptyTree) + Quote(expr, EmptyTree) } } case NEW => diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 5b8b367e21be..b2b9196b0a13 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -716,10 +716,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { "Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}" case MacroTree(call) => keywordStr("macro ") ~ toTextGlobal(call) - case QuotedExpr(expr, tpt) => + case Quote(expr, tpt) => val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(!tpt.isEmpty && printDebug) keywordStr("'") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") - case SplicedExpr(expr, tpt) => + case Splice(expr, tpt) => val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(!tpt.isEmpty && printDebug) keywordStr("$") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") case Hole(isTermHole, idx, args, content, tpt) => diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 512e5ab6ab4c..748b053d7eaa 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -97,14 +97,14 @@ class CrossStageSafety extends TreeMapWithStages { } /** Transform quoted trees while maintaining level correctness */ - override protected def transformQuotedExpr(body: Tree, quote: QuotedExpr)(using Context): Tree = { + override protected def transformQuote(body: Tree, quote: Quote)(using Context): Tree = { if (ctx.property(InAnnotation).isDefined) report.error("Cannot have a quote in an annotation", quote.srcPos) val transformedBody = transformQuoteBody(body, quote.span) val stripAnnotsDeep: TypeMap = new TypeMap: def apply(tp: Type): Type = mapOver(tp.stripAnnots) val tpt1 = TypeTree(healType(quote.tpt.srcPos)(stripAnnotsDeep(quote.tpt.tpe))) - cpy.QuotedExpr(quote)(transformedBody, tpt1) + cpy.Quote(quote)(transformedBody, tpt1) } override protected def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = { @@ -142,7 +142,7 @@ class CrossStageSafety extends TreeMapWithStages { * - If inside inlined code, expand the macro code. * - If inside of a macro definition, check the validity of the macro. */ - protected def transformSplice(body: Tree, splice: SplicedExpr)(using Context): Tree = { + protected def transformSplice(body: Tree, splice: Splice)(using Context): Tree = { val body1 = transform(body)(using spliceContext) val tpt1 = if level == 0 then @@ -150,7 +150,7 @@ class CrossStageSafety extends TreeMapWithStages { else val tp = healType(splice.srcPos)(splice.tpe.widenTermRefExpr) TypeTree(tp).withSpan(splice.tpt.span) - cpy.SplicedExpr(splice)(body1, tpt1) + cpy.Splice(splice)(body1, tpt1) } protected def transformSpliceType(body: Tree, splice: Select)(using Context): Tree = { diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index 1ed6d4c37e19..a292b4b079be 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -25,8 +25,8 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { * * - `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T]()` */ - protected def transformQuotedExpr(body: Tree, quote: QuotedExpr)(using Context): Tree = - cpy.QuotedExpr(quote)(body, quote.tpt) + protected def transformQuote(body: Tree, quote: Quote)(using Context): Tree = + cpy.Quote(quote)(body, quote.tpt) /** Transform the quote `quote` which contains the quoted `body`. * @@ -37,7 +37,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, body :: Nil), quote.args) /** Transform the expression splice `splice` which contains the spliced `body`. */ - protected def transformSplice(body: Tree, splice: SplicedExpr)(using Context): Tree + protected def transformSplice(body: Tree, splice: Splice)(using Context): Tree /** Transform the type splice `splice` which contains the spliced `body`. */ protected def transformSpliceType(body: Tree, splice: Select)(using Context): Tree @@ -62,24 +62,24 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { try transformQuotedType(quotedTree, tree) finally inQuoteOrSplice = old - case tree @ QuotedExpr(quotedTree, _) => + case tree @ Quote(quotedTree, _) => val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(quotedTree) match { - case SplicedExpr(t, _) => + case Splice(t, _) => // Optimization: `'{ $x }` --> `x` // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]` transform(t).asInstance(tree.tpe) case _ => - transformQuotedExpr(quotedTree, tree) + transformQuote(quotedTree, tree) } finally inQuoteOrSplice = old - case tree @ SplicedExpr(splicedTree, _) => + case tree @ Splice(splicedTree, _) => val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(splicedTree) match { - case QuotedExpr(t, _) => + case Quote(t, _) => // Optimization: `${ 'x }` --> `x` transform(t) case _ => diff --git a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala index 6e264385fd3e..976df4f436f7 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala @@ -160,9 +160,9 @@ class ElimByName extends MiniPhase, InfoTransformer: } override def transformOther(tree: Tree)(using Context): Tree = tree match - case tree @ SplicedExpr(spliced, tpt) => + case tree @ Splice(spliced, tpt) => assert(dotty.tools.dotc.inlines.Inlines.inInlineMethod) - cpy.SplicedExpr(tree)(transformAllDeep(spliced), tpt) + cpy.Splice(tree)(transformAllDeep(spliced), tpt) case tree => tree object ElimByName: diff --git a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala index cad54982d10d..9d874bf6b00f 100644 --- a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -153,9 +153,9 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase => override def transformOther(tree: Tree)(using Context): Tree = tree match { case tree: Export => EmptyTree case tree: NamedArg => transformAllDeep(tree.arg) - case tree @ SplicedExpr(expr, tpt) => + case tree @ Splice(expr, tpt) => assert(dotty.tools.dotc.inlines.Inlines.inInlineMethod) - cpy.SplicedExpr(tree)(transformAllDeep(expr), transformAllDeep(tpt)) + cpy.Splice(tree)(transformAllDeep(expr), transformAllDeep(tpt)) case tree => if (tree.isType) toTypeTree(tree) else tree } diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 341633e30960..60714d273bf0 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -45,11 +45,11 @@ class Inlining extends MacroTransform { new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match - case _: QuotedExpr => + case _: Quote => traverseChildren(tree)(using StagingLevel.quoteContext) case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of => traverseChildren(tree)(using StagingLevel.quoteContext) - case _: SplicedExpr => + case _: Splice => traverseChildren(tree)(using StagingLevel.spliceContext) case tree: RefTree if !Inlines.inInlineMethod && StagingLevel.level == 0 => assert(!tree.symbol.isInlineMethod, tree.show) @@ -99,11 +99,11 @@ class Inlining extends MacroTransform { val tree1 = super.transform(tree) if tree1.tpe.isError then tree1 else Inlines.inlineCall(tree1) - case _: QuotedExpr => + case _: Quote => super.transform(tree)(using StagingLevel.quoteContext) case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of => super.transform(tree)(using StagingLevel.quoteContext) - case _: SplicedExpr => + case _: Splice => super.transform(tree)(using StagingLevel.spliceContext) case _: PackageDef => super.transform(tree) match diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index c020a53e7fdf..c60562baaa7f 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -83,9 +83,9 @@ class PickleQuotes extends MacroTransform { override def checkPostCondition(tree: Tree)(using Context): Unit = tree match - case tree: QuotedExpr => + case tree: Quote => assert(Inlines.inInlineMethod) - case tree: SplicedExpr => + case tree: Splice => assert(Inlines.inInlineMethod) case tree: RefTree if !Inlines.inInlineMethod => assert(tree.symbol != defn.QuotedTypeModule_of) @@ -100,7 +100,7 @@ class PickleQuotes extends MacroTransform { protected def newTransformer(using Context): Transformer = new Transformer { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match - case Apply(Select(QuotedExpr(expr, tpt), nme.apply), List(quotes)) => + case Apply(Select(Quote(expr, tpt), nme.apply), List(quotes)) => val (contents, codeWithHoles) = makeHoles(expr) val sourceRef = Inlines.inlineCallTrace(ctx.owner, tree.sourcePos) val codeWithHoles2 = Inlined(sourceRef, Nil, codeWithHoles) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 5cfa74fd5610..eac9a8d70654 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -485,7 +485,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase ) case Block(_, Closure(_, _, tpt)) if ExpandSAMs.needsWrapperClass(tpt.tpe) => superAcc.withInvalidCurrentClass(super.transform(tree)) - case QuotedExpr(expr, _) => + case Quote(expr, _) => ctx.compilationUnit.needsStaging = true super.transform(tree) case tree => diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 2e6549bc8ec8..4b6d434d80b0 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -44,7 +44,7 @@ object Splicer { * See: `Staging` */ def splice(tree: Tree, splicePos: SrcPos, spliceExpansionPos: SrcPos, classLoader: ClassLoader)(using Context): Tree = tree match { - case QuotedExpr(quotedTree, _) => quotedTree + case Quote(quotedTree, _) => quotedTree case _ => val macroOwner = newSymbol(ctx.owner, nme.MACROkw, Macro | Synthetic, defn.AnyType, coord = tree.span) try @@ -136,7 +136,7 @@ object Splicer { * See: `Staging` */ def checkValidMacroBody(tree: Tree)(using Context): Unit = tree match { - case QuotedExpr(_, _) => // ok + case Quote(_, _) => // ok case _ => type Env = Set[Symbol] @@ -155,10 +155,10 @@ object Splicer { case Block(Nil, expr) => checkIfValidArgument(expr) case Typed(expr, _) => checkIfValidArgument(expr) - case Apply(Select(QuotedExpr(expr, tpt), nme.apply), _) => + case Apply(Select(Quote(expr, tpt), nme.apply), _) => val noSpliceChecker = new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match - case SplicedExpr(_, _) => + case Splice(_, _) => report.error("Quoted argument of macros may not have splices", tree.srcPos) case _ => traverseChildren(tree) @@ -203,7 +203,7 @@ object Splicer { case Typed(expr, _) => checkIfValidStaticCall(expr) - case Apply(Select(QuotedExpr(quoted, tpt), nme.apply), _) => + case Apply(Select(Quote(quoted, tpt), nme.apply), _) => // OK, canceled and warning emitted case Call(fn, args) @@ -240,7 +240,7 @@ object Splicer { override protected def interpretTree(tree: Tree)(implicit env: Env): Object = tree match { // Interpret level -1 quoted code `'{...}` (assumed without level 0 splices) - case Apply(Select(QuotedExpr(expr, _), nme.apply), _) => + case Apply(Select(Quote(expr, _), nme.apply), _) => val expr1 = expr match { case expr: Ident if expr.symbol.isAllOf(InlineByNameProxy) => // inline proxy for by-name parameter diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index ca3acb6b8fac..af77588419b5 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -86,7 +86,7 @@ class Splicing extends MacroTransform: override def transform(tree: tpd.Tree)(using Context): tpd.Tree = assert(level == 0) tree match - case Apply(Select(QuotedExpr(expr, _), nme.apply),List(quotes)) => + case Apply(Select(Quote(expr, _), nme.apply),List(quotes)) => QuoteTransformer().transform(tree) case TypeApply(_, _) if tree.symbol == defn.QuotedTypeModule_of => QuoteTransformer().transform(tree) @@ -111,10 +111,10 @@ class Splicing extends MacroTransform: override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match - case tree: SplicedExpr => + case tree: Splice => if level > 1 then val expr1 = super.transform(tree.expr)(using spliceContext) - cpy.SplicedExpr(tree)(expr1, tree.tpt) + cpy.Splice(tree)(expr1, tree.tpt) else val holeIdx = numHoles numHoles += 1 @@ -134,7 +134,7 @@ class Splicing extends MacroTransform: typeHoles.put(qual, hole) hole cpy.TypeDef(tree)(rhs = hole) - case Apply(Select(QuotedExpr(expr, tpt), nme.apply),List(quotes)) => + case Apply(Select(Quote(expr, tpt), nme.apply),List(quotes)) => super.transform(tree)(using quoteContext) case _: Template => for sym <- tree.symbol.owner.info.decls do @@ -234,10 +234,10 @@ class Splicing extends MacroTransform: case tree @ Assign(lhs: RefTree, rhs) => if isCaptured(lhs.symbol) then transformSplicedAssign(tree) else super.transform(tree) - case SplicedExpr(expr, tpt) => + case Splice(expr, tpt) => val expr1 = transform(expr)(using spliceContext) - cpy.SplicedExpr(tree)(expr1, tpt) - case Apply(sel @ Select(app @ QuotedExpr(expr, tpt), nme.apply), quotesArgs) => + cpy.Splice(tree)(expr1, tpt) + case Apply(sel @ Select(app @ Quote(expr, tpt), nme.apply), quotesArgs) => expr match case expr: RefTree if isCaptured(expr.symbol) => capturedTerm(expr) @@ -246,7 +246,7 @@ class Splicing extends MacroTransform: if level > 1 then transform(expr)(using quoteContext) else transformLevel0QuoteContent(expr)(using quoteContext) } - cpy.Apply(tree)(cpy.Select(sel)(cpy.QuotedExpr(app)(newExpr, tpt), nme.apply), quotesArgs) + cpy.Apply(tree)(cpy.Select(sel)(cpy.Quote(app)(newExpr, tpt), nme.apply), quotesArgs) case Apply(TypeApply(typeof, List(tpt)), List(quotes)) if tree.symbol == defn.QuotedTypeModule_of && containsCapturedType(tpt.tpe) => val newContent = capturedPartTypes(tpt) @@ -409,10 +409,10 @@ class Splicing extends MacroTransform: body(using ctx.withOwner(meth)).changeOwner(ctx.owner, meth) } }) - SplicedExpr(closure, TypeTree(tpe)) + Splice(closure, TypeTree(tpe)) private def quoted(expr: Tree)(using Context): Tree = - QuotedExpr(expr, TypeTree(expr.tpe.widenTermRefExpr)) + Quote(expr, TypeTree(expr.tpe.widenTermRefExpr)) .select(nme.apply) .appliedTo(quotes.nn) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index f7518ec8b9f5..23c851a33408 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -312,7 +312,7 @@ object SpaceEngine { def isIrrefutableQuotedPattern(unapp: tpd.Tree, implicits: List[tpd.Tree], pt: Type)(using Context): Boolean = { implicits.headOption match // pattern '{ $x: T } - case Some(tpd.Apply(tpd.Select(tpd.QuotedExpr(tpd.TypeApply(fn, List(tpt)), _), nme.apply), _)) + case Some(tpd.Apply(tpd.Select(tpd.Quote(tpd.TypeApply(fn, List(tpt)), _), nme.apply), _)) if unapp.symbol.owner.eq(defn.QuoteMatching_ExprMatchModule) && fn.symbol.eq(defn.QuotedRuntimePatterns_patternHole) => pt <:< defn.QuotedExprClass.typeRef.appliedTo(tpt.tpe) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 77c9eb519db1..79d6501ccb2d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1097,7 +1097,7 @@ trait Applications extends Compatibility { } else { val app = tree.fun match - case _: untpd.SplicedExpr if ctx.mode.is(Mode.QuotedPattern) => typedAppliedSplice(tree, pt) + case _: untpd.Splice if ctx.mode.is(Mode.QuotedPattern) => typedAppliedSplice(tree, pt) case _ => realApply app match { case Apply(fn @ Select(left, _), right :: Nil) if fn.hasType => diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index d47c7abf3fc3..9ec4a8648695 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -31,15 +31,15 @@ trait QuotesAndSplices { import tpd._ - def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = + def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = if tree.tpt.isEmpty then typedQuoteSyntactic(tree, pt) else val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem)(using quoteContext) - assignType(untpd.cpy.QuotedExpr(tree)(expr1, tpt1), tpt1) + assignType(untpd.cpy.Quote(tree)(expr1, tpt1), tpt1) - def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = + def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = if tree.tpt.isEmpty then typedSpliceSyntactic(tree, pt) else @@ -48,7 +48,7 @@ trait QuotesAndSplices { defn.FunctionType(1, isContextual = true) .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) val expr1 = typed(tree.expr, splicedType)(using spliceContext) - assignType(untpd.cpy.SplicedExpr(tree)(expr1, tpt1), tpt1) + assignType(untpd.cpy.Splice(tree)(expr1, tpt1), tpt1) def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree = val tpt = typedType(tree.tpt) @@ -57,10 +57,10 @@ trait QuotesAndSplices { /** Translate `'{ e }` into `scala.quoted.Expr.apply(e)` and `'[T]` into `scala.quoted.Type.apply[T]` * while tracking the quotation level in the context. */ - private def typedQuoteSyntactic(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = { + private def typedQuoteSyntactic(tree: untpd.Quote, pt: Type)(using Context): Tree = { record("typedQuote") tree.expr match { - case untpd.SplicedExpr(innerExpr, _) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => + case untpd.Splice(innerExpr, _) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => report.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.srcPos) case _ => } @@ -83,7 +83,7 @@ trait QuotesAndSplices { else val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.expr) val quotedExpr = typedApply(exprQuoteTree, pt)(using quoteContext) match - case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => QuotedExpr(quotedExpr, tpt) + case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => Quote(quotedExpr, tpt) makeInlineable(quotedExpr.select(nme.apply).appliedTo(quotes).withSpan(tree.span)) } @@ -93,11 +93,11 @@ trait QuotesAndSplices { } /** Translate `${ t: Expr[T] }` into expression `t.splice` while tracking the quotation level in the context */ - private def typedSpliceSyntactic(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = { + private def typedSpliceSyntactic(tree: untpd.Splice, pt: Type)(using Context): Tree = { record("typedSplice") checkSpliceOutsideQuote(tree) tree.expr match { - case untpd.QuotedExpr(innerExpr, _) if innerExpr.isTerm => + case untpd.Quote(innerExpr, _) if innerExpr.isTerm => report.warning("Canceled quote directly inside a splice. ${ '{ XYZ } } is equivalent to XYZ.", tree.srcPos) return typed(innerExpr, pt) case _ => @@ -110,7 +110,7 @@ trait QuotesAndSplices { using spliceContext.retractMode(Mode.QuotedPattern).addMode(Mode.Pattern).withOwner(spliceOwner(ctx))) val baseType = pat.tpe.baseType(defn.QuotedExprClass) val argType = if baseType != NoType then baseType.argTypesHi.head else defn.NothingType - SplicedExpr(pat, TypeTree(argType)).withSpan(tree.span) + Splice(pat, TypeTree(argType)).withSpan(tree.span) } else { report.error(em"Type must be fully defined.\nConsider annotating the splice using a type ascription:\n ($tree: XYZ).", tree.expr.srcPos) @@ -132,7 +132,7 @@ trait QuotesAndSplices { typedApply(internalSplice, pt)(using spliceContext).withSpan(tree.span) match case tree @ Apply(TypeApply(_, tpt :: Nil), spliced :: Nil) if tree.symbol == defn.QuotedRuntime_exprSplice => - cpy.SplicedExpr(tree)(spliced, tpt) + cpy.Splice(tree)(spliced, tpt) case tree => tree } } @@ -146,7 +146,7 @@ trait QuotesAndSplices { */ def typedAppliedSplice(tree: untpd.Apply, pt: Type)(using Context): Tree = { assert(ctx.mode.is(Mode.QuotedPattern)) - val untpd.Apply(splice: untpd.SplicedExpr, args) = tree: @unchecked + val untpd.Apply(splice: untpd.Splice, args) = tree: @unchecked if !isFullyDefined(pt, ForceDegree.flipBottom) then report.error(em"Type must be fully defined.", splice.srcPos) tree.withType(UnspecifiedErrorType) @@ -245,12 +245,12 @@ trait QuotesAndSplices { val freshTypeBindingsBuff = new mutable.ListBuffer[Tree] val typePatBuf = new mutable.ListBuffer[Tree] override def transform(tree: Tree)(using Context) = tree match { - case Typed(SplicedExpr(pat, _), tpt) if !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => + case Typed(Splice(pat, _), tpt) if !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => val tpt1 = transform(tpt) // Transform type bindings val exprTpt = AppliedTypeTree(TypeTree(defn.QuotedExprClass.typeRef), tpt1 :: Nil) - val newSplice = cpy.SplicedExpr(tree)(pat, tpt1) + val newSplice = cpy.Splice(tree)(pat, tpt1) transform(newSplice) - case Apply(TypeApply(fn, targs), SplicedExpr(pat, _) :: args :: Nil) if fn.symbol == defn.QuotedRuntimePatterns_patternHigherOrderHole => + case Apply(TypeApply(fn, targs), Splice(pat, _) :: args :: Nil) if fn.symbol == defn.QuotedRuntimePatterns_patternHigherOrderHole => args match // TODO support these patterns. Possibly using scala.quoted.util.Var case SeqLiteral(args, _) => for arg <- args; if arg.symbol.is(Mutable) do @@ -262,7 +262,7 @@ trait QuotesAndSplices { val pat1 = if (patType eq patType1) pat else pat.withType(patType1) patBuf += pat1 } - case SplicedExpr(pat, _) => + case Splice(pat, _) => try ref(defn.QuotedRuntimePatterns_patternHole.termRef).appliedToType(tree.tpe).withSpan(tree.span) finally { val patType = pat.tpe.widen @@ -401,7 +401,7 @@ trait QuotesAndSplices { * ) => ... * ``` */ - private def typedQuotePattern(tree: untpd.QuotedExpr, pt: Type, qctx: Tree)(using Context): Tree = { + private def typedQuotePattern(tree: untpd.Quote, pt: Type, qctx: Tree)(using Context): Tree = { val quoted = tree.expr if quoted.isTerm && !pt.derivesFrom(defn.QuotedExprClass) then report.error("Quote pattern can only match scrutinees of type scala.quoted.Expr", tree.srcPos) @@ -461,7 +461,7 @@ trait QuotesAndSplices { val quoteClass = if (quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass val quotedPattern = - if (quoted.isTerm) tpd.QuotedExpr(shape, TypeTree(defn.AnyType)).select(nme.apply).appliedTo(qctx) + if (quoted.isTerm) tpd.Quote(shape, TypeTree(defn.AnyType)).select(nme.apply).appliedTo(qctx) else ref(defn.QuotedTypeModule_of.termRef).appliedToTypeTree(shape).appliedTo(qctx) val matchModule = if quoted.isTerm then defn.QuoteMatching_ExprMatch else defn.QuoteMatching_TypeMatch diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 9fe679a0583b..fc51af646b17 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -392,13 +392,13 @@ trait TypeAssigner { def assignType(tree: untpd.Inlined, bindings: List[Tree], expansion: Tree)(using Context): Inlined = tree.withType(avoidingType(expansion, bindings)) - def assignType(tree: untpd.QuotedExpr, tpt: Tree)(using Context): QuotedExpr = + def assignType(tree: untpd.Quote, tpt: Tree)(using Context): Quote = val lambdaType = // Quotes ?=> Expr[T] defn.FunctionType(1, isContextual = true) .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt.tpe)) tree.withType(lambdaType) - def assignType(tree: untpd.SplicedExpr, tpt: Tree)(using Context): SplicedExpr = + def assignType(tree: untpd.Splice, tpt: Tree)(using Context): Splice = tree.withType(tpt.tpe) def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(using Context): If = diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index aa0243146241..4bc012b5b226 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3075,8 +3075,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case tree: untpd.ParsedTry => typedTry(tree, pt) case tree @ untpd.PostfixOp(qual, Ident(nme.WILDCARD)) => typedAsFunction(tree, pt) case untpd.EmptyTree => tpd.EmptyTree - case tree: untpd.QuotedExpr => typedQuotedExpr(tree, pt) - case tree: untpd.SplicedExpr => typedSplicedExpr(tree, pt) + case tree: untpd.Quote => typedQuote(tree, pt) + case tree: untpd.Splice => typedSplice(tree, pt) case tree: untpd.MacroTree => report.error("Unexpected macro", tree.srcPos); tpd.nullLiteral // ill-formed code may reach here case tree: untpd.Hole => typedHole(tree, pt) case _ => typedUnadapted(desugar(tree, pt), pt, locked) From 76d0daf885f10041bbf9ee8bea89dcb089fa817e Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 25 Apr 2023 13:25:50 +0200 Subject: [PATCH 12/46] Add documentation --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 65a0cd6ba710..546e55ee22e3 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -677,11 +677,23 @@ object Trees { override def isType = expansion.isType } + /** A tree representing a quote `'{ expr } + * + * @param expr The tree that was quoted + * @param tpt The type of the tree that was quoted, + * EmptyTree if this tree comes from the parser. + */ case class Quote[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Quote[T] } + /** A tree representing a splice `${ expr }` + * + * @param expr The tree that was spliced + * @param tpt The type of the tree that was spliced, + * EmptyTree if this tree comes from the parser. + */ case class Splice[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Splice[T] From 75bacdbadf9ecc9db4c39223e05669e0a329b526 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 25 Apr 2023 13:37:10 +0200 Subject: [PATCH 13/46] Backwards compatibility support for quotes and splice in reflection API --- .../quoted/runtime/impl/QuotesImpl.scala | 23 ++++++++++++++++--- tests/run-staging/multi-staging.check | 2 +- tests/run-staging/quote-nested-5.check | 4 ++-- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 1949304ca287..f63ef3feb041 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -609,11 +609,13 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler end extension end NamedArgMethods - type Apply = tpd.Apply + type Apply = tpd.Apply | tpd.Quote | tpd.Splice object ApplyTypeTest extends TypeTest[Tree, Apply]: def unapply(x: Tree): Option[Apply & x.type] = x match case x: (tpd.Apply & x.type) => Some(x) + case x: (tpd.Quote & x.type) => Some(x) // TODO expose Quote AST in Quotes + case x: (tpd.Splice & x.type) => Some(x) // TODO expose Splice AST in Quotes case _ => None end ApplyTypeTest @@ -630,8 +632,23 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler given ApplyMethods: ApplyMethods with extension (self: Apply) - def fun: Term = self.fun - def args: List[Term] = self.args + def fun: Term = self match + case self: tpd.Apply => self.fun + case self: tpd.Quote => // TODO expose Quote AST in Quotes + import dotty.tools.dotc.ast.tpd.TreeOps + tpd.ref(dotc.core.Symbols.defn.QuotedRuntime_exprQuote) + .appliedToTypeTree(self.tpt) + .withSpan(self.span) + case self: tpd.Splice => // TODO expose Splice AST in Quotes + import dotty.tools.dotc.ast.tpd.TreeOps + tpd.ref(dotc.core.Symbols.defn.QuotedRuntime_exprSplice) + .appliedToTypeTree(self.tpt) + .withSpan(self.span) + + def args: List[Term] = self match + case self: tpd.Apply => self.args + case self: tpd.Quote => List(self.expr) // TODO expose Quote AST in Quotes + case self: tpd.Splice => List(self.expr) // TODO expose Splice AST in Quotes end extension end ApplyMethods diff --git a/tests/run-staging/multi-staging.check b/tests/run-staging/multi-staging.check index 5d12306ba4ef..76adcfec3034 100644 --- a/tests/run-staging/multi-staging.check +++ b/tests/run-staging/multi-staging.check @@ -1,5 +1,5 @@ stage1 code: ((q1: scala.quoted.Quotes) ?=> { val x1: scala.Int = 2 - scala.quoted.runtime.Expr.quote[scala.Int](1.+(scala.quoted.runtime.Expr.nestedSplice[scala.Int](q1)(((evidence$5: scala.quoted.Quotes) ?=> scala.quoted.Expr.apply[scala.Int](x1)(scala.quoted.ToExpr.IntToExpr[scala.Int])(evidence$5))))).apply(using q1) + scala.quoted.runtime.Expr.quote[scala.Int](1.+(scala.quoted.runtime.Expr.splice[scala.Int](((evidence$5: scala.quoted.Quotes) ?=> scala.quoted.Expr.apply[scala.Int](x1)(scala.quoted.ToExpr.IntToExpr[scala.Int])(evidence$5))))).apply(using q1) }) 3 diff --git a/tests/run-staging/quote-nested-5.check b/tests/run-staging/quote-nested-5.check index f29acb3b347a..d5fee708c460 100644 --- a/tests/run-staging/quote-nested-5.check +++ b/tests/run-staging/quote-nested-5.check @@ -1,4 +1,4 @@ ((q: scala.quoted.Quotes) ?=> { val a: scala.quoted.Expr[scala.Int] = scala.quoted.runtime.Expr.quote[scala.Int](4).apply(using q) - ((q2: scala.quoted.Quotes) ?=> ((evidence$3: scala.quoted.Quotes) ?=> a).asInstanceOf[scala.ContextFunction1[scala.quoted.Quotes, scala.quoted.Expr[scala.Int]]].apply(using q2)).apply(using q) -}) + ((q2: scala.quoted.Quotes) ?=> ((evidence$2: scala.quoted.Quotes) ?=> a).asInstanceOf[scala.ContextFunction1[scala.quoted.Quotes, scala.quoted.Expr[scala.Int]]].apply(using q2)) +}.apply(using q)) From 0e6dc2eee80a5aed21876355a62906a84166ffb3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 25 Apr 2023 13:48:25 +0200 Subject: [PATCH 14/46] Add regression test Closes #6991 --- tests/neg-macros/i6991.check | 10 ++++++++++ tests/neg-macros/i6991.scala | 16 ++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/neg-macros/i6991.check create mode 100644 tests/neg-macros/i6991.scala diff --git a/tests/neg-macros/i6991.check b/tests/neg-macros/i6991.check new file mode 100644 index 000000000000..57d611a09053 --- /dev/null +++ b/tests/neg-macros/i6991.check @@ -0,0 +1,10 @@ +-- [E050] Type Error: tests/neg-macros/i6991.scala:11:14 --------------------------------------------------------------- +11 | case '{($x: Foo)($bar: String)} => '{"Hello World"} // error + | ^^^^^^^ + | expression does not take parameters + | + | longer explanation available when compiling with `-explain` +-- [E008] Not Found Error: tests/neg-macros/i6991.scala:12:23 ---------------------------------------------------------- +12 | case '{($x: Foo).apply($bar: String)} => '{"Hello World"} // error + | ^^^^^^^^^^^^^^^ + | value apply is not a member of macros.Foo diff --git a/tests/neg-macros/i6991.scala b/tests/neg-macros/i6991.scala new file mode 100644 index 000000000000..c6838261ed7a --- /dev/null +++ b/tests/neg-macros/i6991.scala @@ -0,0 +1,16 @@ +import scala.quoted._ + +object macros { + inline def mcr(x: => Any): Any = ${mcrImpl('x)} + + class Foo // { def apply(str: String) = "100" } + class Bar { def apply(str: String) = "100" } + + def mcrImpl(body: Expr[Any])(using ctx: Quotes): Expr[Any] = { + body match { + case '{($x: Foo)($bar: String)} => '{"Hello World"} // error + case '{($x: Foo).apply($bar: String)} => '{"Hello World"} // error + case '{($x: Bar)($bar: String)} => '{"Hello World"} + } + } +} From cdd5ffb3f47211a8fb9805f11f76234846c1fba8 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 26 Apr 2023 08:30:13 +0200 Subject: [PATCH 15/46] Adapt REPL test output --- compiler/test-resources/repl-macros/i5551 | 2 +- compiler/test-resources/repl/i10355 | 6 ++++-- staging/test-resources/repl-staging/i6007 | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/test-resources/repl-macros/i5551 b/compiler/test-resources/repl-macros/i5551 index d71251e9a824..984551438b51 100644 --- a/compiler/test-resources/repl-macros/i5551 +++ b/compiler/test-resources/repl-macros/i5551 @@ -1,7 +1,7 @@ scala> import scala.quoted._ scala> def assertImpl(expr: Expr[Boolean])(using q: Quotes) = '{ if !($expr) then throw new AssertionError("failed assertion")} def assertImpl - (expr: quoted.Expr[Boolean])(using q: quoted.Quotes): quoted.Expr[Unit] + (expr: quoted.Expr[Boolean])(using q: quoted.Quotes): scala.quoted.Expr[Unit] scala> inline def assert(expr: => Boolean): Unit = ${ assertImpl('{expr}) } def assert(expr: => Boolean): Unit diff --git a/compiler/test-resources/repl/i10355 b/compiler/test-resources/repl/i10355 index bfe3af835c87..294b9d7f1101 100644 --- a/compiler/test-resources/repl/i10355 +++ b/compiler/test-resources/repl/i10355 @@ -1,5 +1,7 @@ scala> import scala.quoted._ scala> def foo(expr: Expr[Any])(using Quotes) = expr match { case '{ $x: t } => '{ $x: Any } } -def foo(expr: quoted.Expr[Any])(using x$2: quoted.Quotes): quoted.Expr[Any] +def foo + (expr: quoted.Expr[Any])(using x$2: quoted.Quotes): scala.quoted.Expr[Any] scala> def bar(expr: Expr[Any])(using Quotes) = expr match { case '{ $x: t } => '{ val a: t = ??? ; ???} } -def bar(expr: quoted.Expr[Any])(using x$2: quoted.Quotes): quoted.Expr[Nothing] +def bar + (expr: quoted.Expr[Any])(using x$2: quoted.Quotes): scala.quoted.Expr[Nothing] diff --git a/staging/test-resources/repl-staging/i6007 b/staging/test-resources/repl-staging/i6007 index be9d5c0f92d6..0d6fbd0cffb1 100644 --- a/staging/test-resources/repl-staging/i6007 +++ b/staging/test-resources/repl-staging/i6007 @@ -3,7 +3,7 @@ scala> import quoted.staging.{Compiler => StagingCompiler, _} scala> implicit def compiler: StagingCompiler = StagingCompiler.make(getClass.getClassLoader) def compiler: quoted.staging.Compiler scala> def v(using Quotes) = '{ (if true then Some(1) else None).map(v => v+1) } -def v(using x$1: quoted.Quotes): quoted.Expr[Option[Int]] +def v(using x$1: quoted.Quotes): scala.quoted.Expr[Option[Int]] scala> scala.quoted.staging.withQuotes(v.show) val res0: String = (if (true) scala.Some.apply[scala.Int](1) else scala.None).map[scala.Int](((v: scala.Int) => v.+(1))) scala> scala.quoted.staging.run(v) From 04f020f8a824f3a73f47de6e74a52becfb26410a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 27 Apr 2023 08:18:36 +0200 Subject: [PATCH 16/46] Remove `Splice.isInBraces` --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 1 - compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 546e55ee22e3..d748d531845f 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -697,7 +697,6 @@ object Trees { case class Splice[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Splice[T] - def isInBraces: Boolean = span.end != expr.span.end } /** A type tree that represents an existing or inferred type */ diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 9ec4a8648695..ed026d331b60 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -147,10 +147,11 @@ trait QuotesAndSplices { def typedAppliedSplice(tree: untpd.Apply, pt: Type)(using Context): Tree = { assert(ctx.mode.is(Mode.QuotedPattern)) val untpd.Apply(splice: untpd.Splice, args) = tree: @unchecked + def isInBraces: Boolean = splice.span.end != splice.expr.span.end if !isFullyDefined(pt, ForceDegree.flipBottom) then report.error(em"Type must be fully defined.", splice.srcPos) tree.withType(UnspecifiedErrorType) - else if splice.isInBraces then // ${x}(...) match an application + else if isInBraces then // ${x}(...) match an application val typedArgs = args.map(arg => typedExpr(arg)) val argTypes = typedArgs.map(_.tpe.widenTermRefExpr) val splice1 = typedSpliceSyntactic(splice, defn.FunctionOf(argTypes, pt)) From 35db4a1c7015781556962e0a07f4f5e099f85495 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 27 Apr 2023 09:16:29 +0200 Subject: [PATCH 17/46] Move Quote/Splice retyping into ReTyper --- .../tools/dotc/typer/QuotesAndSplices.scala | 37 +++++-------------- .../src/dotty/tools/dotc/typer/ReTyper.scala | 14 +++++++ 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index ed026d331b60..f81c0c2de56a 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -31,34 +31,12 @@ trait QuotesAndSplices { import tpd._ - def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = - if tree.tpt.isEmpty then - typedQuoteSyntactic(tree, pt) - else - val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) - val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem)(using quoteContext) - assignType(untpd.cpy.Quote(tree)(expr1, tpt1), tpt1) - - def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = - if tree.tpt.isEmpty then - typedSpliceSyntactic(tree, pt) - else - val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) - val splicedType = // Quotes ?=> Expr[T] - defn.FunctionType(1, isContextual = true) - .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) - val expr1 = typed(tree.expr, splicedType)(using spliceContext) - assignType(untpd.cpy.Splice(tree)(expr1, tpt1), tpt1) - - def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree = - val tpt = typedType(tree.tpt) - assignType(tree, tpt) - /** Translate `'{ e }` into `scala.quoted.Expr.apply(e)` and `'[T]` into `scala.quoted.Type.apply[T]` * while tracking the quotation level in the context. */ - private def typedQuoteSyntactic(tree: untpd.Quote, pt: Type)(using Context): Tree = { + def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = { record("typedQuote") + assert(tree.tpt.isEmpty) tree.expr match { case untpd.Splice(innerExpr, _) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => report.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.srcPos) @@ -93,8 +71,9 @@ trait QuotesAndSplices { } /** Translate `${ t: Expr[T] }` into expression `t.splice` while tracking the quotation level in the context */ - private def typedSpliceSyntactic(tree: untpd.Splice, pt: Type)(using Context): Tree = { + def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = { record("typedSplice") + assert(tree.tpt.isEmpty) checkSpliceOutsideQuote(tree) tree.expr match { case untpd.Quote(innerExpr, _) if innerExpr.isTerm => @@ -137,6 +116,10 @@ trait QuotesAndSplices { } } + def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree = + val tpt = typedType(tree.tpt) + assignType(tree, tpt) + /** Types a splice applied to some arguments `$f(arg1, ..., argn)` in a quote pattern. * * The tree is desugared into `$f.apply(arg1, ..., argn)` where the expression `$f` @@ -154,7 +137,7 @@ trait QuotesAndSplices { else if isInBraces then // ${x}(...) match an application val typedArgs = args.map(arg => typedExpr(arg)) val argTypes = typedArgs.map(_.tpe.widenTermRefExpr) - val splice1 = typedSpliceSyntactic(splice, defn.FunctionOf(argTypes, pt)) + val splice1 = typedSplice(splice, defn.FunctionOf(argTypes, pt)) Apply(splice1.select(nme.apply), typedArgs).withType(pt).withSpan(tree.span) else // $x(...) higher-order quasipattern val typedArgs = args.map { @@ -167,7 +150,7 @@ trait QuotesAndSplices { if args.isEmpty then report.error("Missing arguments for open pattern", tree.srcPos) val argTypes = typedArgs.map(_.tpe.widenTermRefExpr) - val typedPat = typedSpliceSyntactic(splice, defn.FunctionOf(argTypes, pt)) + val typedPat = typedSplice(splice, defn.FunctionOf(argTypes, pt)) ref(defn.QuotedRuntimePatterns_patternHigherOrderHole).appliedToType(pt).appliedTo(typedPat, SeqLiteral(typedArgs, TypeTree(defn.AnyType))) } diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index c64f541fd811..9efe3b31a356 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -12,6 +12,7 @@ import ast.{tpd, untpd} import scala.util.control.NonFatal import util.Spans.Span import Nullables._ +import staging.StagingLevel.* /** A version of Typer that keeps all symbols defined and referenced in a * previously typed tree. @@ -94,6 +95,19 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking override def typedUnApply(tree: untpd.Apply, selType: Type)(using Context): Tree = typedApply(tree, selType) + override def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = + val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) + val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem)(using quoteContext) + assignType(untpd.cpy.Quote(tree)(expr1, tpt1), tpt1) + + override def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = + val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) + val splicedType = // Quotes ?=> Expr[T] + defn.FunctionType(1, isContextual = true) + .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) + val expr1 = typed(tree.expr, splicedType)(using spliceContext) + assignType(untpd.cpy.Splice(tree)(expr1, tpt1), tpt1) + override def localDummy(cls: ClassSymbol, impl: untpd.Template)(using Context): Symbol = impl.symbol override def retrieveSym(tree: untpd.Tree)(using Context): Symbol = tree.symbol From 1ef7f59095b901201074f662fb9af6b4f4ad0c76 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 27 Apr 2023 09:55:45 +0200 Subject: [PATCH 18/46] Add documentation --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index d748d531845f..3427146a6baa 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -677,11 +677,20 @@ object Trees { override def isType = expansion.isType } - /** A tree representing a quote `'{ expr } + /** A tree representing a quote `'{ expr }` + * `Quote`s are created by the `Parser` with an empty `tpt`. In typer + * they can be typed as a `Quote` with a known `tpt` or desugared and + * typed as a quote pattern. + * + * `Quotes` are checked transformed in the `staging`, `splicing` and `pickleQuotes` + * phases. After `pickleQuotes` phase, the only quotes that exist are in `inline` + * methods. These are dropped when we remove the inline method implementations. + * + * The `tpt` will be transformed in `staging` and used in `pickleQuotes` to create the + * pickled representation of the quote. * * @param expr The tree that was quoted - * @param tpt The type of the tree that was quoted, - * EmptyTree if this tree comes from the parser. + * @param tpt The type of the tree that was quoted */ case class Quote[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { @@ -689,11 +698,20 @@ object Trees { } /** A tree representing a splice `${ expr }` - * - * @param expr The tree that was spliced - * @param tpt The type of the tree that was spliced, - * EmptyTree if this tree comes from the parser. - */ + * + * `Splice`s are created by the `Parser` with an empty `tpt`. In typer + * they can be typed as a `Splice` with a known `tpt` or desugared and + * typed as a quote pattern holes. + * + * `Splice` are checked transformed in the `staging` and `splicing` phases. + * After `splicing` phase, the only quotes that exist are in `inline` + * methods. These are dropped when we remove the inline method implementations. + * + * The `tpt` will be transformed in `staging` and used in `splicing` to create `Hole`s. + * + * @param expr The tree that was spliced + * @param tpt The type of the spliced tree + */ case class Splice[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Splice[T] From 687d06ea78087cdca1ef4161427c166ae02df9e1 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 27 Apr 2023 10:13:47 +0200 Subject: [PATCH 19/46] Transform Quote/Splice in MiniPhase To transform quotes and splices in inline methods. --- .../tools/dotc/transform/ElimByName.scala | 6 --- .../tools/dotc/transform/FirstTransform.scala | 3 -- .../tools/dotc/transform/MegaPhase.scala | 51 +++++++++++++++++++ 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala index 976df4f436f7..151e841f0e48 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala @@ -159,12 +159,6 @@ class ElimByName extends MiniPhase, InfoTransformer: else tree } - override def transformOther(tree: Tree)(using Context): Tree = tree match - case tree @ Splice(spliced, tpt) => - assert(dotty.tools.dotc.inlines.Inlines.inInlineMethod) - cpy.Splice(tree)(transformAllDeep(spliced), tpt) - case tree => tree - object ElimByName: val name: String = "elimByName" val description: String = "map by-name parameters to functions" diff --git a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala index 9d874bf6b00f..a7e0795ce195 100644 --- a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -153,9 +153,6 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase => override def transformOther(tree: Tree)(using Context): Tree = tree match { case tree: Export => EmptyTree case tree: NamedArg => transformAllDeep(tree.arg) - case tree @ Splice(expr, tpt) => - assert(dotty.tools.dotc.inlines.Inlines.inInlineMethod) - cpy.Splice(tree)(transformAllDeep(expr), transformAllDeep(tpt)) case tree => if (tree.isType) toTypeTree(tree) else tree } diff --git a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala index d4dd911241d3..77d6b2f3ac61 100644 --- a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala +++ b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala @@ -5,6 +5,7 @@ package transform import core._ import Contexts._, Phases._, Symbols._, Decorators._ import Flags.PackageVal +import staging.StagingLevel.* /** A MegaPhase combines a number of mini-phases which are all executed in * a single tree traversal. @@ -66,6 +67,8 @@ object MegaPhase { def prepareForTry(tree: Try)(using Context): Context = ctx def prepareForSeqLiteral(tree: SeqLiteral)(using Context): Context = ctx def prepareForInlined(tree: Inlined)(using Context): Context = ctx + def prepareForQuote(tree: Quote)(using Context): Context = ctx + def prepareForSplice(tree: Splice)(using Context): Context = ctx def prepareForTypeTree(tree: TypeTree)(using Context): Context = ctx def prepareForBind(tree: Bind)(using Context): Context = ctx def prepareForAlternative(tree: Alternative)(using Context): Context = ctx @@ -100,6 +103,8 @@ object MegaPhase { def transformTry(tree: Try)(using Context): Tree = tree def transformSeqLiteral(tree: SeqLiteral)(using Context): Tree = tree def transformInlined(tree: Inlined)(using Context): Tree = tree + def transformQuote(tree: Quote)(using Context): Tree = tree + def transformSplice(tree: Splice)(using Context): Tree = tree def transformTypeTree(tree: TypeTree)(using Context): Tree = tree def transformBind(tree: Bind)(using Context): Tree = tree def transformAlternative(tree: Alternative)(using Context): Tree = tree @@ -394,6 +399,18 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase { val expansion = transformTree(tree.expansion, start)(using inlineContext(tree.call)) goInlined(cpy.Inlined(tree)(tree.call, bindings, expansion), start) } + case tree: Quote => + inContext(prepQuote(tree, start)(using outerCtx)) { + val expr = transformTree(tree.expr, start)(using quoteContext) + val tpt = transformTree(tree.tpt, start) + goQuote(cpy.Quote(tree)(expr, tpt), start) + } + case tree: Splice => + inContext(prepSplice(tree, start)(using outerCtx)) { + val expr = transformTree(tree.expr, start)(using spliceContext) + val tpt = transformTree(tree.tpt, start) + goSplice(cpy.Splice(tree)(expr, tpt), start) + } case tree: Return => inContext(prepReturn(tree, start)(using outerCtx)) { val expr = transformTree(tree.expr, start) @@ -546,6 +563,10 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase { private val nxSeqLiteralTransPhase = init("transformSeqLiteral") private val nxInlinedPrepPhase = init("prepareForInlined") private val nxInlinedTransPhase = init("transformInlined") + private val nxQuotePrepPhase = init("prepareForQuote") + private val nxQuoteTransPhase = init("transformQuote") + private val nxSplicePrepPhase = init("prepareForPrep") + private val nxSpliceTransPhase = init("transformSplice") private val nxTypeTreePrepPhase = init("prepareForTypeTree") private val nxTypeTreeTransPhase = init("transformTypeTree") private val nxBindPrepPhase = init("prepareForBind") @@ -893,6 +914,36 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase { } } + def prepQuote(tree: Quote, start: Int)(using Context): Context = { + val phase = nxQuotePrepPhase(start) + if (phase == null) ctx + else prepQuote(tree, phase.idxInGroup + 1)(using phase.prepareForQuote(tree)) + } + + def goQuote(tree: Quote, start: Int)(using Context): Tree = { + val phase = nxQuoteTransPhase(start) + if (phase == null) tree + else phase.transformQuote(tree) match { + case tree1: Quote => goQuote(tree1, phase.idxInGroup + 1) + case tree1 => transformNode(tree1, phase.idxInGroup + 1) + } + } + + def prepSplice(tree: Splice, start: Int)(using Context): Context = { + val phase = nxSplicePrepPhase(start) + if (phase == null) ctx + else prepSplice(tree, phase.idxInGroup + 1)(using phase.prepareForSplice(tree)) + } + + def goSplice(tree: Splice, start: Int)(using Context): Tree = { + val phase = nxSpliceTransPhase(start) + if (phase == null) tree + else phase.transformSplice(tree) match { + case tree1: Splice => goSplice(tree1, phase.idxInGroup + 1) + case tree1 => transformNode(tree1, phase.idxInGroup + 1) + } + } + def prepTypeTree(tree: TypeTree, start: Int)(using Context): Context = { val phase = nxTypeTreePrepPhase(start) if (phase == null) ctx From 1f62049a67d8119102269c2f0ed26d1742354505 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 28 Apr 2023 10:37:27 +0200 Subject: [PATCH 20/46] Add TODOs and fix typos --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 6 +++--- compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 3427146a6baa..215998353816 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -682,7 +682,7 @@ object Trees { * they can be typed as a `Quote` with a known `tpt` or desugared and * typed as a quote pattern. * - * `Quotes` are checked transformed in the `staging`, `splicing` and `pickleQuotes` + * `Quotes` are checked and transformed in the `staging`, `splicing` and `pickleQuotes` * phases. After `pickleQuotes` phase, the only quotes that exist are in `inline` * methods. These are dropped when we remove the inline method implementations. * @@ -703,8 +703,8 @@ object Trees { * they can be typed as a `Splice` with a known `tpt` or desugared and * typed as a quote pattern holes. * - * `Splice` are checked transformed in the `staging` and `splicing` phases. - * After `splicing` phase, the only quotes that exist are in `inline` + * `Splice` are checked and transformed in the `staging` and `splicing` phases. + * After `splicing` phase, the only splices that exist are in `inline` * methods. These are dropped when we remove the inline method implementations. * * The `tpt` will be transformed in `staging` and used in `splicing` to create `Hole`s. diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index f81c0c2de56a..749fbe423dd3 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -59,6 +59,7 @@ trait QuotesAndSplices { report.error(msg, tree.srcPos) EmptyTree else + // TODO typecheck directly (without `exprQuote`) val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.expr) val quotedExpr = typedApply(exprQuoteTree, pt)(using quoteContext) match case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => Quote(quotedExpr, tpt) @@ -106,9 +107,9 @@ trait QuotesAndSplices { markAsMacro(ctx) } + // TODO typecheck directly (without `exprSplice`) val internalSplice = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprSplice.termRef), tree.expr) - typedApply(internalSplice, pt)(using spliceContext).withSpan(tree.span) match case tree @ Apply(TypeApply(_, tpt :: Nil), spliced :: Nil) if tree.symbol == defn.QuotedRuntime_exprSplice => cpy.Splice(tree)(spliced, tpt) From 5ae7861eb246461bc0f970d0e446c2b080440d18 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 28 Apr 2023 12:32:34 +0200 Subject: [PATCH 21/46] Remove `tpt` from `Quote` --- .../dotty/tools/dotc/CompilationUnit.scala | 2 +- .../src/dotty/tools/dotc/ast/Desugar.scala | 2 +- compiler/src/dotty/tools/dotc/ast/Trees.scala | 40 ++++++++++++------- compiler/src/dotty/tools/dotc/ast/tpd.scala | 4 +- compiler/src/dotty/tools/dotc/ast/untpd.scala | 2 +- .../tools/dotc/core/tasty/TreePickler.scala | 6 +-- .../tools/dotc/core/tasty/TreeUnpickler.scala | 2 +- .../dotty/tools/dotc/inlines/Inliner.scala | 4 +- .../dotty/tools/dotc/parsing/Parsers.scala | 4 +- .../tools/dotc/printing/RefinedPrinter.scala | 6 +-- .../tools/dotc/staging/CrossStageSafety.scala | 4 +- .../dotc/staging/TreeMapWithStages.scala | 6 +-- .../tools/dotc/transform/MegaPhase.scala | 3 +- .../tools/dotc/transform/PickleQuotes.scala | 6 +-- .../tools/dotc/transform/PostTyper.scala | 2 +- .../dotty/tools/dotc/transform/Splicer.scala | 10 ++--- .../dotty/tools/dotc/transform/Splicing.scala | 10 ++--- .../tools/dotc/transform/patmat/Space.scala | 2 +- .../tools/dotc/typer/QuotesAndSplices.scala | 7 ++-- .../src/dotty/tools/dotc/typer/ReTyper.scala | 6 +-- .../dotty/tools/dotc/typer/TypeAssigner.scala | 6 --- .../quoted/runtime/impl/QuotesImpl.scala | 2 +- 22 files changed, 69 insertions(+), 67 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index 9a41f685f6c3..8e4989f13c3d 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -157,7 +157,7 @@ object CompilationUnit { if tree.symbol.is(Flags.Inline) then containsInline = true tree match - case tpd.Quote(_, _) => + case tpd.Quote(_) => containsQuote = true case tree: tpd.Apply if tree.symbol == defn.QuotedTypeModule_of => containsQuote = true diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 5c2410d3fbdc..60962f0d1996 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1986,7 +1986,7 @@ object desugar { trees foreach collect case Block(Nil, expr) => collect(expr) - case Quote(expr, _) => + case Quote(expr) => new UntypedTreeTraverser { def traverse(tree: untpd.Tree)(using Context): Unit = tree match { case Splice(expr, _) => collect(expr) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 215998353816..e626a06829dd 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -678,23 +678,33 @@ object Trees { } /** A tree representing a quote `'{ expr }` - * `Quote`s are created by the `Parser` with an empty `tpt`. In typer - * they can be typed as a `Quote` with a known `tpt` or desugared and - * typed as a quote pattern. + * `Quote`s are created by the `Parser`. In typer they can be typed as a + * `Quote` with a known `tpt` or desugared and typed as a quote pattern. * * `Quotes` are checked and transformed in the `staging`, `splicing` and `pickleQuotes` * phases. After `pickleQuotes` phase, the only quotes that exist are in `inline` * methods. These are dropped when we remove the inline method implementations. * - * The `tpt` will be transformed in `staging` and used in `pickleQuotes` to create the - * pickled representation of the quote. - * * @param expr The tree that was quoted - * @param tpt The type of the tree that was quoted */ - case class Quote[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) + case class Quote[+T <: Untyped] private[ast] (expr: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Quote[T] + + /** Type of the quoted expression as seen from outside the quote */ + def exprType(using Context): Type = + val quoteType = typeOpt // Quotes ?=> Expr[T] + val exprType = quoteType.argInfos.last // Expr[T] + exprType.argInfos.head // T + + /** Set the type of the quoted expression as seen from outside the quote */ + def withExprType(tpe: Type)(using Context): Quote[Type] = + val exprType = // Expr[T] + defn.QuotedExprClass.typeRef.appliedTo(tpe) + val quoteType = // Quotes ?=> Expr[T] + defn.FunctionType(1, isContextual = true) + .appliedTo(defn.QuotesClass.typeRef, exprType) + withType(quoteType) } /** A tree representing a splice `${ expr }` @@ -1299,9 +1309,9 @@ object Trees { case tree: Inlined if (call eq tree.call) && (bindings eq tree.bindings) && (expansion eq tree.expansion) => tree case _ => finalize(tree, untpd.Inlined(call, bindings, expansion)(sourceFile(tree))) } - def Quote(tree: Tree)(expr: Tree, tpt: Tree)(using Context): Quote = tree match { - case tree: Quote if (expr eq tree.expr) && (tpt eq tree.tpt) => tree - case _ => finalize(tree, untpd.Quote(expr, tpt)(sourceFile(tree))) + def Quote(tree: Tree)(expr: Tree)(using Context): Quote = tree match { + case tree: Quote if (expr eq tree.expr) => tree + case _ => finalize(tree, untpd.Quote(expr)(sourceFile(tree))) } def Splice(tree: Tree)(expr: Tree, tpt: Tree)(using Context): Splice = tree match { case tree: Splice if (expr eq tree.expr) && (tpt eq tree.tpt) => tree @@ -1546,8 +1556,8 @@ object Trees { case Thicket(trees) => val trees1 = transform(trees) if (trees1 eq trees) tree else Thicket(trees1) - case tree @ Quote(expr, tpt) => - cpy.Quote(tree)(transform(expr), transform(tpt)) + case tree @ Quote(expr) => + cpy.Quote(tree)(transform(expr)) case tree @ Splice(expr, tpt) => cpy.Splice(tree)(transform(expr), transform(tpt)) case tree @ Hole(_, _, args, content, tpt) => @@ -1691,8 +1701,8 @@ object Trees { this(this(x, arg), annot) case Thicket(ts) => this(x, ts) - case Quote(expr, tpt) => - this(this(x, expr), tpt) + case Quote(expr) => + this(x, expr) case Splice(expr, tpt) => this(this(x, expr), tpt) case Hole(_, _, args, content, tpt) => diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 66c0bedc12c3..78de609850db 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -170,8 +170,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Inlined(call: Tree, bindings: List[MemberDef], expansion: Tree)(using Context): Inlined = ta.assignType(untpd.Inlined(call, bindings, expansion), bindings, expansion) - def Quote(expr: Tree, tpt: Tree)(using Context): Quote = - ta.assignType(untpd.Quote(expr, tpt), tpt) + def Quote(expr: Tree, tpe: Type)(using Context): Quote = + untpd.Quote(expr).withExprType(tpe) def Splice(expr: Tree, tpt: Tree)(using Context): Splice = ta.assignType(untpd.Splice(expr, tpt), tpt) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 45b2fcd49595..4fdd9115f2c9 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -397,7 +397,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def SeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): SeqLiteral = new SeqLiteral(elems, elemtpt) def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt) def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion) - def Quote(expr: Tree, tpt: Tree)(implicit src: SourceFile): Quote = new Quote(expr, tpt) + def Quote(expr: Tree)(implicit src: SourceFile): Quote = new Quote(expr) def Splice(expr: Tree, tpt: Tree)(implicit src: SourceFile): Splice = new Splice(expr, tpt) def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree() def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree() diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 864004a3e5ec..6346c2f3dd03 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -665,11 +665,11 @@ class TreePickler(pickler: TastyPickler) { pickleTree(hi) pickleTree(alias) } - case Quote(expr, tpt) => + case tree @ Quote(expr) => pickleTree( - // scala.quoted.runtime.Expr.quoted[]() + // scala.quoted.runtime.Expr.quoted[]() ref(defn.QuotedRuntime_exprQuote) - .appliedToTypeTree(tpt) + .appliedToType(tree.exprType) .appliedTo(expr) .withSpan(tree.span) ) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 79d2008eb698..02e79854c666 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1269,7 +1269,7 @@ class TreeUnpickler(reader: TastyReader, def quotedExpr(fn: Tree, args: List[Tree]): Tree = val TypeApply(_, targs) = fn: @unchecked - Quote(args.head, targs.head) + Quote(args.head, targs.head.tpe) def splicedExpr(fn: Tree, args: List[Tree]): Tree = val TypeApply(_, targs) = fn: @unchecked diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index e2fd4c6688c1..8f3c4df237a6 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -827,7 +827,7 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = super.typedQuote(tree, pt) match - case Quote(Splice(inner, _), _) => inner + case Quote(Splice(inner, _)) => inner case tree1 => ctx.compilationUnit.needsStaging = true tree1 @@ -1070,7 +1070,7 @@ class Inliner(val call: tpd.Tree)(using Context): else tree match { case tree: RefTree if tree.isTerm && tree.symbol.isDefinedInCurrentRun && !tree.symbol.isLocal => foldOver(tree.symbol :: syms, tree) - case Quote(body, _) => + case Quote(body) => level += 1 try apply(syms, body) finally level -= 1 diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index c2e2e4466d98..d98f407edc73 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1262,7 +1262,7 @@ object Parsers { } } in.nextToken() - Quote(t, EmptyTree) + Quote(t) } else if !in.featureEnabled(Feature.symbolLiterals) then @@ -2497,7 +2497,7 @@ object Parsers { val expr = if (in.token == LBRACKET) inBrackets(typ()) else stagedBlock() - Quote(expr, EmptyTree) + Quote(expr) } } case NEW => diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index b2b9196b0a13..8cf9ab00b091 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -716,9 +716,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { "Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}" case MacroTree(call) => keywordStr("macro ") ~ toTextGlobal(call) - case Quote(expr, tpt) => - val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(!tpt.isEmpty && printDebug) - keywordStr("'") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") + case tree @ Quote(expr) => + val exprTypeText = (keywordStr("[") ~ toTextGlobal(tree.exprType) ~ keywordStr("]")).provided(printDebug) + keywordStr("'") ~ exprTypeText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") case Splice(expr, tpt) => val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(!tpt.isEmpty && printDebug) keywordStr("$") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 748b053d7eaa..36b2b9caa050 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -103,8 +103,8 @@ class CrossStageSafety extends TreeMapWithStages { val transformedBody = transformQuoteBody(body, quote.span) val stripAnnotsDeep: TypeMap = new TypeMap: def apply(tp: Type): Type = mapOver(tp.stripAnnots) - val tpt1 = TypeTree(healType(quote.tpt.srcPos)(stripAnnotsDeep(quote.tpt.tpe))) - cpy.Quote(quote)(transformedBody, tpt1) + val exprType1 = healType(quote.srcPos)(stripAnnotsDeep(quote.exprType)) + cpy.Quote(quote)(transformedBody).withExprType(exprType1) } override protected def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = { diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index a292b4b079be..0a85ef5e9e84 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -26,7 +26,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { * - `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T]()` */ protected def transformQuote(body: Tree, quote: Quote)(using Context): Tree = - cpy.Quote(quote)(body, quote.tpt) + cpy.Quote(quote)(body) /** Transform the quote `quote` which contains the quoted `body`. * @@ -62,7 +62,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { try transformQuotedType(quotedTree, tree) finally inQuoteOrSplice = old - case tree @ Quote(quotedTree, _) => + case tree @ Quote(quotedTree) => val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(quotedTree) match { @@ -79,7 +79,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(splicedTree) match { - case Quote(t, _) => + case Quote(t) => // Optimization: `${ 'x }` --> `x` transform(t) case _ => diff --git a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala index 77d6b2f3ac61..86393cf2d466 100644 --- a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala +++ b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala @@ -402,8 +402,7 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase { case tree: Quote => inContext(prepQuote(tree, start)(using outerCtx)) { val expr = transformTree(tree.expr, start)(using quoteContext) - val tpt = transformTree(tree.tpt, start) - goQuote(cpy.Quote(tree)(expr, tpt), start) + goQuote(cpy.Quote(tree)(expr), start) } case tree: Splice => inContext(prepSplice(tree, start)(using outerCtx)) { diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index c60562baaa7f..f23558e8fe9a 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -100,11 +100,11 @@ class PickleQuotes extends MacroTransform { protected def newTransformer(using Context): Transformer = new Transformer { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match - case Apply(Select(Quote(expr, tpt), nme.apply), List(quotes)) => - val (contents, codeWithHoles) = makeHoles(expr) + case Apply(Select(quote: Quote, nme.apply), List(quotes)) => + val (contents, codeWithHoles) = makeHoles(quote.expr) val sourceRef = Inlines.inlineCallTrace(ctx.owner, tree.sourcePos) val codeWithHoles2 = Inlined(sourceRef, Nil, codeWithHoles) - val pickled = PickleQuotes(quotes, codeWithHoles2, contents, tpt.tpe, false) + val pickled = PickleQuotes(quotes, codeWithHoles2, contents, quote.exprType, false) transform(pickled) // pickle quotes that are in the contents case Apply(TypeApply(_, List(tpt)), List(quotes)) if tree.symbol == defn.QuotedTypeModule_of => tpt match diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index eac9a8d70654..6861084b1dcb 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -485,7 +485,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase ) case Block(_, Closure(_, _, tpt)) if ExpandSAMs.needsWrapperClass(tpt.tpe) => superAcc.withInvalidCurrentClass(super.transform(tree)) - case Quote(expr, _) => + case Quote(expr) => ctx.compilationUnit.needsStaging = true super.transform(tree) case tree => diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 4b6d434d80b0..d65823ff9407 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -44,7 +44,7 @@ object Splicer { * See: `Staging` */ def splice(tree: Tree, splicePos: SrcPos, spliceExpansionPos: SrcPos, classLoader: ClassLoader)(using Context): Tree = tree match { - case Quote(quotedTree, _) => quotedTree + case Quote(quotedTree) => quotedTree case _ => val macroOwner = newSymbol(ctx.owner, nme.MACROkw, Macro | Synthetic, defn.AnyType, coord = tree.span) try @@ -136,7 +136,7 @@ object Splicer { * See: `Staging` */ def checkValidMacroBody(tree: Tree)(using Context): Unit = tree match { - case Quote(_, _) => // ok + case Quote(_) => // ok case _ => type Env = Set[Symbol] @@ -155,7 +155,7 @@ object Splicer { case Block(Nil, expr) => checkIfValidArgument(expr) case Typed(expr, _) => checkIfValidArgument(expr) - case Apply(Select(Quote(expr, tpt), nme.apply), _) => + case Apply(Select(Quote(expr), nme.apply), _) => val noSpliceChecker = new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match case Splice(_, _) => @@ -203,7 +203,7 @@ object Splicer { case Typed(expr, _) => checkIfValidStaticCall(expr) - case Apply(Select(Quote(quoted, tpt), nme.apply), _) => + case Apply(Select(Quote(quoted), nme.apply), _) => // OK, canceled and warning emitted case Call(fn, args) @@ -240,7 +240,7 @@ object Splicer { override protected def interpretTree(tree: Tree)(implicit env: Env): Object = tree match { // Interpret level -1 quoted code `'{...}` (assumed without level 0 splices) - case Apply(Select(Quote(expr, _), nme.apply), _) => + case Apply(Select(Quote(expr), nme.apply), _) => val expr1 = expr match { case expr: Ident if expr.symbol.isAllOf(InlineByNameProxy) => // inline proxy for by-name parameter diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index af77588419b5..1b3013bdcfff 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -86,7 +86,7 @@ class Splicing extends MacroTransform: override def transform(tree: tpd.Tree)(using Context): tpd.Tree = assert(level == 0) tree match - case Apply(Select(Quote(expr, _), nme.apply),List(quotes)) => + case Apply(Select(Quote(expr), nme.apply),List(quotes)) => QuoteTransformer().transform(tree) case TypeApply(_, _) if tree.symbol == defn.QuotedTypeModule_of => QuoteTransformer().transform(tree) @@ -134,7 +134,7 @@ class Splicing extends MacroTransform: typeHoles.put(qual, hole) hole cpy.TypeDef(tree)(rhs = hole) - case Apply(Select(Quote(expr, tpt), nme.apply),List(quotes)) => + case Apply(Select(Quote(expr), nme.apply),List(quotes)) => super.transform(tree)(using quoteContext) case _: Template => for sym <- tree.symbol.owner.info.decls do @@ -237,7 +237,7 @@ class Splicing extends MacroTransform: case Splice(expr, tpt) => val expr1 = transform(expr)(using spliceContext) cpy.Splice(tree)(expr1, tpt) - case Apply(sel @ Select(app @ Quote(expr, tpt), nme.apply), quotesArgs) => + case Apply(sel @ Select(app @ Quote(expr), nme.apply), quotesArgs) => expr match case expr: RefTree if isCaptured(expr.symbol) => capturedTerm(expr) @@ -246,7 +246,7 @@ class Splicing extends MacroTransform: if level > 1 then transform(expr)(using quoteContext) else transformLevel0QuoteContent(expr)(using quoteContext) } - cpy.Apply(tree)(cpy.Select(sel)(cpy.Quote(app)(newExpr, tpt), nme.apply), quotesArgs) + cpy.Apply(tree)(cpy.Select(sel)(cpy.Quote(app)(newExpr), nme.apply), quotesArgs) case Apply(TypeApply(typeof, List(tpt)), List(quotes)) if tree.symbol == defn.QuotedTypeModule_of && containsCapturedType(tpt.tpe) => val newContent = capturedPartTypes(tpt) @@ -412,7 +412,7 @@ class Splicing extends MacroTransform: Splice(closure, TypeTree(tpe)) private def quoted(expr: Tree)(using Context): Tree = - Quote(expr, TypeTree(expr.tpe.widenTermRefExpr)) + Quote(expr, expr.tpe.widenTermRefExpr) .select(nme.apply) .appliedTo(quotes.nn) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 23c851a33408..2a2c15f95774 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -312,7 +312,7 @@ object SpaceEngine { def isIrrefutableQuotedPattern(unapp: tpd.Tree, implicits: List[tpd.Tree], pt: Type)(using Context): Boolean = { implicits.headOption match // pattern '{ $x: T } - case Some(tpd.Apply(tpd.Select(tpd.Quote(tpd.TypeApply(fn, List(tpt)), _), nme.apply), _)) + case Some(tpd.Apply(tpd.Select(tpd.Quote(tpd.TypeApply(fn, List(tpt))), nme.apply), _)) if unapp.symbol.owner.eq(defn.QuoteMatching_ExprMatchModule) && fn.symbol.eq(defn.QuotedRuntimePatterns_patternHole) => pt <:< defn.QuotedExprClass.typeRef.appliedTo(tpt.tpe) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 749fbe423dd3..01b4a4384e9f 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -36,7 +36,6 @@ trait QuotesAndSplices { */ def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = { record("typedQuote") - assert(tree.tpt.isEmpty) tree.expr match { case untpd.Splice(innerExpr, _) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => report.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.srcPos) @@ -62,7 +61,7 @@ trait QuotesAndSplices { // TODO typecheck directly (without `exprQuote`) val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.expr) val quotedExpr = typedApply(exprQuoteTree, pt)(using quoteContext) match - case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => Quote(quotedExpr, tpt) + case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => Quote(quotedExpr, tpt.tpe) makeInlineable(quotedExpr.select(nme.apply).appliedTo(quotes).withSpan(tree.span)) } @@ -77,7 +76,7 @@ trait QuotesAndSplices { assert(tree.tpt.isEmpty) checkSpliceOutsideQuote(tree) tree.expr match { - case untpd.Quote(innerExpr, _) if innerExpr.isTerm => + case untpd.Quote(innerExpr) if innerExpr.isTerm => report.warning("Canceled quote directly inside a splice. ${ '{ XYZ } } is equivalent to XYZ.", tree.srcPos) return typed(innerExpr, pt) case _ => @@ -446,7 +445,7 @@ trait QuotesAndSplices { val quoteClass = if (quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass val quotedPattern = - if (quoted.isTerm) tpd.Quote(shape, TypeTree(defn.AnyType)).select(nme.apply).appliedTo(qctx) + if (quoted.isTerm) tpd.Quote(shape, defn.AnyType).select(nme.apply).appliedTo(qctx) else ref(defn.QuotedTypeModule_of.termRef).appliedToTypeTree(shape).appliedTo(qctx) val matchModule = if quoted.isTerm then defn.QuoteMatching_ExprMatch else defn.QuoteMatching_TypeMatch diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index 9efe3b31a356..c96dbad2d01a 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -96,9 +96,9 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking typedApply(tree, selType) override def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = - val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) - val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem)(using quoteContext) - assignType(untpd.cpy.Quote(tree)(expr1, tpt1), tpt1) + assertTyped(tree) + val expr1 = typed(tree.expr, tree.exprType)(using quoteContext) + untpd.cpy.Quote(tree)(expr1).withType(tree.typeOpt) override def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index fc51af646b17..c22d1eb72aa6 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -392,12 +392,6 @@ trait TypeAssigner { def assignType(tree: untpd.Inlined, bindings: List[Tree], expansion: Tree)(using Context): Inlined = tree.withType(avoidingType(expansion, bindings)) - def assignType(tree: untpd.Quote, tpt: Tree)(using Context): Quote = - val lambdaType = // Quotes ?=> Expr[T] - defn.FunctionType(1, isContextual = true) - .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt.tpe)) - tree.withType(lambdaType) - def assignType(tree: untpd.Splice, tpt: Tree)(using Context): Splice = tree.withType(tpt.tpe) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index f63ef3feb041..dc72962401cc 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -637,7 +637,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler case self: tpd.Quote => // TODO expose Quote AST in Quotes import dotty.tools.dotc.ast.tpd.TreeOps tpd.ref(dotc.core.Symbols.defn.QuotedRuntime_exprQuote) - .appliedToTypeTree(self.tpt) + .appliedToType(self.exprType) .withSpan(self.span) case self: tpd.Splice => // TODO expose Splice AST in Quotes import dotty.tools.dotc.ast.tpd.TreeOps From 3976d062314e12eda6af23b57537064228e4f9b1 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 28 Apr 2023 14:20:29 +0200 Subject: [PATCH 22/46] Remove `tpt` from `Splice` --- .../src/dotty/tools/dotc/ast/Desugar.scala | 2 +- compiler/src/dotty/tools/dotc/ast/Trees.scala | 26 +++++++------------ compiler/src/dotty/tools/dotc/ast/tpd.scala | 4 +-- compiler/src/dotty/tools/dotc/ast/untpd.scala | 2 +- .../tools/dotc/core/tasty/TreePickler.scala | 8 +++--- .../tools/dotc/core/tasty/TreeUnpickler.scala | 4 +-- .../dotty/tools/dotc/inlines/Inliner.scala | 6 ++--- .../dotc/inlines/PrepareInlineable.scala | 2 +- .../dotty/tools/dotc/parsing/Parsers.scala | 2 +- .../tools/dotc/printing/RefinedPrinter.scala | 8 +++--- .../tools/dotc/staging/CrossStageSafety.scala | 11 +++----- .../dotc/staging/TreeMapWithStages.scala | 4 +-- .../tools/dotc/transform/MegaPhase.scala | 3 +-- .../dotty/tools/dotc/transform/Splicer.scala | 2 +- .../dotty/tools/dotc/transform/Splicing.scala | 8 +++--- .../tools/dotc/typer/QuotesAndSplices.scala | 19 ++++++-------- .../src/dotty/tools/dotc/typer/ReTyper.scala | 12 +++++---- .../dotty/tools/dotc/typer/TypeAssigner.scala | 3 --- .../quoted/runtime/impl/QuotesImpl.scala | 2 +- 19 files changed, 57 insertions(+), 71 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 60962f0d1996..c1dd78451bae 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1989,7 +1989,7 @@ object desugar { case Quote(expr) => new UntypedTreeTraverser { def traverse(tree: untpd.Tree)(using Context): Unit = tree match { - case Splice(expr, _) => collect(expr) + case Splice(expr) => collect(expr) case _ => traverseChildren(tree) } }.traverse(expr) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index e626a06829dd..e5b7f32a674d 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -709,20 +709,16 @@ object Trees { /** A tree representing a splice `${ expr }` * - * `Splice`s are created by the `Parser` with an empty `tpt`. In typer - * they can be typed as a `Splice` with a known `tpt` or desugared and - * typed as a quote pattern holes. + * `Splice`s are created by the `Parser`. In typer they can be typed as a + * `Splice` with a known `tpt` or desugared and typed as a quote pattern holes. * * `Splice` are checked and transformed in the `staging` and `splicing` phases. * After `splicing` phase, the only splices that exist are in `inline` * methods. These are dropped when we remove the inline method implementations. * - * The `tpt` will be transformed in `staging` and used in `splicing` to create `Hole`s. - * * @param expr The tree that was spliced - * @param tpt The type of the spliced tree */ - case class Splice[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) + case class Splice[+T <: Untyped] private[ast] (expr: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Splice[T] } @@ -1313,9 +1309,9 @@ object Trees { case tree: Quote if (expr eq tree.expr) => tree case _ => finalize(tree, untpd.Quote(expr)(sourceFile(tree))) } - def Splice(tree: Tree)(expr: Tree, tpt: Tree)(using Context): Splice = tree match { - case tree: Splice if (expr eq tree.expr) && (tpt eq tree.tpt) => tree - case _ => finalize(tree, untpd.Splice(expr, tpt)(sourceFile(tree))) + def Splice(tree: Tree)(expr: Tree)(using Context): Splice = tree match { + case tree: Splice if (expr eq tree.expr) => tree + case _ => finalize(tree, untpd.Splice(expr)(sourceFile(tree))) } def SingletonTypeTree(tree: Tree)(ref: Tree)(using Context): SingletonTypeTree = tree match { case tree: SingletonTypeTree if (ref eq tree.ref) => tree @@ -1422,8 +1418,6 @@ object Trees { TypeDef(tree: Tree)(name, rhs) def Template(tree: Template)(using Context)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, derived: List[untpd.Tree] = tree.derived, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody): Template = Template(tree: Tree)(constr, parents, derived, self, body) - def Splice(tree: Splice)(expr: Tree = tree.expr, tpt: Tree = tree.tpt)(using Context): Splice = - Splice(tree: Tree)(expr, tpt) def Hole(tree: Hole)(isTerm: Boolean = tree.isTerm, idx: Int = tree.idx, args: List[Tree] = tree.args, content: Tree = tree.content, tpt: Tree = tree.tpt)(using Context): Hole = Hole(tree: Tree)(isTerm, idx, args, content, tpt) @@ -1558,8 +1552,8 @@ object Trees { if (trees1 eq trees) tree else Thicket(trees1) case tree @ Quote(expr) => cpy.Quote(tree)(transform(expr)) - case tree @ Splice(expr, tpt) => - cpy.Splice(tree)(transform(expr), transform(tpt)) + case tree @ Splice(expr) => + cpy.Splice(tree)(transform(expr)) case tree @ Hole(_, _, args, content, tpt) => cpy.Hole(tree)(args = transform(args), content = transform(content), tpt = transform(tpt)) case _ => @@ -1703,8 +1697,8 @@ object Trees { this(x, ts) case Quote(expr) => this(x, expr) - case Splice(expr, tpt) => - this(this(x, expr), tpt) + case Splice(expr) => + this(x, expr) case Hole(_, _, args, content, tpt) => this(this(this(x, args), content), tpt) case _ => diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 78de609850db..ce9bd3ea0595 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -173,8 +173,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Quote(expr: Tree, tpe: Type)(using Context): Quote = untpd.Quote(expr).withExprType(tpe) - def Splice(expr: Tree, tpt: Tree)(using Context): Splice = - ta.assignType(untpd.Splice(expr, tpt), tpt) + def Splice(expr: Tree, tpe: Type)(using Context): Splice = + untpd.Splice(expr).withType(tpe) def TypeTree(tp: Type, inferred: Boolean = false)(using Context): TypeTree = (if inferred then untpd.InferredTypeTree() else untpd.TypeTree()).withType(tp) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 4fdd9115f2c9..ec85ffa689b6 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -398,7 +398,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt) def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion) def Quote(expr: Tree)(implicit src: SourceFile): Quote = new Quote(expr) - def Splice(expr: Tree, tpt: Tree)(implicit src: SourceFile): Splice = new Splice(expr, tpt) + def Splice(expr: Tree)(implicit src: SourceFile): Splice = new Splice(expr) def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree() def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree() def SingletonTypeTree(ref: Tree)(implicit src: SourceFile): SingletonTypeTree = new SingletonTypeTree(ref) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 6346c2f3dd03..7682e042304a 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -667,17 +667,17 @@ class TreePickler(pickler: TastyPickler) { } case tree @ Quote(expr) => pickleTree( - // scala.quoted.runtime.Expr.quoted[]() + // scala.quoted.runtime.Expr.quoted[]() ref(defn.QuotedRuntime_exprQuote) .appliedToType(tree.exprType) .appliedTo(expr) .withSpan(tree.span) ) - case Splice(expr, tpt) => + case Splice(expr) => pickleTree( - // scala.quoted.runtime.Expr.splice[]() + // scala.quoted.runtime.Expr.splice[]() ref(defn.QuotedRuntime_exprSplice) - .appliedToTypeTree(tpt) + .appliedToType(tree.tpe) .appliedTo(expr) .withSpan(tree.span) ) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 02e79854c666..4f81e1477ea7 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1273,12 +1273,12 @@ class TreeUnpickler(reader: TastyReader, def splicedExpr(fn: Tree, args: List[Tree]): Tree = val TypeApply(_, targs) = fn: @unchecked - Splice(args.head, targs.head) + Splice(args.head, targs.head.tpe) def nestedSpliceExpr(fn: Tree, args: List[Tree]): Tree = fn match case Apply(TypeApply(_, targs), _ :: Nil) => // nestedSplice[T](quotes)(expr) - Splice(args.head, targs.head) + Splice(args.head, targs.head.tpe) case _ => // nestedSplice[T](quotes) tpd.Apply(fn, args) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 8f3c4df237a6..9179e92527e6 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -827,14 +827,14 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = super.typedQuote(tree, pt) match - case Quote(Splice(inner, _)) => inner + case Quote(Splice(inner)) => inner case tree1 => ctx.compilationUnit.needsStaging = true tree1 override def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = super.typedSplice(tree, pt) match - case tree1 @ Splice(expr, tpt) + case tree1 @ Splice(expr) if StagingLevel.level == 0 && !hasInliningErrors => val expanded = expandMacro(expr, tree1.srcPos) @@ -1078,7 +1078,7 @@ class Inliner(val call: tpd.Tree)(using Context): level += 1 try apply(syms, body) finally level -= 1 - case Splice(body, _) => + case Splice(body) => level -= 1 try apply(syms, body) finally level += 1 diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index 11d4d9881230..c771eff9e840 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -291,7 +291,7 @@ object PrepareInlineable { if (inlined.is(Macro) && !ctx.isAfterTyper) { def checkMacro(tree: Tree): Unit = tree match { - case Splice(code, _) => + case Splice(code) => if (code.symbol.flags.is(Inline)) report.error("Macro cannot be implemented with an `inline` method", code.srcPos) Splicer.checkValidMacroBody(code) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index d98f407edc73..9e169d334bd5 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1764,7 +1764,7 @@ object Parsers { syntaxError(em"$msg\n\nHint: $hint", Span(start, in.lastOffset)) Ident(nme.ERROR.toTypeName) else - Splice(expr, EmptyTree) + Splice(expr) } /** SimpleType ::= SimpleLiteral diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 8cf9ab00b091..7ed8ede8aaf9 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -717,11 +717,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case MacroTree(call) => keywordStr("macro ") ~ toTextGlobal(call) case tree @ Quote(expr) => - val exprTypeText = (keywordStr("[") ~ toTextGlobal(tree.exprType) ~ keywordStr("]")).provided(printDebug) + val exprTypeText = (keywordStr("[") ~ toTextGlobal(tree.exprType) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists) keywordStr("'") ~ exprTypeText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") - case Splice(expr, tpt) => - val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(!tpt.isEmpty && printDebug) - keywordStr("$") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") + case Splice(expr) => + val spliceTypeText = (keywordStr("[") ~ toTextGlobal(tree.typeOpt) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists) + keywordStr("$") ~ spliceTypeText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") case Hole(isTermHole, idx, args, content, tpt) => val (prefix, postfix) = if isTermHole then ("{{{", "}}}") else ("[[[", "]]]") val argsText = toTextGlobal(args, ", ") diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 36b2b9caa050..32b54ffa7ff8 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -144,13 +144,10 @@ class CrossStageSafety extends TreeMapWithStages { */ protected def transformSplice(body: Tree, splice: Splice)(using Context): Tree = { val body1 = transform(body)(using spliceContext) - val tpt1 = - if level == 0 then - transform(splice.tpt) - else - val tp = healType(splice.srcPos)(splice.tpe.widenTermRefExpr) - TypeTree(tp).withSpan(splice.tpt.span) - cpy.Splice(splice)(body1, tpt1) + val tpe1 = + if level == 0 then splice.tpe + else healType(splice.srcPos)(splice.tpe.widenTermRefExpr) + untpd.cpy.Splice(splice)(body1).withType(tpe1) } protected def transformSpliceType(body: Tree, splice: Select)(using Context): Tree = { diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index 0a85ef5e9e84..ddf2017af368 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -66,7 +66,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(quotedTree) match { - case Splice(t, _) => + case Splice(t) => // Optimization: `'{ $x }` --> `x` // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]` transform(t).asInstance(tree.tpe) @@ -75,7 +75,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { } finally inQuoteOrSplice = old - case tree @ Splice(splicedTree, _) => + case tree @ Splice(splicedTree) => val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(splicedTree) match { diff --git a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala index 86393cf2d466..cf34c60330fa 100644 --- a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala +++ b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala @@ -407,8 +407,7 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase { case tree: Splice => inContext(prepSplice(tree, start)(using outerCtx)) { val expr = transformTree(tree.expr, start)(using spliceContext) - val tpt = transformTree(tree.tpt, start) - goSplice(cpy.Splice(tree)(expr, tpt), start) + goSplice(cpy.Splice(tree)(expr), start) } case tree: Return => inContext(prepReturn(tree, start)(using outerCtx)) { diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index d65823ff9407..38f1005cd813 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -158,7 +158,7 @@ object Splicer { case Apply(Select(Quote(expr), nme.apply), _) => val noSpliceChecker = new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match - case Splice(_, _) => + case Splice(_) => report.error("Quoted argument of macros may not have splices", tree.srcPos) case _ => traverseChildren(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 1b3013bdcfff..fd195e8f068f 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -114,7 +114,7 @@ class Splicing extends MacroTransform: case tree: Splice => if level > 1 then val expr1 = super.transform(tree.expr)(using spliceContext) - cpy.Splice(tree)(expr1, tree.tpt) + cpy.Splice(tree)(expr1) else val holeIdx = numHoles numHoles += 1 @@ -234,9 +234,9 @@ class Splicing extends MacroTransform: case tree @ Assign(lhs: RefTree, rhs) => if isCaptured(lhs.symbol) then transformSplicedAssign(tree) else super.transform(tree) - case Splice(expr, tpt) => + case Splice(expr) => val expr1 = transform(expr)(using spliceContext) - cpy.Splice(tree)(expr1, tpt) + cpy.Splice(tree)(expr1) case Apply(sel @ Select(app @ Quote(expr), nme.apply), quotesArgs) => expr match case expr: RefTree if isCaptured(expr.symbol) => @@ -409,7 +409,7 @@ class Splicing extends MacroTransform: body(using ctx.withOwner(meth)).changeOwner(ctx.owner, meth) } }) - Splice(closure, TypeTree(tpe)) + Splice(closure, tpe) private def quoted(expr: Tree)(using Context): Tree = Quote(expr, expr.tpe.widenTermRefExpr) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 01b4a4384e9f..4642bdbbb9ba 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -37,7 +37,7 @@ trait QuotesAndSplices { def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = { record("typedQuote") tree.expr match { - case untpd.Splice(innerExpr, _) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => + case untpd.Splice(innerExpr) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => report.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.srcPos) case _ => } @@ -73,7 +73,6 @@ trait QuotesAndSplices { /** Translate `${ t: Expr[T] }` into expression `t.splice` while tracking the quotation level in the context */ def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = { record("typedSplice") - assert(tree.tpt.isEmpty) checkSpliceOutsideQuote(tree) tree.expr match { case untpd.Quote(innerExpr) if innerExpr.isTerm => @@ -89,7 +88,7 @@ trait QuotesAndSplices { using spliceContext.retractMode(Mode.QuotedPattern).addMode(Mode.Pattern).withOwner(spliceOwner(ctx))) val baseType = pat.tpe.baseType(defn.QuotedExprClass) val argType = if baseType != NoType then baseType.argTypesHi.head else defn.NothingType - Splice(pat, TypeTree(argType)).withSpan(tree.span) + Splice(pat, argType).withSpan(tree.span) } else { report.error(em"Type must be fully defined.\nConsider annotating the splice using a type ascription:\n ($tree: XYZ).", tree.expr.srcPos) @@ -111,7 +110,7 @@ trait QuotesAndSplices { untpd.Apply(untpd.ref(defn.QuotedRuntime_exprSplice.termRef), tree.expr) typedApply(internalSplice, pt)(using spliceContext).withSpan(tree.span) match case tree @ Apply(TypeApply(_, tpt :: Nil), spliced :: Nil) if tree.symbol == defn.QuotedRuntime_exprSplice => - cpy.Splice(tree)(spliced, tpt) + cpy.Splice(tree)(spliced) case tree => tree } } @@ -229,12 +228,10 @@ trait QuotesAndSplices { val freshTypeBindingsBuff = new mutable.ListBuffer[Tree] val typePatBuf = new mutable.ListBuffer[Tree] override def transform(tree: Tree)(using Context) = tree match { - case Typed(Splice(pat, _), tpt) if !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => - val tpt1 = transform(tpt) // Transform type bindings - val exprTpt = AppliedTypeTree(TypeTree(defn.QuotedExprClass.typeRef), tpt1 :: Nil) - val newSplice = cpy.Splice(tree)(pat, tpt1) - transform(newSplice) - case Apply(TypeApply(fn, targs), Splice(pat, _) :: args :: Nil) if fn.symbol == defn.QuotedRuntimePatterns_patternHigherOrderHole => + case Typed(splice: Splice, tpt) if !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => + transform(tpt) // Collect type bindings + transform(splice) + case Apply(TypeApply(fn, targs), Splice(pat) :: args :: Nil) if fn.symbol == defn.QuotedRuntimePatterns_patternHigherOrderHole => args match // TODO support these patterns. Possibly using scala.quoted.util.Var case SeqLiteral(args, _) => for arg <- args; if arg.symbol.is(Mutable) do @@ -246,7 +243,7 @@ trait QuotesAndSplices { val pat1 = if (patType eq patType1) pat else pat.withType(patType1) patBuf += pat1 } - case Splice(pat, _) => + case Splice(pat) => try ref(defn.QuotedRuntimePatterns_patternHole.termRef).appliedToType(tree.tpe).withSpan(tree.span) finally { val patType = pat.tpe.widen diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index c96dbad2d01a..b43722d54cc7 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -101,12 +101,14 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking untpd.cpy.Quote(tree)(expr1).withType(tree.typeOpt) override def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = - val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) - val splicedType = // Quotes ?=> Expr[T] + assertTyped(tree) + val exprType = // Expr[T] + defn.QuotedExprClass.typeRef.appliedTo(tree.typeOpt) + val quoteType = // Quotes ?=> Expr[T] defn.FunctionType(1, isContextual = true) - .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) - val expr1 = typed(tree.expr, splicedType)(using spliceContext) - assignType(untpd.cpy.Splice(tree)(expr1, tpt1), tpt1) + .appliedTo(defn.QuotesClass.typeRef, exprType) + val expr1 = typed(tree.expr, quoteType)(using spliceContext) + untpd.cpy.Splice(tree)(expr1).withType(tree.typeOpt) override def localDummy(cls: ClassSymbol, impl: untpd.Template)(using Context): Symbol = impl.symbol diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index c22d1eb72aa6..6ac45cbcf04d 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -392,9 +392,6 @@ trait TypeAssigner { def assignType(tree: untpd.Inlined, bindings: List[Tree], expansion: Tree)(using Context): Inlined = tree.withType(avoidingType(expansion, bindings)) - def assignType(tree: untpd.Splice, tpt: Tree)(using Context): Splice = - tree.withType(tpt.tpe) - def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(using Context): If = tree.withType(thenp.tpe | elsep.tpe) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index dc72962401cc..c0b778756d41 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -642,7 +642,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler case self: tpd.Splice => // TODO expose Splice AST in Quotes import dotty.tools.dotc.ast.tpd.TreeOps tpd.ref(dotc.core.Symbols.defn.QuotedRuntime_exprSplice) - .appliedToTypeTree(self.tpt) + .appliedToType(self.tpe) .withSpan(self.span) def args: List[Term] = self match From d4b2f0933f7b38c30ce1fb16ddbfaa74834282b0 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 28 Apr 2023 16:19:20 +0200 Subject: [PATCH 23/46] Rename `Quote.{expr=>body}` Quotes can contain terms `'{..}` or types `'[..]`. --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 4 ++-- compiler/src/dotty/tools/dotc/ast/Trees.scala | 16 ++++++++-------- compiler/src/dotty/tools/dotc/ast/tpd.scala | 4 ++-- compiler/src/dotty/tools/dotc/ast/untpd.scala | 2 +- .../tools/dotc/core/tasty/TreePickler.scala | 6 +++--- .../src/dotty/tools/dotc/parsing/Parsers.scala | 4 ++-- .../tools/dotc/printing/RefinedPrinter.scala | 4 ++-- .../dotty/tools/dotc/transform/MegaPhase.scala | 4 ++-- .../tools/dotc/transform/PickleQuotes.scala | 2 +- .../dotty/tools/dotc/transform/PostTyper.scala | 2 +- .../src/dotty/tools/dotc/transform/Splicer.scala | 14 +++++++------- .../dotty/tools/dotc/transform/Splicing.scala | 4 ++-- .../tools/dotc/typer/QuotesAndSplices.scala | 10 +++++----- .../src/dotty/tools/dotc/typer/ReTyper.scala | 4 ++-- .../scala/quoted/runtime/impl/QuotesImpl.scala | 2 +- 15 files changed, 41 insertions(+), 41 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index c1dd78451bae..d6f9e0d24c91 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1986,13 +1986,13 @@ object desugar { trees foreach collect case Block(Nil, expr) => collect(expr) - case Quote(expr) => + case Quote(body) => new UntypedTreeTraverser { def traverse(tree: untpd.Tree)(using Context): Unit = tree match { case Splice(expr) => collect(expr) case _ => traverseChildren(tree) } - }.traverse(expr) + }.traverse(body) case CapturingTypeTree(refs, parent) => collect(parent) case _ => diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index e5b7f32a674d..0bcc28d360e9 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -687,7 +687,7 @@ object Trees { * * @param expr The tree that was quoted */ - case class Quote[+T <: Untyped] private[ast] (expr: Tree[T])(implicit @constructorOnly src: SourceFile) + case class Quote[+T <: Untyped] private[ast] (body: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Quote[T] @@ -1305,9 +1305,9 @@ object Trees { case tree: Inlined if (call eq tree.call) && (bindings eq tree.bindings) && (expansion eq tree.expansion) => tree case _ => finalize(tree, untpd.Inlined(call, bindings, expansion)(sourceFile(tree))) } - def Quote(tree: Tree)(expr: Tree)(using Context): Quote = tree match { - case tree: Quote if (expr eq tree.expr) => tree - case _ => finalize(tree, untpd.Quote(expr)(sourceFile(tree))) + def Quote(tree: Tree)(body: Tree)(using Context): Quote = tree match { + case tree: Quote if (body eq tree.body) => tree + case _ => finalize(tree, untpd.Quote(body)(sourceFile(tree))) } def Splice(tree: Tree)(expr: Tree)(using Context): Splice = tree match { case tree: Splice if (expr eq tree.expr) => tree @@ -1550,8 +1550,8 @@ object Trees { case Thicket(trees) => val trees1 = transform(trees) if (trees1 eq trees) tree else Thicket(trees1) - case tree @ Quote(expr) => - cpy.Quote(tree)(transform(expr)) + case tree @ Quote(body) => + cpy.Quote(tree)(transform(body)) case tree @ Splice(expr) => cpy.Splice(tree)(transform(expr)) case tree @ Hole(_, _, args, content, tpt) => @@ -1695,8 +1695,8 @@ object Trees { this(this(x, arg), annot) case Thicket(ts) => this(x, ts) - case Quote(expr) => - this(x, expr) + case Quote(body) => + this(x, body) case Splice(expr) => this(x, expr) case Hole(_, _, args, content, tpt) => diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index ce9bd3ea0595..a44472fed658 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -170,8 +170,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Inlined(call: Tree, bindings: List[MemberDef], expansion: Tree)(using Context): Inlined = ta.assignType(untpd.Inlined(call, bindings, expansion), bindings, expansion) - def Quote(expr: Tree, tpe: Type)(using Context): Quote = - untpd.Quote(expr).withExprType(tpe) + def Quote(body: Tree, tpe: Type)(using Context): Quote = + untpd.Quote(body).withExprType(tpe) def Splice(expr: Tree, tpe: Type)(using Context): Splice = untpd.Splice(expr).withType(tpe) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index ec85ffa689b6..30f58fba44ec 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -397,7 +397,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def SeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): SeqLiteral = new SeqLiteral(elems, elemtpt) def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt) def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion) - def Quote(expr: Tree)(implicit src: SourceFile): Quote = new Quote(expr) + def Quote(body: Tree)(implicit src: SourceFile): Quote = new Quote(body) def Splice(expr: Tree)(implicit src: SourceFile): Splice = new Splice(expr) def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree() def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree() diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 7682e042304a..97bfe4d28854 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -665,12 +665,12 @@ class TreePickler(pickler: TastyPickler) { pickleTree(hi) pickleTree(alias) } - case tree @ Quote(expr) => + case tree @ Quote(body) => pickleTree( - // scala.quoted.runtime.Expr.quoted[]() + // scala.quoted.runtime.Expr.quoted[]() ref(defn.QuotedRuntime_exprQuote) .appliedToType(tree.exprType) - .appliedTo(expr) + .appliedTo(body) .withSpan(tree.span) ) case Splice(expr) => diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 9e169d334bd5..324c3037d8ce 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2494,10 +2494,10 @@ object Parsers { case QUOTE => atSpan(in.skipToken()) { withinStaged(StageKind.Quoted | (if (location.inPattern) StageKind.QuotedPattern else 0)) { - val expr = + val body = if (in.token == LBRACKET) inBrackets(typ()) else stagedBlock() - Quote(expr) + Quote(body) } } case NEW => diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 7ed8ede8aaf9..d93cf1d87e02 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -716,9 +716,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { "Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}" case MacroTree(call) => keywordStr("macro ") ~ toTextGlobal(call) - case tree @ Quote(expr) => + case tree @ Quote(body) => val exprTypeText = (keywordStr("[") ~ toTextGlobal(tree.exprType) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists) - keywordStr("'") ~ exprTypeText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") + keywordStr("'") ~ exprTypeText ~ keywordStr("{") ~ toTextGlobal(body) ~ keywordStr("}") case Splice(expr) => val spliceTypeText = (keywordStr("[") ~ toTextGlobal(tree.typeOpt) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists) keywordStr("$") ~ spliceTypeText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") diff --git a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala index cf34c60330fa..7ad109b67751 100644 --- a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala +++ b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala @@ -401,8 +401,8 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase { } case tree: Quote => inContext(prepQuote(tree, start)(using outerCtx)) { - val expr = transformTree(tree.expr, start)(using quoteContext) - goQuote(cpy.Quote(tree)(expr), start) + val body = transformTree(tree.body, start)(using quoteContext) + goQuote(cpy.Quote(tree)(body), start) } case tree: Splice => inContext(prepSplice(tree, start)(using outerCtx)) { diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index f23558e8fe9a..a0ea03f238df 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -101,7 +101,7 @@ class PickleQuotes extends MacroTransform { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match case Apply(Select(quote: Quote, nme.apply), List(quotes)) => - val (contents, codeWithHoles) = makeHoles(quote.expr) + val (contents, codeWithHoles) = makeHoles(quote.body) val sourceRef = Inlines.inlineCallTrace(ctx.owner, tree.sourcePos) val codeWithHoles2 = Inlined(sourceRef, Nil, codeWithHoles) val pickled = PickleQuotes(quotes, codeWithHoles2, contents, quote.exprType, false) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 6861084b1dcb..d74392f201ba 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -485,7 +485,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase ) case Block(_, Closure(_, _, tpt)) if ExpandSAMs.needsWrapperClass(tpt.tpe) => superAcc.withInvalidCurrentClass(super.transform(tree)) - case Quote(expr) => + case _: Quote => ctx.compilationUnit.needsStaging = true super.transform(tree) case tree => diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 38f1005cd813..8f45fd94d205 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -155,7 +155,7 @@ object Splicer { case Block(Nil, expr) => checkIfValidArgument(expr) case Typed(expr, _) => checkIfValidArgument(expr) - case Apply(Select(Quote(expr), nme.apply), _) => + case Apply(Select(Quote(body), nme.apply), _) => val noSpliceChecker = new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match case Splice(_) => @@ -163,7 +163,7 @@ object Splicer { case _ => traverseChildren(tree) } - noSpliceChecker.traverse(expr) + noSpliceChecker.traverse(body) case Apply(TypeApply(fn, List(quoted)), _)if fn.symbol == defn.QuotedTypeModule_of => // OK @@ -240,15 +240,15 @@ object Splicer { override protected def interpretTree(tree: Tree)(implicit env: Env): Object = tree match { // Interpret level -1 quoted code `'{...}` (assumed without level 0 splices) - case Apply(Select(Quote(expr), nme.apply), _) => - val expr1 = expr match { + case Apply(Select(Quote(body), nme.apply), _) => + val body1 = body match { case expr: Ident if expr.symbol.isAllOf(InlineByNameProxy) => // inline proxy for by-name parameter expr.symbol.defTree.asInstanceOf[DefDef].rhs - case Inlined(EmptyTree, _, expr1) => expr1 - case _ => expr + case Inlined(EmptyTree, _, body1) => body1 + case _ => body } - new ExprImpl(Inlined(EmptyTree, Nil, QuoteUtils.changeOwnerOfTree(expr1, ctx.owner)).withSpan(expr1.span), SpliceScope.getCurrent) + new ExprImpl(Inlined(EmptyTree, Nil, QuoteUtils.changeOwnerOfTree(body1, ctx.owner)).withSpan(body1.span), SpliceScope.getCurrent) // Interpret level -1 `Type.of[T]` case Apply(TypeApply(fn, quoted :: Nil), _) if fn.symbol == defn.QuotedTypeModule_of => diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index fd195e8f068f..a0f105fa9745 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -86,7 +86,7 @@ class Splicing extends MacroTransform: override def transform(tree: tpd.Tree)(using Context): tpd.Tree = assert(level == 0) tree match - case Apply(Select(Quote(expr), nme.apply),List(quotes)) => + case Apply(Select(_: Quote, nme.apply), _) => QuoteTransformer().transform(tree) case TypeApply(_, _) if tree.symbol == defn.QuotedTypeModule_of => QuoteTransformer().transform(tree) @@ -134,7 +134,7 @@ class Splicing extends MacroTransform: typeHoles.put(qual, hole) hole cpy.TypeDef(tree)(rhs = hole) - case Apply(Select(Quote(expr), nme.apply),List(quotes)) => + case Apply(Select(_: Quote, nme.apply),List(quotes)) => super.transform(tree)(using quoteContext) case _: Template => for sym <- tree.symbol.owner.info.decls do diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 4642bdbbb9ba..ebff31414d68 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -36,8 +36,8 @@ trait QuotesAndSplices { */ def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = { record("typedQuote") - tree.expr match { - case untpd.Splice(innerExpr) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => + tree.body match { + case _: untpd.Splice if tree.isTerm && !ctx.mode.is(Mode.Pattern) => report.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.srcPos) case _ => } @@ -50,7 +50,7 @@ trait QuotesAndSplices { if ctx.mode.is(Mode.Pattern) then typedQuotePattern(tree, pt, quotes).withSpan(tree.span) - else if tree.expr.isType then + else if tree.body.isType then val msg = em"""Quoted types `'[..]` can only be used in patterns. | |Hint: To get a scala.quoted.Type[T] use scala.quoted.Type.of[T] instead. @@ -59,7 +59,7 @@ trait QuotesAndSplices { EmptyTree else // TODO typecheck directly (without `exprQuote`) - val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.expr) + val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.body) val quotedExpr = typedApply(exprQuoteTree, pt)(using quoteContext) match case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => Quote(quotedExpr, tpt.tpe) makeInlineable(quotedExpr.select(nme.apply).appliedTo(quotes).withSpan(tree.span)) @@ -383,7 +383,7 @@ trait QuotesAndSplices { * ``` */ private def typedQuotePattern(tree: untpd.Quote, pt: Type, qctx: Tree)(using Context): Tree = { - val quoted = tree.expr + val quoted = tree.body if quoted.isTerm && !pt.derivesFrom(defn.QuotedExprClass) then report.error("Quote pattern can only match scrutinees of type scala.quoted.Expr", tree.srcPos) else if quoted.isType && !pt.derivesFrom(defn.QuotedTypeClass) then diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index b43722d54cc7..d8e9b115ea49 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -97,8 +97,8 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking override def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = assertTyped(tree) - val expr1 = typed(tree.expr, tree.exprType)(using quoteContext) - untpd.cpy.Quote(tree)(expr1).withType(tree.typeOpt) + val body1 = typed(tree.body, tree.exprType)(using quoteContext) + untpd.cpy.Quote(tree)(body1).withType(tree.typeOpt) override def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = assertTyped(tree) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index c0b778756d41..1df2abcf0453 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -647,7 +647,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def args: List[Term] = self match case self: tpd.Apply => self.args - case self: tpd.Quote => List(self.expr) // TODO expose Quote AST in Quotes + case self: tpd.Quote => List(self.body) // TODO expose Quote AST in Quotes case self: tpd.Splice => List(self.expr) // TODO expose Splice AST in Quotes end extension end ApplyMethods From 35408ec652effb8ecd03cb6b76128cd561ba2245 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 28 Apr 2023 16:29:24 +0200 Subject: [PATCH 24/46] Fix staging level tracking in Transformer --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 5 +++-- .../dotty/tools/dotc/transform/Splicing.scala | 20 +++++++------------ 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 0bcc28d360e9..c9840cd36cdf 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -17,6 +17,7 @@ import annotation.unchecked.uncheckedVariance import annotation.constructorOnly import compiletime.uninitialized import Decorators._ +import staging.StagingLevel.* object Trees { @@ -1551,9 +1552,9 @@ object Trees { val trees1 = transform(trees) if (trees1 eq trees) tree else Thicket(trees1) case tree @ Quote(body) => - cpy.Quote(tree)(transform(body)) + cpy.Quote(tree)(transform(body)(using quoteContext)) case tree @ Splice(expr) => - cpy.Splice(tree)(transform(expr)) + cpy.Splice(tree)(transform(expr)(using spliceContext)) case tree @ Hole(_, _, args, content, tpt) => cpy.Hole(tree)(args = transform(args), content = transform(content), tpt = transform(tpt)) case _ => diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index a0f105fa9745..0b50343905f8 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -111,17 +111,13 @@ class Splicing extends MacroTransform: override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match - case tree: Splice => - if level > 1 then - val expr1 = super.transform(tree.expr)(using spliceContext) - cpy.Splice(tree)(expr1) - else - val holeIdx = numHoles - numHoles += 1 - val splicer = SpliceTransformer(ctx.owner, quotedDefs.contains) - val newSplicedCode1 = splicer.transformSplice(tree.expr, tree.tpe, holeIdx)(using spliceContext) - val newSplicedCode2 = Level0QuoteTransformer.transform(newSplicedCode1)(using spliceContext) - newSplicedCode2 + case tree: Splice if level == 1 => + val holeIdx = numHoles + numHoles += 1 + val splicer = SpliceTransformer(ctx.owner, quotedDefs.contains) + val newSplicedCode1 = splicer.transformSplice(tree.expr, tree.tpe, holeIdx)(using spliceContext) + val newSplicedCode2 = Level0QuoteTransformer.transform(newSplicedCode1)(using spliceContext) + newSplicedCode2 case tree: TypeDef if tree.symbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => val tp @ TypeRef(qual: TermRef, _) = tree.rhs.tpe.hiBound: @unchecked quotedDefs += tree.symbol @@ -134,8 +130,6 @@ class Splicing extends MacroTransform: typeHoles.put(qual, hole) hole cpy.TypeDef(tree)(rhs = hole) - case Apply(Select(_: Quote, nme.apply),List(quotes)) => - super.transform(tree)(using quoteContext) case _: Template => for sym <- tree.symbol.owner.info.decls do quotedDefs += sym From e73eab74b8d681bbf9ce61d9a8df50addfe00e62 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 28 Apr 2023 16:40:01 +0200 Subject: [PATCH 25/46] Cleanup unnecessary staging level updates --- .../dotc/inlines/PrepareInlineable.scala | 2 -- .../dotty/tools/dotc/transform/Inlining.scala | 8 -------- .../dotty/tools/dotc/transform/Splicing.scala | 20 +++++++------------ 3 files changed, 7 insertions(+), 23 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index c771eff9e840..ed8683bf210e 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -91,9 +91,7 @@ object PrepareInlineable { } private def stagingContext(tree: Tree)(using Context): Context = tree match - case tree: Quote => StagingLevel.quoteContext case tree: Apply if tree.symbol eq defn.QuotedTypeModule_of => StagingLevel.quoteContext - case tree: Splice => StagingLevel.spliceContext case _ => ctx } diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 60714d273bf0..f5b72759b57e 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -45,12 +45,8 @@ class Inlining extends MacroTransform { new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match - case _: Quote => - traverseChildren(tree)(using StagingLevel.quoteContext) case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of => traverseChildren(tree)(using StagingLevel.quoteContext) - case _: Splice => - traverseChildren(tree)(using StagingLevel.spliceContext) case tree: RefTree if !Inlines.inInlineMethod && StagingLevel.level == 0 => assert(!tree.symbol.isInlineMethod, tree.show) case _ => @@ -99,12 +95,8 @@ class Inlining extends MacroTransform { val tree1 = super.transform(tree) if tree1.tpe.isError then tree1 else Inlines.inlineCall(tree1) - case _: Quote => - super.transform(tree)(using StagingLevel.quoteContext) case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of => super.transform(tree)(using StagingLevel.quoteContext) - case _: Splice => - super.transform(tree)(using StagingLevel.spliceContext) case _: PackageDef => super.transform(tree) match case tree1: PackageDef => diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 0b50343905f8..e8102a5db224 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -228,19 +228,10 @@ class Splicing extends MacroTransform: case tree @ Assign(lhs: RefTree, rhs) => if isCaptured(lhs.symbol) then transformSplicedAssign(tree) else super.transform(tree) - case Splice(expr) => - val expr1 = transform(expr)(using spliceContext) - cpy.Splice(tree)(expr1) - case Apply(sel @ Select(app @ Quote(expr), nme.apply), quotesArgs) => - expr match - case expr: RefTree if isCaptured(expr.symbol) => - capturedTerm(expr) - case _ => - val newExpr = withCurrentQuote(quotesArgs.head) { - if level > 1 then transform(expr)(using quoteContext) - else transformLevel0QuoteContent(expr)(using quoteContext) - } - cpy.Apply(tree)(cpy.Select(sel)(cpy.Quote(app)(newExpr), nme.apply), quotesArgs) + case Apply(sel @ Select(app @ Quote(body), nme.apply), quotesArgs) => + body match + case body: RefTree if isCaptured(body.symbol) => capturedTerm(body) + case _ => withCurrentQuote(quotesArgs.head) { super.transform(tree) } case Apply(TypeApply(typeof, List(tpt)), List(quotes)) if tree.symbol == defn.QuotedTypeModule_of && containsCapturedType(tpt.tpe) => val newContent = capturedPartTypes(tpt) @@ -253,6 +244,9 @@ class Splicing extends MacroTransform: ref(capturedType(newContent))(using ctx.withSource(tree.source)).withSpan(tree.span) case CapturedApplication(fn, argss) => transformCapturedApplication(tree, fn, argss) + case Quote(expr) if level == 0 => + val newExpr = transformLevel0QuoteContent(expr)(using quoteContext) + cpy.Quote(tree)(newExpr) case _ => super.transform(tree) From e06b0f95809f22c6dfb5c1e6283584d88bfa102d Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 10:19:46 +0200 Subject: [PATCH 26/46] Use `Quote to encode `Type.of` in the `staging phase` --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 28 +++++++++++------ compiler/src/dotty/tools/dotc/ast/tpd.scala | 4 +-- .../tools/dotc/core/tasty/TreePickler.scala | 10 ++++-- .../tools/dotc/core/tasty/TreeUnpickler.scala | 2 +- .../tools/dotc/printing/RefinedPrinter.scala | 6 ++-- .../tools/dotc/staging/CrossStageSafety.scala | 11 ++++--- .../dotc/staging/TreeMapWithStages.scala | 4 --- .../tools/dotc/transform/PickleQuotes.scala | 24 ++++++-------- .../tools/dotc/transform/ReifiedReflect.scala | 4 +-- .../dotty/tools/dotc/transform/Splicing.scala | 31 +++++++------------ .../dotty/tools/dotc/transform/Staging.scala | 7 +++++ .../tools/dotc/typer/QuotesAndSplices.scala | 10 +++--- .../src/dotty/tools/dotc/typer/ReTyper.scala | 2 +- .../quoted/runtime/impl/QuotesImpl.scala | 2 +- 14 files changed, 74 insertions(+), 71 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index c9840cd36cdf..19c189047c6d 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -678,7 +678,7 @@ object Trees { override def isType = expansion.isType } - /** A tree representing a quote `'{ expr }` + /** A tree representing a quote `'{ body }` or `'[ body ]`. * `Quote`s are created by the `Parser`. In typer they can be typed as a * `Quote` with a known `tpt` or desugared and typed as a quote pattern. * @@ -686,23 +686,31 @@ object Trees { * phases. After `pickleQuotes` phase, the only quotes that exist are in `inline` * methods. These are dropped when we remove the inline method implementations. * - * @param expr The tree that was quoted + * Type quotes `'[body]` from the parser are desugared into quote patterns (using a `Type.of[T]]`) + * when type checking. TASTy files will not contain type quotes. Type quotes are used again + * in the `staging` phase to represent the reification of `Type.of[T]]`. + * + * @param body The tree that was quoted */ case class Quote[+T <: Untyped] private[ast] (body: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Quote[T] + /** Is this a type quote `'[tpe]' */ + def isTypeQuote = body.isType + /** Type of the quoted expression as seen from outside the quote */ - def exprType(using Context): Type = - val quoteType = typeOpt // Quotes ?=> Expr[T] - val exprType = quoteType.argInfos.last // Expr[T] + def bodyType(using Context): Type = + val quoteType = typeOpt // `Quotes ?=> Expr[T]` or `Quotes ?=> Type[T]` + val exprType = quoteType.argInfos.last // `Expr[T]` or `Type[T]` exprType.argInfos.head // T - /** Set the type of the quoted expression as seen from outside the quote */ - def withExprType(tpe: Type)(using Context): Quote[Type] = - val exprType = // Expr[T] - defn.QuotedExprClass.typeRef.appliedTo(tpe) - val quoteType = // Quotes ?=> Expr[T] + /** Set the type of the body of the quote */ + def withBodyType(tpe: Type)(using Context): Quote[Type] = + val exprType = // `Expr[T]` or `Type[T]` + if body.isTerm then defn.QuotedExprClass.typeRef.appliedTo(tpe) + else defn.QuotedTypeClass.typeRef.appliedTo(tpe) + val quoteType = // `Quotes ?=> Expr[T]` or `Quotes ?=> Type[T]` defn.FunctionType(1, isContextual = true) .appliedTo(defn.QuotesClass.typeRef, exprType) withType(quoteType) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index a44472fed658..3a957c8f4612 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -170,8 +170,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Inlined(call: Tree, bindings: List[MemberDef], expansion: Tree)(using Context): Inlined = ta.assignType(untpd.Inlined(call, bindings, expansion), bindings, expansion) - def Quote(body: Tree, tpe: Type)(using Context): Quote = - untpd.Quote(body).withExprType(tpe) + def Quote(body: Tree)(using Context): Quote = + untpd.Quote(body).withBodyType(body.tpe) def Splice(expr: Tree, tpe: Type)(using Context): Splice = untpd.Splice(expr).withType(tpe) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 97bfe4d28854..87620db36482 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -666,15 +666,19 @@ class TreePickler(pickler: TastyPickler) { pickleTree(alias) } case tree @ Quote(body) => + // Add QUOTE tag to TASTy + assert(body.isTerm, + """Quote with type should not be pickled. + |Quote with type should only exists after staging phase at level staging level 0.""".stripMargin) pickleTree( - // scala.quoted.runtime.Expr.quoted[]() + // scala.quoted.runtime.Expr.quoted[]() ref(defn.QuotedRuntime_exprQuote) - .appliedToType(tree.exprType) + .appliedToType(tree.bodyType) .appliedTo(body) .withSpan(tree.span) ) case Splice(expr) => - pickleTree( + pickleTree( // Add SPLICE tag to TASTy // scala.quoted.runtime.Expr.splice[]() ref(defn.QuotedRuntime_exprSplice) .appliedToType(tree.tpe) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 4f81e1477ea7..10395a488640 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1269,7 +1269,7 @@ class TreeUnpickler(reader: TastyReader, def quotedExpr(fn: Tree, args: List[Tree]): Tree = val TypeApply(_, targs) = fn: @unchecked - Quote(args.head, targs.head.tpe) + untpd.Quote(args.head).withBodyType(targs.head.tpe) def splicedExpr(fn: Tree, args: List[Tree]): Tree = val TypeApply(_, targs) = fn: @unchecked diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index d93cf1d87e02..93de33778750 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -717,8 +717,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case MacroTree(call) => keywordStr("macro ") ~ toTextGlobal(call) case tree @ Quote(body) => - val exprTypeText = (keywordStr("[") ~ toTextGlobal(tree.exprType) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists) - keywordStr("'") ~ exprTypeText ~ keywordStr("{") ~ toTextGlobal(body) ~ keywordStr("}") + val exprTypeText = (keywordStr("[") ~ toTextGlobal(tree.bodyType) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists) + val open = if (body.isTerm) keywordStr("{") else keywordStr("[") + val close = if (body.isTerm) keywordStr("}") else keywordStr("]") + keywordStr("'") ~ exprTypeText ~ open ~ toTextGlobal(body) ~ close case Splice(expr) => val spliceTypeText = (keywordStr("[") ~ toTextGlobal(tree.typeOpt) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists) keywordStr("$") ~ spliceTypeText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 32b54ffa7ff8..cb5f3520429a 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -101,10 +101,10 @@ class CrossStageSafety extends TreeMapWithStages { if (ctx.property(InAnnotation).isDefined) report.error("Cannot have a quote in an annotation", quote.srcPos) val transformedBody = transformQuoteBody(body, quote.span) - val stripAnnotsDeep: TypeMap = new TypeMap: + val stripAnnotationsDeep: TypeMap = new TypeMap: def apply(tp: Type): Type = mapOver(tp.stripAnnots) - val exprType1 = healType(quote.srcPos)(stripAnnotsDeep(quote.exprType)) - cpy.Quote(quote)(transformedBody).withExprType(exprType1) + val bodyType1 = healType(quote.srcPos)(stripAnnotationsDeep(quote.bodyType)) + cpy.Quote(quote)(transformedBody).withBodyType(bodyType1) } override protected def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = { @@ -120,10 +120,11 @@ class CrossStageSafety extends TreeMapWithStages { // Optimization: `quoted.Type.of[@SplicedType type T = x.Underlying; T](quotes)` --> `x` ref(termRef).withSpan(quote.span) case transformedBody => - val quotes = quote.args.mapConserve(transform) + val quotes = transform(quote.args.head) // `quoted.Type.of[](quotes)` --> `quoted.Type.of[](quotes)` val TypeApply(fun, _) = quote.fun: @unchecked - cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, transformedBody :: Nil), quotes) + if level != 0 then cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, transformedBody :: Nil), quotes :: Nil) + else tpd.Quote(transformedBody).select(nme.apply).appliedTo(quotes).withSpan(quote.span) } private def transformQuoteBody(body: Tree, span: Span)(using Context): Tree = { diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index ddf2017af368..e262530ba11e 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -52,10 +52,6 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { } tree match { - case Apply(Select(QuotedTypeOf(SplicedType(t)), _), _) => - // Optimization: `quoted.Type.of[x.Underlying]` --> `x` - transform(t) - case tree @ QuotedTypeOf(quotedTree) => val old = inQuoteOrSplice inQuoteOrSplice = true diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index a0ea03f238df..b1a707a70cb1 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -103,17 +103,11 @@ class PickleQuotes extends MacroTransform { case Apply(Select(quote: Quote, nme.apply), List(quotes)) => val (contents, codeWithHoles) = makeHoles(quote.body) val sourceRef = Inlines.inlineCallTrace(ctx.owner, tree.sourcePos) - val codeWithHoles2 = Inlined(sourceRef, Nil, codeWithHoles) - val pickled = PickleQuotes(quotes, codeWithHoles2, contents, quote.exprType, false) + val bodyWithHoles2 = + if quote.body.isType then codeWithHoles + else Inlined(sourceRef, Nil, codeWithHoles) + val pickled = PickleQuotes.pickle(quotes, bodyWithHoles2, contents, quote.bodyType) transform(pickled) // pickle quotes that are in the contents - case Apply(TypeApply(_, List(tpt)), List(quotes)) if tree.symbol == defn.QuotedTypeModule_of => - tpt match - case Select(t, _) if tpt.symbol == defn.QuotedType_splice => - // `Type.of[t.Underlying](quotes)` --> `t` - ref(t.symbol)(using ctx.withSource(tpt.source)).withSpan(tpt.span) - case _ => - val (contents, tptWithHoles) = makeHoles(tpt) - PickleQuotes(quotes, tptWithHoles, contents, tpt.tpe, true) case tree: DefDef if !tree.rhs.isEmpty && tree.symbol.isInlineMethod => // Shrink size of the tree. The methods have already been inlined. // TODO move to FirstTransform to trigger even without quotes @@ -208,7 +202,7 @@ object PickleQuotes { val name: String = "pickleQuotes" val description: String = "turn quoted trees into explicit run-time data structures" - def apply(quotes: Tree, body: Tree, contents: List[Tree], originalTp: Type, isType: Boolean)(using Context) = { + def pickle(quotes: Tree, body: Tree, contents: List[Tree], originalTp: Type)(using Context) = { /** Helper methods to construct trees calling methods in `Quotes.reflect` based on the current `quotes` tree */ object reflect extends ReifiedReflect { val quotesTree = quotes @@ -340,14 +334,14 @@ object PickleQuotes { case _ => Match(args(0).annotated(New(ref(defn.UncheckedAnnot.typeRef))), cases) ) - val quoteClass = if isType then defn.QuotedTypeClass else defn.QuotedExprClass + val quoteClass = if body.isType then defn.QuotedTypeClass else defn.QuotedExprClass val quotedType = quoteClass.typeRef.appliedTo(originalTp) val lambdaTpe = MethodType(defn.QuotesClass.typeRef :: Nil, quotedType) val unpickleMeth = - if isType then defn.QuoteUnpickler_unpickleTypeV2 + if body.isType then defn.QuoteUnpickler_unpickleTypeV2 else defn.QuoteUnpickler_unpickleExprV2 val unpickleArgs = - if isType then List(pickledQuoteStrings, types) + if body.isType then List(pickledQuoteStrings, types) else List(pickledQuoteStrings, types, termHoles) quotes .asInstance(defn.QuoteUnpicklerClass.typeRef) @@ -378,7 +372,7 @@ object PickleQuotes { case Inlined(_, Nil, e) => getLiteral(e) case _ => None - if (isType) then + if body.isType then if contents.isEmpty && body.symbol.isPrimitiveValueClass then taggedType() else pickleAsTasty() else diff --git a/compiler/src/dotty/tools/dotc/transform/ReifiedReflect.scala b/compiler/src/dotty/tools/dotc/transform/ReifiedReflect.scala index b2059195b8e4..bd0c063cd21a 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifiedReflect.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifiedReflect.scala @@ -75,8 +75,8 @@ trait ReifiedReflect: .select(defn.Quotes_reflect_TypeRepr_of) .appliedToType(tpe) .appliedTo( - ref(defn.QuotedTypeModule_of) - .appliedToType(tpe) + tpd.Quote(TypeTree(tpe)) + .select(nme.apply) .appliedTo(quotesTree) ) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index e8102a5db224..c833a0800e87 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -88,8 +88,6 @@ class Splicing extends MacroTransform: tree match case Apply(Select(_: Quote, nme.apply), _) => QuoteTransformer().transform(tree) - case TypeApply(_, _) if tree.symbol == defn.QuotedTypeModule_of => - QuoteTransformer().transform(tree) case tree: DefDef if tree.symbol.is(Inline) => // Quotes in inlined methods are only pickled after they are inlined. tree @@ -228,25 +226,18 @@ class Splicing extends MacroTransform: case tree @ Assign(lhs: RefTree, rhs) => if isCaptured(lhs.symbol) then transformSplicedAssign(tree) else super.transform(tree) - case Apply(sel @ Select(app @ Quote(body), nme.apply), quotesArgs) => - body match - case body: RefTree if isCaptured(body.symbol) => capturedTerm(body) - case _ => withCurrentQuote(quotesArgs.head) { super.transform(tree) } - case Apply(TypeApply(typeof, List(tpt)), List(quotes)) - if tree.symbol == defn.QuotedTypeModule_of && containsCapturedType(tpt.tpe) => - val newContent = capturedPartTypes(tpt) - newContent match - case block: Block => - inContext(ctx.withSource(tree.source)) { - Apply(TypeApply(typeof, List(newContent)), List(quotes)).withSpan(tree.span) - } - case _ => - ref(capturedType(newContent))(using ctx.withSource(tree.source)).withSpan(tree.span) case CapturedApplication(fn, argss) => transformCapturedApplication(tree, fn, argss) - case Quote(expr) if level == 0 => - val newExpr = transformLevel0QuoteContent(expr)(using quoteContext) - cpy.Quote(tree)(newExpr) + case Apply(sel @ Select(app @ Quote(body), nme.apply), quotes :: Nil) if level == 0 && body.isTerm => + body match + case _: RefTree if isCaptured(body.symbol) => capturedTerm(body) + case _ => withCurrentQuote(quotes) { super.transform(tree) } + case Quote(body) if level == 0 => + val newBody = + if body.isTerm then transformLevel0QuoteContent(body)(using quoteContext) + else if containsCapturedType(body.tpe) then capturedPartTypes(body) + else body + cpy.Quote(tree)(newBody) case _ => super.transform(tree) @@ -400,7 +391,7 @@ class Splicing extends MacroTransform: Splice(closure, tpe) private def quoted(expr: Tree)(using Context): Tree = - Quote(expr, expr.tpe.widenTermRefExpr) + untpd.Quote(expr).withBodyType(expr.tpe.widenTermRefExpr) // TODO do we need widenTermRefExpr? .select(nme.apply) .appliedTo(quotes.nn) diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala index a025f86a1db6..581fd2753504 100644 --- a/compiler/src/dotty/tools/dotc/transform/Staging.scala +++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala @@ -57,6 +57,13 @@ class Staging extends MacroTransform { case _ => } + tree match { + case tree: RefTree => + assert(level != 0 || tree.symbol != defn.QuotedTypeModule_of, + "scala.quoted.Type.of at level 0 should have been replaced with Quote AST in staging phase") + case _ => + } + tree.tpe match { case tpe @ TypeRef(prefix, _) if tpe.typeSymbol.isTypeSplice => // Type splices must have a know term ref, usually to an implicit argument diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index ebff31414d68..5372dc4d04b6 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -61,7 +61,7 @@ trait QuotesAndSplices { // TODO typecheck directly (without `exprQuote`) val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.body) val quotedExpr = typedApply(exprQuoteTree, pt)(using quoteContext) match - case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => Quote(quotedExpr, tpt.tpe) + case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => untpd.Quote(quotedExpr).withBodyType(tpt.tpe) makeInlineable(quotedExpr.select(nme.apply).appliedTo(quotes).withSpan(tree.span)) } @@ -382,7 +382,7 @@ trait QuotesAndSplices { * ) => ... * ``` */ - private def typedQuotePattern(tree: untpd.Quote, pt: Type, qctx: Tree)(using Context): Tree = { + private def typedQuotePattern(tree: untpd.Quote, pt: Type, quotes: Tree)(using Context): Tree = { val quoted = tree.body if quoted.isTerm && !pt.derivesFrom(defn.QuotedExprClass) then report.error("Quote pattern can only match scrutinees of type scala.quoted.Expr", tree.srcPos) @@ -442,11 +442,11 @@ trait QuotesAndSplices { val quoteClass = if (quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass val quotedPattern = - if (quoted.isTerm) tpd.Quote(shape, defn.AnyType).select(nme.apply).appliedTo(qctx) - else ref(defn.QuotedTypeModule_of.termRef).appliedToTypeTree(shape).appliedTo(qctx) + if (quoted.isTerm) tpd.Quote(shape).select(nme.apply).appliedTo(quotes) + else ref(defn.QuotedTypeModule_of.termRef).appliedToTypeTree(shape).appliedTo(quotes) val matchModule = if quoted.isTerm then defn.QuoteMatching_ExprMatch else defn.QuoteMatching_TypeMatch - val unapplyFun = qctx.asInstance(defn.QuoteMatchingClass.typeRef).select(matchModule).select(nme.unapply) + val unapplyFun = quotes.asInstance(defn.QuoteMatchingClass.typeRef).select(matchModule).select(nme.unapply) UnApply( fun = unapplyFun.appliedToTypeTrees(typeBindingsTuple :: TypeTree(patType) :: Nil), diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index d8e9b115ea49..f8cfd4ca3ae0 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -97,7 +97,7 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking override def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = assertTyped(tree) - val body1 = typed(tree.body, tree.exprType)(using quoteContext) + val body1 = typed(tree.body, tree.bodyType)(using quoteContext) untpd.cpy.Quote(tree)(body1).withType(tree.typeOpt) override def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 1df2abcf0453..245bcdac70af 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -637,7 +637,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler case self: tpd.Quote => // TODO expose Quote AST in Quotes import dotty.tools.dotc.ast.tpd.TreeOps tpd.ref(dotc.core.Symbols.defn.QuotedRuntime_exprQuote) - .appliedToType(self.exprType) + .appliedToType(self.bodyType) .withSpan(self.span) case self: tpd.Splice => // TODO expose Splice AST in Quotes import dotty.tools.dotc.ast.tpd.TreeOps From 095a7f1e53b8339c21b238e77245f0027e0c5719 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 11:00:07 +0200 Subject: [PATCH 27/46] Remove state from TreeMapWithStages --- .../tools/dotc/staging/CrossStageSafety.scala | 2 +- .../tools/dotc/staging/StagingLevel.scala | 4 +++ .../dotc/staging/TreeMapWithStages.scala | 27 +++---------------- 3 files changed, 9 insertions(+), 24 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index cb5f3520429a..fbf9eac7af03 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -50,7 +50,7 @@ class CrossStageSafety extends TreeMapWithStages { override def transform(tree: Tree)(using Context): Tree = if (tree.source != ctx.source && tree.source.exists) transform(tree)(using ctx.withSource(tree.source)) - else if !isInQuoteOrSplice then + else if !inQuoteOrSpliceScope then checkAnnotations(tree) super.transform(tree) else tree match { diff --git a/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala b/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala index 4704501e38ff..05b3efab408c 100644 --- a/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala +++ b/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala @@ -31,6 +31,10 @@ object StagingLevel { def spliceContext(using Context): FreshContext = ctx.fresh.setProperty(LevelKey, level - 1) + /** If we are inside a quote or a splice */ + def inQuoteOrSpliceScope(using Context): Boolean = + ctx.property(LevelKey).isDefined + /** The quotation level of the definition of the locally defined symbol */ def levelOf(sym: Symbol)(using Context): Int = ctx.property(LevelOfKey) match diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index e262530ba11e..6c193ee7a34b 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -15,12 +15,6 @@ import scala.collection.mutable abstract class TreeMapWithStages extends TreeMapWithImplicits { import tpd._ - /** If we are inside a quote or a splice */ - private[this] var inQuoteOrSplice = false - - /** If we are inside a quote or a splice */ - protected def isInQuoteOrSplice: Boolean = inQuoteOrSplice - /** Transform the quote `quote` which contains the quoted `body`. * * - `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T]()` @@ -53,15 +47,9 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { tree match { case tree @ QuotedTypeOf(quotedTree) => - val old = inQuoteOrSplice - inQuoteOrSplice = true - try transformQuotedType(quotedTree, tree) - finally inQuoteOrSplice = old - + transformQuotedType(quotedTree, tree) case tree @ Quote(quotedTree) => - val old = inQuoteOrSplice - inQuoteOrSplice = true - try dropEmptyBlocks(quotedTree) match { + dropEmptyBlocks(quotedTree) match { case Splice(t) => // Optimization: `'{ $x }` --> `x` // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]` @@ -69,25 +57,18 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { case _ => transformQuote(quotedTree, tree) } - finally inQuoteOrSplice = old case tree @ Splice(splicedTree) => - val old = inQuoteOrSplice - inQuoteOrSplice = true - try dropEmptyBlocks(splicedTree) match { + dropEmptyBlocks(splicedTree) match { case Quote(t) => // Optimization: `${ 'x }` --> `x` transform(t) case _ => transformSplice(splicedTree, tree) } - finally inQuoteOrSplice = old case tree @ SplicedType(splicedTree) => - val old = inQuoteOrSplice - inQuoteOrSplice = true - try transformSpliceType(splicedTree, tree) - finally inQuoteOrSplice = old + transformSpliceType(splicedTree, tree) case Block(stats, _) => val defSyms = stats.collect { case defTree: DefTree => defTree.symbol } From 10cbc605f9ead7a144b70af607c72e46fa105e54 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 11:13:43 +0200 Subject: [PATCH 28/46] Fix wrong staging level error message The tests were failing when the code was correct. The wrong message was ``` access to parameter t from wrong staging level: - the definition is at level 0, - but the access is at level -1. ``` The access to `t.Underlying` or `t.x.Underlying` are fine when we have an implicit `Type.of[t.Underlying]` or `Type.of[t.x.Underlying]`. These will probably be of the type of the aliased type of `A`, wherefore summoning `Type.of[A]`. --- .../src/dotty/tools/dotc/staging/CrossStageSafety.scala | 4 +++- .../src/dotty/tools/dotc/staging/TreeMapWithStages.scala | 6 ------ tests/neg-macros/i10127-a.scala | 2 +- tests/neg-macros/i10127-b.scala | 2 +- tests/pos-macros/i10127-a.scala | 8 ++++++++ 5 files changed, 13 insertions(+), 9 deletions(-) create mode 100644 tests/pos-macros/i10127-a.scala diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index fbf9eac7af03..02404337db80 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -92,6 +92,8 @@ class CrossStageSafety extends TreeMapWithStages { case tree: TypeDef if tree.symbol.is(Case) && level > 0 => report.error(reporting.CaseClassInInlinedCode(tree), tree) super.transform(tree) + case tree @ SplicedType(splicedTree) => + transformSpliceType(splicedTree, tree) case _ => super.transform(tree) } @@ -151,7 +153,7 @@ class CrossStageSafety extends TreeMapWithStages { untpd.cpy.Splice(splice)(body1).withType(tpe1) } - protected def transformSpliceType(body: Tree, splice: Select)(using Context): Tree = { + private def transformSpliceType(body: Tree, splice: Select)(using Context): Tree = { val body1 = transform(body)(using spliceContext) if ctx.reporter.hasErrors then splice diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index 6c193ee7a34b..31a764fe7040 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -33,9 +33,6 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { /** Transform the expression splice `splice` which contains the spliced `body`. */ protected def transformSplice(body: Tree, splice: Splice)(using Context): Tree - /** Transform the type splice `splice` which contains the spliced `body`. */ - protected def transformSpliceType(body: Tree, splice: Select)(using Context): Tree - override def transform(tree: Tree)(using Context): Tree = if (tree.source != ctx.source && tree.source.exists) transform(tree)(using ctx.withSource(tree.source)) @@ -67,9 +64,6 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { transformSplice(splicedTree, tree) } - case tree @ SplicedType(splicedTree) => - transformSpliceType(splicedTree, tree) - case Block(stats, _) => val defSyms = stats.collect { case defTree: DefTree => defTree.symbol } super.transform(tree)(using symbolsInCurrentLevel(defSyms)) diff --git a/tests/neg-macros/i10127-a.scala b/tests/neg-macros/i10127-a.scala index 3e23cf10bd30..2da4d0924870 100644 --- a/tests/neg-macros/i10127-a.scala +++ b/tests/neg-macros/i10127-a.scala @@ -1,7 +1,7 @@ import scala.quoted.* object T { - def impl[A](using t: Type[A])(using Quotes): Expr[Unit] = { + def impl[A](t: Type[A])(using Quotes): Expr[Unit] = { Expr.summon[t.Underlying] // error '{} } diff --git a/tests/neg-macros/i10127-b.scala b/tests/neg-macros/i10127-b.scala index 2e87e92efa63..13992bf95362 100644 --- a/tests/neg-macros/i10127-b.scala +++ b/tests/neg-macros/i10127-b.scala @@ -4,7 +4,7 @@ case class T(x: Type[_ <: Any]) object T { def impl[A](t: T)(using ctx: Quotes): Expr[Unit] = { - Expr.summon[t.x.Underlying] // error // error + Expr.summon[t.x.Underlying] // error '{} } } \ No newline at end of file diff --git a/tests/pos-macros/i10127-a.scala b/tests/pos-macros/i10127-a.scala new file mode 100644 index 000000000000..3b9efc2a829d --- /dev/null +++ b/tests/pos-macros/i10127-a.scala @@ -0,0 +1,8 @@ +import scala.quoted.* + +object T { + def impl[A](using t: Type[A])(using Quotes): Expr[Unit] = { + Expr.summon[t.Underlying] + '{} + } +} \ No newline at end of file From 3097f490dc3e56a7ff6146753f93285216477bc0 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 11:28:55 +0200 Subject: [PATCH 29/46] Simplify quoted type transformation in CrossStageSafety --- .../dotty/tools/dotc/staging/CrossStageSafety.scala | 9 +++++++-- .../dotty/tools/dotc/staging/TreeMapWithStages.scala | 10 ---------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 02404337db80..d4ccf3321222 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -52,7 +52,10 @@ class CrossStageSafety extends TreeMapWithStages { transform(tree)(using ctx.withSource(tree.source)) else if !inQuoteOrSpliceScope then checkAnnotations(tree) - super.transform(tree) + tree match + case tree @ QuotedTypeOf(quotedTree) => + transformQuotedType(quotedTree, tree) + case _ => super.transform(tree) else tree match { case _: TypeTree => val tp1 = transformTypeAnnotationSplices(tree.tpe) @@ -92,6 +95,8 @@ class CrossStageSafety extends TreeMapWithStages { case tree: TypeDef if tree.symbol.is(Case) && level > 0 => report.error(reporting.CaseClassInInlinedCode(tree), tree) super.transform(tree) + case tree @ QuotedTypeOf(quotedTree) => + transformQuotedType(quotedTree, tree) case tree @ SplicedType(splicedTree) => transformSpliceType(splicedTree, tree) case _ => @@ -109,7 +114,7 @@ class CrossStageSafety extends TreeMapWithStages { cpy.Quote(quote)(transformedBody).withBodyType(bodyType1) } - override protected def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = { + private def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = { if (ctx.property(InAnnotation).isDefined) report.error("Cannot have a quote in an annotation", quote.srcPos) body.tpe match diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index 31a764fe7040..67fb17551253 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -22,14 +22,6 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { protected def transformQuote(body: Tree, quote: Quote)(using Context): Tree = cpy.Quote(quote)(body) - /** Transform the quote `quote` which contains the quoted `body`. - * - * - `quoted.Type.of[](quotes)` --> `quoted.Type.of[](quotes)` - */ - protected def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = - val TypeApply(fun, _) = quote.fun: @unchecked - cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, body :: Nil), quote.args) - /** Transform the expression splice `splice` which contains the spliced `body`. */ protected def transformSplice(body: Tree, splice: Splice)(using Context): Tree @@ -43,8 +35,6 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { } tree match { - case tree @ QuotedTypeOf(quotedTree) => - transformQuotedType(quotedTree, tree) case tree @ Quote(quotedTree) => dropEmptyBlocks(quotedTree) match { case Splice(t) => From 8a9871fe5c0599ae42bb8378157b265e4269d247 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 11:45:39 +0200 Subject: [PATCH 30/46] Simplify Quote/Splice transformations in CrossStageSafety --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 21 +++++++++++ .../tools/dotc/staging/CrossStageSafety.scala | 20 +++++++++- .../dotc/staging/TreeMapWithStages.scala | 37 +------------------ 3 files changed, 40 insertions(+), 38 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 19c189047c6d..fbc68ebe63d7 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -714,6 +714,17 @@ object Trees { defn.FunctionType(1, isContextual = true) .appliedTo(defn.QuotesClass.typeRef, exprType) withType(quoteType) + + /** Cancel this Quote if it contains a Splice */ + def cancelled(using Context): Option[Tree[T]] = + def rec(tree: Tree[T]): Option[Tree[T]] = tree match + case Block(Nil, expr) => rec(expr) + case Splice(inner) => + // Optimization: `'{ $x }` --> `x` + // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]` + Some(inner) + case _ => None + rec(body) } /** A tree representing a splice `${ expr }` @@ -730,6 +741,16 @@ object Trees { case class Splice[+T <: Untyped] private[ast] (expr: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Splice[T] + + /** Cancel this Splice if it contains a Quote */ + def cancelled(using Context): Option[Tree[T]] = + def rec(tree: Tree[T]): Option[Tree[T]] = tree match + case Block(Nil, expr1) => rec(expr1) + case Quote(body) => + // Optimization: `${ 'x }` --> `x` + Some(body) + case _ => None + rec(expr) } /** A type tree that represents an existing or inferred type */ diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index d4ccf3321222..37654bc0673b 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -53,6 +53,14 @@ class CrossStageSafety extends TreeMapWithStages { else if !inQuoteOrSpliceScope then checkAnnotations(tree) tree match + case tree @ Quote(quotedTree) => + tree.cancelled match + case Some(tree1) => transform(tree1.asInstance(tree.tpe)) // TODO is this asInstance still needed? + case None => transformQuote(quotedTree, tree) + case tree @ Splice(splicedTree) => + tree.cancelled match + case Some(tree1) => transform(tree1) + case None => transformSplice(splicedTree, tree) case tree @ QuotedTypeOf(quotedTree) => transformQuotedType(quotedTree, tree) case _ => super.transform(tree) @@ -95,6 +103,14 @@ class CrossStageSafety extends TreeMapWithStages { case tree: TypeDef if tree.symbol.is(Case) && level > 0 => report.error(reporting.CaseClassInInlinedCode(tree), tree) super.transform(tree) + case tree @ Quote(quotedTree) => + tree.cancelled match + case Some(tree1) => transform(tree1.asInstance(tree.tpe)) // TODO is this asInstance still needed? + case None => transformQuote(quotedTree, tree) + case tree @ Splice(splicedTree) => + tree.cancelled match + case Some(tree1) => transform(tree1) + case None => transformSplice(splicedTree, tree) case tree @ QuotedTypeOf(quotedTree) => transformQuotedType(quotedTree, tree) case tree @ SplicedType(splicedTree) => @@ -104,7 +120,7 @@ class CrossStageSafety extends TreeMapWithStages { } /** Transform quoted trees while maintaining level correctness */ - override protected def transformQuote(body: Tree, quote: Quote)(using Context): Tree = { + private def transformQuote(body: Tree, quote: Quote)(using Context): Tree = { if (ctx.property(InAnnotation).isDefined) report.error("Cannot have a quote in an annotation", quote.srcPos) val transformedBody = transformQuoteBody(body, quote.span) @@ -150,7 +166,7 @@ class CrossStageSafety extends TreeMapWithStages { * - If inside inlined code, expand the macro code. * - If inside of a macro definition, check the validity of the macro. */ - protected def transformSplice(body: Tree, splice: Splice)(using Context): Tree = { + private def transformSplice(body: Tree, splice: Splice)(using Context): Tree = { val body1 = transform(body)(using spliceContext) val tpe1 = if level == 0 then splice.tpe diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index 67fb17551253..674dfff2f642 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -6,7 +6,6 @@ import dotty.tools.dotc.config.Printers.staging import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.Symbols._ -import dotty.tools.dotc.util.Property import dotty.tools.dotc.staging.StagingLevel.* import scala.collection.mutable @@ -15,45 +14,11 @@ import scala.collection.mutable abstract class TreeMapWithStages extends TreeMapWithImplicits { import tpd._ - /** Transform the quote `quote` which contains the quoted `body`. - * - * - `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T]()` - */ - protected def transformQuote(body: Tree, quote: Quote)(using Context): Tree = - cpy.Quote(quote)(body) - - /** Transform the expression splice `splice` which contains the spliced `body`. */ - protected def transformSplice(body: Tree, splice: Splice)(using Context): Tree - override def transform(tree: Tree)(using Context): Tree = if (tree.source != ctx.source && tree.source.exists) transform(tree)(using ctx.withSource(tree.source)) - else reporting.trace(i"StagingTransformer.transform $tree at $level", staging, show = true) { - def dropEmptyBlocks(tree: Tree): Tree = tree match { - case Block(Nil, expr) => dropEmptyBlocks(expr) - case _ => tree - } - + else reporting.trace(i"TreeMapWithStages.transform $tree at $level", staging, show = true) { tree match { - case tree @ Quote(quotedTree) => - dropEmptyBlocks(quotedTree) match { - case Splice(t) => - // Optimization: `'{ $x }` --> `x` - // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]` - transform(t).asInstance(tree.tpe) - case _ => - transformQuote(quotedTree, tree) - } - - case tree @ Splice(splicedTree) => - dropEmptyBlocks(splicedTree) match { - case Quote(t) => - // Optimization: `${ 'x }` --> `x` - transform(t) - case _ => - transformSplice(splicedTree, tree) - } - case Block(stats, _) => val defSyms = stats.collect { case defTree: DefTree => defTree.symbol } super.transform(tree)(using symbolsInCurrentLevel(defSyms)) From 966835b1b20f61c65fd60c6fc68dfbd20e2e0add Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 11:50:57 +0200 Subject: [PATCH 31/46] Deduplicate code that transforms `Quote`, `Splice`, `Type.of` --- .../tools/dotc/staging/CrossStageSafety.scala | 41 +++++++------------ 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 37654bc0673b..350f90216ba4 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -50,21 +50,20 @@ class CrossStageSafety extends TreeMapWithStages { override def transform(tree: Tree)(using Context): Tree = if (tree.source != ctx.source && tree.source.exists) transform(tree)(using ctx.withSource(tree.source)) - else if !inQuoteOrSpliceScope then - checkAnnotations(tree) - tree match - case tree @ Quote(quotedTree) => - tree.cancelled match - case Some(tree1) => transform(tree1.asInstance(tree.tpe)) // TODO is this asInstance still needed? - case None => transformQuote(quotedTree, tree) - case tree @ Splice(splicedTree) => - tree.cancelled match - case Some(tree1) => transform(tree1) - case None => transformSplice(splicedTree, tree) - case tree @ QuotedTypeOf(quotedTree) => - transformQuotedType(quotedTree, tree) - case _ => super.transform(tree) - else tree match { + else tree match + case tree @ Quote(quotedTree) => + tree.cancelled match + case Some(tree1) => transform(tree1.asInstance(tree.tpe)) // TODO is this asInstance still needed? + case None => transformQuote(quotedTree, tree) + case tree @ Splice(splicedTree) => + tree.cancelled match + case Some(tree1) => transform(tree1) + case None => transformSplice(splicedTree, tree) + case tree @ QuotedTypeOf(quotedTree) => + transformQuotedType(quotedTree, tree) + case _ if !inQuoteOrSpliceScope => + checkAnnotations(tree) + super.transform(tree) case _: TypeTree => val tp1 = transformTypeAnnotationSplices(tree.tpe) val healedType = healType(tree.srcPos)(tp1) @@ -103,21 +102,11 @@ class CrossStageSafety extends TreeMapWithStages { case tree: TypeDef if tree.symbol.is(Case) && level > 0 => report.error(reporting.CaseClassInInlinedCode(tree), tree) super.transform(tree) - case tree @ Quote(quotedTree) => - tree.cancelled match - case Some(tree1) => transform(tree1.asInstance(tree.tpe)) // TODO is this asInstance still needed? - case None => transformQuote(quotedTree, tree) - case tree @ Splice(splicedTree) => - tree.cancelled match - case Some(tree1) => transform(tree1) - case None => transformSplice(splicedTree, tree) - case tree @ QuotedTypeOf(quotedTree) => - transformQuotedType(quotedTree, tree) case tree @ SplicedType(splicedTree) => transformSpliceType(splicedTree, tree) case _ => super.transform(tree) - } + end transform /** Transform quoted trees while maintaining level correctness */ private def transformQuote(body: Tree, quote: Quote)(using Context): Tree = { From 95c39bcd8ec7ff07c009b5824d5f19ae2b5ca873 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 12:55:08 +0200 Subject: [PATCH 32/46] Cleanup --- compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala | 2 +- tests/run-staging/quote-nested-2.check | 2 +- tests/run-staging/quote-nested-5.check | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 350f90216ba4..a7a74a2a3465 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -53,7 +53,7 @@ class CrossStageSafety extends TreeMapWithStages { else tree match case tree @ Quote(quotedTree) => tree.cancelled match - case Some(tree1) => transform(tree1.asInstance(tree.tpe)) // TODO is this asInstance still needed? + case Some(tree1) => transform(tree1) case None => transformQuote(quotedTree, tree) case tree @ Splice(splicedTree) => tree.cancelled match diff --git a/tests/run-staging/quote-nested-2.check b/tests/run-staging/quote-nested-2.check index b0af638da1a8..7db9edb0424e 100644 --- a/tests/run-staging/quote-nested-2.check +++ b/tests/run-staging/quote-nested-2.check @@ -1,4 +1,4 @@ ((q: scala.quoted.Quotes) ?=> { val a: scala.quoted.Expr[scala.Int] = scala.quoted.runtime.Expr.quote[scala.Int](4).apply(using q) - ((evidence$2: scala.quoted.Quotes) ?=> a).asInstanceOf[scala.ContextFunction1[scala.quoted.Quotes, scala.quoted.Expr[scala.Int]]].apply(using q) + ((evidence$2: scala.quoted.Quotes) ?=> a).apply(using q) }) diff --git a/tests/run-staging/quote-nested-5.check b/tests/run-staging/quote-nested-5.check index d5fee708c460..53600d16a8da 100644 --- a/tests/run-staging/quote-nested-5.check +++ b/tests/run-staging/quote-nested-5.check @@ -1,4 +1,4 @@ ((q: scala.quoted.Quotes) ?=> { val a: scala.quoted.Expr[scala.Int] = scala.quoted.runtime.Expr.quote[scala.Int](4).apply(using q) - ((q2: scala.quoted.Quotes) ?=> ((evidence$2: scala.quoted.Quotes) ?=> a).asInstanceOf[scala.ContextFunction1[scala.quoted.Quotes, scala.quoted.Expr[scala.Int]]].apply(using q2)) + ((q2: scala.quoted.Quotes) ?=> ((evidence$2: scala.quoted.Quotes) ?=> a).apply(using q2)) }.apply(using q)) From 0ca606641d87ec249459f58434e4cdf55e0e79b2 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 13:05:25 +0200 Subject: [PATCH 33/46] Remove redundant case --- .../dotty/tools/dotc/staging/CrossStageSafety.scala | 11 ----------- compiler/src/dotty/tools/dotc/staging/HealType.scala | 2 +- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index a7a74a2a3465..5711fb96cda2 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -102,8 +102,6 @@ class CrossStageSafety extends TreeMapWithStages { case tree: TypeDef if tree.symbol.is(Case) && level > 0 => report.error(reporting.CaseClassInInlinedCode(tree), tree) super.transform(tree) - case tree @ SplicedType(splicedTree) => - transformSpliceType(splicedTree, tree) case _ => super.transform(tree) end transform @@ -163,15 +161,6 @@ class CrossStageSafety extends TreeMapWithStages { untpd.cpy.Splice(splice)(body1).withType(tpe1) } - private def transformSpliceType(body: Tree, splice: Select)(using Context): Tree = { - val body1 = transform(body)(using spliceContext) - if ctx.reporter.hasErrors then - splice - else - val tagRef = getQuoteTypeTags.getTagRef(splice.qualifier.tpe.asInstanceOf[TermRef]) - ref(tagRef).withSpan(splice.span) - } - def transformTypeAnnotationSplices(tp: Type)(using Context) = new TypeMap { def apply(tp: Type): Type = tp match case tp: AnnotatedType => diff --git a/compiler/src/dotty/tools/dotc/staging/HealType.scala b/compiler/src/dotty/tools/dotc/staging/HealType.scala index 337c7e8c9b3b..2facb64746b3 100644 --- a/compiler/src/dotty/tools/dotc/staging/HealType.scala +++ b/compiler/src/dotty/tools/dotc/staging/HealType.scala @@ -63,7 +63,7 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap { private def checkNotWildcardSplice(splice: TypeRef): Unit = splice.prefix.termSymbol.info.argInfos match - case (tb: TypeBounds) :: _ => report.error(em"Cannot splice $splice because it is a wildcard type", pos) + case (tb: TypeBounds) :: _ => report.error(em"Cannot stage $splice because it is an alias to a wildcard type", pos) case _ => /** Return the root of this path if it is a variable defined in a previous level. From e3735faabe2824e173cb3eca5b9bd067d50db047 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 13:19:26 +0200 Subject: [PATCH 34/46] Refactor --- .../tools/dotc/staging/CrossStageSafety.scala | 96 +++++++++---------- 1 file changed, 43 insertions(+), 53 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 5711fb96cda2..cceb60719289 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -51,19 +51,53 @@ class CrossStageSafety extends TreeMapWithStages { if (tree.source != ctx.source && tree.source.exists) transform(tree)(using ctx.withSource(tree.source)) else tree match - case tree @ Quote(quotedTree) => + case tree: Quote => tree.cancelled match - case Some(tree1) => transform(tree1) - case None => transformQuote(quotedTree, tree) - case tree @ Splice(splicedTree) => + case Some(tree1) => + transform(tree1) + case None => + if (ctx.property(InAnnotation).isDefined) + report.error("Cannot have a quote in an annotation", tree.srcPos) + val body1 = transformQuoteBody(tree.body, tree.span) + val stripAnnotationsDeep: TypeMap = new TypeMap: + def apply(tp: Type): Type = mapOver(tp.stripAnnots) + val bodyType1 = healType(tree.srcPos)(stripAnnotationsDeep(tree.bodyType)) + cpy.Quote(tree)(body1).withBodyType(bodyType1) + + case tree: Splice => tree.cancelled match - case Some(tree1) => transform(tree1) - case None => transformSplice(splicedTree, tree) - case tree @ QuotedTypeOf(quotedTree) => - transformQuotedType(quotedTree, tree) + case Some(tree1) => + transform(tree1) + case None => + val body1 = transform(tree.expr)(using spliceContext) + val tpe1 = + if level == 0 then tree.tpe + else healType(tree.srcPos)(tree.tpe.widenTermRefExpr) + untpd.cpy.Splice(tree)(body1).withType(tpe1) + + case tree @ QuotedTypeOf(body) => + if (ctx.property(InAnnotation).isDefined) + report.error("Cannot have a quote in an annotation", tree.srcPos) + body.tpe match + case DirectTypeOf(termRef) => + // Optimization: `quoted.Type.of[x.Underlying](quotes)` --> `x` + ref(termRef).withSpan(tree.span) + case _ => + transformQuoteBody(body, tree.span) match + case DirectTypeOf.Healed(termRef) => + // Optimization: `quoted.Type.of[@SplicedType type T = x.Underlying; T](quotes)` --> `x` + ref(termRef).withSpan(tree.span) + case transformedBody => + val quotes = transform(tree.args.head) + // `quoted.Type.of[](quotes)` --> `quoted.Type.of[](quotes)` + val TypeApply(fun, _) = tree.fun: @unchecked + if level != 0 then cpy.Apply(tree)(cpy.TypeApply(tree.fun)(fun, transformedBody :: Nil), quotes :: Nil) + else tpd.Quote(transformedBody).select(nme.apply).appliedTo(quotes).withSpan(tree.span) + case _ if !inQuoteOrSpliceScope => - checkAnnotations(tree) + checkAnnotations(tree) // Check quotes in annotations super.transform(tree) + case _: TypeTree => val tp1 = transformTypeAnnotationSplices(tree.tpe) val healedType = healType(tree.srcPos)(tp1) @@ -106,37 +140,6 @@ class CrossStageSafety extends TreeMapWithStages { super.transform(tree) end transform - /** Transform quoted trees while maintaining level correctness */ - private def transformQuote(body: Tree, quote: Quote)(using Context): Tree = { - if (ctx.property(InAnnotation).isDefined) - report.error("Cannot have a quote in an annotation", quote.srcPos) - val transformedBody = transformQuoteBody(body, quote.span) - val stripAnnotationsDeep: TypeMap = new TypeMap: - def apply(tp: Type): Type = mapOver(tp.stripAnnots) - val bodyType1 = healType(quote.srcPos)(stripAnnotationsDeep(quote.bodyType)) - cpy.Quote(quote)(transformedBody).withBodyType(bodyType1) - } - - private def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = { - if (ctx.property(InAnnotation).isDefined) - report.error("Cannot have a quote in an annotation", quote.srcPos) - body.tpe match - case DirectTypeOf(termRef) => - // Optimization: `quoted.Type.of[x.Underlying](quotes)` --> `x` - ref(termRef).withSpan(quote.span) - case _ => - transformQuoteBody(body, quote.span) match - case DirectTypeOf.Healed(termRef) => - // Optimization: `quoted.Type.of[@SplicedType type T = x.Underlying; T](quotes)` --> `x` - ref(termRef).withSpan(quote.span) - case transformedBody => - val quotes = transform(quote.args.head) - // `quoted.Type.of[](quotes)` --> `quoted.Type.of[](quotes)` - val TypeApply(fun, _) = quote.fun: @unchecked - if level != 0 then cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, transformedBody :: Nil), quotes :: Nil) - else tpd.Quote(transformedBody).select(nme.apply).appliedTo(quotes).withSpan(quote.span) - } - private def transformQuoteBody(body: Tree, span: Span)(using Context): Tree = { val taggedTypes = new QuoteTypeTags(span) val contextWithQuote = @@ -148,19 +151,6 @@ class CrossStageSafety extends TreeMapWithStages { case tags => tpd.Block(tags, transformedBody).withSpan(body.span) } - /** Transform splice - * - If inside a quote, transform the contents of the splice. - * - If inside inlined code, expand the macro code. - * - If inside of a macro definition, check the validity of the macro. - */ - private def transformSplice(body: Tree, splice: Splice)(using Context): Tree = { - val body1 = transform(body)(using spliceContext) - val tpe1 = - if level == 0 then splice.tpe - else healType(splice.srcPos)(splice.tpe.widenTermRefExpr) - untpd.cpy.Splice(splice)(body1).withType(tpe1) - } - def transformTypeAnnotationSplices(tp: Type)(using Context) = new TypeMap { def apply(tp: Type): Type = tp match case tp: AnnotatedType => From 6956c43356010d6d6a144713ee0737bd0baac7c9 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 13:27:00 +0200 Subject: [PATCH 35/46] Refactor quote cancellation logic --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 21 -------- .../tools/dotc/staging/CrossStageSafety.scala | 52 ++++++++++++------- 2 files changed, 32 insertions(+), 41 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index fbc68ebe63d7..19c189047c6d 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -714,17 +714,6 @@ object Trees { defn.FunctionType(1, isContextual = true) .appliedTo(defn.QuotesClass.typeRef, exprType) withType(quoteType) - - /** Cancel this Quote if it contains a Splice */ - def cancelled(using Context): Option[Tree[T]] = - def rec(tree: Tree[T]): Option[Tree[T]] = tree match - case Block(Nil, expr) => rec(expr) - case Splice(inner) => - // Optimization: `'{ $x }` --> `x` - // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]` - Some(inner) - case _ => None - rec(body) } /** A tree representing a splice `${ expr }` @@ -741,16 +730,6 @@ object Trees { case class Splice[+T <: Untyped] private[ast] (expr: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Splice[T] - - /** Cancel this Splice if it contains a Quote */ - def cancelled(using Context): Option[Tree[T]] = - def rec(tree: Tree[T]): Option[Tree[T]] = tree match - case Block(Nil, expr1) => rec(expr1) - case Quote(body) => - // Optimization: `${ 'x }` --> `x` - Some(body) - case _ => None - rec(expr) } /** A type tree that represents an existing or inferred type */ diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index cceb60719289..032067eca482 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -51,29 +51,25 @@ class CrossStageSafety extends TreeMapWithStages { if (tree.source != ctx.source && tree.source.exists) transform(tree)(using ctx.withSource(tree.source)) else tree match + case CancelledQuote(tree) => + transform(tree) // Optimization: `'{ $x }` --> `x` case tree: Quote => - tree.cancelled match - case Some(tree1) => - transform(tree1) - case None => - if (ctx.property(InAnnotation).isDefined) - report.error("Cannot have a quote in an annotation", tree.srcPos) - val body1 = transformQuoteBody(tree.body, tree.span) - val stripAnnotationsDeep: TypeMap = new TypeMap: - def apply(tp: Type): Type = mapOver(tp.stripAnnots) - val bodyType1 = healType(tree.srcPos)(stripAnnotationsDeep(tree.bodyType)) - cpy.Quote(tree)(body1).withBodyType(bodyType1) + if (ctx.property(InAnnotation).isDefined) + report.error("Cannot have a quote in an annotation", tree.srcPos) + val body1 = transformQuoteBody(tree.body, tree.span) + val stripAnnotationsDeep: TypeMap = new TypeMap: + def apply(tp: Type): Type = mapOver(tp.stripAnnots) + val bodyType1 = healType(tree.srcPos)(stripAnnotationsDeep(tree.bodyType)) + cpy.Quote(tree)(body1).withBodyType(bodyType1) + case CancelledSplice(tree) => + transform(tree) // Optimization: `${ 'x }` --> `x` case tree: Splice => - tree.cancelled match - case Some(tree1) => - transform(tree1) - case None => - val body1 = transform(tree.expr)(using spliceContext) - val tpe1 = - if level == 0 then tree.tpe - else healType(tree.srcPos)(tree.tpe.widenTermRefExpr) - untpd.cpy.Splice(tree)(body1).withType(tpe1) + val body1 = transform(tree.expr)(using spliceContext) + val tpe1 = + if level == 0 then tree.tpe + else healType(tree.srcPos)(tree.tpe.widenTermRefExpr) + untpd.cpy.Splice(tree)(body1).withType(tpe1) case tree @ QuotedTypeOf(body) => if (ctx.property(InAnnotation).isDefined) @@ -224,4 +220,20 @@ class CrossStageSafety extends TreeMapWithStages { | - but the access is at level $level.$hint""", pos) tp } + + private object CancelledQuote: + def unapply(tree: Quote): Option[Tree] = + def rec(tree: Tree): Option[Tree] = tree match + case Block(Nil, expr) => rec(expr) + case Splice(inner) => Some(inner) + case _ => None + rec(tree.body) + + private object CancelledSplice: + def unapply(tree: Splice): Option[Tree] = + def rec(tree: Tree): Option[Tree] = tree match + case Block(Nil, expr) => rec(expr) + case Quote(inner) => Some(inner) + case _ => None + rec(tree.expr) } From 859f6e6bff9d0b113909507ec4c7acb2b75aee42 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 14:18:47 +0200 Subject: [PATCH 36/46] Remove unnecessary quotation level change --- .../src/dotty/tools/dotc/inlines/PrepareInlineable.scala | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index ed8683bf210e..060c8d21f390 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -86,13 +86,7 @@ object PrepareInlineable { } override def transform(tree: Tree)(using Context): Tree = - inContext(stagingContext(tree)) { - postTransform(super.transform(preTransform(tree))) - } - - private def stagingContext(tree: Tree)(using Context): Context = tree match - case tree: Apply if tree.symbol eq defn.QuotedTypeModule_of => StagingLevel.quoteContext - case _ => ctx + postTransform(super.transform(preTransform(tree))) } /** Direct approach: place the accessor with the accessed symbol. This has the From e4ddc88ba974117e3c95466e40d3ba2414705967 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 14:21:55 +0200 Subject: [PATCH 37/46] Add missing change of staging level --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 19c189047c6d..77583a9a77e2 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -1705,9 +1705,9 @@ object Trees { case Thicket(ts) => this(x, ts) case Quote(body) => - this(x, body) + this(x, body)(using quoteContext) case Splice(expr) => - this(x, expr) + this(x, expr)(using spliceContext) case Hole(_, _, args, content, tpt) => this(this(this(x, args), content), tpt) case _ => From 7e8d2b106f5f852c13b2fc8d1ffc89e10b0a426d Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 14:42:14 +0200 Subject: [PATCH 38/46] Simplify macroDependencies using level from context --- .../dotty/tools/dotc/inlines/Inliner.scala | 38 ++++--------------- 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 9179e92527e6..ccef777380e1 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -23,7 +23,7 @@ import util.Spans.Span import dotty.tools.dotc.transform.Splicer import dotty.tools.dotc.transform.BetaReduce import quoted.QuoteUtils -import staging.StagingLevel +import staging.StagingLevel.{level, spliceContext} import scala.annotation.constructorOnly /** General support for inlining */ @@ -834,9 +834,7 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = super.typedSplice(tree, pt) match - case tree1 @ Splice(expr) - if StagingLevel.level == 0 - && !hasInliningErrors => + case tree1 @ Splice(expr) if level == 0 && !hasInliningErrors => val expanded = expandMacro(expr, tree1.srcPos) transform.TreeChecker.checkMacroGeneratedTree(tree1, expanded) typedExpr(expanded) // Inline calls and constant fold code generated by the macro @@ -1032,9 +1030,9 @@ class Inliner(val call: tpd.Tree)(using Context): } private def expandMacro(body: Tree, splicePos: SrcPos)(using Context) = { - assert(StagingLevel.level == 0) + assert(level == 0) val inlinedFrom = enclosingInlineds.last - val dependencies = macroDependencies(body) + val dependencies = macroDependencies(body)(using spliceContext) val suspendable = ctx.compilationUnit.isSuspendable if dependencies.nonEmpty && !ctx.reporter.errorsReported then for sym <- dependencies do @@ -1064,32 +1062,12 @@ class Inliner(val call: tpd.Tree)(using Context): */ private def macroDependencies(tree: Tree)(using Context) = new TreeAccumulator[List[Symbol]] { - private var level = -1 override def apply(syms: List[Symbol], tree: tpd.Tree)(using Context): List[Symbol] = - if level != -1 then foldOver(syms, tree) - else tree match { - case tree: RefTree if tree.isTerm && tree.symbol.isDefinedInCurrentRun && !tree.symbol.isLocal => + tree match { + case tree: RefTree if tree.isTerm && level == -1 && tree.symbol.isDefinedInCurrentRun && !tree.symbol.isLocal => foldOver(tree.symbol :: syms, tree) - case Quote(body) => - level += 1 - try apply(syms, body) - finally level -= 1 - case QuotedTypeOf(body) => - level += 1 - try apply(syms, body) - finally level -= 1 - case Splice(body) => - level -= 1 - try apply(syms, body) - finally level += 1 - case SplicedType(body) => - level -= 1 - try apply(syms, body) - finally level += 1 - case _: TypTree => - syms - case _ => - foldOver(syms, tree) + case _: TypTree => syms + case _ => foldOver(syms, tree) } }.apply(Nil, tree) end Inliner From a3c657ba8c86960caf866020443a743f6410b032 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 14:44:01 +0200 Subject: [PATCH 39/46] Fix typo --- compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 87620db36482..924f2e9ffa92 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -669,7 +669,7 @@ class TreePickler(pickler: TastyPickler) { // Add QUOTE tag to TASTy assert(body.isTerm, """Quote with type should not be pickled. - |Quote with type should only exists after staging phase at level staging level 0.""".stripMargin) + |Quote with type should only exists after staging phase at staging level 0.""".stripMargin) pickleTree( // scala.quoted.runtime.Expr.quoted[]() ref(defn.QuotedRuntime_exprQuote) From be3cd481c160336ebfaa9bc2b6e8ebd28a2e2142 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 14:58:08 +0200 Subject: [PATCH 40/46] Refactor use of Quote --- .../tools/dotc/transform/PickleQuotes.scala | 22 +++++++++++-------- .../tools/dotc/typer/QuotesAndSplices.scala | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index b1a707a70cb1..75af32af247e 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -104,9 +104,10 @@ class PickleQuotes extends MacroTransform { val (contents, codeWithHoles) = makeHoles(quote.body) val sourceRef = Inlines.inlineCallTrace(ctx.owner, tree.sourcePos) val bodyWithHoles2 = - if quote.body.isType then codeWithHoles + if quote.isTypeQuote then codeWithHoles else Inlined(sourceRef, Nil, codeWithHoles) - val pickled = PickleQuotes.pickle(quotes, bodyWithHoles2, contents, quote.bodyType) + val quote1 = cpy.Quote(quote)(body = bodyWithHoles2) + val pickled = PickleQuotes.pickle(quote1, quotes, contents) transform(pickled) // pickle quotes that are in the contents case tree: DefDef if !tree.rhs.isEmpty && tree.symbol.isInlineMethod => // Shrink size of the tree. The methods have already been inlined. @@ -202,7 +203,10 @@ object PickleQuotes { val name: String = "pickleQuotes" val description: String = "turn quoted trees into explicit run-time data structures" - def pickle(quotes: Tree, body: Tree, contents: List[Tree], originalTp: Type)(using Context) = { + def pickle(quote: Quote, quotes: Tree, contents: List[Tree])(using Context) = { + val body = quote.body + val bodyType = quote.bodyType + /** Helper methods to construct trees calling methods in `Quotes.reflect` based on the current `quotes` tree */ object reflect extends ReifiedReflect { val quotesTree = quotes @@ -256,7 +260,7 @@ object PickleQuotes { */ def liftedValue(lit: Literal, lifter: Symbol) = val exprType = defn.QuotedExprClass.typeRef.appliedTo(body.tpe) - ref(lifter).appliedToType(originalTp).select(nme.apply).appliedTo(lit).appliedTo(quotes) + ref(lifter).appliedToType(bodyType).select(nme.apply).appliedTo(lit).appliedTo(quotes) def pickleAsValue(lit: Literal) = { // TODO should all constants be pickled as Literals? @@ -334,18 +338,18 @@ object PickleQuotes { case _ => Match(args(0).annotated(New(ref(defn.UncheckedAnnot.typeRef))), cases) ) - val quoteClass = if body.isType then defn.QuotedTypeClass else defn.QuotedExprClass - val quotedType = quoteClass.typeRef.appliedTo(originalTp) + val quoteClass = if quote.isTypeQuote then defn.QuotedTypeClass else defn.QuotedExprClass + val quotedType = quoteClass.typeRef.appliedTo(bodyType) val lambdaTpe = MethodType(defn.QuotesClass.typeRef :: Nil, quotedType) val unpickleMeth = - if body.isType then defn.QuoteUnpickler_unpickleTypeV2 + if quote.isTypeQuote then defn.QuoteUnpickler_unpickleTypeV2 else defn.QuoteUnpickler_unpickleExprV2 val unpickleArgs = - if body.isType then List(pickledQuoteStrings, types) + if quote.isTypeQuote then List(pickledQuoteStrings, types) else List(pickledQuoteStrings, types, termHoles) quotes .asInstance(defn.QuoteUnpicklerClass.typeRef) - .select(unpickleMeth).appliedToType(originalTp) + .select(unpickleMeth).appliedToType(bodyType) .appliedToArgs(unpickleArgs).withSpan(body.span) } diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 5372dc4d04b6..3a5ea05726a5 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -50,7 +50,7 @@ trait QuotesAndSplices { if ctx.mode.is(Mode.Pattern) then typedQuotePattern(tree, pt, quotes).withSpan(tree.span) - else if tree.body.isType then + else if tree.isTypeQuote then val msg = em"""Quoted types `'[..]` can only be used in patterns. | |Hint: To get a scala.quoted.Type[T] use scala.quoted.Type.of[T] instead. From 7bd7c921eb480a21ad5533d2966ee8b7a5dd736b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 16:36:09 +0200 Subject: [PATCH 41/46] Do not traverse type trees to find inline method calls --- compiler/src/dotty/tools/dotc/transform/Inlining.scala | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index f5b72759b57e..10f73fa94e08 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -45,8 +45,6 @@ class Inlining extends MacroTransform { new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match - case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of => - traverseChildren(tree)(using StagingLevel.quoteContext) case tree: RefTree if !Inlines.inInlineMethod && StagingLevel.level == 0 => assert(!tree.symbol.isInlineMethod, tree.show) case _ => @@ -95,8 +93,6 @@ class Inlining extends MacroTransform { val tree1 = super.transform(tree) if tree1.tpe.isError then tree1 else Inlines.inlineCall(tree1) - case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of => - super.transform(tree)(using StagingLevel.quoteContext) case _: PackageDef => super.transform(tree) match case tree1: PackageDef => @@ -108,7 +104,8 @@ class Inlining extends MacroTransform { case _ => tree1 case tree1 => tree1 case _ => - super.transform(tree) + if tree.isType then tree + else super.transform(tree) } } } From b991b4066b481d0515441731ea6c1e87b55a77f3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 16:38:12 +0200 Subject: [PATCH 42/46] Remove post-condition that is already checked by Staging --- compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 75af32af247e..190d0c983a1c 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -87,8 +87,6 @@ class PickleQuotes extends MacroTransform { assert(Inlines.inInlineMethod) case tree: Splice => assert(Inlines.inInlineMethod) - case tree: RefTree if !Inlines.inInlineMethod => - assert(tree.symbol != defn.QuotedTypeModule_of) case _ : TypeDef if !Inlines.inInlineMethod => assert(!tree.symbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot), s"${tree.symbol} should have been removed by PickledQuotes because it has a @quoteTypeTag") From e611139bf865ce11fd7aa35868a5bf930ca06a01 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 16:52:46 +0200 Subject: [PATCH 43/46] Refactor quote pickling case --- .../tools/dotc/transform/PickleQuotes.scala | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 190d0c983a1c..0c3a94e7d9cd 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -99,12 +99,7 @@ class PickleQuotes extends MacroTransform { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match case Apply(Select(quote: Quote, nme.apply), List(quotes)) => - val (contents, codeWithHoles) = makeHoles(quote.body) - val sourceRef = Inlines.inlineCallTrace(ctx.owner, tree.sourcePos) - val bodyWithHoles2 = - if quote.isTypeQuote then codeWithHoles - else Inlined(sourceRef, Nil, codeWithHoles) - val quote1 = cpy.Quote(quote)(body = bodyWithHoles2) + val (contents, quote1) = makeHoles(quote) val pickled = PickleQuotes.pickle(quote1, quotes, contents) transform(pickled) // pickle quotes that are in the contents case tree: DefDef if !tree.rhs.isEmpty && tree.symbol.isInlineMethod => @@ -115,8 +110,7 @@ class PickleQuotes extends MacroTransform { super.transform(tree) } - private def makeHoles(tree: tpd.Tree)(using Context): (List[Tree], tpd.Tree) = - + private def makeHoles(quote: tpd.Quote)(using Context): (List[Tree], tpd.Quote) = class HoleContentExtractor extends Transformer: private val contents = List.newBuilder[Tree] override def transform(tree: tpd.Tree)(using Context): tpd.Tree = @@ -187,10 +181,13 @@ class PickleQuotes extends MacroTransform { end HoleContentExtractor val holeMaker = new HoleContentExtractor - val newTree = holeMaker.transform(tree) - (holeMaker.getContents(), newTree) - + val body1 = holeMaker.transform(quote.body) + val body2 = + if quote.isTypeQuote then body1 + else Inlined(Inlines.inlineCallTrace(ctx.owner, quote.sourcePos), Nil, body1) + val quote1 = cpy.Quote(quote)(body2) + (holeMaker.getContents(), quote1) end makeHoles } From 37200bef72c019ea9e23e169ea8a06664f777d56 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 17:21:51 +0200 Subject: [PATCH 44/46] Cleanup --- compiler/src/dotty/tools/dotc/transform/Splicing.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index c833a0800e87..5c333c8c22ea 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -228,7 +228,7 @@ class Splicing extends MacroTransform: else super.transform(tree) case CapturedApplication(fn, argss) => transformCapturedApplication(tree, fn, argss) - case Apply(sel @ Select(app @ Quote(body), nme.apply), quotes :: Nil) if level == 0 && body.isTerm => + case Apply(Select(Quote(body), nme.apply), quotes :: Nil) if level == 0 && body.isTerm => body match case _: RefTree if isCaptured(body.symbol) => capturedTerm(body) case _ => withCurrentQuote(quotes) { super.transform(tree) } From e75cafe8b297157ea3f36fe9ce3576999b1f20e9 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 17:26:13 +0200 Subject: [PATCH 45/46] Remove unnecessary widening --- compiler/src/dotty/tools/dotc/transform/Splicing.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 5c333c8c22ea..4b288349fc33 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -391,9 +391,7 @@ class Splicing extends MacroTransform: Splice(closure, tpe) private def quoted(expr: Tree)(using Context): Tree = - untpd.Quote(expr).withBodyType(expr.tpe.widenTermRefExpr) // TODO do we need widenTermRefExpr? - .select(nme.apply) - .appliedTo(quotes.nn) + tpd.Quote(expr).select(nme.apply).appliedTo(quotes.nn) /** Helper methods to construct trees calling methods in `Quotes.reflect` based on the current `quotes` tree */ private object reflect extends ReifiedReflect { From 9214daa630cd7450ee47a85b964beef1e893fb01 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 21:04:46 +0200 Subject: [PATCH 46/46] Apply suggestions from code review Co-authored-by: Guillaume Martres --- compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 924f2e9ffa92..5662b17b6697 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -666,7 +666,7 @@ class TreePickler(pickler: TastyPickler) { pickleTree(alias) } case tree @ Quote(body) => - // Add QUOTE tag to TASTy + // TODO: Add QUOTE tag to TASTy assert(body.isTerm, """Quote with type should not be pickled. |Quote with type should only exists after staging phase at staging level 0.""".stripMargin) @@ -678,7 +678,7 @@ class TreePickler(pickler: TastyPickler) { .withSpan(tree.span) ) case Splice(expr) => - pickleTree( // Add SPLICE tag to TASTy + pickleTree( // TODO: Add SPLICE tag to TASTy // scala.quoted.runtime.Expr.splice[]() ref(defn.QuotedRuntime_exprSplice) .appliedToType(tree.tpe)