Skip to content

Commit f092f78

Browse files
committed
Refactor level checking / type healing logic
Disentangle level checking from type healing.
1 parent 28676ae commit f092f78

File tree

3 files changed

+50
-52
lines changed

3 files changed

+50
-52
lines changed

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

Lines changed: 48 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -61,24 +61,26 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
6161
super.transform(tree)
6262
else tree match {
6363

64-
case _: TypeTree | _: RefTree if tree.isType =>
64+
case _: TypeTree | _: RefTree if tree.isType =>
6565
val healedType = healType(tree.srcPos)(tree.tpe)
6666
if healedType == tree.tpe then tree
6767
else TypeTree(healedType).withSpan(tree.span)
68+
case tree: Ident if isWildcardArg(tree) =>
69+
tree.withType(healType(tree.srcPos)(tree.tpe))
70+
case tree: Ident => // this is a term Ident
71+
checkLevelConsistency(tree)
72+
tree
73+
case tree: This =>
74+
checkLevelConsistency(tree)
75+
tree
6876
case _: AppliedTypeTree =>
6977
super.transform(tree) match
7078
case tree1: AppliedTypeTree if tree1 ne tree =>
7179
// propagate healed types
7280
tree1.withType(tree1.tpt.tpe.appliedTo(tree1.args.map(_.tpe)))
7381
case tree1 => tree1
74-
75-
case _: Ident | _: This =>
76-
tree.withType(healTypeOfTerm(tree.srcPos)(tree.tpe))
77-
78-
// Remove inline defs in quoted code. Already fully inlined.
7982
case tree: DefDef if tree.symbol.is(Inline) && level > 0 =>
80-
EmptyTree
81-
83+
EmptyTree // Remove inline defs in quoted code. Already fully inlined.
8284
case tree: ValOrDefDef =>
8385
checkAnnotations(tree)
8486
healInfo(tree, tree.tpt.srcPos)
@@ -88,7 +90,7 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
8890
healInfo(tree, tree.srcPos)
8991
super.transform(tree)
9092
case tree: UnApply =>
91-
super.transform(tree).withType(healTypeOfTerm(tree.srcPos)(tree.tpe))
93+
super.transform(tree).withType(healType(tree.srcPos)(tree.tpe))
9294
case tree: TypeDef if tree.symbol.is(Case) && level > 0 =>
9395
report.error(reporting.CaseClassInInlinedCode(tree), tree)
9496
super.transform(tree)
@@ -115,7 +117,7 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
115117
if body.isTerm then
116118
// `quoted.runtime.Expr.quote[T](<body>)` --> `quoted.runtime.Expr.quote[T2](<body2>)`
117119
val TypeApply(fun, targs) = quote.fun: @unchecked
118-
val targs2 = targs.map(targ => TypeTree(healTypeOfTerm(quote.fun.srcPos)(targ.tpe)))
120+
val targs2 = targs.map(targ => TypeTree(healType(quote.fun.srcPos)(targ.tpe)))
119121
cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, targs2), body2 :: Nil)
120122
else
121123
val quotes = quote.args.mapConserve(transform)
@@ -190,61 +192,57 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
190192
def apply(tp: Type): Type =
191193
tp match
192194
case tp: TypeRef =>
193-
tp.prefix match
194-
case NoPrefix if level > levelOf(tp.symbol) && !tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) =>
195-
tryHealTypeOfType(tp.symbol, tp, pos)
196-
case prefix: ThisType if !tp.symbol.isStatic && level > levelOf(prefix.cls) =>
197-
tryHealTypeOfType(tp.symbol, tp, pos)
198-
case prefix: TermRef if tp.symbol.isTypeSplice =>
199-
prefix.symbol.info.argInfos match
200-
case (tb: TypeBounds) :: _ =>
201-
report.error(em"Cannot splice $tp because it is a wildcard type", pos)
202-
case _ =>
203-
// Heal explicit type splice in the code
204-
if level > 0 then getQuoteTypeTags.getTagRef(prefix) else tp
205-
case prefix: TermRef if !prefix.symbol.isStatic && level > levelOf(prefix.symbol) =>
206-
tryHealTypeOfType(prefix.symbol, tp, pos)
207-
case _ =>
208-
mapOver(tp)
195+
healTypeRef(tp)
209196
case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level > levelOf(tp.symbol) =>
210197
levelError(tp.symbol, tp, pos)
211-
case tp: ThisType if level != -1 && level != levelOf(tp.cls) =>
212-
levelError(tp.cls, tp, pos)
213198
case tp: AnnotatedType =>
214199
val newAnnotTree = transform(tp.annot.tree)
215200
derivedAnnotatedType(tp, apply(tp.parent), tp.annot.derivedAnnotation(newAnnotTree))
216201
case _ =>
217202
mapOver(tp)
218203

219-
/** Try to dealias or heal reference to type `T` used in a higher level than its definition.
220-
* Returns a reference to a type tag generated by `QuoteTypeTags` that contains a
221-
* reference to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`.
222-
* Emits and error if `T` cannot be healed and returns `T`.
223-
*/
224-
private def tryHealTypeOfType(sym: Symbol, tp: TypeRef, pos: SrcPos)(using Context): Type = {
204+
private def healTypeRef(tp: TypeRef): Type =
205+
tp.prefix match
206+
case prefix: TermRef if tp.symbol.isTypeSplice =>
207+
checkNotWildcardSplice(tp)
208+
if level == 0 then tp else getQuoteTypeTags.getTagRef(prefix)
209+
case prefix: TermRef if !prefix.symbol.isStatic && level > levelOf(prefix.symbol) =>
210+
dealiasAndTryHeal(prefix.symbol, tp, pos)
211+
case NoPrefix if level > levelOf(tp.symbol) && !tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) =>
212+
dealiasAndTryHeal(tp.symbol, tp, pos)
213+
case prefix: ThisType if level > levelOf(prefix.cls) && !tp.symbol.isStatic =>
214+
dealiasAndTryHeal(tp.symbol, tp, pos)
215+
case _ =>
216+
mapOver(tp)
217+
218+
private def dealiasAndTryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos)(using Context): Type =
225219
val tp1 = tp.dealias
226220
if tp1 != tp then apply(tp1)
227221
else tryHeal(tp.symbol, tp, pos)
228-
}
229-
}
230222

231-
/** Check phase consistency of terms and heal inconsistent type references. */
232-
private def healTypeOfTerm(pos: SrcPos)(using Context) = new TypeMap {
233-
def apply(tp: Type): Type =
234-
tp match
235-
case tp @ TypeRef(NoPrefix, _) if level > levelOf(tp.symbol) =>
236-
tryHeal(tp.symbol, tp, pos)
237-
case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level != levelOf(tp.symbol) =>
238-
levelError(tp.symbol, tp, pos)
239-
case tp: ThisType if level != -1 && level != levelOf(tp.cls) =>
240-
levelError(tp.cls, tp, pos)
241-
case tp: AnnotatedType =>
242-
derivedAnnotatedType(tp, apply(tp.parent), tp.annot)
223+
private def checkNotWildcardSplice(splice: TypeRef)(using Context): Unit =
224+
splice.prefix.termSymbol.info.argInfos match
225+
case (tb: TypeBounds) :: _ => report.error(em"Cannot splice $splice because it is a wildcard type", pos)
243226
case _ =>
244-
if tp.typeSymbol.is(Package) then tp
245-
else mapOver(tp)
246227
}
247228

229+
/** Check level consistency of terms references */
230+
private def checkLevelConsistency(tree: Ident | This)(using Context): Unit =
231+
new TypeTraverser {
232+
def traverse(tp: Type): Unit =
233+
tp match
234+
case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level != levelOf(tp.symbol) =>
235+
levelError(tp.symbol, tp, tree.srcPos)
236+
case tp: ThisType if level != -1 && level != levelOf(tp.cls) =>
237+
levelError(tp.cls, tp, tree.srcPos)
238+
case tp: AnnotatedType =>
239+
traverse(tp.parent)
240+
case _ if tp.typeSymbol.is(Package) =>
241+
// OK
242+
case _ =>
243+
traverseChildren(tp)
244+
}.traverse(tree.tpe)
245+
248246
/** Try to heal reference to type `T` used in a higher level than its definition.
249247
* Returns a reference to a type tag generated by `QuoteTypeTags` that contains a
250248
* reference to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,7 @@ object TreeChecker {
678678
defn.FunctionOf(List(defn.QuotesClass.typeRef), expectedResultType, isContextual = true)
679679
val expectedContentType =
680680
defn.FunctionOf(argQuotedTypes, contextualResult)
681-
assert(content.typeOpt =:= expectedContentType)
681+
assert(content.typeOpt =:= expectedContentType, i"expected content of the hole to be ${expectedContentType} but got ${content.typeOpt}")
682682

683683
tree1
684684
}

tests/pos-macros/i7519b.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ inline def quote[T]: Quoted[T] = ${ quoteImpl[T] }
99

1010
def quoteImpl[T: Type](using Quotes): Expr[Quoted[T]] = {
1111
val value: Expr[Int] = '{ 42 }
12-
'{ new Quoted[T @Annot($value)] }
12+
'{ new Quoted[T @Annot($value)]: Quoted[T] } // ascription is added to ensure that the splice of the annotation does not leak outside of the quote
1313
}

0 commit comments

Comments
 (0)