Trait method shadowing can make adding a method to a sealed trait a breaking change and silently change behaviour in downstream crates #128509
Labels
C-bug
Category: This is a bug.
T-compiler
Relevant to the compiler team, which will review and decide on the PR/issue.
T-lang
Relevant to the language team, which will review and decide on the PR/issue.
T-types
Relevant to the types team, which will review and decide on the PR/issue.
(I already reported this as a diagnostic bug at #127703, but I was advised to re-submit it as a "real" bug as this has more serious consequences than I initially thought, and updates to that issue would be unlikely to be noticed at this point. I'll say everything here so you don't have to read the previous issue.)
In short, the issue is that in a trait impl for a type, if a method exists in both a dependent trait for that type and for the trait being implemented, the method in the dependent trait is used with no warning. If the method only exists in the trait being implemented, the method from the trait itself is used. The switch from the latter to the first can be triggered with no changes to the crate itself by adding a matching method to the dependent trait in an upstream crate, or the upstream crate can cause a compile error in the downstream crate by adding a method with the same name as in the downstream crate's trait but with an incompatible signature. This does not use supertraits.
As an example, make a crate
a
with this code in itslib.rs
:(
TraitA
is sealed for illustrative purposes; sealing isn't required for this issue to happen)Now make a crate
b
that depends on cratea
and has this code in itsmain.rs
:This is the output when you
cargo run
crateb
:Now uncomment the commented lines in crate
a
. After this change,cargo run
on crateb
outputs this:Crate
b
changed from callingTraitB::do_more_things
to callingTraitA::do_more_things
. Unlike the example in the previous bug report, there are no warnings for unused functions here.TraitA
is a sealed trait, and the common wisdom is that just adding a new method to a sealed trait can't break downstream crates, but in this case it silently changed the logic of crateb
. Ifdo_more_things
wasunsafe
in both traits, this could even silently introduce undefined behavior if the preconditions for the two methods were different.If the signature of
TraitA::do_more_things
is changed to be incompatible withTraitB::do_more_things
, crateb
will fail to compile.This happens with associated functions used through
Self
as well as for methods.TraitA
doesn't have to be sealed.Playground link with another example from the previous bug report
The text was updated successfully, but these errors were encountered: