From 36e3d64c3e6030d661d48bf881ed2fef58170be6 Mon Sep 17 00:00:00 2001 From: Michael Sullivan Date: Wed, 12 Jun 2013 11:05:03 -0700 Subject: [PATCH] Fix a lot of the handling of default methods and type parameters. Closes #4099, #4102. --- src/librustc/middle/trans/base.rs | 4 +- src/librustc/middle/trans/callee.rs | 77 +++++++++++++++++-- src/librustc/middle/trans/monomorphize.rs | 23 ++---- .../trait-default-method-bound-subst.rs | 4 +- .../trait-default-method-bound-subst2.rs | 2 +- 5 files changed, 84 insertions(+), 26 deletions(-) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 43a07821513b..b5a298464d7b 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -506,9 +506,11 @@ pub fn get_res_dtor(ccx: @CrateContext, did }; assert_eq!(did.crate, ast::local_crate); + let tsubsts = ty::substs { self_r: None, self_ty: None, + tps: /*bad*/ substs.to_owned() }; let (val, _) = monomorphize::monomorphic_fn(ccx, did, - substs, + &tsubsts, None, None, None); diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index 840bad92b198..52df3abfbb5b 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -41,6 +41,7 @@ use middle::trans::meth; use middle::trans::monomorphize; use middle::trans::type_of; use middle::ty; +use middle::subst::Subst; use middle::typeck; use util::ppaux::Repr; @@ -230,11 +231,75 @@ pub fn trans_fn_ref_with_vtables( // Polytype of the function item (may have type params) let fn_tpt = ty::lookup_item_type(tcx, def_id); - // Modify the def_id if this is a default method; we want to be - // monomorphizing the trait's code. - let (def_id, opt_impl_did) = match tcx.provided_method_sources.find(&def_id) { - None => (def_id, None), - Some(source) => (source.method_id, Some(source.impl_id)) + let substs = ty::substs { self_r: None, self_ty: None, + tps: /*bad*/ type_params.to_owned() }; + + + // We need to do a bunch of special handling for default methods. + // We need to modify the def_id and our substs in order to monomorphize + // the function. + let (def_id, opt_impl_did, substs) = match tcx.provided_method_sources.find(&def_id) { + None => (def_id, None, substs), + Some(source) => { + // There are two relevant substitutions when compiling + // default methods. First, there is the substitution for + // the type parameters of the impl we are using and the + // method we are calling. This substitution is the substs + // argument we already have. + // In order to compile a default method, though, we need + // to consider another substitution: the substitution for + // the type parameters on trait; the impl we are using + // implements the trait at some particular type + // parameters, and we need to substitute for those first. + // So, what we need to do is find this substitution and + // compose it with the one we already have. + + // In order to find the substitution for the trait params, + // we look up the impl in the ast map, find its trait_ref + // id, then look up its trait ref. I feel like there + // should be a better way. + let map_node = session::expect( + ccx.sess, + ccx.tcx.items.find_copy(&source.impl_id.node), + || fmt!("couldn't find node while monomorphizing \ + default method: %?", source.impl_id.node)); + let item = match map_node { + ast_map::node_item(item, _) => item, + _ => ccx.tcx.sess.bug("Not an item") + }; + let ast_trait_ref = match copy item.node { + ast::item_impl(_, Some(tr), _, _) => tr, + _ => ccx.tcx.sess.bug("Not an impl with trait_ref") + }; + let trait_ref = ccx.tcx.trait_refs.get(&ast_trait_ref.ref_id); + + // The substs from the trait_ref only substitues for the + // trait parameters. Our substitution also needs to be + // able to substitute for the actual method type + // params. To do this, we figure out how many method + // parameters there are and pad out the substitution with + // substitution for the variables. + let item_ty = ty::lookup_item_type(tcx, source.method_id); + let num_params = item_ty.generics.type_param_defs.len() - + trait_ref.substs.tps.len(); + let id_subst = do vec::from_fn(num_params) |i| { + ty::mk_param(tcx, i, ast::def_id {crate: 0, node: 0}) + }; + // Merge the two substitions together now. + let first_subst = ty::substs {tps: trait_ref.substs.tps + id_subst, + .. trait_ref.substs}; + + // And compose them. + let new_substs = first_subst.subst(tcx, &substs); + debug!("trans_fn_with_vtables - default method: \ + substs = %s, id_subst = %s, trait_subst = %s, \ + first_subst = %s, new_subst = %s", + substs.repr(tcx), + id_subst.repr(tcx), trait_ref.substs.repr(tcx), + first_subst.repr(tcx), new_substs.repr(tcx)); + + (source.method_id, Some(source.impl_id), new_substs) + } }; // Check whether this fn has an inlined copy and, if so, redirect @@ -279,7 +344,7 @@ pub fn trans_fn_ref_with_vtables( assert_eq!(def_id.crate, ast::local_crate); let mut (val, must_cast) = - monomorphize::monomorphic_fn(ccx, def_id, type_params, + monomorphize::monomorphic_fn(ccx, def_id, &substs, vtables, opt_impl_did, Some(ref_id)); if must_cast && ref_id != 0 { // Monotype of the REFERENCE to the function (type params diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs index 866daabff339..35ff3a56df4e 100644 --- a/src/librustc/middle/trans/monomorphize.rs +++ b/src/librustc/middle/trans/monomorphize.rs @@ -43,7 +43,7 @@ use syntax::abi::AbiSet; pub fn monomorphic_fn(ccx: @CrateContext, fn_id: ast::def_id, - real_substs: &[ty::t], + real_substs: &ty::substs, vtables: Option, impl_did_opt: Option, ref_id: Option) @@ -61,17 +61,17 @@ pub fn monomorphic_fn(ccx: @CrateContext, impl_did_opt.repr(ccx.tcx), ref_id); - assert!(real_substs.all(|t| !ty::type_needs_infer(*t))); + assert!(real_substs.tps.all(|t| !ty::type_needs_infer(*t))); let _icx = ccx.insn_ctxt("monomorphic_fn"); let mut must_cast = false; - let substs = vec::map(real_substs, |t| { + let substs = vec::map(real_substs.tps, |t| { match normalize_for_monomorphization(ccx.tcx, *t) { Some(t) => { must_cast = true; t } None => *t } }); - for real_substs.each() |s| { assert!(!ty::type_has_params(*s)); } + for real_substs.tps.each() |s| { assert!(!ty::type_has_params(*s)); } for substs.each() |s| { assert!(!ty::type_has_params(*s)); } let param_uses = type_use::type_uses_for(ccx, fn_id, substs.len()); let hash_id = make_mono_id(ccx, fn_id, substs, vtables, impl_did_opt, @@ -145,17 +145,8 @@ pub fn monomorphic_fn(ccx: @CrateContext, ast_map::node_struct_ctor(_, i, pt) => (pt, i.ident, i.span) }; - // Look up the impl type if we're translating a default method. - // XXX: Generics. - let impl_ty_opt; - match impl_did_opt { - None => impl_ty_opt = None, - Some(impl_did) => { - impl_ty_opt = Some(ty::lookup_item_type(ccx.tcx, impl_did).ty); - } - } - - let mono_ty = ty::subst_tps(ccx.tcx, substs, impl_ty_opt, llitem_ty); + let mono_ty = ty::subst_tps(ccx.tcx, substs, + real_substs.self_ty, llitem_ty); let llfty = type_of_fn_from_ty(ccx, mono_ty); ccx.stats.n_monos += 1; @@ -186,7 +177,7 @@ pub fn monomorphic_fn(ccx: @CrateContext, tys: substs, vtables: vtables, type_param_defs: tpt.generics.type_param_defs, - self_ty: impl_ty_opt + self_ty: real_substs.self_ty }); let lldecl = match map_node { diff --git a/src/test/run-pass/trait-default-method-bound-subst.rs b/src/test/run-pass/trait-default-method-bound-subst.rs index dc0af7f7d541..adabafc082a0 100644 --- a/src/test/run-pass/trait-default-method-bound-subst.rs +++ b/src/test/run-pass/trait-default-method-bound-subst.rs @@ -8,10 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// xfail-test +#[allow(default_methods)]; trait A { - fn g(x: T, y: U) -> (T, U) { (x, y) } + fn g(&self, x: T, y: U) -> (T, U) { (x, y) } } impl A for int { } diff --git a/src/test/run-pass/trait-default-method-bound-subst2.rs b/src/test/run-pass/trait-default-method-bound-subst2.rs index 93cc752527b8..dee9782d5ae7 100644 --- a/src/test/run-pass/trait-default-method-bound-subst2.rs +++ b/src/test/run-pass/trait-default-method-bound-subst2.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// xfail-test +#[allow(default_methods)]; trait A { fn g(&self, x: T) -> T { x }