Skip to content

Commit fd80aa0

Browse files
authored
Merge pull request #9920 from slavapestov/lazy-without-explicit-self
Allow lazy property initializers to reference instance members without explicit 'self.'
2 parents 5b091ce + ab7d222 commit fd80aa0

File tree

10 files changed

+145
-80
lines changed

10 files changed

+145
-80
lines changed

include/swift/AST/Initializer.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,17 +70,21 @@ class Initializer : public DeclContext {
7070
class PatternBindingInitializer : public Initializer {
7171
PatternBindingDecl *Binding;
7272

73+
// created lazily for 'self' lookup from lazy property initializer
74+
ParamDecl *SelfParam;
75+
7376
friend class ASTContext; // calls reset on unused contexts
7477

7578
void reset(DeclContext *parent) {
7679
setParent(parent);
7780
Binding = nullptr;
81+
SelfParam = nullptr;
7882
}
7983

8084
public:
8185
explicit PatternBindingInitializer(DeclContext *parent)
8286
: Initializer(InitializerKind::PatternBinding, parent),
83-
Binding(nullptr) {
87+
Binding(nullptr), SelfParam(nullptr) {
8488
SpareBits = 0;
8589
}
8690

@@ -95,6 +99,8 @@ class PatternBindingInitializer : public Initializer {
9599

96100
unsigned getBindingIndex() const { return SpareBits; }
97101

102+
ParamDecl *getImplicitSelfDecl();
103+
98104
static bool classof(const DeclContext *DC) {
99105
if (auto init = dyn_cast<Initializer>(DC))
100106
return classof(init);

include/swift/AST/LazyResolver.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,6 @@ class LazyResolver {
8282
/// Bind an extension to its extended type.
8383
virtual void bindExtension(ExtensionDecl *ext) = 0;
8484

85-
/// Introduce the accessors for a 'lazy' variable.
86-
virtual void introduceLazyVarAccessors(VarDecl *var) = 0;
87-
8885
/// Resolve the type of an extension.
8986
///
9087
/// This can be called to ensure that the members of an extension can be
@@ -151,10 +148,6 @@ class DelegatingLazyResolver : public LazyResolver {
151148
Principal.bindExtension(ext);
152149
}
153150

154-
void introduceLazyVarAccessors(VarDecl *var) override {
155-
Principal.introduceLazyVarAccessors(var);
156-
}
157-
158151
void resolveExtension(ExtensionDecl *ext) override {
159152
Principal.resolveExtension(ext);
160153
}

lib/AST/ASTScope.cpp

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1903,28 +1903,17 @@ SmallVector<ValueDecl *, 4> ASTScope::getLocalBindings() const {
19031903
}
19041904
break;
19051905

1906-
case ASTScopeKind::PatternInitializer:
1907-
// FIXME: This causes recursion that we cannot yet handle.
1908-
#if false
1906+
case ASTScopeKind::PatternInitializer: {
19091907
// 'self' is available within the pattern initializer of a 'lazy' variable.
1910-
if (auto singleVar = patternBinding.decl->getSingleVar()) {
1911-
if (singleVar->getAttrs().hasAttribute<LazyAttr>() &&
1912-
singleVar->getDeclContext()->isTypeContext()) {
1913-
// If there is no getter (yet), add them.
1914-
if (!singleVar->getGetter()) {
1915-
ASTContext &ctx = singleVar->getASTContext();
1916-
if (auto resolver = ctx.getLazyResolver())
1917-
resolver->introduceLazyVarAccessors(singleVar);
1918-
}
1919-
1920-
// Add the getter's 'self'.
1921-
if (auto getter = singleVar->getGetter())
1922-
if (auto self = getter->getImplicitSelfDecl())
1923-
result.push_back(self);
1924-
}
1908+
auto *initContext = cast_or_null<PatternBindingInitializer>(
1909+
patternBinding.decl->getPatternList()[0].getInitContext());
1910+
if (initContext) {
1911+
if (auto *selfParam = initContext->getImplicitSelfDecl())
1912+
result.push_back(selfParam);
19251913
}
1926-
#endif
1914+
19271915
break;
1916+
}
19281917

19291918
case ASTScopeKind::Closure:
19301919
// Note: Parameters all at once is different from functions, but it's not

lib/AST/Decl.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,25 @@ PatternBindingDecl *PatternBindingDecl::createDeserialized(
986986
return PBD;
987987
}
988988

989+
ParamDecl *PatternBindingInitializer::getImplicitSelfDecl() {
990+
if (SelfParam)
991+
return SelfParam;
992+
993+
if (auto singleVar = getBinding()->getSingleVar()) {
994+
auto *DC = singleVar->getDeclContext();
995+
if (singleVar->getAttrs().hasAttribute<LazyAttr>() &&
996+
DC->isTypeContext()) {
997+
bool isInOut = !DC->getDeclaredTypeOfContext()->hasReferenceSemantics();
998+
SelfParam = ParamDecl::createSelf(SourceLoc(), DC,
999+
singleVar->isStatic(),
1000+
isInOut);
1001+
SelfParam->setDeclContext(this);
1002+
}
1003+
}
1004+
1005+
return SelfParam;
1006+
}
1007+
9891008
static bool patternContainsVarDeclBinding(const Pattern *P, const VarDecl *VD) {
9901009
bool Result = false;
9911010
P->forEachVariable([&](VarDecl *FoundVD) {
@@ -3916,9 +3935,12 @@ Pattern *VarDecl::getParentPattern() const {
39163935
}
39173936

39183937
bool VarDecl::isSelfParameter() const {
3919-
if (isa<ParamDecl>(this))
3938+
if (isa<ParamDecl>(this)) {
39203939
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(getDeclContext()))
39213940
return AFD->getImplicitSelfDecl() == this;
3941+
if (auto *PBI = dyn_cast<PatternBindingInitializer>(getDeclContext()))
3942+
return PBI->getImplicitSelfDecl() == this;
3943+
}
39223944

39233945
return false;
39243946
}

lib/AST/NameLookup.cpp

Lines changed: 49 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -510,29 +510,11 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC,
510510
// Pattern binding initializers are only interesting insofar as they
511511
// affect lookup in an enclosing nominal type or extension thereof.
512512
if (auto *bindingInit = dyn_cast<PatternBindingInitializer>(dc)) {
513-
if (auto binding = bindingInit->getBinding()) {
514-
// Look for 'self' for a lazy variable initializer.
515-
if (auto singleVar = binding->getSingleVar())
516-
// We only care about lazy variables.
517-
if (singleVar->getAttrs().hasAttribute<LazyAttr>()) {
518-
519-
// 'self' will be listed in the local bindings.
520-
for (auto local : localBindings) {
521-
auto param = dyn_cast<ParamDecl>(local);
522-
if (!param) continue;
523-
524-
525-
// If we have a variable that's the implicit self of its enclosing
526-
// context, mark it as 'self'.
527-
if (auto func = dyn_cast<FuncDecl>(param->getDeclContext())) {
528-
if (param == func->getImplicitSelfDecl()) {
529-
selfDecl = param;
530-
break;
531-
}
532-
}
533-
}
534-
}
535-
}
513+
// Lazy variable initializer contexts have a 'self' parameter for
514+
// instance member lookup.
515+
if (auto *selfParam = bindingInit->getImplicitSelfDecl())
516+
selfDecl = selfParam;
517+
536518
continue;
537519
}
538520

@@ -646,17 +628,46 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC,
646628
Type ExtendedType;
647629
bool isTypeLookup = false;
648630

649-
// If this declcontext is an initializer for a static property, then we're
650-
// implicitly doing a static lookup into the parent declcontext.
651-
if (auto *PBI = dyn_cast<PatternBindingInitializer>(DC))
652-
if (!DC->getParent()->isModuleScopeContext()) {
653-
if (auto *PBD = PBI->getBinding()) {
654-
isTypeLookup = PBD->isStatic();
655-
DC = DC->getParent();
656-
}
631+
if (auto *PBI = dyn_cast<PatternBindingInitializer>(DC)) {
632+
auto *PBD = PBI->getBinding();
633+
assert(PBD);
634+
635+
// Lazy variable initializer contexts have a 'self' parameter for
636+
// instance member lookup.
637+
if (auto *selfParam = PBI->getImplicitSelfDecl()) {
638+
Consumer.foundDecl(selfParam,
639+
DeclVisibilityKind::FunctionParameter);
640+
if (!Results.empty())
641+
return;
642+
643+
DC = DC->getParent();
644+
645+
BaseDecl = selfParam;
646+
ExtendedType = DC->getSelfTypeInContext();
647+
MetaBaseDecl = DC->getAsNominalTypeOrNominalTypeExtensionContext();
648+
649+
isTypeLookup = PBD->isStatic();
650+
}
651+
// Initializers for stored properties of types perform static
652+
// lookup into the surrounding context.
653+
else if (PBD->getDeclContext()->isTypeContext()) {
654+
DC = DC->getParent();
655+
656+
ExtendedType = DC->getSelfTypeInContext();
657+
MetaBaseDecl = DC->getAsNominalTypeOrNominalTypeExtensionContext();
658+
BaseDecl = MetaBaseDecl;
659+
660+
isTypeLookup = PBD->isStatic(); // FIXME
661+
662+
isCascadingUse = DC->isCascadingContextForLookup(false);
657663
}
658-
659-
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(DC)) {
664+
// Otherwise, we have an initializer for a global or local property.
665+
// There's not much to find here, we'll keep going up to a parent
666+
// context.
667+
668+
if (!isCascadingUse.hasValue())
669+
isCascadingUse = DC->isCascadingContextForLookup(false);
670+
} else if (auto *AFD = dyn_cast<AbstractFunctionDecl>(DC)) {
660671
// Look for local variables; normally, the parser resolves these
661672
// for us, but it can't do the right thing inside local types.
662673
// FIXME: when we can parse and typecheck the function body partially
@@ -690,9 +701,12 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC,
690701
if (FD->isStatic())
691702
isTypeLookup = true;
692703

693-
// If we're not in the body of the function, the base declaration
704+
// If we're not in the body of the function (for example, we
705+
// might be type checking a default argument expression and
706+
// performing name lookup from there), the base declaration
694707
// is the nominal type, not 'self'.
695-
if (Loc.isValid() &&
708+
if (!AFD->isImplicit() &&
709+
Loc.isValid() &&
696710
AFD->getBodySourceRange().isValid() &&
697711
!SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc)) {
698712
BaseDecl = MetaBaseDecl;

lib/Sema/CodeSynthesis.cpp

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/AST/Availability.h"
2323
#include "swift/AST/Expr.h"
2424
#include "swift/AST/GenericEnvironment.h"
25+
#include "swift/AST/Initializer.h"
2526
#include "swift/AST/ParameterList.h"
2627
#include "swift/AST/ProtocolConformance.h"
2728
#include "swift/Basic/Defer.h"
@@ -143,10 +144,25 @@ static FuncDecl *createGetterPrototype(AbstractStorageDecl *storage,
143144
SmallVector<ParameterList*, 2> getterParams;
144145

145146
// The implicit 'self' argument if in a type context.
146-
if (storage->getDeclContext()->isTypeContext())
147-
getterParams.push_back(ParameterList::createSelf(loc,
148-
storage->getDeclContext(),
149-
/*isStatic*/false));
147+
if (storage->getDeclContext()->isTypeContext()) {
148+
ParamDecl *selfDecl;
149+
150+
// For lazy properties, steal the 'self' from the initializer context.
151+
if (storage->getAttrs().hasAttribute<LazyAttr>()) {
152+
auto *varDecl = cast<VarDecl>(storage);
153+
auto *bindingDecl = varDecl->getParentPatternBinding();
154+
auto *bindingInit = cast<PatternBindingInitializer>(
155+
bindingDecl->getPatternEntryForVarDecl(varDecl).getInitContext());
156+
157+
selfDecl = bindingInit->getImplicitSelfDecl();
158+
} else {
159+
selfDecl = ParamDecl::createSelf(loc,
160+
storage->getDeclContext(),
161+
/*isStatic*/false);
162+
}
163+
164+
getterParams.push_back(ParameterList::create(TC.Context, selfDecl));
165+
}
150166

151167
// Add an index-forwarding clause.
152168
getterParams.push_back(buildIndexForwardingParamList(storage, {}));
@@ -1146,7 +1162,6 @@ static FuncDecl *completeLazyPropertyGetter(VarDecl *VD, VarDecl *Storage,
11461162
// realize that they are in the getter function.
11471163
InitValue->walk(RecontextualizeClosures(Get));
11481164

1149-
11501165
Pattern *Tmp2PBDPattern = new (Ctx) NamedPattern(Tmp2VD, /*implicit*/true);
11511166
Tmp2PBDPattern = new (Ctx) TypedPattern(Tmp2PBDPattern,
11521167
TypeLoc::withoutLoc(VD->getType()),
@@ -1673,10 +1688,6 @@ void swift::maybeAddMaterializeForSet(AbstractStorageDecl *storage,
16731688
addMaterializeForSet(storage, TC);
16741689
}
16751690

1676-
void TypeChecker::introduceLazyVarAccessors(VarDecl *var) {
1677-
maybeAddAccessorsToVariable(var, *this);
1678-
}
1679-
16801691
void swift::maybeAddAccessorsToVariable(VarDecl *var, TypeChecker &TC) {
16811692
if (var->getGetter())
16821693
return;

lib/Sema/TypeChecker.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,9 +1229,6 @@ class TypeChecker final : public LazyResolver {
12291229
handleExternalDecl(nominal);
12301230
}
12311231

1232-
/// Introduce the accessors for a 'lazy' variable.
1233-
void introduceLazyVarAccessors(VarDecl *var) override;
1234-
12351232
/// Infer default value witnesses for all requirements in the given protocol.
12361233
void inferDefaultWitnesses(ProtocolDecl *proto);
12371234

test/NameBinding/name_lookup.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ class r21677702 {
528528
// <rdar://problem/16954496> lazy properties must use "self." in their body, and can weirdly refer to class variables directly
529529
class r16954496 {
530530
func bar() {}
531-
lazy var x: Array<(r16954496) -> () -> Void> = [bar]
531+
lazy var x: Array<() -> Void> = [bar]
532532
}
533533

534534

test/NameBinding/scope_map.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -460,8 +460,7 @@ class LazyProperties {
460460
// CHECK-SEARCHES-NEXT: ClassDecl name=LazyProperties
461461
// CHECK-SEARCHES-NEXT: Initializer PatternBinding {{.*}} #0
462462

463-
// FIXME: Re-enable the binding below
464-
// CHECK-SEARCHES-NOT: Local bindings: self
463+
// CHECK-SEARCHES-NEXT: Local bindings: self
465464

466465
// CHECK-SEARCHES-LABEL: ***Complete scope map***
467466
// CHECK-SEARCHES-NEXT: SourceFile {{.*}} '{{.*}}scope_map.swift' [1:1 - [[EOF:[0-9]+:[0-9]+]]] unexpanded

test/decl/var/lazy_properties.swift

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// RUN: %target-typecheck-verify-swift -parse-as-library
1+
// RUN: %target-typecheck-verify-swift -parse-as-library -swift-version 3
2+
// RUN: %target-typecheck-verify-swift -parse-as-library -swift-version 4
23

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

@@ -72,7 +73,8 @@ struct StructTest {
7273

7374
// <rdar://problem/16889110> capture lists in lazy member properties cannot use self
7475
class CaptureListInLazyProperty {
75-
lazy var closure: () -> Int = { [weak self] in return self!.i }
76+
lazy var closure1 = { [weak self] in return self!.i }
77+
lazy var closure2: () -> Int = { [weak self] in return self!.i }
7678
var i = 42
7779
}
7880

@@ -119,3 +121,35 @@ struct Construction {
119121
class Constructor {
120122
lazy var myQ = Construction(x: 3)
121123
}
124+
125+
126+
// Problems with self references
127+
class BaseClass {
128+
var baseInstanceProp = 42
129+
static var baseStaticProp = 42
130+
}
131+
132+
class ReferenceSelfInLazyProperty : BaseClass {
133+
lazy var refs = (i, f())
134+
lazy var trefs: (Int, Int) = (i, f())
135+
136+
lazy var qrefs = (self.i, self.f())
137+
lazy var qtrefs: (Int, Int) = (self.i, self.f())
138+
139+
lazy var crefs = { (i, f()) }()
140+
lazy var ctrefs: (Int, Int) = { (i, f()) }()
141+
142+
lazy var cqrefs = { (self.i, self.f()) }()
143+
lazy var cqtrefs: (Int, Int) = { (self.i, self.f()) }()
144+
145+
lazy var mrefs = { () -> (Int, Int) in return (i, f()) }()
146+
// expected-error@-1 {{call to method 'f' in closure requires explicit 'self.' to make capture semantics explicit}}
147+
// expected-error@-2 {{reference to property 'i' in closure requires explicit 'self.' to make capture semantics explicit}}
148+
lazy var mtrefs: (Int, Int) = { return (i, f()) }()
149+
150+
lazy var mqrefs = { () -> (Int, Int) in (self.i, self.f()) }()
151+
lazy var mqtrefs: (Int, Int) = { return (self.i, self.f()) }()
152+
153+
var i = 42
154+
func f() -> Int { return 0 }
155+
}

0 commit comments

Comments
 (0)