mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Sema: Relax diagnosis of implied marker protocol conformances with mismatched availability
This logic was introduced in https://github.com/swiftlang/swift/pull/75135. The intent was to prevent an implied conformance from overriding an existing unavailable one, for example in the case of Sendable. Let's relax this check a bit to only diagnose if the mismatch is in the unconditional availability, and not OS version. Fixes rdar://142873265.
This commit is contained in:
@@ -599,6 +599,11 @@ ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances(
|
||||
}
|
||||
}
|
||||
|
||||
auto isUnavailable = [](DeclContext *dc) -> bool {
|
||||
auto *ext = dyn_cast<ExtensionDecl>(dc);
|
||||
return ext && ext->isUnavailable();
|
||||
};
|
||||
|
||||
// If only one of the conformances is unconditionally available on the
|
||||
// current deployment target, pick that one.
|
||||
//
|
||||
@@ -610,7 +615,9 @@ ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances(
|
||||
rhs->getDeclContext()->isAlwaysAvailableConformanceContext()) {
|
||||
// Diagnose conflicting marker protocol conformances that differ in
|
||||
// un-availability.
|
||||
diagnoseSuperseded = lhs->getProtocol()->isMarkerProtocol();
|
||||
diagnoseSuperseded = (lhs->getProtocol()->isMarkerProtocol() &&
|
||||
isUnavailable(lhs->getDeclContext()) !=
|
||||
isUnavailable(rhs->getDeclContext()));
|
||||
|
||||
return (lhs->getDeclContext()->isAlwaysAvailableConformanceContext()
|
||||
? Ordering::Before
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
@available(macOS 200, *)
|
||||
extension Conformer1: Derived2 {}
|
||||
|
||||
@available(macOS 100, *)
|
||||
extension Conformer2: Derived1 {}
|
||||
// expected-error@-1 {{conformance of 'Conformer2' to 'Base' is only available in macOS 200 or newer}}
|
||||
|
||||
59
test/Sema/conformance_availability_implied.swift
Normal file
59
test/Sema/conformance_availability_implied.swift
Normal file
@@ -0,0 +1,59 @@
|
||||
// RUN: %target-typecheck-verify-swift -swift-version 6
|
||||
// REQUIRES: OS=macosx
|
||||
|
||||
// Protocols:
|
||||
|
||||
protocol Base {
|
||||
func f() // expected-note {{protocol requirement here}}
|
||||
}
|
||||
|
||||
func takesBase<T: Base>(_: T.Type) {}
|
||||
|
||||
protocol Derived1: Base {}
|
||||
protocol Derived2: Base {}
|
||||
|
||||
@_marker protocol MarkerBase {}
|
||||
|
||||
func takesMarkerBase<T: MarkerBase>(_: T.Type) {}
|
||||
|
||||
protocol MarkerDerived1: MarkerBase {}
|
||||
protocol MarkerDerived2: MarkerBase {}
|
||||
|
||||
// Verify that the implied conformance is macOS 100:
|
||||
struct Conformer1 {}
|
||||
|
||||
@available(macOS 100, *)
|
||||
extension Conformer1: Derived1 {
|
||||
func f() {} // okay!
|
||||
}
|
||||
|
||||
@available(macOS 200, *)
|
||||
extension Conformer1: Derived2 {}
|
||||
|
||||
takesBase(Conformer1.self)
|
||||
// expected-error@-1 {{conformance of 'Conformer1' to 'Base' is only available in macOS 100 or newer}}
|
||||
// expected-note@-2 {{add 'if #available' version check}}
|
||||
|
||||
// No warning about redundant MarkerBase conformance (rdar://142873265):
|
||||
@available(macOS 100, *)
|
||||
extension Conformer1: MarkerDerived1 {}
|
||||
|
||||
@available(macOS 200, *)
|
||||
extension Conformer1: MarkerDerived2 {}
|
||||
|
||||
takesMarkerBase(Conformer1.self)
|
||||
// expected-error@-1 {{conformance of 'Conformer1' to 'MarkerBase' is only available in macOS 100 or newer}}
|
||||
// expected-note@-2 {{add 'if #available' version check}}
|
||||
|
||||
// Bad availability on the Base.f() witness:
|
||||
struct Conformer2 {}
|
||||
|
||||
@available(macOS 100, *)
|
||||
extension Conformer2: Derived1 {
|
||||
// expected-error@-1 {{protocol 'Base' requires 'f()' to be available in macOS 100 and newer}}
|
||||
}
|
||||
|
||||
@available(macOS 200, *)
|
||||
extension Conformer2: Derived2 {
|
||||
func f() {} // expected-note {{'f()' declared here}}
|
||||
}
|
||||
48
test/Sema/conformance_availability_implied_multifile.swift
Normal file
48
test/Sema/conformance_availability_implied_multifile.swift
Normal file
@@ -0,0 +1,48 @@
|
||||
// RUN: %target-swift-frontend -typecheck -verify %s %S/Inputs/conformance_availability_implied_other.swift -swift-version 6
|
||||
// RUN: %target-swift-frontend -typecheck -verify %S/Inputs/conformance_availability_implied_other.swift %s -swift-version 6
|
||||
// REQUIRES: OS=macosx
|
||||
|
||||
protocol Base {
|
||||
func f()
|
||||
}
|
||||
|
||||
func takesBase<T: Base>(_: T.Type) {}
|
||||
|
||||
protocol Derived1: Base {}
|
||||
protocol Derived2: Base {}
|
||||
|
||||
// Verify that the implied conformance is macOS 100:
|
||||
struct Conformer1 {}
|
||||
|
||||
@available(macOS 100, *)
|
||||
extension Conformer1: Derived1 {
|
||||
func f() {} // okay!
|
||||
}
|
||||
|
||||
// Note that Conformer1: Derived2 is in the other file
|
||||
|
||||
func check1() {
|
||||
// expected-note@-1 {{add '@available' attribute to enclosing global function}}
|
||||
takesBase(Conformer1.self)
|
||||
// expected-error@-1 {{conformance of 'Conformer1' to 'Base' is only available in macOS 100 or newer}}
|
||||
// expected-note@-2 {{add 'if #available' version check}}
|
||||
}
|
||||
|
||||
// Verify that the implied conformance is macOS 200:
|
||||
// FIXME: This appears to be unsound!
|
||||
struct Conformer2 {}
|
||||
|
||||
@available(macOS 200, *)
|
||||
extension Conformer2: Derived2 {
|
||||
func f() {}
|
||||
}
|
||||
|
||||
// Note that Conformer2: Derived1 is in the other file
|
||||
|
||||
func check2() {
|
||||
// expected-note@-1 {{add '@available' attribute to enclosing global function}}
|
||||
takesBase(Conformer2.self)
|
||||
// expected-error@-1 {{conformance of 'Conformer2' to 'Base' is only available in macOS 200 or newer}}
|
||||
// expected-note@-2 {{add 'if #available' version check}}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user