Skip to content

Commit aeffaca

Browse files
committed
More robust scheme to re-check definitions once.
The previous scheme relied on subtle and unstated assumptions between symbol updates and re-checking. If they were violated some definitions could not be rechecked at all. The new scheme is more robust. We always re-check except when the checker implementation returns true for `skipRecheck`. And that test is based on an explicitly maintained set of completed symbols.
1 parent 95d23a4 commit aeffaca

File tree

2 files changed

+15
-7
lines changed

2 files changed

+15
-7
lines changed

compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1185,6 +1185,11 @@ class CheckCaptures extends Recheck, SymTransformer:
11851185
case _ =>
11861186
traverseChildren(t)
11871187

1188+
private val completed = new mutable.HashSet[Symbol]
1189+
1190+
override def skipRecheck(sym: Symbol)(using Context): Boolean =
1191+
completed.contains(sym)
1192+
11881193
/** Check a ValDef or DefDef as an action performed in a completer. Since
11891194
* these checks can appear out of order, we need to firsty create the correct
11901195
* environment for checking the definition.
@@ -1205,7 +1210,8 @@ class CheckCaptures extends Recheck, SymTransformer:
12051210
case None => Env(sym, EnvKind.Regular, localSet, restoreEnvFor(sym.owner))
12061211
curEnv = restoreEnvFor(sym.owner)
12071212
capt.println(i"Complete $sym in ${curEnv.outersIterator.toList.map(_.owner)}")
1208-
recheckDef(tree, sym)
1213+
try recheckDef(tree, sym)
1214+
finally completed += sym
12091215
finally
12101216
curEnv = saved
12111217

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -454,12 +454,16 @@ abstract class Recheck extends Phase, SymTransformer:
454454
case _ =>
455455
traverse(stats)
456456

457+
/** A hook to prevent rechecking a ValDef or DefDef.
458+
* Typycally used when definitions are completed on first use.
459+
*/
460+
def skipRecheck(sym: Symbol)(using Context) = false
461+
457462
def recheckDef(tree: ValOrDefDef, sym: Symbol)(using Context): Type =
458-
inContext(ctx.localContext(tree, sym)) {
463+
inContext(ctx.localContext(tree, sym)):
459464
tree match
460465
case tree: ValDef => recheckValDef(tree, sym)
461466
case tree: DefDef => recheckDefDef(tree, sym)
462-
}
463467

464468
/** Recheck tree without adapting it, returning its new type.
465469
* @param tree the original tree
@@ -476,10 +480,8 @@ abstract class Recheck extends Phase, SymTransformer:
476480
case tree: ValOrDefDef =>
477481
if tree.isEmpty then NoType
478482
else
479-
if sym.isUpdatedAfter(preRecheckPhase) then
480-
sym.ensureCompleted() // in this case the symbol's completer should recheck the right hand side
481-
else
482-
recheckDef(tree, sym)
483+
sym.ensureCompleted()
484+
if !skipRecheck(sym) then recheckDef(tree, sym)
483485
sym.termRef
484486
case tree: TypeDef =>
485487
// TODO: Should we allow for completers as for ValDefs or DefDefs?

0 commit comments

Comments
 (0)