Skip to content

Commit d9346d8

Browse files
committed
Add check to compare an impl method type with trait bounds
closes rust-lang#2687
1 parent e99d523 commit d9346d8

File tree

4 files changed

+203
-17
lines changed

4 files changed

+203
-17
lines changed

Diff for: src/librustc/middle/typeck/check/mod.rs

+55-17
Original file line numberDiff line numberDiff line change
@@ -112,15 +112,16 @@ use middle::lang_items::TypeIdLangItem;
112112
use util::common::{block_query, indenter, loop_query};
113113
use util::ppaux;
114114
use util::ppaux::{UserString, Repr};
115-
use util::nodemap::{FnvHashMap, NodeMap};
115+
use util::nodemap::NodeMap;
116+
use collections::{HashMap, HashSet};
116117

117118
use std::cell::{Cell, RefCell};
118-
use collections::HashMap;
119119
use std::mem::replace;
120120
use std::result;
121121
use std::vec;
122122
use std::vec_ng::Vec;
123123
use std::vec_ng;
124+
use std::str::StrVector;
124125
use syntax::abi::AbiSet;
125126
use syntax::ast::{Provided, Required};
126127
use syntax::ast;
@@ -864,25 +865,62 @@ fn compare_impl_method(tcx: ty::ctxt,
864865
return;
865866
}
866867

867-
// FIXME(#2687)---we should be checking that the bounds of the
868-
// trait imply the bounds of the subtype, but it appears we
869-
// are...not checking this.
870-
if impl_param_def.bounds.trait_bounds.len() !=
871-
trait_param_def.bounds.trait_bounds.len()
872-
{
868+
// check that the bounds of the trait imply the bounds of the implementation type
869+
let mut enforced_bounds = HashSet::new();
870+
let mut unspecified_bounds = ~[];
871+
for impl_bound in impl_param_def.bounds.trait_bounds.iter() {
872+
let mut specified = false;
873+
for trait_bound in trait_param_def.bounds.trait_bounds.iter() {
874+
if !enforced_bounds.contains(&trait_bound.def_id) &&
875+
impl_bound.def_id == trait_bound.def_id {
876+
enforced_bounds.insert(trait_bound.def_id);
877+
specified = true;
878+
break
879+
} else {
880+
for s_trait_bound in ty::trait_supertraits(tcx, trait_bound.def_id).iter() {
881+
if !enforced_bounds.contains(&trait_bound.def_id) &&
882+
impl_bound.def_id == s_trait_bound.def_id {
883+
enforced_bounds.insert(trait_bound.def_id);
884+
specified = true;
885+
break
886+
}
887+
}
888+
}
889+
}
890+
if !specified {
891+
unspecified_bounds.push(format!("`{}`", impl_bound.repr(tcx)));
892+
}
893+
}
894+
895+
let unenforced_bounds: ~[~str] =
896+
trait_param_def.bounds.trait_bounds.iter().filter_map(|&trait_bound| {
897+
if !enforced_bounds.contains(&trait_bound.def_id) {
898+
Some(format!("`{}`", trait_bound.repr(tcx)))
899+
} else {
900+
None
901+
}
902+
}).collect();
903+
904+
if unenforced_bounds.len() > 0 {
905+
tcx.sess.span_err(
906+
impl_m_span,
907+
format!("in method `{method}`, \
908+
{nbounds, plural, =1{trait bound} other{trait bounds}} \
909+
{bounds} not enforced by this implementation",
910+
method = token::get_ident(trait_m.ident),
911+
nbounds = unenforced_bounds.len(),
912+
bounds = unenforced_bounds.connect(", ")));
913+
}
914+
if unspecified_bounds.len() > 0 {
873915
tcx.sess.span_err(
874916
impl_m_span,
875917
format!("in method `{method}`, \
876-
type parameter {typaram} has \
877-
{nimpl, plural, =1{# trait bound} other{# trait bounds}}, \
878-
but the corresponding type parameter in \
879-
the trait declaration has \
880-
{ntrait, plural, =1{# trait bound} other{# trait bounds}}",
918+
implementation {nbounds, plural, =1{bound} other{bounds}} \
919+
{bounds} {nbounds, plural, =1{was} other{were}} \
920+
not specified in trait definition",
881921
method = token::get_ident(trait_m.ident),
882-
typaram = i,
883-
nimpl = impl_param_def.bounds.trait_bounds.len(),
884-
ntrait = trait_param_def.bounds.trait_bounds.len()));
885-
return;
922+
nbounds = unspecified_bounds.len(),
923+
bounds = unspecified_bounds.connect(", ")));
886924
}
887925
}
888926

Diff for: src/librustc/middle/typeck/collect.rs

+11
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,17 @@ pub fn ty_generics(ccx: &CrateCtxt,
10341034
TraitTyParamBound(ref b) => {
10351035
let ty = ty::mk_param(ccx.tcx, param_ty.idx, param_ty.def_id);
10361036
let trait_ref = instantiate_trait_ref(ccx, b, ty);
1037+
if added_bounds.contains_key(&trait_ref.def_id) {
1038+
ccx.tcx.sess.span_warn(
1039+
b.path.span,
1040+
format!("duplicated bound `{}`, ignoring it",
1041+
trait_ref.repr(ccx.tcx)));
1042+
continue;
1043+
1044+
} else {
1045+
added_bounds.insert(trait_ref.def_id, ());
1046+
}
1047+
10371048
if !ty::try_add_builtin_trait(
10381049
ccx.tcx, trait_ref.def_id,
10391050
&mut param_bounds.builtin_bounds)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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 test_error6_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 bound `std::cmp::Eq` not enforced by this implementation
36+
//~^^ ERROR implementation bound `std::cmp::Ord` was not specified in trait definition
37+
38+
// invalid bound for T, was defined as Eq + Ord in trait
39+
fn test_error2_fn<T: Eq + B>(&self) {}
40+
//~^ ERROR bound `std::cmp::Ord` not enforced by this implementation
41+
//~^^ ERROR implementation bound `B` was not specified in trait definition
42+
43+
// invalid bound for T, was defined as Eq + Ord in trait
44+
fn test_error3_fn<T: B + Eq>(&self) {}
45+
//~^ ERROR bound `std::cmp::Ord` not enforced by this implementation
46+
//~^^ ERROR implementation bound `B` was not specified in trait definition
47+
48+
// multiple bounds, same order as in trait
49+
fn test3_fn<T: Ord + Eq>(&self) {}
50+
51+
// multiple bounds, different order as in trait
52+
fn test4_fn<T: Eq + Ord>(&self) {}
53+
54+
// parameters in impls must be equal or more general than in the defining trait
55+
fn test_error5_fn<T: B>(&self) {}
56+
//~^ ERROR bound `A` not enforced by this implementation
57+
//~^^ ERROR implementation bound `B` was not specified in trait definition
58+
59+
fn test_error6_fn<T: A>(&self) {}
60+
//~^ ERROR bound `std::cmp::Eq` not enforced by this implementation
61+
62+
fn test_error7_fn<T: A + Eq>(&self) {}
63+
//~^ ERROR implementation bound `std::cmp::Eq` was not specified in trait definition
64+
65+
fn test_error8_fn<T: C>(&self) {}
66+
//~^ ERROR implementation bound `C` was not specified in trait definition
67+
//~^^ ERROR bound `B` not enforced by this implementation
68+
69+
}
70+
71+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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 contravariant type parameters in implementations of traits, see #2687
12+
13+
trait A {
14+
fn a(&self) -> int;
15+
}
16+
17+
impl A for u8 {
18+
fn a(&self) -> int {
19+
8
20+
}
21+
}
22+
23+
impl A for u32 {
24+
fn a(&self) -> int {
25+
32
26+
}
27+
}
28+
29+
trait B: A {
30+
fn b(&self) -> int;
31+
}
32+
33+
impl B for u32 {
34+
fn b(&self) -> int {
35+
-1
36+
}
37+
}
38+
39+
trait Foo {
40+
fn test_fn<T: B>(&self, x: T) -> int;
41+
fn test_duplicated_bounds1_fn<T: B+B>(&self) -> int;
42+
//~^ warn: duplicated bound `B`, ignoring it
43+
fn test_duplicated_bounds2_fn<T: B>(&self) -> int;
44+
}
45+
46+
impl Foo for int {
47+
fn test_fn<T: A>(&self, x: T) -> int {
48+
x.a()
49+
}
50+
51+
fn test_duplicated_bounds1_fn<T: B+B>(&self) -> int {
52+
//~^ warn: duplicated bound `B`, ignoring it
53+
99
54+
}
55+
56+
fn test_duplicated_bounds2_fn<T: B+B>(&self) -> int {
57+
//~^ warn: duplicated bound `B`, ignoring it
58+
199
59+
}
60+
}
61+
62+
fn main() {
63+
let x: int = 0;
64+
assert!(8 == x.test_fn(5u8));
65+
assert!(32 == x.test_fn(5u32));
66+
}

0 commit comments

Comments
 (0)