Skip to content

Commit 511e771

Browse files
committed
Read argument of parent constructors in Templates lazily
Avoid reading the arguments of parent constructors unless someone forces them. We don't need them to determine the parent types of the class to which the template belongs. This makes TreeUnpickler as lazy as Namer in this respect and therefore avoids CyclicReferences during unpickling. Fixes #16673
1 parent 1f7b03c commit 511e771

File tree

13 files changed

+97
-28
lines changed

13 files changed

+97
-28
lines changed

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] =>
313313
*/
314314
def parentsKind(parents: List[Tree])(using Context): FlagSet = parents match {
315315
case Nil => NoInitsInterface
316-
case Apply(_, _ :: _) :: _ => EmptyFlags
316+
case Apply(_, _ :: _) :: _ | Block(_, _) :: _ => EmptyFlags
317317
case _ :: parents1 => parentsKind(parents1)
318318
}
319319

compiler/src/dotty/tools/dotc/ast/TreeMapWithImplicits.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@ class TreeMapWithImplicits extends tpd.TreeMapWithPreciseStatContexts {
5555
transform(tree.tpt),
5656
transform(tree.rhs)(using nestedScopeCtx(tree.paramss.flatten)))
5757
}
58-
case impl @ Template(constr, parents, self, _) =>
58+
case impl @ Template(constr, _, self, _) =>
5959
cpy.Template(tree)(
6060
transformSub(constr),
61-
transform(parents)(using ctx.superCallContext),
61+
transform(impl.parents)(using ctx.superCallContext),
6262
Nil,
6363
transformSelf(self),
6464
transformStats(impl.body, tree.symbol))

compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,11 @@ class TreeTypeMap(
9292
cpy.Inlined(tree)(call, bindings1, expanded1)
9393

9494
override def transform(tree: tpd.Tree)(using Context): tpd.Tree = treeMap(tree) match {
95-
case impl @ Template(constr, parents, self, _) =>
95+
case impl @ Template(constr, _, self, _) =>
9696
val tmap = withMappedSyms(localSyms(impl :: self :: Nil))
9797
cpy.Template(impl)(
9898
constr = tmap.transformSub(constr),
99-
parents = parents.mapconserve(transform),
99+
parents = impl.parents.mapconserve(transform),
100100
self = tmap.transformSub(self),
101101
body = impl.body mapconserve
102102
(tmap.transform(_)(using ctx.withOwner(mapOwner(impl.symbol.owner))))

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -855,16 +855,30 @@ object Trees {
855855
* if this is of class untpd.DerivingTemplate.
856856
* Typed templates only have parents.
857857
*/
858-
case class Template[+T <: Untyped] private[ast] (constr: DefDef[T], parentsOrDerived: List[Tree[T]], self: ValDef[T], private var preBody: LazyTreeList[T])(implicit @constructorOnly src: SourceFile)
858+
case class Template[+T <: Untyped] private[ast] (constr: DefDef[T], private var myParentsOrDerived: LazyTreeList[T], self: ValDef[T], private var preBody: LazyTreeList[T])(implicit @constructorOnly src: SourceFile)
859859
extends DefTree[T] with WithLazyField[List[Tree[T]]] {
860860
type ThisTree[+T <: Untyped] = Template[T]
861861
def unforcedBody: LazyTreeList[T] = unforced
862862
def unforced: LazyTreeList[T] = preBody
863+
863864
protected def force(x: List[Tree[T @uncheckedVariance]]): Unit = preBody = x
865+
866+
// The post-condition of forceIfLazy is that all lazy fields are trees, so
867+
// we need to force parentsAndDerived as well as body.
868+
override def forceIfLazy(using Context) =
869+
parentsOrDerived
870+
super.forceIfLazy
871+
864872
def body(using Context): List[Tree[T]] = forceIfLazy
865873

866-
def parents: List[Tree[T]] = parentsOrDerived // overridden by DerivingTemplate
867-
def derived: List[untpd.Tree] = Nil // overridden by DerivingTemplate
874+
def parentsOrDerived(using Context): List[Tree[T]] = myParentsOrDerived match
875+
case latePs: Lazy[List[Tree[T]]] @unchecked =>
876+
myParentsOrDerived = latePs.complete
877+
parentsOrDerived
878+
case ps: List[Tree[T]] @unchecked => ps
879+
880+
def parents(using Context): List[Tree[T]] = parentsOrDerived // overridden by DerivingTemplate
881+
def derived: List[untpd.Tree] = Nil // overridden by DerivingTemplate
868882
}
869883

870884

@@ -1355,7 +1369,7 @@ object Trees {
13551369
DefDef(tree: Tree)(name, paramss, tpt, rhs)
13561370
def TypeDef(tree: TypeDef)(name: TypeName = tree.name, rhs: Tree = tree.rhs)(using Context): TypeDef =
13571371
TypeDef(tree: Tree)(name, rhs)
1358-
def Template(tree: Template)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, derived: List[untpd.Tree] = tree.derived, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody)(using Context): Template =
1372+
def Template(tree: Template)(using Context)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, derived: List[untpd.Tree] = tree.derived, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody): Template =
13591373
Template(tree: Tree)(constr, parents, derived, self, body)
13601374
def Hole(tree: Hole)(isTerm: Boolean = tree.isTerm, idx: Int = tree.idx, args: List[Tree] = tree.args, content: Tree = tree.content, tpt: Tree = tree.tpt)(using Context): Hole =
13611375
Hole(tree: Tree)(isTerm, idx, args, content, tpt)
@@ -1618,8 +1632,8 @@ object Trees {
16181632
inContext(localCtx(tree)) {
16191633
this(x, rhs)
16201634
}
1621-
case tree @ Template(constr, parents, self, _) if tree.derived.isEmpty =>
1622-
this(this(this(this(x, constr), parents), self), tree.body)
1635+
case tree @ Template(constr, _, self, _) if tree.derived.isEmpty =>
1636+
this(this(this(this(x, constr), tree.parents), self), tree.body)
16231637
case Import(expr, _) =>
16241638
this(x, expr)
16251639
case Export(expr, _) =>

compiler/src/dotty/tools/dotc/ast/untpd.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
5454
*/
5555
class DerivingTemplate(constr: DefDef, parentsOrDerived: List[Tree], self: ValDef, preBody: LazyTreeList, derivedCount: Int)(implicit @constructorOnly src: SourceFile)
5656
extends Template(constr, parentsOrDerived, self, preBody) {
57-
override val parents = parentsOrDerived.dropRight(derivedCount)
57+
private val myParents = parentsOrDerived.dropRight(derivedCount)
58+
override def parents(using Context) = myParents
5859
override val derived = parentsOrDerived.takeRight(derivedCount)
5960
}
6061

@@ -415,6 +416,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
415416
def Template(constr: DefDef, parents: List[Tree], derived: List[Tree], self: ValDef, body: LazyTreeList)(implicit src: SourceFile): Template =
416417
if (derived.isEmpty) new Template(constr, parents, self, body)
417418
else new DerivingTemplate(constr, parents ++ derived, self, body, derived.length)
419+
def Template(constr: DefDef, parents: LazyTreeList, self: ValDef, body: LazyTreeList)(implicit src: SourceFile): Template =
420+
new Template(constr, parents, self, body)
418421
def Import(expr: Tree, selectors: List[ImportSelector])(implicit src: SourceFile): Import = new Import(expr, selectors)
419422
def Export(expr: Tree, selectors: List[ImportSelector])(implicit src: SourceFile): Export = new Export(expr, selectors)
420423
def PackageDef(pid: RefTree, stats: List[Tree])(implicit src: SourceFile): PackageDef = new PackageDef(pid, stats)

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,51 @@ class TreeUnpickler(reader: TastyReader,
959959
tree.setDefTree
960960
}
961961

962+
/** Read enough of parent to determine its type, without reading arguments
963+
* of applications. This is necessary to make TreeUnpickler as lazy as Namer
964+
* in this regard. See i16673 for a test case.
965+
*/
966+
private def readParentType()(using Context): Type =
967+
readByte() match
968+
case TYPEAPPLY =>
969+
val end = readEnd()
970+
val tycon = readParentType()
971+
if tycon.typeParams.isEmpty then
972+
goto(end)
973+
tycon
974+
else
975+
val args = until(end)(readTpt())
976+
val cls = tycon.classSymbol
977+
assert(cls.typeParams.hasSameLengthAs(args))
978+
cls.typeRef.appliedTo(args.tpes)
979+
case APPLY | BLOCK =>
980+
val end = readEnd()
981+
try readParentType()
982+
finally goto(end)
983+
case SELECTin =>
984+
val end = readEnd()
985+
readName()
986+
readTerm() match
987+
case nu: New =>
988+
try nu.tpe
989+
finally goto(end)
990+
case SHAREDterm =>
991+
forkAt(readAddr()).readParentType()
992+
993+
/** Read template parents
994+
* @param withArgs if false, only read enough of parent trees to determine their type
995+
* but skip constructor arguments. Return any trees that were partially
996+
* parsed in this way as InferredTypeTrees.
997+
*/
998+
def readParents(withArgs: Boolean)(using Context): List[Tree] =
999+
collectWhile(nextByte != SELFDEF && nextByte != DEFDEF) {
1000+
nextUnsharedTag match
1001+
case APPLY | TYPEAPPLY | BLOCK =>
1002+
if withArgs then readTerm()
1003+
else InferredTypeTree().withType(readParentType())
1004+
case _ => readTpt()
1005+
}
1006+
9621007
private def readTemplate(using Context): Template = {
9631008
val start = currentAddr
9641009
assert(sourcePathAt(start).isEmpty)
@@ -981,12 +1026,8 @@ class TreeUnpickler(reader: TastyReader,
9811026
while (bodyIndexer.reader.nextByte != DEFDEF) bodyIndexer.skipTree()
9821027
bodyIndexer.indexStats(end)
9831028
}
984-
val parents = collectWhile(nextByte != SELFDEF && nextByte != DEFDEF) {
985-
nextUnsharedTag match {
986-
case APPLY | TYPEAPPLY | BLOCK => readTerm()(using parentCtx)
987-
case _ => readTpt()(using parentCtx)
988-
}
989-
}
1029+
val parentReader = fork
1030+
val parents = readParents(withArgs = false)(using parentCtx)
9901031
val parentTypes = parents.map(_.tpe.dealias)
9911032
val self =
9921033
if (nextByte == SELFDEF) {
@@ -1000,7 +1041,13 @@ class TreeUnpickler(reader: TastyReader,
10001041
selfInfo = if (self.isEmpty) NoType else self.tpt.tpe)
10011042
.integrateOpaqueMembers
10021043
val constr = readIndexedDef().asInstanceOf[DefDef]
1003-
val mappedParents = parents.map(_.changeOwner(localDummy, constr.symbol))
1044+
val mappedParents: LazyTreeList =
1045+
if parents.exists(_.isInstanceOf[InferredTypeTree]) then
1046+
// parents were not read fully, will need to be read again later on demand
1047+
new LazyReader(parentReader, localDummy, ctx.mode, ctx.source,
1048+
_.readParents(withArgs = true)
1049+
.map(_.changeOwner(localDummy, constr.symbol)))
1050+
else parents
10041051

10051052
val lazyStats = readLater(end, rdr => {
10061053
val stats = rdr.readIndexedStats(localDummy, end)
@@ -1009,7 +1056,7 @@ class TreeUnpickler(reader: TastyReader,
10091056
defn.patchStdLibClass(cls)
10101057
NamerOps.addConstructorProxies(cls)
10111058
setSpan(start,
1012-
untpd.Template(constr, mappedParents, Nil, self, lazyStats)
1059+
untpd.Template(constr, mappedParents, self, lazyStats)
10131060
.withType(localDummy.termRef))
10141061
}
10151062

compiler/src/dotty/tools/dotc/interactive/Interactive.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,8 +313,8 @@ object Interactive {
313313
case _ =>
314314
}
315315
localCtx
316-
case tree @ Template(constr, parents, self, _) =>
317-
if ((constr :: self :: parents).contains(nested)) outer
316+
case tree @ Template(constr, _, self, _) =>
317+
if ((constr :: self :: tree.parentsOrDerived).contains(nested)) outer
318318
else contextOfStat(tree.body, nested, tree.symbol, outer.inClassContext(self.symbol))
319319
case _ =>
320320
outer

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ abstract class MacroTransform extends Phase {
3838
tree
3939
case _: PackageDef | _: MemberDef =>
4040
super.transform(tree)(using localCtx(tree))
41-
case impl @ Template(constr, parents, self, _) =>
41+
case impl @ Template(constr, _, self, _) =>
4242
cpy.Template(tree)(
4343
transformSub(constr),
44-
transform(parents)(using ctx.superCallContext),
44+
transform(impl.parents)(using ctx.superCallContext),
4545
Nil,
4646
transformSelf(self),
4747
transformStats(impl.body, tree.symbol))

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,9 +1394,9 @@ trait Checking {
13941394

13951395
if (stat.symbol.isAllOf(EnumCase))
13961396
stat match {
1397-
case TypeDef(_, Template(DefDef(_, paramss, _, _), parents, _, _)) =>
1397+
case TypeDef(_, impl @ Template(DefDef(_, paramss, _, _), _, _, _)) =>
13981398
paramss.foreach(_.foreach(check))
1399-
parents.foreach(check)
1399+
impl.parents.foreach(check)
14001400
case vdef: ValDef =>
14011401
vdef.rhs match {
14021402
case Block((clsDef @ TypeDef(_, impl: Template)) :: Nil, _)

compiler/test/dotty/tools/dotc/parsing/DeSugarTest.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ class DeSugarTest extends ParserTest {
5959
cpy.DefDef(tree1)(name, transformParamss(paramss), transform(tpt, Type), transform(tree1.rhs))
6060
case tree1 @ TypeDef(name, rhs) =>
6161
cpy.TypeDef(tree1)(name, transform(rhs, Type))
62-
case impl @ Template(constr, parents, self, _) =>
63-
cpy.Template(tree1)(transformSub(constr), transform(parents), Nil, transformSub(self), transform(impl.body, Expr))
62+
case impl @ Template(constr, _, self, _) =>
63+
cpy.Template(tree1)(transformSub(constr), transform(impl.parentsOrDerived), Nil, transformSub(self), transform(impl.body, Expr))
6464
case Thicket(trees) =>
6565
Thicket(flatten(trees mapConserve super.transform))
6666
case tree1 =>

tests/pos/i16673/FooStub_1.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
abstract class FooStub(val that: Foo):
2+
val bar = 1337;

tests/pos/i16673/FooStub_2.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
abstract class FooStub(val that: Foo):
2+
val bar = 1337;

tests/pos/i16673/Foo_1.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
final class Foo(_that: Foo) extends FooStub(_that)

0 commit comments

Comments
 (0)