Skip to content

Commit a5363c4

Browse files
committed
Fix #1812, Symbols.mapSymbols shouldn't replace denotations
It will use lazy types instead. The current version transforms a type, with a context that has denotations that may be forcefully replaced by mapSymbols. Types created during this transformation may cache denots, that are-to-be replaced. This is very problematic as this method is called from TreeTypeMap.withMappedSyms in a fixed-point cycle, creating new symbols on every iteration. Those cached denotations could make types keep symbols from previous iterations indefinitely. The changed version does not transform the types eagerly, and instead makes them lazy. Assuming there are no cycles, this should ensure correct ordering. Unfortunatelly, at this point in the compiler we basically always touch everything, and we can't even transform the info of denotation without this denotations info. We basically have a chicked&egg problem here. To solve it, I use the same trick as used by other lazy types by assigning an approximation of future type first. This allows to pass the tests and makes dotty more robust, but I suspect this isn't a complete fix and new similar bugs may arrive.
1 parent 3ea7b99 commit a5363c4

File tree

1 file changed

+15
-6
lines changed

1 file changed

+15
-6
lines changed

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

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -313,25 +313,34 @@ trait Symbols { this: Context =>
313313
newNakedSymbol[original.ThisName](original.coord)
314314
}
315315
val ttmap1 = ttmap.withSubstitution(originals, copies)
316-
(originals, copies).zipped foreach {(original, copy) =>
317-
copy.denot = original.denot // preliminary denotation, so that we can access symbols in subsequent transform
318-
}
319-
(originals, copies).zipped foreach {(original, copy) =>
316+
(originals, copies).zipped foreach { (original, copy) =>
320317
val odenot = original.denot
321318
val oinfo = original.info match {
322319
case ClassInfo(pre, _, parents, decls, selfInfo) =>
323320
assert(original.isClass)
324321
ClassInfo(pre, copy.asClass, parents, decls.cloneScope, selfInfo)
325322
case oinfo => oinfo
326323
}
324+
325+
val completer = new LazyType {
326+
def complete(denot: SymDenotation)(implicit ctx: Context): Unit = {
327+
denot.info = oinfo // needed as otherwise we won't be able to go from Sym -> parents & etc
328+
// Note that this is a hack, but hack commonly used in Dotty
329+
// The same thing is done by other completers all the time
330+
denot.info = ttmap1.mapType(oinfo)
331+
}
332+
}
333+
327334
copy.denot = odenot.copySymDenotation(
328335
symbol = copy,
329336
owner = ttmap1.mapOwner(odenot.owner),
330-
initFlags = odenot.flags &~ Frozen | Fresh,
331-
info = ttmap1.mapType(oinfo),
337+
initFlags = odenot.flags &~ (Frozen | Touched) | Fresh,
338+
info = completer,
332339
privateWithin = ttmap1.mapOwner(odenot.privateWithin), // since this refers to outer symbols, need not include copies (from->to) in ownermap here.
333340
annotations = odenot.annotations.mapConserve(ttmap1.apply))
341+
334342
}
343+
335344
copies
336345
}
337346

0 commit comments

Comments
 (0)