1
Fork 0

rustc: Implement generic cross-crate trait inheritance

This commit is contained in:
Patrick Walton 2012-10-19 21:27:01 -07:00
parent 705afcd844
commit ec1c60c4d6
9 changed files with 203 additions and 42 deletions

View file

@ -24,6 +24,7 @@ export get_enum_variants;
export get_impls_for_mod; export get_impls_for_mod;
export get_trait_methods; export get_trait_methods;
export get_provided_trait_methods; export get_provided_trait_methods;
export get_supertraits;
export get_method_names_if_trait; export get_method_names_if_trait;
export get_type_name_if_impl; export get_type_name_if_impl;
export get_static_methods_if_impl; export get_static_methods_if_impl;
@ -122,6 +123,12 @@ fn get_provided_trait_methods(tcx: ty::ctxt, def: ast::def_id) ->
decoder::get_provided_trait_methods(cstore.intr, cdata, def.node, tcx) decoder::get_provided_trait_methods(cstore.intr, cdata, def.node, tcx)
} }
fn get_supertraits(tcx: ty::ctxt, def: ast::def_id) -> ~[ty::t] {
let cstore = tcx.cstore;
let cdata = cstore::get_crate_data(cstore, def.crate);
decoder::get_supertraits(cdata, def.node, tcx)
}
fn get_method_names_if_trait(cstore: cstore::CStore, def: ast::def_id) fn get_method_names_if_trait(cstore: cstore::CStore, def: ast::def_id)
-> Option<@DVec<(ast::ident, ast::self_ty_)>> { -> Option<@DVec<(ast::ident, ast::self_ty_)>> {

View file

@ -43,6 +43,7 @@ export get_crate_vers;
export get_impls_for_mod; export get_impls_for_mod;
export get_trait_methods; export get_trait_methods;
export get_provided_trait_methods; export get_provided_trait_methods;
export get_supertraits;
export get_method_names_if_trait; export get_method_names_if_trait;
export get_type_name_if_impl; export get_type_name_if_impl;
export get_item_attrs; export get_item_attrs;
@ -771,6 +772,16 @@ fn get_provided_trait_methods(intr: @ident_interner, cdata: cmd,
return move result; return move result;
} }
/// Returns the supertraits of the given trait.
fn get_supertraits(cdata: cmd, id: ast::node_id, tcx: ty::ctxt) -> ~[ty::t] {
let results = dvec::DVec();
let item_doc = lookup_item(id, cdata.data);
for ebml::tagged_docs(item_doc, tag_impl_trait) |trait_doc| {
results.push(doc_type(trait_doc, tcx, cdata));
}
return dvec::unwrap(move results);
}
// If the item in question is a trait, returns its set of methods and // If the item in question is a trait, returns its set of methods and
// their self types. Otherwise, returns none. This overlaps in an // their self types. Otherwise, returns none. This overlaps in an
// annoying way with get_trait_methods. // annoying way with get_trait_methods.

View file

@ -20,6 +20,7 @@ use syntax::print::pprust::*;
use util::ppaux::{ty_to_str, proto_ty_to_str, tys_to_str}; use util::ppaux::{ty_to_str, proto_ty_to_str, tys_to_str};
export ProvidedMethodSource; export ProvidedMethodSource;
export InstantiatedTraitRef;
export TyVid, IntVid, FnVid, RegionVid, vid; export TyVid, IntVid, FnVid, RegionVid, vid;
export br_hashmap; export br_hashmap;
export is_instantiable; export is_instantiable;
@ -66,6 +67,7 @@ export sequence_element_type;
export stmt_node_id; export stmt_node_id;
export sty; export sty;
export subst, subst_tps, substs_is_noop, substs_to_str, substs; export subst, subst_tps, substs_is_noop, substs_to_str, substs;
export subst_substs;
export t; export t;
export new_ty_hash; export new_ty_hash;
export enum_variants, substd_enum_variants, enum_is_univariant; export enum_variants, substd_enum_variants, enum_is_univariant;
@ -73,6 +75,7 @@ export trait_methods, store_trait_methods, impl_traits;
export enum_variant_with_id; export enum_variant_with_id;
export ty_dtor; export ty_dtor;
export ty_param_bounds_and_ty; export ty_param_bounds_and_ty;
export ty_param_substs_and_ty;
export ty_bool, mk_bool, type_is_bool; export ty_bool, mk_bool, type_is_bool;
export ty_bot, mk_bot, type_is_bot; export ty_bot, mk_bot, type_is_bot;
export ty_box, mk_box, mk_imm_box, type_is_box, type_is_boxed; export ty_box, mk_box, mk_imm_box, type_is_box, type_is_boxed;
@ -191,6 +194,7 @@ export region_variance, rv_covariant, rv_invariant, rv_contravariant;
export opt_region_variance; export opt_region_variance;
export determine_inherited_purity; export determine_inherited_purity;
export provided_trait_methods; export provided_trait_methods;
export trait_supertraits;
export AutoAdjustment; export AutoAdjustment;
export AutoRef, AutoRefKind, AutoSlice, AutoPtr; export AutoRef, AutoRefKind, AutoSlice, AutoPtr;
@ -321,6 +325,11 @@ struct ProvidedMethodSource {
impl_id: ast::def_id impl_id: ast::def_id
} }
struct InstantiatedTraitRef {
def_id: ast::def_id,
tpt: ty_param_substs_and_ty
}
type ctxt = type ctxt =
@{diag: syntax::diagnostic::span_handler, @{diag: syntax::diagnostic::span_handler,
interner: HashMap<intern_key, t_box>, interner: HashMap<intern_key, t_box>,
@ -364,7 +373,8 @@ type ctxt =
normalized_cache: HashMap<t, t>, normalized_cache: HashMap<t, t>,
lang_items: middle::lang_items::LanguageItems, lang_items: middle::lang_items::LanguageItems,
legacy_boxed_traits: HashMap<node_id, ()>, legacy_boxed_traits: HashMap<node_id, ()>,
provided_method_sources: HashMap<ast::def_id, ProvidedMethodSource>}; provided_method_sources: HashMap<ast::def_id, ProvidedMethodSource>,
supertraits: HashMap<ast::def_id, @~[InstantiatedTraitRef]>};
enum tbox_flag { enum tbox_flag {
has_params = 1, has_params = 1,
@ -819,6 +829,8 @@ type ty_param_bounds_and_ty = {bounds: @~[param_bounds],
region_param: Option<region_variance>, region_param: Option<region_variance>,
ty: t}; ty: t};
type ty_param_substs_and_ty = {substs: ty::substs, ty: ty::t};
type type_cache = HashMap<ast::def_id, ty_param_bounds_and_ty>; type type_cache = HashMap<ast::def_id, ty_param_bounds_and_ty>;
type constness_cache = HashMap<ast::def_id, const_eval::constness>; type constness_cache = HashMap<ast::def_id, const_eval::constness>;
@ -888,7 +900,8 @@ fn mk_ctxt(s: session::Session,
normalized_cache: new_ty_hash(), normalized_cache: new_ty_hash(),
lang_items: move lang_items, lang_items: move lang_items,
legacy_boxed_traits: HashMap(), legacy_boxed_traits: HashMap(),
provided_method_sources: HashMap()} provided_method_sources: HashMap(),
supertraits: HashMap()}
} }
@ -1486,6 +1499,16 @@ fn subst(cx: ctxt,
} }
} }
// Performs substitutions on a set of substitutions (result = super(sub)) to
// yield a new set of substitutions. This is used in trait inheritance.
fn subst_substs(cx: ctxt, super: &substs, sub: &substs) -> substs {
{
self_r: super.self_r,
self_ty: super.self_ty.map(|typ| subst(cx, sub, *typ)),
tps: super.tps.map(|typ| subst(cx, sub, *typ))
}
}
// Type utilities // Type utilities
fn type_is_nil(ty: t) -> bool { get(ty).sty == ty_nil } fn type_is_nil(ty: t) -> bool { get(ty).sty == ty_nil }
@ -3365,6 +3388,35 @@ fn provided_trait_methods(cx: ctxt, id: ast::def_id) -> ~[ast::ident] {
} }
} }
fn trait_supertraits(cx: ctxt, id: ast::def_id) -> @~[InstantiatedTraitRef] {
// Check the cache.
match cx.supertraits.find(id) {
Some(instantiated_trait_info) => { return instantiated_trait_info; }
None => {} // Continue.
}
// Not in the cache. It had better be in the metadata, which means it
// shouldn't be local.
assert !is_local(id);
// Get the supertraits out of the metadata and create the
// InstantiatedTraitRef for each.
let result = dvec::DVec();
for csearch::get_supertraits(cx, id).each |trait_type| {
match get(*trait_type).sty {
ty_trait(def_id, substs, _) => {
result.push(InstantiatedTraitRef {
def_id: def_id,
tpt: { substs: substs, ty: *trait_type }
});
}
_ => cx.sess.bug(~"trait_supertraits: trait ref wasn't a trait")
}
}
// Unwrap and return the result.
return @dvec::unwrap(move result);
}
fn trait_methods(cx: ctxt, id: ast::def_id) -> @~[method] { fn trait_methods(cx: ctxt, id: ast::def_id) -> @~[method] {
match cx.trait_method_cache.find(id) { match cx.trait_method_cache.find(id) {

View file

@ -51,7 +51,7 @@ use syntax::codemap::span;
use pat_util::{pat_is_variant, pat_id_map, PatIdMap}; use pat_util::{pat_is_variant, pat_id_map, PatIdMap};
use middle::ty; use middle::ty;
use middle::ty::{arg, field, node_type_table, mk_nil, ty_param_bounds_and_ty}; use middle::ty::{arg, field, node_type_table, mk_nil, ty_param_bounds_and_ty};
use middle::ty::{vstore_uniq}; use middle::ty::{ty_param_substs_and_ty, vstore_uniq};
use std::smallintmap; use std::smallintmap;
use std::map; use std::map;
use std::map::HashMap; use std::map::HashMap;
@ -174,8 +174,6 @@ impl vtable_origin {
type vtable_map = HashMap<ast::node_id, vtable_res>; type vtable_map = HashMap<ast::node_id, vtable_res>;
type ty_param_substs_and_ty = {substs: ty::substs, ty: ty::t};
type crate_ctxt_ = {// A mapping from method call sites to traits that have type crate_ctxt_ = {// A mapping from method call sites to traits that have
// that method. // that method.
trait_map: resolve::TraitMap, trait_map: resolve::TraitMap,

View file

@ -313,45 +313,73 @@ impl LookupContext {
} }
}; };
let trait_methods = ty::trait_methods(tcx, trait_id); // Loop over the trait and all of its supertraits.
let pos = { let worklist = dvec::DVec();
// FIXME #3453 can't use trait_methods.position worklist.push((trait_id, move bound_substs));
match vec::position(*trait_methods,
|m| (m.self_ty != ast::sty_static && let mut i = 0;
m.ident == self.m_name)) while i < worklist.len() {
{ let (trait_id, bound_substs) = worklist[i];
Some(pos) => pos, i += 1;
None => {
loop; // check next bound // Replace any appearance of `self` with the type of the
} // generic parameter itself. Note that this is the only
// case where this replacement is necessary: in all other
// cases, we are either invoking a method directly from an
// impl or class (where the self type is not permitted),
// or from a trait type (in which case methods that refer
// to self are not permitted).
let rcvr_ty = ty::mk_param(tcx, param_ty.idx,
param_ty.def_id);
let rcvr_substs = {self_ty: Some(rcvr_ty), ..bound_substs};
// Add all the supertraits of this trait to the worklist.
debug!("finding supertraits for %d:%d", trait_id.crate,
trait_id.node);
let instantiated_trait_refs = ty::trait_supertraits(
tcx, trait_id);
for instantiated_trait_refs.each |instantiated_trait_ref| {
debug!("adding supertrait");
let new_substs = ty::subst_substs(
tcx,
&instantiated_trait_ref.tpt.substs,
&rcvr_substs);
worklist.push(
(instantiated_trait_ref.def_id, new_substs));
} }
};
let method = &trait_methods[pos];
// Replace any appearance of `self` with the type of the let trait_methods = ty::trait_methods(tcx, trait_id);
// generic parameter itself. Note that this is the only let pos = {
// case where this replacement is necessary: in all other // FIXME #3453 can't use trait_methods.position
// cases, we are either invoking a method directly from an match vec::position(*trait_methods,
// impl or class (where the self type is not permitted), |m| (m.self_ty != ast::sty_static &&
// or from a trait type (in which case methods that refer m.ident == self.m_name))
// to self are not permitted). {
let rcvr_ty = ty::mk_param(tcx, param_ty.idx, param_ty.def_id); Some(pos) => pos,
let rcvr_substs = {self_ty: Some(rcvr_ty), ..bound_substs}; None => {
loop; // check next trait or bound
}
}
};
let method = &trait_methods[pos];
let (rcvr_ty, rcvr_substs) = let (rcvr_ty, rcvr_substs) =
self.create_rcvr_ty_and_substs_for_method( self.create_rcvr_ty_and_substs_for_method(
method.self_ty, rcvr_ty, move rcvr_substs); method.self_ty, rcvr_ty, move rcvr_substs);
self.inherent_candidates.push(Candidate { self.inherent_candidates.push(Candidate {
rcvr_ty: rcvr_ty, rcvr_ty: rcvr_ty,
rcvr_substs: rcvr_substs, rcvr_substs: rcvr_substs,
num_method_tps: method.tps.len(), num_method_tps: method.tps.len(),
self_mode: get_mode_from_self_type(method.self_ty), self_mode: get_mode_from_self_type(method.self_ty),
origin: method_param({trait_id:trait_id, origin: method_param({trait_id:trait_id,
method_num:pos, method_num:pos,
param_num:param_ty.idx, param_num:param_ty.idx,
bound_num:this_bound_idx}) bound_num:this_bound_idx})
}); });
}
} }
} }

View file

@ -23,7 +23,7 @@ are represented as `ty_param()` instances.
use astconv::{ast_conv, ty_of_fn_decl, ty_of_arg, ast_ty_to_ty}; use astconv::{ast_conv, ty_of_fn_decl, ty_of_arg, ast_ty_to_ty};
use ast_util::trait_method_to_ty_method; use ast_util::trait_method_to_ty_method;
use rscope::*; use rscope::*;
use ty::{FnTyBase, FnMeta, FnSig}; use ty::{FnTyBase, FnMeta, FnSig, InstantiatedTraitRef};
use util::common::pluralize; use util::common::pluralize;
use util::ppaux::bound_to_str; use util::ppaux::bound_to_str;
@ -239,6 +239,21 @@ fn ensure_trait_methods(ccx: @crate_ctxt, id: ast::node_id, trait_ty: ty::t) {
} }
} }
fn ensure_supertraits(ccx: @crate_ctxt,
id: ast::node_id,
rp: Option<ty::region_variance>,
trait_refs: &[@ast::trait_ref]) {
if ccx.tcx.supertraits.contains_key(local_def(id)) { return; }
let instantiated = dvec::DVec();
for trait_refs.each |trait_ref| {
let (did, tpt) = instantiate_trait_ref(ccx, *trait_ref, rp);
instantiated.push(InstantiatedTraitRef { def_id: did, tpt: tpt });
}
ccx.tcx.supertraits.insert(local_def(id),
@dvec::unwrap(move instantiated));
}
/** /**
* Checks that a method from an impl/class conforms to the signature of * Checks that a method from an impl/class conforms to the signature of
* the same method as declared in the trait. * the same method as declared in the trait.
@ -462,12 +477,13 @@ fn convert(ccx: @crate_ctxt, it: @ast::item) {
check_methods_against_trait(ccx, tps, rp, selfty, *t, cms); check_methods_against_trait(ccx, tps, rp, selfty, *t, cms);
} }
} }
ast::item_trait(tps, _, trait_methods) => { ast::item_trait(tps, supertraits, trait_methods) => {
let tpt = ty_of_item(ccx, it); let tpt = ty_of_item(ccx, it);
debug!("item_trait(it.id=%d, tpt.ty=%s)", debug!("item_trait(it.id=%d, tpt.ty=%s)",
it.id, ty_to_str(tcx, tpt.ty)); it.id, ty_to_str(tcx, tpt.ty));
write_ty_to_tcx(tcx, it.id, tpt.ty); write_ty_to_tcx(tcx, it.id, tpt.ty);
ensure_trait_methods(ccx, it.id, tpt.ty); ensure_trait_methods(ccx, it.id, tpt.ty);
ensure_supertraits(ccx, it.id, rp, supertraits);
let (_, provided_methods) = split_trait_methods(trait_methods); let (_, provided_methods) = split_trait_methods(trait_methods);
let {bounds, _} = mk_substs(ccx, tps, rp); let {bounds, _} = mk_substs(ccx, tps, rp);

View file

@ -0,0 +1,9 @@
pub trait MyNum : Add<self,self>, Sub<self,self>, Mul<self,self> {
}
pub impl int : MyNum {
pure fn add(other: &int) -> int { self + *other }
pure fn sub(other: &int) -> int { self - *other }
pure fn mul(other: &int) -> int { self * *other }
}

View file

@ -0,0 +1,19 @@
// xfail-fast - check-fast doesn't understand aux-build
// aux-build:trait_inheritance_overloading_xc.rs
extern mod trait_inheritance_overloading_xc;
use trait_inheritance_overloading_xc::MyNum;
fn f<T:Copy MyNum>(x: T, y: T) -> (T, T, T) {
return (x + y, x - y, x * y);
}
fn main() {
let (x, y) = (3, 5);
let (a, b, c) = f(x, y);
assert a == 8;
assert b == -2;
assert c == 15;
}

View file

@ -0,0 +1,21 @@
trait MyNum : Add<self,self>, Sub<self,self>, Mul<self,self> {
}
impl int : MyNum {
pure fn add(other: &int) -> int { self + *other }
pure fn sub(other: &int) -> int { self - *other }
pure fn mul(other: &int) -> int { self * *other }
}
fn f<T:Copy MyNum>(x: T, y: T) -> (T, T, T) {
return (x + y, x - y, x * y);
}
fn main() {
let (x, y) = (3, 5);
let (a, b, c) = f(x, y);
assert a == 8;
assert b == -2;
assert c == 15;
}