From 98740a59da4f73617351098c118d707134e561a8 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 25 May 2012 08:42:39 -0700 Subject: [PATCH] detect ambig. calls to iface bounds, use transactions cc #2433 --- src/rustc/middle/typeck/check.rs | 4 ++ src/rustc/middle/typeck/check/method.rs | 49 +++++++++++++++++----- src/rustc/middle/typeck/infer.rs | 37 +++++++++++----- src/test/compile-fail/ambig_impl_1.rs | 4 +- src/test/compile-fail/ambig_impl_2_exe.rs | 4 +- src/test/compile-fail/ambig_impl_bounds.rs | 10 +++++ src/test/compile-fail/ambig_impl_unify.rs | 12 ++++++ 7 files changed, 96 insertions(+), 24 deletions(-) create mode 100644 src/test/compile-fail/ambig_impl_bounds.rs create mode 100644 src/test/compile-fail/ambig_impl_unify.rs diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs index 07eeada10f89..7da1f27d3daf 100644 --- a/src/rustc/middle/typeck/check.rs +++ b/src/rustc/middle/typeck/check.rs @@ -527,6 +527,10 @@ impl methods for @fn_ctxt { infer::mk_subty(self.infcx, sub, sup) } + fn can_mk_subty(sub: ty::t, sup: ty::t) -> result<(), ty::type_err> { + infer::can_mk_subty(self.infcx, sub, sup) + } + fn mk_eqty(sub: ty::t, sup: ty::t) -> result<(), ty::type_err> { infer::mk_eqty(self.infcx, sub, sup) } diff --git a/src/rustc/middle/typeck/check/method.rs b/src/rustc/middle/typeck/check/method.rs index b6f01aed1fe4..af27d8d8da19 100644 --- a/src/rustc/middle/typeck/check/method.rs +++ b/src/rustc/middle/typeck/check/method.rs @@ -45,6 +45,7 @@ impl methods for lookup { let tcx = self.tcx(); let mut iface_bnd_idx = 0u; // count only iface bounds let bounds = tcx.ty_param_bounds.get(did.node); + let mut candidates = []; for vec::each(*bounds) {|bound| let (iid, bound_substs) = alt bound { ty::bound_copy | ty::bound_send | ty::bound_const { @@ -74,14 +75,33 @@ impl methods for lookup { // permitted). let substs = {self_ty: some(self.self_ty) with bound_substs}; - - ret some(self.write_mty_from_m( - substs, ifce_methods[pos], - method_param(iid, pos, n, iface_bnd_idx))); + candidates += [(substs, ifce_methods[pos], + iid, pos, n, iface_bnd_idx)]; } } } - ret none; + + if candidates.len() == 0u { + ret none; + } + + if candidates.len() > 1u { + self.tcx().sess.span_err( + self.expr.span, + "multiple applicable methods in scope"); + + for candidates.eachi { |i, candidate| + let (_, _, iid, _, _, _) = candidate; + self.tcx().sess.span_note( + self.expr.span, + #fmt["candidate #%u derives from the bound `%s`", + (i+1u), ty::item_path_str(self.tcx(), iid)]); + } + } + + let (substs, mty, iid, pos, n, iface_bnd_idx) = candidates[0u]; + ret some(self.write_mty_from_m( + substs, mty, method_param(iid, pos, n, iface_bnd_idx))); } fn method_from_iface( @@ -197,10 +217,11 @@ impl methods for lookup { // if we can assign the caller to the callee, that's a // potential match. Collect those in the vector. - alt self.fcx.mk_subty(ty, self_ty) { + alt self.fcx.can_mk_subty(ty, self_ty) { result::err(_) { /* keep looking */ } result::ok(_) { - results += [(self_substs, m.n_tps, m.did)]; + results += [(ty, self_ty, self_substs, + m.n_tps, m.did)]; } } } @@ -216,7 +237,7 @@ impl methods for lookup { // but I cannot for the life of me figure out how to // annotate resolve to preserve this information. for results.eachi { |i, result| - let (_, _, did) = result; + let (_, _, _, _, did) = result; let span = if did.crate == ast::local_crate { alt check self.tcx().items.get(did.node) { ast_map::node_method(m, _, _) { m.span } @@ -226,13 +247,21 @@ impl methods for lookup { }; self.tcx().sess.span_note( span, - #fmt["candidate #%u is %s", + #fmt["candidate #%u is `%s`", (i+1u), ty::item_path_str(self.tcx(), did)]); } } - let (self_substs, n_tps, did) = results[0]; + let (ty, self_ty, self_substs, n_tps, did) = results[0]; + alt self.fcx.mk_subty(ty, self_ty) { + result::ok(_) {} + result::err(_) { + self.tcx().sess.span_bug( + self.expr.span, + "what was a subtype now is not?"); + } + } let fty = self.ty_from_did(did); ret some(self.write_mty_from_fty( self_substs, n_tps, fty, diff --git a/src/rustc/middle/typeck/infer.rs b/src/rustc/middle/typeck/infer.rs index f94d1656c0bd..d13123496b1a 100644 --- a/src/rustc/middle/typeck/infer.rs +++ b/src/rustc/middle/typeck/infer.rs @@ -160,7 +160,7 @@ import util::common::{indent, indenter}; export infer_ctxt; export new_infer_ctxt; -export mk_subty; +export mk_subty, can_mk_subty; export mk_subr; export mk_eqty; export mk_assignty; @@ -235,6 +235,11 @@ fn mk_subty(cx: infer_ctxt, a: ty::t, b: ty::t) -> ures { indent {|| cx.commit {|| sub(cx).tys(a, b) } }.to_ures() } +fn can_mk_subty(cx: infer_ctxt, a: ty::t, b: ty::t) -> ures { + #debug["can_mk_subty(%s <: %s)", a.to_str(cx), b.to_str(cx)]; + indent {|| cx.probe {|| sub(cx).tys(a, b) } }.to_ures() +} + fn mk_subr(cx: infer_ctxt, a: ty::region, b: ty::region) -> ures { #debug["mk_subr(%s <: %s)", a.to_str(cx), b.to_str(cx)]; indent {|| cx.commit {|| sub(cx).regions(a, b) } }.to_ures() @@ -388,7 +393,17 @@ fn uok() -> ures { ok(()) } +fn rollback_to( + vb: vals_and_bindings, len: uint) { + + while vb.bindings.len() != len { + let (vid, old_v) = vec::pop(vb.bindings); + vb.vals.insert(vid.to_uint(), old_v); + } +} + impl transaction_methods for infer_ctxt { + #[doc = "Execute `f` and commit the bindings if successful"] fn commit(f: fn() -> result) -> result { assert self.vb.bindings.len() == 0u; @@ -404,17 +419,9 @@ impl transaction_methods for infer_ctxt { ret r; } + #[doc = "Execute `f`, unroll bindings on failure"] fn try(f: fn() -> result) -> result { - fn rollback_to( - vb: vals_and_bindings, len: uint) { - - while vb.bindings.len() != len { - let (vid, old_v) = vec::pop(vb.bindings); - vb.vals.insert(vid.to_uint(), old_v); - } - } - let vbl = self.vb.bindings.len(); let rbl = self.rb.bindings.len(); #debug["try(vbl=%u, rbl=%u)", vbl, rbl]; @@ -429,6 +436,16 @@ impl transaction_methods for infer_ctxt { } ret r; } + + #[doc = "Execute `f` then unroll any bindings it creates"] + fn probe(f: fn() -> result) -> result { + assert self.vb.bindings.len() == 0u; + assert self.rb.bindings.len() == 0u; + let r <- f(); + rollback_to(self.vb, 0u); + rollback_to(self.rb, 0u); + ret r; + } } impl methods for infer_ctxt { diff --git a/src/test/compile-fail/ambig_impl_1.rs b/src/test/compile-fail/ambig_impl_1.rs index 434cdf31002e..0eed1c8f89fb 100644 --- a/src/test/compile-fail/ambig_impl_1.rs +++ b/src/test/compile-fail/ambig_impl_1.rs @@ -1,3 +1,3 @@ -impl methods1 for uint { fn me() -> uint { self } } //! NOTE candidate #1 is methods1::me -impl methods2 for uint { fn me() -> uint { self } } //! NOTE candidate #2 is methods2::me +impl methods1 for uint { fn me() -> uint { self } } //! NOTE candidate #1 is `methods1::me` +impl methods2 for uint { fn me() -> uint { self } } //! NOTE candidate #2 is `methods2::me` fn main() { 1u.me(); } //! ERROR multiple applicable methods in scope diff --git a/src/test/compile-fail/ambig_impl_2_exe.rs b/src/test/compile-fail/ambig_impl_2_exe.rs index 49c1fdc2f1df..5bd20db144f5 100644 --- a/src/test/compile-fail/ambig_impl_2_exe.rs +++ b/src/test/compile-fail/ambig_impl_2_exe.rs @@ -2,6 +2,6 @@ // aux-build:ambig_impl_2_lib.rs use ambig_impl_2_lib; import ambig_impl_2_lib::methods1; -impl methods2 for uint { fn me() -> uint { self } } //! NOTE candidate #2 is methods2::me +impl methods2 for uint { fn me() -> uint { self } } //! NOTE candidate #2 is `methods2::me` fn main() { 1u.me(); } //! ERROR multiple applicable methods in scope -//!^ NOTE candidate #1 is ambig_impl_2_lib::methods1::me +//!^ NOTE candidate #1 is `ambig_impl_2_lib::methods1::me` diff --git a/src/test/compile-fail/ambig_impl_bounds.rs b/src/test/compile-fail/ambig_impl_bounds.rs new file mode 100644 index 000000000000..d14e57a21f41 --- /dev/null +++ b/src/test/compile-fail/ambig_impl_bounds.rs @@ -0,0 +1,10 @@ +iface A { fn foo(); } +iface B { fn foo(); } + +fn foo(t: T) { + t.foo(); //! ERROR multiple applicable methods in scope + //!^ NOTE candidate #1 derives from the bound `A` + //!^^ NOTE candidate #2 derives from the bound `B` +} + +fn main() {} \ No newline at end of file diff --git a/src/test/compile-fail/ambig_impl_unify.rs b/src/test/compile-fail/ambig_impl_unify.rs new file mode 100644 index 000000000000..2f020742d540 --- /dev/null +++ b/src/test/compile-fail/ambig_impl_unify.rs @@ -0,0 +1,12 @@ +impl methods for [uint] { + fn foo() -> int {1} //! NOTE candidate #1 is `methods::foo` +} + +impl methods for [int] { + fn foo() -> int {2} //! NOTE candidate #2 is `methods::foo` +} + +fn main() { + let x = []; + x.foo(); //! ERROR multiple applicable methods in scope +} \ No newline at end of file