Skip to content

Commit d44833e

Browse files
committed
Update cross-stage safety nomenclature
Before `3.0` we experimented with the Phase Consistency Principle (PCP) which was described in [A practical unification of multi-stage programming and macros](https://dl.acm.org/doi/10.1145/3278122.3278139). This principle restricted term and types to be used at the same level. Now, and since `3.0` we use a more general version of cross-stage safety where types can be used at any level. Lower levels get erased and higher levels require a given `Type[T]`. The most updated reference is in [Scalable Metaprogramming in Scala 3](https://github.com/nicolasstucki/nicolasstucki/raw/main/Scalable%20Metaprogramming%20in%20Scala%203.pdf). We must remove the use of the term PCP to avoid confusion in the current implementation.
1 parent f92d6f1 commit d44833e

File tree

7 files changed

+34
-36
lines changed

7 files changed

+34
-36
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import dotty.tools.dotc.ast.tpd
44
import dotty.tools.dotc.core.Contexts._
55
import dotty.tools.dotc.ast.tpd
66
import dotty.tools.dotc.util.Property
7-
import dotty.tools.dotc.transform.PCPCheckAndHeal
7+
import dotty.tools.dotc.transform.CrossStageSafety
88

99
object StagingContext {
1010

@@ -16,7 +16,7 @@ object StagingContext {
1616
*/
1717
private val QuotesStack = new Property.Key[List[tpd.Tree]]
1818

19-
private val TaggedTypes = new Property.Key[PCPCheckAndHeal.QuoteTypeTags]
19+
private val TaggedTypes = new Property.Key[CrossStageSafety.QuoteTypeTags]
2020

2121
/** All enclosing calls that are currently inlined, from innermost to outermost. */
2222
def level(using Context): Int =
@@ -36,10 +36,10 @@ object StagingContext {
3636
def spliceContext(using Context): Context =
3737
ctx.fresh.setProperty(QuotationLevel, level - 1)
3838

39-
def contextWithQuoteTypeTags(taggedTypes: PCPCheckAndHeal.QuoteTypeTags)(using Context) =
39+
def contextWithQuoteTypeTags(taggedTypes: CrossStageSafety.QuoteTypeTags)(using Context) =
4040
ctx.fresh.setProperty(TaggedTypes, taggedTypes)
4141

42-
def getQuoteTypeTags(using Context): PCPCheckAndHeal.QuoteTypeTags =
42+
def getQuoteTypeTags(using Context): CrossStageSafety.QuoteTypeTags =
4343
ctx.property(TaggedTypes).get
4444

4545
/** Context with a decremented quotation level and pops the Some of top of the quote context stack or None if the stack is empty.

compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import NameKinds.{InlineAccessorName, UniqueInlineName}
1717
import inlines.Inlines
1818
import NameOps._
1919
import Annotations._
20-
import transform.{AccessProxies, PCPCheckAndHeal, Splicer}
20+
import transform.{AccessProxies, CrossStageSafety, Splicer}
2121
import transform.SymUtils.*
2222
import config.Printers.inlining
2323
import util.Property
@@ -293,7 +293,7 @@ object PrepareInlineable {
293293
if (code.symbol.flags.is(Inline))
294294
report.error("Macro cannot be implemented with an `inline` method", code.srcPos)
295295
Splicer.checkValidMacroBody(code)
296-
new PCPCheckAndHeal(freshStagingContext).transform(body) // Ignore output, only check PCP
296+
new CrossStageSafety(freshStagingContext).transform(body) // Ignore output, only check staging level consistency
297297
case Block(List(stat), Literal(Constants.Constant(()))) => checkMacro(stat)
298298
case Block(Nil, expr) => checkMacro(expr)
299299
case Typed(expr, _) => checkMacro(expr)

compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala renamed to compiler/src/dotty/tools/dotc/transform/CrossStageSafety.scala

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ import dotty.tools.dotc.util.Property
2222

2323
import scala.annotation.constructorOnly
2424

25-
/** Checks that the Phase Consistency Principle (PCP) holds and heals types.
25+
/** Checks that staging level consistency holds and heals staged types.
2626
*
27-
* Local term references are phase consistent if and only if they are used at the same level as their definition.
27+
* Local term references are level consistent if and only if they are used at the same level as their definition.
2828
*
2929
* Local type references can be used at the level of their definition or lower. If used used at a higher level,
3030
* it will be healed if possible, otherwise it is inconsistent.
3131
*
32-
* Type healing consists in transforming a phase inconsistent type `T` into `summon[Type[T]].Underlying`.
32+
* Type healing consists in transforming a level inconsistent type `T` into `summon[Type[T]].Underlying`.
3333
*
3434
* As references to types do not necessarily have an associated tree it is not always possible to replace the types directly.
3535
* Instead we always generate a type alias for it and place it at the start of the surrounding quote. This also avoids duplication.
@@ -48,7 +48,7 @@ import scala.annotation.constructorOnly
4848
* }
4949
*
5050
*/
51-
class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(ictx) with Checking {
51+
class CrossStageSafety(@constructorOnly ictx: Context) extends TreeMapWithStages(ictx) with Checking {
5252
import tpd._
5353

5454
private val InAnnotation = Property.Key[Unit]()
@@ -96,9 +96,9 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
9696
super.transform(tree)
9797
}
9898

99-
/** Transform quoted trees while maintaining phase correctness */
99+
/** Transform quoted trees while maintaining level correctness */
100100
override protected def transformQuotation(body: Tree, quote: Apply)(using Context): Tree = {
101-
val taggedTypes = new PCPCheckAndHeal.QuoteTypeTags(quote.span)
101+
val taggedTypes = new CrossStageSafety.QuoteTypeTags(quote.span)
102102

103103
if (ctx.property(InAnnotation).isDefined)
104104
report.error("Cannot have a quote in an annotation", quote.srcPos)
@@ -219,7 +219,7 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
219219
mapOver(tp)
220220
}
221221

222-
/** Check phase consistency of terms and heal inconsistent type references. */
222+
/** Check level consistency of terms and heal inconsistent type references. */
223223
private def healTypeOfTerm(pos: SrcPos)(using Context) = new TypeMap {
224224
def apply(tp: Type): Type =
225225
tp match
@@ -283,7 +283,7 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
283283

284284
}
285285

286-
object PCPCheckAndHeal {
286+
object CrossStageSafety {
287287
import tpd._
288288

289289
class QuoteTypeTags(span: Span)(using Context) {

compiler/src/dotty/tools/dotc/transform/Splicing.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ class Splicing extends MacroTransform:
190190
private var refBindingMap = mutable.Map.empty[Symbol, (Tree, Symbol)]
191191
/** Reference to the `Quotes` instance of the current level 1 splice */
192192
private var quotes: Tree | Null = null // TODO: add to the context
193-
private var healedTypes: PCPCheckAndHeal.QuoteTypeTags | Null = null // TODO: add to the context
193+
private var healedTypes: CrossStageSafety.QuoteTypeTags | Null = null // TODO: add to the context
194194

195195
def transformSplice(tree: tpd.Tree, tpe: Type, holeIdx: Int)(using Context): tpd.Tree =
196196
assert(level == 0)
@@ -246,7 +246,7 @@ class Splicing extends MacroTransform:
246246
if tree.symbol == defn.QuotedTypeModule_of && containsCapturedType(tpt.tpe) =>
247247
val newContent = capturedPartTypes(tpt)
248248
newContent match
249-
case block: Block =>
249+
case block: Block =>
250250
inContext(ctx.withSource(tree.source)) {
251251
Apply(TypeApply(typeof, List(newContent)), List(quotes)).withSpan(tree.span)
252252
}
@@ -260,7 +260,7 @@ class Splicing extends MacroTransform:
260260
private def transformLevel0QuoteContent(tree: Tree)(using Context): Tree =
261261
// transform and collect new healed types
262262
val old = healedTypes
263-
healedTypes = new PCPCheckAndHeal.QuoteTypeTags(tree.span)
263+
healedTypes = new CrossStageSafety.QuoteTypeTags(tree.span)
264264
val tree1 = transform(tree)
265265
val newHealedTypes = healedTypes.nn.getTypeTags
266266
healedTypes = old
@@ -342,7 +342,7 @@ class Splicing extends MacroTransform:
342342
val bindingSym = refBindingMap.getOrElseUpdate(tree.symbol, (tree, newBinding))._2
343343
ref(bindingSym)
344344

345-
private def newQuotedTypeClassBinding(tpe: Type)(using Context) =
345+
private def newQuotedTypeClassBinding(tpe: Type)(using Context) =
346346
newSymbol(
347347
spliceOwner,
348348
UniqueName.fresh(nme.Type).toTermName,
@@ -358,7 +358,7 @@ class Splicing extends MacroTransform:
358358

359359
private def capturedPartTypes(tpt: Tree)(using Context): Tree =
360360
val old = healedTypes
361-
healedTypes = PCPCheckAndHeal.QuoteTypeTags(tpt.span)
361+
healedTypes = CrossStageSafety.QuoteTypeTags(tpt.span)
362362
val capturePartTypes = new TypeMap {
363363
def apply(tp: Type) = tp match {
364364
case typeRef @ TypeRef(prefix, _) if isCaptured(prefix.typeSymbol) || isCaptured(prefix.termSymbol) =>
@@ -376,7 +376,7 @@ class Splicing extends MacroTransform:
376376
tpt match
377377
case block: Block =>
378378
cpy.Block(block)(newHealedTypes ::: block.stats, TypeTree(captured))
379-
case _ =>
379+
case _ =>
380380
if newHealedTypes.nonEmpty then
381381
cpy.Block(tpt)(newHealedTypes, TypeTree(captured))
382382
else

compiler/src/dotty/tools/dotc/transform/Staging.scala

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,9 @@ import dotty.tools.dotc.util.SrcPos
1313
import dotty.tools.dotc.transform.SymUtils._
1414
import dotty.tools.dotc.transform.TreeMapWithStages._
1515

16-
17-
18-
/** Checks that the Phase Consistency Principle (PCP) holds and heals types.
16+
/** Checks that staging level consistency holds and heals types used in higher levels.
1917
*
20-
* Type healing consists in transforming a phase inconsistent type `T` into `${ implicitly[Type[T]] }`.
18+
* See `CrossStageSafety`
2119
*/
2220
class Staging extends MacroTransform {
2321
import tpd._
@@ -32,10 +30,10 @@ class Staging extends MacroTransform {
3230

3331
override def checkPostCondition(tree: Tree)(using Context): Unit =
3432
if (ctx.phase <= splicingPhase) {
35-
// Recheck that PCP holds but do not heal any inconsistent types as they should already have been heald
33+
// Recheck that staging level consistency holds but do not heal any inconsistent types as they should already have been heald
3634
tree match {
3735
case PackageDef(pid, _) if tree.symbol.owner == defn.RootClass =>
38-
val checker = new PCPCheckAndHeal(freshStagingContext) {
36+
val checker = new CrossStageSafety(freshStagingContext) {
3937
override protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos)(using Context): TypeRef = {
4038
def symStr =
4139
if (sym.is(ModuleClass)) sym.sourceModule.show
@@ -70,7 +68,7 @@ class Staging extends MacroTransform {
7068

7169
protected def newTransformer(using Context): Transformer = new Transformer {
7270
override def transform(tree: tpd.Tree)(using Context): tpd.Tree =
73-
new PCPCheckAndHeal(ctx).transform(tree)
71+
new CrossStageSafety(ctx).transform(tree)
7472
}
7573
}
7674

compiler/src/dotty/tools/dotc/transform/TreeChecker.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ object TreeChecker {
513513
val inliningPhase = ctx.base.inliningPhase
514514
inliningPhase.exists && ctx.phase.id > inliningPhase.id
515515
if isAfterInlining then
516-
// The staging phase destroys in PCPCheckAndHeal the property that
516+
// The staging phase destroys in CrossStageSafety the property that
517517
// tree.expr.tpe <:< pt1. A test case where this arises is run-macros/enum-nat-macro.
518518
// We should follow up why this happens. If the problem is fixed, we can
519519
// drop the isAfterInlining special case. To reproduce the problem, just

tests/pos-with-compiler-cc/dotc/transform/PCPCheckAndHeal.scala renamed to tests/pos-with-compiler-cc/dotc/transform/CrossStageSafety.scala

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ import dotty.tools.dotc.util.Property
2222

2323
import scala.annotation.constructorOnly
2424

25-
/** Checks that the Phase Consistency Principle (PCP) holds and heals types.
25+
/** Checks that staging level consistency holds and heals staged types .
2626
*
27-
* Local term references are phase consistent if and only if they are used at the same level as their definition.
27+
* Local term references are level consistent if and only if they are used at the same level as their definition.
2828
*
2929
* Local type references can be used at the level of their definition or lower. If used used at a higher level,
3030
* it will be healed if possible, otherwise it is inconsistent.
3131
*
32-
* Type healing consists in transforming a phase inconsistent type `T` into `summon[Type[T]].Underlying`.
32+
* Type healing consists in transforming a level inconsistent type `T` into `summon[Type[T]].Underlying`.
3333
*
3434
* As references to types do not necessarily have an associated tree it is not always possible to replace the types directly.
3535
* Instead we always generate a type alias for it and place it at the start of the surrounding quote. This also avoids duplication.
@@ -48,7 +48,7 @@ import scala.annotation.constructorOnly
4848
* }
4949
*
5050
*/
51-
class PCPCheckAndHeal(@constructorOnly ictx: DetachedContext) extends TreeMapWithStages(ictx), Checking, caps.Pure {
51+
class CrossStageSafety(@constructorOnly ictx: DetachedContext) extends TreeMapWithStages(ictx), Checking, caps.Pure {
5252
import tpd._
5353

5454
private val InAnnotation = Property.Key[Unit]()
@@ -96,9 +96,9 @@ class PCPCheckAndHeal(@constructorOnly ictx: DetachedContext) extends TreeMapWit
9696
super.transform(tree)
9797
}
9898

99-
/** Transform quoted trees while maintaining phase correctness */
99+
/** Transform quoted trees while maintaining level correctness */
100100
override protected def transformQuotation(body: Tree, quote: Apply)(using Context): Tree = {
101-
val taggedTypes = new PCPCheckAndHeal.QuoteTypeTags(quote.span)
101+
val taggedTypes = new CrossStageSafety.QuoteTypeTags(quote.span)
102102

103103
if (ctx.property(InAnnotation).isDefined)
104104
report.error("Cannot have a quote in an annotation", quote.srcPos)
@@ -215,7 +215,7 @@ class PCPCheckAndHeal(@constructorOnly ictx: DetachedContext) extends TreeMapWit
215215
mapOver(tp)
216216
}
217217

218-
/** Check phase consistency of terms and heal inconsistent type references. */
218+
/** Check level consistency of terms and heal inconsistent type references. */
219219
private def healTypeOfTerm(pos: SrcPos)(using Context) = new TypeMap {
220220
def apply(tp: Type): Type =
221221
tp match
@@ -275,7 +275,7 @@ class PCPCheckAndHeal(@constructorOnly ictx: DetachedContext) extends TreeMapWit
275275

276276
}
277277

278-
object PCPCheckAndHeal {
278+
object CrossStageSafety {
279279
import tpd._
280280

281281
class QuoteTypeTags(span: Span)(using DetachedContext) extends caps.Pure {

0 commit comments

Comments
 (0)