Skip to content

Commit bd61b66

Browse files
authored
Merge pull request #69974 from hborla/isolated-default-value-fixes
[SE-0411] Fixes for isolated default values.
2 parents c12b6e2 + d6fce01 commit bd61b66

File tree

8 files changed

+179
-46
lines changed

8 files changed

+179
-46
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5352,8 +5352,14 @@ ERROR(distributed_actor_isolated_non_self_reference,none,
53525352
"distributed actor-isolated %kind0 can not be accessed from a "
53535353
"non-isolated context",
53545354
(const ValueDecl *))
5355+
ERROR(conflicting_stored_property_isolation,none,
5356+
"%select{default|memberwise}0 initializer for %1 cannot be both %2 and %3",
5357+
(bool, Type, ActorIsolation, ActorIsolation))
5358+
NOTE(property_requires_actor,none,
5359+
"initializer for %0 %1 is %2",
5360+
(DescriptiveDeclKind, Identifier, ActorIsolation))
53555361
ERROR(isolated_default_argument_context,none,
5356-
"%0 default argument in a %1 context",
5362+
"%0 default value in a %1 context",
53575363
(ActorIsolation, ActorIsolation))
53585364
ERROR(conflicting_default_argument_isolation,none,
53595365
"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
@@ -10637,6 +10637,7 @@ ActorIsolation swift::getActorIsolationOfContext(
1063710637
DeclContext *dc,
1063810638
llvm::function_ref<ActorIsolation(AbstractClosureExpr *)>
1063910639
getClosureActorIsolation) {
10640+
auto &ctx = dc->getASTContext();
1064010641
auto dcToUse = dc;
1064110642
// Defer bodies share actor isolation of their enclosing context.
1064210643
if (auto FD = dyn_cast<FuncDecl>(dcToUse)) {
@@ -10657,6 +10658,13 @@ ActorIsolation swift::getActorIsolationOfContext(
1065710658
// actor isolation as the field itself. That default expression may only
1065810659
// be used from inits that meet the required isolation.
1065910660
if (auto *var = dcToUse->getNonLocalVarDecl()) {
10661+
// If IsolatedDefaultValues are enabled, treat this context as having
10662+
// unspecified isolation. We'll compute the required isolation for
10663+
// the initializer and validate that it matches the isolation of the
10664+
// var itself in the DefaultInitializerIsolation request.
10665+
if (ctx.LangOpts.hasFeature(Feature::IsolatedDefaultValues))
10666+
return ActorIsolation::forUnspecified();
10667+
1066010668
return getActorIsolation(var);
1066110669
}
1066210670

lib/Sema/CodeSynthesis.cpp

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -337,13 +337,64 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
337337
ctor->setSynthesized();
338338
ctor->setAccess(accessLevel);
339339

340+
if (ctx.LangOpts.hasFeature(Feature::IsolatedDefaultValues)) {
341+
// If any of the type's actor-isolated properties:
342+
// 1. Have non-Sendable type, or
343+
// 2. Have an isolated initial value
344+
// then the initializer must also be actor-isolated. If all
345+
// isolated properties have Sendable type and a nonisolated
346+
// default value, then the initializer can be nonisolated.
347+
//
348+
// These rules only apply for global actor isolation, because actor
349+
// initializers apply Sendable checking to arguments at the call-site,
350+
// and actor initializers do not run on the actor, so initial values
351+
// cannot be actor-instance-isolated.
352+
bool shouldAddNonisolated = true;
353+
llvm::Optional<ActorIsolation> existingIsolation = llvm::None;
354+
VarDecl *previousVar = nullptr;
355+
356+
// The memberwise init properties are also effectively what the
357+
// default init uses, e.g. default initializers initialize via
358+
// properties wrapped and init accessors.
359+
for (auto var : decl->getMemberwiseInitProperties()) {
360+
auto type = var->getTypeInContext();
361+
auto isolation = getActorIsolation(var);
362+
if (isolation.isGlobalActor()) {
363+
if (!isSendableType(decl->getModuleContext(), type) ||
364+
var->getInitializerIsolation().isGlobalActor()) {
365+
// If different isolated stored properties require different
366+
// global actors, it is impossible to initialize this type.
367+
if (existingIsolation &&
368+
*existingIsolation != isolation) {
369+
ctx.Diags.diagnose(decl->getLoc(),
370+
diag::conflicting_stored_property_isolation,
371+
ICK == ImplicitConstructorKind::Memberwise,
372+
decl->getDeclaredType(), *existingIsolation, isolation);
373+
previousVar->diagnose(
374+
diag::property_requires_actor,
375+
previousVar->getDescriptiveKind(),
376+
previousVar->getName(), *existingIsolation);
377+
var->diagnose(
378+
diag::property_requires_actor,
379+
var->getDescriptiveKind(),
380+
var->getName(), isolation);
381+
}
382+
383+
existingIsolation = isolation;
384+
previousVar = var;
385+
shouldAddNonisolated = false;
386+
}
387+
}
388+
}
389+
390+
if (shouldAddNonisolated) {
391+
addNonIsolatedToSynthesized(decl, ctor);
392+
}
393+
}
394+
340395
if (ICK == ImplicitConstructorKind::Memberwise) {
341396
ctor->setIsMemberwiseInitializer();
342397

343-
// FIXME: If 'IsolatedDefaultValues' is enabled, the memberwise init
344-
// should be 'nonisolated' if none of the memberwise-initialized properties
345-
// are global actor isolated and have non-Sendable type, and none of the
346-
// initial values require global actor isolation.
347398
if (!ctx.LangOpts.hasFeature(Feature::IsolatedDefaultValues)) {
348399
addNonIsolatedToSynthesized(decl, ctor);
349400
}

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 54 additions & 21 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,14 +4760,19 @@ 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())
47634767
return ActorIsolation::forUnspecified();
47644768

47654769
auto i = pbd->getPatternEntryIndexForVarDecl(var);
4770+
if (!pbd->isInitializerChecked(i))
4771+
TypeChecker::typeCheckPatternBinding(pbd, i);
4772+
47664773
dc = cast<Initializer>(pbd->getInitContext(i));
4767-
initExpr = var->getParentInitializer();
4774+
initExpr = pbd->getInit(i);
4775+
enclosingIsolation = getActorIsolation(var);
47684776
} else if (auto *param = dyn_cast<ParamDecl>(var)) {
47694777
// If this parameter corresponds to a stored property for a
47704778
// memberwise initializer, the default argument is the default
@@ -4782,13 +4790,27 @@ DefaultInitializerIsolation::evaluate(Evaluator &evaluator,
47824790

47834791
dc = param->getDefaultArgumentInitContext();
47844792
initExpr = param->getTypeCheckedDefaultExpr();
4793+
enclosingIsolation =
4794+
getActorIsolationOfContext(param->getDeclContext());
47854795
}
47864796

47874797
if (!dc || !initExpr)
47884798
return ActorIsolation::forUnspecified();
47894799

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

47944816
void swift::checkOverrideActorIsolation(ValueDecl *value) {
@@ -5859,8 +5881,8 @@ bool swift::isAccessibleAcrossActors(
58595881
}
58605882

58615883
ActorReferenceResult ActorReferenceResult::forSameConcurrencyDomain(
5862-
ActorIsolation isolation) {
5863-
return ActorReferenceResult{SameConcurrencyDomain, llvm::None, isolation};
5884+
ActorIsolation isolation, Options options) {
5885+
return ActorReferenceResult{SameConcurrencyDomain, options, isolation};
58645886
}
58655887

58665888
ActorReferenceResult ActorReferenceResult::forEntersActor(
@@ -5869,8 +5891,8 @@ ActorReferenceResult ActorReferenceResult::forEntersActor(
58695891
}
58705892

58715893
ActorReferenceResult ActorReferenceResult::forExitsActorToNonisolated(
5872-
ActorIsolation isolation) {
5873-
return ActorReferenceResult{ExitsActorToNonisolated, llvm::None, isolation};
5894+
ActorIsolation isolation, Options options) {
5895+
return ActorReferenceResult{ExitsActorToNonisolated, options, isolation};
58745896
}
58755897

58765898
// Determine if two actor isolation contexts are considered to be equivalent.
@@ -5906,10 +5928,24 @@ ActorReferenceResult ActorReferenceResult::forReference(
59065928
declIsolation = declIsolation.subst(declRef.getSubstitutions());
59075929
}
59085930

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

59145950
// Compute the isolation of the context, if not provided.
59155951
ActorIsolation contextIsolation = ActorIsolation::forUnspecified();
@@ -5928,11 +5964,11 @@ ActorReferenceResult ActorReferenceResult::forReference(
59285964
if (isAsyncDecl(declRef) && contextIsolation.isActorIsolated() &&
59295965
!declRef.getDecl()->getAttrs()
59305966
.hasAttribute<UnsafeInheritExecutorAttr>())
5931-
return forExitsActorToNonisolated(contextIsolation);
5967+
return forExitsActorToNonisolated(contextIsolation, options);
59325968

59335969
// Otherwise, we stay in the same concurrency domain, whether on an actor
59345970
// or in a task.
5935-
return forSameConcurrencyDomain(declIsolation);
5971+
return forSameConcurrencyDomain(declIsolation, options);
59365972
}
59375973

59385974
// The declaration we are accessing is actor-isolated. First, check whether
@@ -5941,11 +5977,11 @@ ActorReferenceResult ActorReferenceResult::forReference(
59415977
declIsolation.getActorInstanceParameter() == 0) {
59425978
// If this instance is isolated, we're in the same concurrency domain.
59435979
if (actorInstance->isIsolated())
5944-
return forSameConcurrencyDomain(declIsolation);
5980+
return forSameConcurrencyDomain(declIsolation, options);
59455981
} else if (equivalentIsolationContexts(declIsolation, contextIsolation)) {
59465982
// The context isolation matches, so we are in the same concurrency
59475983
// domain.
5948-
return forSameConcurrencyDomain(declIsolation);
5984+
return forSameConcurrencyDomain(declIsolation, options);
59495985
}
59505986

59515987
// Initializing an actor isolated stored property with a value effectively
@@ -5965,7 +6001,8 @@ ActorReferenceResult ActorReferenceResult::forReference(
59656001
// Treat the decl isolation as 'preconcurrency' to downgrade violations
59666002
// to warnings, because violating Sendable here is accepted by the
59676003
// Swift 5.9 compiler.
5968-
return forEntersActor(declIsolation, Flags::Preconcurrency);
6004+
options |= Flags::Preconcurrency;
6005+
return forEntersActor(declIsolation, options);
59696006
}
59706007
}
59716008
}
@@ -5975,7 +6012,7 @@ ActorReferenceResult ActorReferenceResult::forReference(
59756012
if (actorInstance &&
59766013
checkedByFlowIsolation(
59776014
fromDC, *actorInstance, declRef.getDecl(), declRefLoc, useKind))
5978-
return forSameConcurrencyDomain(declIsolation);
6015+
return forSameConcurrencyDomain(declIsolation, options);
59796016

59806017
// If we are delegating to another initializer, treat them as being in the
59816018
// same concurrency domain.
@@ -5984,7 +6021,7 @@ ActorReferenceResult ActorReferenceResult::forReference(
59846021
if (actorInstance && actorInstance->isSelf() &&
59856022
isa<ConstructorDecl>(declRef.getDecl()) &&
59866023
isa<ConstructorDecl>(fromDC))
5987-
return forSameConcurrencyDomain(declIsolation);
6024+
return forSameConcurrencyDomain(declIsolation, options);
59886025

59896026
// If there is an instance that corresponds to 'self',
59906027
// we are in a constructor or destructor, and we have a stored property of
@@ -5994,18 +6031,14 @@ ActorReferenceResult ActorReferenceResult::forReference(
59946031
isNonInheritedStorage(declRef.getDecl(), fromDC) &&
59956032
declIsolation.isGlobalActor() &&
59966033
(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;
6034+
return forSameConcurrencyDomain(declIsolation, options);
60026035

60036036
// At this point, we are accessing the target from outside the actor.
60046037
// First, check whether it is something that can be accessed directly,
60056038
// without any kind of promotion.
60066039
if (isAccessibleAcrossActors(
60076040
declRef.getDecl(), declIsolation, fromDC, options, actorInstance))
6008-
return forEntersActor(declIsolation, llvm::None);
6041+
return forEntersActor(declIsolation, options);
60096042

60106043
// This is a cross-actor reference.
60116044

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;

0 commit comments

Comments
 (0)