Skip to content

Commit e3ac3c0

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 e3d940e commit e3ac3c0

File tree

18 files changed

+222
-22
lines changed

18 files changed

+222
-22
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/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7179,6 +7179,10 @@ ERROR(invalid_macro_role_for_macro_syntax,none,
71797179
(unsigned))
71807180
ERROR(macro_cannot_introduce_names,none,
71817181
"'%0' macros are not allowed to introduce names", (StringRef))
7182+
ERROR(macro_accessor_missing_from_expansion,none,
7183+
"expansion of macro %0 did not produce a %select{non-|}1observing "
7184+
"accessor",
7185+
(DeclName, bool))
71827186

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

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
@@ -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

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/ClangImporter/ClangImporter.cpp

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

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

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/TypeCheckMacros.cpp

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,6 +1231,37 @@ static SourceFile *evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo,
12311231
return macroSourceFile;
12321232
}
12331233

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

12441275
PrettyStackTraceDecl debugStack(
1245-
"type checking expanded declaration macro", storage);
1276+
"type checking expanded accessor macro", storage);
12461277

12471278
// Trigger parsing of the sequence of accessor declarations. This has the
12481279
// side effect of registering those accessor declarations with the storage
12491280
// declaration, so there is nothing further to do.
1281+
bool foundNonObservingAccessor = false;
12501282
for (auto decl : macroSourceFile->getTopLevelItems()) {
12511283
auto accessor = dyn_cast_or_null<AccessorDecl>(decl.dyn_cast<Decl *>());
12521284
if (!accessor)
@@ -1255,17 +1287,30 @@ Optional<unsigned> swift::expandAccessors(
12551287
if (accessor->isObservingAccessor())
12561288
continue;
12571289

1290+
foundNonObservingAccessor = true;
1291+
}
1292+
1293+
auto roleAttr = macro->getMacroRoleAttr(MacroRole::Accessor);
1294+
bool expectedNonObservingAccessor =
1295+
!accessorMacroOnlyIntroducesObservers(macro, roleAttr);
1296+
if (foundNonObservingAccessor) {
12581297
// If any non-observing accessor was added, mark the initializer as
12591298
// subsumed.
12601299
if (auto var = dyn_cast<VarDecl>(storage)) {
12611300
if (auto binding = var->getParentPatternBinding()) {
12621301
unsigned index = binding->getPatternEntryIndexForVarDecl(var);
12631302
binding->setInitializerSubsumed(index);
1264-
break;
12651303
}
12661304
}
12671305
}
12681306

1307+
// Make sure we got non-observing accessors exactly where we expected to.
1308+
if (foundNonObservingAccessor != expectedNonObservingAccessor) {
1309+
storage->diagnose(
1310+
diag::macro_accessor_missing_from_expansion, macro->getName(),
1311+
!expectedNonObservingAccessor);
1312+
}
1313+
12691314
return macroSourceFile->getBufferID();
12701315
}
12711316

lib/Sema/TypeCheckMacros.h

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

Lines changed: 70 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,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)