diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala
index 046b649941b1..8e4989f13c3d 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.Quote(_) =>
+ 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/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/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
index c2147b6af2d3..5b409b6fec9a 100644
--- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
+++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
@@ -1026,33 +1026,19 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
case t => assert(t.span.exists, i"$t")
}
- /** Extractors for quotes */
- object Quoted {
+ 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.QuotedRuntime_exprQuote then
- // quoted.runtime.Expr.quote[T](
)
- Some(tree.args.head)
- else if tree.symbol == defn.QuotedTypeModule_of then
+ if tree.symbol == defn.QuotedTypeModule_of then
// quoted.Type.of[](quotes)
val TypeApply(_, body :: _) = tree.fun: @unchecked
Some(body)
else None
}
- /** Extractors for splices */
- object Spliced {
- /** 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 1c1e80922c05..77583a9a77e2 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 {
@@ -677,6 +678,60 @@ object Trees {
override def isType = expansion.isType
}
+ /** 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.
+ *
+ * `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.
+ *
+ * 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 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 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)
+ }
+
+ /** A tree representing a splice `${ expr }`
+ *
+ * `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.
+ *
+ * @param expr The tree that was spliced
+ */
+ case class Splice[+T <: Untyped] private[ast] (expr: Tree[T])(implicit @constructorOnly src: SourceFile)
+ extends TermTree[T] {
+ type ThisTree[+T <: Untyped] = Splice[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 +1142,8 @@ object Trees {
type SeqLiteral = Trees.SeqLiteral[T]
type JavaSeqLiteral = Trees.JavaSeqLiteral[T]
type Inlined = Trees.Inlined[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]
@@ -1257,6 +1314,14 @@ 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)(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
+ 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
case _ => finalize(tree, untpd.SingletonTypeTree(ref)(sourceFile(tree)))
@@ -1494,6 +1559,10 @@ object Trees {
case Thicket(trees) =>
val trees1 = transform(trees)
if (trees1 eq trees) tree else Thicket(trees1)
+ case tree @ Quote(body) =>
+ cpy.Quote(tree)(transform(body)(using quoteContext))
+ case tree @ Splice(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 _ =>
@@ -1635,6 +1704,10 @@ object Trees {
this(this(x, arg), annot)
case Thicket(ts) =>
this(x, ts)
+ case Quote(body) =>
+ this(x, body)(using quoteContext)
+ case Splice(expr) =>
+ this(x, expr)(using spliceContext)
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..3a957c8f4612 100644
--- a/compiler/src/dotty/tools/dotc/ast/tpd.scala
+++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala
@@ -170,6 +170,12 @@ 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)(using Context): Quote =
+ untpd.Quote(body).withBodyType(body.tpe)
+
+ 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 a262c3658399..30f58fba44ec 100644
--- a/compiler/src/dotty/tools/dotc/ast/untpd.scala
+++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala
@@ -111,10 +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
- }
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
@@ -401,6 +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 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()
def SingletonTypeTree(ref: Tree)(implicit src: SourceFile): SingletonTypeTree = new SingletonTypeTree(ref)
@@ -622,14 +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 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))
- }
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))
@@ -711,10 +701,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) =>
cpy.ForYield(tree)(transform(enums), transform(expr))
case ForDo(enums, body) =>
@@ -772,10 +758,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) =>
this(this(x, enums), expr)
case ForDo(enums, body) =>
diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
index 8a396921f32b..5662b17b6697 100644
--- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
+++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
@@ -665,6 +665,26 @@ class TreePickler(pickler: TastyPickler) {
pickleTree(hi)
pickleTree(alias)
}
+ case tree @ Quote(body) =>
+ // 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)
+ pickleTree(
+ // scala.quoted.runtime.Expr.quoted[]()
+ ref(defn.QuotedRuntime_exprQuote)
+ .appliedToType(tree.bodyType)
+ .appliedTo(body)
+ .withSpan(tree.span)
+ )
+ case Splice(expr) =>
+ pickleTree( // TODO: Add SPLICE tag to TASTy
+ // scala.quoted.runtime.Expr.splice[]()
+ ref(defn.QuotedRuntime_exprSplice)
+ .appliedToType(tree.tpe)
+ .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..10395a488640 100644
--- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
+++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
@@ -1267,6 +1267,21 @@ class TreeUnpickler(reader: TastyReader,
res.withAttachment(SuppressedApplyToNone, ())
else res
+ def quotedExpr(fn: Tree, args: List[Tree]): Tree =
+ val TypeApply(_, targs) = fn: @unchecked
+ untpd.Quote(args.head).withBodyType(targs.head.tpe)
+
+ def splicedExpr(fn: Tree, args: List[Tree]): Tree =
+ val TypeApply(_, targs) = fn: @unchecked
+ 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.tpe)
+ case _ => // nestedSplice[T](quotes)
+ tpd.Apply(fn, args)
+
def simplifyLub(tree: Tree): Tree =
tree.overwriteType(tree.tpe.simplified)
tree
@@ -1283,6 +1298,9 @@ 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 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 f4612e49ccac..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 */
@@ -815,30 +815,31 @@ 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 Quoted(Spliced(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
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 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 typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree =
+ super.typedSplice(tree, pt) match
+ 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
+ case tree1 => tree1
+
override def typedMatch(tree: untpd.Match, pt: Type)(using Context): Tree =
val tree1 =
if tree.isInline then
@@ -1029,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
@@ -1061,28 +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 Quoted(body) =>
- level += 1
- try apply(syms, body)
- finally level -= 1
- case Spliced(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
diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala
index 33bc5a7ef10e..060c8d21f390 100644
--- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala
+++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala
@@ -86,14 +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.isQuote => StagingLevel.quoteContext
- case tree: Apply if tree.symbol.isExprSplice => StagingLevel.spliceContext
- case _ => ctx
+ postTransform(super.transform(preTransform(tree)))
}
/** Direct approach: place the accessor with the accessed symbol. This has the
@@ -154,7 +147,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)
@@ -290,7 +283,7 @@ object PrepareInlineable {
if (inlined.is(Macro) && !ctx.isAfterTyper) {
def checkMacro(tree: Tree): Unit = tree match {
- case Spliced(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 15a639743c15..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)) {
- Quote {
+ val body =
if (in.token == LBRACKET) inBrackets(typ())
else stagedBlock()
- }
+ 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 014e5ddf0d66..93de33778750 100644
--- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
+++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
@@ -432,10 +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
toTextLocal(fun)
~ "("
@@ -716,14 +712,18 @@ 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) =>
"Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}"
case MacroTree(call) =>
keywordStr("macro ") ~ toTextGlobal(call)
+ case tree @ Quote(body) =>
+ 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("}")
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 400a846194e7..032067eca482 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
@@ -51,10 +50,50 @@ 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
- checkAnnotations(tree)
- super.transform(tree)
- else tree match {
+ else tree match
+ case CancelledQuote(tree) =>
+ transform(tree) // Optimization: `'{ $x }` --> `x`
+ case tree: Quote =>
+ 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 =>
+ 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) // Check quotes in annotations
+ super.transform(tree)
+
case _: TypeTree =>
val tp1 = transformTypeAnnotationSplices(tree.tpe)
val healedType = healType(tree.srcPos)(tp1)
@@ -95,79 +134,17 @@ class CrossStageSafety extends TreeMapWithStages {
super.transform(tree)
case _ =>
super.transform(tree)
- }
-
- /** Transform quoted trees while maintaining level correctness */
- override protected def transformQuotation(body: Tree, quote: Apply)(using Context): Tree = {
- val taggedTypes = new QuoteTypeTags(quote.span)
-
- 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)
-
- 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)
-
- }
-
- /** 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.
- */
- protected def transformSplice(body: Tree, splice: Apply)(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 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)
- }
- }
-
- protected 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)
+ end transform
+
+ 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
+ val transformedBody = transform(body)(using contextWithQuote)
+ taggedTypes.getTypeTags match
+ case Nil => transformedBody
+ case tags => tpd.Block(tags, transformedBody).withSpan(body.span)
}
def transformTypeAnnotationSplices(tp: Type)(using Context) = new TypeMap {
@@ -243,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)
}
diff --git a/compiler/src/dotty/tools/dotc/staging/HealType.scala b/compiler/src/dotty/tools/dotc/staging/HealType.scala
index 50c2584b2fd0..2facb64746b3 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._
@@ -64,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.
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/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 adaacafa764a..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,77 +14,11 @@ 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]()`
- * - `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)
-
- /** Transform the expression splice `splice` which contains the spliced `body`. */
- protected def transformSplice(body: Tree, splice: Apply)(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))
- 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 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 tree @ Quoted(quotedTree) =>
- val old = inQuoteOrSplice
- inQuoteOrSplice = true
- try dropEmptyBlocks(quotedTree) match {
- case Spliced(t) =>
- // Optimization: `'{ $x }` --> `x`
- // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]`
- transform(t).asInstance(tree.tpe)
- case _ => transformQuotation(quotedTree, tree)
- }
- finally inQuoteOrSplice = old
-
- case tree @ Spliced(splicedTree) =>
- val old = inQuoteOrSplice
- inQuoteOrSplice = true
- try dropEmptyBlocks(splicedTree) match {
- case Quoted(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
-
case Block(stats, _) =>
val defSyms = stats.collect { case defTree: DefTree => defTree.symbol }
super.transform(tree)(using symbolsInCurrentLevel(defSyms))
diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala
index d6b7f3141b96..10f73fa94e08 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
@@ -46,10 +45,6 @@ class Inlining extends MacroTransform {
new TreeTraverser {
def traverse(tree: Tree)(using Context): Unit =
tree match
- case _: GenericApply if tree.symbol.isQuote =>
- traverseChildren(tree)(using StagingLevel.quoteContext)
- case _: GenericApply if tree.symbol.isExprSplice =>
- traverseChildren(tree)(using StagingLevel.spliceContext)
case tree: RefTree if !Inlines.inInlineMethod && StagingLevel.level == 0 =>
assert(!tree.symbol.isInlineMethod, tree.show)
case _ =>
@@ -98,10 +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.isQuote =>
- super.transform(tree)(using StagingLevel.quoteContext)
- case _: GenericApply if tree.symbol.isExprSplice =>
- super.transform(tree)(using StagingLevel.spliceContext)
case _: PackageDef =>
super.transform(tree) match
case tree1: PackageDef =>
@@ -113,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)
}
}
}
diff --git a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala
index d4dd911241d3..7ad109b67751 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,16 @@ 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 body = transformTree(tree.body, start)(using quoteContext)
+ goQuote(cpy.Quote(tree)(body), start)
+ }
+ case tree: Splice =>
+ inContext(prepSplice(tree, start)(using outerCtx)) {
+ val expr = transformTree(tree.expr, start)(using spliceContext)
+ goSplice(cpy.Splice(tree)(expr), start)
+ }
case tree: Return =>
inContext(prepReturn(tree, start)(using outerCtx)) {
val expr = transformTree(tree.expr, start)
@@ -546,6 +561,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 +912,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
diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala
index 62174c806f09..0c3a94e7d9cd 100644
--- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala
+++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala
@@ -83,9 +83,10 @@ class PickleQuotes extends MacroTransform {
override def checkPostCondition(tree: Tree)(using Context): Unit =
tree match
- case tree: RefTree if !Inlines.inInlineMethod =>
- assert(!tree.symbol.isQuote)
- assert(!tree.symbol.isExprSplice)
+ case tree: Quote =>
+ assert(Inlines.inInlineMethod)
+ case tree: Splice =>
+ assert(Inlines.inInlineMethod)
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")
@@ -97,21 +98,10 @@ 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)
- val sourceRef = Inlines.inlineCallTrace(ctx.owner, tree.sourcePos)
- val codeWithHoles2 = Inlined(sourceRef, Nil, codeWithHoles)
- val pickled = PickleQuotes(quotes, codeWithHoles2, contents, tpt.tpe, false)
+ case Apply(Select(quote: Quote, nme.apply), List(quotes)) =>
+ val (contents, quote1) = makeHoles(quote)
+ val pickled = PickleQuotes.pickle(quote1, quotes, contents)
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
@@ -120,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 =
@@ -192,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
}
@@ -206,7 +198,10 @@ 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(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
@@ -260,7 +255,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?
@@ -338,18 +333,18 @@ object PickleQuotes {
case _ => Match(args(0).annotated(New(ref(defn.UncheckedAnnot.typeRef))), cases)
)
- val quoteClass = if 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 isType then defn.QuoteUnpickler_unpickleTypeV2
+ if quote.isTypeQuote then defn.QuoteUnpickler_unpickleTypeV2
else defn.QuoteUnpickler_unpickleExprV2
val unpickleArgs =
- if 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)
}
@@ -376,7 +371,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/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala
index 7f3e47c14732..d74392f201ba 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 _: Quote =>
+ ctx.compilationUnit.needsStaging = true
+ super.transform(tree)
case tree =>
super.transform(tree)
}
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/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala
index bc4119ad0cff..8f45fd94d205 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 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 Quoted(_) => // ok
+ case Quote(_) => // ok
case _ =>
type Env = Set[Symbol]
@@ -155,15 +155,15 @@ 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(Quote(body), nme.apply), _) =>
val noSpliceChecker = new TreeTraverser {
def traverse(tree: Tree)(using Context): Unit = tree match
- case Spliced(_) =>
+ case Splice(_) =>
report.error("Quoted argument of macros may not have splices", tree.srcPos)
case _ =>
traverseChildren(tree)
}
- noSpliceChecker.traverse(quoted)
+ noSpliceChecker.traverse(body)
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(Quote(quoted), 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(Quote(body), nme.apply), _) =>
+ val body1 = body 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, _, body1) => body1
+ case _ => body
}
- new ExprImpl(Inlined(EmptyTree, Nil, QuoteUtils.changeOwnerOfTree(quoted1, ctx.owner)).withSpan(quoted1.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 e959f5d1451d..4b288349fc33 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
@@ -87,10 +86,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 =>
- QuoteTransformer().transform(tree)
- case TypeApply(_, _) if tree.symbol == defn.QuotedTypeModule_of =>
+ case Apply(Select(_: Quote, nme.apply), _) =>
QuoteTransformer().transform(tree)
case tree: DefDef if tree.symbol.is(Inline) =>
// Quotes in inlined methods are only pickled after they are inlined.
@@ -113,17 +109,13 @@ 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 =>
- if level > 1 then
- val splicedCode1 = super.transform(splicedCode)(using spliceContext)
- cpy.Apply(tree)(fn, List(splicedCode1))
- else
- val holeIdx = numHoles
- numHoles += 1
- val splicer = SpliceTransformer(ctx.owner, quotedDefs.contains)
- val newSplicedCode1 = splicer.transformSplice(splicedCode, 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
@@ -136,9 +128,6 @@ 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 =>
- super.transform(tree)(using quoteContext)
case _: Template =>
for sym <- tree.symbol.owner.info.decls do
quotedDefs += sym
@@ -237,32 +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(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 _ =>
- val newArgs = withCurrentQuote(quotesArgs.head) {
- if level > 1 then args.mapConserve(arg => transform(arg)(using quoteContext))
- else args.mapConserve(arg => transformLevel0QuoteContent(arg)(using quoteContext))
- }
- cpy.Apply(tree)(cpy.Select(sel)(cpy.Apply(app)(fn, newArgs), nme.apply), quotesArgs)
- 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 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) }
+ 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)
@@ -413,18 +388,10 @@ 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)
+ Splice(closure, tpe)
private def quoted(expr: Tree)(using Context): Tree =
- val tpe = expr.tpe.widenTermRefExpr
- ref(defn.QuotedRuntime_exprQuote)
- .appliedToType(tpe)
- .appliedTo(expr)
- .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 {
diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala
index 4fb5f5910440..581fd2753504 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
@@ -58,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/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala
index b945f5820523..feb841ba5c6c 100644
--- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala
+++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala
@@ -315,14 +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
-
/** Is symbol a type splice operation? */
def isTypeSplice(using Context): Boolean =
self == defn.QuotedType_splice
diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala
index 3e05310d7249..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.Quoted(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 8473bd168bc5..3a5ea05726a5 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._
@@ -37,21 +36,21 @@ trait QuotesAndSplices {
*/
def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = {
record("typedQuote")
- tree.quoted 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 _ =>
}
- 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)
- else if tree.quoted.isType then
+ typedQuotePattern(tree, pt, quotes).withSpan(tree.span)
+ 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.
@@ -59,8 +58,11 @@ trait QuotesAndSplices {
report.error(msg, tree.srcPos)
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))
+ // 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) => untpd.Quote(quotedExpr).withBodyType(tpt.tpe)
+ makeInlineable(quotedExpr.select(nme.apply).appliedTo(quotes).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))
@@ -85,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)
+ 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)
@@ -102,17 +105,20 @@ trait QuotesAndSplices {
markAsMacro(ctx)
}
- val (outerQctx, ctx1) = popQuotes()
-
+ // TODO typecheck directly (without `exprSplice`)
val internalSplice =
- 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)
+ 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)
+ case tree => tree
}
}
+ 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`
@@ -123,10 +129,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 = typedSplice(splice, defn.FunctionOf(argTypes, pt))
@@ -164,10 +171,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)
@@ -225,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(Apply(fn, pat :: Nil), tpt) if fn.symbol.isExprSplice && !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))
- transform(newSplice)
- case Apply(TypeApply(fn, targs), Apply(sp, pat :: Nil) :: 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
@@ -242,7 +243,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 Splice(pat) =>
try ref(defn.QuotedRuntimePatterns_patternHole.termRef).appliedToType(tree.tpe).withSpan(tree.span)
finally {
val patType = pat.tpe.widen
@@ -381,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.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)
- 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
@@ -439,13 +440,13 @@ 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) ref(defn.QuotedRuntime_exprQuote.termRef).appliedToType(defn.AnyType).appliedTo(shape).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 tree.quoted.isTerm then defn.QuoteMatching_ExprMatch else defn.QuoteMatching_TypeMatch
- val unapplyFun = qctx.asInstance(defn.QuoteMatchingClass.typeRef).select(matchModule).select(nme.unapply)
+ val matchModule = if quoted.isTerm then defn.QuoteMatching_ExprMatch else defn.QuoteMatching_TypeMatch
+ 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 c64f541fd811..f8cfd4ca3ae0 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,21 @@ 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 =
+ assertTyped(tree)
+ 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 =
+ 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, 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
override def retrieveSym(tree: untpd.Tree)(using Context): Symbol = tree.symbol
diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala
index 1949304ca287..245bcdac70af 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)
+ .appliedToType(self.bodyType)
+ .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)
+ .appliedToType(self.tpe)
+ .withSpan(self.span)
+
+ def args: List[Term] = self match
+ case self: tpd.Apply => self.args
+ 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
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)
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/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"}
+ }
+ }
+}
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
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-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 f29acb3b347a..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$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).apply(using q2))
+}.apply(using q))