Skip to content

Commit 4760b95

Browse files
authored
Merge pull request #67183 from xedin/convert-effects-into-storageRestrictions
[AST/Sema] Implement @storageRestrictions attribute
2 parents 631bcab + 06216fc commit 4760b95

28 files changed

+657
-188
lines changed

include/swift/AST/Attr.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1617,6 +1617,48 @@ class AccessesAttr final
16171617
}
16181618
};
16191619

1620+
class StorageRestrictionsAttr final
1621+
: public DeclAttribute,
1622+
private llvm::TrailingObjects<StorageRestrictionsAttr, Identifier> {
1623+
friend TrailingObjects;
1624+
1625+
size_t NumInitializes;
1626+
size_t NumAccesses;
1627+
1628+
size_t numTrailingObjects(OverloadToken<Identifier>) const {
1629+
return NumInitializes + NumAccesses;
1630+
}
1631+
1632+
public:
1633+
StorageRestrictionsAttr(SourceLoc AtLoc, SourceRange Range,
1634+
ArrayRef<Identifier> initializes,
1635+
ArrayRef<Identifier> accesses, bool Implicit);
1636+
1637+
unsigned getNumInitializesProperties() const { return NumInitializes; }
1638+
1639+
unsigned getNumAccessesProperties() const { return NumAccesses; }
1640+
1641+
ArrayRef<Identifier> getInitializesNames() const {
1642+
return {getTrailingObjects<Identifier>(), NumInitializes};
1643+
}
1644+
1645+
ArrayRef<Identifier> getAccessesNames() const {
1646+
return {getTrailingObjects<Identifier>() + NumInitializes, NumAccesses};
1647+
}
1648+
1649+
ArrayRef<VarDecl *> getInitializesProperties(AccessorDecl *attachedTo) const;
1650+
ArrayRef<VarDecl *> getAccessesProperties(AccessorDecl *attachedTo) const;
1651+
1652+
static StorageRestrictionsAttr *create(ASTContext &ctx, SourceLoc atLoc,
1653+
SourceRange range,
1654+
ArrayRef<Identifier> initializes,
1655+
ArrayRef<Identifier> accesses);
1656+
1657+
static bool classof(const DeclAttribute *DA) {
1658+
return DA->getKind() == DAK_StorageRestrictions;
1659+
}
1660+
};
1661+
16201662
/// The @_implements attribute, which treats a decl as the implementation for
16211663
/// some named protocol requirement (but otherwise not-visible by that name).
16221664
class ImplementsAttr : public DeclAttribute {

include/swift/AST/Decl.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7650,6 +7650,13 @@ class AccessorDecl final : public FuncDecl {
76507650
/// for anything other than a getter or setter.
76517651
void printUserFacingName(llvm::raw_ostream &out) const;
76527652

7653+
/// If this is an init accessor, retrieve a list of instance properties
7654+
/// initialized by it.
7655+
ArrayRef<VarDecl *> getInitializedProperties() const;
7656+
/// If this is an init accessor, retrieve a list of instance properties
7657+
/// accessed by it.
7658+
ArrayRef<VarDecl *> getAccessedProperties() const;
7659+
76537660
static bool classof(const Decl *D) {
76547661
return D->getKind() == DeclKind::Accessor;
76557662
}

include/swift/AST/DiagnosticsParse.def

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2128,5 +2128,17 @@ ERROR(init_accessor_is_not_on_property,none,
21282128
"init accessors could only be associated with properties",
21292129
())
21302130

2131+
ERROR(missing_storage_restrictions_attr_label,none,
2132+
"missing label in @storageRestrictions attribute", ())
2133+
2134+
ERROR(invalid_storage_restrictions_attr_label,none,
2135+
"unexpected label %0 in @storageRestrictions attribute", (Identifier))
2136+
2137+
ERROR(duplicate_storage_restrictions_attr_label,none,
2138+
"duplicate label %0 in @storageRestrictions attribute", (Identifier))
2139+
2140+
ERROR(storage_restrictions_attr_expected_name,none,
2141+
"expected property name in @storageRestrictions list", ())
2142+
21312143
#define UNDEFINE_DIAGNOSTIC_MACROS
21322144
#include "DefineDiagnosticMacros.h"

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7457,6 +7457,9 @@ ERROR(init_accessor_initializes_attribute_on_other_declaration,none,
74577457
ERROR(init_accessor_accesses_attribute_on_other_declaration,none,
74587458
"accesses(...) attribute could only be used with init accessors",
74597459
())
7460+
ERROR(storage_restrictions_attribute_not_on_init_accessor,none,
7461+
"@storageRestrictions attribute could only be used with init accessors",
7462+
())
74607463
ERROR(init_accessor_property_both_init_and_accessed,none,
74617464
"property %0 cannot be both initialized and accessed",
74627465
(DeclName))

include/swift/Parse/Parser.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,11 @@ class Parser {
10731073
llvm::function_ref<bool(Parser &)> parseSILTargetName,
10741074
llvm::function_ref<bool(Parser &)> parseSILSIPModule);
10751075

1076+
/// Parse the @storageRestrictions(initializes:accesses:) attribute.
1077+
/// \p Attr is where to store the parsed attribute
1078+
ParserResult<StorageRestrictionsAttr>
1079+
parseStorageRestrictionsAttribute(SourceLoc AtLoc, SourceLoc Loc);
1080+
10761081
/// Parse the @_implements attribute.
10771082
/// \p Attr is where to store the parsed attribute
10781083
ParserResult<ImplementsAttr> parseImplementsAttribute(SourceLoc AtLoc,

lib/AST/Attr.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1600,6 +1600,8 @@ StringRef DeclAttribute::getAttrName() const {
16001600
return "initializes";
16011601
case DAK_Accesses:
16021602
return "accesses";
1603+
case DAK_StorageRestrictions:
1604+
return "storageRestrictions";
16031605
case DAK_Implements:
16041606
return "_implements";
16051607
case DAK_ClangImporterSynthesizedType:
@@ -2405,6 +2407,29 @@ AccessesAttr::create(ASTContext &ctx, SourceLoc atLoc, SourceRange range,
24052407
return new (mem) AccessesAttr(atLoc, range, properties);
24062408
}
24072409

2410+
StorageRestrictionsAttr::StorageRestrictionsAttr(
2411+
SourceLoc AtLoc, SourceRange Range, ArrayRef<Identifier> initializes,
2412+
ArrayRef<Identifier> accesses, bool Implicit)
2413+
: DeclAttribute(DAK_StorageRestrictions, AtLoc, Range, Implicit),
2414+
NumInitializes(initializes.size()),
2415+
NumAccesses(accesses.size()) {
2416+
std::uninitialized_copy(initializes.begin(), initializes.end(),
2417+
getTrailingObjects<Identifier>());
2418+
std::uninitialized_copy(accesses.begin(), accesses.end(),
2419+
getTrailingObjects<Identifier>() + NumInitializes);
2420+
}
2421+
2422+
StorageRestrictionsAttr *
2423+
StorageRestrictionsAttr::create(
2424+
ASTContext &ctx, SourceLoc atLoc, SourceRange range,
2425+
ArrayRef<Identifier> initializes, ArrayRef<Identifier> accesses) {
2426+
unsigned size =
2427+
totalSizeToAlloc<Identifier>(initializes.size() + accesses.size());
2428+
void *mem = ctx.Allocate(size, alignof(StorageRestrictionsAttr));
2429+
return new (mem) StorageRestrictionsAttr(atLoc, range, initializes, accesses,
2430+
/*implicit=*/false);
2431+
}
2432+
24082433
ImplementsAttr::ImplementsAttr(SourceLoc atLoc, SourceRange range,
24092434
TypeRepr *TyR,
24102435
DeclName MemberName,
@@ -2583,6 +2608,26 @@ AccessesAttr::getPropertyDecls(AccessorDecl *attachedTo) const {
25832608
{});
25842609
}
25852610

2611+
ArrayRef<VarDecl *> StorageRestrictionsAttr::getInitializesProperties(
2612+
AccessorDecl *attachedTo) const {
2613+
auto &ctx = attachedTo->getASTContext();
2614+
return evaluateOrDefault(ctx.evaluator,
2615+
InitAccessorReferencedVariablesRequest{
2616+
const_cast<StorageRestrictionsAttr *>(this),
2617+
attachedTo, getInitializesNames()},
2618+
{});
2619+
}
2620+
2621+
ArrayRef<VarDecl *>
2622+
StorageRestrictionsAttr::getAccessesProperties(AccessorDecl *attachedTo) const {
2623+
auto &ctx = attachedTo->getASTContext();
2624+
return evaluateOrDefault(ctx.evaluator,
2625+
InitAccessorReferencedVariablesRequest{
2626+
const_cast<StorageRestrictionsAttr *>(this),
2627+
attachedTo, getAccessesNames()},
2628+
{});
2629+
}
2630+
25862631
void swift::simple_display(llvm::raw_ostream &out, const DeclAttribute *attr) {
25872632
if (attr)
25882633
attr->print(out);

lib/AST/Decl.cpp

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4948,11 +4948,8 @@ void NominalTypeDecl::collectPropertiesInitializableByInitAccessors(
49484948
std::multimap<VarDecl *, VarDecl *> &result) const {
49494949
for (auto *property : getInitAccessorProperties()) {
49504950
auto *initAccessor = property->getAccessor(AccessorKind::Init);
4951-
if (auto *initAttr =
4952-
initAccessor->getAttrs().getAttribute<InitializesAttr>()) {
4953-
for (auto *subsumed : initAttr->getPropertyDecls(initAccessor))
4954-
result.insert({subsumed, property});
4955-
}
4951+
for (auto *subsumed : initAccessor->getInitializedProperties())
4952+
result.insert({subsumed, property});
49564953
}
49574954
}
49584955

@@ -6868,21 +6865,15 @@ bool VarDecl::isSettable(const DeclContext *UseDC,
68686865
// designated initializer(s) or by init accessors.
68696866
if (isInstanceMember()) {
68706867
// Init accessors allow assignments to `let` properties if a
6871-
// property is part of `initializes(...)` list.
6868+
// property is part of `initializes` list.
68726869
if (auto *accessor =
68736870
dyn_cast<AccessorDecl>(const_cast<DeclContext *>(UseDC))) {
6874-
// Check whether this property is part of `initializes(...)` list,
6871+
// Check whether this property is part of `initializes` list,
68756872
// and allow assignment/mutation if so. DI would be responsible
68766873
// for checking for re-assignment.
6877-
if (auto *initAttr =
6878-
accessor->getAttrs().getAttribute<InitializesAttr>()) {
6879-
return llvm::is_contained(initAttr->getPropertyDecls(accessor),
6880-
const_cast<VarDecl *>(this));
6881-
}
6882-
6883-
// If there is no `initializes` attribute, no referenced properties
6884-
// can be assignment to or mutated.
6885-
return false;
6874+
return accessor->isInitAccessor() &&
6875+
llvm::is_contained(accessor->getInitializedProperties(),
6876+
const_cast<VarDecl *>(this));
68866877
}
68876878

68886879
auto *CD = dyn_cast<ConstructorDecl>(UseDC);
@@ -9548,6 +9539,32 @@ void AccessorDecl::printUserFacingName(raw_ostream &out) const {
95489539
out << ")";
95499540
}
95509541

9542+
ArrayRef<VarDecl *> AccessorDecl::getInitializedProperties() const {
9543+
assert(isInitAccessor());
9544+
9545+
if (auto *SR = getAttrs().getAttribute<StorageRestrictionsAttr>())
9546+
return SR->getInitializesProperties(const_cast<AccessorDecl *>(this));
9547+
9548+
// Fallback to old effect style declaration.
9549+
if (auto *initAttr = getAttrs().getAttribute<InitializesAttr>())
9550+
return initAttr->getPropertyDecls(const_cast<AccessorDecl *>(this));
9551+
9552+
return {};
9553+
}
9554+
9555+
ArrayRef<VarDecl *> AccessorDecl::getAccessedProperties() const {
9556+
assert(isInitAccessor());
9557+
9558+
if (auto *SR = getAttrs().getAttribute<StorageRestrictionsAttr>())
9559+
return SR->getAccessesProperties(const_cast<AccessorDecl *>(this));
9560+
9561+
// Fallback to old effect style declaration.
9562+
if (auto *accessAttr = getAttrs().getAttribute<AccessesAttr>())
9563+
return accessAttr->getPropertyDecls(const_cast<AccessorDecl *>(this));
9564+
9565+
return {};
9566+
}
9567+
95519568
StaticSpellingKind FuncDecl::getCorrectStaticSpelling() const {
95529569
assert(getDeclContext()->isTypeContext());
95539570
if (!isStatic())

lib/Macros/Sources/ObservationMacros/ObservableMacro.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,8 @@ public struct ObservationTrackedMacro: AccessorMacro {
312312

313313
let initAccessor: AccessorDeclSyntax =
314314
"""
315-
init(initialValue) initializes(_\(identifier)) {
315+
@storageRestrictions(initializes: _\(identifier))
316+
init(initialValue) {
316317
_\(identifier) = initialValue
317318
}
318319
"""

lib/Parse/ParseDecl.cpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,6 +1165,111 @@ bool Parser::parseSpecializeAttribute(
11651165
return true;
11661166
}
11671167

1168+
ParserResult<StorageRestrictionsAttr>
1169+
Parser::parseStorageRestrictionsAttribute(SourceLoc AtLoc, SourceLoc Loc) {
1170+
StringRef AttrName = "storageRestrictions";
1171+
ParserStatus Status;
1172+
1173+
if (Tok.isNot(tok::l_paren)) {
1174+
diagnose(Loc, diag::attr_expected_lparen, AttrName,
1175+
/*DeclModifier=*/false);
1176+
Status.setIsParseError();
1177+
return Status;
1178+
}
1179+
1180+
// Consume '('
1181+
SourceLoc lParenLoc = consumeToken();
1182+
1183+
SmallVector<Identifier> initializesProperties;
1184+
SmallVector<Identifier> accessesProperties;
1185+
1186+
auto parseProperties = [&](SourceLoc loc, Identifier label,
1187+
SmallVectorImpl<Identifier> &properties) {
1188+
if (!properties.empty()) {
1189+
diagnose(loc, diag::duplicate_storage_restrictions_attr_label, label);
1190+
return true;
1191+
}
1192+
1193+
bool hasNextProperty = false;
1194+
do {
1195+
// Next is not a property name but a label followed by ':'
1196+
if (peekToken().is(tok::colon))
1197+
break;
1198+
1199+
Identifier propertyName;
1200+
SourceLoc propertyNameLoc;
1201+
if (parseIdentifier(propertyName, propertyNameLoc,
1202+
diag::storage_restrictions_attr_expected_name,
1203+
/*diagnoseDollarPrefix=*/true)) {
1204+
return true;
1205+
}
1206+
1207+
properties.push_back(propertyName);
1208+
1209+
// Parse the comma, if the list continues.
1210+
hasNextProperty = consumeIf(tok::comma);
1211+
} while (hasNextProperty);
1212+
1213+
return false;
1214+
};
1215+
1216+
auto parseArgument = [&](bool isOptional = false) -> bool {
1217+
if (Tok.is(tok::r_paren) && isOptional)
1218+
return false;
1219+
1220+
Identifier accessLabel;
1221+
SourceLoc loc;
1222+
parseOptionalArgumentLabel(accessLabel, loc);
1223+
1224+
if (accessLabel.empty()) {
1225+
diagnose(Loc, diag::missing_storage_restrictions_attr_label);
1226+
return true;
1227+
}
1228+
1229+
enum class AccessKind { Initialization, Access, Invalid };
1230+
1231+
auto access = llvm::StringSwitch<AccessKind>(accessLabel.str())
1232+
.Case("initializes", AccessKind::Initialization)
1233+
.Case("accesses", AccessKind::Access)
1234+
.Default(AccessKind::Invalid);
1235+
1236+
switch (access) {
1237+
case AccessKind::Initialization:
1238+
return parseProperties(loc, accessLabel, initializesProperties);
1239+
1240+
case AccessKind::Access:
1241+
return parseProperties(loc, accessLabel, accessesProperties);
1242+
1243+
case AccessKind::Invalid:
1244+
diagnose(loc, diag::invalid_storage_restrictions_attr_label, accessLabel);
1245+
return true;
1246+
}
1247+
};
1248+
1249+
// Attribute should have at least one argument.
1250+
if (parseArgument() || parseArgument(/*isOptional=*/true)) {
1251+
Status.setIsParseError();
1252+
// Let's skip ahead to `)` to recover.
1253+
skipUntil(tok::r_paren);
1254+
}
1255+
1256+
// Consume ')'
1257+
SourceLoc rParenLoc;
1258+
if (!consumeIf(tok::r_paren, rParenLoc)) {
1259+
diagnose(lParenLoc, diag::attr_expected_rparen, AttrName,
1260+
/*DeclModifier=*/false);
1261+
Status.setIsParseError();
1262+
}
1263+
1264+
if (Status.isErrorOrHasCompletion()) {
1265+
return Status;
1266+
}
1267+
1268+
return ParserResult<StorageRestrictionsAttr>(StorageRestrictionsAttr::create(
1269+
Context, AtLoc, SourceRange(Loc, rParenLoc), initializesProperties,
1270+
accessesProperties));
1271+
}
1272+
11681273
ParserResult<ImplementsAttr>
11691274
Parser::parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc) {
11701275
StringRef AttrName = "_implements";
@@ -3424,6 +3529,15 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
34243529
llvm_unreachable("AccessesAttr not yet implemented");
34253530
}
34263531

3532+
case DAK_StorageRestrictions: {
3533+
ParserResult<StorageRestrictionsAttr> Attr =
3534+
parseStorageRestrictionsAttribute(AtLoc, Loc);
3535+
if (Attr.isNonNull()) {
3536+
Attributes.add(Attr.get());
3537+
}
3538+
break;
3539+
}
3540+
34273541
case DAK_Implements: {
34283542
ParserResult<ImplementsAttr> Attr = parseImplementsAttribute(AtLoc, Loc);
34293543
if (Attr.isNonNull()) {

0 commit comments

Comments
 (0)