Skip to content

Commit a72fb83

Browse files
committed
Requestify AbstractStorageDecl::hasStorage().
The `hasStorage()` computation is used in many places to determine the signatures of other declarations. It currently needs to expand accessor macros, which causes a number of cyclic references. Provide a simplified request to determine `hasStorage` without expanding or resolving macros, breaking a common pattern of cycles when using macros. Fixes rdar://109668383.
1 parent 743eefb commit a72fb83

18 files changed

+222
-22
lines changed

include/swift/AST/Decl.h

+2-7
Original file line numberDiff line numberDiff line change
@@ -5293,10 +5293,7 @@ class AbstractStorageDecl : public ValueDecl {
52935293

52945294
/// Overwrite the registered implementation-info. This should be
52955295
/// used carefully.
5296-
void setImplInfo(StorageImplInfo implInfo) {
5297-
LazySemanticInfo.ImplInfoComputed = 1;
5298-
ImplInfo = implInfo;
5299-
}
5296+
void setImplInfo(StorageImplInfo implInfo);
53005297

53015298
ReadImplKind getReadImpl() const {
53025299
return getImplInfo().getReadImpl();
@@ -5311,9 +5308,7 @@ class AbstractStorageDecl : public ValueDecl {
53115308

53125309
/// Return true if this is a VarDecl that has storage associated with
53135310
/// it.
5314-
bool hasStorage() const {
5315-
return getImplInfo().hasStorage();
5316-
}
5311+
bool hasStorage() const;
53175312

53185313
/// Return true if this storage has the basic accessors/capability
53195314
/// to be mutated. This is generally constant after the accessors are

include/swift/AST/DiagnosticsSema.def

+4
Original file line numberDiff line numberDiff line change
@@ -7181,6 +7181,10 @@ ERROR(invalid_macro_role_for_macro_syntax,none,
71817181
(unsigned))
71827182
ERROR(macro_cannot_introduce_names,none,
71837183
"'%0' macros are not allowed to introduce names", (StringRef))
7184+
ERROR(macro_accessor_missing_from_expansion,none,
7185+
"expansion of macro %0 did not produce a %select{non-|}1observing "
7186+
"accessor",
7187+
(DeclName, bool))
71847188

71857189
ERROR(macro_resolve_circular_reference, none,
71867190
"circular reference resolving %select{freestanding|attached}0 macro %1",

include/swift/AST/Evaluator.h

+6
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,12 @@ class Evaluator {
316316
cache.insert<Request>(request, std::move(output));
317317
}
318318

319+
template<typename Request,
320+
typename std::enable_if<!Request::hasExternalCache>::type* = nullptr>
321+
bool hasCachedResult(const Request &request) {
322+
return cache.find_as(request) != cache.end<Request>();
323+
}
324+
319325
/// Do not introduce new callers of this function.
320326
template<typename Request,
321327
typename std::enable_if<!Request::hasExternalCache>::type* = nullptr>

include/swift/AST/NameLookup.h

+7
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,13 @@ void forEachPotentialResolvedMacro(
556556
llvm::function_ref<void(MacroDecl *, const MacroRoleAttr *)> body
557557
);
558558

559+
/// For each macro with the given role that might be attached to the given
560+
/// declaration, call the body.
561+
void forEachPotentialAttachedMacro(
562+
Decl *decl, MacroRole role,
563+
llvm::function_ref<void(MacroDecl *macro, const MacroRoleAttr *)> body
564+
);
565+
559566
} // end namespace namelookup
560567

561568
/// Describes an inherited nominal entry.

include/swift/AST/TypeCheckRequests.h

+20
Original file line numberDiff line numberDiff line change
@@ -1686,6 +1686,26 @@ class InitAccessorPropertiesRequest :
16861686
ArrayRef<VarDecl *>
16871687
evaluate(Evaluator &evaluator, NominalTypeDecl *decl) const;
16881688

1689+
// Evaluation.
1690+
bool evaluate(Evaluator &evaluator, AbstractStorageDecl *decl) const;
1691+
1692+
public:
1693+
bool isCached() const { return true; }
1694+
};
1695+
1696+
class HasStorageRequest :
1697+
public SimpleRequest<HasStorageRequest,
1698+
bool(AbstractStorageDecl *),
1699+
RequestFlags::Cached> {
1700+
public:
1701+
using SimpleRequest::SimpleRequest;
1702+
1703+
private:
1704+
friend SimpleRequest;
1705+
1706+
// Evaluation.
1707+
bool evaluate(Evaluator &evaluator, AbstractStorageDecl *decl) const;
1708+
16891709
public:
16901710
bool isCached() const { return true; }
16911711
};

include/swift/AST/TypeCheckerTypeIDZone.def

+3
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,9 @@ SWIFT_REQUEST(TypeChecker, SelfAccessKindRequest, SelfAccessKind(FuncDecl *),
302302
SWIFT_REQUEST(TypeChecker, StorageImplInfoRequest,
303303
StorageImplInfo(AbstractStorageDecl *), SeparatelyCached,
304304
NoLocationInfo)
305+
SWIFT_REQUEST(TypeChecker, HasStorageRequest,
306+
bool(AbstractStorageDecl *), Cached,
307+
NoLocationInfo)
305308
SWIFT_REQUEST(TypeChecker, StoredPropertiesAndMissingMembersRequest,
306309
ArrayRef<Decl *>(NominalTypeDecl *), Cached, NoLocationInfo)
307310
SWIFT_REQUEST(TypeChecker, StoredPropertiesRequest,

lib/AST/Decl.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -6393,13 +6393,32 @@ bool ProtocolDecl::hasCircularInheritedProtocols() const {
63936393
ctx.evaluator, HasCircularInheritedProtocolsRequest{mutableThis}, true);
63946394
}
63956395

6396+
bool AbstractStorageDecl::hasStorage() const {
6397+
ASTContext &ctx = getASTContext();
6398+
return evaluateOrDefault(ctx.evaluator,
6399+
HasStorageRequest{const_cast<AbstractStorageDecl *>(this)},
6400+
false);
6401+
}
6402+
63966403
StorageImplInfo AbstractStorageDecl::getImplInfo() const {
63976404
ASTContext &ctx = getASTContext();
63986405
return evaluateOrDefault(ctx.evaluator,
63996406
StorageImplInfoRequest{const_cast<AbstractStorageDecl *>(this)},
64006407
StorageImplInfo::getSimpleStored(StorageIsMutable));
64016408
}
64026409

6410+
void AbstractStorageDecl::setImplInfo(StorageImplInfo implInfo) {
6411+
LazySemanticInfo.ImplInfoComputed = 1;
6412+
ImplInfo = implInfo;
6413+
6414+
if (isImplicit()) {
6415+
auto &evaluator = getASTContext().evaluator;
6416+
HasStorageRequest request{this};
6417+
if (!evaluator.hasCachedResult(request))
6418+
evaluator.cacheOutput(request, implInfo.hasStorage());
6419+
}
6420+
}
6421+
64036422
bool AbstractStorageDecl::hasPrivateAccessor() const {
64046423
for (auto accessor : getAllAccessors()) {
64056424
if (hasPrivateOrFilePrivateFormalAccess(accessor))

lib/AST/NameLookup.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1627,7 +1627,7 @@ void namelookup::forEachPotentialResolvedMacro(
16271627

16281628
/// For each macro with the given role that might be attached to the given
16291629
/// declaration, call the body.
1630-
static void forEachPotentialAttachedMacro(
1630+
void namelookup::forEachPotentialAttachedMacro(
16311631
Decl *decl, MacroRole role,
16321632
llvm::function_ref<void(MacroDecl *macro, const MacroRoleAttr *)> body
16331633
) {

lib/ClangImporter/ClangImporter.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "swift/AST/NameLookupRequests.h"
3535
#include "swift/AST/PrettyStackTrace.h"
3636
#include "swift/AST/SourceFile.h"
37+
#include "swift/AST/TypeCheckRequests.h"
3738
#include "swift/AST/Types.h"
3839
#include "swift/Basic/Defer.h"
3940
#include "swift/Basic/Platform.h"
@@ -5056,6 +5057,7 @@ cloneBaseMemberDecl(ValueDecl *decl, DeclContext *newContext) {
50565057
out->setIsObjC(var->isObjC());
50575058
out->setIsDynamic(var->isDynamic());
50585059
out->copyFormalAccessFrom(var);
5060+
out->getASTContext().evaluator.cacheOutput(HasStorageRequest{out}, false);
50595061
out->setAccessors(SourceLoc(),
50605062
makeBaseClassMemberAccessors(newContext, out, var),
50615063
SourceLoc());

lib/ClangImporter/ImportDecl.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ void ClangImporter::Implementation::makeComputed(AbstractStorageDecl *storage,
126126
AccessorDecl *getter,
127127
AccessorDecl *setter) {
128128
assert(getter);
129+
storage->getASTContext().evaluator.cacheOutput(HasStorageRequest{storage}, false);
129130
if (setter) {
130131
storage->setImplInfo(StorageImplInfo::getMutableComputed());
131132
storage->setAccessors(SourceLoc(), {getter, setter}, SourceLoc());

lib/Sema/DerivedConformanceEquatableHashable.cpp

+4-5
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,10 @@ static ValueDecl *deriveHashable_hashValue(DerivedConformance &derived) {
886886
SourceLoc(), C.Id_hashValue, parentDC);
887887
hashValueDecl->setInterfaceType(intType);
888888
hashValueDecl->setSynthesized();
889+
hashValueDecl->setImplicit();
890+
hashValueDecl->setImplInfo(StorageImplInfo::getImmutableComputed());
891+
hashValueDecl->copyFormalAccessFrom(derived.Nominal,
892+
/*sourceIsParentContext*/ true);
889893

890894
ParameterList *params = ParameterList::createEmpty(C);
891895

@@ -904,12 +908,7 @@ static ValueDecl *deriveHashable_hashValue(DerivedConformance &derived) {
904908
/*sourceIsParentContext*/ true);
905909

906910
// Finish creating the property.
907-
hashValueDecl->setImplicit();
908-
hashValueDecl->setInterfaceType(intType);
909-
hashValueDecl->setImplInfo(StorageImplInfo::getImmutableComputed());
910911
hashValueDecl->setAccessors(SourceLoc(), {getterDecl}, SourceLoc());
911-
hashValueDecl->copyFormalAccessFrom(derived.Nominal,
912-
/*sourceIsParentContext*/ true);
913912

914913
// The derived hashValue of an actor must be nonisolated.
915914
if (!addNonIsolatedToSynthesized(derived.Nominal, hashValueDecl) &&

lib/Sema/TypeCheckMacros.cpp

+47-2
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,37 @@ static SourceFile *evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo,
12401240
return macroSourceFile;
12411241
}
12421242

1243+
bool swift::accessorMacroOnlyIntroducesObservers(
1244+
MacroDecl *macro,
1245+
const MacroRoleAttr *attr
1246+
) {
1247+
// Will this macro introduce observers?
1248+
bool foundObserver = false;
1249+
for (auto name : attr->getNames()) {
1250+
if (name.getKind() == MacroIntroducedDeclNameKind::Named &&
1251+
(name.getName().getBaseName().userFacingName() == "willSet" ||
1252+
name.getName().getBaseName().userFacingName() == "didSet")) {
1253+
foundObserver = true;
1254+
} else {
1255+
// Introduces something other than an observer.
1256+
return false;
1257+
}
1258+
}
1259+
1260+
if (foundObserver)
1261+
return true;
1262+
1263+
// WORKAROUND: Older versions of the Observation library make
1264+
// `ObservationIgnored` an accessor macro that implies that it makes a
1265+
// stored property computed. Override that, because we know it produces
1266+
// nothing.
1267+
if (macro->getName().getBaseName().userFacingName() == "ObservationIgnored") {
1268+
return true;
1269+
}
1270+
1271+
return false;
1272+
}
1273+
12431274
Optional<unsigned> swift::expandAccessors(
12441275
AbstractStorageDecl *storage, CustomAttr *attr, MacroDecl *macro
12451276
) {
@@ -1251,11 +1282,12 @@ Optional<unsigned> swift::expandAccessors(
12511282
return None;
12521283

12531284
PrettyStackTraceDecl debugStack(
1254-
"type checking expanded declaration macro", storage);
1285+
"type checking expanded accessor macro", storage);
12551286

12561287
// Trigger parsing of the sequence of accessor declarations. This has the
12571288
// side effect of registering those accessor declarations with the storage
12581289
// declaration, so there is nothing further to do.
1290+
bool foundNonObservingAccessor = false;
12591291
for (auto decl : macroSourceFile->getTopLevelItems()) {
12601292
auto accessor = dyn_cast_or_null<AccessorDecl>(decl.dyn_cast<Decl *>());
12611293
if (!accessor)
@@ -1264,17 +1296,30 @@ Optional<unsigned> swift::expandAccessors(
12641296
if (accessor->isObservingAccessor())
12651297
continue;
12661298

1299+
foundNonObservingAccessor = true;
1300+
}
1301+
1302+
auto roleAttr = macro->getMacroRoleAttr(MacroRole::Accessor);
1303+
bool expectedNonObservingAccessor =
1304+
!accessorMacroOnlyIntroducesObservers(macro, roleAttr);
1305+
if (foundNonObservingAccessor) {
12671306
// If any non-observing accessor was added, mark the initializer as
12681307
// subsumed.
12691308
if (auto var = dyn_cast<VarDecl>(storage)) {
12701309
if (auto binding = var->getParentPatternBinding()) {
12711310
unsigned index = binding->getPatternEntryIndexForVarDecl(var);
12721311
binding->setInitializerSubsumed(index);
1273-
break;
12741312
}
12751313
}
12761314
}
12771315

1316+
// Make sure we got non-observing accessors exactly where we expected to.
1317+
if (foundNonObservingAccessor != expectedNonObservingAccessor) {
1318+
storage->diagnose(
1319+
diag::macro_accessor_missing_from_expansion, macro->getName(),
1320+
!expectedNonObservingAccessor);
1321+
}
1322+
12781323
return macroSourceFile->getBufferID();
12791324
}
12801325

lib/Sema/TypeCheckMacros.h

+5
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ Optional<unsigned> expandPeers(CustomAttr *attr, MacroDecl *macro, Decl *decl);
7777
Optional<unsigned> expandConformances(CustomAttr *attr, MacroDecl *macro,
7878
NominalTypeDecl *nominal);
7979

80+
/// Determine whether an accessor macro with the given attribute only
81+
/// introduces observers like willSet and didSet.
82+
bool accessorMacroOnlyIntroducesObservers(
83+
MacroDecl *macro, const MacroRoleAttr *attr);
84+
8085
} // end namespace swift
8186

8287
#endif /* SWIFT_SEMA_TYPECHECKMACROS_H */

lib/Sema/TypeCheckStorage.cpp

+70-1
Original file line numberDiff line numberDiff line change
@@ -1392,7 +1392,7 @@ synthesizeTrivialGetterBody(AccessorDecl *getter, TargetImpl target,
13921392
/// underlying storage.
13931393
static std::pair<BraceStmt *, bool>
13941394
synthesizeTrivialGetterBody(AccessorDecl *getter, ASTContext &ctx) {
1395-
assert(getter->getStorage()->hasStorage());
1395+
assert(getter->getStorage()->getImplInfo().hasStorage());
13961396
return synthesizeTrivialGetterBody(getter, TargetImpl::Storage, ctx);
13971397
}
13981398

@@ -3431,6 +3431,73 @@ static StorageImplInfo classifyWithHasStorageAttr(VarDecl *var) {
34313431
return StorageImplInfo(ReadImplKind::Stored, writeImpl, readWriteImpl);
34323432
}
34333433

3434+
bool HasStorageRequest::evaluate(Evaluator &evaluator,
3435+
AbstractStorageDecl *storage) const {
3436+
// Parameters are always stored.
3437+
if (isa<ParamDecl>(storage))
3438+
return true;
3439+
3440+
// Only variables can be stored.
3441+
auto *var = dyn_cast<VarDecl>(storage);
3442+
if (!var)
3443+
return false;
3444+
3445+
// @_hasStorage implies that it... has storage.
3446+
if (var->getAttrs().hasAttribute<HasStorageAttr>())
3447+
return true;
3448+
3449+
// Protocol requirements never have storage.
3450+
if (isa<ProtocolDecl>(storage->getDeclContext()))
3451+
return false;
3452+
3453+
// lazy declarations do not have storage.
3454+
if (storage->getAttrs().hasAttribute<LazyAttr>())
3455+
return false;
3456+
3457+
// @NSManaged attributes don't have storage
3458+
if (storage->getAttrs().hasAttribute<NSManagedAttr>())
3459+
return false;
3460+
3461+
// Any accessors that read or write imply that there is no storage.
3462+
if (storage->getParsedAccessor(AccessorKind::Get) ||
3463+
storage->getParsedAccessor(AccessorKind::Read) ||
3464+
storage->getParsedAccessor(AccessorKind::Address) ||
3465+
storage->getParsedAccessor(AccessorKind::Set) ||
3466+
storage->getParsedAccessor(AccessorKind::Modify) ||
3467+
storage->getParsedAccessor(AccessorKind::MutableAddress))
3468+
return false;
3469+
3470+
// willSet or didSet in an overriding property imply that there is no storage.
3471+
if ((storage->getParsedAccessor(AccessorKind::WillSet) ||
3472+
storage->getParsedAccessor(AccessorKind::DidSet)) &&
3473+
storage->getAttrs().hasAttribute<OverrideAttr>())
3474+
return false;
3475+
3476+
// The presence of a property wrapper implies that there is no storage.
3477+
if (var->hasAttachedPropertyWrapper())
3478+
return false;
3479+
3480+
// Look for any accessor macros that might make this property computed.
3481+
bool hasStorage = true;
3482+
namelookup::forEachPotentialAttachedMacro(
3483+
var, MacroRole::Accessor,
3484+
[&](MacroDecl *macro, const MacroRoleAttr *attr) {
3485+
// Will this macro introduce observers?
3486+
bool foundObserver = accessorMacroOnlyIntroducesObservers(macro, attr);
3487+
3488+
// If it's not (just) introducing observers, it's making the property
3489+
// computed.
3490+
if (!foundObserver)
3491+
hasStorage = false;
3492+
3493+
// If it will introduce observers, and there is an "override",
3494+
// the property doesn't have storage.
3495+
if (foundObserver && storage->getAttrs().hasAttribute<OverrideAttr>())
3496+
hasStorage = false;
3497+
});
3498+
return hasStorage;
3499+
}
3500+
34343501
StorageImplInfo
34353502
StorageImplInfoRequest::evaluate(Evaluator &evaluator,
34363503
AbstractStorageDecl *storage) const {
@@ -3590,6 +3657,8 @@ StorageImplInfoRequest::evaluate(Evaluator &evaluator,
35903657
StorageImplInfo info(readImpl, writeImpl, readWriteImpl);
35913658
finishStorageImplInfo(storage, info);
35923659

3660+
assert(info.hasStorage() == storage->hasStorage() ||
3661+
storage->getASTContext().Diags.hadAnyError());
35933662
return info;
35943663
}
35953664

0 commit comments

Comments
 (0)