Skip to content

Commit 7da7c73

Browse files
committed
Introduce @_staticExclusiveOnly
1 parent 98e65d0 commit 7da7c73

File tree

9 files changed

+122
-1
lines changed

9 files changed

+122
-1
lines changed

include/swift/AST/Attr.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,9 @@ CONTEXTUAL_SIMPLE_DECL_ATTR(actor, Actor,
530530
CONTEXTUAL_SIMPLE_DECL_ATTR(_resultDependsOnSelf, ResultDependsOnSelf,
531531
OnFunc | DeclModifier | UserInaccessible | ABIBreakingToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
532532
150)
533+
SIMPLE_DECL_ATTR(_staticExclusiveOnly, StaticExclusiveOnly,
534+
OnStruct | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
535+
151)
533536

534537
#undef TYPE_ATTR
535538
#undef DECL_ATTR_ALIAS

include/swift/AST/DiagnosticsSema.def

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1917,6 +1917,18 @@ WARNING(extern_c_maybe_invalid_name, none,
19171917
"C name '%0' may be invalid; explicitly specify the name in @_extern(c) to suppress this warning",
19181918
(StringRef))
19191919

1920+
// @_staticExclusiveOnly
1921+
ERROR(attr_static_exclusive_only_disabled,none,
1922+
"attribute requires '-enable-experimental feature StaticExclusiveOnly'", ())
1923+
ERROR(attr_static_exclusive_only_noncopyable,none,
1924+
"@_staticExclusiveOnly can only be applied to noncopyable types", ())
1925+
ERROR(attr_static_exclusive_only_let_only,none,
1926+
"variable of type %0 must be declared with a 'let'", (Type))
1927+
ERROR(attr_static_exclusive_only_let_only_param,none,
1928+
"parameter of type %0 must be declared as either 'borrowing' or 'consuming'", (Type))
1929+
ERROR(attr_static_exclusive_only_mutating,none,
1930+
"@_staticExclusiveOnly type %0 cannot have mutating function %1", (Type, ValueDecl *))
1931+
19201932
ERROR(c_func_variadic, none,
19211933
"cannot declare variadic argument %0 in %kind1",
19221934
(DeclName, const ValueDecl *))

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,9 @@ EXPERIMENTAL_FEATURE(Extern, true)
259259
// global functions and key paths.
260260
EXPERIMENTAL_FEATURE(InferSendableFromCaptures, false)
261261

262+
/// Enable the `@_staticExclusiveOnly` attribute.
263+
EXPERIMENTAL_FEATURE(StaticExclusiveOnly, true)
264+
262265
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
263266
#undef EXPERIMENTAL_FEATURE
264267
#undef UPCOMING_FEATURE

lib/AST/ASTPrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3640,6 +3640,10 @@ static bool usesFeatureExtern(Decl *decl) {
36403640
return decl->getAttrs().hasAttribute<ExternAttr>();
36413641
}
36423642

3643+
static bool usesFeatureStaticExclusiveOnly(Decl *decl) {
3644+
return decl->getAttrs().hasAttribute<StaticExclusiveOnlyAttr>();
3645+
}
3646+
36433647
/// Suppress the printing of a particular feature.
36443648
static void suppressingFeature(PrintOptions &options, Feature feature,
36453649
llvm::function_ref<void()> action) {

lib/Sema/TypeCheckAttr.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,8 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
353353

354354
void visitNonEscapableAttr(NonEscapableAttr *attr);
355355
void visitUnsafeNonEscapableResultAttr(UnsafeNonEscapableResultAttr *attr);
356+
357+
void visitStaticExclusiveOnlyAttr(StaticExclusiveOnlyAttr *attr);
356358
};
357359

358360
} // end anonymous namespace
@@ -501,6 +503,15 @@ void AttributeChecker::visitMutationAttr(DeclAttribute *attr) {
501503
break;
502504
}
503505
}
506+
507+
// Types who are marked @_staticExclusiveOnly cannot have mutating functions.
508+
if (auto SD = contextTy->getStructOrBoundGenericStruct()) {
509+
if (SD->getAttrs().hasAttribute<StaticExclusiveOnlyAttr>() &&
510+
attrModifier == SelfAccessKind::Mutating) {
511+
diagnoseAndRemoveAttr(attr, diag::attr_static_exclusive_only_mutating,
512+
contextTy, FD);
513+
}
514+
}
504515
} else {
505516
diagnoseAndRemoveAttr(attr, diag::mutating_invalid_global_scope,
506517
attrModifier);
@@ -7244,6 +7255,20 @@ void AttributeChecker::visitUnsafeNonEscapableResultAttr(
72447255
}
72457256
}
72467257

7258+
void AttributeChecker::visitStaticExclusiveOnlyAttr(
7259+
StaticExclusiveOnlyAttr *attr) {
7260+
if (!Ctx.LangOpts.hasFeature(Feature::StaticExclusiveOnly)) {
7261+
diagnoseAndRemoveAttr(attr, diag::attr_static_exclusive_only_disabled);
7262+
}
7263+
7264+
// Can only be applied to structs.
7265+
auto structDecl = cast<StructDecl>(D);
7266+
7267+
if (!structDecl->isNoncopyable()) {
7268+
diagnoseAndRemoveAttr(attr, diag::attr_static_exclusive_only_noncopyable);
7269+
}
7270+
}
7271+
72477272
namespace {
72487273

72497274
class ClosureAttributeChecker

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1630,6 +1630,7 @@ namespace {
16301630
UNINTERESTING_ATTR(LexicalLifetimes)
16311631
UNINTERESTING_ATTR(NonEscapable)
16321632
UNINTERESTING_ATTR(UnsafeNonEscapableResult)
1633+
UNINTERESTING_ATTR(StaticExclusiveOnly)
16331634
#undef UNINTERESTING_ATTR
16341635

16351636
void visitAvailableAttr(AvailableAttr *attr) {

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2481,6 +2481,15 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
24812481
}
24822482
}
24832483
}
2484+
2485+
// @_staticExclusiveOnly types cannot be put into 'var's, only 'let'.
2486+
if (auto SD = VD->getInterfaceType()->getStructOrBoundGenericStruct()) {
2487+
if (SD->getAttrs().hasAttribute<StaticExclusiveOnlyAttr>() &&
2488+
!VD->isLet()) {
2489+
VD->diagnose(diag::attr_static_exclusive_only_let_only,
2490+
VD->getInterfaceType());
2491+
}
2492+
}
24842493
}
24852494

24862495
bool checkBoundInOutVarDecl(PatternBindingDecl *pbd, unsigned patternIndex,
@@ -4223,6 +4232,16 @@ void TypeChecker::checkParameterList(ParameterList *params,
42234232
}
42244233
}
42254234
}
4235+
4236+
// @_staticExclusiveOnly types cannot be passed as 'inout', only as either
4237+
// a borrow or as consuming.
4238+
if (auto SD = param->getInterfaceType()->getStructOrBoundGenericStruct()) {
4239+
if (SD->getAttrs().hasAttribute<StaticExclusiveOnlyAttr>() &&
4240+
param->isInOut()) {
4241+
param->diagnose(diag::attr_static_exclusive_only_let_only_param,
4242+
param->getInterfaceType());
4243+
}
4244+
}
42264245
}
42274246

42284247
// For source compatibility, allow duplicate internal parameter names

lib/Sema/TypeCheckType.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3556,10 +3556,22 @@ TypeResolver::resolveASTFunctionTypeParams(TupleTypeRepr *inputRepr,
35563556
}
35573557

35583558
// Validate the presence of ownership for a noncopyable parameter.
3559-
if (inStage(TypeResolutionStage::Interface))
3559+
if (inStage(TypeResolutionStage::Interface)) {
35603560
diagnoseMissingOwnership(getASTContext(), dc, ownership,
35613561
eltTypeRepr, ty, options);
35623562

3563+
// @_staticExclusiveOnly types cannot be passed as 'inout' in function
3564+
// types.
3565+
if (auto SD = ty->getStructOrBoundGenericStruct()) {
3566+
if (SD->getAttrs().hasAttribute<StaticExclusiveOnlyAttr>() &&
3567+
ownership == ParamSpecifier::InOut) {
3568+
diagnose(eltTypeRepr->getLoc(),
3569+
diag::attr_static_exclusive_only_let_only_param,
3570+
ty);
3571+
}
3572+
}
3573+
}
3574+
35633575
Identifier argumentLabel;
35643576
Identifier parameterName;
35653577
if (inputRepr->getElement(i).NameLoc.isValid() &&
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-feature StaticExclusiveOnly
2+
3+
@_staticExclusiveOnly // expected-error{{@_staticExclusiveOnly can only be applied to noncopyable types}}
4+
struct A {}
5+
6+
@_staticExclusiveOnly // OK
7+
struct B: ~Copyable {
8+
mutating func change() { // expected-error {{@_staticExclusiveOnly type 'B' cannot have mutating function 'change()'}}
9+
print("123")
10+
}
11+
}
12+
13+
let b0 = B() // OK
14+
15+
var b1 = B() // expected-error {{variable of type 'B' must be declared with a 'let'}}
16+
17+
class C {
18+
var b2 = B() // expected-error {{variable of type 'B' must be declared with a 'let'}}
19+
let b3 = B() // OK
20+
}
21+
22+
struct D: ~Copyable {
23+
var b4 = B() // expected-error {{variable of type 'B' must be declared with a 'let'}}
24+
let b5 = B() // OK
25+
}
26+
27+
func e(_: borrowing B) {} // OK
28+
29+
func f(_: inout B) {} // expected-error {{parameter of type 'B' must be declared as either 'borrowing' or 'consuming'}}
30+
31+
func g(_: (inout B) -> ()) {} // expected-error {{parameter of type 'B' must be declared as either 'borrowing' or 'consuming'}}
32+
33+
func h(_: (borrowing B) -> ()) {} // OK
34+
35+
func i() {
36+
let _: (Int, Int) -> Int = {
37+
var b6 = B() // expected-error {{variable of type 'B' must be declared with a 'let'}}
38+
// expected-warning@-1 {{initialization of variable 'b6' was never used; consider replacing with assignment to '_' or removing it}}
39+
40+
return $0 + $1
41+
}
42+
}

0 commit comments

Comments
 (0)