Skip to content

Commit e56dbad

Browse files
committed
librustc: Properly compare implementation method type parameter bounds
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]
1 parent 3806575 commit e56dbad

File tree

4 files changed

+200
-43
lines changed

4 files changed

+200
-43
lines changed

src/librustc/middle/typeck/check/mod.rs

+64-43
Original file line numberDiff line numberDiff line change
@@ -920,49 +920,6 @@ fn compare_impl_method(tcx: &ty::ctxt,
920920
let it = trait_m.generics.types.get_vec(subst::FnSpace).iter()
921921
.zip(impl_m.generics.types.get_vec(subst::FnSpace).iter());
922922

923-
for (i, (trait_param_def, impl_param_def)) in it.enumerate() {
924-
// Check that the impl does not require any builtin-bounds
925-
// that the trait does not guarantee:
926-
let extra_bounds =
927-
impl_param_def.bounds.builtin_bounds -
928-
trait_param_def.bounds.builtin_bounds;
929-
if !extra_bounds.is_empty() {
930-
tcx.sess.span_err(
931-
impl_m_span,
932-
format!("in method `{}`, \
933-
type parameter {} requires `{}`, \
934-
which is not required by \
935-
the corresponding type parameter \
936-
in the trait declaration",
937-
token::get_ident(trait_m.ident),
938-
i,
939-
extra_bounds.user_string(tcx)).as_slice());
940-
return;
941-
}
942-
943-
// FIXME(#2687)---we should be checking that the bounds of the
944-
// trait imply the bounds of the subtype, but it appears we
945-
// are...not checking this.
946-
if impl_param_def.bounds.trait_bounds.len() !=
947-
trait_param_def.bounds.trait_bounds.len()
948-
{
949-
let found = impl_param_def.bounds.trait_bounds.len();
950-
let expected = trait_param_def.bounds.trait_bounds.len();
951-
tcx.sess.span_err(
952-
impl_m_span,
953-
format!("in method `{}`, type parameter {} has {} trait \
954-
bound{}, but the corresponding type parameter in \
955-
the trait declaration has {} trait bound{}",
956-
token::get_ident(trait_m.ident),
957-
i,
958-
found,
959-
if found == 1 {""} else {"s"},
960-
expected,
961-
if expected == 1 {""} else {"s"}).as_slice());
962-
return;
963-
}
964-
}
965-
966923
// This code is best explained by example. Consider a trait:
967924
//
968925
// trait Trait<T> {
@@ -1037,6 +994,70 @@ fn compare_impl_method(tcx: &ty::ctxt,
1037994
let trait_fty = ty::mk_bare_fn(tcx, trait_m.fty.clone());
1038995
let trait_fty = trait_fty.subst(tcx, &trait_to_skol_substs);
1039996

997+
// Check bounds.
998+
for (i, (trait_param_def, impl_param_def)) in it.enumerate() {
999+
// Check that the impl does not require any builtin-bounds
1000+
// that the trait does not guarantee:
1001+
let extra_bounds =
1002+
impl_param_def.bounds.builtin_bounds -
1003+
trait_param_def.bounds.builtin_bounds;
1004+
if !extra_bounds.is_empty() {
1005+
tcx.sess.span_err(
1006+
impl_m_span,
1007+
format!("in method `{}`, \
1008+
type parameter {} requires `{}`, \
1009+
which is not required by \
1010+
the corresponding type parameter \
1011+
in the trait declaration",
1012+
token::get_ident(trait_m.ident),
1013+
i,
1014+
extra_bounds.user_string(tcx)).as_slice());
1015+
return;
1016+
}
1017+
1018+
// Check that the trait bounds of the trait imply the bounds of its
1019+
// implementation.
1020+
//
1021+
// FIXME(pcwalton): We could be laxer here regarding sub- and super-
1022+
// traits, but I doubt that'll be wanted often, so meh.
1023+
for impl_trait_bound in impl_param_def.bounds.trait_bounds.iter() {
1024+
let impl_trait_bound =
1025+
impl_trait_bound.subst(tcx, &impl_to_skol_substs);
1026+
1027+
let mut ok = false;
1028+
for trait_bound in trait_param_def.bounds.trait_bounds.iter() {
1029+
let trait_bound =
1030+
trait_bound.subst(tcx, &trait_to_skol_substs);
1031+
let infcx = infer::new_infer_ctxt(tcx);
1032+
match infer::mk_sub_trait_refs(&infcx,
1033+
true,
1034+
infer::Misc(impl_m_span),
1035+
trait_bound,
1036+
impl_trait_bound.clone()) {
1037+
Ok(_) => {
1038+
ok = true;
1039+
break
1040+
}
1041+
Err(_) => continue,
1042+
}
1043+
}
1044+
1045+
if !ok {
1046+
tcx.sess.span_err(impl_m_span,
1047+
format!("in method `{}`, type parameter {} \
1048+
requires bound `{}`, which is not \
1049+
required by the corresponding \
1050+
type parameter in the trait \
1051+
declaration",
1052+
token::get_ident(trait_m.ident),
1053+
i,
1054+
ppaux::trait_ref_to_str(
1055+
tcx,
1056+
&*impl_trait_bound)).as_slice())
1057+
}
1058+
}
1059+
}
1060+
10401061
// Check the impl method type IM is a subtype of the trait method
10411062
// type TM. To see why this makes sense, think of a vtable. The
10421063
// expected type of the function pointers in the vtable is the
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
//
11+
// Make sure rustc checks the type parameter bounds in implementations of traits,
12+
// see #2687
13+
14+
trait A {}
15+
16+
trait B: A {}
17+
18+
trait C: A {}
19+
20+
trait Foo {
21+
fn test_error1_fn<T: Eq>(&self);
22+
fn test_error2_fn<T: Eq + Ord>(&self);
23+
fn test_error3_fn<T: Eq + Ord>(&self);
24+
fn test3_fn<T: Eq + Ord>(&self);
25+
fn test4_fn<T: Eq + Ord>(&self);
26+
fn test_error5_fn<T: A>(&self);
27+
fn test6_fn<T: A + Eq>(&self);
28+
fn test_error7_fn<T: A>(&self);
29+
fn test_error8_fn<T: B>(&self);
30+
}
31+
32+
impl Foo for int {
33+
// invalid bound for T, was defined as Eq in trait
34+
fn test_error1_fn<T: Ord>(&self) {}
35+
//~^ ERROR in method `test_error1_fn`, type parameter 0 requires bound `core::cmp::Ord`
36+
37+
// invalid bound for T, was defined as Eq + Ord in trait
38+
fn test_error2_fn<T: Eq + B>(&self) {}
39+
//~^ ERROR in method `test_error2_fn`, type parameter 0 requires bound `B`
40+
41+
// invalid bound for T, was defined as Eq + Ord in trait
42+
fn test_error3_fn<T: B + Eq>(&self) {}
43+
//~^ ERROR in method `test_error3_fn`, type parameter 0 requires bound `B`
44+
45+
// multiple bounds, same order as in trait
46+
fn test3_fn<T: Ord + Eq>(&self) {}
47+
48+
// multiple bounds, different order as in trait
49+
fn test4_fn<T: Eq + Ord>(&self) {}
50+
51+
// parameters in impls must be equal or more general than in the defining trait
52+
fn test_error5_fn<T: B>(&self) {}
53+
//~^ ERROR in method `test_error5_fn`, type parameter 0 requires bound `B`
54+
55+
// bound `std::cmp::Eq` not enforced by this implementation, but this is OK
56+
fn test6_fn<T: A>(&self) {}
57+
58+
fn test_error7_fn<T: A + Eq>(&self) {}
59+
//~^ ERROR in method `test_error7_fn`, type parameter 0 requires bound `core::cmp::Eq`
60+
61+
fn test_error8_fn<T: C>(&self) {}
62+
//~^ ERROR in method `test_error8_fn`, type parameter 0 requires bound `C`
63+
}
64+
65+
66+
trait Getter<T> { }
67+
68+
trait Trait {
69+
fn method<G:Getter<int>>();
70+
}
71+
72+
impl Trait for uint {
73+
fn method<G: Getter<uint>>() {}
74+
//~^ ERROR in method `method`, type parameter 0 requires bound `Getter<uint>`
75+
}
76+
77+
fn main() {}
78+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Issue #5886: a complex instance of issue #2687.
12+
13+
trait Iterator<A> {
14+
fn next(&mut self) -> Option<A>;
15+
}
16+
17+
trait IteratorUtil<A> {
18+
fn zip<B, U: Iterator<U>>(self, other: U) -> ZipIterator<Self, U>;
19+
}
20+
21+
impl<A, T: Iterator<A>> IteratorUtil<A> for T {
22+
fn zip<B, U: Iterator<B>>(self, other: U) -> ZipIterator<T, U> {
23+
//~^ ERROR in method `zip`, type parameter 1 requires bound `Iterator<B>`
24+
ZipIterator{a: self, b: other}
25+
}
26+
}
27+
28+
struct ZipIterator<T, U> {
29+
a: T, b: U
30+
}
31+
32+
fn main() {}
33+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Tests that type parameter bounds on an implementation need not match the
12+
// trait exactly, as long as the implementation doesn't demand *more* bounds
13+
// than the trait.
14+
15+
trait A {
16+
fn foo<T: Eq + Ord>(&self);
17+
}
18+
19+
impl A for int {
20+
fn foo<T: Ord + Ord>(&self) {}
21+
}
22+
23+
fn main() {}
24+
25+

0 commit comments

Comments
 (0)