Skip to content

Commit 520d6b9

Browse files
authored
Make NS_TYPED_ENUMS ObjectiveCBridgeable when they wrap an object (#15270)
This allows them to be used in generic arguments for NSArray et al. We already do this for the ones that wrap bridged values (like NSString/String), but failed to do it for objects that /weren't/ bridged to Swift values (class instances and protocol compositions), or for Error-which-is-special. In addition to this being a sensible thing to do, /not/ doing this led to IRGen getting very confused (i.e. crashing) when we imported a Objective-C protocol that actually used an NS_TYPED_ENUM in this way. (We actually shouldn't be using Swift's IRGen logic to emit protocol descriptors for imported protocols at all, because it's possible we weren't able to import all the requirements. But that's a separate issue.) https://bugs.swift.org/browse/SR-6844
1 parent 682004b commit 520d6b9

File tree

15 files changed

+428
-8
lines changed

15 files changed

+428
-8
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5358,8 +5358,37 @@ SwiftDeclConverter::importSwiftNewtype(const clang::TypedefNameDecl *decl,
53585358
// implementation in the standard library.
53595359
transferKnown(KnownProtocolKind::Equatable);
53605360
transferKnown(KnownProtocolKind::Hashable);
5361-
bool transferredObjCBridgeable =
5362-
transferKnown(KnownProtocolKind::ObjectiveCBridgeable);
5361+
bool hasObjCBridgeable =
5362+
transferKnown(KnownProtocolKind::ObjectiveCBridgeable);
5363+
bool wantsObjCBridgeableTypealias = hasObjCBridgeable && isBridged;
5364+
5365+
// Wrappers around ObjC classes and protocols are also bridgeable.
5366+
if (!hasObjCBridgeable) {
5367+
if (isBridged) {
5368+
if (auto *proto = dyn_cast_or_null<ProtocolDecl>(computedNominal))
5369+
if (proto->getKnownProtocolKind() == KnownProtocolKind::Error)
5370+
hasObjCBridgeable = true;
5371+
} else {
5372+
if (auto *objcClass = dyn_cast_or_null<ClassDecl>(computedNominal)) {
5373+
switch (objcClass->getForeignClassKind()) {
5374+
case ClassDecl::ForeignKind::Normal:
5375+
case ClassDecl::ForeignKind::RuntimeOnly:
5376+
if (objcClass->hasClangNode())
5377+
hasObjCBridgeable = true;
5378+
break;
5379+
case ClassDecl::ForeignKind::CFType:
5380+
break;
5381+
}
5382+
} else if (storedUnderlyingType->isObjCExistentialType()) {
5383+
hasObjCBridgeable = true;
5384+
}
5385+
}
5386+
5387+
if (hasObjCBridgeable) {
5388+
addKnown(KnownProtocolKind::ObjectiveCBridgeable);
5389+
wantsObjCBridgeableTypealias = true;
5390+
}
5391+
}
53635392

53645393
if (!isBridged) {
53655394
// Simple, our stored type is equivalent to our computed
@@ -5377,10 +5406,11 @@ SwiftDeclConverter::importSwiftNewtype(const clang::TypedefNameDecl *decl,
53775406
computedPropertyUnderlyingType,
53785407
synthesizedProtocols,
53795408
/*makeUnlabeledValueInit=*/unlabeledCtor);
5409+
}
53805410

5381-
if (transferredObjCBridgeable)
5382-
addSynthesizedTypealias(structDecl, ctx.Id_ObjectiveCType,
5383-
storedUnderlyingType);
5411+
if (wantsObjCBridgeableTypealias) {
5412+
addSynthesizedTypealias(structDecl, ctx.Id_ObjectiveCType,
5413+
storedUnderlyingType);
53845414
}
53855415

53865416
Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = structDecl;

stdlib/public/SDK/Foundation/NSError.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,38 @@ extension _BridgedStoredNSError {
526526
}
527527
}
528528

529+
extension _SwiftNewtypeWrapper where Self.RawValue == Error {
530+
@_inlineable // FIXME(sil-serialize-all)
531+
public func _bridgeToObjectiveC() -> NSError {
532+
return rawValue as NSError
533+
}
534+
535+
@_inlineable // FIXME(sil-serialize-all)
536+
public static func _forceBridgeFromObjectiveC(
537+
_ source: NSError,
538+
result: inout Self?
539+
) {
540+
result = Self(rawValue: source)
541+
}
542+
543+
@_inlineable // FIXME(sil-serialize-all)
544+
public static func _conditionallyBridgeFromObjectiveC(
545+
_ source: NSError,
546+
result: inout Self?
547+
) -> Bool {
548+
result = Self(rawValue: source)
549+
return result != nil
550+
}
551+
552+
@_inlineable // FIXME(sil-serialize-all)
553+
public static func _unconditionallyBridgeFromObjectiveC(
554+
_ source: NSError?
555+
) -> Self {
556+
return Self(rawValue: _convertNSErrorToError(source))!
557+
}
558+
}
559+
560+
529561
@available(*, unavailable, renamed: "CocoaError")
530562
public typealias NSCocoaError = CocoaError
531563

@@ -3219,6 +3251,7 @@ extension MachError {
32193251
}
32203252

32213253
public struct ErrorUserInfoKey : RawRepresentable, _SwiftNewtypeWrapper, Equatable, Hashable, _ObjectiveCBridgeable {
3254+
public typealias _ObjectiveCType = NSString
32223255
public init(rawValue: String) { self.rawValue = rawValue }
32233256
public var rawValue: String
32243257
}

stdlib/public/core/NewtypeWrapper.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ extension _SwiftNewtypeWrapper where Self.RawValue : Hashable {
2424

2525
#if _runtime(_ObjC)
2626
extension _SwiftNewtypeWrapper where Self.RawValue : _ObjectiveCBridgeable {
27+
// Note: This is the only default typealias for _ObjectiveCType, because
28+
// constrained extensions aren't allowed to define types in different ways.
29+
// Fortunately the others don't need it.
2730
public typealias _ObjectiveCType = Self.RawValue._ObjectiveCType
2831

2932
@_inlineable // FIXME(sil-serialize-all)
@@ -61,5 +64,36 @@ extension _SwiftNewtypeWrapper where Self.RawValue : _ObjectiveCBridgeable {
6164
rawValue: Self.RawValue._unconditionallyBridgeFromObjectiveC(source))!
6265
}
6366
}
67+
68+
extension _SwiftNewtypeWrapper where Self.RawValue: AnyObject {
69+
@_inlineable // FIXME(sil-serialize-all)
70+
public func _bridgeToObjectiveC() -> Self.RawValue {
71+
return rawValue
72+
}
73+
74+
@_inlineable // FIXME(sil-serialize-all)
75+
public static func _forceBridgeFromObjectiveC(
76+
_ source: Self.RawValue,
77+
result: inout Self?
78+
) {
79+
result = Self(rawValue: source)
80+
}
81+
82+
@_inlineable // FIXME(sil-serialize-all)
83+
public static func _conditionallyBridgeFromObjectiveC(
84+
_ source: Self.RawValue,
85+
result: inout Self?
86+
) -> Bool {
87+
result = Self(rawValue: source)
88+
return result != nil
89+
}
90+
91+
@_inlineable // FIXME(sil-serialize-all)
92+
public static func _unconditionallyBridgeFromObjectiveC(
93+
_ source: Self.RawValue?
94+
) -> Self {
95+
return Self(rawValue: source!)!
96+
}
97+
}
6498
#endif
6599

test/ClangImporter/Inputs/custom-modules/Newtype.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,8 @@ __attribute((swift_name("NSSomeContext.Name")));
102102

103103
extern const NSSomeContextName NSMyContextName;
104104

105+
106+
typedef NSError *ErrorNewType __attribute((swift_newtype(struct)));
107+
108+
void testErrorDictionary(NSDictionary<NSError *, NSString *> * _Nonnull);
109+
void testErrorDictionaryNewtype(NSDictionary<ErrorNewType, NSString *> * _Nonnull);

test/ClangImporter/objc_parse.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,10 @@ class NewtypeUser {
651651
@objc func stringNewtypeOptional(a: SNTErrorDomain?) {} // expected-error {{'SNTErrorDomain' has been renamed to 'ErrorDomain'}}{{39-53=ErrorDomain}}
652652
@objc func intNewtype(a: MyInt) {}
653653
@objc func intNewtypeOptional(a: MyInt?) {} // expected-error {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}}
654+
@objc func intNewtypeArray(a: [MyInt]) {} // expected-error {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}}
655+
@objc func intNewtypeDictionary(a: [MyInt: NSObject]) {} // expected-error {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}}
656+
@objc func cfNewtype(a: CFNewType) {}
657+
@objc func cfNewtypeArray(a: [CFNewType]) {} // expected-error {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}}
654658
}
655659

656660
func testTypeAndValue() {
@@ -676,4 +680,14 @@ func testNonTrivialStructs() {
676680
_ = NonTrivialToCopy() // expected-error {{use of unresolved identifier 'NonTrivialToCopy'}}
677681
_ = NonTrivialToCopyWrapper() // expected-error {{use of unresolved identifier 'NonTrivialToCopyWrapper'}}
678682
_ = TrivialToCopy() // okay
679-
}
683+
}
684+
685+
func testErrorNewtype() {
686+
_ = ErrorNewType(3) // expected-error {{argument type 'Int' does not conform to expected type 'Error'}}
687+
688+
// Since we import NSError as Error, and Error is not Hashable...we end up
689+
// losing the types for these functions, even though the above assignment
690+
// works.
691+
testErrorDictionary(3) // expected-error {{cannot convert value of type 'Int' to expected argument type '[AnyHashable : String]'}}
692+
testErrorDictionaryNewtype(3) // expected-error {{cannot convert value of type 'Int' to expected argument type '[AnyHashable : String]'}}
693+
}

test/IDE/print_omit_needless_words.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
// CHECK-FOUNDATION: func copy(with: NSZone? = nil) -> Any!
4747

4848
// Note: Objective-C type parameter names.
49-
// CHECK-FOUNDATION: func object(forKey: NSCopying) -> Any?
49+
// CHECK-FOUNDATION: func object(forKey: Any) -> Any?
5050
// CHECK-FOUNDATION: func removeObject(forKey: NSCopying)
5151

5252
// Note: Don't drop the name of the first parameter in an initializer entirely.

test/Inputs/clang-importer-sdk/swift-modules-without-ns/Foundation.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,34 @@ public func _convertErrorToNSError(_ error: Error) -> NSError {
184184
return error as NSError
185185
}
186186

187+
extension _SwiftNewtypeWrapper where Self.RawValue == Error {
188+
@_inlineable // FIXME(sil-serialize-all)
189+
public func _bridgeToObjectiveC() -> NSError {
190+
return rawValue as NSError
191+
}
192+
193+
@_inlineable // FIXME(sil-serialize-all)
194+
public static func _forceBridgeFromObjectiveC(
195+
_ source: NSError,
196+
result: inout Self?
197+
) {
198+
result = Self(rawValue: source)
199+
}
200+
201+
@_inlineable // FIXME(sil-serialize-all)
202+
public static func _conditionallyBridgeFromObjectiveC(
203+
_ source: NSError,
204+
result: inout Self?
205+
) -> Bool {
206+
result = Self(rawValue: source)
207+
return result != nil
208+
}
209+
210+
@_inlineable // FIXME(sil-serialize-all)
211+
public static func _unconditionallyBridgeFromObjectiveC(
212+
_ source: NSError?
213+
) -> Self {
214+
return Self(rawValue: _convertNSErrorToError(source))!
215+
}
216+
}
217+

test/Inputs/clang-importer-sdk/swift-modules/Foundation.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,38 @@ public func _convertErrorToNSError(_ x: Error) -> NSError {
245245
return x as NSError
246246
}
247247

248+
extension _SwiftNewtypeWrapper where Self.RawValue == Error {
249+
@_inlineable // FIXME(sil-serialize-all)
250+
public func _bridgeToObjectiveC() -> NSError {
251+
return rawValue as NSError
252+
}
253+
254+
@_inlineable // FIXME(sil-serialize-all)
255+
public static func _forceBridgeFromObjectiveC(
256+
_ source: NSError,
257+
result: inout Self?
258+
) {
259+
result = Self(rawValue: source)
260+
}
261+
262+
@_inlineable // FIXME(sil-serialize-all)
263+
public static func _conditionallyBridgeFromObjectiveC(
264+
_ source: NSError,
265+
result: inout Self?
266+
) -> Bool {
267+
result = Self(rawValue: source)
268+
return result != nil
269+
}
270+
271+
@_inlineable // FIXME(sil-serialize-all)
272+
public static func _unconditionallyBridgeFromObjectiveC(
273+
_ source: NSError?
274+
) -> Self {
275+
return Self(rawValue: _convertNSErrorToError(source))!
276+
}
277+
}
278+
279+
248280

249281
extension NSArray {
250282
@objc(methodIntroducedInOverlay) public func introducedInOverlay() { }

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ __attribute__((availability(ios,introduced=8.0)))
150150
- (id)copyWithZone:(nullable NSZone *)zone;
151151
@end
152152

153-
@interface NSDictionary<KeyType : id<NSCopying>, ObjectType> : NSObject /*<NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>*/
153+
@interface NSDictionary<KeyType, ObjectType> : NSObject /*<NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>*/
154154
@property (readonly) NSUInteger count;
155155
- (nullable ObjectType)objectForKey:(nonnull KeyType)aKey;
156156
- (nonnull NSEnumerator *)keyEnumerator;
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
@import Foundation;
2+
3+
#define GET_UNTYPED_AND_OPAQUE(Name) \
4+
static NSArray * _Nonnull getUntypedNSArrayOf##Name##s() { \
5+
return getTypedNSArrayOf##Name##Wrappers(); \
6+
} \
7+
static id _Nonnull getOpaqueNSArrayOf##Name##s() { \
8+
return getTypedNSArrayOf##Name##Wrappers(); \
9+
}
10+
#define CHECK_UNTYPED_AND_OPAQUE(Name) \
11+
static BOOL checkUntypedNSArrayOf##Name##s(NSArray * _Nonnull array) { \
12+
return checkTypedNSArrayOf##Name##Wrappers(array); \
13+
} \
14+
static BOOL checkOpaqueNSArrayOf##Name##s(id _Nonnull array) { \
15+
return checkTypedNSArrayOf##Name##Wrappers(array); \
16+
}
17+
18+
typedef NSString *StringWrapper NS_TYPED_EXTENSIBLE_ENUM;
19+
static NSArray<StringWrapper> * _Nonnull getTypedNSArrayOfStringWrappers() {
20+
return @[@"abc", @"def"];
21+
}
22+
static BOOL
23+
checkTypedNSArrayOfStringWrappers(NSArray<StringWrapper> * _Nonnull array) {
24+
return [array isEqual:getTypedNSArrayOfStringWrappers()];
25+
}
26+
GET_UNTYPED_AND_OPAQUE(String)
27+
CHECK_UNTYPED_AND_OPAQUE(String)
28+
29+
30+
typedef id <NSCopying, NSCoding> CopyingAndCodingWrapper
31+
NS_TYPED_EXTENSIBLE_ENUM;
32+
static NSArray<CopyingAndCodingWrapper> * _Nonnull
33+
getTypedNSArrayOfCopyingAndCodingWrappers() {
34+
return @[@"abc", @[]];
35+
}
36+
static BOOL checkTypedNSArrayOfCopyingAndCodingWrappers(
37+
NSArray<CopyingAndCodingWrapper> * _Nonnull array) {
38+
return [array isEqual:getTypedNSArrayOfCopyingAndCodingWrappers()];
39+
}
40+
GET_UNTYPED_AND_OPAQUE(CopyingAndCoding)
41+
CHECK_UNTYPED_AND_OPAQUE(CopyingAndCoding)
42+
43+
typedef id <NSCopying> CopyingWrapper NS_TYPED_EXTENSIBLE_ENUM;
44+
static NSArray<CopyingWrapper> * _Nonnull getTypedNSArrayOfCopyingWrappers() {
45+
return getTypedNSArrayOfCopyingAndCodingWrappers();
46+
}
47+
static BOOL
48+
checkTypedNSArrayOfCopyingWrappers(NSArray<CopyingWrapper> * _Nonnull array) {
49+
return [array isEqual:getTypedNSArrayOfCopyingWrappers()];
50+
}
51+
GET_UNTYPED_AND_OPAQUE(Copying)
52+
CHECK_UNTYPED_AND_OPAQUE(Copying)
53+
54+
typedef NSObject *ObjectWrapper NS_TYPED_EXTENSIBLE_ENUM;
55+
static NSArray<ObjectWrapper> * _Nonnull getTypedNSArrayOfObjectWrappers() {
56+
return (NSArray<ObjectWrapper> *)getTypedNSArrayOfCopyingAndCodingWrappers();
57+
}
58+
static BOOL
59+
checkTypedNSArrayOfObjectWrappers(NSArray<ObjectWrapper> * _Nonnull array) {
60+
return [array isEqual:getTypedNSArrayOfObjectWrappers()];
61+
}
62+
GET_UNTYPED_AND_OPAQUE(Object)
63+
CHECK_UNTYPED_AND_OPAQUE(Object)
64+
65+
typedef NSError *ErrorWrapper NS_TYPED_EXTENSIBLE_ENUM;
66+
static NSArray<ErrorWrapper> * _Nonnull getTypedNSArrayOfErrorWrappers() {
67+
return @[[NSError errorWithDomain:@"x" code:11 userInfo:nil],
68+
[NSError errorWithDomain:@"x" code:22 userInfo:nil]];
69+
}
70+
static BOOL
71+
checkTypedNSArrayOfErrorWrappers(NSArray<ErrorWrapper> * _Nonnull array) {
72+
return [[array valueForKey:@"code"] isEqual:@[@11, @22]] &&
73+
NSNotFound == [array indexOfObjectPassingTest:^BOOL(ErrorWrapper error,
74+
NSUInteger idx,
75+
BOOL *stop) {
76+
return ![error isKindOfClass:[NSError class]];
77+
}];
78+
}
79+
GET_UNTYPED_AND_OPAQUE(Error)
80+
CHECK_UNTYPED_AND_OPAQUE(Error)

0 commit comments

Comments
 (0)