Skip to content

Commit ac1f84c

Browse files
committed
Don't check impl ty params for equality with trait ty params
This was too restrictive. We need to check the number of ty params, and that the bounds are equal, but otherwise require_same_types does the job. Closes #2611
1 parent c6b5154 commit ac1f84c

File tree

7 files changed

+115
-9
lines changed

7 files changed

+115
-9
lines changed

src/rustc/middle/typeck/collect.rs

+31-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ use astconv::{ast_conv, ty_of_fn_decl, ty_of_arg, ast_ty_to_ty};
2424
use ast_util::trait_method_to_ty_method;
2525
use rscope::*;
2626
use ty::{FnTyBase, FnMeta, FnSig};
27+
use util::common::pluralize;
28+
use util::ppaux::bound_to_str;
2729

2830
fn collect_item_types(ccx: @crate_ctxt, crate: @ast::crate) {
2931

@@ -263,9 +265,13 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span,
263265
self type", tcx.sess.str_of(impl_m.ident)));
264266
}
265267

266-
if impl_m.tps != trait_m.tps {
267-
tcx.sess.span_err(sp, ~"method `" + tcx.sess.str_of(trait_m.ident) +
268-
~"` has an incompatible set of type parameters");
268+
if impl_m.tps.len() != trait_m.tps.len() {
269+
tcx.sess.span_err(sp, #fmt("method `%s` \
270+
has %u type %s, but its trait declaration has %u type %s",
271+
tcx.sess.str_of(trait_m.ident), impl_m.tps.len(),
272+
pluralize(impl_m.tps.len(), ~"parameter"),
273+
trait_m.tps.len(),
274+
pluralize(trait_m.tps.len(), ~"parameter")));
269275
return;
270276
}
271277

@@ -278,6 +284,28 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span,
278284
return;
279285
}
280286

287+
for trait_m.tps.eachi() |i, trait_param_bounds| {
288+
// For each of the corresponding impl ty param's bounds...
289+
let impl_param_bounds = impl_m.tps[i];
290+
// Make sure the bounds lists have the same length
291+
// Would be nice to use the ty param names in the error message,
292+
// but we don't have easy access to them here
293+
if impl_param_bounds.len() != trait_param_bounds.len() {
294+
tcx.sess.span_err(sp, #fmt("in method `%s`, \
295+
type parameter %u has %u %s, but the same type \
296+
parameter in its trait declaration has %u %s",
297+
tcx.sess.str_of(trait_m.ident),
298+
i, impl_param_bounds.len(),
299+
pluralize(impl_param_bounds.len(), ~"bound"),
300+
trait_param_bounds.len(),
301+
pluralize(trait_param_bounds.len(), ~"bound")));
302+
return;
303+
}
304+
// tjc: I'm mildly worried that there's something I'm
305+
// not checking that require_same_types doesn't catch,
306+
// but I can't figure out what.
307+
}
308+
281309
// Perform substitutions so that the trait/impl methods are expressed
282310
// in terms of the same set of type/region parameters:
283311
// - replace trait type parameters with those from `trait_substs`

src/rustc/util/common.rs

+5
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ fn is_main_name(path: syntax::ast_map::path) -> bool {
9494
)
9595
}
9696

97+
fn pluralize(n: uint, s: ~str) -> ~str {
98+
if n == 1 { s }
99+
else { str::concat([s, ~"s"]) }
100+
}
101+
97102
//
98103
// Local Variables:
99104
// mode: rust

src/rustc/util/ppaux.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
use std::map::hashmap;
22
use middle::ty;
33
use middle::ty::{arg, canon_mode};
4+
use middle::ty::{bound_copy, bound_const, bound_owned, bound_send,
5+
bound_trait};
46
use middle::ty::{bound_region, br_anon, br_named, br_self, br_cap_avoid};
57
use middle::ty::{ck_block, ck_box, ck_uniq, ctxt, field, method};
6-
use middle::ty::{mt, t};
8+
use middle::ty::{mt, t, param_bound};
79
use middle::ty::{re_bound, re_free, re_scope, re_var, re_static, region};
810
use middle::ty::{ty_bool, ty_bot, ty_box, ty_class, ty_enum};
911
use middle::ty::{ty_estr, ty_evec, ty_float, ty_fn, ty_trait, ty_int};
@@ -233,6 +235,16 @@ fn tys_to_str(cx: ctxt, ts: ~[t]) -> ~str {
233235
rs
234236
}
235237

238+
fn bound_to_str(cx: ctxt, b: param_bound) -> ~str {
239+
match b {
240+
bound_copy => ~"copy",
241+
bound_owned => ~"owned",
242+
bound_send => ~"send",
243+
bound_const => ~"const",
244+
bound_trait(t) => ty_to_str(cx, t)
245+
}
246+
}
247+
236248
fn ty_to_str(cx: ctxt, typ: t) -> ~str {
237249
fn fn_input_to_str(cx: ctxt, input: {mode: ast::mode, ty: t}) ->
238250
~str {

src/test/compile-fail/issue-2611-3.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Tests that impl methods are matched to traits exactly:
2+
// we might be tempted to think matching is contravariant, but if
3+
// we let an impl method can have more permissive bounds than the trait
4+
// method it's implementing, the return type might be less specific than
5+
// needed. Just punt and make it invariant.
6+
import iter;
7+
import iter::BaseIter;
8+
9+
trait A {
10+
fn b<C:copy const, D>(x: C) -> C;
11+
}
12+
13+
struct E {
14+
f: int;
15+
}
16+
17+
impl E: A {
18+
fn b<F:copy, G>(_x: F) -> F { fail } //~ ERROR in method `b`, type parameter 0 has 1 bound, but
19+
}
20+
21+
fn main() {}

src/test/compile-fail/issue-2611-4.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Tests that an impl method's bounds aren't *more* restrictive
2+
// than the trait method it's implementing
3+
import iter;
4+
import iter::BaseIter;
5+
6+
trait A {
7+
fn b<C:copy, D>(x: C) -> C;
8+
}
9+
10+
struct E {
11+
f: int;
12+
}
13+
14+
impl E: A {
15+
fn b<F:copy const, G>(_x: F) -> F { fail } //~ ERROR in method `b`, type parameter 0 has 2 bounds, but
16+
}
17+
18+
fn main() {}

src/test/compile-fail/issue-2611-5.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Tests that ty params get matched correctly when comparing
2+
// an impl against a trait
3+
import iter;
4+
import iter::BaseIter;
5+
6+
trait A {
7+
fn b<C:copy, D>(x: C) -> C;
8+
}
9+
10+
struct E {
11+
f: int;
12+
}
13+
14+
impl E: A {
15+
// n.b. The error message is awful -- see #3404
16+
fn b<F:copy, G>(_x: G) -> G { fail } //~ ERROR method `b` has an incompatible type
17+
}
18+
19+
fn main() {}

src/test/run-pass/issue-2611.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
// xfail-test
2-
use iter::base_iter;
1+
use iter::BaseIter;
32

4-
impl Q<A> for base_iter<A> {
5-
fn flat_map_to_vec<B:copy, IB:base_iter<B>>(op: fn(B) -> IB) -> ~[B] {
6-
iter::flat_map_to_vec(self, op)
3+
trait FlatMapToVec<A> {
4+
fn flat_map_to_vec<B:copy, IB:BaseIter<B>>(op: fn(A) -> IB) -> ~[B];
5+
}
6+
7+
impl<A:copy> BaseIter<A>: FlatMapToVec<A> {
8+
fn flat_map_to_vec<B:copy, IB:BaseIter<B>>(op: fn(A) -> IB) -> ~[B] {
9+
iter::flat_map_to_vec(self, op)
710
}
811
}
912

0 commit comments

Comments
 (0)