diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 73d6741feea..5f0f3bc9593 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -1330,16 +1330,12 @@ fn find_vtable(tcx: ty::ctxt, ps: ¶m_substs, debug!("find_vtable_in_fn_ctxt(n_param=%u, n_bound=%u, ps=%?)", n_param, n_bound, param_substs_to_str(tcx, ps)); - let mut vtable_off = n_bound, i = 0u; // Vtables are stored in a flat array, finding the right one is // somewhat awkward - for vec::each(*ps.bounds) |bounds| { - if i >= n_param { break; } - for vec::each(**bounds) |bound| { - match *bound { ty::bound_trait(_) => vtable_off += 1u, _ => () } - } - i += 1u; - } + let first_n_bounds = ps.bounds.view(0, n_param); + let vtables_to_skip = + ty::count_traits_and_supertraits(tcx, first_n_bounds); + let vtable_off = vtables_to_skip + n_bound; ps.vtables.get()[vtable_off] } diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index 4633310e606..ba6ec2a44c9 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -243,17 +243,7 @@ fn trans_static_method_callee(bcx: block, // one we are interested in. let bound_index = { let trait_polyty = ty::lookup_item_type(bcx.tcx(), trait_id); - let mut index = 0; - for trait_polyty.bounds.each |param_bounds| { - for param_bounds.each |param_bound| { - match *param_bound { - ty::bound_trait(_) => { index += 1; } - ty::bound_copy | ty::bound_owned | - ty::bound_send | ty::bound_const => {} - } - } - } - index + ty::count_traits_and_supertraits(bcx.tcx(), *trait_polyty.bounds) }; let mname = if method_id.crate == ast::local_crate { diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index ad6ca83fe4e..d52f507e53e 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -204,6 +204,8 @@ export DerivedFieldInfo; export AutoAdjustment; export AutoRef; export AutoRefKind, AutoPtr, AutoBorrowVec, AutoBorrowFn; +export iter_bound_traits_and_supertraits; +export count_traits_and_supertraits; // Data types @@ -4530,6 +4532,64 @@ pure fn determine_inherited_purity(parent_purity: ast::purity, } } +// Iterate over a type parameter's bounded traits and any supertraits +// of those traits, ignoring kinds. +fn iter_bound_traits_and_supertraits(tcx: ctxt, + bounds: param_bounds, + f: &fn(t) -> bool) { + for bounds.each |bound| { + + let bound_trait_ty = match *bound { + ty::bound_trait(bound_t) => bound_t, + + ty::bound_copy | ty::bound_send | + ty::bound_const | ty::bound_owned => { + loop; // skip non-trait bounds + } + }; + + let mut worklist = ~[]; + + let init_trait_ty = bound_trait_ty; + + worklist.push(init_trait_ty); + + let mut i = 0; + while i < worklist.len() { + let init_trait_ty = worklist[i]; + i += 1; + + let init_trait_id = match ty_to_def_id(init_trait_ty) { + Some(id) => id, + None => tcx.sess.bug( + ~"trait type should have def_id") + }; + + // Add supertraits to worklist + let supertraits = trait_supertraits(tcx, + init_trait_id); + for supertraits.each |supertrait| { + worklist.push(supertrait.tpt.ty); + } + + if !f(init_trait_ty) { + return; + } + } + } +} + +fn count_traits_and_supertraits(tcx: ctxt, + boundses: &[param_bounds]) -> uint { + let mut total = 0; + for boundses.each |bounds| { + for iter_bound_traits_and_supertraits(tcx, *bounds) |_trait_ty| { + total += 1; + } + } + return total; +} + impl mt : cmp::Eq { pure fn eq(&self, other: &mt) -> bool { (*self).ty == (*other).ty && (*self).mutbl == (*other).mutbl diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 78cd6777257..b4f5441c981 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -241,7 +241,7 @@ impl LookupContext { loop { match get(self_ty).sty { ty_param(p) => { - self.push_inherent_candidates_from_param(p); + self.push_inherent_candidates_from_param(self_ty, p); } ty_trait(did, ref substs, vstore) => { self.push_inherent_candidates_from_trait( @@ -305,7 +305,8 @@ impl LookupContext { } } - fn push_inherent_candidates_from_param(&self, param_ty: param_ty) { + fn push_inherent_candidates_from_param(&self, rcvr_ty: ty::t, + param_ty: param_ty) { debug!("push_inherent_candidates_from_param(param_ty=%?)", param_ty); let _indenter = indenter(); @@ -313,8 +314,9 @@ impl LookupContext { let tcx = self.tcx(); let mut next_bound_idx = 0; // count only trait bounds let bounds = tcx.ty_param_bounds.get(param_ty.def_id.node); + for vec::each(*bounds) |bound| { - let bound_t = match *bound { + let bound_trait_ty = match *bound { ty::bound_trait(bound_t) => bound_t, ty::bound_copy | ty::bound_send | @@ -323,56 +325,64 @@ impl LookupContext { } }; - let this_bound_idx = next_bound_idx; - next_bound_idx += 1; - let (trait_id, bound_substs) = match ty::get(bound_t).sty { - ty::ty_trait(i, substs, _) => (i, substs), + let bound_substs = match ty::get(bound_trait_ty).sty { + ty::ty_trait(_, substs, _) => substs, _ => { self.bug(fmt!("add_candidates_from_param: \ non-trait bound %s", - self.ty_to_str(bound_t))); + self.ty_to_str(bound_trait_ty))); } }; + // Loop over the trait and all of its supertraits. - let worklist = dvec::DVec(); - worklist.push((trait_id, move bound_substs)); + let mut worklist = ~[]; + + let init_trait_ty = bound_trait_ty; + let init_substs = bound_substs; + + // 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 init_substs = {self_ty: Some(rcvr_ty), ..init_substs}; + + worklist.push((init_trait_ty, init_substs)); let mut i = 0; while i < worklist.len() { - let (trait_id, bound_substs) = worklist[i]; + let (init_trait_ty, init_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}; + let init_trait_id = ty::ty_to_def_id(init_trait_ty).get(); // 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 supertraits = ty::trait_supertraits(tcx, + init_trait_id); + for supertraits.each |supertrait| { + debug!("adding supertrait: %?", + supertrait.def_id); let new_substs = ty::subst_substs( tcx, - &instantiated_trait_ref.tpt.substs, - &rcvr_substs); + &supertrait.tpt.substs, + &init_substs); - worklist.push( - (instantiated_trait_ref.def_id, new_substs)); + // Again replacing the self type + let new_substs = {self_ty: Some(rcvr_ty), ..new_substs}; + + worklist.push((supertrait.tpt.ty, new_substs)); } - let trait_methods = ty::trait_methods(tcx, trait_id); + + let this_bound_idx = next_bound_idx; + next_bound_idx += 1; + + let trait_methods = ty::trait_methods(tcx, init_trait_id); let pos = { // FIXME #3453 can't use trait_methods.position match vec::position(*trait_methods, @@ -381,6 +391,8 @@ impl LookupContext { { Some(pos) => pos, None => { + debug!("trait doesn't contain method: %?", + init_trait_id); loop; // check next trait or bound } } @@ -389,18 +401,21 @@ impl LookupContext { let (rcvr_ty, rcvr_substs) = self.create_rcvr_ty_and_substs_for_method( - method.self_ty, rcvr_ty, move rcvr_substs); + method.self_ty, rcvr_ty, move init_substs); - self.inherent_candidates.push(Candidate { + let cand = 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, + origin: method_param({trait_id:init_trait_id, method_num:pos, param_num:param_ty.idx, bound_num:this_bound_idx}) - }); + }; + + debug!("pushing inherent candidate for param: %?", cand); + self.inherent_candidates.push(cand); } } } @@ -775,6 +790,8 @@ impl LookupContext { let relevant_candidates = candidates.filter_to_vec(|c| self.is_relevant(self_ty, &c)); + let relevant_candidates = self.merge_candidates(relevant_candidates); + if relevant_candidates.len() == 0 { return None; } @@ -791,6 +808,52 @@ impl LookupContext { Some(self.confirm_candidate(self_ty, &relevant_candidates[0])) } + fn merge_candidates(&self, candidates: &[Candidate]) -> ~[Candidate] { + let mut merged = ~[]; + let mut i = 0; + while i < candidates.len() { + let candidate_a = candidates[i]; + + let mut skip = false; + + let mut j = i + 1; + while j < candidates.len() { + let candidate_b = candidates[j]; + debug!("attempting to merge %? and %?", + candidate_a, candidate_b); + let candidates_same = match (&candidate_a.origin, + &candidate_b.origin) { + (&method_param(p1), &method_param(p2)) => { + let same_trait = p1.trait_id == p2.trait_id; + let same_method = p1.method_num == p2.method_num; + let same_param = p1.param_num == p2.param_num; + // The bound number may be different because + // multiple bounds may lead to the same trait + // impl + same_trait && same_method && same_param + } + _ => false + }; + if candidates_same { + skip = true; + break; + } + j += 1; + } + + i += 1; + + if skip { + // There are more than one of these and we need only one + loop; + } else { + merged.push(candidate_a); + } + } + + return merged; + } + fn confirm_candidate(&self, self_ty: ty::t, candidate: &Candidate) diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index 674bb8d6f85..99999ca2ae1 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -67,28 +67,43 @@ fn lookup_vtables(vcx: &VtableContext, let tcx = vcx.tcx(); let mut result = ~[], i = 0u; for substs.tps.each |ty| { - for vec::each(*bounds[i]) |bound| { - match *bound { - ty::bound_trait(i_ty) => { - let i_ty = ty::subst(tcx, substs, i_ty); - match lookup_vtable_covariant(vcx, location_info, *ty, i_ty, - allow_unsafe, is_early) { - Some(vtable) => result.push(vtable), - None => { - vcx.tcx().sess.span_fatal( - location_info.span, - fmt!("failed to find an implementation of trait \ - %s for %s", - ty_to_str(vcx.tcx(), i_ty), - ty_to_str(vcx.tcx(), *ty))); - } + for ty::iter_bound_traits_and_supertraits( + tcx, bounds[i]) |trait_ty| { + + debug!("about to subst: %?, %?", + ty_to_str(tcx, trait_ty), + ty::substs_to_str(tcx, substs)); + + let new_substs = {self_ty: Some(*ty), ..*substs}; + let trait_ty = ty::subst(tcx, &new_substs, trait_ty); + + debug!("after subst: %?", + ty_to_str(tcx, trait_ty)); + + match lookup_vtable(vcx, location_info, *ty, trait_ty, + allow_unsafe, is_early) { + Some(vtable) => result.push(vtable), + None => { + vcx.tcx().sess.span_fatal( + location_info.span, + fmt!("failed to find an implementation of \ + trait %s for %s", + ty_to_str(vcx.tcx(), trait_ty), + ty_to_str(vcx.tcx(), *ty))); } - } - _ => () } } i += 1u; } + debug!("lookup_vtables result(\ + location_info=%?, + # bounds=%?, \ + substs=%s, \ + result=%?", + location_info, + bounds.len(), + ty::substs_to_str(vcx.tcx(), substs), + result); @result } @@ -112,32 +127,15 @@ fn relate_trait_tys(vcx: &VtableContext, location_info: &LocationInfo, } // Look up the vtable to use when treating an item of type `t` as if it has -// type `trait_ty`. This does allow subtraits. -fn lookup_vtable_covariant(vcx: &VtableContext, - location_info: &LocationInfo, - ty: ty::t, - trait_ty: ty::t, - allow_unsafe: bool, - is_early: bool) - -> Option { - debug!("lookup_vtable_covariant(ty: %s, trait_ty=%s)", - vcx.infcx.ty_to_str(ty), - vcx.infcx.ty_to_str(trait_ty)); - - lookup_vtable_invariant(vcx, location_info, ty, trait_ty, - allow_unsafe, is_early) -} - -// Look up the vtable to use when treating an item of type `t` as if it has -// type `trait_ty`. This does not allow subtraits. -fn lookup_vtable_invariant(vcx: &VtableContext, - location_info: &LocationInfo, - ty: ty::t, - trait_ty: ty::t, - allow_unsafe: bool, - is_early: bool) - -> Option { - debug!("lookup_vtable_invariant(ty=%s, trait_ty=%s)", +// type `trait_ty` +fn lookup_vtable(vcx: &VtableContext, + location_info: &LocationInfo, + ty: ty::t, + trait_ty: ty::t, + allow_unsafe: bool, + is_early: bool) + -> Option { + debug!("lookup_vtable(ty=%s, trait_ty=%s)", vcx.infcx.ty_to_str(ty), vcx.infcx.ty_to_str(trait_ty)); let _i = indenter(); @@ -145,7 +143,7 @@ fn lookup_vtable_invariant(vcx: &VtableContext, let (trait_id, trait_substs, trait_vstore) = match ty::get(trait_ty).sty { ty::ty_trait(did, substs, vstore) => (did, substs, vstore), _ => tcx.sess.impossible_case(location_info.span, - "lookup_vtable_invariant: \ + "lookup_vtable: \ don't know how to handle a non-trait") }; let ty = match fixup_ty(vcx, location_info, ty, is_early) { @@ -163,32 +161,35 @@ fn lookup_vtable_invariant(vcx: &VtableContext, match ty::get(ty).sty { ty::ty_param({idx: n, def_id: did}) => { let mut n_bound = 0; - for vec::each(*tcx.ty_param_bounds.get(did.node)) |bound| { - match *bound { - ty::bound_send | ty::bound_copy | ty::bound_const | - ty::bound_owned => { - /* ignore */ - } - ty::bound_trait(ity) => { - match ty::get(ity).sty { - ty::ty_trait(idid, _, _) => { - if trait_id == idid { - debug!("(checking vtable) @0 relating \ - ty to trait ty with did %?", - idid); - relate_trait_tys(vcx, location_info, - trait_ty, ity); - return Some(vtable_param(n, n_bound)); - } - } - _ => tcx.sess.impossible_case( - location_info.span, - "lookup_vtable_invariant: in loop, \ - don't know how to handle a non-trait ity") + let bounds = tcx.ty_param_bounds.get(did.node); + for ty::iter_bound_traits_and_supertraits( + tcx, bounds) |ity| { + debug!("checking bounds trait %?", + vcx.infcx.ty_to_str(ity)); + + match ty::get(ity).sty { + ty::ty_trait(idid, _, _) => { + if trait_id == idid { + debug!("(checking vtable) @0 \ + relating ty to trait \ + ty with did %?", + idid); + relate_trait_tys(vcx, location_info, + trait_ty, ity); + let vtable = vtable_param(n, n_bound); + debug!("found param vtable: %?", + vtable); + return Some(vtable); } - n_bound += 1u; } + _ => tcx.sess.impossible_case( + location_info.span, + "lookup_vtable: in loop, \ + don't know how to handle a \ + non-trait ity") } + + n_bound += 1; } } @@ -283,8 +284,6 @@ fn lookup_vtable_invariant(vcx: &VtableContext, // impl. let {substs: substs, ty: for_ty} = impl_self_ty(vcx, location_info, im.did); - let im_bs = ty::lookup_item_type(tcx, - im.did).bounds; match infer::mk_subty(vcx.infcx, false, location_info.span, @@ -369,6 +368,8 @@ fn lookup_vtable_invariant(vcx: &VtableContext, // to. connect_trait_tps requires these // lists of types to unify pairwise. + let im_bs = ty::lookup_item_type(tcx, + im.did).bounds; connect_trait_tps(vcx, location_info, substs_f.tps, @@ -493,8 +494,9 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) { Some(ref substs) => { let def = cx.tcx.def_map.get(ex.id); let did = ast_util::def_id_of_def(def); - debug!("early resolve expr: def %?", def); let item_ty = ty::lookup_item_type(cx.tcx, did); + debug!("early resolve expr: def %? %?, %?, %?", ex.id, did, def, + fcx.infcx().ty_to_str(item_ty.ty)); if has_trait_bounds(*item_ty.bounds) { for item_ty.bounds.each |bounds| { debug!("early_resolve_expr: looking up vtables for bound \ @@ -527,6 +529,7 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) { ast::expr_field(_, _, _) => ex.id, _ => ex.callee_id }; + let substs = fcx.node_ty_substs(callee_id); let vcx = VtableContext { ccx: fcx.ccx, infcx: fcx.infcx() }; let vtbls = lookup_vtables(&vcx, &location_info_for_expr(ex), @@ -551,12 +554,12 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) { let ty = fcx.expr_ty(src); let vcx = VtableContext { ccx: fcx.ccx, infcx: fcx.infcx() }; let vtable_opt = - lookup_vtable_invariant(&vcx, - &location_info_for_expr(ex), - ty, - target_ty, - true, - is_early); + lookup_vtable(&vcx, + &location_info_for_expr(ex), + ty, + target_ty, + true, + is_early); match vtable_opt { None => { // Try the new-style boxed trait; "@int as @Trait". @@ -577,12 +580,12 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) { let location_info = &location_info_for_expr(ex); let vtable_opt = - lookup_vtable_invariant(&vcx, - location_info, - mt.ty, - target_ty, - true, - is_early); + lookup_vtable(&vcx, + location_info, + mt.ty, + target_ty, + true, + is_early); match vtable_opt { Some(vtable) => { // Map this expression to that diff --git a/src/test/auxiliary/trait_inheritance_auto_xc_2_aux.rs b/src/test/auxiliary/trait_inheritance_auto_xc_2_aux.rs new file mode 100644 index 00000000000..99f4119b1cc --- /dev/null +++ b/src/test/auxiliary/trait_inheritance_auto_xc_2_aux.rs @@ -0,0 +1,11 @@ +pub trait Foo { fn f() -> int; } +pub trait Bar { fn g() -> int; } +pub trait Baz { fn h() -> int; } + +pub struct A { x: int } + +impl A : Foo { fn f() -> int { 10 } } +impl A : Bar { fn g() -> int { 20 } } +impl A : Baz { fn h() -> int { 30 } } + + diff --git a/src/test/auxiliary/trait_inheritance_auto_xc_aux.rs b/src/test/auxiliary/trait_inheritance_auto_xc_aux.rs new file mode 100644 index 00000000000..8fe7b72f8e7 --- /dev/null +++ b/src/test/auxiliary/trait_inheritance_auto_xc_aux.rs @@ -0,0 +1,7 @@ +trait Foo { fn f() -> int; } +trait Bar { fn g() -> int; } +trait Baz { fn h() -> int; } + +trait Quux: Foo, Bar, Baz { } + +impl T: Quux { } diff --git a/src/test/auxiliary/trait_inheritance_cross_trait_call_xc_aux.rs b/src/test/auxiliary/trait_inheritance_cross_trait_call_xc_aux.rs new file mode 100644 index 00000000000..00029d11bf4 --- /dev/null +++ b/src/test/auxiliary/trait_inheritance_cross_trait_call_xc_aux.rs @@ -0,0 +1,12 @@ + +pub trait Foo { + fn f() -> int; +} + +pub struct A { + x: int +} + +impl A : Foo { + fn f() -> int { 10 } +} diff --git a/src/test/auxiliary/trait_inheritance_overloading_xc.rs b/src/test/auxiliary/trait_inheritance_overloading_xc.rs index 235a174c838..ceeee89de6a 100644 --- a/src/test/auxiliary/trait_inheritance_overloading_xc.rs +++ b/src/test/auxiliary/trait_inheritance_overloading_xc.rs @@ -1,9 +1,31 @@ -pub trait MyNum : Add, Sub, Mul { +use cmp::Eq; + +pub trait MyNum : Add, Sub, Mul, Eq { } -pub impl int : MyNum { - pure fn add(other: &int) -> int { self + *other } - pure fn sub(&self, other: &int) -> int { *self - *other } - pure fn mul(&self, other: &int) -> int { *self * *other } +pub struct MyInt { + val: int } +pub impl MyInt : Add { + pure fn add(other: &MyInt) -> MyInt { mi(self.val + other.val) } +} + +pub impl MyInt : Sub { + pure fn sub(&self, other: &MyInt) -> MyInt { mi(self.val - other.val) } +} + +pub impl MyInt : Mul { + pure fn mul(&self, other: &MyInt) -> MyInt { mi(self.val * other.val) } +} + +pub impl MyInt : Eq { + pure fn eq(&self, other: &MyInt) -> bool { self.val == other.val } + + pure fn ne(&self, other: &MyInt) -> bool { !self.eq(other) } +} + +pub impl MyInt : MyNum; + +pure fn mi(v: int) -> MyInt { MyInt { val: v } } + diff --git a/src/test/compile-fail/trait-inheritance-missing-requirement.rs b/src/test/compile-fail/trait-inheritance-missing-requirement.rs new file mode 100644 index 00000000000..3105f72415a --- /dev/null +++ b/src/test/compile-fail/trait-inheritance-missing-requirement.rs @@ -0,0 +1,23 @@ +// xfail-test +// error-pattern: what + +trait Foo { + fn f(); +} + +trait Bar : Foo { + fn g(); +} + +struct A { + x: int +} + +// Can't implement Bar without an impl of Foo +impl A : Bar { + fn g() { } +} + +fn main() { +} + diff --git a/src/test/run-pass/trait-inheritance-auto-xc-2.rs b/src/test/run-pass/trait-inheritance-auto-xc-2.rs new file mode 100644 index 00000000000..4eac9710250 --- /dev/null +++ b/src/test/run-pass/trait-inheritance-auto-xc-2.rs @@ -0,0 +1,23 @@ +// xfail-fast +// aux-build:trait_inheritance_auto_xc_2_aux.rs + +extern mod aux(name = "trait_inheritance_auto_xc_2_aux"); + +// aux defines impls of Foo, Bar and Baz for A +use aux::{Foo, Bar, Baz, A}; + +// We want to extend all Foo, Bar, Bazes to Quuxes +pub trait Quux: Foo, Bar, Baz { } +impl T: Quux { } + +fn f(a: &T) { + assert a.f() == 10; + assert a.g() == 20; + assert a.h() == 30; +} + +fn main() { + let a = &A { x: 3 }; + f(a); +} + diff --git a/src/test/run-pass/trait-inheritance-auto-xc.rs b/src/test/run-pass/trait-inheritance-auto-xc.rs new file mode 100644 index 00000000000..4bc62fab51b --- /dev/null +++ b/src/test/run-pass/trait-inheritance-auto-xc.rs @@ -0,0 +1,24 @@ +// xfail-fast +// aux-build:trait_inheritance_auto_xc_aux.rs + +extern mod aux(name = "trait_inheritance_auto_xc_aux"); + +use aux::{Foo, Bar, Baz, Quux}; + +struct A { x: int } + +impl A : Foo { fn f() -> int { 10 } } +impl A : Bar { fn g() -> int { 20 } } +impl A : Baz { fn h() -> int { 30 } } + +fn f(a: &T) { + assert a.f() == 10; + assert a.g() == 20; + assert a.h() == 30; +} + +fn main() { + let a = &A { x: 3 }; + f(a); +} + diff --git a/src/test/run-pass/trait-inheritance-auto.rs b/src/test/run-pass/trait-inheritance-auto.rs new file mode 100644 index 00000000000..29de9f111dc --- /dev/null +++ b/src/test/run-pass/trait-inheritance-auto.rs @@ -0,0 +1,27 @@ +// Testing that this impl turns A into a Quux, because +// A is already a Foo Bar Baz +impl T: Quux { } + +trait Foo { fn f() -> int; } +trait Bar { fn g() -> int; } +trait Baz { fn h() -> int; } + +trait Quux: Foo, Bar, Baz { } + +struct A { x: int } + +impl A : Foo { fn f() -> int { 10 } } +impl A : Bar { fn g() -> int { 20 } } +impl A : Baz { fn h() -> int { 30 } } + +fn f(a: &T) { + assert a.f() == 10; + assert a.g() == 20; + assert a.h() == 30; +} + +fn main() { + let a = &A { x: 3 }; + f(a); +} + diff --git a/src/test/run-pass/trait-inheritance-call-bound-inherited.rs b/src/test/run-pass/trait-inheritance-call-bound-inherited.rs new file mode 100644 index 00000000000..dd3f53cfbdd --- /dev/null +++ b/src/test/run-pass/trait-inheritance-call-bound-inherited.rs @@ -0,0 +1,18 @@ +trait Foo { fn f() -> int; } +trait Bar : Foo { fn g() -> int; } + +struct A { x: int } + +impl A : Foo { fn f() -> int { 10 } } +impl A : Bar { fn g() -> int { 20 } } + +// Call a function on Foo, given a T: Bar +fn gg(a: &T) -> int { + a.f() +} + +fn main() { + let a = &A { x: 3 }; + assert gg(a) == 10; +} + diff --git a/src/test/run-pass/trait-inheritance-call-bound-inherited2.rs b/src/test/run-pass/trait-inheritance-call-bound-inherited2.rs new file mode 100644 index 00000000000..222d8e7291d --- /dev/null +++ b/src/test/run-pass/trait-inheritance-call-bound-inherited2.rs @@ -0,0 +1,21 @@ +trait Foo { fn f() -> int; } +trait Bar : Foo { fn g() -> int; } +trait Baz : Bar { fn h() -> int; } + +struct A { x: int } + +impl A : Foo { fn f() -> int { 10 } } +impl A : Bar { fn g() -> int { 20 } } +impl A : Baz { fn h() -> int { 30 } } + +// Call a function on Foo, given a T: Baz, +// which is inherited via Bar +fn gg(a: &T) -> int { + a.f() +} + +fn main() { + let a = &A { x: 3 }; + assert gg(a) == 10; +} + diff --git a/src/test/run-pass/trait-inheritance-cast-without-call-to-supertrait.rs b/src/test/run-pass/trait-inheritance-cast-without-call-to-supertrait.rs new file mode 100644 index 00000000000..6cb05516520 --- /dev/null +++ b/src/test/run-pass/trait-inheritance-cast-without-call-to-supertrait.rs @@ -0,0 +1,31 @@ +// Testing that we can cast to a subtrait and call subtrait +// methods. Not testing supertrait methods + +trait Foo { + fn f() -> int; +} + +trait Bar : Foo { + fn g() -> int; +} + +struct A { + x: int +} + +impl A : Foo { + fn f() -> int { 10 } +} + +impl A : Bar { + fn g() -> int { 20 } +} + +fn main() { + let a = &A { x: 3 }; + let afoo = a as &Foo; + let abar = a as &Bar; + assert afoo.f() == 10; + assert abar.g() == 20; +} + diff --git a/src/test/run-pass/trait-inheritance-cast.rs b/src/test/run-pass/trait-inheritance-cast.rs new file mode 100644 index 00000000000..1cd23f49733 --- /dev/null +++ b/src/test/run-pass/trait-inheritance-cast.rs @@ -0,0 +1,33 @@ +// xfail-test +// Testing that supertrait methods can be called on subtrait object types +// It's not clear yet that we want this + +trait Foo { + fn f() -> int; +} + +trait Bar : Foo { + fn g() -> int; +} + +struct A { + x: int +} + +impl A : Foo { + fn f() -> int { 10 } +} + +impl A : Bar { + fn g() -> int { 20 } +} + +fn main() { + let a = &A { x: 3 }; + let afoo = a as &Foo; + let abar = a as &Bar; + assert afoo.f() == 10; + assert abar.g() == 20; + assert abar.f() == 10; +} + diff --git a/src/test/run-pass/trait-inheritance-cross-trait-call-xc.rs b/src/test/run-pass/trait-inheritance-cross-trait-call-xc.rs new file mode 100644 index 00000000000..9f588ddb907 --- /dev/null +++ b/src/test/run-pass/trait-inheritance-cross-trait-call-xc.rs @@ -0,0 +1,18 @@ +// xfail-fast +// aux-build:trait_inheritance_cross_trait_call_xc_aux.rs + +extern mod aux(name = "trait_inheritance_cross_trait_call_xc_aux"); + +trait Bar : aux::Foo { + fn g() -> int; +} + +impl aux::A : Bar { + fn g() -> int { self.f() } +} + +fn main() { + let a = &aux::A { x: 3 }; + assert a.g() == 10; +} + diff --git a/src/test/run-pass/trait-inheritance-cross-trait-call.rs b/src/test/run-pass/trait-inheritance-cross-trait-call.rs new file mode 100644 index 00000000000..a96bfb41f44 --- /dev/null +++ b/src/test/run-pass/trait-inheritance-cross-trait-call.rs @@ -0,0 +1,17 @@ +trait Foo { fn f() -> int; } +trait Bar : Foo { fn g() -> int; } + +struct A { x: int } + +impl A : Foo { fn f() -> int { 10 } } + +impl A : Bar { + // Testing that this impl can call the impl of Foo + fn g() -> int { self.f() } +} + +fn main() { + let a = &A { x: 3 }; + assert a.g() == 10; +} + diff --git a/src/test/run-pass/trait-inheritance-diamond.rs b/src/test/run-pass/trait-inheritance-diamond.rs new file mode 100644 index 00000000000..4c18d92a8c0 --- /dev/null +++ b/src/test/run-pass/trait-inheritance-diamond.rs @@ -0,0 +1,25 @@ +// B and C both require A, so D does as well, twice, but that's just fine + +trait A { fn a(&self) -> int; } +trait B: A { fn b(&self) -> int; } +trait C: A { fn c(&self) -> int; } +trait D: B, C { fn d(&self) -> int; } + +struct S { bogus: () } + +impl S: A { fn a(&self) -> int { 10 } } +impl S: B { fn b(&self) -> int { 20 } } +impl S: C { fn c(&self) -> int { 30 } } +impl S: D { fn d(&self) -> int { 40 } } + +fn f(x: &T) { + assert x.a() == 10; + assert x.b() == 20; + assert x.c() == 30; + assert x.d() == 40; +} + +fn main() { + let value = &S { bogus: () }; + f(value); +} \ No newline at end of file diff --git a/src/test/run-pass/trait-inheritance-multiple-inheritors.rs b/src/test/run-pass/trait-inheritance-multiple-inheritors.rs new file mode 100644 index 00000000000..fdc68a31997 --- /dev/null +++ b/src/test/run-pass/trait-inheritance-multiple-inheritors.rs @@ -0,0 +1,20 @@ +trait A { fn a(&self) -> int; } +trait B: A { fn b(&self) -> int; } +trait C: A { fn c(&self) -> int; } + +struct S { bogus: () } + +impl S: A { fn a(&self) -> int { 10 } } +impl S: B { fn b(&self) -> int { 20 } } +impl S: C { fn c(&self) -> int { 30 } } + +// Both B and C inherit from A +fn f(x: &T) { + assert x.a() == 10; + assert x.b() == 20; + assert x.c() == 30; +} + +fn main() { + f(&S { bogus: () }) +} \ No newline at end of file diff --git a/src/test/run-pass/trait-inheritance-multiple-params.rs b/src/test/run-pass/trait-inheritance-multiple-params.rs new file mode 100644 index 00000000000..0a5330b68f8 --- /dev/null +++ b/src/test/run-pass/trait-inheritance-multiple-params.rs @@ -0,0 +1,23 @@ +trait A { fn a(&self) -> int; } +trait B: A { fn b(&self) -> int; } +trait C: A { fn c(&self) -> int; } + +struct S { bogus: () } + +impl S: A { fn a(&self) -> int { 10 } } +impl S: B { fn b(&self) -> int { 20 } } +impl S: C { fn c(&self) -> int { 30 } } + +// Multiple type params, multiple levels of inheritance +fn f(x: &X, y: &Y, z: &Z) { + assert x.a() == 10; + assert y.a() == 10; + assert y.b() == 20; + assert z.a() == 10; + assert z.c() == 30; +} + +fn main() { + let s = &S { bogus: () }; + f(s, s, s); +} \ No newline at end of file diff --git a/src/test/run-pass/trait-inheritance-num.rs b/src/test/run-pass/trait-inheritance-num.rs new file mode 100644 index 00000000000..2266b67c234 --- /dev/null +++ b/src/test/run-pass/trait-inheritance-num.rs @@ -0,0 +1,14 @@ +use cmp::{Eq, Ord}; +use num::from_int; + +extern mod std; +use std::cmp::FuzzyEq; + +pub trait NumExt: Num, Eq, Ord {} + +pub trait FloatExt: NumExt, FuzzyEq {} + +fn greater_than_one(n: &T) -> bool { *n > from_int(1) } +fn greater_than_one_float(n: &T) -> bool { *n > from_int(1) } + +fn main() {} diff --git a/src/test/run-pass/trait-inheritance-num0.rs b/src/test/run-pass/trait-inheritance-num0.rs new file mode 100644 index 00000000000..6c3b22894f5 --- /dev/null +++ b/src/test/run-pass/trait-inheritance-num0.rs @@ -0,0 +1,16 @@ +// Extending Num and using inherited static methods + +use num::from_int; + +trait Num { + static fn from_int(i: int) -> self; + fn gt(&self, other: &self) -> bool; +} + +pub trait NumExt: Num { } + +fn greater_than_one(n: &T) -> bool { + n.gt(&from_int(1)) +} + +fn main() {} diff --git a/src/test/run-pass/trait-inheritance-num1.rs b/src/test/run-pass/trait-inheritance-num1.rs new file mode 100644 index 00000000000..57487f072f3 --- /dev/null +++ b/src/test/run-pass/trait-inheritance-num1.rs @@ -0,0 +1,12 @@ +// Using the real Num from core + +use cmp::Ord; +use num::from_int; + +pub trait NumExt: Num, Ord { } + +fn greater_than_one(n: &T) -> bool { + *n > from_int(1) +} + +fn main() {} diff --git a/src/test/run-pass/trait-inheritance-num2.rs b/src/test/run-pass/trait-inheritance-num2.rs new file mode 100644 index 00000000000..5f51f943817 --- /dev/null +++ b/src/test/run-pass/trait-inheritance-num2.rs @@ -0,0 +1,96 @@ +// A more complex example of numeric extensions + +use cmp::{Eq, Ord}; +use num::from_int; + +extern mod std; +use std::cmp::FuzzyEq; + +pub trait TypeExt {} + + +pub impl u8: TypeExt {} +pub impl u16: TypeExt {} +pub impl u32: TypeExt {} +pub impl u64: TypeExt {} +pub impl uint: TypeExt {} + +pub impl i8: TypeExt {} +pub impl i16: TypeExt {} +pub impl i32: TypeExt {} +pub impl i64: TypeExt {} +pub impl int: TypeExt {} + +pub impl f32: TypeExt {} +pub impl f64: TypeExt {} +pub impl float: TypeExt {} + + +pub trait NumExt: TypeExt, Eq, Ord, Num {} + +pub impl u8: NumExt {} +pub impl u16: NumExt {} +pub impl u32: NumExt {} +pub impl u64: NumExt {} +pub impl uint: NumExt {} + +pub impl i8: NumExt {} +pub impl i16: NumExt {} +pub impl i32: NumExt {} +pub impl i64: NumExt {} +pub impl int: NumExt {} + +pub impl f32: NumExt {} +pub impl f64: NumExt {} +pub impl float: NumExt {} + + +pub trait UnSignedExt: NumExt {} + +pub impl u8: UnSignedExt {} +pub impl u16: UnSignedExt {} +pub impl u32: UnSignedExt {} +pub impl u64: UnSignedExt {} +pub impl uint: UnSignedExt {} + + +pub trait SignedExt: NumExt {} + +pub impl i8: SignedExt {} +pub impl i16: SignedExt {} +pub impl i32: SignedExt {} +pub impl i64: SignedExt {} +pub impl int: SignedExt {} + +pub impl f32: SignedExt {} +pub impl f64: SignedExt {} +pub impl float: SignedExt {} + + +pub trait IntegerExt: NumExt {} + +pub impl u8: IntegerExt {} +pub impl u16: IntegerExt {} +pub impl u32: IntegerExt {} +pub impl u64: IntegerExt {} +pub impl uint: IntegerExt {} + +pub impl i8: IntegerExt {} +pub impl i16: IntegerExt {} +pub impl i32: IntegerExt {} +pub impl i64: IntegerExt {} +pub impl int: IntegerExt {} + + +pub trait FloatExt: NumExt , FuzzyEq {} + +pub impl f32: FloatExt {} +pub impl f64: FloatExt {} +pub impl float: FloatExt {} + + +fn test_float_ext(n: T) { io::println(fmt!("%?", n < n)) } + +fn main() { + test_float_ext(1f32); +} \ No newline at end of file diff --git a/src/test/run-pass/trait-inheritance-num3.rs b/src/test/run-pass/trait-inheritance-num3.rs new file mode 100644 index 00000000000..656e6390662 --- /dev/null +++ b/src/test/run-pass/trait-inheritance-num3.rs @@ -0,0 +1,12 @@ +use cmp::{Eq, Ord}; +use num::from_int; + +pub trait NumExt: Eq, Ord, Num {} + +pub impl f32: NumExt {} + +fn num_eq_one(n: T) { io::println(fmt!("%?", n == from_int(1))) } + +fn main() { + num_eq_one(1f32); // you need to actually use the function to trigger the ICE +} \ No newline at end of file diff --git a/src/test/run-pass/trait-inheritance-num5.rs b/src/test/run-pass/trait-inheritance-num5.rs new file mode 100644 index 00000000000..fa30ceff8ad --- /dev/null +++ b/src/test/run-pass/trait-inheritance-num5.rs @@ -0,0 +1,15 @@ +use cmp::{Eq, Ord}; +use num::from_int; + +pub trait NumExt: Eq, Num {} + +pub impl f32: NumExt {} +pub impl int: NumExt {} + +fn num_eq_one() -> T { + from_int(1) +} + +fn main() { + num_eq_one::(); // you need to actually use the function to trigger the ICE +} diff --git a/src/test/run-pass/trait-inheritance-overloading-simple.rs b/src/test/run-pass/trait-inheritance-overloading-simple.rs new file mode 100644 index 00000000000..13867eed52f --- /dev/null +++ b/src/test/run-pass/trait-inheritance-overloading-simple.rs @@ -0,0 +1,25 @@ +use cmp::Eq; + +trait MyNum : Eq { } + +struct MyInt { val: int } + +impl MyInt : Eq { + pure fn eq(&self, other: &MyInt) -> bool { self.val == other.val } + pure fn ne(&self, other: &MyInt) -> bool { !self.eq(other) } +} + +impl MyInt : MyNum; + +fn f(x: T, y: T) -> bool { + return x == y; +} + +pure fn mi(v: int) -> MyInt { MyInt { val: v } } + +fn main() { + let (x, y, z) = (mi(3), mi(5), mi(3)); + assert x != y; + assert x == z; +} + diff --git a/src/test/run-pass/trait-inheritance-overloading-xc-exe.rs b/src/test/run-pass/trait-inheritance-overloading-xc-exe.rs index a38a834fb72..585ce63b389 100644 --- a/src/test/run-pass/trait-inheritance-overloading-xc-exe.rs +++ b/src/test/run-pass/trait-inheritance-overloading-xc-exe.rs @@ -2,18 +2,19 @@ // aux-build:trait_inheritance_overloading_xc.rs extern mod trait_inheritance_overloading_xc; -use trait_inheritance_overloading_xc::MyNum; +use trait_inheritance_overloading_xc::{MyNum, MyInt}; fn f(x: T, y: T) -> (T, T, T) { return (x + y, x - y, x * y); } +pure fn mi(v: int) -> MyInt { MyInt { val: v } } + fn main() { - let (x, y) = (3, 5); + let (x, y) = (mi(3), mi(5)); let (a, b, c) = f(x, y); - assert a == 8; - assert b == -2; - assert c == 15; + assert a == mi(8); + assert b == mi(-2); + assert c == mi(15); } - diff --git a/src/test/run-pass/trait-inheritance-overloading.rs b/src/test/run-pass/trait-inheritance-overloading.rs index f8bf9faa186..def37d1f950 100644 --- a/src/test/run-pass/trait-inheritance-overloading.rs +++ b/src/test/run-pass/trait-inheritance-overloading.rs @@ -1,21 +1,39 @@ -trait MyNum : Add, Sub, Mul { +use cmp::Eq; + +trait MyNum : Add, Sub, Mul, Eq { } + +struct MyInt { val: int } + +impl MyInt : Add { + pure fn add(other: &MyInt) -> MyInt { mi(self.val + other.val) } } -impl int : MyNum { - pure fn add(other: &int) -> int { self + *other } - pure fn sub(&self, other: &int) -> int { *self - *other } - pure fn mul(&self, other: &int) -> int { *self * *other } +impl MyInt : Sub { + pure fn sub(&self, other: &MyInt) -> MyInt { mi(self.val - other.val) } } +impl MyInt : Mul { + pure fn mul(&self, other: &MyInt) -> MyInt { mi(self.val * other.val) } +} + +impl MyInt : Eq { + pure fn eq(&self, other: &MyInt) -> bool { self.val == other.val } + pure fn ne(&self, other: &MyInt) -> bool { !self.eq(other) } +} + +impl MyInt : MyNum; + fn f(x: T, y: T) -> (T, T, T) { return (x + y, x - y, x * y); } +pure fn mi(v: int) -> MyInt { MyInt { val: v } } + fn main() { - let (x, y) = (3, 5); + let (x, y) = (mi(3), mi(5)); let (a, b, c) = f(x, y); - assert a == 8; - assert b == -2; - assert c == 15; + assert a == mi(8); + assert b == mi(-2); + assert c == mi(15); } diff --git a/src/test/run-pass/trait-inheritance-simple.rs b/src/test/run-pass/trait-inheritance-simple.rs index fcd4cf1de6b..9725b18ca0f 100644 --- a/src/test/run-pass/trait-inheritance-simple.rs +++ b/src/test/run-pass/trait-inheritance-simple.rs @@ -1,26 +1,22 @@ -trait Foo { - fn f(); +trait Foo { fn f() -> int; } +trait Bar : Foo { fn g() -> int; } + +struct A { x: int } + +impl A : Foo { fn f() -> int { 10 } } +impl A : Bar { fn g() -> int { 20 } } + +fn ff(a: &T) -> int { + a.f() } -trait Bar : Foo { - fn g(); -} - -struct A { - x: int -} - -impl A : Bar { - fn g() { io::println("in g"); } - fn f() { io::println("in f"); } -} - -fn h(a: &T) { - a.f(); +fn gg(a: &T) -> int { + a.g() } fn main() { - let a = A { x: 3 }; - h(&a); + let a = &A { x: 3 }; + assert ff(a) == 10; + assert gg(a) == 20; } diff --git a/src/test/run-pass/trait-inheritance-static.rs b/src/test/run-pass/trait-inheritance-static.rs new file mode 100644 index 00000000000..a3788d8647b --- /dev/null +++ b/src/test/run-pass/trait-inheritance-static.rs @@ -0,0 +1,24 @@ +trait MyNum { + static fn from_int(int) -> self; +} + +pub trait NumExt: MyNum { } + +struct S { v: int } + +impl S: MyNum { + static fn from_int(i: int) -> S { + S { + v: i + } + } +} + +impl S: NumExt { } + +fn greater_than_one() -> T { from_int(1) } + +fn main() { + let v: S = greater_than_one(); + assert v.v == 1; +} diff --git a/src/test/run-pass/trait-inheritance-static2.rs b/src/test/run-pass/trait-inheritance-static2.rs new file mode 100644 index 00000000000..e6f3f1fb991 --- /dev/null +++ b/src/test/run-pass/trait-inheritance-static2.rs @@ -0,0 +1,28 @@ +trait MyEq { } + +trait MyNum { + static fn from_int(int) -> self; +} + +pub trait NumExt: MyEq, MyNum { } + +struct S { v: int } + +impl S: MyEq { } + +impl S: MyNum { + static fn from_int(i: int) -> S { + S { + v: i + } + } +} + +impl S: NumExt { } + +fn greater_than_one() -> T { from_int(1) } + +fn main() { + let v: S = greater_than_one(); + assert v.v == 1; +} diff --git a/src/test/run-pass/trait-inheritance-subst.rs b/src/test/run-pass/trait-inheritance-subst.rs new file mode 100644 index 00000000000..3fdf96b404e --- /dev/null +++ b/src/test/run-pass/trait-inheritance-subst.rs @@ -0,0 +1,26 @@ +pub trait Add { + pure fn add(rhs: &RHS) -> Result; +} + +trait MyNum : Add { } + +struct MyInt { val: int } + +impl MyInt : Add { + pure fn add(other: &MyInt) -> MyInt { mi(self.val + other.val) } +} + +impl MyInt : MyNum; + +fn f(x: T, y: T) -> T { + return x.add(&y); +} + +pure fn mi(v: int) -> MyInt { MyInt { val: v } } + +fn main() { + let (x, y) = (mi(3), mi(5)); + let z = f(x, y); + assert z.val == 8 +} + diff --git a/src/test/run-pass/trait-inheritance-subst2.rs b/src/test/run-pass/trait-inheritance-subst2.rs new file mode 100644 index 00000000000..343c567ab97 --- /dev/null +++ b/src/test/run-pass/trait-inheritance-subst2.rs @@ -0,0 +1,36 @@ +trait Panda { + fn chomp(bamboo: &T) -> T; +} + +trait Add: Panda { + fn add(rhs: &RHS) -> Result; +} + +trait MyNum : Add { } + +struct MyInt { val: int } + +impl MyInt : Panda { + fn chomp(bamboo: &MyInt) -> MyInt { + mi(self.val + bamboo.val) + } +} + +impl MyInt : Add { + fn add(other: &MyInt) -> MyInt { self.chomp(other) } +} + +impl MyInt : MyNum; + +fn f(x: T, y: T) -> T { + return x.add(&y).chomp(&y); +} + +fn mi(v: int) -> MyInt { MyInt { val: v } } + +fn main() { + let (x, y) = (mi(3), mi(5)); + let z = f(x, y); + assert z.val == 13; +} + diff --git a/src/test/run-pass/trait-inheritance-visibility.rs b/src/test/run-pass/trait-inheritance-visibility.rs new file mode 100644 index 00000000000..1fbe35328b6 --- /dev/null +++ b/src/test/run-pass/trait-inheritance-visibility.rs @@ -0,0 +1,18 @@ +mod traits { + pub trait Foo { fn f() -> int; } + + impl int: Foo { fn f() -> int { 10 } } +} + +trait Quux: traits::Foo { } +impl T: Quux { } + +// Foo is not in scope but because Quux is we can still access +// Foo's methods on a Quux bound typaram +fn f(x: &T) { + assert x.f() == 10; +} + +fn main() { + f(&0) +} \ No newline at end of file diff --git a/src/test/run-pass/trait-inheritance2.rs b/src/test/run-pass/trait-inheritance2.rs new file mode 100644 index 00000000000..7d4c2cacfea --- /dev/null +++ b/src/test/run-pass/trait-inheritance2.rs @@ -0,0 +1,24 @@ +trait Foo { fn f() -> int; } +trait Bar { fn g() -> int; } +trait Baz { fn h() -> int; } + +trait Quux: Foo, Bar, Baz { } + +struct A { x: int } + +impl A : Foo { fn f() -> int { 10 } } +impl A : Bar { fn g() -> int { 20 } } +impl A : Baz { fn h() -> int { 30 } } +impl A : Quux; + +fn f(a: &T) { + assert a.f() == 10; + assert a.g() == 20; + assert a.h() == 30; +} + +fn main() { + let a = &A { x: 3 }; + f(a); +} +