Skip to content
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

[ObjC Bridging] Consistently bridge block types verbatim #77496

Merged
merged 1 commit into from
Nov 11, 2024

Conversation

tbkka
Copy link
Contributor

@tbkka tbkka commented Nov 8, 2024

A @convention(block) closure in Swift is completely compatible with Objective-C and does not need to be wrapped in a __SwiftValue box for use.

Previously, it was bridged verbatim when appearing by itself, but could end up boxed when it went through array bridging.

The test verifies that:

  • Objective-C does not see a __SwiftValue box
  • Swift type(of:) does not see a __SwiftValue box
  • Objective-C can actually call the closure

Resolves rdar://138132321

A `@convention(block)` closure in Swift is completely compatible with Objective-C
and does not need to be wrapped in a `__SwiftValue` box for use.

Previously, it was bridged verbatim when appearing by itself, but
could end up boxed when it went through array bridging.

The test verifies that:
* Objective-C does not see a `__SwiftValue` box
* Swift `type(of:)` does not see a `__SwiftValue` box
* Objective-C can actually call the closure

Resolves rdar://138132321
// Dig through existential types.
if (auto srcExistentialTy = dyn_cast<ExistentialTypeMetadata>(srcType)) {
else if (auto srcExistentialTy = dyn_cast<ExistentialTypeMetadata>(srcType)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function has always been a long if-else chain; I've reformatted it some to make that a bit more explicit.

I've also adjusted the comment layout to hopefully make it clearer:

// Explain first if
if (...) {
  ...
}
// Explain second if
else if (....) {
  ...
}
// Explain third if
else if (...) {
  ...
}

// Bridge the source value to an NSError.
auto flags = consume ? DynamicCastFlags::TakeOnSuccess
: DynamicCastFlags::Default;
return dynamicCastValueToNSError(src, srcType, srcErrorWitness, flags);
}
// Handle functions: "Block" types can be bridged literally
else if (auto fn = dyn_cast<FunctionTypeMetadata>(srcType)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the new case: If this is a function type with a block convention, then bridge it verbatim.

CastsTests.test("block closures in array should bridge without __SwiftValue")
{
let f: @convention(block) () -> Void = {}
let x = ([f] as NSArray)[0]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test does NSArray bridging explicitly here, but the bug also manifests when a Swift array is implicitly bridged to NSArray when calling Obj-C code.


#import "Cast_Blocks.h"

BOOL ObjCThinksObjectIsSwiftValue(id obj) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's important to verify that Objective-C really does see what we expect.

@tbkka
Copy link
Contributor Author

tbkka commented Nov 8, 2024

@swift-ci Please test

Copy link
Contributor

@mikeash mikeash left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One possible optimization, but looks good.

@tbkka tbkka merged commit 694c533 into swiftlang:main Nov 11, 2024
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants