Skip to content

SE-0139: Bridge all standard number types to NSNumber. #4933

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
Sep 23, 2016

Conversation

jckarter
Copy link
Contributor

Extend NSNumber bridging to cover not only Int, UInt, Double, and Bool, but all of the standard types as well. Extend the TypePreservingNSNumber subclass to accommodate all of these types, so that we preserve type identity for AnyHashable and dynamic casting of Swift-bridged NSNumbers. If a pure Cocoa NSNumber is cast, just trust that the user knows what they're doing.

@jckarter
Copy link
Contributor Author

@swift-ci Please test

@jckarter
Copy link
Contributor Author

@DougGregor Mind reviewing this for inclusion in the Swift 3 branch?

@swift-ci
Copy link
Contributor

Build failed
Jenkins build - Swift Test OS X Platform
Git Commit - 3bf86ce9a942a7d77009064bfa7ee631c5a85a96
Test requested by - @jckarter

@implementation _SwiftTypePreservingNSNumber {
/*alignas(StorageSize)*/ char storage[StorageSize];
Copy link
Member

Choose a reason for hiding this comment

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

Seems like you want alignas(Double) alignas(int64_t) alignas(uint64_t) to be really picky here.

Copy link
Contributor Author

@jckarter jckarter Sep 23, 2016

Choose a reason for hiding this comment

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

Unfortunately, Clang doesn't seem to let you put any alignas decorations at all in an ObjC++ implementation without an "attributes can't be put here" error. It shouldn't really be necessary since we consistently memcpy everything in and out of the storage.

DEFINE_ACCESSOR(int, intValue)
DEFINE_ACCESSOR(unsigned int, unsignedIntValue)
DEFINE_ACCESSOR(long long, longLongValue)
DEFINE_ACCESSOR(unsigned long long, unsignedLongLongValue)
DEFINE_ACCESSOR(float, floatValue)
DEFINE_ACCESSOR(double, doubleValue)
DEFINE_ACCESSOR(NSInteger, integerValue)
DEFINE_ACCESSOR(NSUInteger, unsignedIntegerValue)
Copy link
Member

Choose a reason for hiding this comment

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

Huh, we just forgot these?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, and the default implementations in the NSNumber root class just infinitely looped trying to redispatch themselves. Fun.

nsNumberBridging.test("${AType} to ${BType} casting fails") {
let original: ${AType} = 0
let bridged = original as AnyObject as Any
expectEqual(.none, bridged as? ${BType})
Copy link
Member

Choose a reason for hiding this comment

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

expectNotNil works for this, FWIW

}
// If the NSNumber instance preserved its Swift type, we only want to allow
// the cast if the type matches.
if let tag = _SwiftTypePreservingNSNumberTag(rawValue:
Copy link
Member

Choose a reason for hiding this comment

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

I get the sneaky trick with the NonSwift value here, but it's really subtle. Could you document _SwiftTypePreservingNSNumberTag and it's C counterpart so nobody surprises themselves by trying to put NonSwift in _SwiftTypePreservingNSNumberTag?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I elaborated the comment a bit. How's it look now?

@jckarter
Copy link
Contributor Author

@swift-ci Please test

@swift-ci
Copy link
Contributor

Build failed
Jenkins build - Swift Test OS X Platform
Git Commit - 3bf86ce9a942a7d77009064bfa7ee631c5a85a96
Test requested by - @jckarter

/// enums. For an Optional<_SwiftTypePreservingNSNumberTag>, Swift uses the
/// first invalid integer value to represent `.none`. This representation on
/// the C side allows _SwiftTypePreservingNSNumberTag(rawValue:) on the Swift
/// side to compile down to a no-op.
Copy link
Contributor

Choose a reason for hiding this comment

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

How about using 0 for this instead of 14?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's how it was before, but that then forces a representation change when we convert from the raw C value to the Swift enum on the Swift side, since the Swift enum always uses the compacted representation starting from zero. Definitely a microoptimization.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, bleah, right—the raw value isn't the representation value.

Extend NSNumber bridging to cover not only `Int`, `UInt`, `Double`, and `Bool`, but all of the standard types as well. Extend the `TypePreservingNSNumber` subclass to accommodate all of these types, so that we preserve type identity for `AnyHashable` and dynamic casting of Swift-bridged NSNumbers. If a pure Cocoa NSNumber is cast, just trust that the user knows what they're doing.

This XFAILs a couple of serialization tests that attempt to build the Foundation overlay, but which don't properly handle `gyb` files.
@jckarter
Copy link
Contributor Author

@swift-ci Please test

@jckarter jckarter merged commit a506af0 into swiftlang:master Sep 23, 2016
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.

4 participants