Get cross crate default methods working.

This fixes the large number of problems that prevented cross crate
methods from ever working. It also fixes a couple lingering bugs with
polymorphic default methods and cleans up some of the code paths.

Closes #4102. Closes #4103.
This commit is contained in:
Michael Sullivan 2013-06-12 14:22:17 -07:00
parent 6759ce4fd2
commit 1a8969f64b
10 changed files with 254 additions and 216 deletions

View File

@ -229,7 +229,7 @@ pub fn get_impl_trait(tcx: ty::ctxt,
pub fn get_impl_method(cstore: @mut cstore::CStore,
def: ast::def_id,
mname: ast::ident)
-> ast::def_id {
-> Option<ast::def_id> {
let cdata = cstore::get_crate_data(cstore, def.crate);
decoder::get_impl_method(cstore.intr, cdata, def.node, mname)
}

View File

@ -415,7 +415,7 @@ pub fn get_impl_trait(cdata: cmd,
}
pub fn get_impl_method(intr: @ident_interner, cdata: cmd, id: ast::node_id,
name: ast::ident) -> ast::def_id {
name: ast::ident) -> Option<ast::def_id> {
let items = reader::get_doc(reader::Doc(cdata.data), tag_items);
let mut found = None;
for reader::tagged_docs(find_item(id, items), tag_item_impl_method)
@ -425,7 +425,7 @@ pub fn get_impl_method(intr: @ident_interner, cdata: cmd, id: ast::node_id,
found = Some(translate_def_id(cdata, m_did));
}
}
found.get()
found
}
pub fn get_symbol(data: @~[u8], id: ast::node_id) -> ~str {
@ -755,40 +755,13 @@ pub fn get_provided_trait_methods(intr: @ident_interner, cdata: cmd,
let item = lookup_item(id, data);
let mut result = ~[];
for reader::tagged_docs(item, tag_item_trait_method) |mth| {
for reader::tagged_docs(item, tag_item_trait_method) |mth_id| {
let did = item_def_id(mth_id, cdata);
let mth = lookup_item(did.node, data);
if item_method_sort(mth) != 'p' { loop; }
let did = item_def_id(mth, cdata);
let type_param_defs =
item_ty_param_defs(mth, tcx, cdata,
tag_items_data_item_ty_param_bounds);
let name = item_name(intr, mth);
let ty = doc_type(mth, tcx, cdata);
let fty = match ty::get(ty).sty {
ty::ty_bare_fn(ref f) => copy *f,
_ => {
tcx.diag.handler().bug("get_provided_trait_methods(): id \
has non-function type");
}
};
let transformed_self_ty = doc_transformed_self_ty(mth, tcx, cdata);
let explicit_self = get_explicit_self(mth);
let ty_method = ty::Method::new(
name,
ty::Generics {
type_param_defs: type_param_defs,
region_param: None
},
transformed_self_ty,
fty,
explicit_self,
ast::public,
did
);
let ty_method = get_method(intr, cdata, did.node, tcx);
let provided_trait_method_info = ProvidedTraitMethodInfo {
ty: ty_method,
def_id: did

View File

@ -42,6 +42,7 @@ use middle::trans::type_of;
use middle::ty;
use middle::subst::Subst;
use middle::typeck;
use middle::typeck::coherence::make_substs_for_receiver_types;
use util::ppaux::Repr;
use core::vec;
@ -253,50 +254,24 @@ pub fn trans_fn_ref_with_vtables(
// 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);
let trait_ref = ty::impl_trait_ref(tcx, source.impl_id)
.expect("could not find trait_ref for impl with \
default methods");
let method = ty::method(tcx, source.method_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};
// Compute the first substitution
let first_subst = make_substs_for_receiver_types(
tcx, source.impl_id, trait_ref, method);
// And compose them.
// 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, \
substs = %s, trait_subst = %s, \
first_subst = %s, new_subst = %s",
substs.repr(tcx),
id_subst.repr(tcx), trait_ref.substs.repr(tcx),
substs.repr(tcx), trait_ref.substs.repr(tcx),
first_subst.repr(tcx), new_substs.repr(tcx));
(source.method_id, Some(source.impl_id), new_substs)
}
};

View File

@ -93,11 +93,16 @@ pub fn maybe_instantiate_inline(ccx: @mut CrateContext, fn_id: ast::def_id,
csearch::found(ast::ii_method(impl_did, mth)) => {
ccx.stats.n_inlines += 1;
ccx.external.insert(fn_id, Some(mth.id));
let impl_tpt = ty::lookup_item_type(ccx.tcx, impl_did);
let num_type_params =
impl_tpt.generics.type_param_defs.len() +
mth.generics.ty_params.len();
if translate && num_type_params == 0 {
// If this is a default method, we can't look up the
// impl type. But we aren't going to translate anyways, so don't.
if !translate { return local_def(mth.id); }
let impl_tpt = ty::lookup_item_type(ccx.tcx, impl_did);
let num_type_params =
impl_tpt.generics.type_param_defs.len() +
mth.generics.ty_params.len();
if num_type_params == 0 {
let llfn = get_item_val(ccx, mth.id);
let path = vec::append(
ty::item_path(ccx.tcx, impl_did),

View File

@ -383,71 +383,48 @@ pub fn method_with_name_or_default(ccx: @mut CrateContext,
name: ast::ident) -> ast::def_id {
let imp = ccx.impl_method_cache.find_copy(&(impl_id, name));
match imp {
Some(m) => m,
None => {
let imp = if impl_id.crate == ast::local_crate {
match ccx.tcx.items.get_copy(&impl_id.node) {
ast_map::node_item(@ast::item {
node: ast::item_impl(_, _, _, ref ms), _
}, _) => {
let did = method_from_methods(*ms, name);
if did.is_some() {
did.get()
} else {
// Look for a default method
let pmm = ccx.tcx.provided_methods;
match pmm.find(&impl_id) {
Some(pmis) => {
for pmis.each |pmi| {
if pmi.method_info.ident == name {
debug!("pmi.method_info.did = %?", pmi.method_info.did);
return pmi.method_info.did;
}
}
fail!()
}
None => fail!()
}
}
Some(m) => return m,
None => {}
}
// None of this feels like it should be the best way to do this.
let mut did = if impl_id.crate == ast::local_crate {
match ccx.tcx.items.get_copy(&impl_id.node) {
ast_map::node_item(@ast::item {
node: ast::item_impl(_, _, _, ref ms), _
}, _) => { method_from_methods(*ms, name) },
_ => fail!("method_with_name")
}
} else {
csearch::get_impl_method(ccx.sess.cstore, impl_id, name)
};
if did.is_none() {
// Look for a default method
let pmm = ccx.tcx.provided_methods;
match pmm.find(&impl_id) {
Some(pmis) => {
for pmis.each |pmi| {
if pmi.method_info.ident == name {
debug!("pmi.method_info.did = %?",
pmi.method_info.did);
did = Some(pmi.method_info.did);
}
_ => fail!("method_with_name")
}
} else {
csearch::get_impl_method(ccx.sess.cstore, impl_id, name)
};
ccx.impl_method_cache.insert((impl_id, name), imp);
imp
}
None => {}
}
}
let imp = did.expect("could not find method while translating");
ccx.impl_method_cache.insert((impl_id, name), imp);
imp
}
pub fn method_ty_param_count(ccx: &CrateContext, m_id: ast::def_id,
i_id: ast::def_id) -> uint {
debug!("method_ty_param_count: m_id: %?, i_id: %?", m_id, i_id);
if m_id.crate == ast::local_crate {
match ccx.tcx.items.find(&m_id.node) {
Some(&ast_map::node_method(m, _, _)) => m.generics.ty_params.len(),
None => {
match ccx.tcx.provided_method_sources.find(&m_id) {
Some(source) => {
method_ty_param_count(
ccx, source.method_id, source.impl_id)
}
None => fail!()
}
}
Some(&ast_map::node_trait_method(@ast::provided(@ref m),
_, _)) => {
m.generics.ty_params.len()
}
ref e => fail!("method_ty_param_count %?", *e)
}
} else {
csearch::get_type_param_count(ccx.sess.cstore, m_id) -
csearch::get_type_param_count(ccx.sess.cstore, i_id)
}
ty::method(ccx.tcx, m_id).generics.type_param_defs.len()
}
pub fn trans_monomorphized_callee(bcx: block,

View File

@ -541,6 +541,10 @@ impl<'self> LookupContext<'self> {
if !self.impl_dups.insert(impl_info.did) {
return; // already visited
}
debug!("push_candidates_from_impl: %s %s %s",
self.m_name.repr(self.tcx()),
impl_info.ident.repr(self.tcx()),
impl_info.methods.map(|m| m.ident).repr(self.tcx()));
let idx = {
match impl_info.methods.position(|m| m.ident == self.m_name) {

View File

@ -279,7 +279,7 @@ impl CoherenceChecker {
trait_ref.repr(self.crate_context.tcx),
self.crate_context.tcx.sess.str_of(item.ident));
self.instantiate_default_methods(item.id, trait_ref);
self.instantiate_default_methods(local_def(item.id), trait_ref);
let implementation;
if implementation_opt.is_none() {
@ -327,13 +327,13 @@ impl CoherenceChecker {
// and trait pair. Then, for each provided method in the trait, inserts a
// `ProvidedMethodInfo` instance into the `provided_method_sources` map.
pub fn instantiate_default_methods(&self,
impl_id: ast::node_id,
impl_id: ast::def_id,
trait_ref: &ty::TraitRef) {
let tcx = self.crate_context.tcx;
debug!("instantiate_default_methods(impl_id=%?, trait_ref=%s)",
impl_id, trait_ref.repr(tcx));
let impl_poly_type = ty::lookup_item_type(tcx, local_def(impl_id));
let impl_poly_type = ty::lookup_item_type(tcx, impl_id);
for self.each_provided_trait_method(trait_ref.def_id) |trait_method| {
// Synthesize an ID.
@ -375,7 +375,7 @@ impl CoherenceChecker {
// ID of the method.
let source = ProvidedMethodSource {
method_id: trait_method.def_id,
impl_id: local_def(impl_id)
impl_id: impl_id
};
self.crate_context.tcx.provided_method_sources.insert(new_did,
@ -393,7 +393,7 @@ impl CoherenceChecker {
};
let pmm = self.crate_context.tcx.provided_methods;
match pmm.find(&local_def(impl_id)) {
match pmm.find(&impl_id) {
Some(&mis) => {
// If the trait already has an entry in the
// provided_methods_map, we just need to add this
@ -410,7 +410,7 @@ impl CoherenceChecker {
for method `%s`",
self.crate_context.tcx.sess.str_of(
provided_method_info.method_info.ident));
pmm.insert(local_def(impl_id),
pmm.insert(impl_id,
@mut ~[provided_method_info]);
}
}
@ -733,10 +733,12 @@ impl CoherenceChecker {
}
// Default methods
for ty::provided_trait_methods(tcx, trait_did).each |ident| {
debug!("inserting provided method %s", ident.repr(tcx));
provided_names.insert(*ident);
}
for (*ty::trait_methods(tcx, trait_did)).each |method| {
debug!("checking for %s", method.ident.repr(tcx));
if provided_names.contains(&method.ident) { loop; }
tcx.sess.span_err(trait_ref_span,
@ -785,19 +787,41 @@ impl CoherenceChecker {
}
}
fn add_provided_methods_to_impl(
&self,
all_methods: &mut ~[@MethodInfo],
trait_did: &ast::def_id,
impl_id: &ast::def_id) {
match self.crate_context.tcx
.provided_methods
.find(impl_id) {
None => {
debug!("(creating impl) trait with node_id `%d` \
has no provided methods", trait_did.node);
/* fall through */
}
Some(&all_provided_methods) => {
debug!("(creating impl) trait with node_id `%d` \
has provided methods", trait_did.node);
// Add all provided methods.
for all_provided_methods.each |provided_method| {
debug!(
"(creating impl) adding provided method \
`%s` to impl",
provided_method.method_info
.ident.repr(self.crate_context.tcx));
vec::push(all_methods, provided_method.method_info);
}
}
}
}
// Converts an implementation in the AST to an Impl structure.
pub fn create_impl_from_item(&self, item: @item) -> @Impl {
fn add_provided_methods(all_methods: &mut ~[@MethodInfo],
all_provided_methods: &mut ~[@ProvidedMethodInfo],
sess: driver::session::Session) {
for all_provided_methods.each |provided_method| {
debug!(
"(creating impl) adding provided method `%s` to impl",
sess.str_of(provided_method.method_info.ident));
vec::push(all_methods, provided_method.method_info);
}
}
match item.node {
item_impl(_, ref trait_refs, _, ref ast_methods) => {
let mut methods = ~[];
@ -820,27 +844,11 @@ impl CoherenceChecker {
// if a method of that name is not inherent to the
// impl, use the provided definition in the trait.
for trait_refs.iter().advance |trait_ref| {
let trait_did =
self.trait_ref_to_trait_def_id(*trait_ref);
match self.crate_context.tcx
.provided_methods
.find(&local_def(item.id)) {
None => {
debug!("(creating impl) trait with node_id `%d` \
has no provided methods", trait_did.node);
/* fall through */
}
Some(&all_provided) => {
debug!("(creating impl) trait with node_id `%d` \
has provided methods", trait_did.node);
// Add all provided methods.
add_provided_methods(
&mut methods,
all_provided,
self.crate_context.tcx.sess);
}
}
let trait_did = self.trait_ref_to_trait_def_id(*trait_ref);
self.add_provided_methods_to_impl(
&mut methods,
&trait_did,
&local_def(item.id));
}
return @Impl {
@ -917,9 +925,23 @@ impl CoherenceChecker {
}
}
let mut implementation = *implementation;
// Record all the trait methods.
for associated_traits.iter().advance |trait_ref| {
self.add_trait_method(trait_ref.def_id, *implementation);
self.instantiate_default_methods(implementation.did,
&**trait_ref);
// Could we avoid these copies when we don't need them?
let mut methods = /*bad?*/ copy implementation.methods;
self.add_provided_methods_to_impl(
&mut methods,
&trait_ref.def_id,
&implementation.did);
implementation = @Impl { methods: methods,
.. *implementation };
self.add_trait_method(trait_ref.def_id, implementation);
}
// Add the implementation to the mapping from
@ -937,7 +959,7 @@ impl CoherenceChecker {
// `impl Trait for Type`:
if associated_traits.is_none() {
self.add_inherent_method(base_type_def_id,
*implementation);
implementation);
}
self.base_type_def_ids.insert(implementation.did,
@ -947,38 +969,6 @@ impl CoherenceChecker {
}
}
pub fn add_default_methods_for_external_trait(&self,
trait_def_id: ast::def_id) {
let tcx = self.crate_context.tcx;
let pmm = tcx.provided_methods;
if pmm.contains_key(&trait_def_id) { return; }
debug!("(adding default methods for trait) processing trait");
for csearch::get_provided_trait_methods(tcx, trait_def_id).each
|trait_method_info| {
debug!("(adding default methods for trait) found default method");
// Create a new def ID for this provided method.
let parse_sess = &self.crate_context.tcx.sess.parse_sess;
let new_did = local_def(parse::next_node_id(*parse_sess));
let provided_method_info =
@ProvidedMethodInfo {
method_info: @MethodInfo {
did: new_did,
n_tps: trait_method_info.ty.generics.type_param_defs.len(),
ident: trait_method_info.ty.ident,
explicit_self: trait_method_info.ty.explicit_self
},
trait_method_def_id: trait_method_info.def_id
};
pmm.insert(trait_def_id, @mut ~[provided_method_info]);
}
}
// Adds implementations and traits from external crates to the coherence
// info.
pub fn add_external_crates(&self) {
@ -998,9 +988,6 @@ impl CoherenceChecker {
crate_store,
def_id);
}
dl_def(def_trait(def_id)) => {
self.add_default_methods_for_external_trait(def_id);
}
dl_def(_) | dl_impl(_) | dl_field => {
// Skip this.
loop;
@ -1063,12 +1050,11 @@ impl CoherenceChecker {
}
}
fn subst_receiver_types_in_method_ty(tcx: ty::ctxt,
impl_id: ast::node_id,
trait_ref: &ty::TraitRef,
new_def_id: ast::def_id,
method: &ty::Method)
-> ty::Method {
pub fn make_substs_for_receiver_types(tcx: ty::ctxt,
impl_id: ast::def_id,
trait_ref: &ty::TraitRef,
method: &ty::Method)
-> ty::substs {
/*!
* Substitutes the values for the receiver's type parameters
* that are found in method, leaving the method's type parameters
@ -1079,7 +1065,7 @@ fn subst_receiver_types_in_method_ty(tcx: ty::ctxt,
// determine how many type parameters were declared on the impl
let num_impl_type_parameters = {
let impl_polytype = ty::lookup_item_type(tcx, local_def(impl_id));
let impl_polytype = ty::lookup_item_type(tcx, impl_id);
impl_polytype.generics.type_param_defs.len()
};
@ -1105,11 +1091,22 @@ fn subst_receiver_types_in_method_ty(tcx: ty::ctxt,
}
});
let combined_substs = ty::substs {
return ty::substs {
self_r: trait_ref.substs.self_r,
self_ty: trait_ref.substs.self_ty,
tps: combined_tps
};
}
fn subst_receiver_types_in_method_ty(tcx: ty::ctxt,
impl_id: ast::def_id,
trait_ref: &ty::TraitRef,
new_def_id: ast::def_id,
method: &ty::Method)
-> ty::Method {
let combined_substs = make_substs_for_receiver_types(
tcx, impl_id, trait_ref, method);
ty::Method::new(
method.ident,

View File

@ -0,0 +1,34 @@
#[allow(default_methods)];
pub trait A {
fn f(&self) -> int;
fn g(&self) -> int { 10 }
fn h(&self) -> int { 10 }
}
impl A for int {
fn f(&self) -> int { 10 }
}
trait B<T> {
fn thing<U>(&self, x: T, y: U) -> (T, U) { (x, y) }
}
impl<T> B<T> for int { }
impl B<float> for bool { }
pub trait TestEquality {
fn test_eq(&self, rhs: &Self) -> bool;
fn test_neq(&self, rhs: &Self) -> bool {
!self.test_eq(rhs)
}
}
impl TestEquality for int {
fn test_eq(&self, rhs: &int) -> bool {
*self == *rhs
}
}

View File

@ -15,6 +15,7 @@ trait A<T> {
}
impl A<int> for int { }
impl<T> A<T> for uint { }
fn f<T, U, V: A<T>>(i: V, j: T, k: U) -> (T, U) {
i.g(j, k)
@ -22,4 +23,5 @@ fn f<T, U, V: A<T>>(i: V, j: T, k: U) -> (T, U) {
pub fn main () {
assert_eq!(f(0, 1, 2), (1, 2));
assert_eq!(f(0u, 1, 2), (1, 2));
}

View File

@ -0,0 +1,71 @@
// xfail-fast
// aux-build:trait_default_method_xc_aux.rs
#[allow(default_methods)];
extern mod aux(name = "trait_default_method_xc_aux");
use aux::{A, B, TestEquality};
fn f<T: aux::A>(i: T) {
assert_eq!(i.g(), 10);
}
pub struct thing { x: int }
impl A for thing {
fn f(&self) -> int { 10 }
}
fn g<T, U, V: B<T>>(i: V, j: T, k: U) -> (T, U) {
i.thing(j, k)
}
fn eq<T: TestEquality>(lhs: &T, rhs: &T) -> bool {
lhs.test_eq(rhs)
}
fn neq<T: TestEquality>(lhs: &T, rhs: &T) -> bool {
lhs.test_neq(rhs)
}
impl TestEquality for thing {
fn test_eq(&self, rhs: &thing) -> bool {
//self.x.test_eq(&rhs.x)
eq(&self.x, &rhs.x)
}
}
fn main () {
// Some tests of random things
f(0);
let a = thing { x: 0 };
let b = thing { x: 1 };
assert_eq!(0i.g(), 10);
assert_eq!(a.g(), 10);
assert_eq!(a.h(), 10);
assert_eq!(0i.thing(3.14, 1), (3.14, 1));
assert_eq!(g(0i, 3.14, 1), (3.14, 1));
assert_eq!(g(false, 3.14, 1), (3.14, 1));
let obj = @0i as @A;
assert_eq!(obj.h(), 10);
// Trying out a real one
assert!(12.test_neq(&10));
assert!(!10.test_neq(&10));
assert!(a.test_neq(&b));
assert!(!a.test_neq(&a));
assert!(neq(&12, &10));
assert!(!neq(&10, &10));
assert!(neq(&a, &b));
assert!(!neq(&a, &a));
}