From 94f05c1936e85d4805e7177fe546d4c6f40340d8 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 1 Nov 2012 15:08:36 -0700 Subject: [PATCH] rustc: Stop overwriting trait static method types when checking generic trait refs. Closes #3903. rs=blocking-burg --- src/rustc/middle/typeck/check/vtable.rs | 13 +++++++- src/rustc/middle/typeck/collect.rs | 27 +++++++++++++--- .../trait-static-method-overwriting.rs | 32 +++++++++++++++++++ 3 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 src/test/run-pass/trait-static-method-overwriting.rs diff --git a/src/rustc/middle/typeck/check/vtable.rs b/src/rustc/middle/typeck/check/vtable.rs index 85a65d7432fd..bfd6d5a44e8f 100644 --- a/src/rustc/middle/typeck/check/vtable.rs +++ b/src/rustc/middle/typeck/check/vtable.rs @@ -100,6 +100,10 @@ fn lookup_vtable_covariant(fcx: @fn_ctxt, allow_unsafe: bool, is_early: bool) -> Option { + debug!("lookup_vtable_covariant(ty: %s, trait_ty=%s)", + fcx.infcx().ty_to_str(ty), + fcx.infcx().ty_to_str(trait_ty)); + let worklist = dvec::DVec(); worklist.push(trait_ty); while worklist.len() > 0 { @@ -475,9 +479,16 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) { ast::expr_path(*) => { match fcx.opt_node_ty_substs(ex.id) { Some(ref substs) => { - let did = ast_util::def_id_of_def(cx.tcx.def_map.get(ex.id)); + let def = cx.tcx.def_map.get(ex.id); + let did = ast_util::def_id_of_def(def); + debug!("early resolve expr: def %?", def); let item_ty = ty::lookup_item_type(cx.tcx, did); if has_trait_bounds(*item_ty.bounds) { + for item_ty.bounds.each |bounds| { + debug!("early_resolve_expr: looking up vtables for bound \ + %s", + ty::param_bounds_to_str(fcx.tcx(), *bounds)); + } let vtbls = lookup_vtables(fcx, ex, item_ty.bounds, substs, false, is_early); if !is_early { cx.vtable_map.insert(ex.id, vtbls); } diff --git a/src/rustc/middle/typeck/collect.rs b/src/rustc/middle/typeck/collect.rs index ed0cd48504b5..ca12e355636a 100644 --- a/src/rustc/middle/typeck/collect.rs +++ b/src/rustc/middle/typeck/collect.rs @@ -231,10 +231,11 @@ fn ensure_trait_methods(ccx: @crate_ctxt, id: ast::node_id, trait_ty: ty::t) { let trait_bounds = ty_param_bounds(ccx, params); let ty_m = trait_method_to_ty_method(*m); let method_ty = ty_of_ty_method(ccx, ty_m, region_paramd, def_id); - if ty_m.self_ty.node == ast::sty_static { - make_static_method_ty(ccx, ty_m, region_paramd, - method_ty, trait_ty, trait_bounds); - } + if ty_m.self_ty.node == ast::sty_static { + make_static_method_ty(ccx, ty_m, region_paramd, + method_ty, trait_ty, + trait_bounds); + } method_ty }); } @@ -420,9 +421,25 @@ fn check_methods_against_trait(ccx: @crate_ctxt, let tcx = ccx.tcx; let (did, tpt) = instantiate_trait_ref(ccx, a_trait_ty, rp); + if did.crate == ast::local_crate { - ensure_trait_methods(ccx, did.node, tpt.ty); + // NB: This is subtle. We need to do this on the type of the trait + // item *itself*, not on the type that includes the parameter + // substitutions provided by the programmer at this particular + // trait ref. Otherwise, we will potentially overwrite the types of + // the methods within the trait with bogus results. (See issue #3903.) + + match tcx.items.find(did.node) { + Some(ast_map::node_item(item, _)) => { + let tpt = ty_of_item(ccx, item); + ensure_trait_methods(ccx, did.node, tpt.ty); + } + _ => { + tcx.sess.bug(~"trait ref didn't resolve to trait"); + } + } } + for vec::each(*ty::trait_methods(tcx, did)) |trait_m| { match vec::find(impl_ms, |impl_m| trait_m.ident == impl_m.mty.ident) { Some(ref cm) => { diff --git a/src/test/run-pass/trait-static-method-overwriting.rs b/src/test/run-pass/trait-static-method-overwriting.rs new file mode 100644 index 000000000000..a47e27d24519 --- /dev/null +++ b/src/test/run-pass/trait-static-method-overwriting.rs @@ -0,0 +1,32 @@ +mod base { + pub trait HasNew { + static pure fn new() -> T; + } + + pub struct Foo { + dummy: (), + } + + pub impl Foo : base::HasNew { + static pure fn new() -> Foo { + unsafe { io::println("Foo"); } + Foo { dummy: () } + } + } + + pub struct Bar { + dummy: (), + } + + pub impl Bar : base::HasNew { + static pure fn new() -> Bar { + unsafe { io::println("Bar"); } + Bar { dummy: () } + } + } +} + +fn main() { + let f: base::Foo = base::new::(); + let b: base::Bar = base::new::(); +}