diff --git a/src/rustc/metadata/csearch.rs b/src/rustc/metadata/csearch.rs index 141613c2240..bbc324fbdff 100644 --- a/src/rustc/metadata/csearch.rs +++ b/src/rustc/metadata/csearch.rs @@ -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_)>> { diff --git a/src/rustc/metadata/decoder.rs b/src/rustc/metadata/decoder.rs index 4e30132b1a7..bc6a8da96b8 100644 --- a/src/rustc/metadata/decoder.rs +++ b/src/rustc/metadata/decoder.rs @@ -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. diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index 90527e88bc8..8c4c4232b00 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -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, @@ -364,7 +373,8 @@ type ctxt = normalized_cache: HashMap, lang_items: middle::lang_items::LanguageItems, legacy_boxed_traits: HashMap, - provided_method_sources: HashMap}; + provided_method_sources: HashMap, + supertraits: HashMap}; enum tbox_flag { has_params = 1, @@ -819,6 +829,8 @@ type ty_param_bounds_and_ty = {bounds: @~[param_bounds], region_param: Option, ty: t}; +type ty_param_substs_and_ty = {substs: ty::substs, ty: ty::t}; + type type_cache = HashMap; type constness_cache = HashMap; @@ -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) { diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs index 90aacb4b7ce..afb13cf52ab 100644 --- a/src/rustc/middle/typeck.rs +++ b/src/rustc/middle/typeck.rs @@ -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; -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, diff --git a/src/rustc/middle/typeck/check/method.rs b/src/rustc/middle/typeck/check/method.rs index eaf1a45afa9..eb28d06fdea 100644 --- a/src/rustc/middle/typeck/check/method.rs +++ b/src/rustc/middle/typeck/check/method.rs @@ -313,45 +313,73 @@ impl LookupContext { } }; - let trait_methods = ty::trait_methods(tcx, trait_id); - let pos = { - // FIXME #3453 can't use trait_methods.position - match vec::position(*trait_methods, - |m| (m.self_ty != ast::sty_static && - m.ident == self.m_name)) - { - Some(pos) => pos, - None => { - loop; // check next bound - } + // 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 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 trait_methods = ty::trait_methods(tcx, trait_id); + let pos = { + // FIXME #3453 can't use trait_methods.position + match vec::position(*trait_methods, + |m| (m.self_ty != ast::sty_static && + m.ident == self.m_name)) + { + Some(pos) => pos, + None => { + loop; // check next trait or bound + } + } + }; + let method = &trait_methods[pos]; - let (rcvr_ty, rcvr_substs) = - self.create_rcvr_ty_and_substs_for_method( - method.self_ty, rcvr_ty, move rcvr_substs); + let (rcvr_ty, rcvr_substs) = + self.create_rcvr_ty_and_substs_for_method( + method.self_ty, rcvr_ty, move rcvr_substs); - self.inherent_candidates.push(Candidate { - rcvr_ty: rcvr_ty, - rcvr_substs: rcvr_substs, - num_method_tps: method.tps.len(), - self_mode: get_mode_from_self_type(method.self_ty), - origin: method_param({trait_id:trait_id, - method_num:pos, - param_num:param_ty.idx, - bound_num:this_bound_idx}) - }); + self.inherent_candidates.push(Candidate { + rcvr_ty: rcvr_ty, + rcvr_substs: rcvr_substs, + num_method_tps: method.tps.len(), + self_mode: get_mode_from_self_type(method.self_ty), + origin: method_param({trait_id:trait_id, + method_num:pos, + param_num:param_ty.idx, + bound_num:this_bound_idx}) + }); + } } } diff --git a/src/rustc/middle/typeck/collect.rs b/src/rustc/middle/typeck/collect.rs index e12104a21da..7e1696549e1 100644 --- a/src/rustc/middle/typeck/collect.rs +++ b/src/rustc/middle/typeck/collect.rs @@ -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, + 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); diff --git a/src/test/auxiliary/trait_inheritance_overloading_xc.rs b/src/test/auxiliary/trait_inheritance_overloading_xc.rs new file mode 100644 index 00000000000..1608a332fe5 --- /dev/null +++ b/src/test/auxiliary/trait_inheritance_overloading_xc.rs @@ -0,0 +1,9 @@ +pub trait MyNum : Add, Sub, Mul { +} + +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 } +} + diff --git a/src/test/run-pass/trait-inheritance-overloading-xc-exe.rs b/src/test/run-pass/trait-inheritance-overloading-xc-exe.rs new file mode 100644 index 00000000000..a38a834fb72 --- /dev/null +++ b/src/test/run-pass/trait-inheritance-overloading-xc-exe.rs @@ -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(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; +} + + diff --git a/src/test/run-pass/trait-inheritance-overloading.rs b/src/test/run-pass/trait-inheritance-overloading.rs new file mode 100644 index 00000000000..4014cd2c623 --- /dev/null +++ b/src/test/run-pass/trait-inheritance-overloading.rs @@ -0,0 +1,21 @@ +trait MyNum : Add, Sub, Mul { +} + +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(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; +} +