Skip to content

Commit 047d644

Browse files
authored
Merge pull request #79717 from xedin/objc-handlers-are-execution-caller
[Sema/SILGen] Import ObjC async functions as `nonisolated(nonsending)` by …
2 parents 54e4400 + c25d2fa commit 047d644

File tree

5 files changed

+259
-47
lines changed

5 files changed

+259
-47
lines changed

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1706,8 +1706,12 @@ class DestructureInputs {
17061706

17071707
// If we are an async function that is unspecified or nonisolated, insert an
17081708
// isolated parameter if AsyncCallerExecution is enabled.
1709+
//
1710+
// NOTE: The parameter is not inserted for async functions imported
1711+
// from ObjC because they are handled in a special way that doesn't
1712+
// require it.
17091713
if (IsolationInfo && IsolationInfo->isCallerIsolationInheriting() &&
1710-
extInfoBuilder.isAsync()) {
1714+
extInfoBuilder.isAsync() && !Foreign.async) {
17111715
auto actorProtocol = TC.Context.getProtocol(KnownProtocolKind::Actor);
17121716
auto actorType =
17131717
ExistentialType::get(actorProtocol->getDeclaredInterfaceType());

lib/SILGen/SILGenBridging.cpp

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,11 +1373,16 @@ emitObjCThunkArguments(SILGenFunction &SGF, SILLocation loc, SILDeclRef thunk,
13731373
auto inputs = objcFnTy->getParameters();
13741374
auto nativeInputs = swiftFnTy->getParameters();
13751375
auto fnConv = SGF.silConv.getFunctionConventions(swiftFnTy);
1376-
assert(nativeInputs.size() == bridgedFormalTypes.size());
1377-
assert(nativeInputs.size() == nativeFormalTypes.size());
1376+
bool nativeInputsHasImplicitIsolatedParam = false;
1377+
if (auto param = swiftFnTy->maybeGetIsolatedParameter())
1378+
nativeInputsHasImplicitIsolatedParam = param->hasOption(SILParameterInfo::ImplicitLeading);
1379+
assert(nativeInputs.size() - unsigned(nativeInputsHasImplicitIsolatedParam) == bridgedFormalTypes.size());
1380+
assert(nativeInputs.size() - unsigned(nativeInputsHasImplicitIsolatedParam) == nativeFormalTypes.size());
13781381
assert(inputs.size() ==
13791382
nativeInputs.size() + unsigned(foreignError.has_value())
1380-
+ unsigned(foreignAsync.has_value()));
1383+
+ unsigned(foreignAsync.has_value()) -
1384+
unsigned(nativeInputsHasImplicitIsolatedParam));
1385+
13811386
for (unsigned i = 0, e = inputs.size(); i < e; ++i) {
13821387
SILType argTy = SGF.getSILType(inputs[i], objcFnTy);
13831388
SILValue arg = SGF.F.begin()->createFunctionArgument(argTy);
@@ -1423,13 +1428,13 @@ emitObjCThunkArguments(SILGenFunction &SGF, SILLocation loc, SILDeclRef thunk,
14231428
+ unsigned(foreignAsync.has_value())
14241429
== objcFnTy->getParameters().size() &&
14251430
"objc inputs don't match number of arguments?!");
1426-
assert(bridgedArgs.size() == swiftFnTy->getParameters().size() &&
1431+
assert(bridgedArgs.size() == swiftFnTy->getParameters().size() - bool(nativeInputsHasImplicitIsolatedParam) &&
14271432
"swift inputs don't match number of arguments?!");
14281433
assert((foreignErrorSlot || !foreignError) &&
14291434
"didn't find foreign error slot");
14301435

14311436
// Bridge the input types.
1432-
assert(bridgedArgs.size() == nativeInputs.size());
1437+
assert(bridgedArgs.size() == nativeInputs.size() - bool(nativeInputsHasImplicitIsolatedParam));
14331438
for (unsigned i = 0, size = bridgedArgs.size(); i < size; ++i) {
14341439
// Consider the bridged values to be "call results" since they're coming
14351440
// from potentially nil-unsound ObjC callers.
@@ -1656,6 +1661,31 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) {
16561661
// we will deallocate it too early.
16571662
Scope argScope(Cleanups, CleanupLocation(loc));
16581663

1664+
// See if our native function has an implicitly isolated parameter. In such a
1665+
// case, we need to pass in the isolation.
1666+
if (auto isolatedParameter = substTy->maybeGetIsolatedParameter();
1667+
isolatedParameter && isolatedParameter->hasOption(SILParameterInfo::ImplicitLeading)) {
1668+
assert(F.isAsync() && "Can only be async");
1669+
assert(isolation && "No isolation?!");
1670+
switch (isolation->getKind()) {
1671+
case ActorIsolation::Unspecified:
1672+
case ActorIsolation::Nonisolated:
1673+
case ActorIsolation::NonisolatedUnsafe:
1674+
case ActorIsolation::CallerIsolationInheriting:
1675+
args.push_back(emitNonIsolatedIsolation(loc).getValue());
1676+
break;
1677+
case ActorIsolation::ActorInstance:
1678+
llvm::report_fatal_error("Should never see this");
1679+
break;
1680+
case ActorIsolation::GlobalActor:
1681+
args.push_back(emitLoadGlobalActorExecutor(isolation->getGlobalActor()));
1682+
break;
1683+
case ActorIsolation::Erased:
1684+
llvm::report_fatal_error("Should never see this");
1685+
break;
1686+
}
1687+
}
1688+
16591689
// Bridge the arguments.
16601690
SILValue foreignErrorSlot;
16611691
SILValue foreignAsyncSlot;
@@ -2117,7 +2147,25 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) {
21172147
indirectResult = F.begin()->createFunctionArgument(
21182148
nativeConv.getSingleSILResultType(F.getTypeExpansionContext()));
21192149
}
2120-
2150+
2151+
// Before we do anything, see if our function type is async and has an
2152+
// implicit isolated parameter. In such a case, we need to implicitly insert
2153+
// it here before we insert other parameters.
2154+
//
2155+
// NOTE: We do not jump to it or do anything further since as mentioned above,
2156+
// we might switch to the callee's actor as part of making the call... but we
2157+
// don't need to do anything further than that because we're going to
2158+
// immediately return.
2159+
bool hasImplicitIsolatedParameter = false;
2160+
if (auto isolatedParameter = nativeFnTy->maybeGetIsolatedParameter();
2161+
isolatedParameter && nativeFnTy->isAsync() &&
2162+
isolatedParameter->hasOption(SILParameterInfo::ImplicitLeading)) {
2163+
auto loweredTy = getLoweredTypeForFunctionArgument(
2164+
isolatedParameter->getArgumentType(&F));
2165+
F.begin()->createFunctionArgument(loweredTy);
2166+
hasImplicitIsolatedParameter = true;
2167+
}
2168+
21212169
// Forward the arguments.
21222170
SmallVector<SILValue, 8> params;
21232171

@@ -2182,9 +2230,12 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) {
21822230
getParameterTypes(nativeCI.LoweredType.getParams(), hasSelfParam);
21832231

21842232
for (unsigned nativeParamIndex : indices(params)) {
2233+
// Adjust the parameter if we inserted an implicit isolated parameter.
2234+
unsigned nativeFnTyParamIndex = nativeParamIndex + hasImplicitIsolatedParameter;
2235+
21852236
// Bring the parameter to +1.
21862237
auto paramValue = params[nativeParamIndex];
2187-
auto thunkParam = nativeFnTy->getParameters()[nativeParamIndex];
2238+
auto thunkParam = nativeFnTy->getParameters()[nativeFnTyParamIndex];
21882239
// TODO: Could avoid a retain if the bridged parameter is also +0 and
21892240
// doesn't require a bridging conversion.
21902241
ManagedValue param;

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5840,13 +5840,41 @@ computeDefaultInferredActorIsolation(ValueDecl *value) {
58405840
// Use the overridden decl's isolation as the default isolation for this
58415841
// decl.
58425842
auto isolation = getOverriddenIsolationFor(value);
5843+
5844+
// If this is an override of an async completion handler, mark
5845+
// it `@concurrent` instead of inferring `nonisolated(nonsending)`
5846+
// to preserve pre-SE-0461 behavior.
5847+
if (isolation.isCallerIsolationInheriting() &&
5848+
overriddenValue->hasClangNode()) {
5849+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(overriddenValue)) {
5850+
if (AFD->getForeignAsyncConvention()) {
5851+
return {{ActorIsolation::forNonisolated(/*unsafe=*/false),
5852+
IsolationSource(overriddenValue, IsolationSource::Override)},
5853+
overriddenValue,
5854+
isolation};
5855+
}
5856+
}
5857+
}
5858+
58435859
return {{isolation,
58445860
IsolationSource(overriddenValue, IsolationSource::Override)},
58455861
overriddenValue,
58465862
isolation}; // use the overridden decl's iso as the default
58475863
// isolation for this decl.
58485864
}
58495865

5866+
// Asynchronous variants for functions imported from ObjC are
5867+
// `nonisolated(nonsending)` by default.
5868+
if (value->hasClangNode()) {
5869+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(value)) {
5870+
if (!isa<ProtocolDecl>(AFD->getDeclContext()) &&
5871+
AFD->getForeignAsyncConvention()) {
5872+
return {
5873+
{ActorIsolation::forCallerIsolationInheriting(), {}}, nullptr, {}};
5874+
}
5875+
}
5876+
}
5877+
58505878
// We did not find anything special, return unspecified.
58515879
return {{ActorIsolation::forUnspecified(), {}}, nullptr, {}};
58525880
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %empty-directory(%t/src)
3+
// RUN: split-file %s %t/src
4+
5+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck %t/src/main.swift \
6+
// RUN: -import-objc-header %t/src/Test.h \
7+
// RUN: -swift-version 6 \
8+
// RUN: -module-name main -I %t -verify
9+
10+
// REQUIRES: objc_interop
11+
12+
//--- Test.h
13+
#define MAIN_ACTOR __attribute__((__swift_attr__("@MainActor")))
14+
15+
#pragma clang assume_nonnull begin
16+
17+
@import Foundation;
18+
19+
@interface Test : NSObject
20+
- (void)loadWithCompletionHandler:(void (^)(void)) completionHandler;
21+
@end
22+
23+
MAIN_ACTOR
24+
@interface TestIsolated : NSObject
25+
- (void)loadWithCompletionHandler:(void (^)(void)) completionHandler;
26+
@end
27+
28+
#pragma clang assume_nonnull end
29+
30+
//--- main.swift
31+
32+
class OverrideTest1 : Test {
33+
override func load() async {}
34+
}
35+
36+
class OverrideTest2 : TestIsolated {
37+
override func load() async {}
38+
}
39+
40+
func test(t: Test, i: TestIsolated) async throws {
41+
let fn = t.load // nonisolated(nonsending) () async -> Void
42+
43+
let _: @isolated(any) () async -> Void = fn
44+
// expected-error@-1 {{cannot convert value of type 'nonisolated(nonsending) () async -> Void' to specified type '@isolated(any) () async -> Void'}}
45+
46+
let isolatedFn = i.load
47+
let _: () -> Void = isolatedFn
48+
// expected-error@-1 {{invalid conversion from 'async' function of type '@MainActor @Sendable () async -> Void' to synchronous function type '() -> Void'}}
49+
}
50+
51+
func testOverrides(o1: OverrideTest1, o2: OverrideTest2) {
52+
let _: () -> Void = o1.load
53+
// expected-error@-1 {{invalid conversion from 'async' function of type '() async -> ()' to synchronous function type '() -> Void'}}
54+
let _: () -> Void = o2.load
55+
// expected-error@-1 {{invalid conversion from 'async' function of type '@MainActor @Sendable () async -> ()' to synchronous function type '() -> Void'}}
56+
}

0 commit comments

Comments
 (0)