[swift-evolution] [Idea] ObjectiveCBridgeable

Russ Bishop xenadu at gmail.com
Wed Mar 23 15:25:14 CDT 2016

> On Mar 23, 2016, at 11:49 AM, Douglas Gregor <dgregor at apple.com> wrote:
> Great. The suggestion to use an extension won’t actually work:

Doh. Thanks for catching that, I pushed a fix.

>> I assume this is a static function to avoid allocating memory by calling the initializer directly for each element, given the point is to defer the work? I wonder if we can skip the static though and just call the initializer directly? It would simplify the protocol a tiny bit.
> From an implementation perspective, the entry point for an initializer in a protocol handles the allocation itself. It’s a static function because it was easy to implement that way and the actual definitions get a bit more flexibility in how they can come up with the object (since we don’t have factory initializers).
Good point about the factory initializers. I’ve been trying to keep the whole “class cluster” situation in mind. I wonder if there should be an equivalent static function for the conditional bridging for similar reasons?

>>> (This was #5) It is an error for bridging to be ambiguous.
>>> A Swift type may bridge to an Objective-C base class, then provide different subclass instances at runtime but no other Swift type may bridge to that base class or any of its subclasses.
>>> The compiler must emit a diagnostic when it detects two Swift types attempting to bridge to the same ObjectiveCType.
>>> This is a tricky area. Currently, Int/Float/Double/Bool/CGFloat/UInt all have _ObjectiveCBridgeable conformances, although those conformances only really kick in at runtime (e.g., when dynamically casting an [AnyObject] or [NSNumber] to [Int] or [Double] with as? or matching a switch case). They would run afoul of this rule. However, this rule does generally make sense: if two Swift types have the same ObjectiveCType, we won’t know how to map an Objective-C API back into Swift. Those numeric types only work because they are trivially mapped between Swift and (Objective-)C; they don’t need to go through the _ObjectiveCBridgeable conformance. 
>> Perhaps the rule should simply be that any Objective-C API imported that has ambiguity is imported as the Objective-C type without automatic bridging support. The compiler can continue to import Int and friends with special magic. Creating an ambiguity just leaves you to resolve the problem manually? The rule would be something like "omitting the SWIFT_BRIDGED() attribute from ObjC //or// multiple Swift types bridging to the same ObjC type" turns off automatic thunk generation but bridged collections will still call the protocol where appropriate.
> Yeah. Thinking about it a bit further, I like your rule basically as you’ve stated it, because having two _ObjectiveCBridgeable conformances mapping to the same type is a problem the user should have to resolve. I guess I’m just looking for something small to indicate that a direct mapping of the representation (e.g., NSInteger -> Int) supersedes the _ObjectiveCBridgeable conformance when mapping APIs between Objective-C and Swift.

I added a separate section on Ambiguity and what the behavior is. I think you should be able to resolve ambiguity by casting so I went ahead and put that in. An example:

//Bar and Foo bridge to SomeObjectiveCType
struct Bar<T>: ObjectiveCBridgeable { }
struct Foo<T>: ObjectiveCBridgeable { }

class API {
    let foo: Foo<Int>
    func objCVersionOfAFunction(obj: SomeObjectiveCType) -> SomeObjectiveCType {
        let x = obj as! Bar<Int>
        // We've told the compiler which protocol impl to call
        return foo as! SomeObjectiveCType

Any problems with this approach? It makes handling the ambiguous or manual bridging case relatively straightforward, though there may be objections to using casting this way. [Be careful, I still mourn the loss of @conversion so I’m biased :)]

`NSInteger` is already imported as `Int` from Objective-C right? I assume a Clang attribute is specifying that somewhere but there wouldn’t be any ambiguity on the Swift side during import. I could definitely see having an attribute to declare that this specific parameter or return value should bridge to a specific Swift type (this NSNumber should import as Int) but that’s a lot of work and may be worth a separate proposal.

It seems like the problem is going the other direction: you want to materialize this parameter or whatever as `NSInteger` instead of the default `NSNumber *` but only when directly bridged, not inside collections. There’s no existing Objective-C header to tell us what to do. I’m not sure how we can resolve this without a Swift attribute to tell the compiler because the handling of it would be specific to each declaration. 

We could just  say that we aren’t going to let people have that level of granularity. Then just introduce a BuiltInBridgeable protocol that supersedes ObjectiveCBridgeable. A type adopting both will cause the compiler to prefer the built-in protocol when generating a bridging header, but the collection types can ignore that and just use ObjectiveCBridgeable. Presumably the BuiltInBridgeable protocol would just have an associated type to indicate that the bits are directly mapped to BuiltIn.Word or whatever.

>> An update has been posted to https://github.com/russbishop/swift-evolution/blob/master/proposals/0000-objectivecbridgeable.md <https://github.com/russbishop/swift-evolution/blob/master/proposals/0000-objectivecbridgeable.md>
> Thanks!
>> Would you prefer if I did / did not add your name to the proposal? I feel guilty taking all the credit.
> I don’t have a strong preference. Feel free to add my name if you’d like. I really appreciate your work on driving this proposal forward!
> 	- Doug

Happy to do so!


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160323/ee3c67eb/attachment.html>

More information about the swift-evolution mailing list