Skip to content

Commit 9992af5

Browse files
committed
[Parse] InitAccessors: Implement @storageRestrictions parsing, diagnostics and error recovery
1 parent 387bcf3 commit 9992af5

File tree

5 files changed

+165
-1
lines changed

5 files changed

+165
-1
lines changed

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/Parse/ParseDecl.cpp

Lines changed: 111 additions & 1 deletion
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";
@@ -3425,7 +3530,12 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
34253530
}
34263531

34273532
case DAK_StorageRestrictions: {
3428-
llvm_unreachable("StorageRestrictionsAttr not yet implemented");
3533+
ParserResult<StorageRestrictionsAttr> Attr =
3534+
parseStorageRestrictionsAttribute(AtLoc, Loc);
3535+
if (Attr.isNonNull()) {
3536+
Attributes.add(Attr.get());
3537+
}
3538+
break;
34293539
}
34303540

34313541
case DAK_Implements: {

test/decl/var/init_accessors.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,3 +521,37 @@ do {
521521
}
522522
}
523523
}
524+
525+
func test_invalid_storage_restrictions() {
526+
struct Test {
527+
var a: Int {
528+
@storageRestrictions()
529+
// expected-error@-1 {{missing label in @storageRestrictions attribute}}
530+
init {}
531+
}
532+
533+
var b: Int {
534+
@storageRestrictions(initializes:)
535+
// expected-error@-1 {{expected property name in @storageRestrictions list}}
536+
init {}
537+
}
538+
539+
var c: Int {
540+
@storageRestrictions(initializes: a, initializes: b)
541+
// expected-error@-1 {{duplicate label 'initializes' in @storageRestrictions attribute}}
542+
init {}
543+
}
544+
545+
var d: Int {
546+
@storageRestrictions(accesses: a, accesses: c)
547+
// expected-error@-1 {{duplicate label 'accesses' in @storageRestrictions attribute}}
548+
init {}
549+
}
550+
551+
var e: Int {
552+
@storageRestrictions(initialize: a, b, accesses: c, d)
553+
// expected-error@-1 {{unexpected label 'initialize' in @storageRestrictions attribute}}
554+
init {}
555+
}
556+
}
557+
}

0 commit comments

Comments
 (0)