Skip to content

Commit e214ba5

Browse files
authored
Merge pull request #69735 from Azoy/static-exclusive-only
Introduce @_staticExclusiveOnly
2 parents 1fdb7fb + 6234478 commit e214ba5

File tree

9 files changed

+167
-1
lines changed

9 files changed

+167
-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 | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
535+
151)
533536

534537
#undef TYPE_ATTR
535538
#undef DECL_ATTR_ALIAS

include/swift/AST/DiagnosticsSema.def

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1917,6 +1917,20 @@ 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+
NOTE(attr_static_exclusive_only_type_nonmutating,none,
1928+
"%0 is a non-mutable type", (Type))
1929+
ERROR(attr_static_exclusive_only_let_only_param,none,
1930+
"parameter of type %0 must be declared as either 'borrowing' or 'consuming'", (Type))
1931+
ERROR(attr_static_exclusive_only_mutating,none,
1932+
"type %0 cannot have mutating function %1", (Type, ValueDecl *))
1933+
19201934
ERROR(c_func_variadic, none,
19211935
"cannot declare variadic argument %0 in %kind1",
19221936
(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: 27 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,22 @@ 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+
return;
7263+
}
7264+
7265+
// Can only be applied to structs.
7266+
auto structDecl = cast<StructDecl>(D);
7267+
7268+
if (!structDecl->getDeclaredInterfaceType()
7269+
->isNoncopyable(D->getDeclContext())) {
7270+
diagnoseAndRemoveAttr(attr, diag::attr_static_exclusive_only_noncopyable);
7271+
}
7272+
}
7273+
72477274
namespace {
72487275

72497276
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: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2468,6 +2468,21 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
24682468
}
24692469
}
24702470
}
2471+
2472+
// @_staticExclusiveOnly types cannot be put into 'var's, only 'let'.
2473+
if (auto SD = VD->getInterfaceType()->getStructOrBoundGenericStruct()) {
2474+
if (getASTContext().LangOpts.hasFeature(Feature::StaticExclusiveOnly) &&
2475+
SD->getAttrs().hasAttribute<StaticExclusiveOnlyAttr>() &&
2476+
!VD->isLet()) {
2477+
SD->getASTContext().Diags.diagnoseWithNotes(
2478+
VD->diagnose(diag::attr_static_exclusive_only_let_only,
2479+
VD->getInterfaceType()),
2480+
[&]() {
2481+
SD->diagnose(diag::attr_static_exclusive_only_type_nonmutating,
2482+
SD->getDeclaredInterfaceType());
2483+
});
2484+
}
2485+
}
24712486
}
24722487

24732488
bool checkBoundInOutVarDecl(PatternBindingDecl *pbd, unsigned patternIndex,
@@ -4146,6 +4161,22 @@ void TypeChecker::checkParameterList(ParameterList *params,
41464161
}
41474162
}
41484163
}
4164+
4165+
// @_staticExclusiveOnly types cannot be passed as 'inout', only as either
4166+
// a borrow or as consuming.
4167+
if (auto SD = param->getInterfaceType()->getStructOrBoundGenericStruct()) {
4168+
if (SD->getASTContext().LangOpts.hasFeature(Feature::StaticExclusiveOnly) &&
4169+
SD->getAttrs().hasAttribute<StaticExclusiveOnlyAttr>() &&
4170+
param->isInOut()) {
4171+
SD->getASTContext().Diags.diagnoseWithNotes(
4172+
param->diagnose(diag::attr_static_exclusive_only_let_only_param,
4173+
param->getInterfaceType()),
4174+
[&]() {
4175+
SD->diagnose(diag::attr_static_exclusive_only_type_nonmutating,
4176+
SD->getDeclaredInterfaceType());
4177+
});
4178+
}
4179+
}
41494180
}
41504181

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

lib/Sema/TypeCheckType.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3556,10 +3556,23 @@ 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 (getASTContext().LangOpts.hasFeature(Feature::StaticExclusiveOnly) &&
3567+
SD->getAttrs().hasAttribute<StaticExclusiveOnlyAttr>() &&
3568+
ownership == ParamSpecifier::InOut) {
3569+
diagnose(eltTypeRepr->getLoc(),
3570+
diag::attr_static_exclusive_only_let_only_param,
3571+
ty);
3572+
}
3573+
}
3574+
}
3575+
35633576
Identifier argumentLabel;
35643577
Identifier parameterName;
35653578
if (inputRepr->getElement(i).NameLoc.isValid() &&
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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 { // expected-note {{'B' is a non-mutable type}}
8+
// expected-note@-1 {{'B' is a non-mutable type}}
9+
// expected-note@-2 {{'B' is a non-mutable type}}
10+
// expected-note@-3 {{'B' is a non-mutable type}}
11+
// expected-note@-4 {{'B' is a non-mutable type}}
12+
mutating func change() { // expected-error {{type 'B' cannot have mutating function 'change()'}}
13+
print("123")
14+
}
15+
}
16+
17+
let b0 = B() // OK
18+
19+
var b1 = B() // expected-error {{variable of type 'B' must be declared with a 'let'}}
20+
21+
class C {
22+
var b2 = B() // expected-error {{variable of type 'B' must be declared with a 'let'}}
23+
let b3 = B() // OK
24+
}
25+
26+
struct D: ~Copyable {
27+
var b4 = B() // expected-error {{variable of type 'B' must be declared with a 'let'}}
28+
let b5 = B() // OK
29+
}
30+
31+
func e(_: borrowing B) {} // OK
32+
33+
func f(_: inout B) {} // expected-error {{parameter of type 'B' must be declared as either 'borrowing' or 'consuming'}}
34+
35+
func g(_: (inout B) -> ()) {} // expected-error {{parameter of type 'B' must be declared as either 'borrowing' or 'consuming'}}
36+
37+
func h(_: (borrowing B) -> ()) {} // OK
38+
39+
func i() {
40+
let _: (Int, Int) -> Int = {
41+
var b6 = B() // expected-error {{variable of type 'B' must be declared with a 'let'}}
42+
// expected-warning@-1 {{initialization of variable 'b6' was never used; consider replacing with assignment to '_' or removing it}}
43+
44+
return $0 + $1
45+
}
46+
}
47+
48+
@_staticExclusiveOnly // expected-error {{@_staticExclusiveOnly may only be used on 'struct' declarations}}
49+
enum J {}
50+
51+
@_staticExclusiveOnly // expected-error {{@_staticExclusiveOnly may only be used on 'struct' declarations}}
52+
class K {}
53+
54+
@_staticExclusiveOnly // expected-error {{@_staticExclusiveOnly may only be used on 'struct' declarations}}
55+
func l() {}
56+
57+
@_staticExclusiveOnly // expected-error {{@_staticExclusiveOnly may only be used on 'struct' declarations}}
58+
let m = 123
59+
60+
@_staticExclusiveOnly // expected-error {{@_staticExclusiveOnly may only be used on 'struct' declarations}}
61+
protocol N {}
62+
63+
func o(_: consuming B) {} // OK
64+
65+
func p(_: (consuming B) -> ()) {} // OK
66+
67+
@_staticExclusiveOnly
68+
struct Q<T>: ~Copyable {} // expected-note {{'Q<T>' is a non-mutable type}}
69+
70+
var r0 = Q<Int>() // expected-error {{variable of type 'Q<Int>' must be declared with a 'let'}}

0 commit comments

Comments
 (0)