Skip to content

Commit 32e5e9d

Browse files
committed
[Concurrency] Handle nested decl contexts while computing the required isolation
for a default argument, and improve diagnostics for conflicting isolation.
1 parent fa56fb6 commit 32e5e9d

File tree

3 files changed

+78
-27
lines changed

3 files changed

+78
-27
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5194,6 +5194,9 @@ ERROR(isolated_default_argument,none,
51945194
"%0 default argument cannot be synchronously evaluated from a "
51955195
"%1 context",
51965196
(ActorIsolation, ActorIsolation))
5197+
ERROR(conflicting_default_argument_isolation,none,
5198+
"default argument cannot be both %0 and %1",
5199+
(ActorIsolation, ActorIsolation))
51975200
ERROR(distributed_actor_needs_explicit_distributed_import,none,
51985201
"'Distributed' module not imported, required for 'distributed actor'",
51995202
())

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1939,11 +1939,11 @@ namespace {
19391939
llvm::function_ref<ActorIsolation(AbstractClosureExpr *)>
19401940
getClosureActorIsolation;
19411941

1942-
bool shouldRefineIsolation = false;
1942+
SourceLoc requiredIsolationLoc;
19431943

19441944
/// Used under the mode to compute required actor isolation for
19451945
/// an expression or function.
1946-
ActorIsolation requiredIsolation = ActorIsolation::forNonisolated();
1946+
llvm::SmallDenseMap<const DeclContext *, ActorIsolation> requiredIsolation;
19471947

19481948
/// Keeps track of the capture context of variables that have been
19491949
/// explicitly captured in closures.
@@ -2130,31 +2130,60 @@ namespace {
21302130
}
21312131

21322132
bool refineRequiredIsolation(ActorIsolation refinedIsolation) {
2133-
if (!shouldRefineIsolation)
2133+
if (requiredIsolationLoc.isInvalid())
21342134
return false;
21352135

2136-
if (auto *closure = dyn_cast<AbstractClosureExpr>(getDeclContext())) {
2137-
// We cannot infer a more specific actor isolation for a @Sendable
2138-
// closure. It is an error to cast away actor isolation from a function
2139-
// type, but this is okay for non-Sendable closures because they cannot
2140-
// leave the isolation domain they're created in anyway.
2141-
if (closure->isSendable())
2142-
return false;
2136+
auto infersIsolationFromContext =
2137+
[](const DeclContext *dc) -> bool {
2138+
// Isolation for declarations is based solely on explicit
2139+
// annotations; only infer isolation for initializer expressions
2140+
// and closures.
2141+
if (dc->getAsDecl())
2142+
return false;
2143+
2144+
if (auto *closure = dyn_cast<AbstractClosureExpr>(dc)) {
2145+
// We cannot infer a more specific actor isolation for a Sendable
2146+
// closure. It is an error to cast away actor isolation from a
2147+
// function type, but this is okay for non-Sendable closures
2148+
// because they cannot leave the isolation domain they're created
2149+
// in anyway.
2150+
if (closure->isSendable())
2151+
return false;
21432152

2144-
if (closure->getActorIsolation().isActorIsolated())
2153+
if (closure->getActorIsolation().isActorIsolated())
2154+
return false;
2155+
}
2156+
2157+
return true;
2158+
};
2159+
2160+
// For the call to require the given actor isolation, every DeclContext
2161+
// in the current context stack must require the same isolation. If
2162+
// along the way to the innermost context, we find a DeclContext that
2163+
// has a different isolation (e.g. it's a local function that does not
2164+
// recieve isolation from its decl context), then the expression cannot
2165+
// require a different isolation.
2166+
for (auto *dc : contextStack) {
2167+
if (!infersIsolationFromContext(dc))
21452168
return false;
2146-
}
21472169

2148-
// To refine the required isolation, the existing requirement
2149-
// must either be 'nonisolated' or exactly the same as the
2150-
// new refinement.
2151-
if (requiredIsolation == ActorIsolation::Nonisolated ||
2152-
requiredIsolation == refinedIsolation) {
2153-
requiredIsolation = refinedIsolation;
2154-
return true;
2170+
// To refine the required isolation, the existing requirement
2171+
// must either be 'nonisolated' or exactly the same as the
2172+
// new refinement.
2173+
auto isolation = requiredIsolation.find(dc);
2174+
if (isolation == requiredIsolation.end() ||
2175+
isolation->second == ActorIsolation::Nonisolated) {
2176+
requiredIsolation[dc] = refinedIsolation;
2177+
} else if (isolation->second != refinedIsolation) {
2178+
dc->getASTContext().Diags.diagnose(
2179+
requiredIsolationLoc,
2180+
diag::conflicting_default_argument_isolation,
2181+
isolation->second, refinedIsolation);
2182+
return true;
2183+
}
21552184
}
21562185

2157-
return false;
2186+
return true;
21582187
}
21592188

21602189
void checkDefaultArgument(DefaultArgumentExpr *expr) {
@@ -2248,11 +2277,12 @@ namespace {
22482277
ActorIsolation computeRequiredIsolation(Expr *expr) {
22492278
auto &ctx = getDeclContext()->getASTContext();
22502279

2251-
shouldRefineIsolation =
2252-
ctx.LangOpts.hasFeature(Feature::IsolatedDefaultArguments);
2280+
if (ctx.LangOpts.hasFeature(Feature::IsolatedDefaultArguments))
2281+
requiredIsolationLoc = expr->getLoc();
2282+
22532283
expr->walk(*this);
2254-
shouldRefineIsolation = false;
2255-
return requiredIsolation;
2284+
requiredIsolationLoc = SourceLoc();
2285+
return requiredIsolation[getDeclContext()];
22562286
}
22572287

22582288
/// Searches the applyStack from back to front for the inner-most CallExpr

test/Concurrency/isolated_default_arguments.swift

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ actor SomeGlobalActor {
1313
static let shared = SomeGlobalActor()
1414
}
1515

16+
// expected-note@+2 2 {{calls to global function 'requiresMainActor()' from outside of its actor context are implicitly asynchronous}}
1617
@MainActor
1718
func requiresMainActor() -> Int { 0 }
1819

19-
// expected-note@+2 2 {{calls to global function 'requiresSomeGlobalActor()' from outside of its actor context are implicitly asynchronous}}
2020
@SomeGlobalActor
2121
func requiresSomeGlobalActor() -> Int { 0 }
2222

@@ -77,7 +77,7 @@ func nonisolatedAsyncCaller() async {
7777
}
7878

7979
func conflictingIsolationDefaultArg(
80-
// expected-error@+1 {{call to global actor 'SomeGlobalActor'-isolated global function 'requiresSomeGlobalActor()' in a synchronous nonisolated context}}
80+
// expected-error@+1 {{default argument cannot be both main actor-isolated and global actor 'SomeGlobalActor'-isolated}}
8181
value: (Int, Int) = (requiresMainActor(), requiresSomeGlobalActor())
8282
) {}
8383

@@ -100,9 +100,27 @@ func closureIsolationMismatch(
100100
) {}
101101

102102
func conflictingClosureIsolation(
103+
// expected-error@+1 {{default argument cannot be both main actor-isolated and global actor 'SomeGlobalActor'-isolated}}
103104
closure: () -> Void = {
104105
_ = requiresMainActor()
105-
// expected-error@+1 {{call to global actor 'SomeGlobalActor'-isolated global function 'requiresSomeGlobalActor()' in a synchronous nonisolated context}}
106106
_ = requiresSomeGlobalActor()
107107
}
108108
) {}
109+
110+
func isolationInLocalFunction(
111+
closure: () -> Void = {
112+
nonisolated func local() -> Int {
113+
// expected-error@+1 {{call to main actor-isolated global function 'requiresMainActor()' in a synchronous nonisolated context}}
114+
requiresMainActor()
115+
}
116+
}
117+
) {}
118+
119+
actor A {}
120+
121+
func closureWithIsolatedParam(
122+
closure: (isolated A) -> Void = { _ in
123+
// expected-error@+1 {{call to main actor-isolated global function 'requiresMainActor()' in a synchronous actor-isolated context}}
124+
_ = requiresMainActor()
125+
}
126+
) {}

0 commit comments

Comments
 (0)