Swift's NSNumber bridging is properly broken

Originator:automate.mantasystems
Number:rdar://22011202 Date Originated:2015-07-27
Status:Open Resolved:
Product:Developer Tools Product Version:Xcode Version 7.0 beta 4 (7A165t)
Classification:Serious Bug Reproducible:Always
 
Summary:

Swift treats all NSNumber instances as Bool, Int, and Double, regardless of actual numerical type. e.g. *All* of the following always test as *true:*

import Cocoa

let b:AnyObject = NSNumber(bool:true)
let n:AnyObject = NSNumber(int:100)
let f:AnyObject = NSNumber(double:1.1)

b is Bool // true
b is Int // true
b is Double // true

n is Bool // true
n is Int // true
n is Double // true

f is Bool // true
f is Int // true
f is Double // true

Aside from being wrong and unhelpful, this is also inconsistent with behaviors of native Swift Bools, Ints, and Doubles:

import Cocoa

let b:Any = true
let n:Any = 100
let f:Any = 1.1

b is Bool // true
b is Int // false
b is Double // false

n is Bool // true
n is Int // false
n is Double // false

f is Bool // true
f is Int // false
f is Double // false

This creates further confusion when native Swift Bools, Ints, and Doubles are stored in variables of type AnyObject, at which point Swift's not-so-seamless-after-all Cocoa bridging kicks in, transmogrifying them into Cocoa objects and destroying their own Type information in the process:

import Cocoa

let b:AnyObject = true
let n:AnyObject = 100
let f:AnyObject = 1.1

b is Bool // true
b is Int // true
b is Double // true

n is Bool // true
n is Int // true
n is Double // true

f is Bool // true
f is Int // true
f is Double // true

b.dynamicType // __NSCFBoolean.Type
n.dynamicType // __NSCFNumber.Type
f.dynamicType // __NSCFNumber.Type

(This gets especially irritating in Swift code that creates the correct Swift Bool/Int/Double representation of a particular value, only to have that representation summarily trashed the moment that value passes through a Cocoa-friendly API.)

Just to round things out, storing NSNumber instances in variables of type Any does not produce the converse effect (i.e. NSNumbers are not transmogrified to Bools/Ints/Doubles):

import Cocoa

let b:Any = NSNumber(bool:true)
let n:Any = NSNumber(int:100)
let f:Any = NSNumber(double:1.1)

b is Bool // true
b is Int // true
b is Double // true

n is Bool // true
n is Int // true
n is Double // true

f is Bool // true
f is Int // true
f is Double // true

b.dynamicType // __NSCFBoolean.Type
n.dynamicType // __NSCFNumber.Type
f.dynamicType // __NSCFNumber.Type

Obviously, the NSNumber class cluster is a dead loss as far as compile-time type checking is concerned, but between their private __NSCFBoolean and __NSCFNumber representations and NSNumber's objCType property there is sufficient runtime information available for Swift to handle them correctly during execution.

(The alternative would be not to bridge NSNumber instances automatically, UNLESS the variable's type is totally unambiguous, i.e. Bool OR Int OR Double, NOT Any/AnyObject. Though this would be the less desirable choice, and would no doubt cause irritations of its own.)




Version:
Xcode Version 7.0 beta 4 (7A165t)
OS X 10.10.4 (14E46)


Notes:
No doubt this is a dupe, but hopefully this issue gets enough pile-on for it to be fixed before Swift 2.0 final ships, because it's damnably annoying and clearly lends the lie to Swift's ostensibly "seamless" Cocoa bridging.

Comments


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!