Skip to content

Commit 3521f6b

Browse files
committed
Initial lazy vals support
1 parent 5c51b7b commit 3521f6b

File tree

1 file changed

+20
-6
lines changed

1 file changed

+20
-6
lines changed

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

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ object CheckCaptures:
8181
end Env
8282

8383
def definesEnv(sym: Symbol)(using Context): Boolean =
84-
sym.is(Method) || sym.isClass
84+
sym.is(Method) || sym.isClass || sym.is(Lazy)
8585

8686
/** Similar normal substParams, but this is an approximating type map that
8787
* maps parameters in contravariant capture sets to the empty set.
@@ -225,7 +225,7 @@ object CheckCaptures:
225225
def needsSepCheck: Boolean
226226

227227
/** If a tree is an argument for which needsSepCheck is true,
228-
* the type of the formal paremeter corresponding to the argument.
228+
* the type of the formal parameter corresponding to the argument.
229229
*/
230230
def formalType: Type
231231

@@ -441,7 +441,7 @@ class CheckCaptures extends Recheck, SymTransformer:
441441
*/
442442
def capturedVars(sym: Symbol)(using Context): CaptureSet =
443443
myCapturedVars.getOrElseUpdate(sym,
444-
if sym.isTerm || !sym.owner.isStaticOwner
444+
if sym.isTerm || !sym.owner.isStaticOwner || sym.is(Lazy) // FIXME: are lazy vals in static owners a thing?
445445
then CaptureSet.Var(sym, nestedOK = false)
446446
else CaptureSet.empty)
447447

@@ -655,8 +655,10 @@ class CheckCaptures extends Recheck, SymTransformer:
655655
*/
656656
override def recheckIdent(tree: Ident, pt: Type)(using Context): Type =
657657
val sym = tree.symbol
658-
if sym.is(Method) then
659-
// If ident refers to a parameterless method, charge its cv to the environment
658+
if sym.is(Method) || sym.is(Lazy) then
659+
// If ident refers to a parameterless method or lazy val, charge its cv to the environment.
660+
// Lazy vals are like parameterless methods: accessing them may trigger initialization
661+
// that uses captured references.
660662
includeCallCaptures(sym, sym.info, tree)
661663
else if sym.exists && !sym.isStatic then
662664
markPathFree(sym.termRef, pt, tree)
@@ -1078,6 +1080,7 @@ class CheckCaptures extends Recheck, SymTransformer:
10781080
* - for externally visible definitions: check that their inferred type
10791081
* does not refine what was known before capture checking.
10801082
* - Interpolate contravariant capture set variables in result type.
1083+
* - for lazy vals: create a nested environment to track captures (similar to methods)
10811084
*/
10821085
override def recheckValDef(tree: ValDef, sym: Symbol)(using Context): Type =
10831086
val savedEnv = curEnv
@@ -1100,8 +1103,16 @@ class CheckCaptures extends Recheck, SymTransformer:
11001103
""
11011104
disallowBadRootsIn(
11021105
tree.tpt.nuType, NoSymbol, i"Mutable $sym", "have type", addendum, sym.srcPos)
1103-
if runInConstructor then
1106+
1107+
// Lazy vals need their own environment to track captures from their RHS,
1108+
// similar to how methods work
1109+
if sym.is(Lazy) then
1110+
val localSet = capturedVars(sym)
1111+
if localSet ne CaptureSet.empty then
1112+
curEnv = Env(sym, EnvKind.Regular, localSet, curEnv, nestedClosure = NoSymbol)
1113+
else if runInConstructor then
11041114
pushConstructorEnv()
1115+
11051116
checkInferredResult(super.recheckValDef(tree, sym), tree)
11061117
finally
11071118
if !sym.is(Param) then
@@ -1115,6 +1126,9 @@ class CheckCaptures extends Recheck, SymTransformer:
11151126
if runInConstructor && savedEnv.owner.isClass then
11161127
curEnv = savedEnv
11171128
markFree(declaredCaptures, tree, addUseInfo = false)
1129+
else if sym.is(Lazy) then
1130+
// Restore environment after checking lazy val
1131+
curEnv = savedEnv
11181132

11191133
if sym.owner.isStaticOwner && !declaredCaptures.elems.isEmpty && sym != defn.captureRoot then
11201134
def where =

0 commit comments

Comments
 (0)