Skip to content

Commit 5ae4b57

Browse files
authored
Merge pull request #70839 from hborla/5.10-enable-se-0411
[5.10][SE-0411] Promote `IsolatedDefaultValues` from an experimental feature to an upcoming feature.
2 parents 6a90011 + aaa1c85 commit 5ae4b57

30 files changed

+538
-145
lines changed

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,30 @@
33
> **Note**\
44
> This is in reverse chronological order, so newer entries are added to the top.
55
6+
## Swift 5.10
7+
8+
* [SE-0411][]:
9+
10+
Default value expressions can now have the same isolation as the enclosing
11+
function or the corresponding stored property:
12+
13+
```swift
14+
@MainActor
15+
func requiresMainActor() -> Int { ... }
16+
17+
class C {
18+
@MainActor
19+
var x: Int = requiresMainActor()
20+
}
21+
22+
@MainActor func defaultArg(value: Int = requiresMainActor()) { ... }
23+
```
24+
25+
For isolated default values of stored properties, the implicit initialization
26+
only happens in the body of an `init` with the same isolation. This closes
27+
an important data-race safety hole where global-actor-isolated default values
28+
could inadvertently run synchronously from outside the actor.
29+
630
## Swift 5.9.2
731

832
* [SE-0407][]:
@@ -9846,6 +9870,7 @@ using the `.dynamicType` member to retrieve the type of an expression should mig
98469870
[SE-0394]: https://github.com/apple/swift-evolution/blob/main/proposals/0394-swiftpm-expression-macros.md
98479871
[SE-0397]: https://github.com/apple/swift-evolution/blob/main/proposals/0397-freestanding-declaration-macros.md
98489872
[SE-0407]: https://github.com/apple/swift-evolution/blob/main/proposals/0407-member-macro-conformances.md
9873+
[SE-0411]: https://github.com/apple/swift-evolution/blob/main/proposals/0411-isolated-default-values.md
98499874
[#64927]: <https://github.com/apple/swift/issues/64927>
98509875
[#42697]: <https://github.com/apple/swift/issues/42697>
98519876
[#42728]: <https://github.com/apple/swift/issues/42728>

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5200,9 +5200,14 @@ 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(conflicting_stored_property_isolation,none,
5204+
"%select{default|memberwise}0 initializer for %1 cannot be both %2 and %3",
5205+
(bool, Type, ActorIsolation, ActorIsolation))
5206+
NOTE(property_requires_actor,none,
5207+
"initializer for %0 %1 is %2",
5208+
(DescriptiveDeclKind, Identifier, ActorIsolation))
5209+
ERROR(isolated_default_argument_context,none,
5210+
"%0 default value in a %1 context",
52065211
(ActorIsolation, ActorIsolation))
52075212
ERROR(conflicting_default_argument_isolation,none,
52085213
"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
}

include/swift/Basic/Features.def

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ UPCOMING_FEATURE(BareSlashRegexLiterals, 354, 6)
118118
UPCOMING_FEATURE(DeprecateApplicationMain, 383, 6)
119119
UPCOMING_FEATURE(ImportObjcForwardDeclarations, 384, 6)
120120
UPCOMING_FEATURE(DisableOutwardActorInference, 401, 6)
121+
UPCOMING_FEATURE(IsolatedDefaultValues, 411, 6)
121122
UPCOMING_FEATURE(GlobalConcurrency, 412, 6)
122123

123124
UPCOMING_FEATURE(ExistentialAny, 335, 7)
@@ -222,9 +223,6 @@ EXPERIMENTAL_FEATURE(StrictConcurrency, true)
222223
/// Defer Sendable checking to SIL diagnostic phase.
223224
EXPERIMENTAL_FEATURE(SendNonSendable, false)
224225

225-
/// Allow default values to require isolation at the call-site.
226-
EXPERIMENTAL_FEATURE(IsolatedDefaultValues, false)
227-
228226
/// Enable extended callbacks (with additional parameters) to be used when the
229227
/// "playground transform" is enabled.
230228
EXPERIMENTAL_FEATURE(PlaygroundExtendedCallbacks, true)

lib/AST/Decl.cpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10289,6 +10289,7 @@ ActorIsolation swift::getActorIsolationOfContext(
1028910289
DeclContext *dc,
1029010290
llvm::function_ref<ActorIsolation(AbstractClosureExpr *)>
1029110291
getClosureActorIsolation) {
10292+
auto &ctx = dc->getASTContext();
1029210293
auto dcToUse = dc;
1029310294
// Defer bodies share actor isolation of their enclosing context.
1029410295
if (auto FD = dyn_cast<FuncDecl>(dcToUse)) {
@@ -10300,21 +10301,21 @@ ActorIsolation swift::getActorIsolationOfContext(
1030010301
return getActorIsolation(vd);
1030110302

1030210303
// In the context of the initializing or default-value expression of a
10303-
// stored property, the isolation varies between instance and type members:
10304+
// stored property:
1030410305
// - For a static stored property, the isolation matches the VarDecl.
1030510306
// Static properties are initialized upon first use, so the isolation
1030610307
// of the initializer must match the isolation required to access the
1030710308
// 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.
10309+
// - For a field of a nominal type, the expression can require the same
10310+
// actor isolation as the field itself. That default expression may only
10311+
// be used from inits that meet the required isolation.
1031110312
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-
}
10313+
// If IsolatedDefaultValues are enabled, treat this context as having
10314+
// unspecified isolation. We'll compute the required isolation for
10315+
// the initializer and validate that it matches the isolation of the
10316+
// var itself in the DefaultInitializerIsolation request.
10317+
if (ctx.LangOpts.hasFeature(Feature::IsolatedDefaultValues))
10318+
return ActorIsolation::forUnspecified();
1031810319

1031910320
return getActorIsolation(var);
1032010321
}

lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -976,8 +976,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
976976
Opts.StrictConcurrencyLevel = StrictConcurrency::Minimal;
977977
}
978978

979-
// StrictConcurrency enables all data-race safety upcoming features.
980-
if (Opts.hasFeature(Feature::StrictConcurrency)) {
979+
// StrictConcurrency::Complete enables all data-race safety features.
980+
if (Opts.StrictConcurrencyLevel == StrictConcurrency::Complete) {
981+
Opts.Features.insert(Feature::IsolatedDefaultValues);
981982
Opts.Features.insert(Feature::GlobalConcurrency);
982983
}
983984

lib/SILGen/SILGenApply.cpp

Lines changed: 110 additions & 8 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)
@@ -2690,6 +2695,28 @@ class DelayedArgument {
26902695
return LV().Loc;
26912696
}
26922697

2698+
bool isDefaultArg() const {
2699+
return Kind == DefaultArgument;
2700+
}
2701+
2702+
SILLocation getDefaultArgLoc() const {
2703+
assert(isDefaultArg());
2704+
auto storage = Value.get<DefaultArgumentStorage>(Kind);
2705+
return storage.loc;
2706+
}
2707+
2708+
llvm::Optional<ActorIsolation> getIsolation() const {
2709+
if (!isDefaultArg())
2710+
return llvm::None;
2711+
2712+
auto storage = Value.get<DefaultArgumentStorage>(Kind);
2713+
if (!storage.implicitlyAsync)
2714+
return llvm::None;
2715+
2716+
auto callee = storage.defaultArgsOwner.getDecl();
2717+
return getActorIsolation(callee);
2718+
}
2719+
26932720
void emit(SILGenFunction &SGF, SmallVectorImpl<ManagedValue> &args,
26942721
size_t &argIndex) {
26952722
switch (Kind) {
@@ -2915,6 +2942,31 @@ static void emitDelayedArguments(SILGenFunction &SGF,
29152942
MutableArrayRef<SmallVector<ManagedValue, 4>> args) {
29162943
assert(!delayedArgs.empty());
29172944

2945+
// If any of the delayed arguments are isolated default arguments,
2946+
// argument evaluation happens in the following order:
2947+
//
2948+
// 1. Left-to-right evalution of explicit r-value arguments
2949+
// 2. Left-to-right evaluation of formal access arguments
2950+
// 3. Hop to the callee's isolation domain
2951+
// 4. Left-to-right evaluation of default arguments
2952+
2953+
// So, if any delayed arguments are isolated, all default arguments
2954+
// are collected during the first pass over the delayed arguments,
2955+
// and emitted separately after a hop to the callee's isolation domain.
2956+
2957+
llvm::Optional<ActorIsolation> defaultArgIsolation;
2958+
for (auto &arg : delayedArgs) {
2959+
if (auto isolation = arg.getIsolation()) {
2960+
defaultArgIsolation = isolation;
2961+
break;
2962+
}
2963+
}
2964+
2965+
SmallVector<std::tuple<
2966+
/*delayedArgIt*/decltype(delayedArgs)::iterator,
2967+
/*siteArgsIt*/decltype(args)::iterator,
2968+
/*index*/size_t>, 2> isolatedArgs;
2969+
29182970
SmallVector<std::pair<SILValue, SILLocation>, 4> emittedInoutArgs;
29192971
auto delayedNext = delayedArgs.begin();
29202972

@@ -2923,7 +2975,8 @@ static void emitDelayedArguments(SILGenFunction &SGF,
29232975
// wherever there's a delayed argument to insert.
29242976
//
29252977
// Note that this also begins the formal accesses in evaluation order.
2926-
for (auto &siteArgs : args) {
2978+
for (auto argsIt = args.begin(); argsIt != args.end(); ++argsIt) {
2979+
auto &siteArgs = *argsIt;
29272980
// NB: siteArgs.size() may change during iteration
29282981
for (size_t i = 0; i < siteArgs.size(); ) {
29292982
auto &siteArg = siteArgs[i];
@@ -2936,6 +2989,15 @@ static void emitDelayedArguments(SILGenFunction &SGF,
29362989
assert(delayedNext != delayedArgs.end());
29372990
auto &delayedArg = *delayedNext;
29382991

2992+
if (defaultArgIsolation && delayedArg.isDefaultArg()) {
2993+
isolatedArgs.push_back(std::make_tuple(delayedNext, argsIt, i));
2994+
if (++delayedNext == delayedArgs.end()) {
2995+
goto done;
2996+
} else {
2997+
continue;
2998+
}
2999+
}
3000+
29393001
// Emit the delayed argument and replace it in the arguments array.
29403002
delayedArg.emit(SGF, siteArgs, i);
29413003

@@ -2956,6 +3018,45 @@ static void emitDelayedArguments(SILGenFunction &SGF,
29563018

29573019
done:
29583020

3021+
if (defaultArgIsolation) {
3022+
assert(SGF.F.isAsync());
3023+
assert(!isolatedArgs.empty());
3024+
3025+
auto &firstArg = *std::get<0>(isolatedArgs[0]);
3026+
auto loc = firstArg.getDefaultArgLoc();
3027+
3028+
SILValue executor;
3029+
switch (*defaultArgIsolation) {
3030+
case ActorIsolation::GlobalActor:
3031+
case ActorIsolation::GlobalActorUnsafe:
3032+
executor = SGF.emitLoadGlobalActorExecutor(
3033+
defaultArgIsolation->getGlobalActor());
3034+
break;
3035+
3036+
case ActorIsolation::ActorInstance:
3037+
llvm_unreachable("default arg cannot be actor instance isolated");
3038+
3039+
case ActorIsolation::Unspecified:
3040+
case ActorIsolation::Nonisolated:
3041+
case ActorIsolation::NonisolatedUnsafe:
3042+
llvm_unreachable("Not isolated");
3043+
}
3044+
3045+
// Hop to the target isolation domain once to evaluate all
3046+
// default arguments.
3047+
SGF.emitHopToTargetExecutor(loc, executor);
3048+
3049+
size_t argsEmitted = 0;
3050+
for (auto &isolatedArg : isolatedArgs) {
3051+
auto &delayedArg = *std::get<0>(isolatedArg);
3052+
auto &siteArgs = *std::get<1>(isolatedArg);
3053+
auto argIndex = std::get<2>(isolatedArg) + argsEmitted;
3054+
auto origIndex = argIndex;
3055+
delayedArg.emit(SGF, siteArgs, argIndex);
3056+
argsEmitted += (argIndex - origIndex);
3057+
}
3058+
}
3059+
29593060
// Check to see if we have multiple inout arguments which obviously
29603061
// alias. Note that we could do this in a later SILDiagnostics pass
29613062
// as well: this would be stronger (more equivalences exposed) but
@@ -3261,7 +3362,7 @@ class ArgEmitter {
32613362
defArg->getParamIndex(),
32623363
substParamType, origParamType,
32633364
claimNextParameters(numParams),
3264-
Rep);
3365+
Rep, defArg->isImplicitlyAsync());
32653366
Args.push_back(ManagedValue());
32663367

32673368
maybeEmitForeignArgument();
@@ -4255,7 +4356,8 @@ void DelayedArgument::emitDefaultArgument(SILGenFunction &SGF,
42554356
auto value = SGF.emitApplyOfDefaultArgGenerator(info.loc,
42564357
info.defaultArgsOwner,
42574358
info.destIndex,
4258-
info.resultType);
4359+
info.resultType,
4360+
info.implicitlyAsync);
42594361

42604362
SmallVector<ManagedValue, 4> loweredArgs;
42614363
SmallVector<DelayedArgument, 4> delayedArgs;

lib/SILGen/SILGenExpr.cpp

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

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(

0 commit comments

Comments
 (0)