Skip to content

Commit 4d3f757

Browse files
authored
Refactor handling of rechecked types (#22229)
- Always store new types on rechecking - Store them in a hashmap which is associated with the rechecker of the current compilation unit - After rechecking is done, the map is forgotten, unless keepTypes is true. Under keepTypes, the map is kept in an attachment of the unit's root tree. Change in nomenclature: knownType --> nuType rememberType --> setNuType hasRememberedType --> hasNuType
2 parents d50973b + 5ac4d73 commit 4d3f757

File tree

3 files changed

+105
-92
lines changed

3 files changed

+105
-92
lines changed

Diff for: compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala

+29-17
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,22 @@ object CheckCaptures:
223223
checkNotUniversal.traverse(tpe.widen)
224224
end checkNotUniversalInUnboxedResult
225225

226+
trait CheckerAPI:
227+
/** Complete symbol info of a val or a def */
228+
def completeDef(tree: ValOrDefDef, sym: Symbol)(using Context): Type
229+
230+
extension [T <: Tree](tree: T)
231+
232+
/** Set new type of the tree if none was installed yet. */
233+
def setNuType(tpe: Type): Unit
234+
235+
/** The new type of the tree, or if none was installed, the original type */
236+
def nuType(using Context): Type
237+
238+
/** Was a new type installed for this tree? */
239+
def hasNuType: Boolean
240+
end CheckerAPI
241+
226242
class CheckCaptures extends Recheck, SymTransformer:
227243
thisPhase =>
228244

@@ -243,7 +259,7 @@ class CheckCaptures extends Recheck, SymTransformer:
243259

244260
val ccState1 = new CCState // Dotty problem: Rename to ccState ==> Crash in ExplicitOuter
245261

246-
class CaptureChecker(ictx: Context) extends Rechecker(ictx):
262+
class CaptureChecker(ictx: Context) extends Rechecker(ictx), CheckerAPI:
247263

248264
/** The current environment */
249265
private val rootEnv: Env = inContext(ictx):
@@ -261,10 +277,6 @@ class CheckCaptures extends Recheck, SymTransformer:
261277
*/
262278
private val todoAtPostCheck = new mutable.ListBuffer[() => Unit]
263279

264-
override def keepType(tree: Tree) =
265-
super.keepType(tree)
266-
|| tree.isInstanceOf[Try] // type of `try` needs tp be checked for * escapes
267-
268280
/** Instantiate capture set variables appearing contra-variantly to their
269281
* upper approximation.
270282
*/
@@ -286,8 +298,8 @@ class CheckCaptures extends Recheck, SymTransformer:
286298
*/
287299
private def interpolateVarsIn(tpt: Tree)(using Context): Unit =
288300
if tpt.isInstanceOf[InferredTypeTree] then
289-
interpolator().traverse(tpt.knownType)
290-
.showing(i"solved vars in ${tpt.knownType}", capt)
301+
interpolator().traverse(tpt.nuType)
302+
.showing(i"solved vars in ${tpt.nuType}", capt)
291303
for msg <- ccState.approxWarnings do
292304
report.warning(msg, tpt.srcPos)
293305
ccState.approxWarnings.clear()
@@ -501,11 +513,11 @@ class CheckCaptures extends Recheck, SymTransformer:
501513
then ("\nThis is often caused by a local capability$where\nleaking as part of its result.", fn.srcPos)
502514
else if arg.span.exists then ("", arg.srcPos)
503515
else ("", fn.srcPos)
504-
disallowRootCapabilitiesIn(arg.knownType, NoSymbol,
516+
disallowRootCapabilitiesIn(arg.nuType, NoSymbol,
505517
i"Type variable $pname of $sym", "be instantiated to", addendum, pos)
506518

507519
val param = fn.symbol.paramNamed(pname)
508-
if param.isUseParam then markFree(arg.knownType.deepCaptureSet, pos)
520+
if param.isUseParam then markFree(arg.nuType.deepCaptureSet, pos)
509521
end disallowCapInTypeArgs
510522

511523
override def recheckIdent(tree: Ident, pt: Type)(using Context): Type =
@@ -769,8 +781,8 @@ class CheckCaptures extends Recheck, SymTransformer:
769781
*/
770782
def checkContains(tree: TypeApply)(using Context): Unit = tree match
771783
case ContainsImpl(csArg, refArg) =>
772-
val cs = csArg.knownType.captureSet
773-
val ref = refArg.knownType
784+
val cs = csArg.nuType.captureSet
785+
val ref = refArg.nuType
774786
capt.println(i"check contains $cs , $ref")
775787
ref match
776788
case ref: CaptureRef if ref.isTracked =>
@@ -852,7 +864,7 @@ class CheckCaptures extends Recheck, SymTransformer:
852864
case _ =>
853865
(sym, "")
854866
disallowRootCapabilitiesIn(
855-
tree.tpt.knownType, carrier, i"Mutable $sym", "have type", addendum, sym.srcPos)
867+
tree.tpt.nuType, carrier, i"Mutable $sym", "have type", addendum, sym.srcPos)
856868
checkInferredResult(super.recheckValDef(tree, sym), tree)
857869
finally
858870
if !sym.is(Param) then
@@ -1533,7 +1545,7 @@ class CheckCaptures extends Recheck, SymTransformer:
15331545
private val setup: SetupAPI = thisPhase.prev.asInstanceOf[Setup]
15341546

15351547
override def checkUnit(unit: CompilationUnit)(using Context): Unit =
1536-
setup.setupUnit(unit.tpdTree, completeDef)
1548+
setup.setupUnit(unit.tpdTree, this)
15371549
collectCapturedMutVars.traverse(unit.tpdTree)
15381550

15391551
if ctx.settings.YccPrintSetup.value then
@@ -1676,7 +1688,7 @@ class CheckCaptures extends Recheck, SymTransformer:
16761688
traverseChildren(tp)
16771689

16781690
if tree.isInstanceOf[InferredTypeTree] then
1679-
checker.traverse(tree.knownType)
1691+
checker.traverse(tree.nuType)
16801692
end healTypeParam
16811693

16821694
/** Under the unsealed policy: Arrays are like vars, check that their element types
@@ -1716,10 +1728,10 @@ class CheckCaptures extends Recheck, SymTransformer:
17161728
check(tree)
17171729
def check(tree: Tree)(using Context) = tree match
17181730
case TypeApply(fun, args) =>
1719-
fun.knownType.widen match
1731+
fun.nuType.widen match
17201732
case tl: PolyType =>
17211733
val normArgs = args.lazyZip(tl.paramInfos).map: (arg, bounds) =>
1722-
arg.withType(arg.knownType.forceBoxStatus(
1734+
arg.withType(arg.nuType.forceBoxStatus(
17231735
bounds.hi.isBoxedCapturing | bounds.lo.isBoxedCapturing))
17241736
checkBounds(normArgs, tl)
17251737
args.lazyZip(tl.paramNames).foreach(healTypeParam(_, _, fun.symbol))
@@ -1739,7 +1751,7 @@ class CheckCaptures extends Recheck, SymTransformer:
17391751
def traverse(t: Tree)(using Context) = t match
17401752
case tree: InferredTypeTree =>
17411753
case tree: New =>
1742-
case tree: TypeTree => checkAppliedTypesIn(tree.withKnownType)
1754+
case tree: TypeTree => checkAppliedTypesIn(tree.withType(tree.nuType))
17431755
case _ => traverseChildren(t)
17441756
checkApplied.traverse(unit)
17451757
end postCheck

Diff for: compiler/src/dotty/tools/dotc/cc/Setup.scala

+24-26
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,16 @@ import printing.{Printer, Texts}, Texts.{Text, Str}
1919
import collection.mutable
2020
import CCState.*
2121
import dotty.tools.dotc.util.NoSourcePosition
22+
import CheckCaptures.CheckerAPI
2223

2324
/** Operations accessed from CheckCaptures */
2425
trait SetupAPI:
2526

26-
/** The operation to recheck a ValDef or DefDef */
27-
type DefRecheck = (tpd.ValOrDefDef, Symbol) => Context ?=> Type
28-
2927
/** Setup procedure to run for each compilation unit
3028
* @param tree the typed tree of the unit to check
31-
* @param recheckDef the recheck method to run on completion of symbols with
32-
* inferred (result-) types
29+
* @param checker the capture checker which will run subsequently.
3330
*/
34-
def setupUnit(tree: Tree, recheckDef: DefRecheck)(using Context): Unit
31+
def setupUnit(tree: Tree, checker: CheckerAPI)(using Context): Unit
3532

3633
/** Symbol is a term member of a class that was not capture checked
3734
* The info of these symbols is made fluid.
@@ -378,15 +375,6 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
378375
tp2
379376
end transformExplicitType
380377

381-
/** Transform type of tree, and remember the transformed type as the type the tree */
382-
private def transformTT(tree: TypeTree, boxed: Boolean)(using Context): Unit =
383-
if !tree.hasRememberedType then
384-
val transformed =
385-
if tree.isInferred
386-
then transformInferredType(tree.tpe)
387-
else transformExplicitType(tree.tpe, tptToCheck = tree)
388-
tree.rememberType(if boxed then box(transformed) else transformed)
389-
390378
/** Substitute parameter symbols in `from` to paramRefs in corresponding
391379
* method or poly types `to`. We use a single BiTypeMap to do everything.
392380
* @param from a list of lists of type or term parameter symbols of a curried method
@@ -436,7 +424,17 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
436424
atPhase(thisPhase.next)(sym.info)
437425

438426
/** A traverser that adds knownTypes and updates symbol infos */
439-
def setupTraverser(recheckDef: DefRecheck) = new TreeTraverserWithPreciseImportContexts:
427+
def setupTraverser(checker: CheckerAPI) = new TreeTraverserWithPreciseImportContexts:
428+
import checker.*
429+
430+
/** Transform type of tree, and remember the transformed type as the type the tree */
431+
private def transformTT(tree: TypeTree, boxed: Boolean)(using Context): Unit =
432+
if !tree.hasNuType then
433+
val transformed =
434+
if tree.isInferred
435+
then transformInferredType(tree.tpe)
436+
else transformExplicitType(tree.tpe, tptToCheck = tree)
437+
tree.setNuType(if boxed then box(transformed) else transformed)
440438

441439
/** Transform the type of a val or var or the result type of a def */
442440
def transformResultType(tpt: TypeTree, sym: Symbol)(using Context): Unit =
@@ -464,7 +462,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
464462
traverse(parent)
465463
case _ =>
466464
traverseChildren(tp)
467-
addDescription.traverse(tpt.knownType)
465+
addDescription.traverse(tpt.nuType)
468466
end transformResultType
469467

470468
def traverse(tree: Tree)(using Context): Unit =
@@ -504,7 +502,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
504502

505503
case tree @ SeqLiteral(elems, tpt: TypeTree) =>
506504
traverse(elems)
507-
tpt.rememberType(box(transformInferredType(tpt.tpe)))
505+
tpt.setNuType(box(transformInferredType(tpt.tpe)))
508506

509507
case tree: Block =>
510508
inNestedLevel(traverseChildren(tree))
@@ -537,22 +535,22 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
537535
// with special treatment for constructors.
538536
def localReturnType =
539537
if sym.isConstructor then constrReturnType(sym.info, sym.paramSymss)
540-
else tree.tpt.knownType
538+
else tree.tpt.nuType
541539

542540
// A test whether parameter signature might change. This returns true if one of
543-
// the parameters has a remembered type. The idea here is that we store a remembered
541+
// the parameters has a new type installee. The idea here is that we store a new
544542
// type only if the transformed type is different from the original.
545543
def paramSignatureChanges = tree.match
546544
case tree: DefDef =>
547545
tree.paramss.nestedExists:
548-
case param: ValDef => param.tpt.hasRememberedType
549-
case param: TypeDef => param.rhs.hasRememberedType
546+
case param: ValDef => param.tpt.hasNuType
547+
case param: TypeDef => param.rhs.hasNuType
550548
case _ => false
551549

552550
// A symbol's signature changes if some of its parameter types or its result type
553551
// have a new type installed here (meaning hasRememberedType is true)
554552
def signatureChanges =
555-
tree.tpt.hasRememberedType && !sym.isConstructor || paramSignatureChanges
553+
tree.tpt.hasNuType && !sym.isConstructor || paramSignatureChanges
556554

557555
// Replace an existing symbol info with inferred types where capture sets of
558556
// TypeParamRefs and TermParamRefs are put in correspondence by BiTypeMaps with the
@@ -616,7 +614,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
616614
capt.println(i"forcing $sym, printing = ${ctx.mode.is(Mode.Printing)}")
617615
//if ctx.mode.is(Mode.Printing) then new Error().printStackTrace()
618616
denot.info = newInfo
619-
recheckDef(tree, sym)
617+
completeDef(tree, sym)
620618
updateInfo(sym, updatedInfo)
621619

622620
case tree: Bind =>
@@ -833,8 +831,8 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
833831
/** Run setup on a compilation unit with given `tree`.
834832
* @param recheckDef the function to run for completing a val or def
835833
*/
836-
def setupUnit(tree: Tree, recheckDef: DefRecheck)(using Context): Unit =
837-
setupTraverser(recheckDef).traverse(tree)(using ctx.withPhase(thisPhase))
834+
def setupUnit(tree: Tree, checker: CheckerAPI)(using Context): Unit =
835+
setupTraverser(checker).traverse(tree)(using ctx.withPhase(thisPhase))
838836

839837
// ------ Checks to run after main capture checking --------------------------
840838

0 commit comments

Comments
 (0)