Skip to content

Commit 98740a5

Browse files
committed
detect ambig. calls to iface bounds, use transactions
cc #2433
1 parent 93633ea commit 98740a5

File tree

7 files changed

+96
-24
lines changed

7 files changed

+96
-24
lines changed

src/rustc/middle/typeck/check.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,10 @@ impl methods for @fn_ctxt {
527527
infer::mk_subty(self.infcx, sub, sup)
528528
}
529529

530+
fn can_mk_subty(sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
531+
infer::can_mk_subty(self.infcx, sub, sup)
532+
}
533+
530534
fn mk_eqty(sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
531535
infer::mk_eqty(self.infcx, sub, sup)
532536
}

src/rustc/middle/typeck/check/method.rs

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ impl methods for lookup {
4545
let tcx = self.tcx();
4646
let mut iface_bnd_idx = 0u; // count only iface bounds
4747
let bounds = tcx.ty_param_bounds.get(did.node);
48+
let mut candidates = [];
4849
for vec::each(*bounds) {|bound|
4950
let (iid, bound_substs) = alt bound {
5051
ty::bound_copy | ty::bound_send | ty::bound_const {
@@ -74,14 +75,33 @@ impl methods for lookup {
7475
// permitted).
7576
let substs = {self_ty: some(self.self_ty)
7677
with bound_substs};
77-
78-
ret some(self.write_mty_from_m(
79-
substs, ifce_methods[pos],
80-
method_param(iid, pos, n, iface_bnd_idx)));
78+
candidates += [(substs, ifce_methods[pos],
79+
iid, pos, n, iface_bnd_idx)];
8180
}
8281
}
8382
}
84-
ret none;
83+
84+
if candidates.len() == 0u {
85+
ret none;
86+
}
87+
88+
if candidates.len() > 1u {
89+
self.tcx().sess.span_err(
90+
self.expr.span,
91+
"multiple applicable methods in scope");
92+
93+
for candidates.eachi { |i, candidate|
94+
let (_, _, iid, _, _, _) = candidate;
95+
self.tcx().sess.span_note(
96+
self.expr.span,
97+
#fmt["candidate #%u derives from the bound `%s`",
98+
(i+1u), ty::item_path_str(self.tcx(), iid)]);
99+
}
100+
}
101+
102+
let (substs, mty, iid, pos, n, iface_bnd_idx) = candidates[0u];
103+
ret some(self.write_mty_from_m(
104+
substs, mty, method_param(iid, pos, n, iface_bnd_idx)));
85105
}
86106

87107
fn method_from_iface(
@@ -197,10 +217,11 @@ impl methods for lookup {
197217

198218
// if we can assign the caller to the callee, that's a
199219
// potential match. Collect those in the vector.
200-
alt self.fcx.mk_subty(ty, self_ty) {
220+
alt self.fcx.can_mk_subty(ty, self_ty) {
201221
result::err(_) { /* keep looking */ }
202222
result::ok(_) {
203-
results += [(self_substs, m.n_tps, m.did)];
223+
results += [(ty, self_ty, self_substs,
224+
m.n_tps, m.did)];
204225
}
205226
}
206227
}
@@ -216,7 +237,7 @@ impl methods for lookup {
216237
// but I cannot for the life of me figure out how to
217238
// annotate resolve to preserve this information.
218239
for results.eachi { |i, result|
219-
let (_, _, did) = result;
240+
let (_, _, _, _, did) = result;
220241
let span = if did.crate == ast::local_crate {
221242
alt check self.tcx().items.get(did.node) {
222243
ast_map::node_method(m, _, _) { m.span }
@@ -226,13 +247,21 @@ impl methods for lookup {
226247
};
227248
self.tcx().sess.span_note(
228249
span,
229-
#fmt["candidate #%u is %s",
250+
#fmt["candidate #%u is `%s`",
230251
(i+1u),
231252
ty::item_path_str(self.tcx(), did)]);
232253
}
233254
}
234255

235-
let (self_substs, n_tps, did) = results[0];
256+
let (ty, self_ty, self_substs, n_tps, did) = results[0];
257+
alt self.fcx.mk_subty(ty, self_ty) {
258+
result::ok(_) {}
259+
result::err(_) {
260+
self.tcx().sess.span_bug(
261+
self.expr.span,
262+
"what was a subtype now is not?");
263+
}
264+
}
236265
let fty = self.ty_from_did(did);
237266
ret some(self.write_mty_from_fty(
238267
self_substs, n_tps, fty,

src/rustc/middle/typeck/infer.rs

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ import util::common::{indent, indenter};
160160

161161
export infer_ctxt;
162162
export new_infer_ctxt;
163-
export mk_subty;
163+
export mk_subty, can_mk_subty;
164164
export mk_subr;
165165
export mk_eqty;
166166
export mk_assignty;
@@ -235,6 +235,11 @@ fn mk_subty(cx: infer_ctxt, a: ty::t, b: ty::t) -> ures {
235235
indent {|| cx.commit {|| sub(cx).tys(a, b) } }.to_ures()
236236
}
237237

238+
fn can_mk_subty(cx: infer_ctxt, a: ty::t, b: ty::t) -> ures {
239+
#debug["can_mk_subty(%s <: %s)", a.to_str(cx), b.to_str(cx)];
240+
indent {|| cx.probe {|| sub(cx).tys(a, b) } }.to_ures()
241+
}
242+
238243
fn mk_subr(cx: infer_ctxt, a: ty::region, b: ty::region) -> ures {
239244
#debug["mk_subr(%s <: %s)", a.to_str(cx), b.to_str(cx)];
240245
indent {|| cx.commit {|| sub(cx).regions(a, b) } }.to_ures()
@@ -388,7 +393,17 @@ fn uok() -> ures {
388393
ok(())
389394
}
390395

396+
fn rollback_to<V:copy vid, T:copy>(
397+
vb: vals_and_bindings<V, T>, len: uint) {
398+
399+
while vb.bindings.len() != len {
400+
let (vid, old_v) = vec::pop(vb.bindings);
401+
vb.vals.insert(vid.to_uint(), old_v);
402+
}
403+
}
404+
391405
impl transaction_methods for infer_ctxt {
406+
#[doc = "Execute `f` and commit the bindings if successful"]
392407
fn commit<T,E>(f: fn() -> result<T,E>) -> result<T,E> {
393408

394409
assert self.vb.bindings.len() == 0u;
@@ -404,17 +419,9 @@ impl transaction_methods for infer_ctxt {
404419
ret r;
405420
}
406421

422+
#[doc = "Execute `f`, unroll bindings on failure"]
407423
fn try<T,E>(f: fn() -> result<T,E>) -> result<T,E> {
408424

409-
fn rollback_to<V:copy vid, T:copy>(
410-
vb: vals_and_bindings<V, T>, len: uint) {
411-
412-
while vb.bindings.len() != len {
413-
let (vid, old_v) = vec::pop(vb.bindings);
414-
vb.vals.insert(vid.to_uint(), old_v);
415-
}
416-
}
417-
418425
let vbl = self.vb.bindings.len();
419426
let rbl = self.rb.bindings.len();
420427
#debug["try(vbl=%u, rbl=%u)", vbl, rbl];
@@ -429,6 +436,16 @@ impl transaction_methods for infer_ctxt {
429436
}
430437
ret r;
431438
}
439+
440+
#[doc = "Execute `f` then unroll any bindings it creates"]
441+
fn probe<T,E>(f: fn() -> result<T,E>) -> result<T,E> {
442+
assert self.vb.bindings.len() == 0u;
443+
assert self.rb.bindings.len() == 0u;
444+
let r <- f();
445+
rollback_to(self.vb, 0u);
446+
rollback_to(self.rb, 0u);
447+
ret r;
448+
}
432449
}
433450

434451
impl methods for infer_ctxt {

src/test/compile-fail/ambig_impl_1.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
impl methods1 for uint { fn me() -> uint { self } } //! NOTE candidate #1 is methods1::me
2-
impl methods2 for uint { fn me() -> uint { self } } //! NOTE candidate #2 is methods2::me
1+
impl methods1 for uint { fn me() -> uint { self } } //! NOTE candidate #1 is `methods1::me`
2+
impl methods2 for uint { fn me() -> uint { self } } //! NOTE candidate #2 is `methods2::me`
33
fn main() { 1u.me(); } //! ERROR multiple applicable methods in scope

src/test/compile-fail/ambig_impl_2_exe.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
// aux-build:ambig_impl_2_lib.rs
33
use ambig_impl_2_lib;
44
import ambig_impl_2_lib::methods1;
5-
impl methods2 for uint { fn me() -> uint { self } } //! NOTE candidate #2 is methods2::me
5+
impl methods2 for uint { fn me() -> uint { self } } //! NOTE candidate #2 is `methods2::me`
66
fn main() { 1u.me(); } //! ERROR multiple applicable methods in scope
7-
//!^ NOTE candidate #1 is ambig_impl_2_lib::methods1::me
7+
//!^ NOTE candidate #1 is `ambig_impl_2_lib::methods1::me`
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
iface A { fn foo(); }
2+
iface B { fn foo(); }
3+
4+
fn foo<T: A B>(t: T) {
5+
t.foo(); //! ERROR multiple applicable methods in scope
6+
//!^ NOTE candidate #1 derives from the bound `A`
7+
//!^^ NOTE candidate #2 derives from the bound `B`
8+
}
9+
10+
fn main() {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
impl methods for [uint] {
2+
fn foo() -> int {1} //! NOTE candidate #1 is `methods::foo`
3+
}
4+
5+
impl methods for [int] {
6+
fn foo() -> int {2} //! NOTE candidate #2 is `methods::foo`
7+
}
8+
9+
fn main() {
10+
let x = [];
11+
x.foo(); //! ERROR multiple applicable methods in scope
12+
}

0 commit comments

Comments
 (0)