Skip to content

Commit d9ade19

Browse files
committed
When typing a DerivedFromParamTree, only complete constructors
Parameter accessors have a derived rhs based on the corresponding class constructor parameter. Before this commit, typing such a derived tree would involve completing the enclosing class, which requires typing the class parents, which might end up requiring the info of the original parameter accessor. This doesn't always cause cyclic reference since `Namer#typeDefSig` sets temporary empty bounds when completing a type definition, but it can lead to puzzling compilation errors. In particular, after the changes related to completions in this PR, it broke the bootstrap. We fix this by making `DerivedFromParamTree#ensureCompletions` force less: we only really need to force the constructors, this requires some refactoring in `ClassCompleter` to allow such partial completions.
1 parent d8391c6 commit d9ade19

File tree

2 files changed

+59
-34
lines changed

2 files changed

+59
-34
lines changed

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

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import util.Spans._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags
77
import Symbols._, StdNames._, Trees._
88
import Decorators._, transform.SymUtils._
99
import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName}
10-
import typer.FrontEnd
10+
import typer.{FrontEnd, Namer}
1111
import util.{Property, SourceFile, SourcePosition}
1212
import util.NameTransformer.avoidIllegalChars
1313
import collection.mutable.ListBuffer
@@ -75,21 +75,27 @@ object desugar {
7575
}
7676

7777
/** A type tree that computes its type from an existing parameter. */
78-
class DerivedFromParamTree(implicit @constructorOnly src: SourceFile) extends DerivedTypeTree {
78+
class DerivedFromParamTree()(implicit @constructorOnly src: SourceFile) extends DerivedTypeTree {
7979

80-
/** Make sure that for all enclosing module classes their companion classes
81-
* are completed. Reason: We need the constructor of such companion classes to
82-
* be completed so that OriginalSymbol attachments are pushed to DerivedTypeTrees
83-
* in apply/unapply methods.
80+
/** Complete the appropriate constructors so that OriginalSymbol attachments are
81+
* pushed to DerivedTypeTrees.
8482
*/
85-
override def ensureCompletions(implicit ctx: Context): Unit =
83+
override def ensureCompletions(implicit ctx: Context): Unit = {
84+
def completeConstructor(sym: Symbol) =
85+
sym.infoOrCompleter match {
86+
case completer: Namer#ClassCompleter =>
87+
completer.completeConstructor(sym)
88+
case _ =>
89+
}
90+
8691
if (!ctx.owner.is(Package))
8792
if (ctx.owner.isClass) {
88-
ctx.owner.ensureCompleted()
93+
completeConstructor(ctx.owner)
8994
if (ctx.owner.is(ModuleClass))
90-
ctx.owner.linkedClass.ensureCompleted()
95+
completeConstructor(ctx.owner.linkedClass)
9196
}
9297
else ensureCompletions(ctx.outer)
98+
}
9399

94100
/** Return info of original symbol, where all references to siblings of the
95101
* original symbol (i.e. sibling and original symbol have the same owner)

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

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,10 @@ class Namer { typer: Typer =>
913913

914914
protected implicit val ctx: Context = localContext(cls).setMode(ictx.mode &~ Mode.InSuperCall)
915915

916+
private[this] var localCtx: Context = _
917+
/** info to be used temporarily while completing the class, to avoid cyclic references. */
918+
private[this] var tempInfo: TempClassInfo = _
919+
916920
val TypeDef(name, impl @ Template(constr, _, self, _)) = original
917921

918922
private val (params, rest): (List[Tree], List[Tree]) = impl.body.span {
@@ -1040,6 +1044,42 @@ class Namer { typer: Typer =>
10401044
forwarderss.foreach(_.foreach(fwdr => fwdr.symbol.entered))
10411045
}
10421046

1047+
/** Ensure constructor is completed so that any parameter accessors
1048+
* which have type trees deriving from its parameters can be
1049+
* completed in turn. Note that parent types access such parameter
1050+
* accessors, that's why the constructor needs to be completed before
1051+
* the parent types are elaborated.
1052+
*/
1053+
def completeConstructor(denot: SymDenotation): Unit = {
1054+
if (tempInfo != null) // Constructor has been completed already
1055+
return
1056+
1057+
addAnnotations(denot.symbol)
1058+
1059+
val selfInfo: TypeOrSymbol =
1060+
if (self.isEmpty) NoType
1061+
else if (cls.is(Module)) {
1062+
val moduleType = cls.owner.thisType select sourceModule
1063+
if (self.name == nme.WILDCARD) moduleType
1064+
else recordSym(
1065+
ctx.newSymbol(cls, self.name, self.mods.flags, moduleType, coord = self.span),
1066+
self)
1067+
}
1068+
else createSymbol(self)
1069+
1070+
val savedInfo = denot.infoOrCompleter
1071+
tempInfo = new TempClassInfo(cls.owner.thisType, cls, decls, selfInfo)
1072+
denot.info = tempInfo
1073+
1074+
localCtx = ctx.inClassContext(selfInfo)
1075+
1076+
index(constr)
1077+
index(rest)(localCtx)
1078+
symbolOfTree(constr).ensureCompleted()
1079+
1080+
denot.info = savedInfo
1081+
}
1082+
10431083
/** The type signature of a ClassDef with given symbol */
10441084
override def completeInCreationContext(denot: SymDenotation): Unit = {
10451085
val parents = impl.parents
@@ -1102,34 +1142,11 @@ class Namer { typer: Typer =>
11021142
}
11031143
}
11041144

1105-
addAnnotations(denot.symbol)
1145+
if (tempInfo == null) // Constructor has not been completed yet
1146+
completeConstructor(denot)
11061147

1107-
val selfInfo: TypeOrSymbol =
1108-
if (self.isEmpty) NoType
1109-
else if (cls.is(Module)) {
1110-
val moduleType = cls.owner.thisType select sourceModule
1111-
if (self.name == nme.WILDCARD) moduleType
1112-
else recordSym(
1113-
ctx.newSymbol(cls, self.name, self.mods.flags, moduleType, coord = self.span),
1114-
self)
1115-
}
1116-
else createSymbol(self)
1117-
1118-
// pre-set info, so that parent types can refer to type params
1119-
val tempInfo = new TempClassInfo(cls.owner.thisType, cls, decls, selfInfo)
11201148
denot.info = tempInfo
11211149

1122-
val localCtx = ctx.inClassContext(selfInfo)
1123-
1124-
// Ensure constructor is completed so that any parameter accessors
1125-
// which have type trees deriving from its parameters can be
1126-
// completed in turn. Note that parent types access such parameter
1127-
// accessors, that's why the constructor needs to be completed before
1128-
// the parent types are elaborated.
1129-
index(constr)
1130-
index(rest)(localCtx)
1131-
symbolOfTree(constr).ensureCompleted()
1132-
11331150
val parentTypes = defn.adjustForTuple(cls, cls.typeParams,
11341151
ensureFirstIsClass(parents.map(checkedParentType(_)), cls.span))
11351152
typr.println(i"completing $denot, parents = $parents%, %, parentTypes = $parentTypes%, %")
@@ -1145,6 +1162,8 @@ class Namer { typer: Typer =>
11451162
}
11461163

11471164
tempInfo.finalize(denot, parentTypes)
1165+
// The temporary info can now be garbage-collected
1166+
tempInfo = null
11481167

11491168
Checking.checkWellFormed(cls)
11501169
if (isDerivedValueClass(cls)) cls.setFlag(Final)

0 commit comments

Comments
 (0)