Skip to content

Commit 3702473

Browse files
authored
Merge pull request swiftlang#35803 from slavapestov/lazy-local-vars
Allow 'lazy' local variables
2 parents efbfaac + ed247a1 commit 3702473

File tree

9 files changed

+101
-34
lines changed

9 files changed

+101
-34
lines changed

CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,33 @@ Swift Next
4949
}
5050
```
5151
52+
* The 'lazy' keyword now works in local contexts, making the following valid:
53+
54+
```swift
55+
func test(useIt: Bool) {
56+
lazy var result = getPotentiallyExpensiveResult()
57+
if useIt {
58+
doIt(result)
59+
}
60+
}
61+
```
62+
5263
Swift 5.4
5364
---------
5465

66+
* Property wrappers now work in local contexts, making the following valid:
67+
68+
```swift
69+
@propertyWrapper
70+
struct Wrapper<T> {
71+
var wrappedValue: T
72+
}
73+
74+
func test() {
75+
@Wrapper var value = 10
76+
}
77+
```
78+
5579
* [SR-10069][]:
5680

5781
Function overloading now works in local contexts, making the following valid:

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3076,20 +3076,18 @@ ERROR(attr_MainType_without_main,none,
30763076
ERROR(lazy_not_on_let,none,
30773077
"'lazy' cannot be used on a let", ())
30783078
ERROR(lazy_not_on_computed,none,
3079-
"'lazy' must not be used on a computed property", ())
3079+
"'lazy' cannot be used on a computed property", ())
30803080

30813081
ERROR(lazy_on_already_lazy_global,none,
3082-
"'lazy' must not be used on an already-lazy global", ())
3082+
"'lazy' cannot be used on an already-lazy global", ())
30833083
ERROR(lazy_not_in_protocol,none,
3084-
"'lazy' isn't allowed on a protocol requirement", ())
3084+
"'lazy' cannot be used on a protocol requirement", ())
30853085
ERROR(lazy_requires_initializer,none,
30863086
"lazy properties must have an initializer", ())
30873087

30883088
ERROR(lazy_requires_single_var,none,
30893089
"'lazy' cannot destructure an initializer", ())
30903090

3091-
ERROR(lazy_must_be_property,none,
3092-
"lazy is only valid for members of a struct or class", ())
30933091
ERROR(lazy_not_strong,none,
30943092
"lazy properties cannot be %0", (ReferenceOwnership))
30953093
ERROR(lazy_var_storage_access,none,

lib/AST/Decl.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5936,16 +5936,18 @@ void VarDecl::visitAuxiliaryDecls(llvm::function_ref<void(VarDecl *)> visit) con
59365936
if (getDeclContext()->isTypeContext())
59375937
return;
59385938

5939-
// Avoid request evaluator overhead in the common case where there's
5940-
// no wrapper.
5941-
if (!getAttrs().hasAttribute<CustomAttr>())
5942-
return;
5939+
if (getAttrs().hasAttribute<LazyAttr>()) {
5940+
if (auto *backingVar = getLazyStorageProperty())
5941+
visit(backingVar);
5942+
}
59435943

5944-
if (auto *backingVar = getPropertyWrapperBackingProperty())
5945-
visit(backingVar);
5944+
if (getAttrs().hasAttribute<CustomAttr>()) {
5945+
if (auto *backingVar = getPropertyWrapperBackingProperty())
5946+
visit(backingVar);
59465947

5947-
if (auto *projectionVar = getPropertyWrapperProjectionVar())
5948-
visit(projectionVar);
5948+
if (auto *projectionVar = getPropertyWrapperProjectionVar())
5949+
visit(projectionVar);
5950+
}
59495951
}
59505952

59515953
VarDecl *VarDecl::getLazyStorageProperty() const {

lib/SIL/IR/TypeLowering.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2790,6 +2790,14 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) {
27902790
collectFunctionCaptures(accessor);
27912791
};
27922792

2793+
// 'Lazy' properties don't fit into the below categorization,
2794+
// and they have a synthesized getter, not a parsed one.
2795+
if (capturedVar->getAttrs().hasAttribute<LazyAttr>()) {
2796+
if (auto *getter = capturedVar->getSynthesizedAccessor(
2797+
AccessorKind::Get))
2798+
collectFunctionCaptures(getter);
2799+
}
2800+
27932801
if (!capture.isDirect()) {
27942802
auto impl = capturedVar->getImplInfo();
27952803

lib/SILGen/SILGenDecl.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1227,14 +1227,15 @@ void SILGenFunction::visitVarDecl(VarDecl *D) {
12271227
auto wrapperInfo = D->getPropertyWrapperBackingPropertyInfo();
12281228
if (wrapperInfo && wrapperInfo.initializeFromOriginal)
12291229
SGM.emitPropertyWrapperBackingInitializer(D);
1230+
}
12301231

1231-
D->visitAuxiliaryDecls([&](VarDecl *var) {
1232-
if (auto *patternBinding = var->getParentPatternBinding())
1233-
visitPatternBindingDecl(patternBinding);
1232+
// Emit lazy and property wrapper backing storage.
1233+
D->visitAuxiliaryDecls([&](VarDecl *var) {
1234+
if (auto *patternBinding = var->getParentPatternBinding())
1235+
visitPatternBindingDecl(patternBinding);
12341236

1235-
visit(var);
1236-
});
1237-
}
1237+
visit(var);
1238+
});
12381239

12391240
// Emit the variable's accessors.
12401241
D->visitEmittedAccessors([&](AccessorDecl *accessor) {

lib/Sema/TypeCheckAttr.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -867,14 +867,8 @@ void AttributeChecker::visitLazyAttr(LazyAttr *attr) {
867867

868868
// 'lazy' is not allowed on a global variable or on a static property (which
869869
// are already lazily initialized).
870-
// TODO: we can't currently support lazy properties on non-type-contexts.
871-
if (VD->isStatic() ||
872-
(varDC->isModuleScopeContext() &&
873-
!varDC->getParentSourceFile()->isScriptMode())) {
870+
if (VD->isStatic() || varDC->isModuleScopeContext())
874871
diagnoseAndRemoveAttr(attr, diag::lazy_on_already_lazy_global);
875-
} else if (!VD->getDeclContext()->isTypeContext()) {
876-
diagnoseAndRemoveAttr(attr, diag::lazy_must_be_property);
877-
}
878872
}
879873

880874
bool AttributeChecker::visitAbstractAccessControlAttr(

lib/Sema/TypeCheckStorage.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1330,7 +1330,8 @@ synthesizeLazyGetterBody(AccessorDecl *Get, VarDecl *VD, VarDecl *Storage,
13301330

13311331
// Recontextualize any closure declcontexts nested in the initializer to
13321332
// realize that they are in the getter function.
1333-
Get->getImplicitSelfDecl()->setDeclContext(Get);
1333+
if (Get->hasImplicitSelfDecl())
1334+
Get->getImplicitSelfDecl()->setDeclContext(Get);
13341335

13351336
InitValue->walk(RecontextualizeClosures(Get));
13361337

test/SILGen/lazy_locals.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %target-swift-emit-silgen -primary-file %s | %FileCheck %s
2+
// RUN: %target-swift-frontend -emit-ir -primary-file %s
3+
4+
// CHECK-LABEL: sil hidden [ossa] @$s11lazy_locals6simpleSiyF : $@convention(thin) () -> Int {
5+
func simple() -> Int {
6+
lazy var x = 123
7+
return x
8+
}
9+
10+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s11lazy_locals6simpleSiyF1xL_Sivg : $@convention(thin) (@guaranteed { var Optional<Int> }) -> Int {
11+
12+
// CHECK-LABEL: sil hidden [ossa] @$s11lazy_locals8captures1xS2i_tF : $@convention(thin) (Int) -> Int {
13+
func captures(x: Int) -> Int {
14+
let y = x * x
15+
lazy var z = x + y
16+
let fn = { z }
17+
return fn()
18+
}
19+
20+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s11lazy_locals8captures1xS2i_tF1zL_Sivg : $@convention(thin) (@guaranteed { var Optional<Int> }, Int, Int) -> Int {
21+
// CHECK-LABEL: sil private [ossa] @$s11lazy_locals8captures1xS2i_tFSiycfU_ : $@convention(thin) (@guaranteed { var Optional<Int> }, Int, Int) -> Int {
22+
23+
// CHECK-LABEL: sil hidden [ossa] @$s11lazy_locals6assignSiyF : $@convention(thin) () -> Int {
24+
func assign() -> Int {
25+
lazy var z = 123
26+
z = 321
27+
return z
28+
}
29+
30+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s11lazy_locals6assignSiyF1zL_Sivg : $@convention(thin) (@guaranteed { var Optional<Int> }) -> Int {
31+
// CHECK-LABEL: sil private [ossa] @$s11lazy_locals6assignSiyF1zL_Sivs : $@convention(thin) (Int, @guaranteed { var Optional<Int> }) -> () {
32+
33+
// CHECK-LABEL: sil hidden [ossa] @$s11lazy_locals7generic1xxx_tlF : $@convention(thin) <T> (@in_guaranteed T) -> @out T {
34+
func generic<T>(x: T) -> T {
35+
lazy var z = x
36+
return z
37+
}
38+
39+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s11lazy_locals7generic1xxx_tlF1zL_xvg : $@convention(thin) <T> (@guaranteed <τ_0_0> { var Optional<τ_0_0> } <T>, @in_guaranteed T) -> @out T {

test/decl/var/lazy_properties.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@
22

33
lazy func lazy_func() {} // expected-error {{'lazy' may only be used on 'var' declarations}} {{1-6=}}
44

5-
lazy var b = 42 // expected-error {{'lazy' must not be used on an already-lazy global}} {{1-6=}}
5+
lazy var b = 42 // expected-error {{'lazy' cannot be used on an already-lazy global}} {{1-6=}}
66

77
struct S {
8-
lazy static var lazy_global = 42 // expected-error {{'lazy' must not be used on an already-lazy global}} {{3-8=}}
8+
lazy static var lazy_global = 42 // expected-error {{'lazy' cannot be used on an already-lazy global}} {{3-8=}}
99
}
1010

1111
protocol SomeProtocol {
12-
lazy var x : Int // expected-error {{'lazy' isn't allowed on a protocol requirement}} {{3-8=}}
12+
lazy var x : Int // expected-error {{'lazy' cannot be used on a protocol requirement}} {{3-8=}}
1313
// expected-error@-1 {{property in protocol must have explicit { get } or { get set } specifier}} {{19-19= { get <#set#> \}}}
1414
// expected-error@-2 {{lazy properties must have an initializer}}
15-
lazy var y : Int { get } // expected-error {{'lazy' isn't allowed on a protocol requirement}} {{3-8=}}
16-
// expected-error@-1 {{'lazy' must not be used on a computed property}}
15+
lazy var y : Int { get } // expected-error {{'lazy' cannot be used on a protocol requirement}} {{3-8=}}
16+
// expected-error@-1 {{'lazy' cannot be used on a computed property}}
1717
// expected-error@-2 {{lazy properties must have an initializer}}
1818
}
1919

@@ -24,7 +24,7 @@ class TestClass {
2424

2525
lazy let b = 42 // expected-error {{'lazy' cannot be used on a let}} {{3-8=}}
2626

27-
lazy var c : Int { return 42 } // expected-error {{'lazy' must not be used on a computed property}} {{3-8=}}
27+
lazy var c : Int { return 42 } // expected-error {{'lazy' cannot be used on a computed property}} {{3-8=}}
2828
// expected-error@-1 {{lazy properties must have an initializer}}
2929

3030
lazy var d : Int // expected-error {{lazy properties must have an initializer}} {{3-8=}}
@@ -55,7 +55,7 @@ class TestClass {
5555
}
5656

5757
init() {
58-
lazy var localvar = 42 // expected-error {{lazy is only valid for members of a struct or class}} {{5-10=}}
58+
lazy var localvar = 42 // Okay
5959
localvar += 1
6060
_ = localvar
6161
}

0 commit comments

Comments
 (0)