mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Work around Foundation NS_TYPED_ENUM bug
Consider code like:
```
// Foo.h
typealias NSString * FooKey NS_EXTENSIBLE_TYPED_ENUM;
// Foo.swift
extension FooKey { … }
```
When Swift binds the extension to `FooKey`, that forces ClangImporter to import `FooKey`. ClangImporter’s newtype logic, among other things, checks whether the underlying type (`Swift.String` here) is Objective-C bridgeable and, if so, makes `FooKey` bridgeable too.
But what happens if this code is actually *in* Foundation, which is where the `extension String: _ObjectiveCBridgeable` lives? Well, if the compiler has already bound that extension, it works fine…but if it hasn’t, `FooKey` ends up unbridgeable, which can cause both type checking failures and IRGen crashes when code tries to use its bridging capabilities. And these symptoms are sensitive to precise details of the order Swift happens to bind extensions in, so e.g. adding empty files to the project can make the bug appear or disappear. Spooky.
Add a narrow hack to ClangImporter (only active for types in Foundation) to *assume* that `String` is bridgeable even if the extension declaring this hasn’t been bound yet.
Fixes rdar://142693093.
This commit is contained in:
@@ -6335,6 +6335,19 @@ SwiftDeclConverter::importSwiftNewtype(const clang::TypedefNameDecl *decl,
|
||||
synthesizedProtocols.push_back(kind);
|
||||
return true;
|
||||
}
|
||||
// HACK: This method may be called before all extensions have been bound.
|
||||
// This is a problem for newtypes in Foundation, which is what provides the
|
||||
// `String: _ObjectiveCBridgeable` conformance; it can cause us to create
|
||||
// `String`-backed newtypes which aren't bridgeable, causing typecheck
|
||||
// failures and crashes down the line (rdar://142693093). Hardcode knowledge
|
||||
// that this conformance will exist.
|
||||
// FIXME: Defer adding conformances to newtypes instead of this. (#78731)
|
||||
if (structDecl->getModuleContext()->isFoundationModule()
|
||||
&& kind == KnownProtocolKind::ObjectiveCBridgeable
|
||||
&& computedNominal == ctx.getStringDecl()) {
|
||||
synthesizedProtocols.push_back(kind);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ func acceptHashable<T: Hashable>(_: T) {}
|
||||
func acceptComparable<T: Comparable>(_: T) {}
|
||||
// expected-note@-1 {{where 'T' = 'NSNotification.Name'}}
|
||||
|
||||
func testNewTypeWrapper(x: NSNotification.Name, y: NSNotification.Name) {
|
||||
func testNewTypeWrapper(x: NSNotification.Name, y: NSNotification.Name, z: NSFileAttributeKey) {
|
||||
acceptEquatable(x)
|
||||
acceptHashable(x)
|
||||
acceptComparable(x) // expected-error {{global function 'acceptComparable' requires that 'NSNotification.Name' conform to 'Comparable'}}
|
||||
@@ -28,6 +28,8 @@ func testNewTypeWrapper(x: NSNotification.Name, y: NSNotification.Name) {
|
||||
_ = x != y
|
||||
_ = x.hashValue
|
||||
_ = x as NSString
|
||||
|
||||
_ = z as NSString
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
|
||||
public let NSUTF8StringEncoding: UInt = 8
|
||||
|
||||
// This extension will cause ClangImporter/newtype_conformance.swift to fail
|
||||
// unless rdar://142693093 is fixed. To reproduce, it's important that this
|
||||
// extension come *before* the _ObjectiveCBridgeable extension for String.
|
||||
extension NSFileAttributeKey { }
|
||||
|
||||
extension AnyHashable : _ObjectiveCBridgeable {
|
||||
public func _bridgeToObjectiveC() -> NSObject {
|
||||
return NSObject()
|
||||
|
||||
@@ -867,6 +867,8 @@ extern void CGColorRelease(CGColorRef color) __attribute__((availability(macosx,
|
||||
|
||||
typedef NSString *_Nonnull NSNotificationName
|
||||
__attribute((swift_newtype(struct)));
|
||||
typedef NSString *_Nonnull NSFileAttributeKey
|
||||
__attribute((swift_newtype(struct)));
|
||||
|
||||
NS_SWIFT_UNAVAILABLE("Use NSXPCConnection instead")
|
||||
extern NSString * const NSConnectionReplyMode;
|
||||
|
||||
Reference in New Issue
Block a user