Skip to content

Commit dac0bdf

Browse files
committed
Dealias quoted types when staging
This change improves the code generation of the contents of splices. In the splicing phase, we have to find all types that are defined in the quote but used in the splice. When there are type aliases, we can end up with several `Type[T]` for the different aliases of `T`. By dealiasing during the staging phase (just before splicing phase) we make sure that the splicer phase will only generate one `Type[T]`. By dealiasing we also optimize some situations where a type from outside a quote is inserted in the quoted code and then used in one of its splice through an alias. In this situation we can use the outer `Type[T]` directly.
1 parent c466fa0 commit dac0bdf

File tree

7 files changed

+85
-28
lines changed

7 files changed

+85
-28
lines changed

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,54 +1374,57 @@ object Types {
13741374
case Atoms.Unknown => Atoms.Unknown
13751375
case _ => Atoms.Unknown
13761376

1377-
private def dealias1(keep: AnnotatedType => Context ?=> Boolean, keepOpaques: Boolean)(using Context): Type = this match {
1377+
private def dealias1(keep: AnnotatedType => Context ?=> Boolean, keepOpaques: Boolean, keepSplicedTypes: Boolean)(using Context): Type = this match {
13781378
case tp: TypeRef =>
13791379
if (tp.symbol.isClass) tp
13801380
else tp.info match {
1381-
case TypeAlias(alias) if !(keepOpaques && tp.symbol.is(Opaque)) =>
1382-
alias.dealias1(keep, keepOpaques)
1381+
case TypeAlias(alias) if !(keepOpaques && tp.symbol.is(Opaque)) && !(keepSplicedTypes && tp.symbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot)) =>
1382+
alias.dealias1(keep, keepOpaques, keepSplicedTypes)
13831383
case _ => tp
13841384
}
13851385
case app @ AppliedType(tycon, _) =>
1386-
val tycon1 = tycon.dealias1(keep, keepOpaques)
1387-
if (tycon1 ne tycon) app.superType.dealias1(keep, keepOpaques)
1386+
val tycon1 = tycon.dealias1(keep, keepOpaques, keepSplicedTypes)
1387+
if (tycon1 ne tycon) app.superType.dealias1(keep, keepOpaques, keepSplicedTypes)
13881388
else this
13891389
case tp: TypeVar =>
13901390
val tp1 = tp.instanceOpt
1391-
if (tp1.exists) tp1.dealias1(keep, keepOpaques) else tp
1391+
if (tp1.exists) tp1.dealias1(keep, keepOpaques, keepSplicedTypes) else tp
13921392
case tp: AnnotatedType =>
1393-
val parent1 = tp.parent.dealias1(keep, keepOpaques)
1393+
val parent1 = tp.parent.dealias1(keep, keepOpaques, keepSplicedTypes)
13941394
tp match
13951395
case tp @ CapturingType(parent, refs) =>
13961396
tp.derivedCapturingType(parent1, refs)
13971397
case _ =>
13981398
if keep(tp) then tp.derivedAnnotatedType(parent1, tp.annot)
13991399
else parent1
14001400
case tp: LazyRef =>
1401-
tp.ref.dealias1(keep, keepOpaques)
1401+
tp.ref.dealias1(keep, keepOpaques, keepSplicedTypes)
14021402
case _ => this
14031403
}
14041404

14051405
/** Follow aliases and dereferences LazyRefs, annotated types and instantiated
14061406
* TypeVars until type is no longer alias type, annotated type, LazyRef,
14071407
* or instantiated type variable.
14081408
*/
1409-
final def dealias(using Context): Type = dealias1(keepNever, keepOpaques = false)
1409+
final def dealias(using Context): Type = dealias1(keepNever, keepOpaques = false, keepSplicedTypes = false)
14101410

14111411
/** Follow aliases and dereferences LazyRefs and instantiated TypeVars until type
14121412
* is no longer alias type, LazyRef, or instantiated type variable.
14131413
* Goes through annotated types and rewraps annotations on the result.
14141414
*/
1415-
final def dealiasKeepAnnots(using Context): Type = dealias1(keepAlways, keepOpaques = false)
1415+
final def dealiasKeepAnnots(using Context): Type = dealias1(keepAlways, keepOpaques = false, keepSplicedTypes = false)
14161416

14171417
/** Like `dealiasKeepAnnots`, but keeps only refining annotations */
1418-
final def dealiasKeepRefiningAnnots(using Context): Type = dealias1(keepIfRefining, keepOpaques = false)
1418+
final def dealiasKeepRefiningAnnots(using Context): Type = dealias1(keepIfRefining, keepOpaques = false, keepSplicedTypes = false)
14191419

14201420
/** Follow non-opaque aliases and dereferences LazyRefs, annotated types and instantiated
14211421
* TypeVars until type is no longer alias type, annotated type, LazyRef,
14221422
* or instantiated type variable.
14231423
*/
1424-
final def dealiasKeepOpaques(using Context): Type = dealias1(keepNever, keepOpaques = true)
1424+
final def dealiasKeepOpaques(using Context): Type = dealias1(keepNever, keepOpaques = true, keepSplicedTypes = false)
1425+
1426+
/** Like `dealias`, but keeps aliases marked with `@SplicedType` */
1427+
final def dealiasKeepSpliceTypes(using Context): Type = dealias1(keepNever, keepOpaques = true, keepSplicedTypes = true)
14251428

14261429
/** Approximate this type with a type that does not contain skolem types. */
14271430
final def deskolemized(using Context): Type =
@@ -1449,7 +1452,7 @@ object Types {
14491452
def tryNormalize(using Context): Type = NoType
14501453

14511454
private def widenDealias1(keep: AnnotatedType => Context ?=> Boolean)(using Context): Type = {
1452-
val res = this.widen.dealias1(keep, keepOpaques = false)
1455+
val res = this.widen.dealias1(keep, keepOpaques = false, keepSplicedTypes = false)
14531456
if (res eq this) res else res.widenDealias1(keep)
14541457
}
14551458

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

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,29 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
118118
val targs2 = targs.map(targ => TypeTree(healTypeOfTerm(quote.fun.srcPos)(targ.tpe)))
119119
cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, targs2), body2 :: Nil)
120120
else
121-
val quotes = quote.args.mapConserve(transform)
122-
body.tpe match
123-
case tp @ TypeRef(x: TermRef, _) if tp.symbol == defn.QuotedType_splice =>
124-
// Optimization: `quoted.Type.of[x.Underlying](quotes)` --> `x`
125-
ref(x)
121+
object DirectTypeOfRef:
122+
def unapply(body: Tree): Option[Tree] =
123+
body.tpe match
124+
case tp @ TypeRef(x: TermRef, _) if tp.symbol == defn.QuotedType_splice =>
125+
// Optimization: `quoted.Type.of[x.Underlying](quotes)` --> `x`
126+
Some(ref(x).withSpan(quote.span))
127+
case _ =>
128+
body2 match
129+
case Block(List(tdef: TypeDef), tpt: TypeTree) =>
130+
tpt.tpe match
131+
case tpe: TypeRef if tpe.typeSymbol == tdef.symbol =>
132+
tdef.rhs.tpe.hiBound match
133+
case tp @ TypeRef(x: TermRef, _) if tp.symbol == defn.QuotedType_splice =>
134+
// Optimization: `quoted.Type.of[@SplicedType type T = x.Underlying; T](quotes)` --> `x`
135+
Some(ref(x).withSpan(quote.span))
136+
case _ => None
137+
case _ => None
138+
case _ => None
139+
140+
body match
141+
case DirectTypeOfRef(ref) => ref
126142
case _ =>
143+
val quotes = quote.args.mapConserve(transform)
127144
// `quoted.Type.of[<body>](quotes)` --> `quoted.Type.of[<body2>](quotes)`
128145
val TypeApply(fun, _) = quote.fun: @unchecked
129146
cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, body2 :: Nil), quotes)
@@ -191,10 +208,12 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
191208
tp match
192209
case tp: TypeRef =>
193210
tp.prefix match
194-
case NoPrefix if level > levelOf(tp.symbol) && !tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) =>
195-
val tp1 = tp.dealias
196-
if tp1 != tp then apply(tp1)
197-
else tryHeal(tp.symbol, tp, pos)
211+
case NoPrefix =>
212+
val tp1 = tp.dealiasKeepSpliceTypes
213+
if tp != tp1 then apply(tp1)
214+
else if level > levelOf(tp.symbol) && !tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) then
215+
tryHeal(tp.symbol, tp, pos)
216+
else tp
198217
case prefix: ThisType if !tp.symbol.isStatic && level > levelOf(prefix.cls) =>
199218
tryHeal(tp.symbol, tp, pos)
200219
case prefix: TermRef if tp.symbol.isTypeSplice =>

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ class Splicing extends MacroTransform:
246246
if tree.symbol == defn.QuotedTypeModule_of && containsCapturedType(tpt.tpe) =>
247247
val newContent = capturedPartTypes(tpt)
248248
newContent match
249-
case block: Block =>
249+
case block: Block =>
250250
inContext(ctx.withSource(tree.source)) {
251251
Apply(TypeApply(typeof, List(newContent)), List(quotes)).withSpan(tree.span)
252252
}
@@ -342,7 +342,7 @@ class Splicing extends MacroTransform:
342342
val bindingSym = refBindingMap.getOrElseUpdate(tree.symbol, (tree, newBinding))._2
343343
ref(bindingSym)
344344

345-
private def newQuotedTypeClassBinding(tpe: Type)(using Context) =
345+
private def newQuotedTypeClassBinding(tpe: Type)(using Context) =
346346
newSymbol(
347347
spliceOwner,
348348
UniqueName.fresh(nme.Type).toTermName,
@@ -376,7 +376,7 @@ class Splicing extends MacroTransform:
376376
tpt match
377377
case block: Block =>
378378
cpy.Block(block)(newHealedTypes ::: block.stats, TypeTree(captured))
379-
case _ =>
379+
case _ =>
380380
if newHealedTypes.nonEmpty then
381381
cpy.Block(tpt)(newHealedTypes, TypeTree(captured))
382382
else

tests/pos-macros/i8100b.scala

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import scala.quoted.*
2+
3+
def f[T: Type](using Quotes) =
4+
'{
5+
type T2 = T
6+
${
7+
8+
val t0: T = ???
9+
val t1: T2 = ???
10+
val tp = Type.of[T]
11+
val tp1 = Type.of[T2]
12+
'{
13+
val t3: T = ???
14+
val t4: T2 = ???
15+
}
16+
}
17+
}
18+
19+
def g(using Quotes) =
20+
'{
21+
type U
22+
type U2 = U
23+
${
24+
25+
val u1: U = ???
26+
val u2: U2 = ???
27+
28+
val tp = Type.of[U]
29+
val tp1 = Type.of[U2]
30+
'{
31+
val u3: U = ???
32+
val u4: U2 = ???
33+
}
34+
}
35+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
type T = scala.Predef.String
33
val x: java.lang.String = "foo"
4-
val z: T = x
4+
val z: java.lang.String = x
55

66
(x: java.lang.String)
77
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
type T[X] = scala.List[X]
33
val x: java.lang.String = "foo"
4-
val z: T[scala.Predef.String] = scala.List.apply[java.lang.String](x)
4+
val z: [X >: scala.Nothing <: scala.Any] => scala.collection.immutable.List[X][scala.Predef.String] = scala.List.apply[java.lang.String](x)
55

66
(x: java.lang.String)
77
}

tests/run-staging/quote-owners-2.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
def ff: scala.Int = {
33
val a: scala.collection.immutable.List[scala.Int] = {
44
type T = scala.collection.immutable.List[scala.Int]
5-
val b: T = scala.Nil.::[scala.Int](3)
5+
val b: scala.collection.immutable.List[scala.Int] = scala.Nil.::[scala.Int](3)
66

77
(b: scala.collection.immutable.List[scala.Int])
88
}

0 commit comments

Comments
 (0)