Skip to content

Commit 915c44c

Browse files
committed
Extract HealType from PCPCheckAndHeal
1 parent f32e204 commit 915c44c

File tree

4 files changed

+125
-89
lines changed

4 files changed

+125
-89
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package dotty.tools.dotc
2+
package staging
3+
4+
import dotty.tools.dotc.core.Contexts._
5+
import dotty.tools.dotc.core.Decorators._
6+
import dotty.tools.dotc.core.Flags._
7+
import dotty.tools.dotc.core.StagingContext._
8+
import dotty.tools.dotc.core.Symbols._
9+
import dotty.tools.dotc.core.Types._
10+
import dotty.tools.dotc.staging.StagingLevel.*
11+
import dotty.tools.dotc.transform.SymUtils._
12+
import dotty.tools.dotc.typer.Implicits.SearchFailureType
13+
import dotty.tools.dotc.util.SrcPos
14+
15+
class HealType(pos: SrcPos)(using Context) extends TypeMap {
16+
17+
/** If the type refers to a locally defined symbol (either directly, or in a pickled type),
18+
* check that its staging level matches the current level.
19+
* - Static types and term are allowed at any level.
20+
* - If a type reference is used a higher level, then it is inconsistent.
21+
* Will attempt to heal before failing.
22+
* - If a term reference is used a higher level, then it is inconsistent.
23+
* It cannot be healed because the term will not exist in any future stage.
24+
*
25+
* If `T` is a reference to a type at the wrong level, try to heal it by replacing it with
26+
* a type tag of type `quoted.Type[T]`.
27+
* The tag is generated by an instance of `QuoteTypeTags` directly if the splice is explicit
28+
* or indirectly by `tryHeal`.
29+
*/
30+
def apply(tp: Type): Type =
31+
tp match
32+
case tp: TypeRef =>
33+
healTypeRef(tp)
34+
case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level > levelOf(tp.symbol) =>
35+
levelError(tp.symbol, tp, pos)
36+
case tp: AnnotatedType =>
37+
derivedAnnotatedType(tp, apply(tp.parent), tp.annot)
38+
case _ =>
39+
mapOver(tp)
40+
41+
private def healTypeRef(tp: TypeRef): Type =
42+
tp.prefix match
43+
case prefix: TermRef if tp.symbol.isTypeSplice =>
44+
checkNotWildcardSplice(tp)
45+
if level == 0 then tp else getQuoteTypeTags.getTagRef(prefix)
46+
case prefix: TermRef if !prefix.symbol.isStatic && level > levelOf(prefix.symbol) =>
47+
dealiasAndTryHeal(prefix.symbol, tp, pos)
48+
case NoPrefix if level > levelOf(tp.symbol) && !tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) =>
49+
dealiasAndTryHeal(tp.symbol, tp, pos)
50+
case prefix: ThisType if level > levelOf(prefix.cls) && !tp.symbol.isStatic =>
51+
dealiasAndTryHeal(tp.symbol, tp, pos)
52+
case _ =>
53+
mapOver(tp)
54+
55+
private def checkNotWildcardSplice(splice: TypeRef): Unit =
56+
splice.prefix.termSymbol.info.argInfos match
57+
case (tb: TypeBounds) :: _ => report.error(em"Cannot splice $splice because it is a wildcard type", pos)
58+
case _ =>
59+
60+
private def dealiasAndTryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos): Type =
61+
val tp1 = tp.dealias
62+
if tp1 != tp then apply(tp1)
63+
else tryHeal(tp.symbol, tp, pos)
64+
65+
/** Try to heal reference to type `T` used in a higher level than its definition.
66+
* Returns a reference to a type tag generated by `QuoteTypeTags` that contains a
67+
* reference to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`.
68+
* Emits and error if `T` cannot be healed and returns `T`.
69+
*/
70+
protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos): TypeRef = {
71+
val reqType = defn.QuotedTypeClass.typeRef.appliedTo(tp)
72+
val tag = ctx.typer.inferImplicitArg(reqType, pos.span)
73+
tag.tpe match
74+
case tp: TermRef =>
75+
ctx.typer.checkStable(tp, pos, "type witness")
76+
getQuoteTypeTags.getTagRef(tp)
77+
case _: SearchFailureType =>
78+
report.error(
79+
ctx.typer.missingArgMsg(tag, reqType, "")
80+
.prepend(i"Reference to $tp within quotes requires a given $reqType in scope.\n")
81+
.append("\n"),
82+
pos)
83+
tp
84+
case _ =>
85+
report.error(em"""Reference to $tp within quotes requires a given $reqType in scope.
86+
|
87+
|""", pos)
88+
tp
89+
}
90+
91+
private def levelError(sym: Symbol, tp: Type, pos: SrcPos): tp.type = {
92+
report.error(
93+
em"""access to $sym from wrong staging level:
94+
| - the definition is at level ${levelOf(sym)},
95+
| - but the access is at level $level""", pos)
96+
tp
97+
}
98+
}

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

Lines changed: 5 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package dotty.tools.dotc
22
package staging
33

44
import dotty.tools.dotc.ast.{tpd, untpd}
5-
import dotty.tools.dotc.core.Annotations.BodyAnnotation
5+
import dotty.tools.dotc.core.Annotations._
66
import dotty.tools.dotc.core.Contexts._
77
import dotty.tools.dotc.core.Decorators._
88
import dotty.tools.dotc.core.Flags._
@@ -11,15 +11,10 @@ import dotty.tools.dotc.core.StagingContext._
1111
import dotty.tools.dotc.core.StdNames._
1212
import dotty.tools.dotc.core.Symbols._
1313
import dotty.tools.dotc.core.Types._
14-
import dotty.tools.dotc.util.SrcPos
15-
import dotty.tools.dotc.util.Spans._
16-
import dotty.tools.dotc.transform.SymUtils._
17-
import dotty.tools.dotc.typer.Checking
18-
import dotty.tools.dotc.typer.Implicits.SearchFailureType
19-
import dotty.tools.dotc.core.Annotations._
2014
import dotty.tools.dotc.staging.StagingLevel.*
21-
2215
import dotty.tools.dotc.util.Property
16+
import dotty.tools.dotc.util.Spans._
17+
import dotty.tools.dotc.util.SrcPos
2318

2419
/** Checks that the Phase Consistency Principle (PCP) holds and heals types.
2520
*
@@ -203,42 +198,8 @@ class PCPCheckAndHeal extends TreeMapWithStages {
203198
* The tag is generated by an instance of `QuoteTypeTags` directly if the splice is explicit
204199
* or indirectly by `tryHeal`.
205200
*/
206-
private def healType(pos: SrcPos)(using Context) = new TypeMap {
207-
def apply(tp: Type): Type =
208-
tp match
209-
case tp: TypeRef =>
210-
healTypeRef(tp)
211-
case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level > levelOf(tp.symbol) =>
212-
levelError(tp.symbol, tp, pos)
213-
case tp: AnnotatedType =>
214-
derivedAnnotatedType(tp, apply(tp.parent), tp.annot)
215-
case _ =>
216-
mapOver(tp)
217-
218-
private def healTypeRef(tp: TypeRef): Type =
219-
tp.prefix match
220-
case prefix: TermRef if tp.symbol.isTypeSplice =>
221-
checkNotWildcardSplice(tp)
222-
if level == 0 then tp else getQuoteTypeTags.getTagRef(prefix)
223-
case prefix: TermRef if !prefix.symbol.isStatic && level > levelOf(prefix.symbol) =>
224-
dealiasAndTryHeal(prefix.symbol, tp, pos)
225-
case NoPrefix if level > levelOf(tp.symbol) && !tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) =>
226-
dealiasAndTryHeal(tp.symbol, tp, pos)
227-
case prefix: ThisType if level > levelOf(prefix.cls) && !tp.symbol.isStatic =>
228-
dealiasAndTryHeal(tp.symbol, tp, pos)
229-
case _ =>
230-
mapOver(tp)
231-
232-
private def dealiasAndTryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos)(using Context): Type =
233-
val tp1 = tp.dealias
234-
if tp1 != tp then apply(tp1)
235-
else tryHeal(tp.symbol, tp, pos)
236-
237-
private def checkNotWildcardSplice(splice: TypeRef)(using Context): Unit =
238-
splice.prefix.termSymbol.info.argInfos match
239-
case (tb: TypeBounds) :: _ => report.error(em"Cannot splice $splice because it is a wildcard type", pos)
240-
case _ =>
241-
}
201+
protected def healType(pos: SrcPos)(using Context) =
202+
new HealType(pos)
242203

243204
/** Check level consistency of terms references */
244205
private def checkLevelConsistency(tree: Ident | This)(using Context): Unit =
@@ -257,33 +218,6 @@ class PCPCheckAndHeal extends TreeMapWithStages {
257218
traverseChildren(tp)
258219
}.traverse(tree.tpe)
259220

260-
/** Try to heal reference to type `T` used in a higher level than its definition.
261-
* Returns a reference to a type tag generated by `QuoteTypeTags` that contains a
262-
* reference to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`.
263-
* Emits and error if `T` cannot be healed and returns `T`.
264-
*/
265-
protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos)(using Context): TypeRef = {
266-
val reqType = defn.QuotedTypeClass.typeRef.appliedTo(tp)
267-
val tag = ctx.typer.inferImplicitArg(reqType, pos.span)
268-
tag.tpe match
269-
270-
case tp: TermRef =>
271-
ctx.typer.checkStable(tp, pos, "type witness")
272-
getQuoteTypeTags.getTagRef(tp)
273-
case _: SearchFailureType =>
274-
report.error(
275-
ctx.typer.missingArgMsg(tag, reqType, "")
276-
.prepend(i"Reference to $tp within quotes requires a given $reqType in scope.\n")
277-
.append("\n"),
278-
pos)
279-
tp
280-
case _ =>
281-
report.error(em"""Reference to $tp within quotes requires a given $reqType in scope.
282-
|
283-
|""", pos)
284-
tp
285-
}
286-
287221
private def levelError(sym: Symbol, tp: Type, pos: SrcPos)(using Context): tp.type = {
288222
def symStr =
289223
if (!tp.isInstanceOf[ThisType]) sym.show
@@ -301,7 +235,6 @@ class PCPCheckAndHeal extends TreeMapWithStages {
301235
| - but the access is at level $level.$hint""", pos)
302236
tp
303237
}
304-
305238
}
306239

307240
object PCPCheckAndHeal {

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
package dotty.tools.dotc.staging
1+
package dotty.tools.dotc
2+
package staging
23

3-
import dotty.tools.dotc.ast.{TreeMapWithImplicits, tpd}
4-
import dotty.tools.dotc.config.Printers.staging
5-
import dotty.tools.dotc.core.Decorators._
64
import dotty.tools.dotc.core.Contexts._
5+
import dotty.tools.dotc.core.Decorators._
6+
import dotty.tools.dotc.core.Flags._
77
import dotty.tools.dotc.core.StagingContext._
88
import dotty.tools.dotc.core.Symbols._
9+
import dotty.tools.dotc.core.Types._
910
import dotty.tools.dotc.util.Property
11+
import dotty.tools.dotc.util.SrcPos
1012

1113
import scala.collection.mutable
1214

@@ -29,4 +31,4 @@ object StagingLevel {
2931
val syms1 = syms//.filter(sym => !levelOfMap.contains(sym))
3032
val newMap = syms1.foldLeft(levelOfMap)((acc, sym) => acc.updated(sym, level))
3133
ctx.fresh.setProperty(LevelOfKey, newMap)
32-
}
34+
}

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

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import dotty.tools.dotc.util.SrcPos
1313
import dotty.tools.dotc.transform.SymUtils._
1414
import dotty.tools.dotc.staging.StagingLevel.*
1515
import dotty.tools.dotc.staging.PCPCheckAndHeal
16+
import dotty.tools.dotc.staging.HealType
1617

1718
/** Checks that the Phase Consistency Principle (PCP) holds and heals types.
1819
*
@@ -35,19 +36,21 @@ class Staging extends MacroTransform {
3536
tree match {
3637
case PackageDef(pid, _) if tree.symbol.owner == defn.RootClass =>
3738
val checker = new PCPCheckAndHeal {
38-
override protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos)(using Context): TypeRef = {
39-
def symStr =
40-
if (sym.is(ModuleClass)) sym.sourceModule.show
41-
else i"${sym.name}.this"
42-
val errMsg = s"\nin ${ctx.owner.fullName}"
43-
assert(
44-
ctx.owner.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) ||
45-
(sym.isType && levelOf(sym) > 0),
46-
em"""access to $symStr from wrong staging level:
47-
| - the definition is at level ${levelOf(sym)},
48-
| - but the access is at level $level.$errMsg""")
39+
override protected def healType(pos: SrcPos)(using Context) = new HealType(pos) {
40+
override protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos): TypeRef = {
41+
def symStr =
42+
if (sym.is(ModuleClass)) sym.sourceModule.show
43+
else i"${sym.name}.this"
44+
val errMsg = s"\nin ${ctx.owner.fullName}"
45+
assert(
46+
ctx.owner.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) ||
47+
(sym.isType && levelOf(sym) > 0),
48+
em"""access to $symStr from wrong staging level:
49+
| - the definition is at level ${levelOf(sym)},
50+
| - but the access is at level $level.$errMsg""")
4951

50-
tp
52+
tp
53+
}
5154
}
5255
}
5356
checker.transform(tree)

0 commit comments

Comments
 (0)