Skip to content

Commit f3db79a

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 ed81385 commit f3db79a

File tree

14 files changed

+108
-39
lines changed

14 files changed

+108
-39
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
@@ -957,6 +957,51 @@ class TreeUnpickler(reader: TastyReader,
957957
tree.setDefTree
958958
}
959959

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

10031050
val lazyStats = readLater(end, rdr => {
10041051
val stats = rdr.readIndexedStats(localDummy, end)
@@ -1007,7 +1054,7 @@ class TreeUnpickler(reader: TastyReader,
10071054
defn.patchStdLibClass(cls)
10081055
NamerOps.addConstructorProxies(cls)
10091056
setSpan(start,
1010-
untpd.Template(constr, mappedParents, Nil, self, lazyStats)
1057+
untpd.Template(constr, mappedParents, self, lazyStats)
10111058
.withType(localDummy.termRef))
10121059
}
10131060

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/transform/TreeChecker.scala

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -710,16 +710,16 @@ object TreeChecker {
710710
object TreeNodeChecker extends untpd.TreeTraverser:
711711
import untpd._
712712
def traverse(tree: Tree)(using Context) = tree match
713-
case t: TypeTree => assert(assertion = false, i"TypeTree not expected: $t")
714-
case t @ TypeApply(fun, _targs) => traverse(fun)
715-
case t @ New(_tpt) =>
716-
case t @ Typed(expr, _tpt) => traverse(expr)
717-
case t @ Closure(env, meth, _tpt) => traverse(env); traverse(meth)
718-
case t @ SeqLiteral(elems, _elemtpt) => traverse(elems)
719-
case t @ ValDef(_, _tpt, _) => traverse(t.rhs)
720-
case t @ DefDef(_, paramss, _tpt, _) => for params <- paramss do traverse(params); traverse(t.rhs)
721-
case t @ TypeDef(_, _rhs) =>
722-
case t @ Template(constr, parents, self, _) => traverse(constr); traverse(parents); traverse(self); traverse(t.body)
723-
case t => traverseChildren(t)
713+
case t: TypeTree => assert(assertion = false, i"TypeTree not expected: $t")
714+
case t @ TypeApply(fun, _targs) => traverse(fun)
715+
case t @ New(_tpt) =>
716+
case t @ Typed(expr, _tpt) => traverse(expr)
717+
case t @ Closure(env, meth, _tpt) => traverse(env); traverse(meth)
718+
case t @ SeqLiteral(elems, _elemtpt) => traverse(elems)
719+
case t @ ValDef(_, _tpt, _) => traverse(t.rhs)
720+
case t @ DefDef(_, paramss, _tpt, _) => for params <- paramss do traverse(params); traverse(t.rhs)
721+
case t @ TypeDef(_, _rhs) =>
722+
case t @ Template(constr, _, self, _) => traverse(constr); traverse(t.parentsOrDerived); traverse(self); traverse(t.body)
723+
case t => traverseChildren(t)
724724
end traverse
725725
}

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

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

13911391
if (stat.symbol.isAllOf(EnumCase))
13921392
stat match {
1393-
case TypeDef(_, Template(DefDef(_, paramss, _, _), parents, _, _)) =>
1393+
case TypeDef(_, impl @ Template(DefDef(_, paramss, _, _), _, _, _)) =>
13941394
paramss.foreach(_.foreach(check))
1395-
parents.foreach(check)
1395+
impl.parents.foreach(check)
13961396
case vdef: ValDef =>
13971397
vdef.rhs match {
13981398
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)