When a type declares a same-named property whose type is a refinement of a protocol requirement’s type, and the protocol provides a default implementation in an extension, Swift silently uses the default as the witness. The refined property is then ignored in generic/existential contexts. This follows current witness-matching rules, but the lack of a diagnostic makes it an easy footgun.
Minimal reproducer:
protocol LayoutDescriptor {}
struct ConcreteLayoutDescriptor: LayoutDescriptor { var value: Int }
protocol DefaultLayoutDescribing {
var defaultLayoutDescriptor: LayoutDescriptor { get }
}
extension DefaultLayoutDescribing {
var defaultLayoutDescriptor: LayoutDescriptor { ConcreteLayoutDescriptor(value: 0) }
}
struct MyPresenter: DefaultLayoutDescribing {
let defaultLayoutDescriptor = ConcreteLayoutDescriptor(value: 42)
}
let presenter: any DefaultLayoutDescribing = MyPresenter()
print("Direct:", MyPresenter().defaultLayoutDescriptor) // 42
print("Existential:", presenter.defaultLayoutDescriptor) // 0
The second print uses the extension default (value: 0), not the concrete declaration (value: 42). This compiles without any warning.
Why this is a problem:
- The developer clearly intends MyPresenter.defaultLayoutDescriptor to satisfy the protocol requirement. The name matches exactly, and ConcreteLayoutDescriptor conforms to
LayoutDescriptor. - Direct access works as expected, masking the issue in some testing scenarios.
- Without the extension default, the compiler correctly errors with "candidate has non-matching type" — so it knows these types don't match. But when a default implementation exists, it
silently falls back to it instead of warning.
Proposal:
Emit a “near-miss” warning when a member has the same name as a protocol requirement, is not selected as the witness due to a type mismatch, and the mismatched type is a more specific (aka refined) type that could satisfy the requirement via an implicit upcast (e.g. ConcreteLayoutDescriptor vs any LayoutDescriptor). If a default implementation is used instead, warn that the default will be the witness.
Something like:
warning: property 'defaultLayoutDescriptor' with type 'ConcreteLayoutDescriptor' does not satisfy requirement with type 'LayoutDescriptor'; default implementation will be used
note: add explicit type annotation 'LayoutDescriptor' to satisfy the requirement
The compiler already has near-miss diagnostics for @objc protocol conformances. This would extend that infrastructure to cover same-name type mismatches when a
default implementation silently takes over.
Prior discussion: This was reported as SR-10695 in 2019, where Jordan Rose suggested near-miss checking for this case. The issue was closed without the diagnostic being implemented.
Real-world impact: I found some instances of this pattern in our production iOS codebase, all silently using the wrong implementation.
I'd be happy to put together a PR to add a diagnostic for this if the direction seems right.