mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[ClangImporter] Avoid full conformance checks for swift_wrapper types.
Avoids yet another circular import issue: conformance on type X ->
associated types -> members -> members of superclass type (for naming
conflicts) -> SwiftWrapper type -> conformances on underlying type X.
Normally this circularity is fine, but I still want to put back the
assertion I reverted in 19a2ad5f17.
I unfortunately don't have a test for the actual failure here; it came
up on some Apple-internal code and heavily depended on both Foundation
and the standard library. I did confirm that this fixes it, and it
looks like it does so without causing any issues for swift_wrapper
types.
More rdar://problem/30785976
This commit is contained in:
@@ -4718,6 +4718,51 @@ Decl *SwiftDeclConverter::importCompatibilityTypeAlias(
|
|||||||
return alias;
|
return alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool inheritanceListContainsProtocol(ArrayRef<TypeLoc> inherited,
|
||||||
|
const ProtocolDecl *proto) {
|
||||||
|
return llvm::any_of(inherited, [proto](TypeLoc type) -> bool {
|
||||||
|
SmallVector<ProtocolDecl *, 8> protos;
|
||||||
|
(void)type.getType()->isExistentialType(protos);
|
||||||
|
return ProtocolType::visitAllProtocols(protos,
|
||||||
|
[proto](const ProtocolDecl *next) {
|
||||||
|
return next == proto;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool conformsToProtocolInOriginalModule(NominalTypeDecl *nominal,
|
||||||
|
const ProtocolDecl *proto,
|
||||||
|
ModuleDecl *foundationModule,
|
||||||
|
LazyResolver *resolver) {
|
||||||
|
if (resolver)
|
||||||
|
resolver->resolveInheritanceClause(nominal);
|
||||||
|
if (inheritanceListContainsProtocol(nominal->getInherited(), proto))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Only consider extensions from the original module...or from an overlay
|
||||||
|
// or the Swift half of a mixed-source framework.
|
||||||
|
const DeclContext *containingFile = nominal->getModuleScopeContext();
|
||||||
|
ModuleDecl *originalModule = containingFile->getParentModule();
|
||||||
|
|
||||||
|
ModuleDecl *adapterModule = nullptr;
|
||||||
|
if (auto *clangUnit = dyn_cast<ClangModuleUnit>(containingFile))
|
||||||
|
adapterModule = clangUnit->getAdapterModule();
|
||||||
|
|
||||||
|
for (ExtensionDecl *extension : nominal->getExtensions()) {
|
||||||
|
ModuleDecl *extensionModule = extension->getParentModule();
|
||||||
|
if (extensionModule != originalModule && extensionModule != adapterModule &&
|
||||||
|
extensionModule != foundationModule) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (resolver)
|
||||||
|
resolver->resolveInheritanceClause(extension);
|
||||||
|
if (inheritanceListContainsProtocol(extension->getInherited(), proto))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Decl *
|
Decl *
|
||||||
SwiftDeclConverter::importSwiftNewtype(const clang::TypedefNameDecl *decl,
|
SwiftDeclConverter::importSwiftNewtype(const clang::TypedefNameDecl *decl,
|
||||||
clang::SwiftNewtypeAttr *newtypeAttr,
|
clang::SwiftNewtypeAttr *newtypeAttr,
|
||||||
@@ -4802,9 +4847,11 @@ SwiftDeclConverter::importSwiftNewtype(const clang::TypedefNameDecl *decl,
|
|||||||
if (!proto)
|
if (!proto)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SmallVector<ProtocolConformance *, 1> conformances;
|
// Break circularity by only looking for declared conformances in the
|
||||||
if (computedNominal->lookupConformance(computedNominal->getParentModule(),
|
// original module, or possibly its adapter.
|
||||||
proto, conformances)) {
|
if (conformsToProtocolInOriginalModule(computedNominal, proto,
|
||||||
|
Impl.tryLoadFoundationModule(),
|
||||||
|
Impl.getTypeResolver())) {
|
||||||
protocols.push_back(proto);
|
protocols.push_back(proto);
|
||||||
synthesizedProtocols.push_back(kind);
|
synthesizedProtocols.push_back(kind);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
// Helper for newtype_conformance.swift
|
||||||
|
|
||||||
|
@_exported import MoreSwiftNewtypes
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension UnbridgedNonNSObject: Equatable /* but not Hashable */ {
|
||||||
|
public static func ==(lhs: UnbridgedNonNSObject, rhs: UnbridgedNonNSObject) -> Bool { return true }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick something other than "Equatable" to test that we're not just looking at
|
||||||
|
// immediately inherited protocols.
|
||||||
|
protocol EquatablePlus: Equatable {}
|
||||||
|
|
||||||
|
public struct BridgedValue : EquatablePlus /* but not Hashable */ {
|
||||||
|
public static func ==(lhs: BridgedValue, rhs: BridgedValue) -> Bool { return true }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BridgedValue: _ObjectiveCBridgeable {
|
||||||
|
public func _bridgeToObjectiveC() -> BridgedNonNSObject {
|
||||||
|
return BridgedNonNSObject()
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func _forceBridgeFromObjectiveC(
|
||||||
|
_ x: BridgedNonNSObject,
|
||||||
|
result: inout BridgedValue?) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func _conditionallyBridgeFromObjectiveC(
|
||||||
|
_ x: BridgedNonNSObject,
|
||||||
|
result: inout BridgedValue?
|
||||||
|
) -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func _unconditionallyBridgeFromObjectiveC(_ source: BridgedNonNSObject?)
|
||||||
|
-> BridgedValue {
|
||||||
|
var result: BridgedValue?
|
||||||
|
_forceBridgeFromObjectiveC(source!, result: &result)
|
||||||
|
return result!
|
||||||
|
}
|
||||||
|
}
|
||||||
24
test/ClangImporter/Inputs/MoreSwiftNewtypes_tests.swift
Normal file
24
test/ClangImporter/Inputs/MoreSwiftNewtypes_tests.swift
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// Helper for newtype_conformance.swift
|
||||||
|
|
||||||
|
@_exported import MoreSwiftNewtypes
|
||||||
|
|
||||||
|
func acceptEquatable<T: Equatable>(_: T) {}
|
||||||
|
|
||||||
|
func testEquatable(wrappedRef: WrappedRef, wrappedValue: WrappedValue) {
|
||||||
|
acceptEquatable(wrappedRef)
|
||||||
|
acceptEquatable(wrappedValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func intIfNonHashable<T>(_: T) -> Int { return 0 }
|
||||||
|
func intIfNonHashable<T: Hashable>(_: T) -> Bool { return false }
|
||||||
|
|
||||||
|
func testNotHashable(wrappedRef: WrappedRef, wrappedValue: WrappedValue) {
|
||||||
|
let refResult = intIfNonHashable(wrappedRef)
|
||||||
|
let _: Int = refResult
|
||||||
|
let valueResult = intIfNonHashable(wrappedValue)
|
||||||
|
let _: Int = valueResult
|
||||||
|
|
||||||
|
let baselineResult = intIfNonHashable(0)
|
||||||
|
let _: Bool = baselineResult
|
||||||
|
}
|
||||||
16
test/ClangImporter/Inputs/custom-modules/MoreSwiftNewtypes.h
Normal file
16
test/ClangImporter/Inputs/custom-modules/MoreSwiftNewtypes.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
@import Foundation;
|
||||||
|
|
||||||
|
__attribute__((objc_root_class))
|
||||||
|
@interface Base
|
||||||
|
- (instancetype)init;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface UnbridgedNonNSObject : Base
|
||||||
|
@end
|
||||||
|
|
||||||
|
__attribute__((swift_bridge("BridgedValue")))
|
||||||
|
@interface BridgedNonNSObject : Base
|
||||||
|
@end
|
||||||
|
|
||||||
|
typedef UnbridgedNonNSObject *WrappedRef __attribute__((swift_wrapper(struct)));
|
||||||
|
typedef BridgedNonNSObject *WrappedValue __attribute__((swift_wrapper(struct)));
|
||||||
@@ -69,6 +69,10 @@ module MissingHeader {
|
|||||||
header "this-header-does-not-exist.h"
|
header "this-header-does-not-exist.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module MoreSwiftNewtypes {
|
||||||
|
header "MoreSwiftNewtypes.h"
|
||||||
|
}
|
||||||
|
|
||||||
module Newtype {
|
module Newtype {
|
||||||
header "Newtype.h"
|
header "Newtype.h"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules %s
|
// RUN: rm -rf %t && mkdir -p %t
|
||||||
|
|
||||||
|
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -primary-file %S/Inputs/MoreSwiftNewtypes_conformances.swift %S/Inputs/MoreSwiftNewtypes_tests.swift -module-name MoreSwiftNewtypes
|
||||||
|
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules %S/Inputs/MoreSwiftNewtypes_conformances.swift -primary-file %S/Inputs/MoreSwiftNewtypes_tests.swift -module-name MoreSwiftNewtypes
|
||||||
|
|
||||||
|
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -I %S/Inputs/custom-modules -o %t %S/Inputs/MoreSwiftNewtypes_conformances.swift %S/Inputs/MoreSwiftNewtypes_tests.swift -module-name MoreSwiftNewtypes
|
||||||
|
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -I %t %s -verify
|
||||||
|
|
||||||
// REQUIRES: objc_interop
|
// REQUIRES: objc_interop
|
||||||
|
|
||||||
// This test can't use '-verify' mode, because the potential error wouldn't
|
|
||||||
// belong to any file.
|
|
||||||
// e.g.:
|
|
||||||
// <unknown>:0: error: type 'NSNotification.Name' does not conform to protocol 'Comparable'
|
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import MoreSwiftNewtypes
|
||||||
|
|
||||||
func acceptEquatable<T: Equatable>(_: T) {}
|
func acceptEquatable<T: Equatable>(_: T) {}
|
||||||
func acceptHashable<T: Hashable>(_: T) {}
|
func acceptHashable<T: Hashable>(_: T) {}
|
||||||
@@ -26,3 +29,11 @@ func testNewTypeWrapperComparable(x: NSNotification.Name, y: NSNotification.Name
|
|||||||
_ = x >= y
|
_ = x >= y
|
||||||
_ = x as NSString
|
_ = x as NSString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func testCustomWrappers(wrappedRef: WrappedRef, wrappedValue: WrappedValue) {
|
||||||
|
acceptEquatable(wrappedRef)
|
||||||
|
acceptEquatable(wrappedValue)
|
||||||
|
acceptHashable(wrappedRef) // expected-error {{does not conform to expected type 'Hashable'}}
|
||||||
|
acceptHashable(wrappedValue) // expected-error {{does not conform to expected type 'Hashable'}}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user