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_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_)>> {

View file

@ -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.

View file

@ -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) {

View file

@ -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,

View file

@ -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,

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 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);

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;
}