mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
ClangImporter: Import unbounded NSSets and NSDictionaries using AnyHashable.
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:)
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() ->
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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: {{^}$}}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user