Skip to content
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

Merged
merged 9 commits into from
Sep 6, 2017

Conversation

qnighy
Copy link
Contributor

@qnighy qnighy commented Jul 23, 2017

I'm going to tackle #23980.

Examples

Trait impl overlap caused by possible downstream impl

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

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

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

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

@rust-highfive
Copy link
Collaborator

r? @eddyb

(rust_highfive has picked a reviewer for you, use r? to override)

@eddyb
Copy link
Member

eddyb commented Jul 23, 2017

r? @nikomatsakis or @aturon

@rust-highfive rust-highfive assigned nikomatsakis and unassigned eddyb Jul 23, 2017
@nikomatsakis
Copy link
Contributor

This looks exciting! :)

@nikomatsakis
Copy link
Contributor

...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 {}",
Copy link
Contributor

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 {} \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Copy link
Contributor

@nikomatsakis nikomatsakis left a 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 {
Copy link
Contributor

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 {}",
Copy link
Contributor

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 `{}`

@qnighy
Copy link
Contributor Author

qnighy commented Jul 24, 2017

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:

  • assemble_candidates gives up early if Self is a typevar.
  • _: A and _: B each have an in-crate candidate because they are object-safe, whereas there are no in-crate solutions satisfying both of them.

@carols10cents carols10cents added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Jul 24, 2017
@qnighy
Copy link
Contributor Author

qnighy commented Jul 26, 2017

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
Copy link
Contributor

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similarly here

@nikomatsakis
Copy link
Contributor

I agree we don't have to solve all problems -- what do you think of the suggestions above though?

@qnighy
Copy link
Contributor Author

qnighy commented Aug 1, 2017

Improved message by printing trait-refs.

r? @nikomatsakis

Copy link
Contributor

@nikomatsakis nikomatsakis left a 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.

DownstreamCrate(DefId),
UpstreamCrateUpdate(DefId),
DownstreamCrate {
trait_desc: String,
Copy link
Contributor

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.

};
let cause = if
trait_ref.def_id.krate != LOCAL_CRATE &&
!self.tcx().has_attr(trait_ref.def_id, "fundamental") {
Copy link
Contributor

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?

@alexcrichton
Copy link
Member

ping @qnighy, just want to make sure this is still on your radar!

@arielb1
Copy link
Contributor

arielb1 commented Aug 22, 2017

I think this just needs that little nit to land. Are you going to close it @qnighy ?

@carols10cents
Copy link
Member

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.

@carols10cents carols10cents added S-inactive Status: Inactive and waiting on the author. This is often applied to closed PRs. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Aug 28, 2017
@nikomatsakis
Copy link
Contributor

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.

@nikomatsakis nikomatsakis reopened this Aug 28, 2017
@nikomatsakis
Copy link
Contributor

OK, I made the changes myself.

@nikomatsakis
Copy link
Contributor

@bors r+

@nikomatsakis
Copy link
Contributor

@bors r+

@bors
Copy link
Contributor

bors commented Sep 5, 2017

📌 Commit 37c9a60 has been approved by nikomatsakis

@arielb1 arielb1 added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Sep 5, 2017
@bors
Copy link
Contributor

bors commented Sep 5, 2017

⌛ Testing commit 37c9a60 with merge c47f8ca1419933d131d422084718f3864c361d2f...

@bors
Copy link
Contributor

bors commented Sep 5, 2017

⌛ Testing commit 37c9a60 with merge 51b907cdbe9cefb646c75bb4952468495218b056...

@bors
Copy link
Contributor

bors commented Sep 5, 2017

💔 Test failed - status-travis

@alexcrichton
Copy link
Member

@bors: retry

  • good ol' travis troubles

@bors
Copy link
Contributor

bors commented Sep 5, 2017

⌛ Testing commit 37c9a60 with merge faf99d5c0b80fa68131f2d0fb8db9d968e345950...

@bors
Copy link
Contributor

bors commented Sep 5, 2017

💔 Test failed - status-travis

@kennytm
Copy link
Member

kennytm commented Sep 5, 2017

@bors retry

check x86_64-apple-darwin timed out, no output for 30 minutes.

@bors
Copy link
Contributor

bors commented Sep 6, 2017

⌛ Testing commit 37c9a60 with merge f83d20e...

bors added a commit that referenced this pull request Sep 6, 2017
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
```
@bors
Copy link
Contributor

bors commented Sep 6, 2017

☀️ Test successful - status-appveyor, status-travis
Approved by: nikomatsakis
Pushing f83d20e to master...

@bors bors merged commit 37c9a60 into rust-lang:master Sep 6, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants