diff --git a/compiler/src/dotty/tools/dotc/core/StagingContext.scala b/compiler/src/dotty/tools/dotc/core/StagingContext.scala
deleted file mode 100644
index 41e77655d5d6..000000000000
--- a/compiler/src/dotty/tools/dotc/core/StagingContext.scala
+++ /dev/null
@@ -1,58 +0,0 @@
-package dotty.tools.dotc.core
-
-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.transform.PCPCheckAndHeal
-
-object StagingContext {
-
- /** A key to be used in a context property that tracks the quotation level */
- private val QuotationLevel = new Property.Key[Int]
-
- /** 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]]
-
- private val TaggedTypes = new Property.Key[PCPCheckAndHeal.QuoteTypeTags]
-
- /** All enclosing calls that are currently inlined, from innermost to outermost. */
- def level(using Context): Int =
- ctx.property(QuotationLevel).getOrElse(0)
-
- /** Context with an incremented quotation level. */
- def quoteContext(using Context): Context =
- ctx.fresh.setProperty(QuotationLevel, level + 1)
-
- /** Context with an incremented quotation level and pushes a reference to a Quotes on the quote context stack */
- def pushQuotes(qctxRef: tpd.Tree)(using Context): Context =
- val old = ctx.property(QuotesStack).getOrElse(List.empty)
- ctx.fresh.setProperty(QuotationLevel, level + 1)
- .setProperty(QuotesStack, qctxRef :: old)
-
- /** Context with a decremented quotation level. */
- def spliceContext(using Context): Context =
- ctx.fresh.setProperty(QuotationLevel, level - 1)
-
- def contextWithQuoteTypeTags(taggedTypes: PCPCheckAndHeal.QuoteTypeTags)(using Context) =
- ctx.fresh.setProperty(TaggedTypes, taggedTypes)
-
- def getQuoteTypeTags(using Context): PCPCheckAndHeal.QuoteTypeTags =
- ctx.property(TaggedTypes).get
-
- /** 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 = ctx.fresh.setProperty(QuotationLevel, level - 1)
- 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/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala
index f2a0d5f03e38..872dc7793ff4 100644
--- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala
+++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala
@@ -23,6 +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 scala.annotation.constructorOnly
/** General support for inlining */
@@ -814,7 +815,7 @@ class Inliner(val call: tpd.Tree)(using Context):
val locked = ctx.typerState.ownedVars
val res = cancelQuotes(constToLiteral(BetaReduce(super.typedApply(tree, pt)))) match {
case res: Apply if res.symbol == defn.QuotedRuntime_exprSplice
- && StagingContext.level == 0
+ && StagingLevel.level == 0
&& !hasInliningErrors =>
val expanded = expandMacro(res.args.head, tree.srcPos)
transform.TreeChecker.checkMacroGeneratedTree(res, expanded)
@@ -1026,7 +1027,7 @@ class Inliner(val call: tpd.Tree)(using Context):
}
private def expandMacro(body: Tree, splicePos: SrcPos)(using Context) = {
- assert(StagingContext.level == 0)
+ assert(StagingLevel.level == 0)
val inlinedFrom = enclosingInlineds.last
val dependencies = macroDependencies(body)
val suspendable = ctx.compilationUnit.isSuspendable
diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala
index 8110fd2de195..f504a4034631 100644
--- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala
+++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala
@@ -14,6 +14,7 @@ import ErrorReporting.errorTree
import dotty.tools.dotc.util.{SourceFile, SourcePosition, SrcPos}
import parsing.Parsers.Parser
import transform.{PostTyper, Inlining, CrossVersionChecks}
+import staging.StagingLevel
import collection.mutable
import reporting.trace
@@ -56,7 +57,7 @@ object Inlines:
case _ =>
isInlineable(tree.symbol)
&& !tree.tpe.widenTermRefExpr.isInstanceOf[MethodOrPoly]
- && StagingContext.level == 0
+ && StagingLevel.level == 0
&& (
ctx.phase == Phases.inliningPhase
|| (ctx.phase == Phases.typerPhase && needsTransparentInlining(tree))
diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala
index 85293d4a82d7..eb1a97ab93c0 100644
--- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala
+++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala
@@ -17,11 +17,12 @@ import NameKinds.{InlineAccessorName, UniqueInlineName}
import inlines.Inlines
import NameOps._
import Annotations._
-import transform.{AccessProxies, PCPCheckAndHeal, Splicer}
+import transform.{AccessProxies, Splicer}
+import staging.PCPCheckAndHeal
import transform.SymUtils.*
import config.Printers.inlining
import util.Property
-import dotty.tools.dotc.transform.TreeMapWithStages._
+import staging.StagingLevel
object PrepareInlineable {
import tpd._
@@ -73,7 +74,7 @@ object PrepareInlineable {
!sym.isContainedIn(inlineSym) &&
!(sym.isStableMember && sym.info.widenTermRefExpr.isInstanceOf[ConstantType]) &&
!sym.isInlineMethod &&
- (Inlines.inInlineMethod || StagingContext.level > 0)
+ (Inlines.inInlineMethod || StagingLevel.level > 0)
def preTransform(tree: Tree)(using Context): Tree
@@ -90,8 +91,8 @@ object PrepareInlineable {
}
private def stagingContext(tree: Tree)(using Context): Context = tree match
- case tree: Apply if tree.symbol.isQuote => StagingContext.quoteContext
- case tree: Apply if tree.symbol.isExprSplice => StagingContext.spliceContext
+ case tree: Apply if tree.symbol.isQuote => StagingLevel.quoteContext
+ case tree: Apply if tree.symbol.isExprSplice => StagingLevel.spliceContext
case _ => ctx
}
@@ -293,7 +294,7 @@ object PrepareInlineable {
if (code.symbol.flags.is(Inline))
report.error("Macro cannot be implemented with an `inline` method", code.srcPos)
Splicer.checkValidMacroBody(code)
- new PCPCheckAndHeal(freshStagingContext).transform(body) // Ignore output, only check PCP
+ (new PCPCheckAndHeal).transform(body) // Ignore output, only check PCP
case Block(List(stat), Literal(Constants.Constant(()))) => checkMacro(stat)
case Block(Nil, expr) => checkMacro(expr)
case Typed(expr, _) => checkMacro(expr)
diff --git a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala
index aadfedd2417c..a98284f4078d 100644
--- a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala
+++ b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala
@@ -19,13 +19,12 @@ import dotty.tools.dotc.core.Denotations.staticRef
import dotty.tools.dotc.core.Flags._
import dotty.tools.dotc.core.NameKinds.FlatName
import dotty.tools.dotc.core.Names._
-import dotty.tools.dotc.core.StagingContext._
import dotty.tools.dotc.core.StdNames._
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.transform.TreeMapWithStages._
+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/HealType.scala b/compiler/src/dotty/tools/dotc/staging/HealType.scala
new file mode 100644
index 000000000000..44843dfa91ca
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/staging/HealType.scala
@@ -0,0 +1,99 @@
+package dotty.tools.dotc
+package staging
+
+import dotty.tools.dotc.core.Contexts._
+import dotty.tools.dotc.core.Decorators._
+import dotty.tools.dotc.core.Flags._
+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._
+import dotty.tools.dotc.typer.Implicits.SearchFailureType
+import dotty.tools.dotc.util.SrcPos
+
+class HealType(pos: SrcPos)(using Context) extends TypeMap {
+
+ /** If the type refers to a locally defined symbol (either directly, or in a pickled type),
+ * check that its staging level matches the current level.
+ * - Static types and term are allowed at any level.
+ * - If a type reference is used a higher level, then it is inconsistent.
+ * Will attempt to heal before failing.
+ * - If a term reference is used a higher level, then it is inconsistent.
+ * It cannot be healed because the term will not exist in any future stage.
+ *
+ * If `T` is a reference to a type at the wrong level, try to heal it by replacing it with
+ * a type tag of type `quoted.Type[T]`.
+ * The tag is generated by an instance of `QuoteTypeTags` directly if the splice is explicit
+ * or indirectly by `tryHeal`.
+ */
+ def apply(tp: Type): Type =
+ tp match
+ case tp: TypeRef =>
+ healTypeRef(tp)
+ case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level > levelOf(tp.symbol) =>
+ levelError(tp.symbol, tp, pos)
+ case tp: AnnotatedType =>
+ derivedAnnotatedType(tp, apply(tp.parent), tp.annot)
+ case _ =>
+ mapOver(tp)
+
+ private def healTypeRef(tp: TypeRef): Type =
+ tp.prefix match
+ case prefix: TermRef if tp.symbol.isTypeSplice =>
+ checkNotWildcardSplice(tp)
+ if level == 0 then tp else getQuoteTypeTags.getTagRef(prefix)
+ case prefix: TermRef if !prefix.symbol.isStatic && level > levelOf(prefix.symbol) =>
+ dealiasAndTryHeal(prefix.symbol, tp, pos)
+ case NoPrefix if level > levelOf(tp.symbol) && !tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) =>
+ dealiasAndTryHeal(tp.symbol, tp, pos)
+ case prefix: ThisType if level > levelOf(prefix.cls) && !tp.symbol.isStatic =>
+ dealiasAndTryHeal(tp.symbol, tp, pos)
+ case _ =>
+ mapOver(tp)
+
+ 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 _ =>
+
+ private def dealiasAndTryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos): Type =
+ val tp1 = tp.dealias
+ if tp1 != tp then apply(tp1)
+ else tryHeal(tp.symbol, tp, pos)
+
+ /** Try to heal reference to type `T` used in a higher level than its definition.
+ * Returns a reference to a type tag generated by `QuoteTypeTags` that contains a
+ * reference to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`.
+ * Emits an error if `T` cannot be healed and returns `T`.
+ */
+ protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos): TypeRef = {
+ val reqType = defn.QuotedTypeClass.typeRef.appliedTo(tp)
+ val tag = ctx.typer.inferImplicitArg(reqType, pos.span)
+ tag.tpe match
+ case tp: TermRef =>
+ ctx.typer.checkStable(tp, pos, "type witness")
+ getQuoteTypeTags.getTagRef(tp)
+ case _: SearchFailureType =>
+ report.error(
+ ctx.typer.missingArgMsg(tag, reqType, "")
+ .prepend(i"Reference to $tp within quotes requires a given $reqType in scope.\n")
+ .append("\n"),
+ pos)
+ tp
+ case _ =>
+ report.error(em"""Reference to $tp within quotes requires a given $reqType in scope.
+ |
+ |""", pos)
+ tp
+ }
+
+ private def levelError(sym: Symbol, tp: Type, pos: SrcPos): tp.type = {
+ report.error(
+ em"""access to $sym from wrong staging level:
+ | - the definition is at level ${levelOf(sym)},
+ | - but the access is at level $level""", pos)
+ tp
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala
similarity index 56%
rename from compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala
rename to compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala
index 6d3b62357d7f..2a1371d8eb0d 100644
--- a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala
+++ b/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala
@@ -1,26 +1,21 @@
package dotty.tools.dotc
-package transform
+package staging
import dotty.tools.dotc.ast.{tpd, untpd}
-import dotty.tools.dotc.core.Annotations.BodyAnnotation
+import dotty.tools.dotc.core.Annotations._
import dotty.tools.dotc.core.Contexts._
import dotty.tools.dotc.core.Decorators._
import dotty.tools.dotc.core.Flags._
import dotty.tools.dotc.core.NameKinds._
-import dotty.tools.dotc.core.StagingContext._
import dotty.tools.dotc.core.StdNames._
import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.core.Types._
-import dotty.tools.dotc.util.SrcPos
-import dotty.tools.dotc.util.Spans._
-import dotty.tools.dotc.transform.SymUtils._
-import dotty.tools.dotc.typer.Checking
-import dotty.tools.dotc.typer.Implicits.SearchFailureType
-import dotty.tools.dotc.core.Annotations._
-
+import dotty.tools.dotc.staging.QuoteContext.*
+import dotty.tools.dotc.staging.StagingLevel.*
+import dotty.tools.dotc.staging.QuoteTypeTags.*
import dotty.tools.dotc.util.Property
-
-import scala.annotation.constructorOnly
+import dotty.tools.dotc.util.Spans._
+import dotty.tools.dotc.util.SrcPos
/** Checks that the Phase Consistency Principle (PCP) holds and heals types.
*
@@ -48,7 +43,7 @@ import scala.annotation.constructorOnly
* }
*
*/
-class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(ictx) with Checking {
+class PCPCheckAndHeal extends TreeMapWithStages {
import tpd._
private val InAnnotation = Property.Key[Unit]()
@@ -60,25 +55,31 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
checkAnnotations(tree)
super.transform(tree)
else tree match {
-
- case _: TypeTree | _: RefTree if tree.isType =>
+ case _: TypeTree =>
+ val tp1 = transformTypeAnnotationSplices(tree.tpe)
+ val healedType = healType(tree.srcPos)(tp1)
+ if healedType == tree.tpe then tree
+ else TypeTree(healedType).withSpan(tree.span)
+ case _: RefTree if tree.isType =>
val healedType = healType(tree.srcPos)(tree.tpe)
if healedType == tree.tpe then tree
else TypeTree(healedType).withSpan(tree.span)
+ case tree: Ident if isWildcardArg(tree) =>
+ tree.withType(healType(tree.srcPos)(tree.tpe))
+ case tree: Ident => // this is a term Ident
+ checkLevelConsistency(tree)
+ tree
+ case tree: This =>
+ checkLevelConsistency(tree)
+ tree
case _: AppliedTypeTree =>
super.transform(tree) match
case tree1: AppliedTypeTree if tree1 ne tree =>
// propagate healed types
tree1.withType(tree1.tpt.tpe.appliedTo(tree1.args.map(_.tpe)))
case tree1 => tree1
-
- case _: Ident | _: This =>
- tree.withType(healTypeOfTerm(tree.srcPos)(tree.tpe))
-
- // Remove inline defs in quoted code. Already fully inlined.
case tree: DefDef if tree.symbol.is(Inline) && level > 0 =>
- EmptyTree
-
+ EmptyTree // Remove inline defs in quoted code. Already fully inlined.
case tree: ValOrDefDef =>
checkAnnotations(tree)
healInfo(tree, tree.tpt.srcPos)
@@ -88,7 +89,7 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
healInfo(tree, tree.srcPos)
super.transform(tree)
case tree: UnApply =>
- super.transform(tree).withType(healTypeOfTerm(tree.srcPos)(tree.tpe))
+ super.transform(tree).withType(healType(tree.srcPos)(tree.tpe))
case tree: TypeDef if tree.symbol.is(Case) && level > 0 =>
report.error(reporting.CaseClassInInlinedCode(tree), tree)
super.transform(tree)
@@ -98,11 +99,14 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
/** Transform quoted trees while maintaining phase correctness */
override protected def transformQuotation(body: Tree, quote: Apply)(using Context): Tree = {
- val taggedTypes = new PCPCheckAndHeal.QuoteTypeTags(quote.span)
+ 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)
+
val contextWithQuote =
if level == 0 then contextWithQuoteTypeTags(taggedTypes)(using quoteContext)
else quoteContext
@@ -115,7 +119,7 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
if body.isTerm then
// `quoted.runtime.Expr.quote[T](
)` --> `quoted.runtime.Expr.quote[T2]()`
val TypeApply(fun, targs) = quote.fun: @unchecked
- val targs2 = targs.map(targ => TypeTree(healTypeOfTerm(quote.fun.srcPos)(targ.tpe)))
+ val targs2 = targs.map(targ => TypeTree(healType(quote.fun.srcPos)(stripAnnotsDeep(targ.tpe))))
cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, targs2), body2 :: Nil)
else
val quotes = quote.args.mapConserve(transform)
@@ -142,11 +146,11 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
// `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(_, _), qctx :: 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), qctx :: Nil), body1 :: Nil)
+ cpy.Apply(splice)(cpy.Apply(f)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), quotes :: Nil), body1 :: Nil)
}
}
@@ -159,6 +163,15 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
ref(tagRef).withSpan(splice.span)
}
+ def transformTypeAnnotationSplices(tp: Type)(using Context) = new TypeMap {
+ def apply(tp: Type): Type = tp match
+ case tp: AnnotatedType =>
+ val newAnnotTree = transform(tp.annot.tree)
+ derivedAnnotatedType(tp, apply(tp.parent), tp.annot.derivedAnnotation(newAnnotTree))
+ case _ =>
+ mapOver(tp)
+ }.apply(tp)
+
/** Check that annotations do not contain quotes and and that splices are valid */
private def checkAnnotations(tree: Tree)(using Context): Unit =
tree match
@@ -186,91 +199,25 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
* The tag is generated by an instance of `QuoteTypeTags` directly if the splice is explicit
* or indirectly by `tryHeal`.
*/
- private def healType(pos: SrcPos)(using Context) = new TypeMap {
- def apply(tp: Type): Type =
- tp match
- case tp: TypeRef =>
- tp.prefix match
- case NoPrefix if level > levelOf(tp.symbol) && !tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) =>
- tryHealTypeOfType(tp.symbol, tp, pos)
- case prefix: ThisType if !tp.symbol.isStatic && level > levelOf(prefix.cls) =>
- tryHealTypeOfType(tp.symbol, tp, pos)
- case prefix: TermRef if tp.symbol.isTypeSplice =>
- prefix.symbol.info.argInfos match
- case (tb: TypeBounds) :: _ =>
- report.error(em"Cannot splice $tp because it is a wildcard type", pos)
- case _ =>
- // Heal explicit type splice in the code
- if level > 0 then getQuoteTypeTags.getTagRef(prefix) else tp
- case prefix: TermRef if !prefix.symbol.isStatic && level > levelOf(prefix.symbol) =>
- tryHealTypeOfType(prefix.symbol, tp, pos)
- case _ =>
- mapOver(tp)
- case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level > levelOf(tp.symbol) =>
- levelError(tp.symbol, tp, pos)
- case tp: ThisType if level != -1 && level != levelOf(tp.cls) =>
- levelError(tp.cls, tp, pos)
- case tp: AnnotatedType =>
- val newAnnotTree = transform(tp.annot.tree)
- derivedAnnotatedType(tp, apply(tp.parent), tp.annot.derivedAnnotation(newAnnotTree))
- case _ =>
- mapOver(tp)
-
- /** Try to dealias or heal reference to type `T` used in a higher level than its definition.
- * Returns a reference to a type tag generated by `QuoteTypeTags` that contains a
- * reference to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`.
- * Emits and error if `T` cannot be healed and returns `T`.
- */
- private def tryHealTypeOfType(sym: Symbol, tp: TypeRef, pos: SrcPos)(using Context): Type = {
- val tp1 = tp.dealias
- if tp1 != tp then apply(tp1)
- else tryHeal(tp.symbol, tp, pos)
- }
- }
-
- /** Check phase consistency of terms and heal inconsistent type references. */
- private def healTypeOfTerm(pos: SrcPos)(using Context) = new TypeMap {
- def apply(tp: Type): Type =
- tp match
- case tp @ TypeRef(NoPrefix, _) if level > levelOf(tp.symbol) =>
- tryHeal(tp.symbol, tp, pos)
- case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level != levelOf(tp.symbol) =>
- levelError(tp.symbol, tp, pos)
- case tp: ThisType if level != -1 && level != levelOf(tp.cls) =>
- levelError(tp.cls, tp, pos)
- case tp: AnnotatedType =>
- derivedAnnotatedType(tp, apply(tp.parent), tp.annot)
- case _ =>
- if tp.typeSymbol.is(Package) then tp
- else mapOver(tp)
- }
-
- /** Try to heal reference to type `T` used in a higher level than its definition.
- * Returns a reference to a type tag generated by `QuoteTypeTags` that contains a
- * reference to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`.
- * Emits and error if `T` cannot be healed and returns `T`.
- */
- protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos)(using Context): TypeRef = {
- val reqType = defn.QuotedTypeClass.typeRef.appliedTo(tp)
- val tag = ctx.typer.inferImplicitArg(reqType, pos.span)
- tag.tpe match
-
- case tp: TermRef =>
- checkStable(tp, pos, "type witness")
- getQuoteTypeTags.getTagRef(tp)
- case _: SearchFailureType =>
- report.error(
- ctx.typer.missingArgMsg(tag, reqType, "")
- .prepend(i"Reference to $tp within quotes requires a given $reqType in scope.\n")
- .append("\n"),
- pos)
- tp
- case _ =>
- report.error(em"""Reference to $tp within quotes requires a given $reqType in scope.
- |
- |""", pos)
- tp
- }
+ protected def healType(pos: SrcPos)(using Context) =
+ new HealType(pos)
+
+ /** Check level consistency of terms references */
+ private def checkLevelConsistency(tree: Ident | This)(using Context): Unit =
+ new TypeTraverser {
+ def traverse(tp: Type): Unit =
+ tp match
+ case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level != levelOf(tp.symbol) =>
+ levelError(tp.symbol, tp, tree.srcPos)
+ case tp: ThisType if level != -1 && level != levelOf(tp.cls) =>
+ levelError(tp.cls, tp, tree.srcPos)
+ case tp: AnnotatedType =>
+ traverse(tp.parent)
+ case _ if tp.typeSymbol.is(Package) =>
+ // OK
+ case _ =>
+ traverseChildren(tp)
+ }.traverse(tree.tpe)
private def levelError(sym: Symbol, tp: Type, pos: SrcPos)(using Context): tp.type = {
def symStr =
@@ -289,37 +236,4 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
| - but the access is at level $level.$hint""", pos)
tp
}
-
-}
-
-object PCPCheckAndHeal {
- import tpd._
-
- class QuoteTypeTags(span: Span)(using Context) {
-
- private val tags = collection.mutable.LinkedHashMap.empty[Symbol, TypeDef]
-
- def getTagRef(spliced: TermRef): TypeRef = {
- val typeDef = tags.getOrElseUpdate(spliced.symbol, mkTagSymbolAndAssignType(spliced))
- typeDef.symbol.typeRef
- }
-
- def getTypeTags: List[TypeDef] = tags.valuesIterator.toList
-
- private def mkTagSymbolAndAssignType(spliced: TermRef): TypeDef = {
- val splicedTree = tpd.ref(spliced).withSpan(span)
- val rhs = splicedTree.select(tpnme.Underlying).withSpan(span)
- val alias = ctx.typeAssigner.assignType(untpd.TypeBoundsTree(rhs, rhs), rhs, rhs, EmptyTree)
- val local = newSymbol(
- owner = ctx.owner,
- name = UniqueName.fresh((splicedTree.symbol.name.toString + "$_").toTermName).toTypeName,
- flags = Synthetic,
- info = TypeAlias(splicedTree.tpe.select(tpnme.Underlying)),
- coord = span).asType
- local.addAnnotation(Annotation(defn.QuotedRuntime_SplicedTypeAnnot, span))
- ctx.typeAssigner.assignType(untpd.TypeDef(local.name, alias), local)
- }
-
- }
-
}
diff --git a/compiler/src/dotty/tools/dotc/staging/QuoteContext.scala b/compiler/src/dotty/tools/dotc/staging/QuoteContext.scala
new file mode 100644
index 000000000000..542e8ecc2b9f
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/staging/QuoteContext.scala
@@ -0,0 +1,35 @@
+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.PCPCheckAndHeal
+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/QuoteTypeTags.scala b/compiler/src/dotty/tools/dotc/staging/QuoteTypeTags.scala
new file mode 100644
index 000000000000..c4c9c611be3a
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/staging/QuoteTypeTags.scala
@@ -0,0 +1,52 @@
+package dotty.tools.dotc.staging
+
+import dotty.tools.dotc.ast.{tpd, untpd}
+import dotty.tools.dotc.core.Annotations._
+import dotty.tools.dotc.core.Contexts._
+import dotty.tools.dotc.core.Decorators._
+import dotty.tools.dotc.core.Flags._
+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.StagingLevel.*
+import dotty.tools.dotc.util.Property
+import dotty.tools.dotc.util.Spans._
+
+object QuoteTypeTags {
+
+ private val TaggedTypes = new Property.Key[QuoteTypeTags]
+
+ def contextWithQuoteTypeTags(taggedTypes: QuoteTypeTags)(using Context) =
+ ctx.fresh.setProperty(TaggedTypes, taggedTypes)
+
+ def getQuoteTypeTags(using Context): QuoteTypeTags =
+ ctx.property(TaggedTypes).get
+}
+
+class QuoteTypeTags(span: Span)(using Context) {
+ import tpd.*
+
+ private val tags = collection.mutable.LinkedHashMap.empty[Symbol, TypeDef]
+
+ def getTagRef(spliced: TermRef): TypeRef = {
+ val typeDef = tags.getOrElseUpdate(spliced.symbol, mkTagSymbolAndAssignType(spliced))
+ typeDef.symbol.typeRef
+ }
+
+ def getTypeTags: List[TypeDef] = tags.valuesIterator.toList
+
+ private def mkTagSymbolAndAssignType(spliced: TermRef): TypeDef = {
+ val splicedTree = tpd.ref(spliced).withSpan(span)
+ val rhs = splicedTree.select(tpnme.Underlying).withSpan(span)
+ val alias = ctx.typeAssigner.assignType(untpd.TypeBoundsTree(rhs, rhs), rhs, rhs, EmptyTree)
+ val local = newSymbol(
+ owner = ctx.owner,
+ name = UniqueName.fresh((splicedTree.symbol.name.toString + "$_").toTermName).toTypeName,
+ flags = Synthetic,
+ info = TypeAlias(splicedTree.tpe.select(tpnme.Underlying)),
+ coord = span).asType
+ local.addAnnotation(Annotation(defn.QuotedRuntime_SplicedTypeAnnot, span))
+ ctx.typeAssigner.assignType(untpd.TypeDef(local.name, alias), local)
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala b/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala
new file mode 100644
index 000000000000..4704501e38ff
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala
@@ -0,0 +1,48 @@
+package dotty.tools.dotc
+package staging
+
+import dotty.tools.dotc.core.Contexts._
+import dotty.tools.dotc.core.Decorators._
+import dotty.tools.dotc.core.Flags._
+import dotty.tools.dotc.core.Symbols._
+import dotty.tools.dotc.core.Types._
+import dotty.tools.dotc.util.Property
+import dotty.tools.dotc.util.SrcPos
+
+import scala.collection.mutable
+
+object StagingLevel {
+
+ /** A key to be used in a context property that tracks the staging level */
+ private val LevelKey = new Property.Key[Int]
+
+ /** A key to be used in a context property that caches the `levelOf` mapping */
+ private val LevelOfKey = new Property.Key[Map[Symbol, Int]]
+
+ /** All enclosing calls that are currently inlined, from innermost to outermost. */
+ def level(using Context): Int =
+ ctx.property(LevelKey).getOrElse(0)
+
+ /** Context with an incremented staging level. */
+ def quoteContext(using Context): FreshContext =
+ ctx.fresh.setProperty(LevelKey, level + 1)
+
+ /** Context with a decremented staging level. */
+ def spliceContext(using Context): FreshContext =
+ ctx.fresh.setProperty(LevelKey, level - 1)
+
+ /** The quotation level of the definition of the locally defined symbol */
+ def levelOf(sym: Symbol)(using Context): Int =
+ ctx.property(LevelOfKey) match
+ case Some(map) => map.getOrElse(sym, 0)
+ case None => 0
+
+ /** Context with the current staging level set for the symbols */
+ def symbolsInCurrentLevel(syms: List[Symbol])(using Context): Context =
+ if level == 0 then ctx
+ else
+ val levelOfMap = ctx.property(LevelOfKey).getOrElse(Map.empty)
+ val syms1 = syms//.filter(sym => !levelOfMap.contains(sym))
+ val newMap = syms1.foldLeft(levelOfMap)((acc, sym) => acc.updated(sym, level))
+ ctx.fresh.setProperty(LevelOfKey, newMap)
+}
diff --git a/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala
similarity index 56%
rename from compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala
rename to compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala
index b514b8a7bf11..adaacafa764a 100644
--- a/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala
+++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala
@@ -1,58 +1,26 @@
package dotty.tools.dotc
-package transform
+package staging
import dotty.tools.dotc.ast.{TreeMapWithImplicits, tpd}
import dotty.tools.dotc.config.Printers.staging
import dotty.tools.dotc.core.Decorators._
import dotty.tools.dotc.core.Contexts._
-import dotty.tools.dotc.core.StagingContext._
import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.util.Property
+import dotty.tools.dotc.staging.StagingLevel.*
import scala.collection.mutable
-import scala.annotation.constructorOnly
-
-/** The main transformer class
- * @param level the current level, where quotes add one and splices subtract one level.
- * The initial level is 0, a level `l` where `l > 0` implies code has been quoted `l` times
- * and `l == -1` is code inside a top level splice (in an inline method).
- * @param levels a stacked map from symbols to the levels in which they were defined
- */
-abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMapWithImplicits {
+/** TreeMap that keeps track of staging levels using StagingLevel. */
+abstract class TreeMapWithStages extends TreeMapWithImplicits {
import tpd._
- import TreeMapWithStages._
-
- /** A map from locally defined symbols to their definition quotation level */
- private[this] val levelOfMap: mutable.HashMap[Symbol, Int] = ictx.property(LevelOfKey).get
-
- /** A stack of entered symbols, to be unwound after scope exit */
- private[this] var enteredSyms: List[Symbol] = Nil
/** If we are inside a quote or a splice */
private[this] var inQuoteOrSplice = false
- /** The quotation level of the definition of the locally defined symbol */
- protected def levelOf(sym: Symbol): Int = levelOfMap.getOrElse(sym, 0)
-
- /** Locally defined symbols seen so far by `StagingTransformer.transform` */
- protected def localSymbols: List[Symbol] = enteredSyms
-
/** If we are inside a quote or a splice */
protected def isInQuoteOrSplice: Boolean = inQuoteOrSplice
- /** Enter staging level of symbol defined by `tree` */
- private def markSymbol(sym: Symbol)(using Context): Unit =
- if level != 0 && !levelOfMap.contains(sym) then
- levelOfMap(sym) = level
- enteredSyms = sym :: enteredSyms
-
- /** Enter staging level of symbol defined by `tree`, if applicable. */
- private def markDef(tree: Tree)(using Context): Unit = tree match {
- case tree: DefTree => markSymbol(tree.symbol)
- case _ =>
- }
-
/** Transform the quote `quote` which contains the quoted `body`.
*
* - `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T]()`
@@ -75,14 +43,6 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap
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 mapOverTree(lastEntered: List[Symbol]) =
- try super.transform(tree)
- finally
- while (enteredSyms ne lastEntered) {
- levelOfMap -= enteredSyms.head
- enteredSyms = enteredSyms.tail
- }
-
def dropEmptyBlocks(tree: Tree): Tree = tree match {
case Block(Nil, expr) => dropEmptyBlocks(expr)
case _ => tree
@@ -127,38 +87,30 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap
finally inQuoteOrSplice = old
case Block(stats, _) =>
- val last = enteredSyms
- stats.foreach(markDef)
- mapOverTree(last)
+ val defSyms = stats.collect { case defTree: DefTree => defTree.symbol }
+ super.transform(tree)(using symbolsInCurrentLevel(defSyms))
case CaseDef(pat, guard, body) =>
- val last = enteredSyms
- tpd.patVars(pat).foreach(markSymbol)
- mapOverTree(last)
+ super.transform(tree)(using symbolsInCurrentLevel(tpd.patVars(pat)))
case (_:Import | _:Export) =>
tree
case _: Template =>
- val last = enteredSyms
- tree.symbol.owner.info.decls.foreach(markSymbol)
- mapOverTree(last)
+ val decls = tree.symbol.owner.info.decls.toList
+ super.transform(tree)(using symbolsInCurrentLevel(decls))
+
+ case LambdaTypeTree(tparams, body) =>
+ super.transform(tree)(using symbolsInCurrentLevel(tparams.map(_.symbol)))
+
+ case tree: DefTree =>
+ val paramSyms = tree match
+ case tree: DefDef => tree.paramss.flatten.map(_.symbol)
+ case _ => Nil
+ super.transform(tree)(using symbolsInCurrentLevel(tree.symbol :: paramSyms))
case _ =>
- markDef(tree)
- mapOverTree(enteredSyms)
+ super.transform(tree)
}
}
}
-
-
-object TreeMapWithStages {
-
- /** A key to be used in a context property that caches the `levelOf` mapping */
- private val LevelOfKey = new Property.Key[mutable.HashMap[Symbol, Int]]
-
- /** Initial context for a StagingTransformer transformation. */
- def freshStagingContext(using Context): Context =
- ctx.fresh.setProperty(LevelOfKey, new mutable.HashMap[Symbol, Int])
-
-}
diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala
index f0ed7026ee91..d6b7f3141b96 100644
--- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala
+++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala
@@ -9,10 +9,11 @@ import SymUtils._
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.ast.Trees._
import dotty.tools.dotc.quoted._
-import dotty.tools.dotc.core.StagingContext._
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 +47,10 @@ class Inlining extends MacroTransform {
def traverse(tree: Tree)(using Context): Unit =
tree match
case _: GenericApply if tree.symbol.isQuote =>
- traverseChildren(tree)(using StagingContext.quoteContext)
+ traverseChildren(tree)(using StagingLevel.quoteContext)
case _: GenericApply if tree.symbol.isExprSplice =>
- traverseChildren(tree)(using StagingContext.spliceContext)
- case tree: RefTree if !Inlines.inInlineMethod && StagingContext.level == 0 =>
+ traverseChildren(tree)(using StagingLevel.spliceContext)
+ case tree: RefTree if !Inlines.inInlineMethod && StagingLevel.level == 0 =>
assert(!tree.symbol.isInlineMethod, tree.show)
case _ =>
traverseChildren(tree)
@@ -76,7 +77,7 @@ class Inlining extends MacroTransform {
else if tree.symbol.is(Param) then super.transform(tree)
else if
!tree.symbol.isPrimaryConstructor
- && StagingContext.level == 0
+ && StagingLevel.level == 0
&& MacroAnnotations.hasMacroAnnotation(tree.symbol)
then
val trees = (new MacroAnnotations).expandAnnotations(tree)
@@ -98,9 +99,9 @@ class Inlining extends MacroTransform {
if tree1.tpe.isError then tree1
else Inlines.inlineCall(tree1)
case _: GenericApply if tree.symbol.isQuote =>
- super.transform(tree)(using StagingContext.quoteContext)
+ super.transform(tree)(using StagingLevel.quoteContext)
case _: GenericApply if tree.symbol.isExprSplice =>
- super.transform(tree)(using StagingContext.spliceContext)
+ super.transform(tree)(using StagingLevel.spliceContext)
case _: PackageDef =>
super.transform(tree) match
case tree1: PackageDef =>
diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala
index 87c6e294c104..728ee9552c81 100644
--- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala
+++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala
@@ -19,7 +19,6 @@ import scala.collection.mutable
import dotty.tools.dotc.core.Annotations._
import dotty.tools.dotc.core.StdNames._
import dotty.tools.dotc.quoted._
-import dotty.tools.dotc.transform.TreeMapWithStages._
import dotty.tools.dotc.inlines.Inlines
import scala.annotation.constructorOnly
@@ -93,7 +92,7 @@ class PickleQuotes extends MacroTransform {
case _ =>
override def run(using Context): Unit =
- if (ctx.compilationUnit.needsStaging) super.run(using freshStagingContext)
+ if (ctx.compilationUnit.needsStaging) super.run
protected def newTransformer(using Context): Transformer = new Transformer {
override def transform(tree: tpd.Tree)(using Context): tpd.Tree =
diff --git a/compiler/src/dotty/tools/dotc/transform/ReifiedReflect.scala b/compiler/src/dotty/tools/dotc/transform/ReifiedReflect.scala
index e462f82b1dad..b2059195b8e4 100644
--- a/compiler/src/dotty/tools/dotc/transform/ReifiedReflect.scala
+++ b/compiler/src/dotty/tools/dotc/transform/ReifiedReflect.scala
@@ -17,7 +17,6 @@ import dotty.tools.dotc.core.Annotations._
import dotty.tools.dotc.core.Names._
import dotty.tools.dotc.core.StdNames._
import dotty.tools.dotc.quoted._
-import dotty.tools.dotc.transform.TreeMapWithStages._
import scala.annotation.constructorOnly
diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala
index 393fe46b8438..2b35188425fb 100644
--- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala
+++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala
@@ -14,15 +14,17 @@ import util.Spans._
import SymUtils._
import NameKinds._
import dotty.tools.dotc.ast.tpd
-import StagingContext._
import scala.collection.mutable
import dotty.tools.dotc.core.Annotations._
import dotty.tools.dotc.core.Names._
import dotty.tools.dotc.core.StdNames._
import dotty.tools.dotc.quoted._
-import dotty.tools.dotc.transform.TreeMapWithStages._
import dotty.tools.dotc.config.ScalaRelease.*
+import dotty.tools.dotc.staging.PCPCheckAndHeal
+import dotty.tools.dotc.staging.QuoteContext.*
+import dotty.tools.dotc.staging.StagingLevel.*
+import dotty.tools.dotc.staging.QuoteTypeTags
import scala.annotation.constructorOnly
@@ -77,7 +79,7 @@ class Splicing extends MacroTransform:
override def run(using Context): Unit =
if ctx.compilationUnit.needsStaging then
- super.run(using freshStagingContext)
+ super.run
protected def newTransformer(using Context): Transformer = Level0QuoteTransformer
@@ -190,7 +192,7 @@ class Splicing extends MacroTransform:
private var refBindingMap = mutable.Map.empty[Symbol, (Tree, Symbol)]
/** Reference to the `Quotes` instance of the current level 1 splice */
private var quotes: Tree | Null = null // TODO: add to the context
- private var healedTypes: PCPCheckAndHeal.QuoteTypeTags | Null = null // TODO: add to the context
+ private var healedTypes: QuoteTypeTags | Null = null // TODO: add to the context
def transformSplice(tree: tpd.Tree, tpe: Type, holeIdx: Int)(using Context): tpd.Tree =
assert(level == 0)
@@ -246,7 +248,7 @@ class Splicing extends MacroTransform:
if tree.symbol == defn.QuotedTypeModule_of && containsCapturedType(tpt.tpe) =>
val newContent = capturedPartTypes(tpt)
newContent match
- case block: Block =>
+ case block: Block =>
inContext(ctx.withSource(tree.source)) {
Apply(TypeApply(typeof, List(newContent)), List(quotes)).withSpan(tree.span)
}
@@ -260,7 +262,7 @@ class Splicing extends MacroTransform:
private def transformLevel0QuoteContent(tree: Tree)(using Context): Tree =
// transform and collect new healed types
val old = healedTypes
- healedTypes = new PCPCheckAndHeal.QuoteTypeTags(tree.span)
+ healedTypes = new QuoteTypeTags(tree.span)
val tree1 = transform(tree)
val newHealedTypes = healedTypes.nn.getTypeTags
healedTypes = old
@@ -342,7 +344,7 @@ class Splicing extends MacroTransform:
val bindingSym = refBindingMap.getOrElseUpdate(tree.symbol, (tree, newBinding))._2
ref(bindingSym)
- private def newQuotedTypeClassBinding(tpe: Type)(using Context) =
+ private def newQuotedTypeClassBinding(tpe: Type)(using Context) =
newSymbol(
spliceOwner,
UniqueName.fresh(nme.Type).toTermName,
@@ -358,7 +360,7 @@ class Splicing extends MacroTransform:
private def capturedPartTypes(tpt: Tree)(using Context): Tree =
val old = healedTypes
- healedTypes = PCPCheckAndHeal.QuoteTypeTags(tpt.span)
+ healedTypes = QuoteTypeTags(tpt.span)
val capturePartTypes = new TypeMap {
def apply(tp: Type) = tp match {
case typeRef @ TypeRef(prefix, _) if isCaptured(prefix.typeSymbol) || isCaptured(prefix.termSymbol) =>
@@ -376,7 +378,7 @@ class Splicing extends MacroTransform:
tpt match
case block: Block =>
cpy.Block(block)(newHealedTypes ::: block.stats, TypeTree(captured))
- case _ =>
+ case _ =>
if newHealedTypes.nonEmpty then
cpy.Block(tpt)(newHealedTypes, TypeTree(captured))
else
diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala
index 1de050a9a6c1..5ebfa25eeacd 100644
--- a/compiler/src/dotty/tools/dotc/transform/Staging.scala
+++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala
@@ -6,14 +6,14 @@ import dotty.tools.dotc.core.Contexts._
import dotty.tools.dotc.core.Phases._
import dotty.tools.dotc.core.Decorators._
import dotty.tools.dotc.core.Flags._
-import dotty.tools.dotc.core.StagingContext._
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.transform.TreeMapWithStages._
-
-
+import dotty.tools.dotc.staging.QuoteContext.*
+import dotty.tools.dotc.staging.StagingLevel.*
+import dotty.tools.dotc.staging.PCPCheckAndHeal
+import dotty.tools.dotc.staging.HealType
/** Checks that the Phase Consistency Principle (PCP) holds and heals types.
*
@@ -35,20 +35,22 @@ class Staging extends MacroTransform {
// Recheck that PCP holds but do not heal any inconsistent types as they should already have been heald
tree match {
case PackageDef(pid, _) if tree.symbol.owner == defn.RootClass =>
- val checker = new PCPCheckAndHeal(freshStagingContext) {
- override protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos)(using Context): TypeRef = {
- def symStr =
- if (sym.is(ModuleClass)) sym.sourceModule.show
- else i"${sym.name}.this"
- val errMsg = s"\nin ${ctx.owner.fullName}"
- assert(
- ctx.owner.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) ||
- (sym.isType && levelOf(sym) > 0),
- em"""access to $symStr from wrong staging level:
- | - the definition is at level ${levelOf(sym)},
- | - but the access is at level $level.$errMsg""")
+ val checker = new PCPCheckAndHeal {
+ override protected def healType(pos: SrcPos)(using Context) = new HealType(pos) {
+ override protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos): TypeRef = {
+ def symStr =
+ if (sym.is(ModuleClass)) sym.sourceModule.show
+ else i"${sym.name}.this"
+ val errMsg = s"\nin ${ctx.owner.fullName}"
+ assert(
+ ctx.owner.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) ||
+ (sym.isType && levelOf(sym) > 0),
+ em"""access to $symStr from wrong staging level:
+ | - the definition is at level ${levelOf(sym)},
+ | - but the access is at level $level.$errMsg""")
- tp
+ tp
+ }
}
}
checker.transform(tree)
@@ -66,11 +68,11 @@ class Staging extends MacroTransform {
}
override def run(using Context): Unit =
- if (ctx.compilationUnit.needsStaging) super.run(using freshStagingContext)
+ if (ctx.compilationUnit.needsStaging) super.run
protected def newTransformer(using Context): Transformer = new Transformer {
override def transform(tree: tpd.Tree)(using Context): tpd.Tree =
- new PCPCheckAndHeal(ctx).transform(tree)
+ (new PCPCheckAndHeal).transform(tree)
}
}
diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala
index 52942ea719f9..309c61f5c7d6 100644
--- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala
+++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala
@@ -678,7 +678,7 @@ object TreeChecker {
defn.FunctionOf(List(defn.QuotesClass.typeRef), expectedResultType, isContextual = true)
val expectedContentType =
defn.FunctionOf(argQuotedTypes, contextualResult)
- assert(content.typeOpt =:= expectedContentType)
+ assert(content.typeOpt =:= expectedContentType, i"expected content of the hole to be ${expectedContentType} but got ${content.typeOpt}")
tree1
}
diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala
index 65d8abfdf6a7..5d04f27a1305 100644
--- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala
+++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala
@@ -11,11 +11,12 @@ import dotty.tools.dotc.core.Decorators._
import dotty.tools.dotc.core.Flags._
import dotty.tools.dotc.core.NameKinds.PatMatGivenVarName
import dotty.tools.dotc.core.Names._
-import dotty.tools.dotc.core.StagingContext._
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._
import dotty.tools.dotc.typer.Inferencing._
@@ -91,7 +92,7 @@ trait QuotesAndSplices {
tree.withType(UnspecifiedErrorType)
}
else {
- if (StagingContext.level == 0) {
+ if (level == 0) {
// Mark the first inline method from the context as a macro
def markAsMacro(c: Context): Unit =
if (c.owner eq c.outer.owner) markAsMacro(c.outer)
diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala
index 96149caf215d..8e8c8b2da1ca 100644
--- a/compiler/src/dotty/tools/dotc/typer/Typer.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala
@@ -44,6 +44,7 @@ import config.Feature
import config.Feature.{sourceVersion, migrateTo3}
import config.SourceVersion._
import rewrites.Rewrites.patch
+import staging.StagingLevel
import transform.SymUtils._
import transform.TypeUtils._
import reporting._
@@ -2410,7 +2411,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
else typedExpr(ddef.rhs, tpt1.tpe.widenExpr)(using rhsCtx))
if sym.isInlineMethod then
- if StagingContext.level > 0 then
+ if StagingLevel.level > 0 then
report.error("inline def cannot be within quotes", sym.sourcePos)
if sym.is(Given)
&& untpd.stripBlock(untpd.unsplice(ddef.rhs)).isInstanceOf[untpd.Function]