Skip to content

[6.0] Properly handle @objc thunks for generic classes and actors #74396

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions lib/SILGen/SILGenBridging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1514,7 +1514,7 @@ SILFunction *SILGenFunction::emitNativeAsyncToForeignThunk(SILDeclRef thunk) {
.withAsync()
.withSendable()
.build();
auto closureTy = objcFnTy->getWithExtInfo(closureExtInfo);
auto closureTy = objcInfo.SILFnType->getWithExtInfo(closureExtInfo);

SmallString<64> closureName(F.getName().begin(), F.getName().end());
// Trim off the thunk suffix and mangle this like a closure nested inside the
Expand All @@ -1541,7 +1541,7 @@ SILFunction *SILGenFunction::emitNativeAsyncToForeignThunk(SILDeclRef thunk) {
IsNotRuntimeAccessible);

auto closureRef = B.createFunctionRef(loc, closure);

auto closureVal = B.createPartialApply(loc, closureRef, subs,
closureArgs,
ParameterConvention::Direct_Guaranteed);
Expand Down Expand Up @@ -1581,15 +1581,17 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) {
}
}

// Use the same generic environment as the native entry point.
// We need to set this before we can call things like
// F.getForwardingSubstitutionMap().
F.setGenericEnvironment(SGM.Types.getConstantGenericEnvironment(native));

auto nativeInfo = getConstantInfo(getTypeExpansionContext(), native);
auto subs = F.getForwardingSubstitutionMap();
auto substTy = nativeInfo.SILFnType->substGenericArgs(
SGM.M, subs, getTypeExpansionContext());
SILFunctionConventions substConv(substTy, SGM.M);

// Use the same generic environment as the native entry point.
F.setGenericEnvironment(SGM.Types.getConstantGenericEnvironment(native));

auto loc = thunk.getAsRegularLocation();
loc.markAutoGenerated();
Scope scope(Cleanups, CleanupLocation(loc));
Expand Down Expand Up @@ -1854,6 +1856,13 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) {
// The immediate function result is an empty tuple.
return SILUndef::get(&F, SGM.Types.getEmptyTupleType());
};

// If the function we're calling isn't actually polymorphic, drop the
// substitutions. This should only happen in concrete specializations.
if (subs && !nativeFn->getType().castTo<SILFunctionType>()->isPolymorphic()) {
assert(subs.getGenericSignature()->areAllParamsConcrete());
subs = SubstitutionMap();
}

if (!substTy->hasErrorResult()) {
// Create the apply.
Expand Down
40 changes: 38 additions & 2 deletions test/SILGen/objc_generic_class.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// RUN: %target-swift-emit-silgen -sdk %S/Inputs -I %S/Inputs -enable-source-import %s | %FileCheck %s
// RUN: %target-swift-emit-silgen -sdk %S/Inputs -I %S/Inputs -enable-source-import %s -disable-availability-checking | %FileCheck %s

// REQUIRES: objc_interop
// REQUIRES: concurrency

import gizmo

Expand All @@ -11,9 +12,23 @@ import gizmo
// CHECK-NOT: sil hidden [ossa] @$sSo7GenericCfd
// CHECK-NOT: sil hidden [ossa] @$sSo8NSObjectCfd

class Generic<T>: NSObject {
class Generic<T>: NSObject, ObjCProtocol {
var x: Int = 10

// CHECK-LABEL: sil private [thunk] [ossa] @$s18objc_generic_class7GenericC5evokeyyFTo : $@convention(objc_method) <T> (Generic<T>) -> () {
// CHECK: [[FN:%.*]] = function_ref @$s18objc_generic_class7GenericC5evokeyyF
// CHECK-NEXT: apply [[FN]]<T>
func evoke() {}

// CHECK-LABEL: sil private [thunk] [ossa] @$s18objc_generic_class7GenericC10evokeAsyncyyYaFTo : $@convention(objc_method) <T> (@convention(block) () -> (), Generic<T>) -> () {
// CHECK: [[FN:%.*]] = function_ref @$s18objc_generic_class7GenericC10evokeAsyncyyYaFyyYacfU_To
// CHECK-NEXT: partial_apply [callee_guaranteed] [[FN]]<T>

// CHECK-LABEL: sil shared [thunk] [ossa] @$s18objc_generic_class7GenericC10evokeAsyncyyYaFyyYacfU_To : $@convention(thin) @Sendable @async <T> (@convention(block) () -> (), Generic<T>) -> ()
// CHECK: [[FN:%.*]] = function_ref @$s18objc_generic_class7GenericC10evokeAsyncyyYaF : $@convention(method) @async <τ_0_0> (@guaranteed Generic<τ_0_0>) -> ()
// CHECK-NEXT: apply [[FN]]<T>
func evokeAsync() async {}

// CHECK-LABEL: sil hidden [ossa] @$s18objc_generic_class7GenericCfD : $@convention(method) <T> (@owned Generic<T>) -> () {
// CHECK: bb0({{%.*}} : @owned $Generic<T>):
// CHECK: } // end sil function '$s18objc_generic_class7GenericCfD'
Expand All @@ -33,6 +48,11 @@ class Generic<T>: NSObject {
// CHECK-NOT: sil hidden [ossa] @$s18objc_generic_class7GenericCfd
// CHECK-NOT: sil hidden [ossa] @$sSo8NSObjectCfd

@objc protocol ObjCProtocol {
func evoke()
func evokeAsync() async
}

// CHECK-LABEL: sil hidden [ossa] @$s18objc_generic_class11SubGeneric1CfD : $@convention(method) <U, V> (@owned SubGeneric1<U, V>) -> () {
// CHECK: bb0([[SELF:%.*]] : @owned $SubGeneric1<U, V>):
// CHECK: [[SUPER_DEALLOC:%.*]] = objc_super_method [[SELF]] : $SubGeneric1<U, V>, #Generic.deinit!deallocator.foreign : <T> (Generic<T>) -> () -> (), $@convention(objc_method) <τ_0_0> (Generic<τ_0_0>) -> ()
Expand All @@ -51,3 +71,19 @@ public extension GenericStruct where T == String {
@objc public func f() -> String { "hello" }
}
}

// rdar://129187133 - handle generic @objc thunks properly
actor GenericActor<T> : SendableObjCProtocol {
// CHECK-LABEL: sil private [thunk] [ossa] @$s18objc_generic_class12GenericActorC10evokeAsyncyyYaFTo : $@convention(objc_method) <T> (@convention(block) () -> (), @sil_isolated GenericActor<T>) -> ()
// CHECK: [[FN:%.*]] = function_ref @$s18objc_generic_class12GenericActorC10evokeAsyncyyYaFyyYacfU_To : $@convention(thin) @Sendable @async <τ_0_0> (@convention(block) () -> (), @sil_isolated GenericActor<τ_0_0>) -> ()
// CHECK-NEXT: partial_apply [callee_guaranteed] [[FN]]<T>
func evokeAsync() async {}

// CHECK-LABEL: sil shared [thunk] [ossa] @$s18objc_generic_class12GenericActorC10evokeAsyncyyYaFyyYacfU_To : $@convention(thin) @Sendable @async <T> (@convention(block) () -> (), @sil_isolated GenericActor<T>) -> ()
// CHECK: [[FN:%.*]] = function_ref @$s18objc_generic_class12GenericActorC10evokeAsyncyyYaF : $@convention(method) @async <τ_0_0> (@sil_isolated @guaranteed GenericActor<τ_0_0>) -> ()
// CHECK-NEXT: apply [[FN]]<T>
}

@objc protocol SendableObjCProtocol : Sendable {
func evokeAsync() async
}