Skip to content

Commit 56e2e13

Browse files
committed
Targeted fix for pulling in Hashable conformance for Set and Dictionary when a nil literal is bridged
When bridging values via the ObjectiveCBridgeable protocol, SILGen has to look up conformances directly, behind the type checker's back. To make sure the ObjectiveCBridgeable conformance is forced, Sema calls useObjectiveCBridgeableConformances() in the right places. However, another conformance we may also need when bridging is the Hashable conformance for a Set or Dictionary's key type. Make sure we force these too, because otherwise when bridging a nil literal nothing needs them in Sema. Fixes <rdar://problem/27470505>.
1 parent 83f2eb0 commit 56e2e13

File tree

5 files changed

+56
-5
lines changed

5 files changed

+56
-5
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4246,13 +4246,25 @@ void TypeChecker::useObjectiveCBridgeableConformances(DeclContext *dc,
42464246
: TC(tc), DC(dc), Proto(proto) { }
42474247

42484248
virtual Action walkToTypePre(Type ty) {
4249+
ConformanceCheckOptions options = ConformanceCheckFlags::InExpression
4250+
| ConformanceCheckFlags::Used
4251+
| ConformanceCheckFlags::SuppressDependencyTracking;
4252+
42494253
// If we have a nominal type, "use" its conformance to
42504254
// _ObjectiveCBridgeable if it has one.
4251-
if (ty->getAnyNominal()) {
4252-
ConformanceCheckOptions options = ConformanceCheckFlags::InExpression
4253-
| ConformanceCheckFlags::Used
4254-
| ConformanceCheckFlags::SuppressDependencyTracking;
4255+
if (auto *nominalDecl = ty->getAnyNominal()) {
42554256
(void)TC.conformsToProtocol(ty, Proto, DC, options);
4257+
4258+
if (nominalDecl == TC.Context.getSetDecl() ||
4259+
nominalDecl == TC.Context.getDictionaryDecl()) {
4260+
auto args = ty->castTo<BoundGenericType>()->getGenericArgs();
4261+
if (!args.empty()) {
4262+
auto keyType = args[0];
4263+
auto *hashableProto =
4264+
TC.Context.getProtocol(KnownProtocolKind::Hashable);
4265+
(void)TC.conformsToProtocol(keyType, hashableProto, DC, options);
4266+
}
4267+
}
42564268
}
42574269

42584270
return Action::Continue;

test/IRGen/objc_generic_class_metadata.sil

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ entry(%0: $Subclass):
7171
unreachable
7272
}
7373

74+
sil @_TToFC27objc_generic_class_metadata8SubclasscfT7optionsGSqGVs10DictionaryVSC13GenericOptionP____GSQS0__ : $@convention(objc_method) (@owned Subclass, @owned NSDictionary) -> @owned Subclass {
75+
entry(%0: $Subclass, %1: $NSDictionary):
76+
unreachable
77+
}
78+
7479
// CHECK-LABEL: define linkonce_odr hidden %swift.type* @_TMaCSo12GenericClass()
7580
// CHECK: [[T0:%.*]] = load %objc_class*, %objc_class** @"OBJC_CLASS_REF_$_GenericClass",
7681
// CHECK: call %objc_class* @rt_swift_getInitializedObjCClass(%objc_class* [[T0]])

test/Inputs/clang-importer-sdk/usr/include/objc_generics.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
#import <Foundation.h>
22

3+
#define _CF_TYPED_ENUM __attribute__((swift_wrapper(enum)))
4+
#define NS_STRING_ENUM _CF_TYPED_ENUM
5+
#define NS_SWIFT_NAME(Name) __attribute__((swift_name(#Name)))
6+
7+
typedef NSString * GenericOption NS_STRING_ENUM;
8+
9+
GenericOption const GenericOptionMultithreaded NS_SWIFT_NAME(multithreaded);
10+
11+
312
@interface GenericClass<T> : NSObject
413
- (id)initWithThing:(T)thing;
514
- (id)initWithArrayOfThings:(NSArray<T> *__nonnull)things;
15+
- (id)initWithOptions:(nullable NSDictionary<GenericOption, id> *)options;
616
- (void)dealloc;
717
- (__nullable T)thing;
818
- (int)count;

test/SILGen/objc_imported_generic.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,5 +103,29 @@ func genericFunc<V: AnyObject>(_ v: V.Type) {
103103
}
104104
}
105105

106+
// CHECK-LABEL: sil hidden @_TF21objc_imported_generic23configureWithoutOptionsFT_T_ : $@convention(thin) () -> ()
107+
// CHECK: [[NIL_FN:%.*]] = function_ref @_TFSqCfT10nilLiteralT__GSqx_ : $@convention(method) <τ_0_0> (@thin Optional<τ_0_0>.Type) -> @out Optional<τ_0_0>
108+
// CHECK: apply [[NIL_FN]]<[GenericOption : Any]>({{.*}})
109+
// CHECK: return
110+
func configureWithoutOptions() {
111+
_ = GenericClass<NSObject>(options: nil)
112+
}
113+
114+
// foreign to native thunk for init(options:), uses GenericOption : Hashable
115+
// conformance
116+
117+
// CHECK-LABEL: sil shared [thunk] @_TTOFCSo12GenericClasscfT7optionsGSqGVs10DictionaryVSC13GenericOptionP____GSQGS_x__ : $@convention(method) <T where T : AnyObject> (@owned Optional<Dictionary<GenericOption, Any>>, @owned GenericClass<T>) -> @owned ImplicitlyUnwrappedOptional<GenericClass<T>>
118+
// CHECK: [[FN:%.*]] = function_ref @_TFE10FoundationVs10Dictionary19_bridgeToObjectiveCfT_CSo12NSDictionary : $@convention(method) <τ_0_0, τ_0_1 where τ_0_0 : Hashable> (@guaranteed Dictionary<τ_0_0, τ_0_1>) -> @owned NSDictionary
119+
// CHECK: apply [[FN]]<GenericOption, Any>({{.*}}) : $@convention(method) <τ_0_0, τ_0_1 where τ_0_0 : Hashable> (@guaranteed Dictionary<τ_0_0, τ_0_1>) -> @owned NSDictionary
120+
// CHECK: return
121+
122+
// This gets emitted down here for some reason
123+
106124
// CHECK-LABEL: sil shared [thunk] @_TTOFCSo12GenericClasscfT13arrayOfThings
107125
// CHECK: class_method [volatile] {{%.*}} : $GenericClass<T>, #GenericClass.init!initializer.1.foreign {{.*}}, $@convention(objc_method) @pseudogeneric <τ_0_0 where τ_0_0 : AnyObject> (NSArray, @owned GenericClass<τ_0_0>) -> @owned ImplicitlyUnwrappedOptional<GenericClass<τ_0_0>>
126+
127+
// Make sure we emitted the witness table for the above conformance
128+
129+
// CHECK-LABEL: sil_witness_table shared [fragile] GenericOption: Hashable module objc_generics {
130+
// CHECK: method #Hashable.hashValue!getter.1: @_TTWVSC13GenericOptions8Hashable13objc_genericsFS0_g9hashValueSi
131+
// CHECK: }

validation-test/stdlib/SceneKit.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ if #available(iOS 8.0, *) {
346346
let sceneData = sceneDescription.data(
347347
using: .utf8,
348348
allowLossyConversion: true)!
349-
let sceneSource = SCNSceneSource(data: sceneData as Data, options: [:])!
349+
let sceneSource = SCNSceneSource(data: sceneData as Data, options: nil)!
350350

351351
do {
352352
var unarchivedPlaneGeometry =

0 commit comments

Comments
 (0)