Skip to content

Commit 8702fe7

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 66a38a1 commit 8702fe7

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
@@ -5295,10 +5295,7 @@ class AbstractStorageDecl : public ValueDecl {
52955295

52965296
/// Overwrite the registered implementation-info. This should be
52975297
/// used carefully.
5298-
void setImplInfo(StorageImplInfo implInfo) {
5299-
LazySemanticInfo.ImplInfoComputed = 1;
5300-
ImplInfo = implInfo;
5301-
}
5298+
void setImplInfo(StorageImplInfo implInfo);
53025299

53035300
ReadImplKind getReadImpl() const {
53045301
return getImplInfo().getReadImpl();
@@ -5313,9 +5310,7 @@ class AbstractStorageDecl : public ValueDecl {
53135310

53145311
/// Return true if this is a VarDecl that has storage associated with
53155312
/// it.
5316-
bool hasStorage() const {
5317-
return getImplInfo().hasStorage();
5318-
}
5313+
bool hasStorage() const;
53195314

53205315
/// Return true if this storage has the basic accessors/capability
53215316
/// 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
@@ -7085,6 +7085,10 @@ ERROR(invalid_macro_role_for_macro_syntax,none,
70857085
(unsigned))
70867086
ERROR(macro_cannot_introduce_names,none,
70877087
"'%0' macros are not allowed to introduce names", (StringRef))
7088+
ERROR(macro_accessor_missing_from_expansion,none,
7089+
"expansion of macro %0 did not produce a %select{non-|}1observing "
7090+
"accessor",
7091+
(DeclName, bool))
70887092

70897093
ERROR(macro_resolve_circular_reference, none,
70907094
"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
@@ -555,6 +555,13 @@ void forEachPotentialResolvedMacro(
555555
llvm::function_ref<void(MacroDecl *, const MacroRoleAttr *)> body
556556
);
557557

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

560567
/// 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
@@ -6378,13 +6378,32 @@ bool ProtocolDecl::hasCircularInheritedProtocols() const {
63786378
ctx.evaluator, HasCircularInheritedProtocolsRequest{mutableThis}, true);
63796379
}
63806380

6381+
bool AbstractStorageDecl::hasStorage() const {
6382+
ASTContext &ctx = getASTContext();
6383+
return evaluateOrDefault(ctx.evaluator,
6384+
HasStorageRequest{const_cast<AbstractStorageDecl *>(this)},
6385+
false);
6386+
}
6387+
63816388
StorageImplInfo AbstractStorageDecl::getImplInfo() const {
63826389
ASTContext &ctx = getASTContext();
63836390
return evaluateOrDefault(ctx.evaluator,
63846391
StorageImplInfoRequest{const_cast<AbstractStorageDecl *>(this)},
63856392
StorageImplInfo::getSimpleStored(StorageIsMutable));
63866393
}
63876394

6395+
void AbstractStorageDecl::setImplInfo(StorageImplInfo implInfo) {
6396+
LazySemanticInfo.ImplInfoComputed = 1;
6397+
ImplInfo = implInfo;
6398+
6399+
if (isImplicit()) {
6400+
auto &evaluator = getASTContext().evaluator;
6401+
HasStorageRequest request{this};
6402+
if (!evaluator.hasCachedResult(request))
6403+
evaluator.cacheOutput(request, implInfo.hasStorage());
6404+
}
6405+
}
6406+
63886407
bool AbstractStorageDecl::hasPrivateAccessor() const {
63896408
for (auto accessor : getAllAccessors()) {
63906409
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"
@@ -5042,6 +5043,7 @@ cloneBaseMemberDecl(ValueDecl *decl, DeclContext *newContext) {
50425043
out->setIsObjC(var->isObjC());
50435044
out->setIsDynamic(var->isDynamic());
50445045
out->copyFormalAccessFrom(var);
5046+
out->getASTContext().evaluator.cacheOutput(HasStorageRequest{out}, false);
50455047
out->setAccessors(SourceLoc(),
50465048
makeBaseClassMemberAccessors(newContext, out, var),
50475049
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
@@ -890,6 +890,10 @@ static ValueDecl *deriveHashable_hashValue(DerivedConformance &derived) {
890890
SourceLoc(), C.Id_hashValue, parentDC);
891891
hashValueDecl->setInterfaceType(intType);
892892
hashValueDecl->setSynthesized();
893+
hashValueDecl->setImplicit();
894+
hashValueDecl->setImplInfo(StorageImplInfo::getImmutableComputed());
895+
hashValueDecl->copyFormalAccessFrom(derived.Nominal,
896+
/*sourceIsParentContext*/ true);
893897

894898
ParameterList *params = ParameterList::createEmpty(C);
895899

@@ -908,12 +912,7 @@ static ValueDecl *deriveHashable_hashValue(DerivedConformance &derived) {
908912
/*sourceIsParentContext*/ true);
909913

910914
// Finish creating the property.
911-
hashValueDecl->setImplicit();
912-
hashValueDecl->setInterfaceType(intType);
913-
hashValueDecl->setImplInfo(StorageImplInfo::getImmutableComputed());
914915
hashValueDecl->setAccessors(SourceLoc(), {getterDecl}, SourceLoc());
915-
hashValueDecl->copyFormalAccessFrom(derived.Nominal,
916-
/*sourceIsParentContext*/ true);
917916

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

lib/Sema/TypeCheckMacros.cpp

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

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

12451276
PrettyStackTraceDecl debugStack(
1246-
"type checking expanded declaration macro", storage);
1277+
"type checking expanded accessor macro", storage);
12471278

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

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

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

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
@@ -1403,7 +1403,7 @@ synthesizeTrivialGetterBody(AccessorDecl *getter, TargetImpl target,
14031403
/// underlying storage.
14041404
static std::pair<BraceStmt *, bool>
14051405
synthesizeTrivialGetterBody(AccessorDecl *getter, ASTContext &ctx) {
1406-
assert(getter->getStorage()->hasStorage());
1406+
assert(getter->getStorage()->getImplInfo().hasStorage());
14071407
return synthesizeTrivialGetterBody(getter, TargetImpl::Storage, ctx);
14081408
}
14091409

@@ -3444,6 +3444,73 @@ static StorageImplInfo classifyWithHasStorageAttr(VarDecl *var) {
34443444
return StorageImplInfo(ReadImplKind::Stored, writeImpl, readWriteImpl);
34453445
}
34463446

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

3673+
assert(info.hasStorage() == storage->hasStorage() ||
3674+
storage->getASTContext().Diags.hadAnyError());
36063675
return info;
36073676
}
36083677

0 commit comments

Comments
 (0)