Skip to content

Commit f7a8bc2

Browse files
authored
Merge pull request #68794 from hborla/isolated-initializer-expressions
[Concurrency] Allow default arguments to require actor isolation.
2 parents 3a7b35e + 67e3326 commit f7a8bc2

22 files changed

+664
-14
lines changed

include/swift/AST/Decl.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2273,7 +2273,9 @@ class PatternBindingDecl final : public Decl,
22732273
void setInitializerSubsumed(unsigned i) {
22742274
getMutablePatternList()[i].setInitializerSubsumed();
22752275
}
2276-
2276+
2277+
ActorIsolation getInitializerIsolation(unsigned i) const;
2278+
22772279
/// Does this binding declare something that requires storage?
22782280
bool hasStorage() const;
22792281

@@ -5973,6 +5975,19 @@ class VarDecl : public AbstractStorageDecl {
59735975
return getParentExecutableInitializer() != nullptr;
59745976
}
59755977

5978+
/// Get the required actor isolation for evaluating the initializer
5979+
/// expression synchronously (if there is one).
5980+
///
5981+
/// If this VarDecl is a stored instance property, the initializer
5982+
/// can only be used in an `init` that meets the required isolation.
5983+
/// Otherwise, the property must be explicitly initialized in the `init`.
5984+
///
5985+
/// If this is a ParamDecl, the initializer isolation is required at
5986+
/// the call-site in order to use the default argument for this parameter.
5987+
/// If the required isolation is not met, an argument must be written
5988+
/// explicitly at the call-site.
5989+
ActorIsolation getInitializerIsolation() const;
5990+
59765991
// Return whether this VarDecl has an initial value, either by checking
59775992
// if it has an initializer in its parent pattern binding or if it has
59785993
// the @_hasInitialValue attribute.

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5236,6 +5236,13 @@ ERROR(distributed_actor_isolated_non_self_reference,none,
52365236
"distributed actor-isolated %kind0 can not be accessed from a "
52375237
"non-isolated context",
52385238
(const ValueDecl *))
5239+
ERROR(isolated_default_argument,none,
5240+
"%0 default argument cannot be synchronously evaluated from a "
5241+
"%1 context",
5242+
(ActorIsolation, ActorIsolation))
5243+
ERROR(conflicting_default_argument_isolation,none,
5244+
"default argument cannot be both %0 and %1",
5245+
(ActorIsolation, ActorIsolation))
52395246
ERROR(distributed_actor_needs_explicit_distributed_import,none,
52405247
"'Distributed' module not imported, required for 'distributed actor'",
52415248
())

include/swift/AST/Expr.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4569,6 +4569,11 @@ class DefaultArgumentExpr final : public Expr {
45694569
/// expression within the context of the call site.
45704570
Expr *getCallerSideDefaultExpr() const;
45714571

4572+
/// Get the required actor isolation for evaluating this default argument
4573+
/// synchronously. If the caller does not meet the required isolation, the
4574+
/// argument must be written explicitly at the call-site.
4575+
ActorIsolation getRequiredIsolation() const;
4576+
45724577
static bool classof(const Expr *E) {
45734578
return E->getKind() == ExprKind::DefaultArgument;
45744579
}

include/swift/AST/TypeCheckRequests.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2934,6 +2934,27 @@ class DefaultArgumentTypeRequest
29342934
void cacheResult(Type type) const;
29352935
};
29362936

2937+
/// Compute the actor isolation needed to synchronously evaluate the
2938+
/// default initializer expression.
2939+
class DefaultInitializerIsolation
2940+
: public SimpleRequest<DefaultInitializerIsolation,
2941+
ActorIsolation(VarDecl *),
2942+
RequestFlags::Cached> {
2943+
public:
2944+
using SimpleRequest::SimpleRequest;
2945+
2946+
private:
2947+
friend SimpleRequest;
2948+
2949+
ActorIsolation evaluate(Evaluator &evaluator,
2950+
VarDecl *) const;
2951+
2952+
public:
2953+
bool isCached() const { return true; }
2954+
};
2955+
2956+
void simple_display(llvm::raw_ostream &out, Initializer *init);
2957+
29372958
/// Computes the fully type-checked caller-side default argument within the
29382959
/// context of the call site that it will be inserted into.
29392960
class CallerSideDefaultArgExprRequest

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ SWIFT_REQUEST(TypeChecker, DefaultArgumentExprRequest,
6262
Expr *(ParamDecl *), SeparatelyCached, NoLocationInfo)
6363
SWIFT_REQUEST(TypeChecker, DefaultArgumentTypeRequest,
6464
Type(ParamDecl *), SeparatelyCached, NoLocationInfo)
65+
SWIFT_REQUEST(TypeChecker, DefaultInitializerIsolation,
66+
ActorIsolation(VarDecl *),
67+
Cached, NoLocationInfo)
6568
SWIFT_REQUEST(TypeChecker, DefaultArgumentInitContextRequest,
6669
Initializer *(ParamDecl *), SeparatelyCached, NoLocationInfo)
6770
SWIFT_REQUEST(TypeChecker, DefaultDefinitionTypeRequest,

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,9 @@ EXPERIMENTAL_FEATURE(SendNonSendable, false)
222222
/// Within strict concurrency, narrow global variable isolation requirements.
223223
EXPERIMENTAL_FEATURE(GlobalConcurrency, false)
224224

225+
/// Allow default arguments to require isolation at the call-site.
226+
EXPERIMENTAL_FEATURE(IsolatedDefaultArguments, false)
227+
225228
/// Enable extended callbacks (with additional parameters) to be used when the
226229
/// "playground transform" is enabled.
227230
EXPERIMENTAL_FEATURE(PlaygroundExtendedCallbacks, true)

lib/AST/ASTPrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3551,6 +3551,10 @@ static bool usesFeatureSendNonSendable(Decl *decl) {
35513551

35523552
static bool usesFeatureGlobalConcurrency(Decl *decl) { return false; }
35533553

3554+
static bool usesFeatureIsolatedDefaultArguments(Decl *decl) {
3555+
return false;
3556+
}
3557+
35543558
static bool usesFeaturePlaygroundExtendedCallbacks(Decl *decl) {
35553559
return false;
35563560
}

lib/AST/Decl.cpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2088,6 +2088,14 @@ bool PatternBindingDecl::isAsyncLet() const {
20882088
return false;
20892089
}
20902090

2091+
ActorIsolation
2092+
PatternBindingDecl::getInitializerIsolation(unsigned i) const {
2093+
auto *var = getPatternList()[i].getAnchoringVarDecl();
2094+
if (!var)
2095+
return ActorIsolation::forUnspecified();
2096+
2097+
return var->getInitializerIsolation();
2098+
}
20912099

20922100
bool PatternBindingDecl::hasStorage() const {
20932101
// Walk the pattern, to check to see if any of the VarDecls included in it
@@ -7127,6 +7135,14 @@ Expr *VarDecl::getParentExecutableInitializer() const {
71277135
return nullptr;
71287136
}
71297137

7138+
ActorIsolation VarDecl::getInitializerIsolation() const {
7139+
auto *mutableThis = const_cast<VarDecl *>(this);
7140+
return evaluateOrDefault(
7141+
getASTContext().evaluator,
7142+
DefaultInitializerIsolation{mutableThis},
7143+
ActorIsolation::forUnspecified());
7144+
}
7145+
71307146
SourceRange VarDecl::getSourceRange() const {
71317147
if (auto Param = dyn_cast<ParamDecl>(this))
71327148
return Param->getSourceRange();
@@ -10445,8 +10461,25 @@ ActorIsolation swift::getActorIsolationOfContext(
1044510461
if (auto *vd = dyn_cast_or_null<ValueDecl>(dcToUse->getAsDecl()))
1044610462
return getActorIsolation(vd);
1044710463

10448-
if (auto *var = dcToUse->getNonLocalVarDecl())
10464+
// In the context of the initializing or default-value expression of a
10465+
// stored property, the isolation varies between instance and type members:
10466+
// - For a static stored property, the isolation matches the VarDecl.
10467+
// Static properties are initialized upon first use, so the isolation
10468+
// of the initializer must match the isolation required to access the
10469+
// property.
10470+
// - For a field of a nominal type, the expression can require a specific
10471+
// actor isolation. That default expression may only be used from inits
10472+
// that meet the required isolation.
10473+
if (auto *var = dcToUse->getNonLocalVarDecl()) {
10474+
auto &ctx = dc->getASTContext();
10475+
if (ctx.LangOpts.hasFeature(Feature::IsolatedDefaultArguments) &&
10476+
var->isInstanceMember() &&
10477+
!var->getAttrs().hasAttribute<LazyAttr>()) {
10478+
return ActorIsolation::forNonisolated();
10479+
}
10480+
1044910481
return getActorIsolation(var);
10482+
}
1045010483

1045110484
if (auto *closure = dyn_cast<AbstractClosureExpr>(dcToUse)) {
1045210485
return getClosureActorIsolation(closure);

lib/AST/Expr.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,6 +1606,11 @@ Expr *DefaultArgumentExpr::getCallerSideDefaultExpr() const {
16061606
new (ctx) ErrorExpr(getSourceRange(), getType()));
16071607
}
16081608

1609+
ActorIsolation
1610+
DefaultArgumentExpr::getRequiredIsolation() const {
1611+
return getParamDecl()->getInitializerIsolation();
1612+
}
1613+
16091614
ValueDecl *ApplyExpr::getCalledValue(bool skipFunctionConversions) const {
16101615
return ::getCalledValue(Fn, skipFunctionConversions);
16111616
}

lib/AST/TypeCheckRequests.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,6 +1302,24 @@ void DefaultArgumentTypeRequest::cacheResult(Type type) const {
13021302
param->setDefaultExprType(type);
13031303
}
13041304

1305+
//----------------------------------------------------------------------------//
1306+
// DefaultInitializerIsolation computation.
1307+
//----------------------------------------------------------------------------//
1308+
1309+
void swift::simple_display(llvm::raw_ostream &out, Initializer *init) {
1310+
switch (init->getInitializerKind()) {
1311+
case InitializerKind::PatternBinding:
1312+
out << "pattern binding initializer";
1313+
break;
1314+
case InitializerKind::DefaultArgument:
1315+
out << "default argument initializer";
1316+
break;
1317+
case InitializerKind::PropertyWrapper:
1318+
out << "property wrapper initializer";
1319+
break;
1320+
}
1321+
}
1322+
13051323
//----------------------------------------------------------------------------//
13061324
// CallerSideDefaultArgExprRequest computation.
13071325
//----------------------------------------------------------------------------//

lib/SILGen/SILGenConstructor.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,6 +1542,27 @@ void SILGenFunction::emitMemberInitializer(DeclContext *dc, VarDecl *selfDecl,
15421542
if (!init)
15431543
continue;
15441544

1545+
// Member initializer expressions are only used in a constructor with
1546+
// matching actor isolation. If the isolation prohibits the member
1547+
// initializer from being evaluated synchronously (or propagating required
1548+
// isolation through closure bodies), then the default value cannot be used
1549+
// and the member must be explicitly initialized in the constructor.
1550+
auto *var = field->getAnchoringVarDecl(i);
1551+
auto requiredIsolation = var->getInitializerIsolation();
1552+
auto contextIsolation = getActorIsolationOfContext(dc);
1553+
switch (requiredIsolation) {
1554+
// 'nonisolated' expressions can be evaluated from anywhere
1555+
case ActorIsolation::Unspecified:
1556+
case ActorIsolation::Nonisolated:
1557+
break;
1558+
1559+
case ActorIsolation::GlobalActor:
1560+
case ActorIsolation::GlobalActorUnsafe:
1561+
case ActorIsolation::ActorInstance:
1562+
if (requiredIsolation != contextIsolation)
1563+
continue;
1564+
}
1565+
15451566
auto *varPattern = field->getPattern(i);
15461567

15471568
// Cleanup after this initialization.

0 commit comments

Comments
 (0)