Skip to content

Commit 67c2b75

Browse files
authored
Merge pull request #40985 from eeckstein/exclusivity-attribute
Add the `@exclusivity` attribute.
2 parents 83484f8 + 5fd941e commit 67c2b75

File tree

14 files changed

+215
-2
lines changed

14 files changed

+215
-2
lines changed

include/swift/AST/Attr.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,11 @@ DECL_ATTR(_unavailableFromAsync, UnavailableFromAsync,
705705
APIBreakingToAdd | APIStableToRemove,
706706
127)
707707

708+
DECL_ATTR(exclusivity, Exclusivity,
709+
OnVar |
710+
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
711+
128)
712+
708713
// If you're adding a new underscored attribute here, please document it in
709714
// docs/ReferenceGuides/UnderscoredAttributes.md.
710715

include/swift/AST/Attr.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,32 @@ class OptimizeAttr : public DeclAttribute {
11991199
}
12001200
};
12011201

1202+
/// Represents the exclusivity attribute.
1203+
class ExclusivityAttr : public DeclAttribute {
1204+
public:
1205+
enum Mode {
1206+
Checked,
1207+
Unchecked
1208+
};
1209+
1210+
private:
1211+
Mode mode;
1212+
1213+
public:
1214+
ExclusivityAttr(SourceLoc atLoc, SourceRange range, Mode mode)
1215+
: DeclAttribute(DAK_Exclusivity, atLoc, range, /*Implicit=*/false),
1216+
mode(mode) {}
1217+
1218+
ExclusivityAttr(Mode mode)
1219+
: ExclusivityAttr(SourceLoc(), SourceRange(), mode) {}
1220+
1221+
Mode getMode() const { return mode; }
1222+
1223+
static bool classof(const DeclAttribute *DA) {
1224+
return DA->getKind() == DAK_Exclusivity;
1225+
}
1226+
};
1227+
12021228
/// Represents the side effects attribute.
12031229
class EffectsAttr : public DeclAttribute {
12041230
public:

include/swift/AST/DiagnosticsSema.def

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5314,6 +5314,17 @@ ERROR(nonobjc_not_allowed,none,
53145314
#undef OBJC_DIAG_SELECT
53155315
#undef OBJC_ATTR_SELECT
53165316

5317+
//------------------------------------------------------------------------------
5318+
// MARK: @exclusivity
5319+
//------------------------------------------------------------------------------
5320+
ERROR(exclusivity_on_wrong_decl,none,
5321+
"@exclusivity can only be used on class properties, static properties and global variables",
5322+
())
5323+
5324+
ERROR(exclusivity_on_computed_property,none,
5325+
"@exclusivity can only be used on stored properties",
5326+
())
5327+
53175328
//------------------------------------------------------------------------------
53185329
// MARK: @_borrowed
53195330
//------------------------------------------------------------------------------

lib/AST/Attr.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,7 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
883883
case DAK_ReferenceOwnership:
884884
case DAK_Effects:
885885
case DAK_Optimize:
886+
case DAK_Exclusivity:
886887
case DAK_NonSendable:
887888
if (DeclAttribute::isDeclModifier(getKind())) {
888889
Printer.printKeyword(getAttrName(), Options);
@@ -1289,6 +1290,16 @@ StringRef DeclAttribute::getAttrName() const {
12891290
llvm_unreachable("Invalid optimization kind");
12901291
}
12911292
}
1293+
case DAK_Exclusivity: {
1294+
switch (cast<ExclusivityAttr>(this)->getMode()) {
1295+
case ExclusivityAttr::Checked:
1296+
return "exclusivity(checked)";
1297+
case ExclusivityAttr::Unchecked:
1298+
return "exclusivity(unchecked)";
1299+
default:
1300+
llvm_unreachable("Invalid optimization kind");
1301+
}
1302+
}
12921303
case DAK_Effects:
12931304
switch (cast<EffectsAttr>(this)->getKind()) {
12941305
case EffectsKind::ReadNone:

lib/Parse/ParseDecl.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1996,6 +1996,21 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
19961996
break;
19971997
}
19981998

1999+
case DAK_Exclusivity: {
2000+
auto mode = parseSingleAttrOption<ExclusivityAttr::Mode>
2001+
(*this, Loc, AttrRange, AttrName, DK)
2002+
.when("checked", ExclusivityAttr::Mode::Checked)
2003+
.when("unchecked", ExclusivityAttr::Mode::Unchecked)
2004+
.diagnoseWhenOmitted();
2005+
if (!mode)
2006+
return false;
2007+
2008+
if (!DiscardAttribute)
2009+
Attributes.add(new (Context) ExclusivityAttr(AtLoc, AttrRange, *mode));
2010+
2011+
break;
2012+
}
2013+
19992014
case DAK_ReferenceOwnership: {
20002015
// Handle weak/unowned/unowned(unsafe).
20012016
auto Kind = AttrName == "weak" ? ReferenceOwnership::Weak

lib/SILGen/SILGenLValue.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,16 @@ static bool shouldUseUnsafeEnforcement(VarDecl *var) {
240240
if (var->isDebuggerVar())
241241
return true;
242242

243-
// TODO: Check for the explicit "unsafe" attribute.
244243
return false;
245244
}
246245

246+
static bool hasExclusivityAttr(VarDecl *var, ExclusivityAttr::Mode mode) {
247+
if (!var)
248+
return false;
249+
auto *exclAttr = var->getAttrs().getAttribute<ExclusivityAttr>();
250+
return exclAttr && exclAttr->getMode() == mode;
251+
}
252+
247253
Optional<SILAccessEnforcement>
248254
SILGenFunction::getStaticEnforcement(VarDecl *var) {
249255
if (var && shouldUseUnsafeEnforcement(var))
@@ -257,6 +263,10 @@ SILGenFunction::getDynamicEnforcement(VarDecl *var) {
257263
if (getOptions().EnforceExclusivityDynamic) {
258264
if (var && shouldUseUnsafeEnforcement(var))
259265
return SILAccessEnforcement::Unsafe;
266+
if (hasExclusivityAttr(var, ExclusivityAttr::Unchecked))
267+
return SILAccessEnforcement::Unsafe;
268+
return SILAccessEnforcement::Dynamic;
269+
} else if (hasExclusivityAttr(var, ExclusivityAttr::Checked)) {
260270
return SILAccessEnforcement::Dynamic;
261271
}
262272
return None;

lib/Sema/TypeCheckAttr.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
239239
void visitUsableFromInlineAttr(UsableFromInlineAttr *attr);
240240
void visitInlinableAttr(InlinableAttr *attr);
241241
void visitOptimizeAttr(OptimizeAttr *attr);
242+
void visitExclusivityAttr(ExclusivityAttr *attr);
242243

243244
void visitDiscardableResultAttr(DiscardableResultAttr *attr);
244245
void visitDynamicReplacementAttr(DynamicReplacementAttr *attr);
@@ -2471,6 +2472,27 @@ void AttributeChecker::visitOptimizeAttr(OptimizeAttr *attr) {
24712472
}
24722473
}
24732474

2475+
void AttributeChecker::visitExclusivityAttr(ExclusivityAttr *attr) {
2476+
if (auto *varDecl = dyn_cast<VarDecl>(D)) {
2477+
if (!varDecl->hasStorage()) {
2478+
diagnose(attr->getLocation(), diag::exclusivity_on_computed_property);
2479+
attr->setInvalid();
2480+
return;
2481+
}
2482+
2483+
if (isa<ClassDecl>(varDecl->getDeclContext()))
2484+
return;
2485+
2486+
if (varDecl->getDeclContext()->isTypeContext() && !varDecl->isInstanceMember())
2487+
return;
2488+
2489+
if (varDecl->getDeclContext()->isModuleScopeContext())
2490+
return;
2491+
}
2492+
diagnose(attr->getLocation(), diag::exclusivity_on_wrong_decl);
2493+
attr->setInvalid();
2494+
}
2495+
24742496
void AttributeChecker::visitDiscardableResultAttr(DiscardableResultAttr *attr) {
24752497
if (auto *FD = dyn_cast<FuncDecl>(D)) {
24762498
if (auto result = FD->getResultInterfaceType()) {

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,6 +1466,7 @@ namespace {
14661466
UNINTERESTING_ATTR(InheritsConvenienceInitializers)
14671467
UNINTERESTING_ATTR(Inline)
14681468
UNINTERESTING_ATTR(Optimize)
1469+
UNINTERESTING_ATTR(Exclusivity)
14691470
UNINTERESTING_ATTR(NoLocks)
14701471
UNINTERESTING_ATTR(NoAllocation)
14711472
UNINTERESTING_ATTR(Inlinable)

lib/Serialization/Deserialization.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4416,6 +4416,14 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() {
44164416
break;
44174417
}
44184418

4419+
case decls_block::Exclusivity_DECL_ATTR: {
4420+
unsigned kind;
4421+
serialization::decls_block::ExclusivityDeclAttrLayout::readRecord(
4422+
scratch, kind);
4423+
Attr = new (ctx) ExclusivityAttr((ExclusivityAttr::Mode)kind);
4424+
break;
4425+
}
4426+
44194427
case decls_block::Effects_DECL_ATTR: {
44204428
unsigned kind;
44214429
serialization::decls_block::EffectsDeclAttrLayout::readRecord(scratch,

lib/Serialization/ModuleFormat.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5656
/// describe what change you made. The content of this comment isn't important;
5757
/// it just ensures a conflict if two people change the module format.
5858
/// Don't worry about adhering to the 80-column limit for this line.
59-
const uint16_t SWIFTMODULE_VERSION_MINOR = 660; // remove nested archetypes
59+
const uint16_t SWIFTMODULE_VERSION_MINOR = 661; // @exclusivity attribute
6060

6161
/// A standard hash seed used for all string hashes in a serialized module.
6262
///
@@ -1923,6 +1923,11 @@ namespace decls_block {
19231923
BCFixed<2> // optimize value
19241924
>;
19251925

1926+
using ExclusivityDeclAttrLayout = BCRecordLayout<
1927+
Optimize_DECL_ATTR,
1928+
BCFixed<2> // exclusivity mode
1929+
>;
1930+
19261931
using AvailableDeclAttrLayout = BCRecordLayout<
19271932
Available_DECL_ATTR,
19281933
BCFixed<1>, // implicit flag

0 commit comments

Comments
 (0)