Skip to content

Commit 49215aa

Browse files
Update macro docs (#17060)
Copy pasted and adapted a bit the contents of chapter 3 of [_Scalable Metaprogramming in Scala 3_](https://infoscience.epfl.ch/record/299370). The previous version was based on [_A practical unification of multi-stage programming and macros_](https://dl.acm.org/doi/10.1145/3278122.3278139). This version was extremely out of date. ##### Old nomenclature of cross-stage safty 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 level consistency 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.
2 parents 7226ba6 + 1dc02d1 commit 49215aa

File tree

19 files changed

+2174
-1681
lines changed

19 files changed

+2174
-1681
lines changed

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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 5 additions & 5 deletions
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

Lines changed: 0 additions & 1 deletion
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

Lines changed: 0 additions & 1 deletion
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

Lines changed: 6 additions & 6 deletions
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

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

docs/_docs/internals/overall-structure.md

Lines changed: 4 additions & 1 deletion
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

0 commit comments

Comments
 (0)