Skip to content

Commit 56f38c4

Browse files
committed
[concurrency] Add support in SILGen/SIL for emitting and calling implicit leading parameters for CallerInheritingIsolation isolation.
This does not change region isolation yet to recognize these as effectively nonisolated.
1 parent 5d4239a commit 56f38c4

File tree

7 files changed

+299
-19
lines changed

7 files changed

+299
-19
lines changed

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1565,6 +1565,7 @@ class DestructureInputs {
15651565
TypeConverter &TC;
15661566
const Conventions &Convs;
15671567
const ForeignInfo &Foreign;
1568+
std::optional<ActorIsolation> IsolationInfo;
15681569
struct ForeignSelfInfo {
15691570
AbstractionPattern OrigSelfParam;
15701571
AnyFunctionType::CanParam SubstSelfParam;
@@ -1577,9 +1578,10 @@ class DestructureInputs {
15771578
public:
15781579
DestructureInputs(TypeExpansionContext expansion, TypeConverter &TC,
15791580
const Conventions &conventions, const ForeignInfo &foreign,
1581+
std::optional<ActorIsolation> isolationInfo,
15801582
SmallVectorImpl<SILParameterInfo> &inputs)
15811583
: expansion(expansion), TC(TC), Convs(conventions), Foreign(foreign),
1582-
Inputs(inputs) {}
1584+
IsolationInfo(isolationInfo), Inputs(inputs) {}
15831585

15841586
void destructure(AbstractionPattern origType,
15851587
CanAnyFunctionType::CanParamArrayRef params,
@@ -1641,6 +1643,23 @@ class DestructureInputs {
16411643
};
16421644
}
16431645

1646+
// If we are an async function that is unspecified or nonisolated, insert an
1647+
// isolated parameter if NonIsolatedAsyncInheritsIsolationFromContext is
1648+
// enabled.
1649+
if (TC.Context.LangOpts.hasFeature(
1650+
Feature::NonIsolatedAsyncInheritsIsolationFromContext) &&
1651+
IsolationInfo &&
1652+
IsolationInfo->getKind() == ActorIsolation::CallerIsolationInheriting &&
1653+
extInfoBuilder.isAsync()) {
1654+
auto actorProtocol = TC.Context.getProtocol(KnownProtocolKind::Actor);
1655+
auto actorType =
1656+
ExistentialType::get(actorProtocol->getDeclaredInterfaceType());
1657+
addParameter(CanType(actorType).wrapInOptionalType(),
1658+
ParameterConvention::Direct_Guaranteed,
1659+
ParameterTypeFlags().withIsolated(true),
1660+
true /*implicit leading parameter*/);
1661+
}
1662+
16441663
// Add any foreign parameters that are positioned at the start
16451664
// of the sequence. visit() will add foreign parameters that are
16461665
// positioned after any parameters it adds.
@@ -2472,8 +2491,17 @@ static CanSILFunctionType getSILFunctionType(
24722491
// Destructure the input tuple type.
24732492
SmallVector<SILParameterInfo, 8> inputs;
24742493
{
2494+
std::optional<ActorIsolation> actorIsolation;
2495+
if (constant) {
2496+
if (constant->kind == SILDeclRef::Kind::Deallocator) {
2497+
actorIsolation = ActorIsolation::forNonisolated(false);
2498+
} else {
2499+
actorIsolation =
2500+
getActorIsolationOfContext(constant->getInnermostDeclContext());
2501+
}
2502+
}
24752503
DestructureInputs destructurer(expansionContext, TC, conventions,
2476-
foreignInfo, inputs);
2504+
foreignInfo, actorIsolation, inputs);
24772505
destructurer.destructure(origType, substFnInterfaceType.getParams(),
24782506
extInfoBuilder, unimplementable);
24792507
}
@@ -2561,8 +2589,9 @@ static CanSILFunctionType getSILFunctionTypeForInitAccessor(
25612589
{
25622590
bool unimplementable = false;
25632591
ForeignInfo foreignInfo;
2592+
std::optional<ActorIsolation> actorIsolation; // For now always null.
25642593
DestructureInputs destructurer(context, TC, conventions, foreignInfo,
2565-
inputs);
2594+
actorIsolation, inputs);
25662595
destructurer.destructure(
25672596
origType, substAccessorType.getParams(),
25682597
extInfoBuilder.withRepresentation(SILFunctionTypeRepresentation::Thin),

lib/SILGen/SILGenApply.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4783,6 +4783,8 @@ struct ParamLowering {
47834783
return ClaimedParamsRef(result, (unsigned)-1);
47844784
}
47854785

4786+
void claimImplicitParameters() { Params = Params.drop_front(); }
4787+
47864788
ArrayRef<SILParameterInfo>
47874789
claimCaptureParams(ArrayRef<ManagedValue> captures) {
47884790
auto firstCapture = Params.size() - captures.size();
@@ -5445,6 +5447,18 @@ ApplyOptions CallEmission::emitArgumentsForNormalApply(
54455447
options |= ApplyFlags::DoesNotAwait;
54465448
}
54475449

5450+
// Before we do anything, claim the implicit parameters. This prevents us
5451+
// from attempting to handle the implicit parameters when we emit explicit
5452+
// parameters.
5453+
//
5454+
// NOTE: The actual work needs to be done /after/ we emit the normal
5455+
// parameters since we are going to reverse the order.
5456+
if (auto isolated = substFnType->maybeGetIsolatedParameter();
5457+
isolated && isolated->hasOption(SILParameterInfo::ImplicitLeading)) {
5458+
assert(SGF.ExpectedExecutor.isNecessary());
5459+
paramLowering.claimImplicitParameters();
5460+
}
5461+
54485462
// Collect the captures, if any.
54495463
if (callee.hasCaptures()) {
54505464
(void)paramLowering.claimCaptureParams(callee.getCaptures());
@@ -5476,6 +5490,18 @@ ApplyOptions CallEmission::emitArgumentsForNormalApply(
54765490
args.back(), delayedArgs, siteForeignError);
54775491
}
54785492

5493+
// Now, actually handle the implicit parameters.
5494+
if (auto isolated = substFnType->maybeGetIsolatedParameter();
5495+
isolated && isolated->hasOption(SILParameterInfo::ImplicitLeading)) {
5496+
auto executor =
5497+
ManagedValue::forBorrowedObjectRValue(SGF.ExpectedExecutor.getEager());
5498+
args.push_back({});
5499+
// NOTE: Even though this calls emitActorInstanceIsolation, this also
5500+
// handles glboal actor isolated cases.
5501+
args.back().push_back(SGF.emitActorInstanceIsolation(
5502+
callSite->Loc, executor, executor.getType().getASTType()));
5503+
}
5504+
54795505
uncurriedLoc = callSite->Loc;
54805506

54815507
// Emit any delayed arguments: formal accesses to inout arguments, etc.

lib/SILGen/SILGenConcurrency.cpp

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,23 +46,48 @@ static void setExpectedExecutorForLocalVar(SILGenFunction &SGF,
4646
SGF.ExpectedExecutor.set(SGF.emitLoadActorExecutor(loc, actorInstance));
4747
}
4848

49-
static void setExpectedExecutorForParameterIsolation(SILGenFunction &SGF,
50-
ActorIsolation actorIsolation) {
49+
static void
50+
setExpectedExecutorForParameterIsolation(SILGenFunction &SGF,
51+
ActorIsolation actorIsolation) {
5152
auto loc = RegularLocation::getAutoGeneratedLocation(SGF.F.getLocation());
52-
ManagedValue actorArg;
53-
if (actorIsolation.isActorInstanceForSelfParameter()) {
54-
ManagedValue selfMV;
55-
auto selfArg = SGF.F.getSelfArgument();
56-
if (selfArg->getOwnershipKind() == OwnershipKind::Guaranteed) {
57-
selfMV = ManagedValue::forBorrowedRValue(selfArg);
58-
} else {
59-
selfMV = ManagedValue::forUnmanagedOwnedValue(selfArg);
53+
54+
if (actorIsolation.isActorInstanceIsolated()) {
55+
if (actorIsolation.isActorInstanceForSelfParameter()) {
56+
ManagedValue selfMV;
57+
auto selfArg = SGF.F.getSelfArgument();
58+
if (selfArg->getOwnershipKind() == OwnershipKind::Guaranteed) {
59+
selfMV = ManagedValue::forBorrowedRValue(selfArg);
60+
} else {
61+
selfMV = ManagedValue::forUnmanagedOwnedValue(selfArg);
62+
}
63+
SGF.ExpectedExecutor.set(SGF.emitLoadActorExecutor(loc, selfMV));
64+
return;
65+
}
66+
67+
// See if our actorIsolation actually has an actor instance associated with
68+
// it.
69+
if (auto param = actorIsolation.getActorInstance()) {
70+
return setExpectedExecutorForLocalVar(SGF, param);
71+
}
72+
}
73+
74+
// If we have caller isolation inheriting... just grab from our isolated
75+
// argument.
76+
if (actorIsolation.getKind() == ActorIsolation::CallerIsolationInheriting) {
77+
if (auto *isolatedArg = SGF.F.maybeGetIsolatedArgument()) {
78+
ManagedValue isolatedMV;
79+
if (isolatedArg->getOwnershipKind() == OwnershipKind::Guaranteed) {
80+
isolatedMV = ManagedValue::forBorrowedRValue(isolatedArg);
81+
} else {
82+
isolatedMV = ManagedValue::forUnmanagedOwnedValue(isolatedArg);
83+
}
84+
85+
SGF.ExpectedExecutor.set(SGF.emitLoadActorExecutor(loc, isolatedMV));
86+
return;
6087
}
61-
SGF.ExpectedExecutor.set(SGF.emitLoadActorExecutor(loc, selfMV));
62-
} else {
63-
auto param = actorIsolation.getActorInstance();
64-
setExpectedExecutorForLocalVar(SGF, param);
6588
}
89+
90+
llvm_unreachable("Unhandled case?!");
6691
}
6792

6893
void SILGenFunction::emitExpectedExecutorProlog() {
@@ -139,7 +164,6 @@ void SILGenFunction::emitExpectedExecutorProlog() {
139164
switch (actorIsolation.getKind()) {
140165
case ActorIsolation::Unspecified:
141166
case ActorIsolation::Nonisolated:
142-
case ActorIsolation::CallerIsolationInheriting:
143167
case ActorIsolation::NonisolatedUnsafe:
144168
break;
145169

@@ -163,6 +187,11 @@ void SILGenFunction::emitExpectedExecutorProlog() {
163187
break;
164188
}
165189

190+
case ActorIsolation::CallerIsolationInheriting:
191+
assert(F.isAsync());
192+
setExpectedExecutorForParameterIsolation(*this, actorIsolation);
193+
break;
194+
166195
case ActorIsolation::GlobalActor:
167196
if (F.isAsync() || wantDataRaceChecks) {
168197
setExpectedExecutorForGlobalActor(*this, actorIsolation.getGlobalActor());

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5608,6 +5608,15 @@ InferredActorIsolation ActorIsolationRequest::evaluate(
56085608
checkClassGlobalActorIsolation(classDecl, *isolationFromAttr);
56095609
}
56105610

5611+
if (ctx.LangOpts.hasFeature(
5612+
Feature::NonIsolatedAsyncInheritsIsolationFromContext)) {
5613+
if (auto *func = dyn_cast<AbstractFunctionDecl>(value);
5614+
func && func->hasAsync() && isolationFromAttr->isNonisolated() &&
5615+
func->getModuleContext() == ctx.MainModule) {
5616+
return {ActorIsolation::forCallerIsolationInheriting(), {}};
5617+
}
5618+
}
5619+
56115620
return {*isolationFromAttr,
56125621
IsolationSource(/*source*/ nullptr, IsolationSource::Explicit)};
56135622
}
@@ -5624,6 +5633,16 @@ InferredActorIsolation ActorIsolationRequest::evaluate(
56245633
defaultIsolation = ActorIsolation::forMainActor(ctx);
56255634
}
56265635

5636+
// If we have an async function... by default we inherit isolation.
5637+
if (ctx.LangOpts.hasFeature(
5638+
Feature::NonIsolatedAsyncInheritsIsolationFromContext)) {
5639+
if (auto *func = dyn_cast<AbstractFunctionDecl>(value);
5640+
func && func->hasAsync() &&
5641+
func->getModuleContext() == ctx.MainModule) {
5642+
defaultIsolation = ActorIsolation::forCallerIsolationInheriting();
5643+
}
5644+
}
5645+
56275646
if (auto func = dyn_cast<AbstractFunctionDecl>(value)) {
56285647
// A @Sendable function is assumed to be actor-independent.
56295648
if (func->isSendable()) {

lib/Serialization/ModuleFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5858
/// describe what change you made. The content of this comment isn't important;
5959
/// it just ensures a conflict if two people change the module format.
6060
/// Don't worry about adhering to the 80-column limit for this line.
61-
const uint16_t SWIFTMODULE_VERSION_MINOR = 910; // unsafe conformances
61+
const uint16_t SWIFTMODULE_VERSION_MINOR = 911; // caller inheriting isolation
6262

6363
/// A standard hash seed used for all string hashes in a serialized module.
6464
///
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 6 -enable-experimental-feature NonIsolatedAsyncInheritsIsolationFromContext -parse-as-library
2+
3+
class NonSendable {} // expected-note {{}}
4+
5+
@MainActor var global = NonSendable()
6+
7+
@MainActor
8+
struct MainActorIsolatedStruct {
9+
init() {}
10+
11+
func syncMethod() {}
12+
func asyncMethod() {}
13+
}
14+
15+
struct NonisolatedStruct {
16+
// Validate we can still not access global state.
17+
func asyncMethod() async {
18+
let _ = await global // expected-error {{non-sendable type 'NonSendable' of var 'global' cannot exit main actor-isolated context}}
19+
20+
let x = await MainActorIsolatedStruct()
21+
await x.syncMethod()
22+
await x.asyncMethod()
23+
}
24+
}

0 commit comments

Comments
 (0)