mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +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;
|
||||
}
|
||||
|
||||
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 *
|
||||
SwiftDeclConverter::importSwiftNewtype(const clang::TypedefNameDecl *decl,
|
||||
clang::SwiftNewtypeAttr *newtypeAttr,
|
||||
@@ -4802,9 +4847,11 @@ SwiftDeclConverter::importSwiftNewtype(const clang::TypedefNameDecl *decl,
|
||||
if (!proto)
|
||||
return;
|
||||
|
||||
SmallVector<ProtocolConformance *, 1> conformances;
|
||||
if (computedNominal->lookupConformance(computedNominal->getParentModule(),
|
||||
proto, conformances)) {
|
||||
// Break circularity by only looking for declared conformances in the
|
||||
// original module, or possibly its adapter.
|
||||
if (conformsToProtocolInOriginalModule(computedNominal, proto,
|
||||
Impl.tryLoadFoundationModule(),
|
||||
Impl.getTypeResolver())) {
|
||||
protocols.push_back(proto);
|
||||
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"
|
||||
}
|
||||
|
||||
module MoreSwiftNewtypes {
|
||||
header "MoreSwiftNewtypes.h"
|
||||
}
|
||||
|
||||
module Newtype {
|
||||
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
|
||||
|
||||
// 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 MoreSwiftNewtypes
|
||||
|
||||
func acceptEquatable<T: Equatable>(_: T) {}
|
||||
func acceptHashable<T: Hashable>(_: T) {}
|
||||
@@ -26,3 +29,11 @@ func testNewTypeWrapperComparable(x: NSNotification.Name, y: NSNotification.Name
|
||||
_ = x >= y
|
||||
_ = 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