rustc: Implement generic cross-crate trait inheritance
This commit is contained in:
parent
705afcd844
commit
ec1c60c4d6
9 changed files with 203 additions and 42 deletions
|
@ -24,6 +24,7 @@ export get_enum_variants;
|
|||
export get_impls_for_mod;
|
||||
export get_trait_methods;
|
||||
export get_provided_trait_methods;
|
||||
export get_supertraits;
|
||||
export get_method_names_if_trait;
|
||||
export get_type_name_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)
|
||||
}
|
||||
|
||||
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)
|
||||
-> Option<@DVec<(ast::ident, ast::self_ty_)>> {
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ export get_crate_vers;
|
|||
export get_impls_for_mod;
|
||||
export get_trait_methods;
|
||||
export get_provided_trait_methods;
|
||||
export get_supertraits;
|
||||
export get_method_names_if_trait;
|
||||
export get_type_name_if_impl;
|
||||
export get_item_attrs;
|
||||
|
@ -771,6 +772,16 @@ fn get_provided_trait_methods(intr: @ident_interner, cdata: cmd,
|
|||
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
|
||||
// their self types. Otherwise, returns none. This overlaps in an
|
||||
// annoying way with get_trait_methods.
|
||||
|
|
|
@ -20,6 +20,7 @@ use syntax::print::pprust::*;
|
|||
use util::ppaux::{ty_to_str, proto_ty_to_str, tys_to_str};
|
||||
|
||||
export ProvidedMethodSource;
|
||||
export InstantiatedTraitRef;
|
||||
export TyVid, IntVid, FnVid, RegionVid, vid;
|
||||
export br_hashmap;
|
||||
export is_instantiable;
|
||||
|
@ -66,6 +67,7 @@ export sequence_element_type;
|
|||
export stmt_node_id;
|
||||
export sty;
|
||||
export subst, subst_tps, substs_is_noop, substs_to_str, substs;
|
||||
export subst_substs;
|
||||
export t;
|
||||
export new_ty_hash;
|
||||
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 ty_dtor;
|
||||
export ty_param_bounds_and_ty;
|
||||
export ty_param_substs_and_ty;
|
||||
export ty_bool, mk_bool, type_is_bool;
|
||||
export ty_bot, mk_bot, type_is_bot;
|
||||
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 determine_inherited_purity;
|
||||
export provided_trait_methods;
|
||||
export trait_supertraits;
|
||||
export AutoAdjustment;
|
||||
export AutoRef, AutoRefKind, AutoSlice, AutoPtr;
|
||||
|
||||
|
@ -321,6 +325,11 @@ struct ProvidedMethodSource {
|
|||
impl_id: ast::def_id
|
||||
}
|
||||
|
||||
struct InstantiatedTraitRef {
|
||||
def_id: ast::def_id,
|
||||
tpt: ty_param_substs_and_ty
|
||||
}
|
||||
|
||||
type ctxt =
|
||||
@{diag: syntax::diagnostic::span_handler,
|
||||
interner: HashMap<intern_key, t_box>,
|
||||
|
@ -364,7 +373,8 @@ type ctxt =
|
|||
normalized_cache: HashMap<t, t>,
|
||||
lang_items: middle::lang_items::LanguageItems,
|
||||
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 {
|
||||
has_params = 1,
|
||||
|
@ -819,6 +829,8 @@ type ty_param_bounds_and_ty = {bounds: @~[param_bounds],
|
|||
region_param: Option<region_variance>,
|
||||
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 constness_cache = HashMap<ast::def_id, const_eval::constness>;
|
||||
|
@ -888,7 +900,8 @@ fn mk_ctxt(s: session::Session,
|
|||
normalized_cache: new_ty_hash(),
|
||||
lang_items: move lang_items,
|
||||
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
|
||||
|
||||
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] {
|
||||
match cx.trait_method_cache.find(id) {
|
||||
|
|
|
@ -51,7 +51,7 @@ use syntax::codemap::span;
|
|||
use pat_util::{pat_is_variant, pat_id_map, PatIdMap};
|
||||
use middle::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::map;
|
||||
use std::map::HashMap;
|
||||
|
@ -174,8 +174,6 @@ impl vtable_origin {
|
|||
|
||||
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
|
||||
// that method.
|
||||
trait_map: resolve::TraitMap,
|
||||
|
|
|
@ -313,6 +313,43 @@ impl LookupContext {
|
|||
}
|
||||
};
|
||||
|
||||
// Loop over the trait and all of its supertraits.
|
||||
let worklist = dvec::DVec();
|
||||
worklist.push((trait_id, move bound_substs));
|
||||
|
||||
let mut i = 0;
|
||||
while i < worklist.len() {
|
||||
let (trait_id, bound_substs) = worklist[i];
|
||||
i += 1;
|
||||
|
||||
// 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 trait_methods = ty::trait_methods(tcx, trait_id);
|
||||
let pos = {
|
||||
// FIXME #3453 can't use trait_methods.position
|
||||
|
@ -322,22 +359,12 @@ impl LookupContext {
|
|||
{
|
||||
Some(pos) => pos,
|
||||
None => {
|
||||
loop; // check next bound
|
||||
loop; // check next trait or bound
|
||||
}
|
||||
}
|
||||
};
|
||||
let method = &trait_methods[pos];
|
||||
|
||||
// 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};
|
||||
|
||||
let (rcvr_ty, rcvr_substs) =
|
||||
self.create_rcvr_ty_and_substs_for_method(
|
||||
method.self_ty, rcvr_ty, move rcvr_substs);
|
||||
|
@ -354,6 +381,7 @@ impl LookupContext {
|
|||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn push_inherent_candidates_from_trait(&self,
|
||||
self_ty: ty::t,
|
||||
|
|
|
@ -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 ast_util::trait_method_to_ty_method;
|
||||
use rscope::*;
|
||||
use ty::{FnTyBase, FnMeta, FnSig};
|
||||
use ty::{FnTyBase, FnMeta, FnSig, InstantiatedTraitRef};
|
||||
use util::common::pluralize;
|
||||
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
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
ast::item_trait(tps, _, trait_methods) => {
|
||||
ast::item_trait(tps, supertraits, trait_methods) => {
|
||||
let tpt = ty_of_item(ccx, it);
|
||||
debug!("item_trait(it.id=%d, tpt.ty=%s)",
|
||||
it.id, ty_to_str(tcx, tpt.ty));
|
||||
write_ty_to_tcx(tcx, 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 {bounds, _} = mk_substs(ccx, tps, rp);
|
||||
|
|
9
src/test/auxiliary/trait_inheritance_overloading_xc.rs
Normal file
9
src/test/auxiliary/trait_inheritance_overloading_xc.rs
Normal 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 }
|
||||
}
|
||||
|
19
src/test/run-pass/trait-inheritance-overloading-xc-exe.rs
Normal file
19
src/test/run-pass/trait-inheritance-overloading-xc-exe.rs
Normal 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;
|
||||
}
|
||||
|
||||
|
21
src/test/run-pass/trait-inheritance-overloading.rs
Normal file
21
src/test/run-pass/trait-inheritance-overloading.rs
Normal 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;
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue