Skip to content

Commit 65ac9f3

Browse files
committed
Fix quotes with references to path dependent types
1 parent f1bc0fb commit 65ac9f3

File tree

5 files changed

+125
-25
lines changed

5 files changed

+125
-25
lines changed

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

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -30,38 +30,41 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap {
3030
*/
3131
def apply(tp: Type): Type =
3232
tp match
33-
case tp: TypeRef =>
34-
healTypeRef(tp)
35-
case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level > levelOf(tp.symbol) =>
36-
levelError(tp.symbol, tp, pos)
33+
case tp @ TypeRef(NoPrefix, _) if tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) =>
34+
tp
35+
case tp @ TypeRef(prefix: TermRef, _) if tp.symbol.isTypeSplice && level > 0 =>
36+
checkNotWildcardSplice(tp)
37+
getQuoteTypeTags.getTagRef(prefix)
38+
case tp @ TypeRef(_: NamedType | _: ThisType | NoPrefix, _) =>
39+
if levelInconsistentRootOfPath(tp).exists then
40+
val tp1 = tp.dealias
41+
if tp1 != tp then apply(tp1)
42+
else tryHeal(tp.symbol, tp, pos)
43+
else
44+
tp
45+
case tp: TermRef =>
46+
val inconsistentRoot = levelInconsistentRootOfPath(tp)
47+
if inconsistentRoot.exists then levelError(inconsistentRoot, tp, pos)
48+
else tp
3749
case tp: AnnotatedType =>
3850
derivedAnnotatedType(tp, apply(tp.parent), tp.annot)
3951
case _ =>
4052
mapOver(tp)
4153

42-
private def healTypeRef(tp: TypeRef): Type =
43-
tp.prefix match
44-
case prefix: TermRef if tp.symbol.isTypeSplice =>
45-
checkNotWildcardSplice(tp)
46-
if level == 0 then tp else getQuoteTypeTags.getTagRef(prefix)
47-
case prefix: TermRef if !prefix.symbol.isStatic && level > levelOf(prefix.symbol) =>
48-
dealiasAndTryHeal(prefix.symbol, tp, pos)
49-
case NoPrefix if level > levelOf(tp.symbol) && !tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) =>
50-
dealiasAndTryHeal(tp.symbol, tp, pos)
51-
case prefix: ThisType if level > levelOf(prefix.cls) && !tp.symbol.isStatic =>
52-
dealiasAndTryHeal(tp.symbol, tp, pos)
53-
case _ =>
54-
mapOver(tp)
55-
5654
private def checkNotWildcardSplice(splice: TypeRef): Unit =
5755
splice.prefix.termSymbol.info.argInfos match
5856
case (tb: TypeBounds) :: _ => report.error(em"Cannot splice $splice because it is a wildcard type", pos)
5957
case _ =>
6058

61-
private def dealiasAndTryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos): Type =
62-
val tp1 = tp.dealias
63-
if tp1 != tp then apply(tp1)
64-
else tryHeal(tp.symbol, tp, pos)
59+
/** Return the root of this path if it is a variable defined in a previous level.
60+
* If the path is consistent, return NoSymbol.
61+
*/
62+
private def levelInconsistentRootOfPath(tp: Type)(using Context): Symbol =
63+
tp match
64+
case tp @ NamedType(NoPrefix, _) if level > levelOf(tp.symbol) => tp.symbol
65+
case tp: NamedType if !tp.symbol.isStatic => levelInconsistentRootOfPath(tp.prefix)
66+
case tp: ThisType if level > levelOf(tp.cls) => tp.cls
67+
case _ => NoSymbol
6568

6669
/** Try to heal reference to type `T` used in a higher level than its definition.
6770
* Returns a reference to a type tag generated by `QuoteTypeTags` that contains a

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,13 +157,18 @@ class PickleQuotes extends MacroTransform {
157157
override def apply(tp: Type): Type = tp match
158158
case tp: TypeRef if tp.typeSymbol.isTypeSplice =>
159159
apply(tp.dealias)
160-
case tp @ TypeRef(pre, _) if pre == NoPrefix || pre.termSymbol.isLocal =>
160+
case tp @ TypeRef(pre, _) if pre == NoPrefix || isLocalPath(pre) =>
161161
val hiBound = tp.typeSymbol.info match
162162
case info: ClassInfo => info.parents.reduce(_ & _)
163163
case info => info.hiBound
164164
apply(hiBound)
165165
case tp =>
166166
mapOver(tp)
167+
168+
private def isLocalPath(tp: Type): Boolean = tp match
169+
case TermRef(NoPrefix, _) => true
170+
case tp @ TermRef(pre, _) if !tp.symbol.is(Package) => isLocalPath(pre)
171+
case _ => false
167172
}
168173

169174
/** Remove references to local types that will not be defined in this quote */

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ class Splicing extends MacroTransform:
224224
// Dealias references to captured types
225225
TypeTree(tree.tpe.dealias)
226226
else super.transform(tree)
227-
case tree: TypeTree =>
227+
case _: TypeTree | _: SingletonTypeTree =>
228228
if containsCapturedType(tree.tpe) && level >= 1 then getTagRefFor(tree)
229229
else tree
230230
case tree @ Assign(lhs: RefTree, rhs) =>
@@ -355,7 +355,7 @@ class Splicing extends MacroTransform:
355355
private def capturedType(tree: Tree)(using Context): Symbol =
356356
val tpe = tree.tpe.widenTermRefExpr
357357
val bindingSym = refBindingMap
358-
.getOrElseUpdate(tree.symbol, (TypeTree(tree.tpe), newQuotedTypeClassBinding(tpe)))._2
358+
.getOrElseUpdate(tree.symbol, (TypeTree(tpe), newQuotedTypeClassBinding(tpe)))._2
359359
bindingSym
360360

361361
private def capturedPartTypes(tpt: Tree)(using Context): Tree =
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import scala.quoted.*
2+
3+
trait A:
4+
type T
5+
val b: B
6+
7+
trait B:
8+
type T
9+
def f: Unit
10+
11+
trait C0:
12+
type U
13+
val d: D0
14+
trait D0:
15+
type U
16+
def h: Unit
17+
object Macro:
18+
inline def generateCode: Unit = ${ generateCodeExpr }
19+
20+
def generateCodeExpr(using Quotes): Expr[Unit] =
21+
'{
22+
$testLocalPathsGlobalClasses
23+
$testLocalPathsLocalClasses
24+
// $testThisPaths // FIXME java.lang.ClassCastException: class scala.quoted.runtime.impl.ExprImpl cannot be cast to class scala.quoted.Type
25+
}
26+
27+
def testLocalPathsGlobalClasses(using Quotes): Expr[Unit] =
28+
'{
29+
type T
30+
val a: A = ???
31+
${
32+
val expr = '{
33+
val t: T = ???
34+
val aT: a.T = ???
35+
val abT: a.b.T = ???
36+
val aRef: a.type = ???
37+
aRef.b
38+
aRef.b.f
39+
val abRef: a.b.type = ???
40+
abRef.f
41+
}
42+
expr
43+
}
44+
}
45+
46+
def testLocalPathsLocalClasses(using Quotes): Expr[Unit] =
47+
'{
48+
type U
49+
trait C extends C0:
50+
type U
51+
val d: D
52+
trait D extends D0:
53+
type U
54+
def h: Unit
55+
val c: C = ???
56+
${
57+
val expr = '{
58+
val u: U = ???
59+
val cU: c.U = ???
60+
val cdU: c.d.U = ???
61+
val cRef: c.type = ???
62+
// cRef.d // FIXME
63+
// cRef.d.h // FIXME
64+
val cdRef: c.d.type = ???
65+
// cdRef.h // FIXME
66+
}
67+
expr
68+
}
69+
}
70+
71+
// def testThisPaths(using Quotes): Expr[Unit] =
72+
// '{
73+
// trait E:
74+
// type V
75+
// val f: F
76+
// ${
77+
// val expr = '{
78+
// val _: Any = this
79+
// val _: Any = f
80+
// val _: this.type = ???
81+
// val _: V = ???
82+
// val _: this.V = ???
83+
// val _: this.f.V = ???
84+
// val _: this.type = ???
85+
// val _: this.f.type = ???
86+
// }
87+
// expr
88+
// }
89+
// trait F:
90+
// type V
91+
// }
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@main def test = Macro.generateCode

0 commit comments

Comments
 (0)