Skip to content

Commit cbdb892

Browse files
authored
Merge pull request #69391 from hborla/isolated-default-value-revision
[Concurrency] Allow isolated default arguments to be used from across isolation boundaries.
2 parents 50a98d3 + f448684 commit cbdb892

12 files changed

+147
-60
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5337,9 +5337,8 @@ ERROR(distributed_actor_isolated_non_self_reference,none,
53375337
"distributed actor-isolated %kind0 can not be accessed from a "
53385338
"non-isolated context",
53395339
(const ValueDecl *))
5340-
ERROR(isolated_default_argument,none,
5341-
"%0 default argument cannot be synchronously evaluated from a "
5342-
"%1 context",
5340+
ERROR(isolated_default_argument_context,none,
5341+
"%0 default argument in a %1 context",
53435342
(ActorIsolation, ActorIsolation))
53445343
ERROR(conflicting_default_argument_isolation,none,
53455344
"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
@@ -4566,6 +4566,10 @@ class DefaultArgumentExpr final : public Expr {
45664566
/// default expression.
45674567
PointerUnion<DeclContext *, Expr *> ContextOrCallerSideExpr;
45684568

4569+
/// Whether this default argument is evaluated asynchronously because
4570+
/// it's isolated to the callee's isolation domain.
4571+
bool implicitlyAsync = false;
4572+
45694573
public:
45704574
explicit DefaultArgumentExpr(ConcreteDeclRef defaultArgsOwner,
45714575
unsigned paramIndex, SourceLoc loc, Type Ty,
@@ -4600,6 +4604,16 @@ class DefaultArgumentExpr final : public Expr {
46004604
/// argument must be written explicitly at the call-site.
46014605
ActorIsolation getRequiredIsolation() const;
46024606

4607+
/// Whether this default argument is evaluated asynchronously because
4608+
/// it's isolated to the callee's isolation domain.
4609+
bool isImplicitlyAsync() const {
4610+
return implicitlyAsync;
4611+
}
4612+
4613+
void setImplicitlyAsync() {
4614+
implicitlyAsync = true;
4615+
}
4616+
46034617
static bool classof(const Expr *E) {
46044618
return E->getKind() == ExprKind::DefaultArgument;
46054619
}

lib/AST/Decl.cpp

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

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

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
@@ -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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 8 additions & 1 deletion
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

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1958,7 +1958,17 @@ bool swift::diagnoseApplyArgSendability(ApplyExpr *apply, const DeclContext *dec
19581958
// Determine the type of the argument, ignoring any implicit
19591959
// conversions that could have stripped sendability.
19601960
if (Expr *argExpr = arg.getExpr()) {
1961-
argType = argExpr->findOriginalType();
1961+
argType = argExpr->findOriginalType();
1962+
1963+
// If this is a default argument expression, don't check Sendability
1964+
// if the argument is evaluated in the callee's isolation domain.
1965+
if (auto *defaultExpr = dyn_cast<DefaultArgumentExpr>(argExpr)) {
1966+
auto argIsolation = defaultExpr->getRequiredIsolation();
1967+
auto calleeIsolation = isolationCrossing->getCalleeIsolation();
1968+
if (argIsolation == calleeIsolation) {
1969+
continue;
1970+
}
1971+
}
19621972
}
19631973
}
19641974

@@ -2217,8 +2227,10 @@ namespace {
22172227
// recieve isolation from its decl context), then the expression cannot
22182228
// require a different isolation.
22192229
for (auto *dc : contextStack) {
2220-
if (!infersIsolationFromContext(dc))
2230+
if (!infersIsolationFromContext(dc)) {
2231+
requiredIsolation.clear();
22212232
return false;
2233+
}
22222234

22232235
// To refine the required isolation, the existing requirement
22242236
// must either be 'nonisolated' or exactly the same as the
@@ -2232,6 +2244,7 @@ namespace {
22322244
requiredIsolationLoc,
22332245
diag::conflicting_default_argument_isolation,
22342246
isolation->second, refinedIsolation);
2247+
requiredIsolation.clear();
22352248
return true;
22362249
}
22372250
}
@@ -2242,8 +2255,8 @@ namespace {
22422255
void checkDefaultArgument(DefaultArgumentExpr *expr) {
22432256
// Check the context isolation against the required isolation for
22442257
// evaluating the default argument synchronously. If the default
2245-
// argument must be evaluated asynchronously, it must be written
2246-
// explicitly in the argument list with 'await'.
2258+
// argument must be evaluated asynchronously, record that in the
2259+
// expression node.
22472260
auto requiredIsolation = expr->getRequiredIsolation();
22482261
auto contextIsolation = getInnermostIsolatedContext(
22492262
getDeclContext(), getClosureActorIsolation);
@@ -2264,10 +2277,7 @@ namespace {
22642277
break;
22652278
}
22662279

2267-
auto &ctx = getDeclContext()->getASTContext();
2268-
ctx.Diags.diagnose(
2269-
expr->getLoc(), diag::isolated_default_argument,
2270-
requiredIsolation, contextIsolation);
2280+
expr->setImplicitlyAsync();
22712281
}
22722282

22732283
/// Check closure captures for Sendable violations.

lib/Sema/TypeCheckDeclPrimary.cpp

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

10371048
if (!ifacety->hasPlaceholder()) {
10381049
continue;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %target-swift-emit-silgen -I %t -disable-availability-checking -strict-concurrency=complete -enable-experimental-feature IsolatedDefaultValues -parse-as-library %s | %FileCheck %s
2+
3+
// REQUIRES: concurrency
4+
// REQUIRES: asserts
5+
6+
@MainActor
7+
func requiresMainActor() -> Int { 0 }
8+
9+
@MainActor
10+
func mainActorDefaultArg(value: Int = requiresMainActor()) {}
11+
12+
// CHECK-LABEL: sil hidden [ossa] @$s30isolated_default_argument_eval15mainActorCalleryyF
13+
@MainActor func mainActorCaller() {
14+
mainActorDefaultArg()
15+
}
16+
17+
// CHECK-LABEL: sil hidden [ossa] @$s30isolated_default_argument_eval22nonisolatedAsyncCalleryyYaF
18+
func nonisolatedAsyncCaller() async {
19+
// CHECK: hop_to_executor {{.*}} : $Optional<Builtin.Executor>
20+
// CHECK: [[GETARG:%[0-9]+]] = function_ref @$s30isolated_default_argument_eval19mainActorDefaultArg5valueySi_tFfA_
21+
// CHECK: hop_to_executor {{.*}} : $MainActor
22+
// CHECK-NEXT: apply [[GETARG]]()
23+
// CHECK-NEXT: hop_to_executor {{.*}} : $Optional<Builtin.Executor>
24+
await mainActorDefaultArg()
25+
}

0 commit comments

Comments
 (0)