ClangImporter: Import unbounded NSSets and NSDictionaries using AnyHashable.

This commit is contained in:
Joe Groff
2016-07-25 16:20:27 -07:00
parent e884ab737d
commit 1e8dd0e34b
18 changed files with 100 additions and 51 deletions

View File

@@ -395,6 +395,9 @@ public:
/// Retrieve the declaration of Swift.Dictionary<K, V>.
NominalTypeDecl *getDictionaryDecl() const;
/// Retrieve the declaration of Swift.AnyHashable.
NominalTypeDecl *getAnyHashableDecl() const;
/// Retrieve the declaration of Swift.Optional or ImplicitlyUnwrappedOptional.
EnumDecl *getOptionalDecl(OptionalTypeKind kind) const;

View File

@@ -119,6 +119,9 @@ struct ASTContext::Implementation {
/// The declaration of Swift.Dictionary<T>.
NominalTypeDecl *DictionaryDecl = nullptr;
/// The declaration of Swift.AnyHashable.
NominalTypeDecl *AnyHashableDecl = nullptr;
/// The declaration of Swift.Optional<T>.
EnumDecl *OptionalDecl = nullptr;
@@ -615,6 +618,12 @@ NominalTypeDecl *ASTContext::getDictionaryDecl() const {
return Impl.DictionaryDecl;
}
NominalTypeDecl *ASTContext::getAnyHashableDecl() const {
if (!Impl.AnyHashableDecl)
Impl.AnyHashableDecl = findStdlibType(*this, "AnyHashable", 0);
return Impl.AnyHashableDecl;
}
EnumDecl *ASTContext::getOptionalDecl(OptionalTypeKind kind) const {
switch (kind) {
case OTK_None:

View File

@@ -908,12 +908,18 @@ namespace {
return Type();
// The first type argument for Dictionary or Set needs
// to be NSObject-bound.
// to be Hashable. Everything that inherits NSObject has a
// -hash code in ObjC, but if something isn't NSObject, fall back
// to AnyHashable as a key type.
if (unboundDecl == Impl.SwiftContext.getDictionaryDecl() ||
unboundDecl == Impl.SwiftContext.getSetDecl()) {
auto &keyType = importedTypeArgs[0];
if (!Impl.matchesNSObjectBound(keyType))
keyType = Impl.getNSObjectType();
if (!Impl.matchesNSObjectBound(keyType)) {
if (auto anyHashable = Impl.SwiftContext.getAnyHashableDecl())
keyType = anyHashable->getDeclaredType();
else
keyType = Type();
}
}
// Form the specialized type.
@@ -2624,6 +2630,7 @@ bool ClangImporter::Implementation::matchesNSObjectBound(Type type) {
return true;
// Struct or enum type must have been bridged.
// TODO: Check that the bridged type is Hashable?
if (type->getStructOrBoundGenericStruct() ||
type->getEnumOrBoundGenericEnum())
return true;

View File

@@ -1160,7 +1160,7 @@ public:
Type getCFStringRefType();
/// \brief Determines whether the given type matches an implicit type
/// bound of "NSObject", which is used to validate NSDictionary/NSSet.
/// bound of "Hashable", which is used to validate NSDictionary/NSSet.
bool matchesNSObjectBound(Type type);
/// \brief Look up and attempt to import a Clang declaration with

View File

@@ -50,9 +50,9 @@ extension CIFilter {
extension CISampler {
// - (id)initWithImage:(CIImage *)im keysAndValues:key0, ...;
convenience init(im: CIImage, elements: (String, Any)...) {
var dict: [NSObject : Any] = [:]
var dict: [AnyHashable : Any] = [:]
for (key, value) in elements {
dict[key as NSObject] = value
dict[key] = value
}
// @objc(initWithImage:options:)

View File

@@ -1237,7 +1237,8 @@ extension NSSet {
/// receiver.
@objc(_swiftInitWithSet_NSSet:)
public convenience init(set anSet: NSSet) {
self.init(set: anSet as Set)
// FIXME: This is a bit weird. Maybe there's a better way?
self.init(set: anSet as Set<NSObject> as Set<AnyHashable>)
}
}
@@ -1250,7 +1251,9 @@ extension NSDictionary {
/// found in `otherDictionary`.
@objc(_swiftInitWithDictionary_NSDictionary:)
public convenience init(dictionary otherDictionary: NSDictionary) {
self.init(dictionary: otherDictionary as Dictionary)
// FIXME: This is a bit weird. Maybe there's a better way?
self.init(dictionary: otherDictionary as [NSObject: AnyObject]
as [AnyHashable: Any])
}
}

View File

@@ -389,11 +389,11 @@ public protocol _BridgedStoredNSError :
}
/// TODO: Better way to do this?
internal func _stringDictToNSObjectDict(_ input: [String : Any])
-> [NSObject : Any] {
var result: [NSObject : Any] = [:]
internal func _stringDictToAnyHashableDict(_ input: [String : Any])
-> [AnyHashable : Any] {
var result: [AnyHashable : Any] = [:]
for (k, v) in input {
result[k as NSString] = v
result[k] = v
}
return result
}
@@ -411,7 +411,7 @@ public extension _BridgedStoredNSError
public init(_ code: Code, userInfo: [String : Any] = [:]) {
self.init(_nsError: NSError(domain: Self._nsErrorDomain,
code: numericCast(code.rawValue),
userInfo: _stringDictToNSObjectDict(userInfo)))
userInfo: _stringDictToAnyHashableDict(userInfo)))
}
/// The user-info dictionary for an error that was bridged from
@@ -432,7 +432,7 @@ public extension _BridgedStoredNSError
public init(_ code: Code, userInfo: [String : Any] = [:]) {
self.init(_nsError: NSError(domain: Self._nsErrorDomain,
code: numericCast(code.rawValue),
userInfo: _stringDictToNSObjectDict(userInfo)))
userInfo: _stringDictToAnyHashableDict(userInfo)))
}
}
@@ -461,7 +461,7 @@ public extension _BridgedStoredNSError {
var errorUserInfo: [String : Any] {
var result: [String : Any] = [:]
for (key, value) in _nsError.userInfo {
guard let stringKey = key as? String else { continue }
guard let stringKey = key.base as? String else { continue }
result[stringKey] = value
}
return result
@@ -528,7 +528,7 @@ public struct CocoaError : _BridgedStoredNSError {
}
public extension CocoaError {
private var _nsUserInfo: [NSObject : Any] {
private var _nsUserInfo: [AnyHashable : Any] {
return (self as NSError).userInfo
}
@@ -1329,7 +1329,7 @@ public struct URLError : _BridgedStoredNSError {
}
public extension URLError {
private var _nsUserInfo: [NSObject : Any] {
private var _nsUserInfo: [AnyHashable : Any] {
return (self as NSError).userInfo
}

View File

@@ -1180,7 +1180,8 @@ extension String {
/// values found in the `String`.
public
func propertyListFromStringsFileFormat() -> [String : String] {
return _ns.propertyListFromStringsFileFormat() as! [String : String]
return _ns.propertyListFromStringsFileFormat()! as [NSObject : AnyObject]
as! [String : String]
}
// - (NSRange)rangeOfCharacterFromSet:(NSCharacterSet *)aSet

View File

@@ -97,10 +97,10 @@ ErrorBridgingTests.test("NSError-to-enum bridging") {
let ns = NSError(domain: NSCocoaErrorDomain,
code: NSFileNoSuchFileError,
userInfo: [
NSFilePathErrorKey as NSObject : "/dev/null",
NSStringEncodingErrorKey as NSObject: /*ASCII=*/1,
NSUnderlyingErrorKey as NSObject: underlyingError,
NSURLErrorKey as NSObject: testURL
AnyHashable(NSFilePathErrorKey): "/dev/null",
AnyHashable(NSStringEncodingErrorKey): /*ASCII=*/1,
AnyHashable(NSUnderlyingErrorKey): underlyingError,
AnyHashable(NSURLErrorKey): testURL
])
objc_setAssociatedObject(ns, &CanaryHandle, NoisyError(),
@@ -143,7 +143,7 @@ ErrorBridgingTests.test("NSError-to-enum bridging") {
// URLError domain
let nsURL = NSError(domain: NSURLErrorDomain,
code: NSURLErrorBadURL,
userInfo: [NSURLErrorFailingURLErrorKey as NSObject : testURL])
userInfo: [AnyHashable(NSURLErrorFailingURLErrorKey): testURL])
let eURL: Error = nsURL
let isBadURLError: Bool
switch eURL {
@@ -162,7 +162,7 @@ ErrorBridgingTests.test("NSError-to-enum bridging") {
// CoreLocation error domain
let nsCL = NSError(domain: kCLErrorDomain,
code: CLError.headingFailure.rawValue,
userInfo: [NSURLErrorKey as NSObject: testURL])
userInfo: [AnyHashable(NSURLErrorKey): testURL])
let eCL: Error = nsCL
let isHeadingFailure: Bool
switch eCL {
@@ -318,7 +318,7 @@ class SomeNSErrorSubclass: NSError {}
ErrorBridgingTests.test("Thrown NSError identity is preserved") {
do {
let e = NSError(domain: "ClericalError", code: 219,
userInfo: ["yeah": "yeah"])
userInfo: [AnyHashable("yeah"): "yeah"])
do {
throw e
} catch let e2 as NSError {
@@ -331,7 +331,7 @@ ErrorBridgingTests.test("Thrown NSError identity is preserved") {
do {
let f = SomeNSErrorSubclass(domain: "ClericalError", code: 219,
userInfo: ["yeah": "yeah"])
userInfo: [AnyHashable("yeah"): "yeah"])
do {
throw f
} catch let f2 as NSError {

View File

@@ -18,7 +18,7 @@ NSEnumeratorAPI.test("Sequence") {
NSEnumeratorAPI.test("keyEnumerator") {
let result = [1 as NSNumber: "one", 2 as NSNumber: "two"]
expectEqualsUnordered(
[1, 2], NSDictionary(dictionary: result).keyEnumerator()) {
[1, 2], NSDictionary(dictionary: result as [AnyHashable: Any]).keyEnumerator()) {
switch ($0 as! Int, $1 as! Int) {
case let (x, y) where x == y: return .eq
case let (x, y) where x < y: return .lt

View File

@@ -16,12 +16,15 @@ tests.test("user info") {
let error = NSError(domain: "MyDomain", code: 1, userInfo: [
// CHECK-WARNINGS: warning: 'localizedDescriptionKey' is deprecated: renamed to 'NSLocalizedDescriptionKey'
// CHECK-WARNINGS: note: use 'NSLocalizedDescriptionKey' instead
ErrorUserInfoKey.localizedDescriptionKey.rawValue as NSString: "description",
NSLocalizedFailureReasonErrorKey as NSString: "reason"
AnyHashable(ErrorUserInfoKey.localizedDescriptionKey.rawValue): "description",
AnyHashable(NSLocalizedFailureReasonErrorKey): "reason"
])
expectEqual("description", error.userInfo[NSLocalizedDescriptionKey as NSString]! as! String)
expectEqual("reason", error.userInfo[ErrorUserInfoKey.localizedFailureReasonErrorKey.rawValue as NSObject]! as! String)
expectEqual("description", error.userInfo[NSLocalizedDescriptionKey]! as! String)
expectEqual("reason", error.userInfo[ErrorUserInfoKey.localizedFailureReasonErrorKey.rawValue]! as! String)
// TODO: Without the 'as NSObject' conversion, this produces nil.
// We may need to forward _CustomAnyHashable through swift_newtypes.
expectEqual("reason", error.userInfo[ErrorUserInfoKey.localizedFailureReasonErrorKey as NSObject]! as! String)
}

View File

@@ -35,11 +35,11 @@ func foo() {
_ = arrayToArray as (Array<Any>!) -> (Array<Any>!)
DummyClass().arrayProperty.onlyOnArray()
_ = dictToDict as (Dictionary<NSObject, Any>!) -> Dictionary<NSObject, Any>!
_ = dictToDict as (Dictionary<AnyHashable, Any>!) -> Dictionary<AnyHashable, Any>!
DummyClass().dictProperty.onlyOnDictionary()
_ = setToSet as (Set<NSObject>!) -> Set<NSObject>!
_ = setToSet as (Set<AnyHashable>!) -> Set<AnyHashable>!
DummyClass().setProperty.onlyOnSet()
}

View File

@@ -13,7 +13,7 @@ func testNSDictionaryBridging(_ hive: Hive) {
_ = hive.beesByName as [String : Bee] // expected-error{{value of optional type '[String : Bee]?' not unwrapped; did you mean to use '!' or '?'?}}
var dict1 = hive.anythingToBees
let dict2: [NSObject : Bee] = dict1
let dict2: [AnyHashable : Bee] = dict1
dict1 = dict2
}

View File

@@ -45,7 +45,9 @@ public func err() {
// DWARF-CHECK: DW_AT_name{{.*}}NSError
// DWARF-CHECK: DW_AT_linkage_name{{.*}}_TtCSo7NSError
let _ = NSError(domain: "myDomain", code: 4,
userInfo: ["a":1,"b":2,"c":3])
userInfo: [AnyHashable("a"):1,
AnyHashable("b"):2,
AnyHashable("c"):3])
}
// LOC-CHECK: define {{.*}}4date

View File

@@ -30,7 +30,7 @@
// Make sure that we don't qualify 'NSErrorPointer'.
// CHECK_NSSTRING: init(contentsOfFile path: String, encoding enc: UInt) throws
// CHECK_DICTIONARY: func propertyListFromStringsFileFormat() -> [NSObject : Any]
// CHECK_DICTIONARY: func propertyListFromStringsFileFormat() -> [AnyHashable : Any]
// RUN: %target-swift-ide-test -print-module -source-filename %s -module-to-print=Foundation -function-definitions=false > %t/Foundation.printed.txt
// RUN: FileCheck -input-file %t/Foundation.printed.txt -check-prefix=CHECK_DUP %s

View File

@@ -163,7 +163,7 @@
// CHECK-FOUNDATION: func normalizingXMLPreservingComments(_: Bool)
// Collection element types.
// CHECK-FOUNDATION: func adding(_: Any) -> Set<NSObject>
// CHECK-FOUNDATION: func adding(_: Any) -> Set<AnyHashable>
// Boolean properties follow the getter.
// CHECK-FOUNDATION: var empty: Bool { get }
@@ -218,8 +218,8 @@
// CHECK-APPKIT: func draw(in: NSView?)
// Note: NSDictionary default arguments for "options"
// CHECK-APPKIT: func drawAnywhere(in: NSView?, options: [NSObject : Any] = [:])
// CHECK-APPKIT: func drawAnywhere(options: [NSObject : Any] = [:])
// CHECK-APPKIT: func drawAnywhere(in: NSView?, options: [AnyHashable : Any] = [:])
// CHECK-APPKIT: func drawAnywhere(options: [AnyHashable : Any] = [:])
// Note: no lowercasing of initialisms when there might be a prefix.
// CHECK-CORECOOLING: func CFBottom() ->

View File

@@ -178,3 +178,24 @@ extension NSError : Error {
public var _code: Int { return code }
}
extension AnyHashable : _ObjectiveCBridgeable {
public func _bridgeToObjectiveC() -> NSObject {
fatalError()
}
public static func _forceBridgeFromObjectiveC(
_ x: NSObject,
result: inout AnyHashable?
) {
}
public static func _conditionallyBridgeFromObjectiveC(
_ x: NSObject,
result: inout AnyHashable?
) -> Bool {
fatalError()
}
public static func _unconditionallyBridgeFromObjectiveC(
_ x: NSObject?
) -> AnyHashable {
fatalError()
}
}

View File

@@ -73,27 +73,27 @@ func testNullUnspecified(_ obj: Test) -> [Any]! {
} // CHECK: {{^}$}}
// CHECK-LABEL: sil hidden @_TF20objc_bridged_results21testNonnullDictionaryFCSo4TestGVs10DictionaryCSo8NSObjectP__
func testNonnullDictionary(_ obj: Test) -> [NSObject: Any] {
// CHECK: [[METHOD:%[0-9]+]] = class_method [volatile] %0 : $Test, #Test.nonnullDictionary!getter.1.foreign : (Test) -> () -> [NSObject : Any] , $@convention(objc_method) (Test) -> @autoreleased Optional<NSDictionary>
// CHECK-LABEL: sil hidden @_TF20objc_bridged_results21testNonnullDictionaryFCSo4TestGVs10DictionaryVs11AnyHashableP__
func testNonnullDictionary(_ obj: Test) -> [AnyHashable: Any] {
// CHECK: [[METHOD:%[0-9]+]] = class_method [volatile] %0 : $Test, #Test.nonnullDictionary!getter.1.foreign : (Test) -> () -> [AnyHashable : Any] , $@convention(objc_method) (Test) -> @autoreleased Optional<NSDictionary>
// CHECK: [[COCOA_VAL:%[0-9]+]] = apply [[METHOD]](%0) : $@convention(objc_method) (Test) -> @autoreleased Optional<NSDictionary>
// CHECK: [[CONVERT:%[0-9]+]] = function_ref @_TZFE10FoundationVs10Dictionary36_unconditionallyBridgeFromObjectiveCfGSqCSo12NSDictionary_GS0_xq__
// CHECK: [[DICT_META:%[0-9]+]] = metatype $@thin Dictionary<NSObject, Any>.Type
// CHECK: [[RESULT:%[0-9]+]] = apply [[CONVERT]]<NSObject, Any>([[COCOA_VAL]], [[DICT_META]])
// CHECK: [[DICT_META:%[0-9]+]] = metatype $@thin Dictionary<AnyHashable, Any>.Type
// CHECK: [[RESULT:%[0-9]+]] = apply [[CONVERT]]<AnyHashable, Any>([[COCOA_VAL]], [[DICT_META]])
// CHECK: strong_release %0 : $Test
// CHECK: return [[RESULT]] : $Dictionary<NSObject, Any>
// CHECK: return [[RESULT]] : $Dictionary<AnyHashable, Any>
return obj.nonnullDictionary
} // CHECK: {{^}$}}
// CHECK-LABEL: sil hidden @_TF20objc_bridged_results14testNonnullSetFCSo4TestGVs3SetCSo8NSObject_
func testNonnullSet(_ obj: Test) -> Set<NSObject> {
// CHECK: [[METHOD:%[0-9]+]] = class_method [volatile] %0 : $Test, #Test.nonnullSet!getter.1.foreign : (Test) -> () -> Set<NSObject> , $@convention(objc_method) (Test) -> @autoreleased Optional<NSSet>
// CHECK-LABEL: sil hidden @_TF20objc_bridged_results14testNonnullSetFCSo4TestGVs3SetVs11AnyHashable_
func testNonnullSet(_ obj: Test) -> Set<AnyHashable> {
// CHECK: [[METHOD:%[0-9]+]] = class_method [volatile] %0 : $Test, #Test.nonnullSet!getter.1.foreign : (Test) -> () -> Set<AnyHashable> , $@convention(objc_method) (Test) -> @autoreleased Optional<NSSet>
// CHECK: [[COCOA_VAL:%[0-9]+]] = apply [[METHOD]](%0) : $@convention(objc_method) (Test) -> @autoreleased Optional<NSSet>
// CHECK: [[CONVERT:%[0-9]+]] = function_ref @_TZFE10FoundationVs3Set36_unconditionallyBridgeFromObjectiveCfGSqCSo5NSSet_GS0_x_
// CHECK: [[SET_META:%[0-9]+]] = metatype $@thin Set<NSObject>.Type
// CHECK: [[RESULT:%[0-9]+]] = apply [[CONVERT]]<NSObject>([[COCOA_VAL]], [[SET_META]])
// CHECK: [[SET_META:%[0-9]+]] = metatype $@thin Set<AnyHashable>.Type
// CHECK: [[RESULT:%[0-9]+]] = apply [[CONVERT]]<AnyHashable>([[COCOA_VAL]], [[SET_META]])
// CHECK: strong_release %0 : $Test
// CHECK: return [[RESULT]] : $Set<NSObject>
// CHECK: return [[RESULT]] : $Set<AnyHashable>
return obj.nonnullSet
} // CHECK: {{^}$}}