Skip to content

Commit debc7d5

Browse files
committed
[Concurrency] Allow default initializer expressions for stored instance
properties to require actor isolation. Member initializer expressions are only used in a constructor with matching actor isolation. If the isolation prohibits the member initializer from being evaluated synchronously (or propagating required isolation through closure bodies), then the default value cannot be used and the member must be explicitly initialized in the constructor. Member initializer expressions are also used as default arguments for the memberwise initializer, and the same rules for default argument isolation apply.
1 parent 32e5e9d commit debc7d5

12 files changed

+230
-24
lines changed

include/swift/AST/Decl.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1960,6 +1960,15 @@ class PatternBindingEntry {
19601960
SourceRange getOriginalInitRange() const;
19611961
void setInit(Expr *E);
19621962

1963+
/// Get the required actor isolation for evaluating the initializer
1964+
/// expression synchronously (if there is one).
1965+
///
1966+
/// If this pattern binding entry is for a stored instance property, the
1967+
/// initializer can only be used in an `init` that meets the required
1968+
/// isolation; otherwise, the property must be explicitly initialized in
1969+
/// the `init`.
1970+
ActorIsolation getInitializerIsolation() const;
1971+
19631972
/// Gets the text of the initializer expression, stripping out inactive
19641973
/// branches of any #ifs inside the expression.
19651974
StringRef getInitStringRepresentation(SmallVectorImpl<char> &scratch) const;
@@ -2213,6 +2222,10 @@ class PatternBindingDecl final : public Decl,
22132222
getMutablePatternList()[i].setOriginalInit(E);
22142223
}
22152224

2225+
ActorIsolation getInitializerIsolation(unsigned i) const {
2226+
return getPatternList()[i].getInitializerIsolation();
2227+
}
2228+
22162229
Pattern *getPattern(unsigned i) const {
22172230
return getPatternList()[i].getPattern();
22182231
}

include/swift/AST/TypeCheckRequests.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2914,23 +2914,27 @@ class DefaultArgumentTypeRequest
29142914
};
29152915

29162916
/// Compute the actor isolation needed to synchronously evaluate the
2917-
/// default argument for the given parameter.
2918-
class DefaultArgumentIsolation
2919-
: public SimpleRequest<DefaultArgumentIsolation,
2920-
ActorIsolation(ParamDecl *),
2917+
/// default initializer expression.
2918+
class DefaultInitializerIsolation
2919+
: public SimpleRequest<DefaultInitializerIsolation,
2920+
ActorIsolation(Initializer *, Expr *),
29212921
RequestFlags::Cached> {
29222922
public:
29232923
using SimpleRequest::SimpleRequest;
29242924

29252925
private:
29262926
friend SimpleRequest;
29272927

2928-
ActorIsolation evaluate(Evaluator &evaluator, ParamDecl *param) const;
2928+
ActorIsolation evaluate(Evaluator &evaluator,
2929+
Initializer *init,
2930+
Expr *initExpr) const;
29292931

29302932
public:
29312933
bool isCached() const { return true; }
29322934
};
29332935

2936+
void simple_display(llvm::raw_ostream &out, Initializer *init);
2937+
29342938
/// Computes the fully type-checked caller-side default argument within the
29352939
/// context of the call site that it will be inserted into.
29362940
class CallerSideDefaultArgExprRequest

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +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, DefaultArgumentIsolation,
66-
ActorIsolation(ParamDecl *), Cached, NoLocationInfo)
65+
SWIFT_REQUEST(TypeChecker, DefaultInitializerIsolation,
66+
ActorIsolation(Initializer *, Expr *),
67+
Cached, NoLocationInfo)
6768
SWIFT_REQUEST(TypeChecker, DefaultArgumentInitContextRequest,
6869
Initializer *(ParamDecl *), SeparatelyCached, NoLocationInfo)
6970
SWIFT_REQUEST(TypeChecker, DefaultDefinitionTypeRequest,

lib/AST/Decl.cpp

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1953,6 +1953,20 @@ void PatternBindingEntry::setInit(Expr *E) {
19531953
PatternFlags::IsText);
19541954
}
19551955

1956+
ActorIsolation PatternBindingEntry::getInitializerIsolation() const {
1957+
if (!isInitialized())
1958+
return ActorIsolation::forUnspecified();
1959+
1960+
auto *dc = cast<Initializer>(getInitContext());
1961+
auto &ctx = dc->getASTContext();
1962+
auto *initExpr = getExecutableInit();
1963+
1964+
return evaluateOrDefault(
1965+
ctx.evaluator,
1966+
DefaultInitializerIsolation{dc, initExpr},
1967+
ActorIsolation::forNonisolated());
1968+
}
1969+
19561970
VarDecl *PatternBindingEntry::getAnchoringVarDecl() const {
19571971
SmallVector<VarDecl *, 8> variables;
19581972
getPattern()->collectVariables(variables);
@@ -8225,10 +8239,26 @@ Type ParamDecl::getTypeOfDefaultExpr() const {
82258239
}
82268240

82278241
ActorIsolation ParamDecl::getDefaultArgumentIsolation() const {
8242+
// If this parameter corresponds to a stored property for a
8243+
// memberwise initializer, the default argument is the default
8244+
// initializer expression.
8245+
auto *var = getStoredProperty();
8246+
if (var && !var->isInvalid()) {
8247+
auto *pbd = var->getParentPatternBinding();
8248+
auto i = pbd->getPatternEntryIndexForVarDecl(var);
8249+
return var->getParentPatternBinding()->getInitializerIsolation(i);
8250+
}
8251+
8252+
if (!hasDefaultExpr())
8253+
return ActorIsolation::forUnspecified();
8254+
82288255
auto &ctx = getASTContext();
8256+
auto *dc = getDefaultArgumentInitContext();
8257+
auto *initExpr = getTypeCheckedDefaultExpr();
8258+
82298259
return evaluateOrDefault(
82308260
ctx.evaluator,
8231-
DefaultArgumentIsolation{const_cast<ParamDecl *>(this)},
8261+
DefaultInitializerIsolation{dc, initExpr},
82328262
ActorIsolation::forNonisolated());
82338263
}
82348264

@@ -10340,8 +10370,25 @@ ActorIsolation swift::getActorIsolationOfContext(
1034010370
if (auto *vd = dyn_cast_or_null<ValueDecl>(dcToUse->getAsDecl()))
1034110371
return getActorIsolation(vd);
1034210372

10343-
if (auto *var = dcToUse->getNonLocalVarDecl())
10373+
// In the context of the initializing or default-value expression of a
10374+
// stored property, the isolation varies between instance and type members:
10375+
// - For a static stored property, the isolation matches the VarDecl.
10376+
// Static properties are initialized upon first use, so the isolation
10377+
// of the initializer must match the isolation required to access the
10378+
// property.
10379+
// - For a field of a nominal type, the expression can require a specific
10380+
// actor isolation. That default expression may only be used from inits
10381+
// that meet the required isolation.
10382+
if (auto *var = dcToUse->getNonLocalVarDecl()) {
10383+
auto &ctx = dc->getASTContext();
10384+
if (ctx.LangOpts.hasFeature(Feature::IsolatedDefaultArguments) &&
10385+
var->isInstanceMember() &&
10386+
!var->getAttrs().hasAttribute<LazyAttr>()) {
10387+
return ActorIsolation::forNonisolated();
10388+
}
10389+
1034410390
return getActorIsolation(var);
10391+
}
1034510392

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

lib/AST/TypeCheckRequests.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,6 +1284,21 @@ void DefaultArgumentTypeRequest::cacheResult(Type type) const {
12841284
param->setDefaultExprType(type);
12851285
}
12861286

1287+
//----------------------------------------------------------------------------//
1288+
// DefaultInitializerIsolation computation.
1289+
//----------------------------------------------------------------------------//
1290+
1291+
void swift::simple_display(llvm::raw_ostream &out, Initializer *init) {
1292+
switch (init->getInitializerKind()) {
1293+
case InitializerKind::PatternBinding:
1294+
out << "pattern binding initializer";
1295+
case InitializerKind::DefaultArgument:
1296+
out << "default argument initializer";
1297+
case InitializerKind::PropertyWrapper:
1298+
out << "property wrapper initializer";
1299+
}
1300+
}
1301+
12871302
//----------------------------------------------------------------------------//
12881303
// CallerSideDefaultArgExprRequest computation.
12891304
//----------------------------------------------------------------------------//

lib/SILGen/SILGenConstructor.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1526,6 +1526,26 @@ void SILGenFunction::emitMemberInitializer(DeclContext *dc, VarDecl *selfDecl,
15261526
if (!init)
15271527
continue;
15281528

1529+
// Member initializer expressions are only used in a constructor with
1530+
// matching actor isolation. If the isolation prohibits the member
1531+
// initializer from being evaluated synchronously (or propagating required
1532+
// isolation through closure bodies), then the default value cannot be used
1533+
// and the member must be explicitly initialized in the constructor.
1534+
auto requiredIsolation = field->getInitializerIsolation(i);
1535+
auto contextIsolation = getActorIsolationOfContext(dc);
1536+
switch (requiredIsolation) {
1537+
// 'nonisolated' expressions can be evaluated from anywhere
1538+
case ActorIsolation::Unspecified:
1539+
case ActorIsolation::Nonisolated:
1540+
break;
1541+
1542+
case ActorIsolation::GlobalActor:
1543+
case ActorIsolation::GlobalActorUnsafe:
1544+
case ActorIsolation::ActorInstance:
1545+
if (requiredIsolation != contextIsolation)
1546+
continue;
1547+
}
1548+
15291549
auto *varPattern = field->getPattern(i);
15301550

15311551
// Cleanup after this initialization.

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3582,11 +3582,6 @@ void swift::checkFunctionActorIsolation(AbstractFunctionDecl *decl) {
35823582
}
35833583
}
35843584

3585-
void swift::checkInitializerActorIsolation(Initializer *init, Expr *expr) {
3586-
ActorIsolationChecker checker(init);
3587-
expr->walk(checker);
3588-
}
3589-
35903585
ActorIsolation
35913586
swift::computeRequiredIsolation(Initializer *init, Expr *expr) {
35923587
ActorIsolationChecker checker(init);

lib/Sema/TypeCheckConcurrency.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ void addAsyncNotes(AbstractFunctionDecl const* func);
5656
/// Check actor isolation rules.
5757
void checkTopLevelActorIsolation(TopLevelCodeDecl *decl);
5858
void checkFunctionActorIsolation(AbstractFunctionDecl *decl);
59-
void checkInitializerActorIsolation(Initializer *init, Expr *expr);
6059
void checkEnumElementActorIsolation(EnumElementDecl *element, Expr *expr);
6160
void checkPropertyWrapperActorIsolation(VarDecl *wrappedVar, Expr *expr);
6261

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,14 +1157,13 @@ Expr *DefaultArgumentExprRequest::evaluate(Evaluator &evaluator,
11571157
}
11581158

11591159
ActorIsolation
1160-
DefaultArgumentIsolation::evaluate(Evaluator &evaluator,
1161-
ParamDecl *param) const {
1162-
if (!param->hasDefaultExpr())
1160+
DefaultInitializerIsolation::evaluate(Evaluator &evaluator,
1161+
Initializer *init,
1162+
Expr *initExpr) const {
1163+
if (!init || !initExpr)
11631164
return ActorIsolation::forUnspecified();
11641165

1165-
auto *dc = param->getDefaultArgumentInitContext();
1166-
auto *initExpr = param->getTypeCheckedDefaultExpr();
1167-
return computeRequiredIsolation(dc, initExpr);
1166+
return computeRequiredIsolation(init, initExpr);
11681167
}
11691168

11701169
Type DefaultArgumentTypeRequest::evaluate(Evaluator &evaluator,
@@ -2477,7 +2476,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
24772476
PBD->getInitContext(i));
24782477
if (initContext) {
24792478
TypeChecker::contextualizeInitializer(initContext, init);
2480-
checkInitializerActorIsolation(initContext, init);
2479+
(void)PBD->getInitializerIsolation(i);
24812480
TypeChecker::checkInitializerEffects(initContext, init);
24822481
}
24832482
}

lib/Sema/TypeCheckStorage.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3210,7 +3210,7 @@ PropertyWrapperInitializerInfoRequest::evaluate(Evaluator &evaluator,
32103210
// synthesize a computed property for '$foo'.
32113211
Expr *projectedValueInit = nullptr;
32123212
if (auto *projection = var->getPropertyWrapperProjectionVar()) {
3213-
createPBD(projection);
3213+
auto *pbd = createPBD(projection);
32143214

32153215
if (var->hasExternalPropertyWrapper()) {
32163216
// Projected-value initialization is currently only supported for parameters.
@@ -3224,7 +3224,7 @@ PropertyWrapperInitializerInfoRequest::evaluate(Evaluator &evaluator,
32243224
// Check initializer effects.
32253225
auto *initContext = new (ctx) PropertyWrapperInitializer(
32263226
dc, param, PropertyWrapperInitializer::Kind::ProjectedValue);
3227-
checkInitializerActorIsolation(initContext, projectedValueInit);
3227+
(void)pbd->getInitializerIsolation(0);
32283228
TypeChecker::checkInitializerEffects(initContext, projectedValueInit);
32293229
}
32303230
}

test/Concurrency/isolated_default_arguments.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,59 @@ func closureWithIsolatedParam(
124124
_ = requiresMainActor()
125125
}
126126
) {}
127+
128+
@MainActor
129+
struct S1 {
130+
var required: Int
131+
132+
var x: Int = requiresMainActor()
133+
134+
lazy var y: Int = requiresMainActor()
135+
136+
static var z: Int = requiresMainActor()
137+
}
138+
139+
@SomeGlobalActor
140+
struct S2 {
141+
var required: Int
142+
143+
var x: Int = requiresSomeGlobalActor()
144+
145+
lazy var y: Int = requiresSomeGlobalActor()
146+
147+
static var z: Int = requiresSomeGlobalActor()
148+
}
149+
150+
@MainActor
151+
func initializeFromMainActor() {
152+
_ = S1(required: 10)
153+
154+
// expected-error@+1 {{global actor 'SomeGlobalActor'-isolated default argument cannot be synchronously evaluated from a main actor-isolated context}}
155+
_ = S2(required: 10)
156+
}
157+
158+
@SomeGlobalActor
159+
func initializeFromSomeGlobalActor() {
160+
// expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a global actor 'SomeGlobalActor'-isolated context}}
161+
_ = S1(required: 10)
162+
163+
_ = S2(required: 10)
164+
}
165+
166+
func initializeFromNonisolated() {
167+
// expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}}
168+
_ = S1(required: 10)
169+
170+
// expected-error@+1 {{global actor 'SomeGlobalActor'-isolated default argument cannot be synchronously evaluated from a nonisolated context}}
171+
_ = S2(required: 10)
172+
}
173+
174+
extension A {
175+
func initializeFromActorInstance() {
176+
// expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a actor-isolated context}}
177+
_ = S1(required: 10)
178+
179+
// expected-error@+1 {{global actor 'SomeGlobalActor'-isolated default argument cannot be synchronously evaluated from a actor-isolated context}}
180+
_ = S2(required: 10)
181+
}
182+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/OtherActors.swiftmodule -module-name OtherActors %S/Inputs/OtherActors.swift -disable-availability-checking
4+
5+
// RUN: %target-swift-frontend -I %t -disable-availability-checking -strict-concurrency=complete -enable-experimental-feature IsolatedDefaultArguments -parse-as-library -emit-sil -o /dev/null -verify %s
6+
// RUN: %target-swift-frontend -I %t -disable-availability-checking -strict-concurrency=complete -parse-as-library -emit-sil -o /dev/null -verify -enable-experimental-feature IsolatedDefaultArguments -enable-experimental-feature SendNonSendable %s
7+
8+
// REQUIRES: concurrency
9+
// REQUIRES: asserts
10+
11+
@globalActor
12+
actor SomeGlobalActor {
13+
static let shared = SomeGlobalActor()
14+
}
15+
16+
@MainActor
17+
func requiresMainActor() -> Int { 0 }
18+
19+
@SomeGlobalActor
20+
func requiresSomeGlobalActor() -> Int { 0 }
21+
22+
struct S1 {
23+
// expected-note@+1 2 {{'self.x' not initialized}}
24+
var x = requiresMainActor()
25+
// expected-note@+1 2 {{'self.y' not initialized}}
26+
var y = requiresSomeGlobalActor()
27+
var z = 10
28+
29+
// expected-error@+1 {{return from initializer without initializing all stored properties}}
30+
nonisolated init(a: Int) {}
31+
32+
// expected-error@+1 {{return from initializer without initializing all stored properties}}
33+
@MainActor init(b: Int) {}
34+
35+
// expected-error@+1 {{return from initializer without initializing all stored properties}}
36+
@SomeGlobalActor init(c: Int) {}
37+
}
38+
39+
struct S2 {
40+
var x = requiresMainActor()
41+
var y = requiresSomeGlobalActor()
42+
var z = 10
43+
44+
nonisolated init(x: Int, y: Int) {
45+
self.x = x
46+
self.y = y
47+
}
48+
49+
@MainActor init(y: Int) {
50+
self.y = y
51+
}
52+
53+
@SomeGlobalActor init(x: Int) {
54+
self.x = x
55+
}
56+
}
57+

0 commit comments

Comments
 (0)