Skip to content

Commit 3772032

Browse files
authored
Merge pull request #78997 from gottesmm/wire-up-execution
[concurrency] Wire up execution(concurrent)/execution(caller)
2 parents 43d8183 + 28826b1 commit 3772032

12 files changed

+179
-75
lines changed

include/swift/AST/Decl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8130,6 +8130,8 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
81308130
return cast_or_null<AbstractFunctionDecl>(ValueDecl::getOverriddenDecl());
81318131
}
81328132

8133+
std::optional<ExecutionKind> getExecutionBehavior() const;
8134+
81338135
/// Whether the declaration is later overridden in the module
81348136
///
81358137
/// Overrides are resolved during type checking; only query this field after

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5967,6 +5967,9 @@ ERROR(actor_isolation_multiple_attr_2,none,
59675967
ERROR(actor_isolation_multiple_attr_3,none,
59685968
"%0 %1 has multiple actor-isolation attributes ('%2', '%3' and '%4')",
59695969
(const Decl *, StringRef, StringRef, StringRef))
5970+
ERROR(actor_isolation_multiple_attr_4,none,
5971+
"%0 %1 has multiple actor-isolation attributes ('%2', '%3', '%4', and '%5')",
5972+
(const Decl *, StringRef, StringRef, StringRef, StringRef))
59705973
ERROR(actor_isolation_override_mismatch,none,
59715974
"%0 %kind1 has different actor isolation from %2 overridden declaration",
59725975
(ActorIsolation, const ValueDecl *, ActorIsolation))
@@ -8243,14 +8246,11 @@ ERROR(attr_abi_incompatible_with_silgen_name,none,
82438246
//===----------------------------------------------------------------------===//
82448247
// MARK: @execution Attribute
82458248
//===----------------------------------------------------------------------===//
8249+
82468250
ERROR(attr_execution_concurrent_only_on_async,none,
82478251
"cannot use '@execution(concurrent)' on non-async %kind0",
82488252
(ValueDecl *))
82498253

8250-
ERROR(attr_execution_concurrent_incompatible_with_global_actor,none,
8251-
"cannot use '@execution(concurrent)' on %kind0 isolated to global actor %1",
8252-
(ValueDecl *, Type))
8253-
82548254
ERROR(attr_execution_concurrent_incompatible_isolated_parameter,none,
82558255
"cannot use '@execution(concurrent)' on %kind0 because it has "
82568256
"an isolated parameter: %1",

lib/AST/Decl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8528,6 +8528,14 @@ void VarDecl::emitLetToVarNoteIfSimple(DeclContext *UseDC) const {
85288528
}
85298529
}
85308530

8531+
std::optional<ExecutionKind>
8532+
AbstractFunctionDecl::getExecutionBehavior() const {
8533+
auto *attr = getAttrs().getAttribute<ExecutionAttr>();
8534+
if (!attr)
8535+
return {};
8536+
return attr->getBehavior();
8537+
}
8538+
85318539
clang::PointerAuthQualifier VarDecl::getPointerAuthQualifier() const {
85328540
if (auto *clangDecl = getClangDecl()) {
85338541
if (auto *valueDecl = dyn_cast<clang::ValueDecl>(clangDecl)) {

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2532,6 +2532,16 @@ static CanSILFunctionType getSILFunctionType(
25322532
if (constant) {
25332533
if (constant->kind == SILDeclRef::Kind::Deallocator) {
25342534
actorIsolation = ActorIsolation::forNonisolated(false);
2535+
} else if (auto *decl = constant->getAbstractFunctionDecl();
2536+
decl && decl->getExecutionBehavior().has_value()) {
2537+
switch (*decl->getExecutionBehavior()) {
2538+
case ExecutionKind::Concurrent:
2539+
actorIsolation = ActorIsolation::forNonisolated(false /*unsafe*/);
2540+
break;
2541+
case ExecutionKind::Caller:
2542+
actorIsolation = ActorIsolation::forCallerIsolationInheriting();
2543+
break;
2544+
}
25352545
} else {
25362546
actorIsolation =
25372547
getActorIsolationOfContext(constant->getInnermostDeclContext());

lib/Sema/TypeCheckAttr.cpp

Lines changed: 76 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -306,18 +306,6 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
306306
}
307307
}
308308

309-
// We need isolation check here because global actor isolation
310-
// could be inferred.
311-
312-
auto isolation = getActorIsolation(F);
313-
if (isolation.isGlobalActor()) {
314-
diagnoseAndRemoveAttr(
315-
attr,
316-
diag::attr_execution_concurrent_incompatible_with_global_actor, F,
317-
isolation.getGlobalActor());
318-
return;
319-
}
320-
321309
break;
322310
}
323311

@@ -4418,6 +4406,77 @@ void AttributeChecker::visitFrozenAttr(FrozenAttr *attr) {
44184406
}
44194407
}
44204408

4409+
static void checkGlobalActorAttr(
4410+
const Decl *decl,
4411+
std::pair<CustomAttr *, NominalTypeDecl *> &globalActorAttr) {
4412+
auto isolatedAttr = decl->getAttrs().getAttribute<IsolatedAttr>();
4413+
auto nonisolatedAttr = decl->getAttrs().getAttribute<NonisolatedAttr>();
4414+
auto executionAttr = decl->getAttrs().getAttribute<ExecutionAttr>();
4415+
struct NameAndRange {
4416+
StringRef name;
4417+
SourceRange range;
4418+
4419+
NameAndRange(StringRef _name, SourceRange _range)
4420+
: name(_name), range(_range) {}
4421+
};
4422+
4423+
llvm::SmallVector<NameAndRange, 4> attributes;
4424+
4425+
attributes.push_back(NameAndRange(globalActorAttr.second->getName().str(),
4426+
globalActorAttr.first->getRangeWithAt()));
4427+
4428+
if (isolatedAttr) {
4429+
attributes.push_back(NameAndRange(isolatedAttr->getAttrName(),
4430+
isolatedAttr->getRangeWithAt()));
4431+
}
4432+
if (nonisolatedAttr) {
4433+
attributes.push_back(NameAndRange(nonisolatedAttr->getAttrName(),
4434+
nonisolatedAttr->getRangeWithAt()));
4435+
}
4436+
if (executionAttr) {
4437+
attributes.push_back(NameAndRange(executionAttr->getAttrName(),
4438+
executionAttr->getRangeWithAt()));
4439+
}
4440+
4441+
if (attributes.size() == 1)
4442+
return;
4443+
4444+
if (attributes.size() == 2) {
4445+
decl->diagnose(diag::actor_isolation_multiple_attr_2, decl,
4446+
attributes[0].name, attributes[1].name)
4447+
.highlight(attributes[0].range)
4448+
.highlight(attributes[1].range)
4449+
.warnUntilSwiftVersion(6)
4450+
.fixItRemove(attributes[1].range);
4451+
return;
4452+
}
4453+
4454+
if (attributes.size() == 3) {
4455+
decl->diagnose(diag::actor_isolation_multiple_attr_3, decl,
4456+
attributes[0].name, attributes[1].name, attributes[2].name)
4457+
.highlight(attributes[0].range)
4458+
.highlight(attributes[1].range)
4459+
.highlight(attributes[2].range)
4460+
.warnUntilSwiftVersion(6)
4461+
.fixItRemove(attributes[1].range)
4462+
.fixItRemove(attributes[2].range);
4463+
return;
4464+
}
4465+
4466+
assert(attributes.size() == 4);
4467+
decl->diagnose(diag::actor_isolation_multiple_attr_4, decl,
4468+
attributes[0].name, attributes[1].name, attributes[2].name,
4469+
attributes[3].name)
4470+
.highlight(attributes[0].range)
4471+
.highlight(attributes[1].range)
4472+
.highlight(attributes[2].range)
4473+
.highlight(attributes[3].range)
4474+
.warnUntilSwiftVersion(6)
4475+
.fixItRemove(attributes[1].range)
4476+
.fixItRemove(attributes[2].range)
4477+
.fixItRemove(attributes[3].range);
4478+
}
4479+
44214480
void AttributeChecker::visitCustomAttr(CustomAttr *attr) {
44224481
auto dc = D->getDeclContext();
44234482

@@ -4601,7 +4660,10 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) {
46014660
// retrieval request perform checking for us.
46024661
if (nominal->isGlobalActor()) {
46034662
diagnoseIsolatedDeinitInValueTypes(attr);
4604-
(void)D->getGlobalActorAttr();
4663+
if (auto g = D->getGlobalActorAttr()) {
4664+
checkGlobalActorAttr(D, *g);
4665+
}
4666+
46054667
if (auto value = dyn_cast<ValueDecl>(D)) {
46064668
(void)getActorIsolation(value);
46074669
} else {
@@ -8065,7 +8127,7 @@ class ClosureAttributeChecker
80658127
ctx.evaluator, GlobalActorAttributeRequest{closure}, std::nullopt);
80668128

80678129
if (globalActorAttr && globalActorAttr->first == attr) {
8068-
// if there is an `isolated` parameter, then this global-actor attribute
8130+
// If there is an `isolated` parameter, then this global-actor attribute
80698131
// is invalid.
80708132
for (auto param : *closure->getParameters()) {
80718133
if (param->isIsolated()) {

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 28 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4688,6 +4688,7 @@ getIsolationFromAttributes(const Decl *decl, bool shouldDiagnose = true,
46884688
auto isolatedAttr = decl->getAttrs().getAttribute<IsolatedAttr>();
46894689
auto nonisolatedAttr = decl->getAttrs().getAttribute<NonisolatedAttr>();
46904690
auto globalActorAttr = decl->getGlobalActorAttr();
4691+
auto concurrentExecutionAttr = decl->getAttrs().getAttribute<ExecutionAttr>();
46914692

46924693
// Remove implicit attributes if we only care about explicit ones.
46934694
if (onlyExplicit) {
@@ -4697,61 +4698,34 @@ getIsolationFromAttributes(const Decl *decl, bool shouldDiagnose = true,
46974698
isolatedAttr = nullptr;
46984699
if (globalActorAttr && globalActorAttr->first->isImplicit())
46994700
globalActorAttr = std::nullopt;
4701+
if (concurrentExecutionAttr && concurrentExecutionAttr->isImplicit())
4702+
concurrentExecutionAttr = nullptr;
47004703
}
47014704

4702-
unsigned numIsolationAttrs = (isolatedAttr ? 1 : 0) +
4703-
(nonisolatedAttr ? 1 : 0) +
4704-
(globalActorAttr ? 1 : 0);
4705+
unsigned numIsolationAttrs =
4706+
(isolatedAttr ? 1 : 0) + (nonisolatedAttr ? 1 : 0) +
4707+
(globalActorAttr ? 1 : 0) + (concurrentExecutionAttr ? 1 : 0);
47054708
if (numIsolationAttrs == 0) {
47064709
if (isa<DestructorDecl>(decl) && !decl->isImplicit()) {
47074710
return ActorIsolation::forNonisolated(false);
47084711
}
47094712
return std::nullopt;
47104713
}
47114714

4712-
// Only one such attribute is valid, but we only actually care of one of
4713-
// them is a global actor.
4714-
if (numIsolationAttrs > 1 && globalActorAttr && shouldDiagnose) {
4715-
struct NameAndRange {
4716-
StringRef name;
4717-
SourceRange range;
4718-
4719-
NameAndRange(StringRef _name, SourceRange _range)
4720-
: name(_name), range(_range) {}
4721-
};
4722-
4723-
llvm::SmallVector<NameAndRange, 3> attributes;
4724-
if (isolatedAttr) {
4725-
attributes.push_back(NameAndRange(isolatedAttr->getAttrName(),
4726-
isolatedAttr->getRangeWithAt()));
4727-
}
4728-
if (nonisolatedAttr) {
4729-
attributes.push_back(NameAndRange(nonisolatedAttr->getAttrName(),
4730-
nonisolatedAttr->getRangeWithAt()));
4731-
}
4732-
if (globalActorAttr) {
4733-
attributes.push_back(
4734-
NameAndRange(globalActorAttr->second->getName().str(),
4735-
globalActorAttr->first->getRangeWithAt()));
4736-
}
4737-
if (attributes.size() == 3) {
4738-
decl->diagnose(diag::actor_isolation_multiple_attr_3, decl,
4739-
attributes[0].name, attributes[1].name, attributes[2].name)
4740-
.highlight(attributes[0].range)
4741-
.highlight(attributes[1].range)
4742-
.highlight(attributes[2].range);
4743-
} else {
4744-
assert(attributes.size() == 2);
4745-
decl->diagnose(diag::actor_isolation_multiple_attr_2, decl,
4746-
attributes[0].name, attributes[1].name)
4747-
.highlight(attributes[0].range)
4748-
.highlight(attributes[1].range);
4749-
}
4750-
}
4751-
47524715
// If the declaration is explicitly marked 'nonisolated', report it as
47534716
// independent.
47544717
if (nonisolatedAttr) {
4718+
// If the nonisolated async inherits isolation from context is set, return
4719+
// caller isolation inheriting.
4720+
if (decl->getASTContext().LangOpts.hasFeature(
4721+
Feature::NonIsolatedAsyncInheritsIsolationFromContext)) {
4722+
if (auto *func = dyn_cast<AbstractFunctionDecl>(decl);
4723+
func && func->hasAsync() &&
4724+
func->getModuleContext() == decl->getASTContext().MainModule) {
4725+
return ActorIsolation::forCallerIsolationInheriting();
4726+
}
4727+
}
4728+
47554729
return ActorIsolation::forNonisolated(nonisolatedAttr->isUnsafe());
47564730
}
47574731

@@ -4842,6 +4816,17 @@ getIsolationFromAttributes(const Decl *decl, bool shouldDiagnose = true,
48424816
.withPreconcurrency(decl->preconcurrency() || isUnsafe);
48434817
}
48444818

4819+
// If the declaration is explicitly marked with 'execution', return the
4820+
// appropriate isolation.
4821+
if (concurrentExecutionAttr) {
4822+
switch (concurrentExecutionAttr->getBehavior()) {
4823+
case ExecutionKind::Concurrent:
4824+
return ActorIsolation::forNonisolated(false /*is unsafe*/);
4825+
case ExecutionKind::Caller:
4826+
return ActorIsolation::forCallerIsolationInheriting();
4827+
}
4828+
}
4829+
48454830
llvm_unreachable("Forgot about an attribute?");
48464831
}
48474832

@@ -5614,15 +5599,6 @@ InferredActorIsolation ActorIsolationRequest::evaluate(
56145599
checkClassGlobalActorIsolation(classDecl, *isolationFromAttr);
56155600
}
56165601

5617-
if (ctx.LangOpts.hasFeature(
5618-
Feature::NonIsolatedAsyncInheritsIsolationFromContext)) {
5619-
if (auto *func = dyn_cast<AbstractFunctionDecl>(value);
5620-
func && func->hasAsync() && isolationFromAttr->isNonisolated() &&
5621-
func->getModuleContext() == ctx.MainModule) {
5622-
return {ActorIsolation::forCallerIsolationInheriting(), {}};
5623-
}
5624-
}
5625-
56265602
return {*isolationFromAttr,
56275603
IsolationSource(/*source*/ nullptr, IsolationSource::Explicit)};
56285604
}

test/Concurrency/Runtime/nonisolated_inherits_isolation.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,17 @@ struct CustomActor {
3333
}
3434
}
3535

36+
@execution(caller)
37+
func executionCallerIsolation() async {
38+
checkIfOnMainQueue()
39+
}
40+
41+
// Expected to always crash
42+
@execution(concurrent)
43+
func executionConcurrentIsolation() async {
44+
checkIfOnMainQueue()
45+
}
46+
3647
let tests = TestSuite("NonIsolatedInheritsIsolation")
3748

3849
tests.test("checkIfOnMainQueue does not crash on the main queue") { @MainActor () -> () in
@@ -87,6 +98,25 @@ tests.test("Check if nonisolated inheriting nonisolated crashes") { () async ->
8798
sleep(5)
8899
}
89100

101+
tests.test("Check if execution concurrent isolation crashes (main actor)") { @MainActor () async -> () in
102+
expectCrashLater()
103+
await executionConcurrentIsolation()
104+
}
105+
106+
tests.test("Check if execution concurrent isolation crashes (custom actor)") { @CustomActor () async -> () in
107+
expectCrashLater()
108+
await executionConcurrentIsolation()
109+
}
110+
111+
tests.test("Check if execution concurrent isolation does not crash (main actor)") { @MainActor () async -> () in
112+
await executionCallerIsolation()
113+
}
114+
115+
tests.test("Check if execution concurrent isolation does crash (custom actor)") { @CustomActor () async -> () in
116+
expectCrashLater()
117+
await executionCallerIsolation()
118+
}
119+
90120
@MainActor func run() async {
91121
await runAllTestsAsync()
92122
}

test/Concurrency/attr_execution.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %target-swift-emit-silgen -enable-experimental-feature NonIsolatedAsyncInheritsIsolationFromContext %s | %FileCheck %s
2+
3+
// REQUIRES: asserts
4+
// REQUIRES: swift_feature_NonIsolatedAsyncInheritsIsolationFromContext
5+
6+
7+
// CHECK-LABEL: // concurrentTest()
8+
// CHECK: // Isolation: nonisolated
9+
// CHECK: sil hidden [ossa] @$s14attr_execution14concurrentTestyyYaF : $@convention(thin) @async () -> () {
10+
@execution(concurrent)
11+
func concurrentTest() async {}
12+
13+
// CHECK-LABEL: // callerTest()
14+
// CHECK: // Isolation: caller_isolation_inheriting
15+
// CHECK: sil hidden [ossa] @$s14attr_execution10callerTestyyYaF : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> () {
16+
@execution(caller)
17+
func callerTest() async {}

test/Concurrency/isolated_parameters.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ func isolatedClosures() {
341341
}
342342
}
343343

344+
// expected-warning@+3 {{global function 'allOfEm' has multiple actor-isolation attributes ('MainActor' and 'nonisolated')}}
344345
// expected-warning@+2 {{global function with 'isolated' parameter cannot be 'nonisolated'; this is an error in the Swift 6 language mode}}{{12-24=}}
345346
// expected-warning@+1 {{global function with 'isolated' parameter cannot have a global actor; this is an error in the Swift 6 language mode}}{{1-12=}}
346347
@MainActor nonisolated func allOfEm(_ a: isolated A) {

test/Concurrency/nonisolated_rules.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ class KlassA {
154154

155155
@MainActor
156156
nonisolated struct Conflict {}
157-
// expected-error@-1 {{struct 'Conflict' has multiple actor-isolation attributes ('nonisolated' and 'MainActor')}}
157+
// expected-error@-1 {{struct 'Conflict' has multiple actor-isolation attributes ('MainActor' and 'nonisolated')}}
158158

159159
struct B: Sendable {
160160
// expected-error@+1 {{'nonisolated' can not be applied to variable with non-'Sendable' type 'NonSendable}}

0 commit comments

Comments
 (0)