Skip to content

Commit b1e30b2

Browse files
committed
[Sema] Impliment initial validation of @execution(concurrent) attribute
1 parent 667a976 commit b1e30b2

File tree

4 files changed

+159
-0
lines changed

4 files changed

+159
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8211,5 +8211,32 @@ ERROR(attr_abi_incompatible_with_silgen_name,none,
82118211
"the same purpose",
82128212
(DescriptiveDeclKind))
82138213

8214+
//===----------------------------------------------------------------------===//
8215+
// MARK: @execution Attribute
8216+
//===----------------------------------------------------------------------===//
8217+
ERROR(attr_execution_concurrent_only_on_async,none,
8218+
"cannot use '@execution(concurrent)' on non-async %kind0",
8219+
(ValueDecl *))
8220+
8221+
ERROR(attr_execution_concurrent_incompatible_with_global_actor,none,
8222+
"cannot use '@execution(concurrent)' on %kind0 isolated to global actor %1",
8223+
(ValueDecl *, ValueDecl *))
8224+
8225+
ERROR(attr_execution_concurrent_incompatible_isolated_parameter,none,
8226+
"cannot use '@execution(concurrent)' on %kind0 because it has "
8227+
"an isolated parameter: %1",
8228+
(ValueDecl *, ValueDecl *))
8229+
8230+
ERROR(attr_execution_concurrent_incompatible_dynamically_isolated_parameter,none,
8231+
"cannot use '@execution(concurrent)' on %kind0 because it has "
8232+
"a dynamically isolated parameter: %1",
8233+
(ValueDecl *, ValueDecl *))
8234+
8235+
ERROR(attr_execution_concurrent_incompatible_with_nonisolated,none,
8236+
"cannot use '@execution(concurrent)' and 'nonisolated' on the same %0 "
8237+
"because they serve the same purpose",
8238+
(ValueDecl *))
8239+
8240+
82148241
#define UNDEFINE_DIAGNOSTIC_MACROS
82158242
#include "DefineDiagnosticMacros.h"

lib/Sema/TypeCheckAttr.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,80 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
253253
}
254254

255255
public:
256+
void visitExecutionAttr(ExecutionAttr *attr) {
257+
auto *F = dyn_cast<FuncDecl>(D);
258+
if (!F)
259+
return;
260+
261+
if (!F->hasAsync()) {
262+
diagnoseAndRemoveAttr(attr, diag::attr_execution_concurrent_only_on_async,
263+
F);
264+
return;
265+
}
266+
267+
// Checks based on explicit attributes, inferred ones would
268+
// have to be handled during actor isolation inference.
269+
270+
switch (attr->getBehavior()) {
271+
case ExecutionKind::Concurrent: {
272+
// 'concurrent' doesn't work with explicit `nonisolated`
273+
if (F->hasExplicitIsolationAttribute()) {
274+
if (F->getAttrs().hasAttribute<NonisolatedAttr>()) {
275+
diagnoseAndRemoveAttr(
276+
attr,
277+
diag::attr_execution_concurrent_incompatible_with_nonisolated, F);
278+
return;
279+
}
280+
281+
if (auto globalActor = F->getGlobalActorAttr()) {
282+
diagnoseAndRemoveAttr(
283+
attr,
284+
diag::attr_execution_concurrent_incompatible_with_global_actor, F,
285+
globalActor->second);
286+
return;
287+
}
288+
}
289+
290+
auto parameters = F->getParameters();
291+
if (!parameters)
292+
return;
293+
294+
for (auto *P : *parameters) {
295+
auto *repr = P->getTypeRepr();
296+
if (!repr)
297+
continue;
298+
299+
// isolated parameters affect isolation of the function itself
300+
if (isa<IsolatedTypeRepr>(repr)) {
301+
diagnoseAndRemoveAttr(
302+
attr,
303+
diag::attr_execution_concurrent_incompatible_isolated_parameter,
304+
F, P);
305+
return;
306+
}
307+
308+
if (auto *attrType = dyn_cast<AttributedTypeRepr>(repr)) {
309+
if (attrType->has(TypeAttrKind::Isolated)) {
310+
diagnoseAndRemoveAttr(
311+
attr,
312+
diag::
313+
attr_execution_concurrent_incompatible_dynamically_isolated_parameter,
314+
F, P);
315+
return;
316+
}
317+
}
318+
}
319+
320+
break;
321+
}
322+
323+
case ExecutionKind::Caller: {
324+
// no restrictions for now.
325+
break;
326+
}
327+
}
328+
}
329+
256330
void visitABIAttr(ABIAttr *attr) {
257331
Decl *AD = attr->abiDecl;
258332
if (isa<VarDecl>(D) && isa<PatternBindingDecl>(AD)) {

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1593,6 +1593,7 @@ namespace {
15931593
UNINTERESTING_ATTR(DynamicCallable)
15941594
UNINTERESTING_ATTR(DynamicMemberLookup)
15951595
UNINTERESTING_ATTR(SILGenName)
1596+
UNINTERESTING_ATTR(Execution)
15961597
UNINTERESTING_ATTR(Exported)
15971598
UNINTERESTING_ATTR(ForbidSerializingReference)
15981599
UNINTERESTING_ATTR(GKInspectable)

test/attr/attr_execution.swift

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// RUN: %target-typecheck-verify-swift -target %target-swift-5.1-abi-triple -enable-experimental-feature NonIsolatedAsyncInheritsIsolationFromContext
2+
3+
// REQUIRES: concurrency
4+
// REQUIRES: swift_feature_NonIsolatedAsyncInheritsIsolationFromContext
5+
6+
@execution(something) func invalidAttr() async {} // expected-error {{unknown option 'something' for attribute 'execution'}}
7+
8+
@execution(concurrent) @execution(caller) func mutipleAttrs() async {}
9+
// expected-error@-1 {{duplicate attribute}} expected-note@-1 {{attribute already specified here}}
10+
11+
@execution(concurrent) func nonAsync1() {}
12+
// expected-error@-1 {{cannot use '@execution(concurrent)' on non-async global function 'nonAsync1()'}}
13+
14+
@execution(caller) func nonAsync2() {}
15+
// expected-error@-1 {{cannot use '@execution(concurrent)' on non-async global function 'nonAsync2()'}}
16+
17+
@execution(concurrent) func testGlobal() async {} // Ok
18+
19+
struct Test {
20+
@execution(concurrent) init() {}
21+
// expected-error@-1 {{@execution(concurrent) may only be used on 'func' declarations}}
22+
23+
@execution(concurrent) func member() {}
24+
// expected-error@-1 {{cannot use '@execution(concurrent)' on non-async instance method 'member()'}}
25+
26+
@execution(concurrent) func member() async {} // Ok
27+
28+
// expected-error@+1 {{@execution(caller) may only be used on 'func' declarations}}
29+
@execution(caller) subscript(a: Int) -> Bool {
30+
get { false }
31+
@execution(concurrent) set { }
32+
// expected-error@-1 {{@execution(concurrent) may only be used on 'func' declarations}}
33+
}
34+
35+
@execution(caller) var x: Int
36+
// expected-error@-1 {{@execution(caller) may only be used on 'func' declarations}}
37+
}
38+
39+
do {
40+
@execution(caller) func local() async {} // Ok
41+
}
42+
43+
struct TestAttributeCollisions {
44+
@execution(concurrent) nonisolated func testNonIsolated() async {}
45+
// expected-error@-1 {{cannot use '@execution(concurrent)' and 'nonisolated' on the same 'testNonIsolated()' because they serve the same purpose}}
46+
47+
@execution(concurrent) func test(arg: isolated MainActor) async {}
48+
// expected-error@-1 {{cannot use '@execution(concurrent)' on instance method 'test(arg:)' because it has an isolated parameter: 'arg'}}
49+
50+
@execution(concurrent) func testIsolationAny(arg: @isolated(any) () -> Void) async {}
51+
// expected-error@-1 {{cannot use '@execution(concurrent)' on instance method 'testIsolationAny(arg:)' because it has a dynamically isolated parameter: 'arg'}}
52+
53+
@MainActor @execution(concurrent) func testGlobalActor() async {}
54+
// expected-error@-1 {{cannot use '@execution(concurrent)' on instance method 'testGlobalActor()' isolated to global actor 'MainActor'}}
55+
56+
@execution(concurrent) @Sendable func test(_: @Sendable () -> Void, _: sending Int) async {} // Ok
57+
}

0 commit comments

Comments
 (0)