Skip to content

Commit 12bf533

Browse files
committed
[Concurrency] Allow isolated default arguments to be used from across isolation
boundaries. (cherry picked from commit 49d6399)
1 parent 8be2c83 commit 12bf533

11 files changed

+121
-59
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5200,9 +5200,8 @@ ERROR(distributed_actor_isolated_non_self_reference,none,
52005200
"distributed actor-isolated %kind0 can not be accessed from a "
52015201
"non-isolated context",
52025202
(const ValueDecl *))
5203-
ERROR(isolated_default_argument,none,
5204-
"%0 default argument cannot be synchronously evaluated from a "
5205-
"%1 context",
5203+
ERROR(isolated_default_argument_context,none,
5204+
"%0 default argument in a %1 context",
52065205
(ActorIsolation, ActorIsolation))
52075206
ERROR(conflicting_default_argument_isolation,none,
52085207
"default argument cannot be both %0 and %1",

include/swift/AST/Expr.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4513,6 +4513,10 @@ class DefaultArgumentExpr final : public Expr {
45134513
/// default expression.
45144514
PointerUnion<DeclContext *, Expr *> ContextOrCallerSideExpr;
45154515

4516+
/// Whether this default argument is evaluated asynchronously because
4517+
/// it's isolated to the callee's isolation domain.
4518+
bool implicitlyAsync = false;
4519+
45164520
public:
45174521
explicit DefaultArgumentExpr(ConcreteDeclRef defaultArgsOwner,
45184522
unsigned paramIndex, SourceLoc loc, Type Ty,
@@ -4547,6 +4551,16 @@ class DefaultArgumentExpr final : public Expr {
45474551
/// argument must be written explicitly at the call-site.
45484552
ActorIsolation getRequiredIsolation() const;
45494553

4554+
/// Whether this default argument is evaluated asynchronously because
4555+
/// it's isolated to the callee's isolation domain.
4556+
bool isImplicitlyAsync() const {
4557+
return implicitlyAsync;
4558+
}
4559+
4560+
void setImplicitlyAsync() {
4561+
implicitlyAsync = true;
4562+
}
4563+
45504564
static bool classof(const Expr *E) {
45514565
return E->getKind() == ExprKind::DefaultArgument;
45524566
}

lib/AST/Decl.cpp

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10300,22 +10300,15 @@ ActorIsolation swift::getActorIsolationOfContext(
1030010300
return getActorIsolation(vd);
1030110301

1030210302
// In the context of the initializing or default-value expression of a
10303-
// stored property, the isolation varies between instance and type members:
10303+
// stored property:
1030410304
// - For a static stored property, the isolation matches the VarDecl.
1030510305
// Static properties are initialized upon first use, so the isolation
1030610306
// of the initializer must match the isolation required to access the
1030710307
// property.
10308-
// - For a field of a nominal type, the expression can require a specific
10309-
// actor isolation. That default expression may only be used from inits
10310-
// that meet the required isolation.
10308+
// - For a field of a nominal type, the expression can require the same
10309+
// actor isolation as the field itself. That default expression may only
10310+
// be used from inits that meet the required isolation.
1031110311
if (auto *var = dcToUse->getNonLocalVarDecl()) {
10312-
auto &ctx = dc->getASTContext();
10313-
if (ctx.LangOpts.hasFeature(Feature::IsolatedDefaultValues) &&
10314-
var->isInstanceMember() &&
10315-
!var->getAttrs().hasAttribute<LazyAttr>()) {
10316-
return ActorIsolation::forNonisolated(/*unsafe=*/false);
10317-
}
10318-
1031910312
return getActorIsolation(var);
1032010313
}
1032110314

lib/SILGen/SILGenApply.cpp

Lines changed: 13 additions & 7 deletions
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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2533,6 +2533,7 @@ SILGenFunction::emitApplyOfDefaultArgGenerator(SILLocation loc,
25332533
ConcreteDeclRef defaultArgsOwner,
25342534
unsigned destIndex,
25352535
CanType resultType,
2536+
bool implicitlyAsync,
25362537
SGFContext C) {
25372538
SILDeclRef generator
25382539
= SILDeclRef::getDefaultArgGenerator(defaultArgsOwner.getDecl(),
@@ -2563,8 +2564,24 @@ SILGenFunction::emitApplyOfDefaultArgGenerator(SILLocation loc,
25632564
emitCaptures(loc, generator, CaptureEmission::ImmediateApplication,
25642565
captures);
25652566

2567+
// The default argument might require the callee's isolation. If so,
2568+
// make sure to emit an actor hop.
2569+
//
2570+
// FIXME: Instead of hopping back and forth for each individual isolated
2571+
// default argument, we should emit one hop for all default arguments if
2572+
// any of them are isolated, and immediately enter the function after.
2573+
llvm::Optional<ActorIsolation> implicitActorHopTarget = llvm::None;
2574+
if (implicitlyAsync) {
2575+
auto *param = getParameterAt(defaultArgsOwner.getDecl(), destIndex);
2576+
auto isolation = param->getInitializerIsolation();
2577+
if (isolation.isActorIsolated()) {
2578+
implicitActorHopTarget = isolation;
2579+
}
2580+
}
2581+
25662582
return emitApply(std::move(resultPtr), std::move(argScope), loc, fnRef, subs,
2567-
captures, calleeTypeInfo, ApplyOptions(), C, llvm::None);
2583+
captures, calleeTypeInfo, ApplyOptions(), C,
2584+
implicitActorHopTarget);
25682585
}
25692586

25702587
RValue SILGenFunction::emitApplyOfStoredPropertyInitializer(

lib/SILGen/SILGenFunction.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1905,6 +1905,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
19051905
ConcreteDeclRef defaultArgsOwner,
19061906
unsigned destIndex,
19071907
CanType resultType,
1908+
bool implicitlyAsync,
19081909
SGFContext C = SGFContext());
19091910

19101911
RValue emitApplyOfStoredPropertyInitializer(

lib/Sema/CodeSynthesis.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,14 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
338338

339339
if (ICK == ImplicitConstructorKind::Memberwise) {
340340
ctor->setIsMemberwiseInitializer();
341-
addNonIsolatedToSynthesized(decl, ctor);
341+
342+
// FIXME: If 'IsolatedDefaultValues' is enabled, the memberwise init
343+
// should be 'nonisolated' if none of the memberwise-initialized properties
344+
// are global actor isolated and have non-Sendable type, and none of the
345+
// initial values require global actor isolation.
346+
if (!ctx.LangOpts.hasFeature(Feature::IsolatedDefaultValues)) {
347+
addNonIsolatedToSynthesized(decl, ctor);
348+
}
342349
}
343350

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

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2001,6 +2001,16 @@ bool swift::diagnoseApplyArgSendability(ApplyExpr *apply, const DeclContext *dec
20012001
if (Expr *argExpr = arg.getExpr()) {
20022002
checkSendable = shouldCheckSendable(argExpr);
20032003
argType = argExpr->findOriginalType();
2004+
2005+
// If this is a default argument expression, don't check Sendability
2006+
// if the argument is evaluated in the callee's isolation domain.
2007+
if (auto *defaultExpr = dyn_cast<DefaultArgumentExpr>(argExpr)) {
2008+
auto argIsolation = defaultExpr->getRequiredIsolation();
2009+
auto calleeIsolation = isolationCrossing->getCalleeIsolation();
2010+
if (argIsolation == calleeIsolation) {
2011+
continue;
2012+
}
2013+
}
20042014
}
20052015
}
20062016

@@ -2260,8 +2270,10 @@ namespace {
22602270
// recieve isolation from its decl context), then the expression cannot
22612271
// require a different isolation.
22622272
for (auto *dc : contextStack) {
2263-
if (!infersIsolationFromContext(dc))
2273+
if (!infersIsolationFromContext(dc)) {
2274+
requiredIsolation.clear();
22642275
return false;
2276+
}
22652277

22662278
// To refine the required isolation, the existing requirement
22672279
// must either be 'nonisolated' or exactly the same as the
@@ -2275,6 +2287,7 @@ namespace {
22752287
requiredIsolationLoc,
22762288
diag::conflicting_default_argument_isolation,
22772289
isolation->second, refinedIsolation);
2290+
requiredIsolation.clear();
22782291
return true;
22792292
}
22802293
}
@@ -2285,8 +2298,8 @@ namespace {
22852298
void checkDefaultArgument(DefaultArgumentExpr *expr) {
22862299
// Check the context isolation against the required isolation for
22872300
// evaluating the default argument synchronously. If the default
2288-
// argument must be evaluated asynchronously, it must be written
2289-
// explicitly in the argument list with 'await'.
2301+
// argument must be evaluated asynchronously, record that in the
2302+
// expression node.
22902303
auto requiredIsolation = expr->getRequiredIsolation();
22912304
auto contextIsolation = getInnermostIsolatedContext(
22922305
getDeclContext(), getClosureActorIsolation);
@@ -2308,10 +2321,7 @@ namespace {
23082321
break;
23092322
}
23102323

2311-
auto &ctx = getDeclContext()->getASTContext();
2312-
ctx.Diags.diagnose(
2313-
expr->getLoc(), diag::isolated_default_argument,
2314-
requiredIsolation, contextIsolation);
2324+
expr->setImplicitlyAsync();
23152325
}
23162326

23172327
/// Check closure captures for Sendable violations.

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1085,7 +1085,18 @@ static void checkDefaultArguments(ParameterList *params) {
10851085
for (auto *param : *params) {
10861086
auto ifacety = param->getInterfaceType();
10871087
auto *expr = param->getTypeCheckedDefaultExpr();
1088-
(void)param->getInitializerIsolation();
1088+
1089+
// If the default argument has isolation, it must match the
1090+
// isolation of the decl context.
1091+
auto defaultArgIsolation = param->getInitializerIsolation();
1092+
if (defaultArgIsolation.isActorIsolated()) {
1093+
auto *dc = param->getDeclContext();
1094+
auto enclosingIsolation = getActorIsolationOfContext(dc);
1095+
if (enclosingIsolation != defaultArgIsolation) {
1096+
param->diagnose(diag::isolated_default_argument_context,
1097+
defaultArgIsolation, enclosingIsolation);
1098+
}
1099+
}
10891100

10901101
if (!ifacety->hasPlaceholder()) {
10911102
continue;

0 commit comments

Comments
 (0)