Skip to content

Commit e9750bc

Browse files
committed
[Concurrency] Diagnose default values whose isolation differs from the corresponding
stored property.
1 parent 6b35328 commit e9750bc

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
@@ -5346,7 +5346,7 @@ ERROR(distributed_actor_isolated_non_self_reference,none,
53465346
"non-isolated context",
53475347
(const ValueDecl *))
53485348
ERROR(isolated_default_argument_context,none,
5349-
"%0 default argument in a %1 context",
5349+
"%0 default value in a %1 context",
53505350
(ActorIsolation, ActorIsolation))
53515351
ERROR(conflicting_default_argument_isolation,none,
53525352
"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
@@ -10613,6 +10613,7 @@ ActorIsolation swift::getActorIsolationOfContext(
1061310613
DeclContext *dc,
1061410614
llvm::function_ref<ActorIsolation(AbstractClosureExpr *)>
1061510615
getClosureActorIsolation) {
10616+
auto &ctx = dc->getASTContext();
1061610617
auto dcToUse = dc;
1061710618
// Defer bodies share actor isolation of their enclosing context.
1061810619
if (auto FD = dyn_cast<FuncDecl>(dcToUse)) {
@@ -10633,6 +10634,13 @@ ActorIsolation swift::getActorIsolationOfContext(
1063310634
// actor isolation as the field itself. That default expression may only
1063410635
// be used from inits that meet the required isolation.
1063510636
if (auto *var = dcToUse->getNonLocalVarDecl()) {
10637+
// If IsolatedDefaultValues are enabled, treat this context as having
10638+
// unspecified isolation. We'll compute the required isolation for
10639+
// the initializer and validate that it matches the isolation of the
10640+
// var itself in the DefaultInitializerIsolation request.
10641+
if (ctx.LangOpts.hasFeature(Feature::IsolatedDefaultValues))
10642+
return ActorIsolation::forUnspecified();
10643+
1063610644
return getActorIsolation(var);
1063710645
}
1063810646

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3143,7 +3143,10 @@ namespace {
31433143
if (!unsatisfiedIsolation)
31443144
return false;
31453145

3146-
if (refineRequiredIsolation(*unsatisfiedIsolation))
3146+
bool onlyArgsCrossIsolation = callOptions.contains(
3147+
ActorReferenceResult::Flags::OnlyArgsCrossIsolation);
3148+
if (!onlyArgsCrossIsolation &&
3149+
refineRequiredIsolation(*unsatisfiedIsolation))
31473150
return false;
31483151

31493152
// At this point, we know a jump is made to the callee that yields
@@ -4757,6 +4760,7 @@ DefaultInitializerIsolation::evaluate(Evaluator &evaluator,
47574760

47584761
Initializer *dc = nullptr;
47594762
Expr *initExpr = nullptr;
4763+
ActorIsolation enclosingIsolation;
47604764

47614765
if (auto *pbd = var->getParentPatternBinding()) {
47624766
if (!var->isParentInitialized())
@@ -4765,6 +4769,7 @@ DefaultInitializerIsolation::evaluate(Evaluator &evaluator,
47654769
auto i = pbd->getPatternEntryIndexForVarDecl(var);
47664770
dc = cast<Initializer>(pbd->getInitContext(i));
47674771
initExpr = var->getParentInitializer();
4772+
enclosingIsolation = getActorIsolation(var);
47684773
} else if (auto *param = dyn_cast<ParamDecl>(var)) {
47694774
// If this parameter corresponds to a stored property for a
47704775
// memberwise initializer, the default argument is the default
@@ -4782,13 +4787,27 @@ DefaultInitializerIsolation::evaluate(Evaluator &evaluator,
47824787

47834788
dc = param->getDefaultArgumentInitContext();
47844789
initExpr = param->getTypeCheckedDefaultExpr();
4790+
enclosingIsolation =
4791+
getActorIsolationOfContext(param->getDeclContext());
47854792
}
47864793

47874794
if (!dc || !initExpr)
47884795
return ActorIsolation::forUnspecified();
47894796

4797+
// If the default argument has isolation, it must match the
4798+
// isolation of the decl context.
47904799
ActorIsolationChecker checker(dc);
4791-
return checker.computeRequiredIsolation(initExpr);
4800+
auto requiredIsolation = checker.computeRequiredIsolation(initExpr);
4801+
if (requiredIsolation.isActorIsolated()) {
4802+
if (enclosingIsolation != requiredIsolation) {
4803+
var->diagnose(
4804+
diag::isolated_default_argument_context,
4805+
requiredIsolation, enclosingIsolation);
4806+
return ActorIsolation::forUnspecified();
4807+
}
4808+
}
4809+
4810+
return requiredIsolation;
47924811
}
47934812

47944813
void swift::checkOverrideActorIsolation(ValueDecl *value) {
@@ -5859,8 +5878,8 @@ bool swift::isAccessibleAcrossActors(
58595878
}
58605879

58615880
ActorReferenceResult ActorReferenceResult::forSameConcurrencyDomain(
5862-
ActorIsolation isolation) {
5863-
return ActorReferenceResult{SameConcurrencyDomain, llvm::None, isolation};
5881+
ActorIsolation isolation, Options options) {
5882+
return ActorReferenceResult{SameConcurrencyDomain, options, isolation};
58645883
}
58655884

58665885
ActorReferenceResult ActorReferenceResult::forEntersActor(
@@ -5869,8 +5888,8 @@ ActorReferenceResult ActorReferenceResult::forEntersActor(
58695888
}
58705889

58715890
ActorReferenceResult ActorReferenceResult::forExitsActorToNonisolated(
5872-
ActorIsolation isolation) {
5873-
return ActorReferenceResult{ExitsActorToNonisolated, llvm::None, isolation};
5891+
ActorIsolation isolation, Options options) {
5892+
return ActorReferenceResult{ExitsActorToNonisolated, options, isolation};
58745893
}
58755894

58765895
// Determine if two actor isolation contexts are considered to be equivalent.
@@ -5906,10 +5925,24 @@ ActorReferenceResult ActorReferenceResult::forReference(
59065925
declIsolation = declIsolation.subst(declRef.getSubstitutions());
59075926
}
59085927

5928+
// Determine what adjustments we need to perform for cross-actor
5929+
// references.
5930+
Options options = llvm::None;
5931+
5932+
// FIXME: Actor constructors are modeled as isolated to the actor
5933+
// so that Sendable checking is applied to their arguments, but the
5934+
// call itself does not hop to another executor.
5935+
if (auto ctor = dyn_cast<ConstructorDecl>(declRef.getDecl())) {
5936+
if (auto nominal = ctor->getDeclContext()->getSelfNominalTypeDecl()) {
5937+
if (nominal->isAnyActor())
5938+
options |= Flags::OnlyArgsCrossIsolation;
5939+
}
5940+
}
5941+
59095942
// If the entity we are referencing is not a value, we're in the same
59105943
// concurrency domain.
59115944
if (isNonValueReference(declRef.getDecl()))
5912-
return forSameConcurrencyDomain(declIsolation);
5945+
return forSameConcurrencyDomain(declIsolation, options);
59135946

59145947
// Compute the isolation of the context, if not provided.
59155948
ActorIsolation contextIsolation = ActorIsolation::forUnspecified();
@@ -5928,11 +5961,11 @@ ActorReferenceResult ActorReferenceResult::forReference(
59285961
if (isAsyncDecl(declRef) && contextIsolation.isActorIsolated() &&
59295962
!declRef.getDecl()->getAttrs()
59305963
.hasAttribute<UnsafeInheritExecutorAttr>())
5931-
return forExitsActorToNonisolated(contextIsolation);
5964+
return forExitsActorToNonisolated(contextIsolation, options);
59325965

59335966
// Otherwise, we stay in the same concurrency domain, whether on an actor
59345967
// or in a task.
5935-
return forSameConcurrencyDomain(declIsolation);
5968+
return forSameConcurrencyDomain(declIsolation, options);
59365969
}
59375970

59385971
// The declaration we are accessing is actor-isolated. First, check whether
@@ -5941,11 +5974,11 @@ ActorReferenceResult ActorReferenceResult::forReference(
59415974
declIsolation.getActorInstanceParameter() == 0) {
59425975
// If this instance is isolated, we're in the same concurrency domain.
59435976
if (actorInstance->isIsolated())
5944-
return forSameConcurrencyDomain(declIsolation);
5977+
return forSameConcurrencyDomain(declIsolation, options);
59455978
} else if (equivalentIsolationContexts(declIsolation, contextIsolation)) {
59465979
// The context isolation matches, so we are in the same concurrency
59475980
// domain.
5948-
return forSameConcurrencyDomain(declIsolation);
5981+
return forSameConcurrencyDomain(declIsolation, options);
59495982
}
59505983

59515984
// Initializing an actor isolated stored property with a value effectively
@@ -5965,7 +5998,8 @@ ActorReferenceResult ActorReferenceResult::forReference(
59655998
// Treat the decl isolation as 'preconcurrency' to downgrade violations
59665999
// to warnings, because violating Sendable here is accepted by the
59676000
// Swift 5.9 compiler.
5968-
return forEntersActor(declIsolation, Flags::Preconcurrency);
6001+
options |= Flags::Preconcurrency;
6002+
return forEntersActor(declIsolation, options);
59696003
}
59706004
}
59716005
}
@@ -5975,7 +6009,7 @@ ActorReferenceResult ActorReferenceResult::forReference(
59756009
if (actorInstance &&
59766010
checkedByFlowIsolation(
59776011
fromDC, *actorInstance, declRef.getDecl(), declRefLoc, useKind))
5978-
return forSameConcurrencyDomain(declIsolation);
6012+
return forSameConcurrencyDomain(declIsolation, options);
59796013

59806014
// If we are delegating to another initializer, treat them as being in the
59816015
// same concurrency domain.
@@ -5984,7 +6018,7 @@ ActorReferenceResult ActorReferenceResult::forReference(
59846018
if (actorInstance && actorInstance->isSelf() &&
59856019
isa<ConstructorDecl>(declRef.getDecl()) &&
59866020
isa<ConstructorDecl>(fromDC))
5987-
return forSameConcurrencyDomain(declIsolation);
6021+
return forSameConcurrencyDomain(declIsolation, options);
59886022

59896023
// If there is an instance that corresponds to 'self',
59906024
// we are in a constructor or destructor, and we have a stored property of
@@ -5994,18 +6028,14 @@ ActorReferenceResult ActorReferenceResult::forReference(
59946028
isNonInheritedStorage(declRef.getDecl(), fromDC) &&
59956029
declIsolation.isGlobalActor() &&
59966030
(isa<ConstructorDecl>(fromDC) || isa<DestructorDecl>(fromDC)))
5997-
return forSameConcurrencyDomain(declIsolation);
5998-
5999-
// Determine what adjustments we need to perform for cross-actor
6000-
// references.
6001-
Options options = llvm::None;
6031+
return forSameConcurrencyDomain(declIsolation, options);
60026032

60036033
// At this point, we are accessing the target from outside the actor.
60046034
// First, check whether it is something that can be accessed directly,
60056035
// without any kind of promotion.
60066036
if (isAccessibleAcrossActors(
60076037
declRef.getDecl(), declIsolation, fromDC, options, actorInstance))
6008-
return forEntersActor(declIsolation, llvm::None);
6038+
return forEntersActor(declIsolation, options);
60096039

60106040
// This is a cross-actor reference.
60116041

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
@@ -1055,15 +1055,7 @@ static void checkDefaultArguments(ParameterList *params) {
10551055

10561056
// If the default argument has isolation, it must match the
10571057
// isolation of the decl context.
1058-
auto defaultArgIsolation = param->getInitializerIsolation();
1059-
if (defaultArgIsolation.isActorIsolated()) {
1060-
auto *dc = param->getDeclContext();
1061-
auto enclosingIsolation = getActorIsolationOfContext(dc);
1062-
if (enclosingIsolation != defaultArgIsolation) {
1063-
param->diagnose(diag::isolated_default_argument_context,
1064-
defaultArgIsolation, enclosingIsolation);
1065-
}
1066-
}
1058+
(void)param->getInitializerIsolation();
10671059

10681060
if (!ifacety->hasPlaceholder()) {
10691061
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)