-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add hints when intercrate ambiguity causes overlap. #43426
Conversation
r? @eddyb (rust_highfive has picked a reviewer for you, use r? to override) |
r? @nikomatsakis or @aturon |
This looks exciting! :) |
...and I didn't mean to close that... |
for cause in &overlap.intercrate_ambiguity_causes { | ||
match cause { | ||
&IntercrateAmbiguityCause::DownstreamCrate(def_id) => { | ||
err.note(&format!("downstream crates may implement {}", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: use backticks, like:
may implement `{}`
tcx.item_path_str(def_id))); | ||
} | ||
&IntercrateAmbiguityCause::UpstreamCrateUpdate(def_id) => { | ||
err.note(&format!("upstream crates may add new impl for {} \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left one nit, but the most important thing that is missing is tests! It would be great to have new tests in src/test/ui/coherence
(you'll have to create the directory) showing these various errors. You could for example move the cases from your PR description into tests. (You can read more about how UI tests work in the src/test/COMPILER_TESTS.md
file.)
I'll have to think about the general heuristic a bit and whether these errors will be misleading, but it seems quite clever to me at first glance. Making a bunch of tests for various scenarios, however, is always a good way to show that it generates good errors!
err.span_label(self.tcx.span_of_impl(item2).unwrap(), | ||
format!("other definition for `{}`", name)); | ||
|
||
for cause in &overlap.intercrate_ambiguity_causes { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: seems like we should extract this into some sort of helper; I feel like the same code was present elsewherein the PR, no?
for cause in &overlap.intercrate_ambiguity_causes { | ||
match cause { | ||
&IntercrateAmbiguityCause::DownstreamCrate(def_id) => { | ||
err.note(&format!("downstream crates may implement {}", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same nit: use backticks around {}
, like:
may implement `{}`
One problem I found by myself after submitting this PR is that it can't handle the example mentioned in #36162: trait A {}
trait B {}
trait Foo { }
impl<T: A> Foo for T { }
impl<T: B> Foo for T { } As far as I understand, there are several causes of this problem:
|
All changes done! For now I'm going to leave the problem I mentioned above... |
impl<T:Sugar> Cake<T> { fn dummy(&self) { } } | ||
//~^ ERROR E0592 | ||
//~| NOTE duplicate definitions for `dummy` | ||
//~| NOTE upstream crates may add new impl for `Sugar` in future versions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I think if we captured a bit more of the trait-ref, we could say something like "upstream crates may implement Sugar
for Box<_>
in the future", which might be more helpful, what do you think?
impl Foo for i16 {} | ||
//~^ ERROR E0119 | ||
//~| NOTE conflicting implementation for `i16` | ||
//~| NOTE upstream crates may add new impl for `coherence_lib::Remote` in future versions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
similarly here
I agree we don't have to solve all problems -- what do you think of the suggestions above though? |
Improved message by printing trait-refs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! I left one more minor nit, just a small refactoring.
src/librustc/traits/select.rs
Outdated
DownstreamCrate(DefId), | ||
UpstreamCrateUpdate(DefId), | ||
DownstreamCrate { | ||
trait_desc: String, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I wonder -- why pass a String
and not a Ty<'tcx>
? The latter has the advantage of being cheaper; but do we only generate these structs in error cases anyway? I guess it's fine then.
src/librustc/traits/select.rs
Outdated
}; | ||
let cause = if | ||
trait_ref.def_id.krate != LOCAL_CRATE && | ||
!self.tcx().has_attr(trait_ref.def_id, "fundamental") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: This feels a bit like a DRY violation -- this same logic appears in traits::trait_ref_is_knowable()
-- maybe factor it out into a helper? Something like trait_ref_permits_any_impl
?
ping @qnighy, just want to make sure this is still on your radar! |
I think this just needs that little nit to land. Are you going to close it @qnighy ? |
Hi @qnighy , thanks for the PR! Since you haven't said anything recently though, we're going to close this-- please feel free to reopen when you have a chance to take care of the last nit. |
Hmm, I hate to see this PR get closed for such a small nit. I could handle it myself, or we could probably even just land it as is, it's not that big a thing. |
OK, I made the changes myself. |
@bors r+ |
3d7cde6
to
37c9a60
Compare
@bors r+ |
📌 Commit 37c9a60 has been approved by |
⌛ Testing commit 37c9a60 with merge c47f8ca1419933d131d422084718f3864c361d2f... |
⌛ Testing commit 37c9a60 with merge 51b907cdbe9cefb646c75bb4952468495218b056... |
💔 Test failed - status-travis |
@bors: retry
|
⌛ Testing commit 37c9a60 with merge faf99d5c0b80fa68131f2d0fb8db9d968e345950... |
💔 Test failed - status-travis |
@bors retry
|
Add hints when intercrate ambiguity causes overlap. I'm going to tackle #23980. # Examples ## Trait impl overlap caused by possible downstream impl ```rust trait Foo<X> {} trait Bar<X> {} impl<X, T> Foo<X> for T where T: Bar<X> {} impl<X> Foo<X> for i32 {} fn main() {} ``` ``` error[E0119]: conflicting implementations of trait `Foo<_>` for type `i32`: --> test1.rs:4:1 | 3 | impl<X, T> Foo<X> for T where T: Bar<X> {} | ------------------------------------------ first implementation here 4 | impl<X> Foo<X> for i32 {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `i32` | = note: downstream crates may implement Bar error: aborting due to previous error ``` ## Trait impl overlap caused by possible upstream update ```rust trait Foo {} impl<T> Foo for T where T: ::std::fmt::Octal {} impl Foo for () {} fn main() {} ``` ``` error[E0119]: conflicting implementations of trait `Foo` for type `()`: --> test2.rs:3:1 | 2 | impl<T> Foo for T where T: ::std::fmt::Octal {} | ----------------------------------------------- first implementation here 3 | impl Foo for () {} | ^^^^^^^^^^^^^^^^^^ conflicting implementation for `()` | = note: upstream crates may add new impl for std::fmt::Octal in future versions error: aborting due to previous error ``` ## Inherent impl overlap caused by possible downstream impl ```rust trait Bar<X> {} struct A<T, X>(T, X); impl<X, T> A<T, X> where T: Bar<X> { fn f(&self) {} } impl<X> A<i32, X> { fn f(&self) {} } fn main() {} ``` ``` error[E0592]: duplicate definitions with name `f` --> test3.rs:4:38 | 4 | impl<X, T> A<T, X> where T: Bar<X> { fn f(&self) {} } | ^^^^^^^^^^^^^^ duplicate definitions for `f` 5 | impl<X> A<i32, X> { fn f(&self) {} } | -------------- other definition for `f` | = note: downstream crates may implement Bar error: aborting due to previous error ``` ## Inherent impl overlap caused by possible upstream update ```rust struct A<T>(T); impl<T> A<T> where T: ::std::fmt::Octal { fn f(&self) {} } impl A<()> { fn f(&self) {} } fn main() {} ``` ``` error[E0592]: duplicate definitions with name `f` --> test4.rs:3:43 | 3 | impl<T> A<T> where T: ::std::fmt::Octal { fn f(&self) {} } | ^^^^^^^^^^^^^^ duplicate definitions for `f` 4 | impl A<()> { fn f(&self) {} } | -------------- other definition for `f` | = note: upstream crates may add new impl for std::fmt::Octal in future versions error: aborting due to previous error ```
☀️ Test successful - status-appveyor, status-travis |
I'm going to tackle #23980.
Examples
Trait impl overlap caused by possible downstream impl
Trait impl overlap caused by possible upstream update
Inherent impl overlap caused by possible downstream impl
Inherent impl overlap caused by possible upstream update