Skip to content

Commit 1157198

Browse files
committed
[Concurrency] Allow isolated default arguments to be used from across isolation
boundaries.
1 parent 936739e commit 1157198

10 files changed

+104
-59
lines changed

Diff for: include/swift/AST/DiagnosticsSema.def

+2-3
Original file line numberDiff line numberDiff line change
@@ -5263,9 +5263,8 @@ ERROR(distributed_actor_isolated_non_self_reference,none,
52635263
"distributed actor-isolated %kind0 can not be accessed from a "
52645264
"non-isolated context",
52655265
(const ValueDecl *))
5266-
ERROR(isolated_default_argument,none,
5267-
"%0 default argument cannot be synchronously evaluated from a "
5268-
"%1 context",
5266+
ERROR(isolated_default_argument_context,none,
5267+
"%0 default argument in a %1 context",
52695268
(ActorIsolation, ActorIsolation))
52705269
ERROR(conflicting_default_argument_isolation,none,
52715270
"default argument cannot be both %0 and %1",

Diff for: include/swift/AST/Expr.h

+14
Original file line numberDiff line numberDiff line change
@@ -4540,6 +4540,10 @@ class DefaultArgumentExpr final : public Expr {
45404540
/// default expression.
45414541
PointerUnion<DeclContext *, Expr *> ContextOrCallerSideExpr;
45424542

4543+
/// Whether this default argument is evaluated asynchronously because
4544+
/// it's isolated to the callee's isolation domain.
4545+
bool implicitlyAsync = false;
4546+
45434547
public:
45444548
explicit DefaultArgumentExpr(ConcreteDeclRef defaultArgsOwner,
45454549
unsigned paramIndex, SourceLoc loc, Type Ty,
@@ -4574,6 +4578,16 @@ class DefaultArgumentExpr final : public Expr {
45744578
/// argument must be written explicitly at the call-site.
45754579
ActorIsolation getRequiredIsolation() const;
45764580

4581+
/// Whether this default argument is evaluated asynchronously because
4582+
/// it's isolated to the callee's isolation domain.
4583+
bool isImplicitlyAsync() const {
4584+
return implicitlyAsync;
4585+
}
4586+
4587+
void setImplicitlyAsync() {
4588+
implicitlyAsync = true;
4589+
}
4590+
45774591
static bool classof(const Expr *E) {
45784592
return E->getKind() == ExprKind::DefaultArgument;
45794593
}

Diff for: lib/AST/Decl.cpp

+4-11
Original file line numberDiff line numberDiff line change
@@ -10536,22 +10536,15 @@ ActorIsolation swift::getActorIsolationOfContext(
1053610536
return getActorIsolation(vd);
1053710537

1053810538
// In the context of the initializing or default-value expression of a
10539-
// stored property, the isolation varies between instance and type members:
10539+
// stored property:
1054010540
// - For a static stored property, the isolation matches the VarDecl.
1054110541
// Static properties are initialized upon first use, so the isolation
1054210542
// of the initializer must match the isolation required to access the
1054310543
// property.
10544-
// - For a field of a nominal type, the expression can require a specific
10545-
// actor isolation. That default expression may only be used from inits
10546-
// that meet the required isolation.
10544+
// - For a field of a nominal type, the expression can require the same
10545+
// actor isolation as the field itself. That default expression may only
10546+
// be used from inits that meet the required isolation.
1054710547
if (auto *var = dcToUse->getNonLocalVarDecl()) {
10548-
auto &ctx = dc->getASTContext();
10549-
if (ctx.LangOpts.hasFeature(Feature::IsolatedDefaultValues) &&
10550-
var->isInstanceMember() &&
10551-
!var->getAttrs().hasAttribute<LazyAttr>()) {
10552-
return ActorIsolation::forNonisolated();
10553-
}
10554-
1055510548
return getActorIsolation(var);
1055610549
}
1055710550

Diff for: lib/SILGen/SILGenApply.cpp

+13-7
Original file line numberDiff line numberDiff line change
@@ -2518,18 +2518,21 @@ class DelayedArgument {
25182518
AbstractionPattern origResultType;
25192519
ClaimedParamsRef paramsToEmit;
25202520
SILFunctionTypeRepresentation functionRepresentation;
2521-
2521+
bool implicitlyAsync;
2522+
25222523
DefaultArgumentStorage(SILLocation loc,
25232524
ConcreteDeclRef defaultArgsOwner,
25242525
unsigned destIndex,
25252526
CanType resultType,
25262527
AbstractionPattern origResultType,
25272528
ClaimedParamsRef paramsToEmit,
2528-
SILFunctionTypeRepresentation functionRepresentation)
2529+
SILFunctionTypeRepresentation functionRepresentation,
2530+
bool implicitlyAsync)
25292531
: loc(loc), defaultArgsOwner(defaultArgsOwner), destIndex(destIndex),
25302532
resultType(resultType), origResultType(origResultType),
25312533
paramsToEmit(paramsToEmit),
2532-
functionRepresentation(functionRepresentation)
2534+
functionRepresentation(functionRepresentation),
2535+
implicitlyAsync(implicitlyAsync)
25332536
{}
25342537
};
25352538
struct BorrowedLValueStorage {
@@ -2656,13 +2659,15 @@ class DelayedArgument {
26562659
CanType resultType,
26572660
AbstractionPattern origResultType,
26582661
ClaimedParamsRef params,
2659-
SILFunctionTypeRepresentation functionTypeRepresentation)
2662+
SILFunctionTypeRepresentation functionTypeRepresentation,
2663+
bool implicitlyAsync)
26602664
: Kind(DefaultArgument) {
26612665
Value.emplace<DefaultArgumentStorage>(Kind, loc, defaultArgsOwner,
26622666
destIndex,
26632667
resultType,
26642668
origResultType, params,
2665-
functionTypeRepresentation);
2669+
functionTypeRepresentation,
2670+
implicitlyAsync);
26662671
}
26672672

26682673
DelayedArgument(DelayedArgument &&other)
@@ -3261,7 +3266,7 @@ class ArgEmitter {
32613266
defArg->getParamIndex(),
32623267
substParamType, origParamType,
32633268
claimNextParameters(numParams),
3264-
Rep);
3269+
Rep, defArg->isImplicitlyAsync());
32653270
Args.push_back(ManagedValue());
32663271

32673272
maybeEmitForeignArgument();
@@ -4255,7 +4260,8 @@ void DelayedArgument::emitDefaultArgument(SILGenFunction &SGF,
42554260
auto value = SGF.emitApplyOfDefaultArgGenerator(info.loc,
42564261
info.defaultArgsOwner,
42574262
info.destIndex,
4258-
info.resultType);
4263+
info.resultType,
4264+
info.implicitlyAsync);
42594265

42604266
SmallVector<ManagedValue, 4> loweredArgs;
42614267
SmallVector<DelayedArgument, 4> delayedArgs;

Diff for: lib/SILGen/SILGenExpr.cpp

+18-1
Original file line numberDiff line numberDiff line change
@@ -2548,6 +2548,7 @@ SILGenFunction::emitApplyOfDefaultArgGenerator(SILLocation loc,
25482548
ConcreteDeclRef defaultArgsOwner,
25492549
unsigned destIndex,
25502550
CanType resultType,
2551+
bool implicitlyAsync,
25512552
SGFContext C) {
25522553
SILDeclRef generator
25532554
= SILDeclRef::getDefaultArgGenerator(defaultArgsOwner.getDecl(),
@@ -2578,8 +2579,24 @@ SILGenFunction::emitApplyOfDefaultArgGenerator(SILLocation loc,
25782579
emitCaptures(loc, generator, CaptureEmission::ImmediateApplication,
25792580
captures);
25802581

2582+
// The default argument might require the callee's isolation. If so,
2583+
// make sure to emit an actor hop.
2584+
//
2585+
// FIXME: Instead of hopping back and forth for each individual isolated
2586+
// default argument, we should emit one hop for all default arguments if
2587+
// any of them are isolated, and immediately enter the function after.
2588+
llvm::Optional<ActorIsolation> implicitActorHopTarget = llvm::None;
2589+
if (implicitlyAsync) {
2590+
auto *param = getParameterAt(defaultArgsOwner.getDecl(), destIndex);
2591+
auto isolation = param->getInitializerIsolation();
2592+
if (isolation.isActorIsolated()) {
2593+
implicitActorHopTarget = isolation;
2594+
}
2595+
}
2596+
25812597
return emitApply(std::move(resultPtr), std::move(argScope), loc, fnRef, subs,
2582-
captures, calleeTypeInfo, ApplyOptions(), C, llvm::None);
2598+
captures, calleeTypeInfo, ApplyOptions(), C,
2599+
implicitActorHopTarget);
25832600
}
25842601

25852602
RValue SILGenFunction::emitApplyOfStoredPropertyInitializer(

Diff for: lib/SILGen/SILGenFunction.h

+1
Original file line numberDiff line numberDiff line change
@@ -1907,6 +1907,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
19071907
ConcreteDeclRef defaultArgsOwner,
19081908
unsigned destIndex,
19091909
CanType resultType,
1910+
bool implicitlyAsync,
19101911
SGFContext C = SGFContext());
19111912

19121913
RValue emitApplyOfStoredPropertyInitializer(

Diff for: lib/Sema/TypeCheckConcurrency.cpp

+18-8
Original file line numberDiff line numberDiff line change
@@ -1921,7 +1921,17 @@ bool swift::diagnoseApplyArgSendability(ApplyExpr *apply, const DeclContext *dec
19211921
// Determine the type of the argument, ignoring any implicit
19221922
// conversions that could have stripped sendability.
19231923
if (Expr *argExpr = arg.getExpr()) {
1924-
argType = argExpr->findOriginalType();
1924+
argType = argExpr->findOriginalType();
1925+
1926+
// If this is a default argument expression, don't check Sendability
1927+
// if the argument is evaluated in the callee's isolation domain.
1928+
if (auto *defaultExpr = dyn_cast<DefaultArgumentExpr>(argExpr)) {
1929+
auto argIsolation = defaultExpr->getRequiredIsolation();
1930+
auto calleeIsolation = isolationCrossing->getCalleeIsolation();
1931+
if (argIsolation == calleeIsolation) {
1932+
continue;
1933+
}
1934+
}
19251935
}
19261936
}
19271937

@@ -2180,8 +2190,10 @@ namespace {
21802190
// recieve isolation from its decl context), then the expression cannot
21812191
// require a different isolation.
21822192
for (auto *dc : contextStack) {
2183-
if (!infersIsolationFromContext(dc))
2193+
if (!infersIsolationFromContext(dc)) {
2194+
requiredIsolation.clear();
21842195
return false;
2196+
}
21852197

21862198
// To refine the required isolation, the existing requirement
21872199
// must either be 'nonisolated' or exactly the same as the
@@ -2195,6 +2207,7 @@ namespace {
21952207
requiredIsolationLoc,
21962208
diag::conflicting_default_argument_isolation,
21972209
isolation->second, refinedIsolation);
2210+
requiredIsolation.clear();
21982211
return true;
21992212
}
22002213
}
@@ -2205,8 +2218,8 @@ namespace {
22052218
void checkDefaultArgument(DefaultArgumentExpr *expr) {
22062219
// Check the context isolation against the required isolation for
22072220
// evaluating the default argument synchronously. If the default
2208-
// argument must be evaluated asynchronously, it must be written
2209-
// explicitly in the argument list with 'await'.
2221+
// argument must be evaluated asynchronously, record that in the
2222+
// expression node.
22102223
auto requiredIsolation = expr->getRequiredIsolation();
22112224
auto contextIsolation = getInnermostIsolatedContext(
22122225
getDeclContext(), getClosureActorIsolation);
@@ -2227,10 +2240,7 @@ namespace {
22272240
break;
22282241
}
22292242

2230-
auto &ctx = getDeclContext()->getASTContext();
2231-
ctx.Diags.diagnose(
2232-
expr->getLoc(), diag::isolated_default_argument,
2233-
requiredIsolation, contextIsolation);
2243+
expr->setImplicitlyAsync();
22342244
}
22352245

22362246
/// Check closure captures for Sendable violations.

Diff for: lib/Sema/TypeCheckDeclPrimary.cpp

+12-1
Original file line numberDiff line numberDiff line change
@@ -1031,7 +1031,18 @@ static void checkDefaultArguments(ParameterList *params) {
10311031
for (auto *param : *params) {
10321032
auto ifacety = param->getInterfaceType();
10331033
auto *expr = param->getTypeCheckedDefaultExpr();
1034-
(void)param->getInitializerIsolation();
1034+
1035+
// If the default argument has isolation, it must match the
1036+
// isolation of the decl context.
1037+
auto defaultArgIsolation = param->getInitializerIsolation();
1038+
if (defaultArgIsolation.isActorIsolated()) {
1039+
auto *dc = param->getDeclContext();
1040+
auto enclosingIsolation = getActorIsolationOfContext(dc);
1041+
if (enclosingIsolation != defaultArgIsolation) {
1042+
param->diagnose(diag::isolated_default_argument_context,
1043+
defaultArgIsolation, enclosingIsolation);
1044+
}
1045+
}
10351046

10361047
if (!ifacety->hasPlaceholder()) {
10371048
continue;

Diff for: test/Concurrency/isolated_default_arguments.swift

+22-27
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,28 @@ 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}}
24+
func mainActorDefaultArgInvalid(value: Int = requiresMainActor()) {}
25+
26+
func mainActorClosureInvalid(
27+
closure: () -> Int = { // expected-error {{main actor-isolated default argument in a nonisolated context}}
28+
requiresMainActor()
29+
}
30+
) {}
31+
32+
// expected-note@+2 {{calls to global function 'mainActorDefaultArg(value:)' from outside of its actor context are implicitly asynchronous}}
33+
@MainActor
2334
func mainActorDefaultArg(value: Int = requiresMainActor()) {}
2435

25-
func mainActorClosure(
36+
// expected-note@+1 {{calls to global function 'mainActorClosure(closure:)' from outside of its actor context are implicitly asynchronous}}
37+
@MainActor func mainActorClosure(
2638
closure: () -> Int = {
2739
requiresMainActor()
2840
}
2941
) {}
3042

3143
func mainActorClosureCall(
32-
closure: Int = {
44+
closure: Int = { // expected-error {{main actor-isolated default argument in a nonisolated context}}
3345
requiresMainActor()
3446
}()
3547
) {}
@@ -40,40 +52,29 @@ func mainActorClosureCall(
4052
mainActorClosureCall()
4153
}
4254

55+
// expected-note@+1 2 {{add '@MainActor' to make global function 'nonisolatedCaller()' part of global actor 'MainActor'}}
4356
func nonisolatedCaller() {
44-
// expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}}
57+
// expected-error@+1 {{call to main actor-isolated global function 'mainActorDefaultArg(value:)' in a synchronous nonisolated context}}
4558
mainActorDefaultArg()
4659

47-
// FIXME: confusing error message.
48-
// expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}}
60+
// expected-error@+1 {{call to main actor-isolated global function 'mainActorClosure(closure:)' in a synchronous nonisolated context}}
4961
mainActorClosure()
50-
51-
// expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}}
52-
mainActorClosureCall()
5362
}
5463

5564
func nonisolatedAsyncCaller() async {
56-
// expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}}
65+
// expected-error@+2 {{expression is 'async' but is not marked with 'await'}}
66+
// expected-note@+1 {{calls to global function 'mainActorDefaultArg(value:)' from outside of its actor context are implicitly asynchronous}}
5767
mainActorDefaultArg()
5868

59-
// expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}}
69+
// expected-error@+2 {{expression is 'async' but is not marked with 'await'}}
70+
// expected-note@+1 {{calls to global function 'mainActorClosure(closure:)' from outside of its actor context are implicitly asynchronous}}
6071
mainActorClosure()
6172

62-
// expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}}
63-
mainActorClosureCall()
64-
6573
await mainActorDefaultArg(value: requiresMainActor())
6674

67-
// 'await' doesn't help closures in default arguments; the calling context needs a matching
68-
// actor isolation for that isolation to be inferred for the closure value.
69-
70-
// expected-error@+2 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}}
71-
// expected-warning@+1 {{no 'async' operations occur within 'await' expression}}
7275
await mainActorClosure()
7376

74-
// expected-error@+2 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}}
75-
// expected-warning@+1 {{no 'async' operations occur within 'await' expression}}
76-
await mainActorClosureCall()
77+
mainActorClosureCall()
7778
}
7879

7980
func conflictingIsolationDefaultArg(
@@ -156,32 +157,26 @@ struct S3 {
156157
func initializeFromMainActor() {
157158
_ = S1(required: 10)
158159

159-
// expected-error@+1 {{global actor 'SomeGlobalActor'-isolated default argument cannot be synchronously evaluated from a main actor-isolated context}}
160160
_ = S2(required: 10)
161161
}
162162

163163
@SomeGlobalActor
164164
func initializeFromSomeGlobalActor() {
165-
// expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a global actor 'SomeGlobalActor'-isolated context}}
166165
_ = S1(required: 10)
167166

168167
_ = S2(required: 10)
169168
}
170169

171170
func initializeFromNonisolated() {
172-
// expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}}
173171
_ = S1(required: 10)
174172

175-
// expected-error@+1 {{global actor 'SomeGlobalActor'-isolated default argument cannot be synchronously evaluated from a nonisolated context}}
176173
_ = S2(required: 10)
177174
}
178175

179176
extension A {
180177
func initializeFromActorInstance() {
181-
// expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a actor-isolated context}}
182178
_ = S1(required: 10)
183179

184-
// expected-error@+1 {{global actor 'SomeGlobalActor'-isolated default argument cannot be synchronously evaluated from a actor-isolated context}}
185180
_ = S2(required: 10)
186181
}
187182
}

Diff for: test/Concurrency/isolated_default_arguments_serialized.swift

-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ func mainActorCaller() {
1616
}
1717

1818
func nonisolatedCaller() async {
19-
// expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}}
2019
await useMainActorDefault()
2120

2221
await useMainActorDefault(mainActorDefaultArg())

0 commit comments

Comments
 (0)