mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[AST] Choose an implied conformance source next to the type, if possible.
If a conformance to a protocol is implied by several other
conformances (i.e. protocol P: Equatable {} and protocol Q: Equatable {} and a
type declares conformance to both P and Q), we should choose a source that's in
the same file as the type, if we can, because automatic synthesis of
conformances (for Equatable, Hashable, etc.) only works in that case.
Fixes rdar://problem/41852654.
This commit is contained in:
@@ -661,6 +661,19 @@ ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances(
|
||||
: Ordering::After;
|
||||
}
|
||||
|
||||
// If one of the conformances comes from the same file as the type
|
||||
// declaration, pick that one; this is so that conformance synthesis works if
|
||||
// there's any implied conformance in the same file as the type.
|
||||
auto NTD =
|
||||
lhs->getDeclContext()->getAsNominalTypeOrNominalTypeExtensionContext();
|
||||
auto typeSF = NTD->getParentSourceFile();
|
||||
if (typeSF) {
|
||||
if (typeSF == lhsSF)
|
||||
return Ordering::Before;
|
||||
if (typeSF == rhsSF)
|
||||
return Ordering::After;
|
||||
}
|
||||
|
||||
// Otherwise, pick the earlier file unit.
|
||||
auto lhsFileUnit
|
||||
= dyn_cast<FileUnit>(lhs->getDeclContext()->getModuleScopeContext());
|
||||
|
||||
@@ -23,3 +23,9 @@ enum GenericOtherFileNonconforming<T> {
|
||||
// expected-note@-1 {{type declared here}}
|
||||
case A(T)
|
||||
}
|
||||
|
||||
protocol ImplierOther: Equatable {}
|
||||
extension ImpliedMain: ImplierMain {}
|
||||
enum ImpliedOther: ImplierOther {
|
||||
case a(Int)
|
||||
}
|
||||
|
||||
@@ -22,3 +22,7 @@ struct GenericOtherFileNonconforming<T> {
|
||||
// expected-note@-1{{type declared here}}
|
||||
let v: T
|
||||
}
|
||||
|
||||
protocol ImplierOther: Equatable {}
|
||||
extension ImpliedMain: ImplierOther {}
|
||||
struct ImpliedOther: ImplierOther {}
|
||||
|
||||
@@ -314,6 +314,18 @@ extension UnusedGenericDeriveExtension: Hashable {}
|
||||
extension GenericOtherFileNonconforming: Equatable where T: Equatable {}
|
||||
// expected-error@-1{{implementation of 'Equatable' cannot be automatically synthesized in an extension in a different file to the type}}
|
||||
|
||||
// rdar://problem/41852654
|
||||
|
||||
// There is a conformance to Equatable (or at least, one that implies Equatable)
|
||||
// in the same file as the type, so the synthesis is okay. Both orderings are
|
||||
// tested, to catch choosing extensions based on the order of the files, etc.
|
||||
protocol ImplierMain: Equatable {}
|
||||
enum ImpliedMain: ImplierMain {
|
||||
case a(Int)
|
||||
}
|
||||
extension ImpliedOther: ImplierMain {}
|
||||
|
||||
|
||||
// FIXME: Remove -verify-ignore-unknown.
|
||||
// <unknown>:0: error: unexpected error produced: invalid redeclaration of 'hashValue'
|
||||
// <unknown>:0: error: unexpected note produced: candidate has non-matching type '(Foo, Foo) -> Bool'
|
||||
|
||||
@@ -253,6 +253,15 @@ extension UnusedGenericDeriveExtension: Hashable {}
|
||||
extension GenericOtherFileNonconforming: Equatable where T: Equatable {}
|
||||
// expected-error@-1{{implementation of 'Equatable' cannot be automatically synthesized in an extension in a different file to the type}}
|
||||
|
||||
// rdar://problem/41852654
|
||||
|
||||
// There is a conformance to Equatable (or at least, one that implies Equatable)
|
||||
// in the same file as the type, so the synthesis is okay. Both orderings are
|
||||
// tested, to catch choosing extensions based on the order of the files, etc.
|
||||
protocol ImplierMain: Equatable {}
|
||||
struct ImpliedMain: ImplierMain {}
|
||||
extension ImpliedOther: ImplierMain {}
|
||||
|
||||
// FIXME: Remove -verify-ignore-unknown.
|
||||
// <unknown>:0: error: unexpected error produced: invalid redeclaration of 'hashValue'
|
||||
// <unknown>:0: error: unexpected note produced: candidate has non-matching type '(Foo, Foo) -> Bool'
|
||||
|
||||
Reference in New Issue
Block a user