diff --git a/src/rustc/metadata/csearch.rs b/src/rustc/metadata/csearch.rs index 141613c2240d8..bbc324fbdff4c 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 4e30132b1a73f..bc6a8da96b89a 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 90527e88bc850..8c4c4232b0024 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 8d10343d78ee0..1e414353604d7 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.rs b/src/rustc/middle/typeck/check.rs index 6de249ebc68f2..4227a69f748b1 100644 --- a/src/rustc/middle/typeck/check.rs +++ b/src/rustc/middle/typeck/check.rs @@ -1441,6 +1441,136 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, return bot; } + fn check_struct_constructor(fcx: @fn_ctxt, + id: ast::node_id, + span: syntax::codemap::span, + class_id: ast::def_id, + fields: ~[ast::field], + base_expr: Option<@ast::expr>) -> bool { + let mut bot = false; + let tcx = fcx.ccx.tcx; + + // Look up the number of type parameters and the raw type, and + // determine whether the class is region-parameterized. + let type_parameter_count, region_parameterized, raw_type; + if class_id.crate == ast::local_crate { + region_parameterized = + tcx.region_paramd_items.find(class_id.node); + match tcx.items.find(class_id.node) { + Some(ast_map::node_item(@{ + node: ast::item_class(_, type_parameters), + _ + }, _)) => { + + type_parameter_count = type_parameters.len(); + + let self_region = + bound_self_region(region_parameterized); + + raw_type = ty::mk_class(tcx, class_id, { + self_r: self_region, + self_ty: None, + tps: ty::ty_params_to_tys(tcx, type_parameters) + }); + } + _ => { + tcx.sess.span_bug(span, + ~"resolve didn't map this to a class"); + } + } + } else { + let item_type = ty::lookup_item_type(tcx, class_id); + type_parameter_count = (*item_type.bounds).len(); + region_parameterized = item_type.region_param; + raw_type = item_type.ty; + } + + // Generate the struct type. + let self_region = + fcx.region_var_if_parameterized(region_parameterized, + span, + ty::re_scope(id)); + let type_parameters = fcx.infcx().next_ty_vars(type_parameter_count); + let substitutions = { + self_r: self_region, + self_ty: None, + tps: type_parameters + }; + + let struct_type = ty::subst(tcx, &substitutions, raw_type); + + // Look up the class fields and build up a map. + let class_fields = ty::lookup_class_fields(tcx, class_id); + let class_field_map = HashMap(); + let mut fields_found = 0; + for class_fields.each |field| { + // XXX: Check visibility here. + class_field_map.insert(field.ident, (field.id, false)); + } + + // Typecheck each field. + for fields.each |field| { + match class_field_map.find(field.node.ident) { + None => { + tcx.sess.span_err( + field.span, + fmt!("structure has no field named field named `%s`", + tcx.sess.str_of(field.node.ident))); + } + Some((_, true)) => { + tcx.sess.span_err( + field.span, + fmt!("field `%s` specified more than once", + tcx.sess.str_of(field.node.ident))); + } + Some((field_id, false)) => { + let expected_field_type = + ty::lookup_field_type(tcx, class_id, field_id, + &substitutions); + bot |= check_expr(fcx, + field.node.expr, + Some(expected_field_type)); + fields_found += 1; + } + } + } + + match base_expr { + None => { + // Make sure the programmer specified all the fields. + assert fields_found <= class_fields.len(); + if fields_found < class_fields.len() { + let mut missing_fields = ~[]; + for class_fields.each |class_field| { + let name = class_field.ident; + let (_, seen) = class_field_map.get(name); + if !seen { + missing_fields.push( + ~"`" + tcx.sess.str_of(name) + ~"`"); + } + } + + tcx.sess.span_err(span, + fmt!("missing field%s: %s", + if missing_fields.len() == 1 { + ~"" + } else { + ~"s" + }, + str::connect(missing_fields, + ~", "))); + } + } + Some(base_expr) => { + // Just check the base expression. + check_expr(fcx, base_expr, Some(struct_type)); + } + } + + // Write in the resulting type. + fcx.write_ty(id, struct_type); + return bot; + } let tcx = fcx.ccx.tcx; let id = expr.id; @@ -1911,136 +2041,16 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, } ast::expr_struct(path, fields, base_expr) => { // Resolve the path. - let class_id; match tcx.def_map.find(id) { Some(ast::def_class(type_def_id)) => { - class_id = type_def_id; + check_struct_constructor(fcx, id, expr.span, type_def_id, + fields, base_expr); } _ => { tcx.sess.span_bug(path.span, ~"structure constructor does \ not name a structure type"); } } - - // Look up the number of type parameters and the raw type, and - // determine whether the class is region-parameterized. - let type_parameter_count, region_parameterized, raw_type; - if class_id.crate == ast::local_crate { - region_parameterized = - tcx.region_paramd_items.find(class_id.node); - match tcx.items.find(class_id.node) { - Some(ast_map::node_item(@{ - node: ast::item_class(_, type_parameters), - _ - }, _)) => { - - type_parameter_count = type_parameters.len(); - - let self_region = - bound_self_region(region_parameterized); - - raw_type = ty::mk_class(tcx, class_id, { - self_r: self_region, - self_ty: None, - tps: ty::ty_params_to_tys(tcx, type_parameters) - }); - } - _ => { - tcx.sess.span_bug(expr.span, - ~"resolve didn't map this to a class"); - } - } - } else { - let item_type = ty::lookup_item_type(tcx, class_id); - type_parameter_count = (*item_type.bounds).len(); - region_parameterized = item_type.region_param; - raw_type = item_type.ty; - } - - // Generate the struct type. - let self_region = - fcx.region_var_if_parameterized(region_parameterized, - expr.span, - ty::re_scope(expr.id)); - let type_parameters = fcx.infcx().next_ty_vars(type_parameter_count); - let substitutions = { - self_r: self_region, - self_ty: None, - tps: type_parameters - }; - - let struct_type = ty::subst(tcx, &substitutions, raw_type); - - // Look up the class fields and build up a map. - let class_fields = ty::lookup_class_fields(tcx, class_id); - let class_field_map = HashMap(); - let mut fields_found = 0; - for class_fields.each |field| { - // XXX: Check visibility here. - class_field_map.insert(field.ident, (field.id, false)); - } - - // Typecheck each field. - for fields.each |field| { - match class_field_map.find(field.node.ident) { - None => { - tcx.sess.span_err( - field.span, - fmt!("structure has no field named field named `%s`", - tcx.sess.str_of(field.node.ident))); - } - Some((_, true)) => { - tcx.sess.span_err( - field.span, - fmt!("field `%s` specified more than once", - tcx.sess.str_of(field.node.ident))); - } - Some((field_id, false)) => { - let expected_field_type = - ty::lookup_field_type(tcx, class_id, field_id, - &substitutions); - bot |= check_expr(fcx, - field.node.expr, - Some(expected_field_type)); - fields_found += 1; - } - } - } - - match base_expr { - None => { - // Make sure the programmer specified all the fields. - assert fields_found <= class_fields.len(); - if fields_found < class_fields.len() { - let mut missing_fields = ~[]; - for class_fields.each |class_field| { - let name = class_field.ident; - let (_, seen) = class_field_map.get(name); - if !seen { - missing_fields.push( - ~"`" + tcx.sess.str_of(name) + ~"`"); - } - } - - tcx.sess.span_err(expr.span, - fmt!("missing field%s: %s", - if missing_fields.len() == 1 { - ~"" - } else { - ~"s" - }, - str::connect(missing_fields, - ~", "))); - } - } - Some(base_expr) => { - // Just check the base expression. - check_expr(fcx, base_expr, Some(struct_type)); - } - } - - // Write in the resulting type. - fcx.write_ty(id, struct_type); } ast::expr_field(base, field, tys) => { bot = check_field(fcx, expr, false, base, field, tys); diff --git a/src/rustc/middle/typeck/check/method.rs b/src/rustc/middle/typeck/check/method.rs index eaf1a45afa9c0..eb28d06fdea7b 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 (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}) - }); + + 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); + + 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 a5390d8f293af..5350f48065491 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 0000000000000..1608a332fe5a8 --- /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 0000000000000..a38a834fb7263 --- /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 0000000000000..4014cd2c62387 --- /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; +} +