Skip to content

Commit d79fcb6

Browse files
committed
[caller-isolation] Teach Sema how to handle isolation of explicit @execution({concurrent,caller}).
1 parent 6a9afa5 commit d79fcb6

File tree

6 files changed

+83
-3
lines changed

6 files changed

+83
-3
lines changed

include/swift/AST/Decl.h

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

8096+
std::optional<ExecutionKind> getExecutionBehavior() const;
8097+
80968098
/// Whether the declaration is later overridden in the module
80978099
///
80988100
/// Overrides are resolved during type checking; only query this field after

lib/AST/Decl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8471,6 +8471,14 @@ void VarDecl::emitLetToVarNoteIfSimple(DeclContext *UseDC) const {
84718471
}
84728472
}
84738473

8474+
std::optional<ExecutionKind>
8475+
AbstractFunctionDecl::getExecutionBehavior() const {
8476+
auto *attr = getAttrs().getAttribute<ExecutionAttr>();
8477+
if (!attr)
8478+
return {};
8479+
return attr->getBehavior();
8480+
}
8481+
84748482
clang::PointerAuthQualifier VarDecl::getPointerAuthQualifier() const {
84758483
if (auto *clangDecl = getClangDecl()) {
84768484
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/TypeCheckConcurrency.cpp

Lines changed: 17 additions & 3 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,11 +4698,13 @@ 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);
@@ -4815,6 +4818,17 @@ getIsolationFromAttributes(const Decl *decl, bool shouldDiagnose = true,
48154818
.withPreconcurrency(decl->preconcurrency() || isUnsafe);
48164819
}
48174820

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

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

0 commit comments

Comments
 (0)