[swift-evolution] [Pitch] SE-0083 revisited: removing bridging behavior from `as`/`is`/`as?` casts

Tony Parker anthony.parker at apple.com
Fri Mar 3 11:56:02 CST 2017

I’m concerned about the large source compatibility impact this change would have. Even if we claim that it’s only for Swift 4, migration would be difficult. Even if it were just a warning, that could be extremely noisy for large projects.

What do you propose to mitigate that problem?

I also think that before proceed with this change any further, we should have a lot more data about the impact that it has on real world applications in general. How many places use as for bridging now? What alternative approach would they use? We should see a diff of some of that real world code and see if we actually like the result. I’m not convinced that it is wrong, semantically, to use an as cast to indicate the conversion of a reference type to a value type. As is used to indicate type conversion. Bridging is a side effect, yes, but warning on this change may just feel pedantic without actually improving the situation.

And finally, as you and I have discussed separately, I don’t believe we should confuse the issue of what happens when bridging NSNumber into Swift with the issue of if ‘as’ bridges or not. The Foundation team believes there is a lot of room for improvement in the behavior of ‘as’ casting for NSNumber that would provide a more predictable and safer answer.

- Tony

> On Mar 1, 2017, at 8:11 PM, Joe Groff via swift-evolution <swift-evolution at swift.org> wrote:
> I’d like to investigate separating Objective-C bridging from the behavior of the as/as?/is operator family again for Swift 4. Last year, I proposed SE–0083 <https://github.com/apple/swift-evolution/blob/master/proposals/0083-remove-bridging-from-dynamic-casts.md>, but we deferred the proposal for lack of time to evaluate its impact. As complicating factors, we now have source compatibility with Swift 3 as a requirement, and the id-as-Any work from SE–0116 <https://github.com/apple/swift-evolution/blob/master/proposals/0116-id-as-any.md> more or less requires bridging dynamic casts to work. I think we can nonetheless make important improvements in this area in order to simplify the core language and provide more portable behavior across platforms with and without ObjC interop. In retrospect, submitting SE–0083 as an omnibus “fix casting” proposal was a mistake. We can separate out a few smaller subproblems from the overall concept:
> Replacing as for bridging coercion
> Swift 0 shipped with implicit conversions between standard library value types and their bridged Cocoa classes in both directions, and as we’ve eased off of the implicit conversions, we still left the as operator with the ability to force the conversions. This complicates the meaning of as: normally, it just provides type context, but it also has the power to force bridging conversions. These meanings are often at odds:
> // `NSNumber` is `ExpressibleByIntegerLiteral`, so this gives type context to the literal 0
> // and is equivalent to `NSNumber(integerLiteral: 0)`
> 0 as NSNumber
> // `x` already has type `Int`, so this forces the bridging conversion and is equivalent to
> // `_bridgeToObjectiveC(x)` (and thereby gives you a different kind of `NSNumber`!)
> let x: Int = 0
> x as NSNumber
> Aside from the complexity and non-portability of this behavior, this is also inconsistent with the Swift naming conventions, which recommend that conversions between related types be presented as initializers. Additionally, the bridging conversions often have specialized behavior for performance or semantic reasons that aren’t intended to be exposed in the normal API of either type (for example, bridging a Swift number type to NSNumber produces a “type-preserving” instance of NSNumber so that the bridge doesn’t lose type information, even though NSNumber’s own API presents a type-agnostic numeric object). Therefore, I propose that we remove the bridging behavior from as, and provide APIs for conversion where they don’t yet exist. as is purely a compile-time construct with no runtime interaction, so the Swift 3 compatibility and ABI issues are much simpler than they are when runtime casting behavior becomes involved.
> Warning on is/as?/as! casts that statically induce bridging
> Without changing the runtime behavior of casting, we could still discourage users from using dynamic casting to perform bridging conversions when it’s statically evident that a bridging conversion is the only way a cast succeeds. For example:
> func abuseBridgingCasts(on object: AnyObject) {
>     // warning: dynamic cast requires a bridging conversion; use `Int(bridgedFrom:)` instead
>     let _ = object as? Int
> }
> This wouldn’t be perfect, since we wouldn’t be able to warn about fully dynamic casts, but it could help encourage users to write portable code that doesn’t rely on the Objective-C bridge in common situations.
> Limiting when the runtime allows bridging in dynamic casts
> Ideally, we would be able to change runtime dynamic casting itself to not involve bridging. However, as I mentioned above, there are at least two situations where bridging dynamic casts are necessary to meet design requirements:
> To maintain compatibility with Swift 3 code
> For dynamic-casting Anys that were bridged from an Objective-C id, since we don’t know statically whether a bridge to any particular Swift type is needed
> However, neither of these is an insurmountable barrier. For Swift 3 compatibility, if nothing else, we could ship a parallel set of runtime entry points for dynamic casting with the Swift 3 behavior that would be used when compiling code in Swift 3 mode (and those entry points could possibly be banished to a separate compatibility dylib to avoid weighing down the ABI-stable Swift runtime forever). For id-as-Any bridging, existentials could potentially carry a bit to indicate whether casting out of that particular value should admit bridging casts. Doing that has complications of its own—having otherwise equivalent Any values have different behavior is undeniably weird—but, if we can keep the behavior isolated to code that directly interfaces with ObjC, I think it’s worth investigating in the interest of making the overall language more predictable.
> -Joe
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

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

More information about the swift-evolution mailing list