Skip to content

Commit 49d6399

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

11 files changed

+122
-60
lines changed

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",

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
}

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

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;

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(

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(

lib/Sema/CodeSynthesis.cpp

+8-1
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,14 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
339339

340340
if (ICK == ImplicitConstructorKind::Memberwise) {
341341
ctor->setIsMemberwiseInitializer();
342-
addNonIsolatedToSynthesized(decl, ctor);
342+
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.
347+
if (!ctx.LangOpts.hasFeature(Feature::IsolatedDefaultValues)) {
348+
addNonIsolatedToSynthesized(decl, ctor);
349+
}
343350
}
344351

345352
// If we are defining a default initializer for a class that has a superclass,

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.

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;

0 commit comments

Comments
 (0)