-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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 test for comparing impl method type with trait bounds #12611
Conversation
I'm not sure this is quite exhaustive enough just yet, it looks like it would still allow code of the form: trait A {
fn foo<T: Eq + Ord>(&self);
}
impl A for int {
fn foo<T: Ord + Ord>(&self) {}
} |
|
||
struct Bar; | ||
impl Foo for Bar { | ||
fn test_fn<T:ParamTrait>(&self, _: T) {} //~ ERROR: in method |
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.
Can you test the error message a little more exhaustively than just "in method" ?
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've added a couple of more tests and better error message checks.
I think this case is missing too: trait A {}
trait B: A {}
trait C {
fn test<T: A>(&self) {}
}
impl C for int {
fn test<T: B>(&self) {}
}
fn main() { } In the example above the bounds of the method (unless I'm missing something) EDIT: I meant to say: trait A {}
trait B: A {}
trait C {
fn test<T: B>(&self) {}
}
impl C for int {
fn test<T: A>(&self) {}
}
fn main() { } |
I have a question concerning the following trait:
Are the following two implementations considered equivalent?
My PR at the moment assumes that the order must be equal as well (the second case would be invalid), but this behavior could be changed easily. |
@fhahn bounds are commutative |
@@ -874,6 +872,33 @@ fn compare_impl_method(tcx: ty::ctxt, | |||
trait_param_def.bounds.trait_bounds.len())); | |||
return; | |||
} | |||
for (i, impl_bound) in impl_param_def.bounds.trait_bounds.iter().enumerate() { |
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 think this for loop could be simplified by using ty::each_bound_trait_and_supertraits
. I'm sorry, I don't have much time to come up with a working example but I'd recommend you to take a look there, it iterates over the bounds and supertraits.
I'm not entirely convinced that this is the right algorithm, and travis seems to agree with me. I'm not sure where supetraits come in to this verification, but I may just not be understanding what's going on. |
@flaper87 I'm not sure if your example from above should be valid.
As far as I understand, trait bounds should work the following way, but I could easily miss something.
This implementation of trait |
Also, I'd like @nikomatsakis to double check this PR. |
ok. |
I've update the patch. It now accepts contravariant argument bounds in implementations. I guess this should be documented as well? I've added a simple run-pass testcase with contravariant arguments. The patch in it's current form requires the bounds to be unique, so the following definition would be problematic:
I'm not sure if a bound definition with non-unique traits should be valid. If they are valid, should |
@fhahn we should probably raise a warning for non-unique bounds and drop 1 of them. I don't think that should be valid. |
This needs to be rebased. @nikomatsakis any comments on this PR ? |
Sorry, I plan to review, but it will take me some time. This week is very busy. |
I've rebased the PR. It raises errors for the following two cases:
Type parameters in the implementation can be supertypes of the type parameters in the trait. The other commit raises a warning for duplicated type parameters and drops the duplicated parameter. One thing that isn't warned about at the moment are duplicated subtypes and supertypes. Should I document this behaviour? |
@@ -1010,11 +1012,23 @@ pub fn ty_generics(ccx: &CrateCtxt, | |||
builtin_bounds: ty::EmptyBuiltinBounds(), | |||
trait_bounds: ~[] | |||
}; | |||
|
|||
let mut added_bounds = HashMap::new(); |
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.
What about using a MutableSet
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.
MutableSet
is a trait??
You probably mean HashSet
.
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.
erm yeah, sorry! I meant HashSet
(I'm falling asleep right now)
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.
Just to make sure there's no confusion. I meant HashSet
Update: Which is implemented using a map
anyway, it just removes the thinking of map
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.
Thanks, I've changed HashMap
to HashSet
@nikomatsakis re-r? (just to make sure) |
Seems like travis has some legitimate failures. |
I've tried to use As far as I understand, But I guess I'm using |
Sorry, I'm very slow getting to reviews right now. This is on my list of "reviews to do", however. |
for impl_bound in impl_param_def.bounds.trait_bounds.iter() { | ||
let mut specified = false; | ||
for trait_bound in trait_param_def.bounds.trait_bounds.iter() { | ||
if !enforced_bounds.contains(&trait_bound.def_id) { |
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.
This still looks wrong. I don't think we can key on def-id. I think you just need to remove this set and do something simpler. I imagine two nested loops (here I wrote it using filter/any). Basically you want to know: Are there any impl bounds which are not implied by some trait bound? If so, that's an error.
let failed_bounds =
impl_bounds.filter(|ib| trait_bounds.any(|tb| implies(infcx, tb, ib)));
fn implies(infcx, trait_bound, impl_bound) {
is impl_bound a sub trait ref of trait_bound?
}
Note that this doesn't take the full generality into account that we could. e.g., If the trait has Ord and the impl has Eq, that should be ok. We should be able to do that too; it's a matter of expanding out the "trait_bounds" vector to account for super bounds (or possibly changing mk_sub_trait_refs, I've been debating that in my head just now). But I think we can leave that aside for now, it'd really only be important for #5236 which is not on the table presently.
ok, I apologize for the delay, I left some comments, ping me when updated, and find me on IRC if you have any questions! Thanks very much for working on this. |
Closing due to inactivity, but feel free to reopen with a rebase! |
with the corresponding trait parameter bounds. This is a version of the patch in PR rust-lang#12611 by Florian Hahn, modified to address Niko's feedback. It does not address the issue of duplicate type parameter bounds, nor does it address the issue of implementation-defined methods that contain *fewer* bounds than the trait, because Niko's review indicates that this should not be necessary (and indeed I believe it is not). A test has been added to ensure that this works. This will break code like: trait Foo { fn bar<T:Baz>(); } impl Foo for Boo { fn bar<T:Baz + Quux>() { ... } // ^~~~ ERROR } This will be rejected because the implementation requires *more* bounds than the trait. It can be fixed by either adding the missing bound to the trait: trait Foo { fn bar<T:Baz + Quux>(); // ^~~~ } impl Foo for Boo { fn bar<T:Baz + Quux>() { ... } // OK } Or by removing the bound from the impl: trait Foo { fn bar<T:Baz>(); } impl Foo for Boo { fn bar<T:Baz>() { ... } // OK // ^ remove Quux } This patch imports the relevant tests from rust-lang#2687, as well as the test case in rust-lang#5886, which is fixed as well by this patch. Closes rust-lang#2687. Closes rust-lang#5886. [breaking-change]
…felix with the corresponding trait parameter bounds. This is a version of the patch in PR #12611 by Florian Hahn, modified to address Niko's feedback. It does not address the issue of duplicate type parameter bounds, nor does it address the issue of implementation-defined methods that contain *fewer* bounds than the trait, because Niko's review indicates that this should not be necessary (and indeed I believe it is not). A test has been added to ensure that this works. This will break code like: trait Foo { fn bar<T:Baz>(); } impl Foo for Boo { fn bar<T:Baz + Quux>() { ... } // ^~~~ ERROR } This will be rejected because the implementation requires *more* bounds than the trait. It can be fixed by either adding the missing bound to the trait: trait Foo { fn bar<T:Baz + Quux>(); // ^~~~ } impl Foo for Boo { fn bar<T:Baz + Quux>() { ... } // OK } Or by removing the bound from the impl: trait Foo { fn bar<T:Baz>(); } impl Foo for Boo { fn bar<T:Baz>() { ... } // OK // ^ remove Quux } This patch imports the relevant tests from #2687, as well as the test case in #5886, which is fixed as well by this patch. Closes #2687. Closes #5886. [breaking-change] r? @pnkfelix
closes #2687
This patch checks if all type bounds of an argument in an implementation of a trait can be found in the trait definition.
I'm not entirely sure about the wording of the error message though.