Skip to content

Commit 8047dda

Browse files
committed
Sema: Move finishStorageImplInfo() to StorageImplInfoRequest evaluation
This merges Sema's special logic for updating the ImplInfo of lazy properties and property wrappers with the StorageImplInfoRequest. I had to move some code from the parser into the StorageImplInfoRequest, to avoid request cycles caused by hasStorage() calls; this is the right thing to do anyway. Since hasStorage() now answers false for lazy properties and property wrappers, we have to move some diagnostic checks from checkDeclAttributesEarly() to the StorageImplInfoRequest implementation itself. Over time, I expect all of the checks currently in checkDeclAttributesEarly() and checkDeclAttributes() to either migrate to requests, or typeCheckDecl().
1 parent 6a30d45 commit 8047dda

File tree

9 files changed

+141
-125
lines changed

9 files changed

+141
-125
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,8 +304,6 @@ ERROR(observing_accessor_conflicts_with_accessor,none,
304304
(unsigned, StringRef))
305305
ERROR(observing_accessor_in_subscript,none,
306306
"%select{'willSet'|'didSet'}0 is not allowed in subscripts", (unsigned))
307-
ERROR(getset_init,none,
308-
"variable with getter/setter cannot have an initial value", ())
309307
ERROR(getset_cannot_be_implied,none,
310308
"variable with implied type cannot have implied getter/setter", ())
311309

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,6 +1377,9 @@ WARNING(enum_frozen_nonpublic,none,
13771377
"%0 has no effect on non-public enums", (DeclAttribute))
13781378

13791379
// Variables (var and let).
1380+
ERROR(getset_init,none,
1381+
"variable with getter/setter cannot have an initial value", ())
1382+
13801383
ERROR(unimplemented_static_var,none,
13811384
"%select{ERROR|static|class}1 stored properties not supported"
13821385
"%select{ in this context| in generic types| in classes| in protocol extensions}0"

lib/Parse/ParseDecl.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5282,12 +5282,6 @@ Parser::parseDeclVar(ParseDeclOptions Flags,
52825282
PatternInit != nullptr, Attributes, Decls);
52835283
if (boundVar.hasCodeCompletion())
52845284
return makeResult(makeParserCodeCompletionStatus());
5285-
if (PatternInit && boundVar.isNonNull() &&
5286-
!boundVar.get()->hasStorage()) {
5287-
diagnose(pattern->getLoc(), diag::getset_init)
5288-
.highlight(PatternInit->getSourceRange());
5289-
PatternInit = nullptr;
5290-
}
52915285
}
52925286

52935287
// Add all parsed vardecls to this scope.

lib/Sema/CodeSynthesis.cpp

Lines changed: 126 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2025,31 +2025,19 @@ void swift::triggerAccessorSynthesis(TypeChecker &TC,
20252025
});
20262026
}
20272027

2028-
static StorageImplInfo getProtocolStorageImpl(AbstractStorageDecl *storage) {
2029-
auto protocol = cast<ProtocolDecl>(storage->getDeclContext());
2030-
if (protocol->isObjC()) {
2031-
return StorageImplInfo::getComputed(storage->supportsMutation());
2032-
} else {
2033-
return StorageImplInfo::getOpaque(storage->supportsMutation(),
2034-
storage->getOpaqueReadOwnership());
2035-
}
2036-
}
2037-
20382028
/// Given a storage declaration in a protocol, set it up with the right
20392029
/// StorageImpl and add the right set of opaque accessors.
2040-
static void finishProtocolStorageImplInfo(AbstractStorageDecl *storage) {
2030+
static void finishProtocolStorageImplInfo(AbstractStorageDecl *storage,
2031+
StorageImplInfo &info) {
20412032
if (auto *var = dyn_cast<VarDecl>(storage)) {
2042-
if (var->hasStorage()) {
2043-
auto &ctx = var->getASTContext();
2033+
if (info.hasStorage()) {
20442034
// Protocols cannot have stored properties.
20452035
if (var->isLet()) {
2046-
ctx.Diags.diagnose(var->getLoc(),
2047-
diag::protocol_property_must_be_computed_var)
2036+
var->diagnose(diag::protocol_property_must_be_computed_var)
20482037
.fixItReplace(var->getParentPatternBinding()->getLoc(), "var")
20492038
.fixItInsertAfter(var->getTypeLoc().getLoc(), " { get }");
20502039
} else {
2051-
auto diag = ctx.Diags.diagnose(var->getLoc(),
2052-
diag::protocol_property_must_be_computed);
2040+
auto diag = var->diagnose(diag::protocol_property_must_be_computed);
20532041
auto braces = var->getBracesRange();
20542042

20552043
if (braces.isValid())
@@ -2060,15 +2048,59 @@ static void finishProtocolStorageImplInfo(AbstractStorageDecl *storage) {
20602048
}
20612049
}
20622050

2063-
storage->setImplInfo(getProtocolStorageImpl(storage));
2051+
auto protocol = cast<ProtocolDecl>(storage->getDeclContext());
2052+
if (protocol->isObjC()) {
2053+
info = StorageImplInfo::getComputed(info.supportsMutation());
2054+
} else {
2055+
info = StorageImplInfo::getOpaque(info.supportsMutation(),
2056+
storage->getOpaqueReadOwnership());
2057+
}
20642058
}
20652059

2066-
static void finishLazyVariableImplInfo(VarDecl *var) {
2067-
// If there are already accessors, something is invalid; bail out.
2068-
if (!var->getImplInfo().isSimpleStored())
2069-
return;
2060+
/// This emits a diagnostic with a fixit to remove the attribute.
2061+
template<typename ...ArgTypes>
2062+
void diagnoseAndRemoveAttr(Decl *D, DeclAttribute *attr,
2063+
ArgTypes &&...Args) {
2064+
auto &ctx = D->getASTContext();
2065+
ctx.Diags.diagnose(attr->getLocation(), std::forward<ArgTypes>(Args)...)
2066+
.fixItRemove(attr->getRangeWithAt());
2067+
}
2068+
2069+
static void finishLazyVariableImplInfo(VarDecl *var,
2070+
StorageImplInfo &info) {
2071+
auto *attr = var->getAttrs().getAttribute<LazyAttr>();
2072+
2073+
// It cannot currently be used on let's since we don't have a mutability model
2074+
// that supports it.
2075+
if (var->isLet())
2076+
diagnoseAndRemoveAttr(var, attr, diag::lazy_not_on_let);
2077+
2078+
// lazy must have an initializer.
2079+
if (!var->getParentInitializer())
2080+
diagnoseAndRemoveAttr(var, attr, diag::lazy_requires_initializer);
2081+
2082+
bool invalid = false;
20702083

2071-
var->setImplInfo(StorageImplInfo::getMutableComputed());
2084+
if (isa<ProtocolDecl>(var->getDeclContext())) {
2085+
diagnoseAndRemoveAttr(var, attr, diag::lazy_not_in_protocol);
2086+
invalid = true;
2087+
}
2088+
2089+
// Lazy properties must be written as stored properties in the source.
2090+
if (!info.isSimpleStored()) {
2091+
diagnoseAndRemoveAttr(var, attr,
2092+
info.hasStorage()
2093+
? diag::lazy_not_observable
2094+
: diag::lazy_not_on_computed);
2095+
invalid = true;
2096+
}
2097+
2098+
// The pattern binding must only bind a single variable.
2099+
if (!var->getParentPatternBinding()->getSingleVar())
2100+
diagnoseAndRemoveAttr(var, attr, diag::lazy_requires_single_var);
2101+
2102+
if (!invalid)
2103+
info = StorageImplInfo::getMutableComputed();
20722104
}
20732105

20742106
/// Determine whether all of the wrapped-value setters for the property
@@ -2090,8 +2122,21 @@ static bool allPropertyWrapperValueSettersAreAccessible(VarDecl *var) {
20902122
return true;
20912123
}
20922124

2093-
static void finishPropertyWrapperImplInfo(VarDecl *var) {
2125+
static void finishPropertyWrapperImplInfo(VarDecl *var,
2126+
StorageImplInfo &info) {
20942127
auto parentSF = var->getDeclContext()->getParentSourceFile();
2128+
if (!parentSF)
2129+
return;
2130+
2131+
// Properties with wrappers must not declare a getter or setter.
2132+
if (!info.hasStorage() && parentSF->Kind != SourceFileKind::Interface) {
2133+
auto &ctx = parentSF->getASTContext();
2134+
for (auto attr : var->getAttrs().getAttributes<CustomAttr>())
2135+
ctx.Diags.diagnose(attr->getLocation(), diag::property_wrapper_computed);
2136+
2137+
return;
2138+
}
2139+
20952140
bool wrapperSetterIsUsable =
20962141
var->getSetter() ||
20972142
(parentSF &&
@@ -2100,36 +2145,68 @@ static void finishPropertyWrapperImplInfo(VarDecl *var) {
21002145
allPropertyWrapperValueSettersAreAccessible(var));
21012146

21022147
if (wrapperSetterIsUsable)
2103-
var->setImplInfo(StorageImplInfo::getMutableComputed());
2148+
info = StorageImplInfo::getMutableComputed();
21042149
else
2105-
var->setImplInfo(StorageImplInfo::getImmutableComputed());
2150+
info = StorageImplInfo::getImmutableComputed();
21062151
}
21072152

2108-
static void finishNSManagedImplInfo(VarDecl *VD) {
2109-
// If it's not still stored, just bail out.
2110-
if (!VD->getImplInfo().isSimpleStored())
2111-
return;
2153+
static void finishNSManagedImplInfo(VarDecl *var,
2154+
StorageImplInfo &info) {
2155+
auto *attr = var->getAttrs().getAttribute<NSManagedAttr>();
21122156

2113-
VD->setImplInfo(StorageImplInfo::getMutableComputed());
2114-
}
2157+
if (var->isLet())
2158+
diagnoseAndRemoveAttr(var, attr, diag::attr_NSManaged_let_property);
21152159

2116-
static void finishStorageImplInfo(AbstractStorageDecl *storage) {
2117-
if (isa<ProtocolDecl>(storage->getDeclContext())) {
2118-
finishProtocolStorageImplInfo(storage);
2119-
return;
2160+
auto diagnoseNotStored = [&](unsigned kind) {
2161+
diagnoseAndRemoveAttr(var, attr, diag::attr_NSManaged_not_stored, kind);
2162+
};
2163+
2164+
// @NSManaged properties must be written as stored.
2165+
if (info.isSimpleStored()) {
2166+
// @NSManaged properties end up being computed; complain if there is
2167+
// an initializer.
2168+
if (var->getParentInitializer()) {
2169+
auto &Diags = var->getASTContext().Diags;
2170+
Diags.diagnose(attr->getLocation(), diag::attr_NSManaged_initial_value)
2171+
.highlight(var->getParentInitializer()->getSourceRange());
2172+
}
2173+
2174+
// Otherwise, ok.
2175+
info = StorageImplInfo::getMutableComputed();
2176+
2177+
} else if (info.getReadImpl() == ReadImplKind::Address ||
2178+
info.getWriteImpl() == WriteImplKind::MutableAddress) {
2179+
diagnoseNotStored(/*addressed*/ 2);
2180+
} else if (info.getWriteImpl() == WriteImplKind::StoredWithObservers ||
2181+
info.getWriteImpl() == WriteImplKind::InheritedWithObservers) {
2182+
diagnoseNotStored(/*observing*/ 1);
2183+
} else {
2184+
diagnoseNotStored(/*computed*/ 0);
21202185
}
2186+
}
21212187

2122-
auto var = dyn_cast<VarDecl>(storage);
2123-
if (var == nullptr)
2124-
return;
2188+
static void finishStorageImplInfo(AbstractStorageDecl *storage,
2189+
StorageImplInfo &info) {
2190+
if (auto var = dyn_cast<VarDecl>(storage)) {
2191+
if (!info.hasStorage()) {
2192+
if (auto *init = var->getParentInitializer()) {
2193+
auto &Diags = var->getASTContext().Diags;
2194+
Diags.diagnose(init->getLoc(), diag::getset_init)
2195+
.highlight(init->getSourceRange());
2196+
}
2197+
}
21252198

2126-
if (var->getAttrs().hasAttribute<LazyAttr>()) {
2127-
finishLazyVariableImplInfo(var);
2128-
} else if (var->getAttrs().hasAttribute<NSManagedAttr>()) {
2129-
finishNSManagedImplInfo(var);
2130-
} else if (var->hasAttachedPropertyWrapper()) {
2131-
finishPropertyWrapperImplInfo(var);
2199+
if (var->getAttrs().hasAttribute<LazyAttr>()) {
2200+
finishLazyVariableImplInfo(var, info);
2201+
} else if (var->getAttrs().hasAttribute<NSManagedAttr>()) {
2202+
finishNSManagedImplInfo(var, info);
2203+
} else if (var->hasAttachedPropertyWrapper()) {
2204+
finishPropertyWrapperImplInfo(var, info);
2205+
}
21322206
}
2207+
2208+
if (isa<ProtocolDecl>(storage->getDeclContext()))
2209+
finishProtocolStorageImplInfo(storage, info);
21332210
}
21342211

21352212
static bool hasParsedAccessor(AbstractStorageDecl *storage, AccessorKind kind) {
@@ -2284,14 +2361,15 @@ StorageImplInfoRequest::evaluate(Evaluator &evaluator,
22842361
readWriteImpl = ReadWriteImplKind::Immutable;
22852362
}
22862363

2287-
return StorageImplInfo(readImpl, writeImpl, readWriteImpl);
2364+
StorageImplInfo info(readImpl, writeImpl, readWriteImpl);
2365+
finishStorageImplInfo(storage, info);
2366+
2367+
return info;
22882368
}
22892369

22902370
/// Try to add the appropriate accessors required a storage declaration.
22912371
/// This needs to be idempotent.
22922372
void swift::maybeAddAccessorsToStorage(AbstractStorageDecl *storage) {
2293-
finishStorageImplInfo(storage);
2294-
22952373
// Implicit properties don't get accessors.
22962374
if (storage->isImplicit() &&
22972375
!(isa<VarDecl>(storage) &&

lib/Sema/TypeCheckAttr.cpp

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -501,36 +501,6 @@ void AttributeEarlyChecker::visitNSManagedAttr(NSManagedAttr *attr) {
501501
// Everything below deals with restrictions on @NSManaged properties.
502502
auto *VD = cast<VarDecl>(D);
503503

504-
if (VD->isLet())
505-
diagnoseAndRemoveAttr(attr, diag::attr_NSManaged_let_property);
506-
507-
auto diagnoseNotStored = [&](unsigned kind) {
508-
TC.diagnose(attr->getLocation(), diag::attr_NSManaged_not_stored, kind);
509-
return attr->setInvalid();
510-
};
511-
512-
// @NSManaged properties must be written as stored.
513-
auto impl = VD->getImplInfo();
514-
if (impl.isSimpleStored()) {
515-
// @NSManaged properties end up being computed; complain if there is
516-
// an initializer.
517-
if (VD->getParentInitializer()) {
518-
TC.diagnose(attr->getLocation(), diag::attr_NSManaged_initial_value)
519-
.highlight(VD->getParentInitializer()->getSourceRange());
520-
auto PBD = VD->getParentPatternBinding();
521-
PBD->setInit(PBD->getPatternEntryIndexForVarDecl(VD), nullptr);
522-
}
523-
// Otherwise, ok.
524-
} else if (impl.getReadImpl() == ReadImplKind::Address ||
525-
impl.getWriteImpl() == WriteImplKind::MutableAddress) {
526-
return diagnoseNotStored(/*addressed*/ 2);
527-
} else if (impl.getWriteImpl() == WriteImplKind::StoredWithObservers ||
528-
impl.getWriteImpl() == WriteImplKind::InheritedWithObservers) {
529-
return diagnoseNotStored(/*observing*/ 1);
530-
} else {
531-
return diagnoseNotStored(/*computed*/ 0);
532-
}
533-
534504
// @NSManaged properties cannot be @NSCopying
535505
if (auto *NSCopy = VD->getAttrs().getAttribute<NSCopyingAttr>())
536506
diagnoseAndRemoveAttr(NSCopy, diag::attr_NSManaged_NSCopying);
@@ -562,21 +532,12 @@ void AttributeEarlyChecker::visitLazyAttr(LazyAttr *attr) {
562532
// lazy may only be used on properties.
563533
auto *VD = cast<VarDecl>(D);
564534

565-
// It cannot currently be used on let's since we don't have a mutability model
566-
// that supports it.
567-
if (VD->isLet())
568-
diagnoseAndRemoveAttr(attr, diag::lazy_not_on_let);
569-
570535
auto attrs = VD->getAttrs();
571536
// 'lazy' is not allowed to have reference attributes
572537
if (auto *refAttr = attrs.getAttribute<ReferenceOwnershipAttr>())
573538
diagnoseAndRemoveAttr(attr, diag::lazy_not_strong, refAttr->get());
574539

575-
// lazy is not allowed on a protocol requirement.
576540
auto varDC = VD->getDeclContext();
577-
if (isa<ProtocolDecl>(varDC))
578-
diagnoseAndRemoveAttr(attr, diag::lazy_not_in_protocol);
579-
580541

581542
// 'lazy' is not allowed on a global variable or on a static property (which
582543
// are already lazily initialized).
@@ -588,25 +549,6 @@ void AttributeEarlyChecker::visitLazyAttr(LazyAttr *attr) {
588549
} else if (!VD->getDeclContext()->isTypeContext()) {
589550
diagnoseAndRemoveAttr(attr, diag::lazy_must_be_property);
590551
}
591-
592-
// lazy must have an initializer, and the pattern binding must be a simple
593-
// one.
594-
if (!VD->getParentInitializer())
595-
diagnoseAndRemoveAttr(attr, diag::lazy_requires_initializer);
596-
597-
if (!VD->getParentPatternBinding()->getSingleVar())
598-
diagnoseAndRemoveAttr(attr, diag::lazy_requires_single_var);
599-
600-
601-
// TODO: Lazy properties can't yet be observed.
602-
auto impl = VD->getImplInfo();
603-
if (impl.isSimpleStored()) {
604-
// ok
605-
} else if (VD->hasStorage()) {
606-
diagnoseAndRemoveAttr(attr, diag::lazy_not_observable);
607-
} else {
608-
diagnoseAndRemoveAttr(attr, diag::lazy_not_on_computed);
609-
}
610552
}
611553

612554
bool AttributeEarlyChecker::visitAbstractAccessControlAttr(

lib/Sema/TypeCheckDecl.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2224,6 +2224,15 @@ IsGetterMutatingRequest::evaluate(Evaluator &evaluator,
22242224
}
22252225
}
22262226

2227+
// Protocol requirements are always written as '{ get }' or '{ get set }';
2228+
// the @_borrowed attribute determines if getReadImpl() becomes Get or Read.
2229+
if (isa<ProtocolDecl>(storage->getDeclContext())) {
2230+
if (!storage->getGetter())
2231+
return false;
2232+
2233+
return storage->getGetter()->isMutating();
2234+
}
2235+
22272236
switch (storage->getReadImpl()) {
22282237
case ReadImplKind::Stored:
22292238
case ReadImplKind::Inherited:

lib/Sema/TypeCheckPropertyWrapper.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -456,12 +456,6 @@ AttachedPropertyWrappersRequest::evaluate(Evaluator &evaluator,
456456
continue;
457457
}
458458
}
459-
460-
// Properties with wrappers must not declare a getter or setter.
461-
if (!var->hasStorage() && sourceFile->Kind != SourceFileKind::Interface) {
462-
ctx.Diags.diagnose(attr->getLocation(), diag::property_wrapper_computed);
463-
continue;
464-
}
465459

466460
result.push_back(mutableAttr);
467461
}

test/decl/var/NSManaged_properties.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,8 @@ class SwiftGizmo : A {
3939
// expected-error@+1{{property cannot be marked @NSManaged because its type cannot be represented in Objective-C}}
4040
@NSManaged var nonobjc_var: SwiftProto?
4141

42-
// expected-error@+4 {{@NSManaged only allowed on an instance property or method}}
43-
// expected-error@+3 {{@NSManaged property cannot have an initial value}}
44-
// expected-error@+2 {{class stored properties not supported in classes; did you mean 'static'?}}
45-
// expected-error@+1 {{'class var' declaration requires an initializer expression or getter/setter specifier}}
42+
// expected-error@+2 {{@NSManaged only allowed on an instance property or method}}
43+
// expected-error@+1 {{@NSManaged property cannot have an initial value}}
4644
@NSManaged class var d: Int = 4
4745

4846
@NSManaged var e: Int { return 4 } // expected-error {{@NSManaged not allowed on computed properties}}

0 commit comments

Comments
 (0)