Skip to content

Commit c0696b1

Browse files
committed
[Concurrency] Diagnose default values whose isolation differs from the corresponding
stored property. (cherry picked from commit e9750bc)
1 parent c479c10 commit c0696b1

File tree

7 files changed

+80
-41
lines changed

7 files changed

+80
-41
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5201,7 +5201,7 @@ ERROR(distributed_actor_isolated_non_self_reference,none,
52015201
"non-isolated context",
52025202
(const ValueDecl *))
52035203
ERROR(isolated_default_argument_context,none,
5204-
"%0 default argument in a %1 context",
5204+
"%0 default value in a %1 context",
52055205
(ActorIsolation, ActorIsolation))
52065206
ERROR(conflicting_default_argument_isolation,none,
52075207
"default argument cannot be both %0 and %1",

lib/AST/Decl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10289,6 +10289,7 @@ ActorIsolation swift::getActorIsolationOfContext(
1028910289
DeclContext *dc,
1029010290
llvm::function_ref<ActorIsolation(AbstractClosureExpr *)>
1029110291
getClosureActorIsolation) {
10292+
auto &ctx = dc->getASTContext();
1029210293
auto dcToUse = dc;
1029310294
// Defer bodies share actor isolation of their enclosing context.
1029410295
if (auto FD = dyn_cast<FuncDecl>(dcToUse)) {
@@ -10309,6 +10310,13 @@ ActorIsolation swift::getActorIsolationOfContext(
1030910310
// actor isolation as the field itself. That default expression may only
1031010311
// be used from inits that meet the required isolation.
1031110312
if (auto *var = dcToUse->getNonLocalVarDecl()) {
10313+
// If IsolatedDefaultValues are enabled, treat this context as having
10314+
// unspecified isolation. We'll compute the required isolation for
10315+
// the initializer and validate that it matches the isolation of the
10316+
// var itself in the DefaultInitializerIsolation request.
10317+
if (ctx.LangOpts.hasFeature(Feature::IsolatedDefaultValues))
10318+
return ActorIsolation::forUnspecified();
10319+
1031210320
return getActorIsolation(var);
1031310321
}
1031410322

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3188,7 +3188,10 @@ namespace {
31883188
if (!unsatisfiedIsolation)
31893189
return false;
31903190

3191-
if (refineRequiredIsolation(*unsatisfiedIsolation))
3191+
bool onlyArgsCrossIsolation = callOptions.contains(
3192+
ActorReferenceResult::Flags::OnlyArgsCrossIsolation);
3193+
if (!onlyArgsCrossIsolation &&
3194+
refineRequiredIsolation(*unsatisfiedIsolation))
31923195
return false;
31933196

31943197
// At this point, we know a jump is made to the callee that yields
@@ -4808,6 +4811,7 @@ DefaultInitializerIsolation::evaluate(Evaluator &evaluator,
48084811

48094812
Initializer *dc = nullptr;
48104813
Expr *initExpr = nullptr;
4814+
ActorIsolation enclosingIsolation;
48114815

48124816
if (auto *pbd = var->getParentPatternBinding()) {
48134817
if (!var->isParentInitialized())
@@ -4816,6 +4820,7 @@ DefaultInitializerIsolation::evaluate(Evaluator &evaluator,
48164820
auto i = pbd->getPatternEntryIndexForVarDecl(var);
48174821
dc = cast<Initializer>(pbd->getInitContext(i));
48184822
initExpr = var->getParentInitializer();
4823+
enclosingIsolation = getActorIsolation(var);
48194824
} else if (auto *param = dyn_cast<ParamDecl>(var)) {
48204825
// If this parameter corresponds to a stored property for a
48214826
// memberwise initializer, the default argument is the default
@@ -4833,13 +4838,27 @@ DefaultInitializerIsolation::evaluate(Evaluator &evaluator,
48334838

48344839
dc = param->getDefaultArgumentInitContext();
48354840
initExpr = param->getTypeCheckedDefaultExpr();
4841+
enclosingIsolation =
4842+
getActorIsolationOfContext(param->getDeclContext());
48364843
}
48374844

48384845
if (!dc || !initExpr)
48394846
return ActorIsolation::forUnspecified();
48404847

4848+
// If the default argument has isolation, it must match the
4849+
// isolation of the decl context.
48414850
ActorIsolationChecker checker(dc);
4842-
return checker.computeRequiredIsolation(initExpr);
4851+
auto requiredIsolation = checker.computeRequiredIsolation(initExpr);
4852+
if (requiredIsolation.isActorIsolated()) {
4853+
if (enclosingIsolation != requiredIsolation) {
4854+
var->diagnose(
4855+
diag::isolated_default_argument_context,
4856+
requiredIsolation, enclosingIsolation);
4857+
return ActorIsolation::forUnspecified();
4858+
}
4859+
}
4860+
4861+
return requiredIsolation;
48434862
}
48444863

48454864
void swift::checkOverrideActorIsolation(ValueDecl *value) {
@@ -5955,8 +5974,8 @@ bool swift::isAccessibleAcrossActors(
59555974
}
59565975

59575976
ActorReferenceResult ActorReferenceResult::forSameConcurrencyDomain(
5958-
ActorIsolation isolation) {
5959-
return ActorReferenceResult{SameConcurrencyDomain, llvm::None, isolation};
5977+
ActorIsolation isolation, Options options) {
5978+
return ActorReferenceResult{SameConcurrencyDomain, options, isolation};
59605979
}
59615980

59625981
ActorReferenceResult ActorReferenceResult::forEntersActor(
@@ -5965,8 +5984,8 @@ ActorReferenceResult ActorReferenceResult::forEntersActor(
59655984
}
59665985

59675986
ActorReferenceResult ActorReferenceResult::forExitsActorToNonisolated(
5968-
ActorIsolation isolation) {
5969-
return ActorReferenceResult{ExitsActorToNonisolated, llvm::None, isolation};
5987+
ActorIsolation isolation, Options options) {
5988+
return ActorReferenceResult{ExitsActorToNonisolated, options, isolation};
59705989
}
59715990

59725991
// Determine if two actor isolation contexts are considered to be equivalent.
@@ -6002,10 +6021,24 @@ ActorReferenceResult ActorReferenceResult::forReference(
60026021
declIsolation = declIsolation.subst(declRef.getSubstitutions());
60036022
}
60046023

6024+
// Determine what adjustments we need to perform for cross-actor
6025+
// references.
6026+
Options options = llvm::None;
6027+
6028+
// FIXME: Actor constructors are modeled as isolated to the actor
6029+
// so that Sendable checking is applied to their arguments, but the
6030+
// call itself does not hop to another executor.
6031+
if (auto ctor = dyn_cast<ConstructorDecl>(declRef.getDecl())) {
6032+
if (auto nominal = ctor->getDeclContext()->getSelfNominalTypeDecl()) {
6033+
if (nominal->isAnyActor())
6034+
options |= Flags::OnlyArgsCrossIsolation;
6035+
}
6036+
}
6037+
60056038
// If the entity we are referencing is not a value, we're in the same
60066039
// concurrency domain.
60076040
if (isNonValueReference(declRef.getDecl()))
6008-
return forSameConcurrencyDomain(declIsolation);
6041+
return forSameConcurrencyDomain(declIsolation, options);
60096042

60106043
// Compute the isolation of the context, if not provided.
60116044
ActorIsolation contextIsolation = ActorIsolation::forUnspecified();
@@ -6024,11 +6057,11 @@ ActorReferenceResult ActorReferenceResult::forReference(
60246057
if (isAsyncDecl(declRef) && contextIsolation.isActorIsolated() &&
60256058
!declRef.getDecl()->getAttrs()
60266059
.hasAttribute<UnsafeInheritExecutorAttr>())
6027-
return forExitsActorToNonisolated(contextIsolation);
6060+
return forExitsActorToNonisolated(contextIsolation, options);
60286061

60296062
// Otherwise, we stay in the same concurrency domain, whether on an actor
60306063
// or in a task.
6031-
return forSameConcurrencyDomain(declIsolation);
6064+
return forSameConcurrencyDomain(declIsolation, options);
60326065
}
60336066

60346067
// The declaration we are accessing is actor-isolated. First, check whether
@@ -6037,11 +6070,11 @@ ActorReferenceResult ActorReferenceResult::forReference(
60376070
declIsolation.getActorInstanceParameter() == 0) {
60386071
// If this instance is isolated, we're in the same concurrency domain.
60396072
if (actorInstance->isIsolated())
6040-
return forSameConcurrencyDomain(declIsolation);
6073+
return forSameConcurrencyDomain(declIsolation, options);
60416074
} else if (equivalentIsolationContexts(declIsolation, contextIsolation)) {
60426075
// The context isolation matches, so we are in the same concurrency
60436076
// domain.
6044-
return forSameConcurrencyDomain(declIsolation);
6077+
return forSameConcurrencyDomain(declIsolation, options);
60456078
}
60466079

60476080
// Initializing an actor isolated stored property with a value effectively
@@ -6061,7 +6094,8 @@ ActorReferenceResult ActorReferenceResult::forReference(
60616094
// Treat the decl isolation as 'preconcurrency' to downgrade violations
60626095
// to warnings, because violating Sendable here is accepted by the
60636096
// Swift 5.9 compiler.
6064-
return forEntersActor(declIsolation, Flags::Preconcurrency);
6097+
options |= Flags::Preconcurrency;
6098+
return forEntersActor(declIsolation, options);
60656099
}
60666100
}
60676101
}
@@ -6071,7 +6105,7 @@ ActorReferenceResult ActorReferenceResult::forReference(
60716105
if (actorInstance &&
60726106
checkedByFlowIsolation(
60736107
fromDC, *actorInstance, declRef.getDecl(), declRefLoc, useKind))
6074-
return forSameConcurrencyDomain(declIsolation);
6108+
return forSameConcurrencyDomain(declIsolation, options);
60756109

60766110
// If we are delegating to another initializer, treat them as being in the
60776111
// same concurrency domain.
@@ -6080,7 +6114,7 @@ ActorReferenceResult ActorReferenceResult::forReference(
60806114
if (actorInstance && actorInstance->isSelf() &&
60816115
isa<ConstructorDecl>(declRef.getDecl()) &&
60826116
isa<ConstructorDecl>(fromDC))
6083-
return forSameConcurrencyDomain(declIsolation);
6117+
return forSameConcurrencyDomain(declIsolation, options);
60846118

60856119
// If there is an instance that corresponds to 'self',
60866120
// we are in a constructor or destructor, and we have a stored property of
@@ -6090,18 +6124,14 @@ ActorReferenceResult ActorReferenceResult::forReference(
60906124
isNonInheritedStorage(declRef.getDecl(), fromDC) &&
60916125
declIsolation.isGlobalActor() &&
60926126
(isa<ConstructorDecl>(fromDC) || isa<DestructorDecl>(fromDC)))
6093-
return forSameConcurrencyDomain(declIsolation);
6094-
6095-
// Determine what adjustments we need to perform for cross-actor
6096-
// references.
6097-
Options options = llvm::None;
6127+
return forSameConcurrencyDomain(declIsolation, options);
60986128

60996129
// At this point, we are accessing the target from outside the actor.
61006130
// First, check whether it is something that can be accessed directly,
61016131
// without any kind of promotion.
61026132
if (isAccessibleAcrossActors(
61036133
declRef.getDecl(), declIsolation, fromDC, options, actorInstance))
6104-
return forEntersActor(declIsolation, llvm::None);
6134+
return forEntersActor(declIsolation, options);
61056135

61066136
// This is a cross-actor reference.
61076137

lib/Sema/TypeCheckConcurrency.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ struct ActorReferenceResult {
202202

203203
/// The declaration is being accessed from a @preconcurrency context.
204204
Preconcurrency = 1 << 3,
205+
206+
/// Only arguments cross an isolation boundary, e.g. because they
207+
/// escape into an actor in a nonisolated actor initializer.
208+
OnlyArgsCrossIsolation = 1 << 4,
205209
};
206210

207211
using Options = OptionSet<Flags>;
@@ -212,13 +216,13 @@ struct ActorReferenceResult {
212216

213217
private:
214218
static ActorReferenceResult forSameConcurrencyDomain(
215-
ActorIsolation isolation);
219+
ActorIsolation isolation, Options options = llvm::None);
216220

217221
static ActorReferenceResult forEntersActor(
218222
ActorIsolation isolation, Options options);
219223

220224
static ActorReferenceResult forExitsActorToNonisolated(
221-
ActorIsolation isolation);
225+
ActorIsolation isolation, Options options = llvm::None);
222226

223227
public:
224228
/// Determine what happens when referencing the given declaration from the

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,15 +1088,7 @@ static void checkDefaultArguments(ParameterList *params) {
10881088

10891089
// If the default argument has isolation, it must match the
10901090
// isolation of the decl context.
1091-
auto defaultArgIsolation = param->getInitializerIsolation();
1092-
if (defaultArgIsolation.isActorIsolated()) {
1093-
auto *dc = param->getDeclContext();
1094-
auto enclosingIsolation = getActorIsolationOfContext(dc);
1095-
if (enclosingIsolation != defaultArgIsolation) {
1096-
param->diagnose(diag::isolated_default_argument_context,
1097-
defaultArgIsolation, enclosingIsolation);
1098-
}
1099-
}
1091+
(void)param->getInitializerIsolation();
11001092

11011093
if (!ifacety->hasPlaceholder()) {
11021094
continue;

test/Concurrency/isolated_default_arguments.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ func requiresMainActor() -> Int { 0 }
2020
@SomeGlobalActor
2121
func requiresSomeGlobalActor() -> Int { 0 }
2222

23-
// expected-error@+1 {{main actor-isolated default argument in a nonisolated context}}
23+
// expected-error@+1 {{main actor-isolated default value in a nonisolated context}}
2424
func mainActorDefaultArgInvalid(value: Int = requiresMainActor()) {}
2525

2626
func mainActorClosureInvalid(
27-
closure: () -> Int = { // expected-error {{main actor-isolated default argument in a nonisolated context}}
27+
closure: () -> Int = { // expected-error {{main actor-isolated default value in a nonisolated context}}
2828
requiresMainActor()
2929
}
3030
) {}
@@ -41,7 +41,7 @@ func mainActorDefaultArg(value: Int = requiresMainActor()) {}
4141
) {}
4242

4343
func mainActorClosureCall(
44-
closure: Int = { // expected-error {{main actor-isolated default argument in a nonisolated context}}
44+
closure: Int = { // expected-error {{main actor-isolated default value in a nonisolated context}}
4545
requiresMainActor()
4646
}()
4747
) {}
@@ -155,6 +155,11 @@ struct S3 {
155155
var (x, y, z) = (requiresMainActor(), requiresSomeGlobalActor(), 10)
156156
}
157157

158+
struct S4 {
159+
// expected-error@+1 {{main actor-isolated default value in a nonisolated context}}
160+
var x: Int = requiresMainActor()
161+
}
162+
158163
@MainActor
159164
func initializeFromMainActor() {
160165
_ = S1(required: 10)

test/Concurrency/isolated_default_property_inits.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ func requiresMainActor() -> Int { 0 }
1919
@SomeGlobalActor
2020
func requiresSomeGlobalActor() -> Int { 0 }
2121

22-
struct S1 {
22+
class C1 {
2323
// expected-note@+1 2 {{'self.x' not initialized}}
24-
var x = requiresMainActor()
24+
@MainActor var x = requiresMainActor()
2525
// expected-note@+1 2 {{'self.y' not initialized}}
26-
var y = requiresSomeGlobalActor()
26+
@SomeGlobalActor var y = requiresSomeGlobalActor()
2727
var z = 10
2828

2929
// expected-error@+1 {{return from initializer without initializing all stored properties}}
@@ -36,9 +36,9 @@ struct S1 {
3636
@SomeGlobalActor init(c: Int) {}
3737
}
3838

39-
struct S2 {
40-
var x = requiresMainActor()
41-
var y = requiresSomeGlobalActor()
39+
class C2 {
40+
@MainActor var x = requiresMainActor()
41+
@SomeGlobalActor var y = requiresSomeGlobalActor()
4242
var z = 10
4343

4444
nonisolated init(x: Int, y: Int) {

0 commit comments

Comments
 (0)