Skip to content

Commit 245a3d5

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 4bacb7f commit 245a3d5

File tree

11 files changed

+169
-19
lines changed

11 files changed

+169
-19
lines changed

include/swift/AST/Decl.h

Lines changed: 2 additions & 7 deletions
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/Evaluator.h

Lines changed: 6 additions & 0 deletions
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

Lines changed: 7 additions & 0 deletions
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

Lines changed: 20 additions & 0 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6381,13 +6381,32 @@ bool ProtocolDecl::hasCircularInheritedProtocols() const {
63816381
ctx.evaluator, HasCircularInheritedProtocolsRequest{mutableThis}, true);
63826382
}
63836383

6384+
bool AbstractStorageDecl::hasStorage() const {
6385+
ASTContext &ctx = getASTContext();
6386+
return evaluateOrDefault(ctx.evaluator,
6387+
HasStorageRequest{const_cast<AbstractStorageDecl *>(this)},
6388+
false);
6389+
}
6390+
63846391
StorageImplInfo AbstractStorageDecl::getImplInfo() const {
63856392
ASTContext &ctx = getASTContext();
63866393
return evaluateOrDefault(ctx.evaluator,
63876394
StorageImplInfoRequest{const_cast<AbstractStorageDecl *>(this)},
63886395
StorageImplInfo::getSimpleStored(StorageIsMutable));
63896396
}
63906397

6398+
void AbstractStorageDecl::setImplInfo(StorageImplInfo implInfo) {
6399+
LazySemanticInfo.ImplInfoComputed = 1;
6400+
ImplInfo = implInfo;
6401+
6402+
if (isImplicit()) {
6403+
auto &evaluator = getASTContext().evaluator;
6404+
HasStorageRequest request{this};
6405+
if (!evaluator.hasCachedResult(request))
6406+
evaluator.cacheOutput(request, implInfo.hasStorage());
6407+
}
6408+
}
6409+
63916410
bool AbstractStorageDecl::hasPrivateAccessor() const {
63926411
for (auto accessor : getAllAccessors()) {
63936412
if (hasPrivateOrFilePrivateFormalAccess(accessor))

lib/AST/NameLookup.cpp

Lines changed: 1 addition & 1 deletion
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/Sema/DerivedConformanceEquatableHashable.cpp

Lines changed: 4 additions & 5 deletions
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/TypeCheckStorage.cpp

Lines changed: 78 additions & 1 deletion
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,81 @@ 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 = false;
3487+
for (auto name : attr->getNames()) {
3488+
if (name.getKind() == MacroIntroducedDeclNameKind::Named &&
3489+
(name.getName().getBaseName().userFacingName() == "willSet" ||
3490+
name.getName().getBaseName().userFacingName() == "didSet")) {
3491+
foundObserver = true;
3492+
break;
3493+
}
3494+
}
3495+
3496+
// If it's not (just) introducing observers, it's making the property
3497+
// computed.
3498+
if (!foundObserver)
3499+
hasStorage = false;
3500+
3501+
// If it will introduce observers, and there is an "override",
3502+
// the property doesn't have storage.
3503+
if (foundObserver && storage->getAttrs().hasAttribute<OverrideAttr>())
3504+
hasStorage = false;
3505+
});
3506+
return hasStorage;
3507+
}
3508+
34343509
StorageImplInfo
34353510
StorageImplInfoRequest::evaluate(Evaluator &evaluator,
34363511
AbstractStorageDecl *storage) const {
@@ -3590,6 +3665,8 @@ StorageImplInfoRequest::evaluate(Evaluator &evaluator,
35903665
StorageImplInfo info(readImpl, writeImpl, readWriteImpl);
35913666
finishStorageImplInfo(storage, info);
35923667

3668+
assert(info.hasStorage() == storage->hasStorage() ||
3669+
storage->getASTContext().Diags.hadAnyError());
35933670
return info;
35943671
}
35953672

lib/Serialization/Deserialization.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2912,16 +2912,18 @@ void ModuleFile::configureStorage(AbstractStorageDecl *decl,
29122912
auto readWriteImpl = getActualReadWriteImplKind(rawReadWriteImplKind);
29132913
if (!readWriteImpl) return;
29142914

2915+
auto implInfo = StorageImplInfo(*readImpl, *writeImpl, *readWriteImpl);
2916+
decl->setImplInfo(implInfo);
2917+
2918+
decl->getASTContext().evaluator.cacheOutput(HasStorageRequest{decl}, implInfo.hasStorage());
2919+
29152920
SmallVector<AccessorDecl*, 8> accessors;
29162921
for (DeclID id : rawIDs.IDs) {
29172922
auto accessor = dyn_cast_or_null<AccessorDecl>(getDecl(id));
29182923
if (!accessor) return;
29192924
accessors.push_back(accessor);
29202925
}
29212926

2922-
auto implInfo = StorageImplInfo(*readImpl, *writeImpl, *readWriteImpl);
2923-
decl->setImplInfo(implInfo);
2924-
29252927
if (implInfo.isSimpleStored() && accessors.empty())
29262928
return;
29272929

@@ -3538,6 +3540,9 @@ class DeclDeserializer {
35383540
var->setIsSetterMutating(isSetterMutating);
35393541
declOrOffset = var;
35403542

3543+
MF.configureStorage(var, opaqueReadOwnership,
3544+
readImpl, writeImpl, readWriteImpl, accessors);
3545+
35413546
auto interfaceTypeOrError = MF.getTypeChecked(interfaceTypeID);
35423547
if (!interfaceTypeOrError)
35433548
return interfaceTypeOrError.takeError();
@@ -3549,8 +3554,6 @@ class DeclDeserializer {
35493554
AddAttribute(
35503555
new (ctx) ReferenceOwnershipAttr(referenceStorage->getOwnership()));
35513556

3552-
MF.configureStorage(var, opaqueReadOwnership,
3553-
readImpl, writeImpl, readWriteImpl, accessors);
35543557
auto accessLevel = getActualAccessLevel(rawAccessLevel);
35553558
if (!accessLevel)
35563559
return MF.diagnoseFatal();
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// REQUIRES: swift_swift_parser
2+
3+
// RUN: %target-typecheck-verify-swift -swift-version 5 -swift-version 5
4+
5+
struct Predicate<T> { }
6+
7+
@freestanding(expression)
8+
macro Predicate<T>(_ body: (T) -> Void) -> Predicate<T> = #externalMacro(module: "A", type: "B")
9+
// expected-warning@-1{{could not be found}}
10+
11+
@attached(accessor)
12+
@attached(peer)
13+
macro Foo<T>(filter: Predicate<T>) = #externalMacro(module: "A", type: "B")
14+
// expected-warning@-1{{could not be found}}
15+
// expected-note@-2 2{{declared here}}
16+
17+
struct Content {
18+
@Foo(filter: #Predicate<Bool> { $0 == true }) var foo: Bool = true
19+
//expected-error@-1 2{{could not be found for macro}}
20+
// expected-warning@-2{{result of operator '==' is unused}}
21+
}

0 commit comments

Comments
 (0)