Skip to content

Commit 4372523

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 f1bc0fb commit 4372523

File tree

13 files changed

+40
-39
lines changed

13 files changed

+40
-39
lines changed

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import inlines.Inlines
1818
import NameOps._
1919
import Annotations._
2020
import transform.{AccessProxies, Splicer}
21-
import staging.PCPCheckAndHeal
21+
import staging.CrossStageSafety
2222
import transform.SymUtils.*
2323
import config.Printers.inlining
2424
import util.Property
@@ -294,7 +294,7 @@ object PrepareInlineable {
294294
if (code.symbol.flags.is(Inline))
295295
report.error("Macro cannot be implemented with an `inline` method", code.srcPos)
296296
Splicer.checkValidMacroBody(code)
297-
(new PCPCheckAndHeal).transform(body) // Ignore output, only check PCP
297+
(new CrossStageSafety).transform(body) // Ignore output, only check cross-stage safety
298298
case Block(List(stat), Literal(Constants.Constant(()))) => checkMacro(stat)
299299
case Block(Nil, expr) => checkMacro(expr)
300300
case Typed(expr, _) => checkMacro(expr)

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

+5-5
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ import dotty.tools.dotc.util.Property
1717
import dotty.tools.dotc.util.Spans._
1818
import dotty.tools.dotc.util.SrcPos
1919

20-
/** Checks that the Phase Consistency Principle (PCP) holds and heals types.
20+
/** Checks that staging level consistency holds and heals staged types .
2121
*
22-
* Local term references are phase consistent if and only if they are used at the same level as their definition.
22+
* Local term references are level consistent if and only if they are used at the same level as their definition.
2323
*
2424
* Local type references can be used at the level of their definition or lower. If used used at a higher level,
2525
* it will be healed if possible, otherwise it is inconsistent.
2626
*
27-
* Type healing consists in transforming a phase inconsistent type `T` into `summon[Type[T]].Underlying`.
27+
* Type healing consists in transforming a level inconsistent type `T` into `summon[Type[T]].Underlying`.
2828
*
2929
* As references to types do not necessarily have an associated tree it is not always possible to replace the types directly.
3030
* Instead we always generate a type alias for it and place it at the start of the surrounding quote. This also avoids duplication.
@@ -43,7 +43,7 @@ import dotty.tools.dotc.util.SrcPos
4343
* }
4444
*
4545
*/
46-
class PCPCheckAndHeal extends TreeMapWithStages {
46+
class CrossStageSafety extends TreeMapWithStages {
4747
import tpd._
4848

4949
private val InAnnotation = Property.Key[Unit]()
@@ -97,7 +97,7 @@ class PCPCheckAndHeal extends TreeMapWithStages {
9797
super.transform(tree)
9898
}
9999

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

compiler/src/dotty/tools/dotc/staging/QuoteContext.scala

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ 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.staging.PCPCheckAndHeal
87
import dotty.tools.dotc.staging.StagingLevel.*
98

109
object QuoteContext {

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

-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import dotty.tools.dotc.core.Names._
2121
import dotty.tools.dotc.core.StdNames._
2222
import dotty.tools.dotc.quoted._
2323
import dotty.tools.dotc.config.ScalaRelease.*
24-
import dotty.tools.dotc.staging.PCPCheckAndHeal
2524
import dotty.tools.dotc.staging.QuoteContext.*
2625
import dotty.tools.dotc.staging.StagingLevel.*
2726
import dotty.tools.dotc.staging.QuoteTypeTags

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ import dotty.tools.dotc.util.SrcPos
1212
import dotty.tools.dotc.transform.SymUtils._
1313
import dotty.tools.dotc.staging.QuoteContext.*
1414
import dotty.tools.dotc.staging.StagingLevel.*
15-
import dotty.tools.dotc.staging.PCPCheckAndHeal
15+
import dotty.tools.dotc.staging.CrossStageSafety
1616
import dotty.tools.dotc.staging.HealType
1717

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

3333
override def checkPostCondition(tree: Tree)(using Context): Unit =
3434
if (ctx.phase <= splicingPhase) {
35-
// Recheck that PCP holds but do not heal any inconsistent types as they should already have been heald
35+
// Recheck that staging level consistency holds but do not heal any inconsistent types as they should already have been heald
3636
tree match {
3737
case PackageDef(pid, _) if tree.symbol.owner == defn.RootClass =>
38-
val checker = new PCPCheckAndHeal {
38+
val checker = new CrossStageSafety {
3939
override protected def healType(pos: SrcPos)(using Context) = new HealType(pos) {
4040
override protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos): TypeRef = {
4141
def symStr =
@@ -72,7 +72,7 @@ class Staging extends MacroTransform {
7272

7373
protected def newTransformer(using Context): Transformer = new Transformer {
7474
override def transform(tree: tpd.Tree)(using Context): tpd.Tree =
75-
(new PCPCheckAndHeal).transform(tree)
75+
(new CrossStageSafety).transform(tree)
7676
}
7777
}
7878

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

+1-1
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

docs/_docs/internals/overall-structure.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,17 @@ phases. The current list of phases is specified in class [Compiler] as follows:
104104
List(new semanticdb.ExtractSemanticDB) :: // Extract info into .semanticdb files
105105
List(new PostTyper) :: // Additional checks and cleanups after type checking
106106
List(new sjs.PrepJSInterop) :: // Additional checks and transformations for Scala.js (Scala.js only)
107-
List(new Staging) :: // Check PCP, heal quoted types and expand macros
108107
List(new sbt.ExtractAPI) :: // Sends a representation of the API of classes to sbt via callbacks
109108
List(new SetRootTree) :: // Set the `rootTreeOrProvider` on class symbols
110109
Nil
111110

112111
/** Phases dealing with TASTY tree pickling and unpickling */
113112
protected def picklerPhases: List[List[Phase]] =
114113
List(new Pickler) :: // Generate TASTY info
114+
List(new Inlining) :: // Inline and execute macros
115+
List(new PostInlining) :: // Add mirror support for inlined code
116+
List(new Staging) :: // Check staging levels and heal staged types
117+
List(new Splicing) :: // Replace level 1 splices with holes
115118
List(new PickleQuotes) :: // Turn quoted trees into explicit run-time data structures
116119
Nil
117120

tests/pos-with-compiler-cc/dotc/core/StagingContext.scala

+4-4
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.

tests/pos-with-compiler-cc/dotc/inlines/PrepareInlineable.scala

+2-2
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
@@ -294,7 +294,7 @@ object PrepareInlineable {
294294
if (code.symbol.flags.is(Inline))
295295
report.error("Macro cannot be implemented with an `inline` method", code.srcPos)
296296
Splicer.checkValidMacroBody(code)
297-
new PCPCheckAndHeal(freshStagingContext).transform(body) // Ignore output, only check PCP
297+
new CrossStageSafety(freshStagingContext).transform(body) // Ignore output, only check staging levels
298298
case Block(List(stat), Literal(Constants.Constant(()))) => checkMacro(stat)
299299
case Block(Nil, expr) => checkMacro(expr)
300300
case Typed(expr, _) => checkMacro(expr)

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

+8-8
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 {

tests/pos-with-compiler-cc/dotc/transform/Splicing.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ class Splicing extends MacroTransform:
191191
private var refBindingMap = mutable.Map.empty[Symbol, (Tree, Symbol)]
192192
/** Reference to the `Quotes` instance of the current level 1 splice */
193193
private var quotes: Tree | Null = null // TODO: add to the context
194-
private var healedTypes: PCPCheckAndHeal.QuoteTypeTags | Null = null // TODO: add to the context
194+
private var healedTypes: CrossStageSafety.QuoteTypeTags | Null = null // TODO: add to the context
195195

196196
def transformSplice(tree: tpd.Tree, tpe: Type, holeIdx: Int)(using Context): tpd.Tree =
197197
assert(level == 0)
@@ -254,7 +254,7 @@ class Splicing extends MacroTransform:
254254
private def transformLevel0QuoteContent(tree: Tree)(using Context): Tree =
255255
// transform and collect new healed types
256256
val old = healedTypes
257-
healedTypes = new PCPCheckAndHeal.QuoteTypeTags(tree.span)
257+
healedTypes = new CrossStageSafety.QuoteTypeTags(tree.span)
258258
val tree1 = transform(tree)
259259
val newHealedTypes = healedTypes.nn.getTypeTags
260260
healedTypes = old

tests/pos-with-compiler-cc/dotc/transform/Staging.scala

+5-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import dotty.tools.dotc.transform.TreeMapWithStages._
1515

1616

1717

18-
/** Checks that the Phase Consistency Principle (PCP) holds and heals types.
18+
/** Checks that staging level consistency holds and heals staged types.
1919
*
2020
* Type healing consists in transforming a phase inconsistent type `T` into `${ implicitly[Type[T]] }`.
2121
*/
@@ -32,12 +32,12 @@ class Staging extends MacroTransform {
3232

3333
override def checkPostCondition(tree: Tree)(using Context): Unit =
3434
if (ctx.phase <= splicingPhase) {
35-
// Recheck that PCP holds but do not heal any inconsistent types as they should already have been heald
35+
// Recheck that staging levels hold but do not heal any inconsistent types as they should already have been heald
3636
tree match {
3737
case PackageDef(pid, _) if tree.symbol.owner == defn.RootClass =>
3838
val stagingCtx = freshStagingContext
39-
val checker = new PCPCheckAndHeal(stagingCtx) {
40-
// !cc! type error is checker is defined as val checker = new PCPCheckAndHeal { ... }
39+
val checker = new CrossStageSafety(stagingCtx) {
40+
// !cc! type error is checker is defined as val checker = new CrossStageSafety { ... }
4141
override protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos)(using Context): TypeRef = {
4242
def symStr =
4343
if (sym.is(ModuleClass)) sym.sourceModule.show
@@ -72,7 +72,7 @@ class Staging extends MacroTransform {
7272

7373
protected def newTransformer(using Context): Transformer = new Transformer {
7474
override def transform(tree: tpd.Tree)(using Context): tpd.Tree =
75-
new PCPCheckAndHeal(ctx.detach).transform(tree)
75+
new CrossStageSafety(ctx.detach).transform(tree)
7676
}
7777
}
7878

tests/pos-with-compiler-cc/dotc/transform/TreeChecker.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ class TreeChecker extends Phase with SymTransformer {
458458
val inliningPhase = ctx.base.inliningPhase
459459
inliningPhase.exists && ctx.phase.id > inliningPhase.id
460460
if isAfterInlining then
461-
// The staging phase destroys in PCPCheckAndHeal the property that
461+
// The staging phase destroys in CrossStageSafety the property that
462462
// tree.expr.tpe <:< pt1. A test case where this arises is run-macros/enum-nat-macro.
463463
// We should follow up why this happens. If the problem is fixed, we can
464464
// drop the isAfterInlining special case. To reproduce the problem, just

0 commit comments

Comments
 (0)