[Clang importer] Make sure that the first argument of Set/Dictionary types are Hashable.

Extend the check to make sure that the first type argument to an
imported Set or Dictionary type is Hashable actually checks
struct/enum types for Hashable conformances.

Fixes rdar://problem/30622665.
This commit is contained in:
Doug Gregor
2017-06-26 13:08:39 -07:00
parent 946b150398
commit aa215e7e54
5 changed files with 29 additions and 8 deletions

View File

@@ -984,13 +984,12 @@ namespace {
return Type();
// The first type argument for Dictionary or Set needs
// to be Hashable. Everything that inherits NSObject has a
// -hash code in ObjC, but if something isn't NSObject, fall back
// to be Hashable. If something isn't Hashable, 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)) {
if (!Impl.matchesHashableBound(keyType)) {
if (auto anyHashable = Impl.SwiftContext.getAnyHashableDecl())
keyType = anyHashable->getDeclaredType();
else
@@ -2413,7 +2412,7 @@ Type ClangImporter::Implementation::getNSObjectType() {
return Type();
}
bool ClangImporter::Implementation::matchesNSObjectBound(Type type) {
bool ClangImporter::Implementation::matchesHashableBound(Type type) {
Type NSObjectType = getNSObjectType();
if (!NSObjectType)
return false;
@@ -2425,8 +2424,14 @@ bool ClangImporter::Implementation::matchesNSObjectBound(Type type) {
// Struct or enum type must have been bridged.
// TODO: Check that the bridged type is Hashable?
if (type->getStructOrBoundGenericStruct() ||
type->getEnumOrBoundGenericEnum())
return true;
type->getEnumOrBoundGenericEnum()) {
auto nominal = type->getAnyNominal();
auto hashable = SwiftContext.getProtocol(KnownProtocolKind::Hashable);
SmallVector<ProtocolConformance *, 2> conformances;
return hashable &&
nominal->lookupConformance(nominal->getParentModule(), hashable,
conformances);
}
return false;
}

View File

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

View File

@@ -0,0 +1,6 @@
@import ObjectiveC;
@import Foundation;
@interface ObjCBridgeNonconforming
@property NSSet<NSDictionary<NSString *, id> *> * _Nonnull foo;
@end

View File

@@ -175,3 +175,7 @@ module MacrosRedefB {
module IndirectFields {
header "IndirectFields.h"
}
module ObjCBridgeNonconforming {
header "ObjCBridgeNonconforming.h"
}

View File

@@ -1,9 +1,10 @@
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -parse-as-library -verify -swift-version 4 %s
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -parse-as-library -verify -swift-version 4 -I %S/Inputs/custom-modules %s
// REQUIRES: objc_interop
import Foundation
import objc_generics
import ObjCBridgeNonconforming
func testNSArrayBridging(_ hive: Hive) {
_ = hive.bees as [Bee]
@@ -392,3 +393,8 @@ let third: Third! = Third()
func useThird() {
_ = third.description
}
func testNonconforming(bnc: ObjCBridgeNonconforming) {
let _: Int = bnc.foo // expected-error{{cannot convert value of type 'Set<AnyHashable>' to specified type 'Int'}}
}