Skip to content

Commit 8686a8b

Browse files
authored
Merge pull request #82710 from rjmccall/nonisolated-nonsending-thunks-6.2
[6.2] Correctly forward the implicit `nonisolated(nonsending)` parameter in thunks
2 parents 6ddd1c9 + dae036a commit 8686a8b

File tree

6 files changed

+139
-14
lines changed

6 files changed

+139
-14
lines changed

lib/SILGen/SILGenAvailability.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,9 @@ void SILGenFunction::emitBackDeploymentThunk(SILDeclRef thunk) {
508508
SmallVector<ManagedValue, 4> params;
509509
SmallVector<ManagedValue, 4> indirectParams;
510510
SmallVector<ManagedValue, 4> indirectErrorResults;
511-
collectThunkParams(loc, params, &indirectParams, &indirectErrorResults);
511+
ManagedValue implicitIsolationParam;
512+
collectThunkParams(loc, params, &indirectParams, &indirectErrorResults,
513+
&implicitIsolationParam);
512514

513515
// Build up the list of arguments that we're going to invoke the real
514516
// function with.
@@ -519,6 +521,9 @@ void SILGenFunction::emitBackDeploymentThunk(SILDeclRef thunk) {
519521
for (auto indirectErrorResult : indirectErrorResults) {
520522
paramsForForwarding.emplace_back(indirectErrorResult.getLValueAddress());
521523
}
524+
if (implicitIsolationParam) {
525+
paramsForForwarding.emplace_back(implicitIsolationParam.forward(*this));
526+
}
522527

523528
for (auto param : params) {
524529
// We're going to directly call either the original function or the fallback

lib/SILGen/SILGenFunction.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2623,7 +2623,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
26232623
void collectThunkParams(
26242624
SILLocation loc, SmallVectorImpl<ManagedValue> &params,
26252625
SmallVectorImpl<ManagedValue> *indirectResultParams = nullptr,
2626-
SmallVectorImpl<ManagedValue> *indirectErrorParams = nullptr);
2626+
SmallVectorImpl<ManagedValue> *indirectErrorParams = nullptr,
2627+
ManagedValue *implicitIsolationParam = nullptr);
26272628

26282629
/// Build the type of a function transformation thunk.
26292630
CanSILFunctionType buildThunkType(CanSILFunctionType &sourceType,

lib/SILGen/SILGenPoly.cpp

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -986,7 +986,8 @@ ManagedValue Transform::transformTuple(ManagedValue inputTuple,
986986
void SILGenFunction::collectThunkParams(
987987
SILLocation loc, SmallVectorImpl<ManagedValue> &params,
988988
SmallVectorImpl<ManagedValue> *indirectResults,
989-
SmallVectorImpl<ManagedValue> *indirectErrors) {
989+
SmallVectorImpl<ManagedValue> *indirectErrors,
990+
ManagedValue *implicitIsolation) {
990991
// Add the indirect results.
991992
for (auto resultTy : F.getConventions().getIndirectSILResultTypes(
992993
getTypeExpansionContext())) {
@@ -1028,8 +1029,12 @@ void SILGenFunction::collectThunkParams(
10281029
// If our thunk has an implicit param and we are being asked to forward it,
10291030
// to the callee, skip it. We are going to handle it especially later.
10301031
if (param.hasOption(SILParameterInfo::ImplicitLeading) &&
1031-
param.hasOption(SILParameterInfo::Isolated))
1032+
param.hasOption(SILParameterInfo::Isolated)) {
1033+
if (implicitIsolation)
1034+
*implicitIsolation = functionArgument;
10321035
continue;
1036+
}
1037+
10331038
params.push_back(functionArgument);
10341039
}
10351040
}
@@ -5462,9 +5467,18 @@ static void buildThunkBody(SILGenFunction &SGF, SILLocation loc,
54625467
options |= ThunkGenFlag::CalleeHasImplicitIsolatedParam;
54635468
}
54645469

5470+
// Collect the thunk parameters. We don't need to collect the indirect
5471+
// error parameter because it'll be stored as IndirectErrorResult, which
5472+
// gets used implicitly by emitApplyWithRethrow.
54655473
SmallVector<ManagedValue, 8> params;
54665474
SmallVector<ManagedValue, 4> indirectResultParams;
5467-
SGF.collectThunkParams(loc, params, &indirectResultParams);
5475+
ManagedValue implicitIsolationParam;
5476+
SGF.collectThunkParams(loc, params, &indirectResultParams,
5477+
/*indirect error params*/ nullptr,
5478+
&implicitIsolationParam);
5479+
5480+
assert(bool(options & ThunkGenFlag::ThunkHasImplicitIsolatedParam)
5481+
== implicitIsolationParam.isValid());
54685482

54695483
// Ignore the self parameter at the SIL level. IRGen will use it to
54705484
// recover type metadata.
@@ -5510,9 +5524,11 @@ static void buildThunkBody(SILGenFunction &SGF, SILLocation loc,
55105524
case FunctionTypeIsolation::Kind::NonIsolated:
55115525
break;
55125526

5527+
// For a function for caller isolation, we'll have to figure out what the
5528+
// output function's formal isolation is. This is quite doable, but we don't
5529+
// have to do it yet.
55135530
case FunctionTypeIsolation::Kind::NonIsolatedCaller:
5514-
hopToIsolatedParameter = true;
5515-
break;
5531+
llvm_unreachable("synchronous function has caller isolation?");
55165532

55175533
// For a function with parameter isolation, we'll have to dig the
55185534
// argument out after translation but before making the call.
@@ -5594,12 +5610,15 @@ static void buildThunkBody(SILGenFunction &SGF, SILLocation loc,
55945610
/*mandatory*/false);
55955611
}
55965612

5597-
// If we are thunking a nonisolated caller to nonisolated or global actor, we
5598-
// need to load the actor.
5613+
// If the input function has caller isolation, we need to fill in that
5614+
// argument with the formal isolation of the output function.
55995615
if (options.contains(ThunkGenFlag::CalleeHasImplicitIsolatedParam)) {
56005616
auto outputIsolation = outputSubstType->getIsolation();
56015617
switch (outputIsolation.getKind()) {
56025618
case FunctionTypeIsolation::Kind::NonIsolated:
5619+
case FunctionTypeIsolation::Kind::Erased:
5620+
// Converting a caller-isolated function to @isolated(any) makes
5621+
// it @concurrent. In either case, emit a nil actor reference.
56035622
argValues.push_back(SGF.emitNonIsolatedIsolation(loc).getValue());
56045623
break;
56055624
case FunctionTypeIsolation::Kind::GlobalActor: {
@@ -5609,11 +5628,17 @@ static void buildThunkBody(SILGenFunction &SGF, SILLocation loc,
56095628
SGF.emitGlobalActorIsolation(loc, globalActor).getValue());
56105629
break;
56115630
}
5631+
case FunctionTypeIsolation::Kind::NonIsolatedCaller: {
5632+
argValues.push_back(implicitIsolationParam.getValue());
5633+
break;
5634+
}
56125635
case FunctionTypeIsolation::Kind::Parameter:
5613-
case FunctionTypeIsolation::Kind::Erased:
5614-
case FunctionTypeIsolation::Kind::NonIsolatedCaller:
5636+
// This would require a conversion from a type with caller
5637+
// isolation to a type with parameter isolation, which is not
5638+
// currently allowed and probably won't ever be. Anyway, to
5639+
// implement it, we'd need to borrow the isolated parameter
5640+
// and wrap it up as an `Optional<any Actor>`.
56155641
llvm_unreachable("Should never see this");
5616-
break;
56175642
}
56185643
}
56195644

@@ -5964,7 +5989,9 @@ static void buildWithoutActuallyEscapingThunkBody(SILGenFunction &SGF,
59645989
SmallVector<ManagedValue, 8> params;
59655990
SmallVector<ManagedValue, 8> indirectResults;
59665991
SmallVector<ManagedValue, 1> indirectErrorResults;
5967-
SGF.collectThunkParams(loc, params, &indirectResults, &indirectErrorResults);
5992+
ManagedValue implicitIsolationParam;
5993+
SGF.collectThunkParams(loc, params, &indirectResults, &indirectErrorResults,
5994+
&implicitIsolationParam);
59685995

59695996
// Ignore the self parameter at the SIL level. IRGen will use it to
59705997
// recover type metadata.
@@ -5985,6 +6012,11 @@ static void buildWithoutActuallyEscapingThunkBody(SILGenFunction &SGF,
59856012
for (auto indirectError : indirectErrorResults)
59866013
argValues.push_back(indirectError.getLValueAddress());
59876014

6015+
// Forward the implicit isolation parameter.
6016+
if (implicitIsolationParam.isValid()) {
6017+
argValues.push_back(implicitIsolationParam.getValue());
6018+
}
6019+
59886020
// Add the rest of the arguments.
59896021
forwardFunctionArguments(SGF, loc, fnType, params, argValues);
59906022

@@ -6174,6 +6206,7 @@ ManagedValue SILGenFunction::getThunkedAutoDiffLinearMap(
61746206
return getThunkedResult();
61756207
thunk->setGenericEnvironment(genericEnv);
61766208

6209+
// FIXME: handle implicit isolation parameter here
61776210
SILGenFunction thunkSGF(SGM, *thunk, FunctionDC);
61786211
SmallVector<ManagedValue, 4> params;
61796212
SmallVector<ManagedValue, 4> thunkIndirectResults;
@@ -6516,6 +6549,7 @@ SILFunction *SILGenModule::getOrCreateCustomDerivativeThunk(
65166549
thunk->setGenericEnvironment(thunkGenericEnv);
65176550

65186551
SILGenFunction thunkSGF(*this, *thunk, customDerivativeFn->getDeclContext());
6552+
// FIXME: handle implicit isolation parameter here
65196553
SmallVector<ManagedValue, 4> params;
65206554
SmallVector<ManagedValue, 4> indirectResults;
65216555
SmallVector<ManagedValue, 1> indirectErrorResults;

lib/SILGen/SILGenType.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1304,8 +1304,9 @@ SILFunction *SILGenModule::emitDefaultOverride(SILDeclRef replacement,
13041304
SmallVector<ManagedValue, 4> params;
13051305
SmallVector<ManagedValue, 4> indirectResults;
13061306
SmallVector<ManagedValue, 4> indirectErrors;
1307+
ManagedValue implicitIsolationParam;
13071308
SGF.collectThunkParams(replacement.getDecl(), params, &indirectResults,
1308-
&indirectErrors);
1309+
&indirectErrors, &implicitIsolationParam);
13091310

13101311
auto self = params.back();
13111312

@@ -1321,6 +1322,11 @@ SILFunction *SILGenModule::emitDefaultOverride(SILDeclRef replacement,
13211322
for (auto result : indirectResults) {
13221323
args.push_back(result.forward(SGF));
13231324
}
1325+
// Indirect errors would go here, but we don't currently support
1326+
// throwing coroutines.
1327+
if (implicitIsolationParam.isValid()) {
1328+
args.push_back(implicitIsolationParam.forward(SGF));
1329+
}
13241330
for (auto param : params) {
13251331
args.push_back(param.forward(SGF));
13261332
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// RUN: %target-swift-frontend -emit-sil -parse-as-library -swift-version 6 -enable-upcoming-feature NonisolatedNonsendingByDefault %s | %FileCheck %s
2+
3+
// REQUIRES: swift_feature_NonisolatedNonsendingByDefault
4+
5+
protocol P: Sendable {
6+
func f() async
7+
}
8+
9+
// Our map closure (abstracted as returning T) returns a function
10+
// value, which requires it to be reabstracted to the most general
11+
// abstraction pattern, i.e. from
12+
// nonisolated(nonsending) () async -> ()
13+
// to
14+
// nonisolated(nonsending) () async -> U, where U == ()
15+
// Note that we preserve nonisolated(nonsending).
16+
//
17+
// The thunk code did not expect the output function type to be
18+
// nonisolated(nonsending), so it didn't handle and propagate the
19+
// implicit isolation argument correctly.
20+
func testPartialApplication(p: [any P]) async {
21+
_ = p.map { $0.f }
22+
}
23+
// CHECK-LABEL: sil private @$s22nonisolated_nonsending22testPartialApplication1pySayAA1P_pG_tYaFyyYaYbYCcAaD_pXEfU_ :
24+
// CHECK: function_ref @$sScA_pSgIeghHgIL_AAytIeghHgILr_TR :
25+
26+
// Reabstraction thunk from caller-isolated () -> () to caller-isolated () -> T
27+
// CHECK-LABEL: sil shared [transparent] [reabstraction_thunk] @$sScA_pSgIeghHgIL_AAytIeghHgILr_TR :
28+
// CHECK: bb0(%0 : $*(), %1 : $Optional<any Actor>, %2 : $@Sendable @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> ()):
29+
// CHECK-NEXT: apply %2(%1) :
30+
31+
func takesGenericAsyncFunction<T>(_ fn: nonisolated(nonsending) (T) async -> Void) {}
32+
33+
// This is a more direct test of the partial-application code above.
34+
// Note that we have to make the functions explicitly nonisolated(nonsending)
35+
// because NonisolatedNonsendingByDefault only applies to declarations,
36+
// not function types in the abstract.
37+
func testReabstractionPreservingCallerIsolation(fn: nonisolated(nonsending) (Int) async -> Void) {
38+
takesGenericAsyncFunction(fn)
39+
}
40+
// CHECK-LABEL: sil hidden @$s22nonisolated_nonsending42testReabstractionPreservingCallerIsolation2fnyySiYaYCXE_tF :
41+
// CHECK: bb0(%0 : $@noescape @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>, Int) -> ()):
42+
// CHECK: [[THUNK:%.*]] = function_ref @$sScA_pSgSiIgHgILy_AASiIegHgILn_TR :
43+
44+
// CHECK-LABEL: sil shared [transparent] [reabstraction_thunk] @$sScA_pSgSiIgHgILy_AASiIegHgILn_TR :
45+
// CHECK: bb0(%0 : $Optional<any Actor>, %1 : $*Int, %2 : $@noescape @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>, Int) -> ()):
46+
// CHECK-NEXT: %3 = load %1
47+
// CHECK-NEXT: apply %2(%0, %3)
48+
49+
func takesAsyncIsolatedAnyFunction(_ fn: @isolated(any) () async -> Void) {}
50+
func takesGenericAsyncIsolatedAnyFunction<T>(_ fn: @isolated(any) (T) async -> Void) {}
51+
52+
// These would be good to test, but we apparently reject this conversion.
53+
#if false
54+
55+
// The same bug, but converting to an @isolated(any) function type.
56+
func testConversionToIsolatedAny(fn: nonisolated(nonsending) () async -> Void) {
57+
takesAsyncIsolatedAnyFunction(fn)
58+
}
59+
60+
func testReabstractionToIsolatedAny(fn: nonisolated(nonsending) (Int) async -> Void) {
61+
takesGenericAsyncIsolatedAnyFunction(fn)
62+
}
63+
64+
#endif

test/SILGen/back_deployed_attr.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,18 @@ public func backDeployedCaller(_ s: inout S<Z>) {
8585
// CHECK: function_ref @$s11back_deploy1SV1xxvsTwb : $@convention(method) <τ_0_0> (@in τ_0_0, @inout S<τ_0_0>) -> ()
8686
s.x = Z()
8787
}
88+
89+
// The same bug from test/Concurrency/nonisolated_nonsending.swift also applied to
90+
// back-deployment thunks.
91+
92+
// CHECK-LABEL: sil non_abi [serialized] [back_deployed_thunk] [ossa] @$s11back_deploy0A29DeployedNonisolatedNonsendingSiyYaFTwb :
93+
@backDeployed(before: macOS 10.52)
94+
nonisolated(nonsending)
95+
public func backDeployedNonisolatedNonsending() async -> Int {
96+
// CHECK: bb0(%0 : @guaranteed $Optional<any Actor>):
97+
// CHECK: [[FALLBACK_FN:%.*]] = function_ref @$s11back_deploy0A29DeployedNonisolatedNonsendingSiyYaFTwB :
98+
// CHECK: apply [[FALLBACK_FN]](%0)
99+
// CHECK: [[SHIPPING_FN:%.*]] = function_ref @$s11back_deploy0A29DeployedNonisolatedNonsendingSiyYaF :
100+
// CHECK: apply [[SHIPPING_FN]](%0)
101+
return 0
102+
}

0 commit comments

Comments
 (0)