Skip to content

Commit c5d46e2

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 b13c17f commit c5d46e2

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
@@ -912,6 +912,10 @@ class Namer { typer: Typer =>
912912

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

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

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

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

1104-
addAnnotations(denot.symbol)
1144+
if (tempInfo == null) // Constructor has not been completed yet
1145+
completeConstructor(denot)
11051146

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

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

11461163
tempInfo.finalize(denot, parentTypes)
1164+
// The temporary info can now be garbage-collected
1165+
tempInfo = null
11471166

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

0 commit comments

Comments
 (0)