Skip to content

Commit a6f6dc0

Browse files
committed
Sema: Fix order dependency between lazy getter body synthesis and capture computation
If we computed captures before completing a lazy getter body, we would fail to consider the 'self' capture properly. Instead make it resilient to such ordering issues by checking in capture computation if the lazy property has a getter yet or not.
1 parent ac53f89 commit a6f6dc0

File tree

4 files changed

+28
-14
lines changed

4 files changed

+28
-14
lines changed

lib/Sema/CodeSynthesis.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,10 +203,10 @@ static AccessorDecl *createGetterPrototype(AbstractStorageDecl *storage,
203203
getter->setImplicit();
204204

205205
// If we're stealing the 'self' from a lazy initializer, set it now.
206-
if (selfDecl) {
206+
// Note that we don't re-parent the 'self' declaration to be part of
207+
// the getter until we synthesize the body of the getter later.
208+
if (selfDecl)
207209
*getter->getImplicitSelfDeclStorage() = selfDecl;
208-
selfDecl->setDeclContext(getter);
209-
}
210210

211211
// We need to install the generic environment here because:
212212
// 1) validating the getter will change the implicit self decl's DC to it,
@@ -1292,6 +1292,7 @@ static void synthesizeLazyGetterBody(AbstractFunctionDecl *fn, void *context) {
12921292

12931293
// Recontextualize any closure declcontexts nested in the initializer to
12941294
// realize that they are in the getter function.
1295+
Get->getImplicitSelfDecl()->setDeclContext(Get);
12951296
InitValue->walk(RecontextualizeClosures(Get));
12961297

12971298
// Wrap the initializer in a LazyInitializerExpr to avoid problems with

lib/Sema/TypeCheckCaptures.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -283,15 +283,14 @@ class FindCapturedVars : public ASTWalker {
283283
// recontextualized into it, so treat it as if it's already there.
284284
if (auto init = dyn_cast<PatternBindingInitializer>(TmpDC)) {
285285
if (auto lazyVar = init->getInitializedLazyVar()) {
286-
// Referring to the 'self' parameter is fine.
287-
if (D == init->getImplicitSelfDecl())
288-
return { false, DRE };
289-
290-
// Otherwise, act as if we're in the getter.
291-
auto getter = lazyVar->getGetter();
292-
assert(getter && "lazy variable without getter");
293-
TmpDC = getter;
294-
continue;
286+
// If we have a getter with a body, we're already re-parented
287+
// everything so pretend we're inside the getter.
288+
if (auto getter = lazyVar->getGetter()) {
289+
if (getter->getBody(/*canSynthesize=*/false)) {
290+
TmpDC = getter;
291+
continue;
292+
}
293+
}
295294
}
296295
}
297296

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
11
public struct Test1 {
2-
lazy var property: String = "help"
2+
lazy var property: String = "help"
3+
}
4+
5+
public class Test2 {
6+
var x = 0
7+
var y = 1
8+
9+
lazy var property = {
10+
return [self.x, self.y]
11+
}()
312
}

test/multifile/lazy.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
// RUN: %target-swift-frontend -emit-sil -verify -primary-file %s %S/Inputs/external_lazy_property.swift
2+
// RUN: %target-swift-frontend -emit-sil -verify -primary-file %s -primary-file %S/Inputs/external_lazy_property.swift
23

34
// rdar://45712204
45
func test1(s: Test1) {
5-
s.property // expected-error {{cannot use mutating getter on immutable value: 's' is a 'let' constant}}
6+
_ = s.property // expected-error {{cannot use mutating getter on immutable value: 's' is a 'let' constant}}
7+
}
8+
9+
func test2(s: Test2) {
10+
_ = s.property
611
}

0 commit comments

Comments
 (0)