Implement trait inheritance for bounded type parameters
This commit is contained in:
parent
daa89e0861
commit
78ee821154
38 changed files with 1004 additions and 176 deletions
|
@ -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=%?)",
|
debug!("find_vtable_in_fn_ctxt(n_param=%u, n_bound=%u, ps=%?)",
|
||||||
n_param, n_bound, param_substs_to_str(tcx, 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
|
// Vtables are stored in a flat array, finding the right one is
|
||||||
// somewhat awkward
|
// somewhat awkward
|
||||||
for vec::each(*ps.bounds) |bounds| {
|
let first_n_bounds = ps.bounds.view(0, n_param);
|
||||||
if i >= n_param { break; }
|
let vtables_to_skip =
|
||||||
for vec::each(**bounds) |bound| {
|
ty::count_traits_and_supertraits(tcx, first_n_bounds);
|
||||||
match *bound { ty::bound_trait(_) => vtable_off += 1u, _ => () }
|
let vtable_off = vtables_to_skip + n_bound;
|
||||||
}
|
|
||||||
i += 1u;
|
|
||||||
}
|
|
||||||
ps.vtables.get()[vtable_off]
|
ps.vtables.get()[vtable_off]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -243,17 +243,7 @@ fn trans_static_method_callee(bcx: block,
|
||||||
// one we are interested in.
|
// one we are interested in.
|
||||||
let bound_index = {
|
let bound_index = {
|
||||||
let trait_polyty = ty::lookup_item_type(bcx.tcx(), trait_id);
|
let trait_polyty = ty::lookup_item_type(bcx.tcx(), trait_id);
|
||||||
let mut index = 0;
|
ty::count_traits_and_supertraits(bcx.tcx(), *trait_polyty.bounds)
|
||||||
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
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mname = if method_id.crate == ast::local_crate {
|
let mname = if method_id.crate == ast::local_crate {
|
||||||
|
|
|
@ -204,6 +204,8 @@ export DerivedFieldInfo;
|
||||||
export AutoAdjustment;
|
export AutoAdjustment;
|
||||||
export AutoRef;
|
export AutoRef;
|
||||||
export AutoRefKind, AutoPtr, AutoBorrowVec, AutoBorrowFn;
|
export AutoRefKind, AutoPtr, AutoBorrowVec, AutoBorrowFn;
|
||||||
|
export iter_bound_traits_and_supertraits;
|
||||||
|
export count_traits_and_supertraits;
|
||||||
|
|
||||||
// Data types
|
// 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 {
|
impl mt : cmp::Eq {
|
||||||
pure fn eq(&self, other: &mt) -> bool {
|
pure fn eq(&self, other: &mt) -> bool {
|
||||||
(*self).ty == (*other).ty && (*self).mutbl == (*other).mutbl
|
(*self).ty == (*other).ty && (*self).mutbl == (*other).mutbl
|
||||||
|
|
|
@ -241,7 +241,7 @@ impl LookupContext {
|
||||||
loop {
|
loop {
|
||||||
match get(self_ty).sty {
|
match get(self_ty).sty {
|
||||||
ty_param(p) => {
|
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) => {
|
ty_trait(did, ref substs, vstore) => {
|
||||||
self.push_inherent_candidates_from_trait(
|
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=%?)",
|
debug!("push_inherent_candidates_from_param(param_ty=%?)",
|
||||||
param_ty);
|
param_ty);
|
||||||
let _indenter = indenter();
|
let _indenter = indenter();
|
||||||
|
@ -313,8 +314,9 @@ impl LookupContext {
|
||||||
let tcx = self.tcx();
|
let tcx = self.tcx();
|
||||||
let mut next_bound_idx = 0; // count only trait bounds
|
let mut next_bound_idx = 0; // count only trait bounds
|
||||||
let bounds = tcx.ty_param_bounds.get(param_ty.def_id.node);
|
let bounds = tcx.ty_param_bounds.get(param_ty.def_id.node);
|
||||||
|
|
||||||
for vec::each(*bounds) |bound| {
|
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_trait(bound_t) => bound_t,
|
||||||
|
|
||||||
ty::bound_copy | ty::bound_send |
|
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 {
|
let bound_substs = match ty::get(bound_trait_ty).sty {
|
||||||
ty::ty_trait(i, substs, _) => (i, substs),
|
ty::ty_trait(_, substs, _) => substs,
|
||||||
_ => {
|
_ => {
|
||||||
self.bug(fmt!("add_candidates_from_param: \
|
self.bug(fmt!("add_candidates_from_param: \
|
||||||
non-trait bound %s",
|
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.
|
// Loop over the trait and all of its supertraits.
|
||||||
let worklist = dvec::DVec();
|
let mut worklist = ~[];
|
||||||
worklist.push((trait_id, move bound_substs));
|
|
||||||
|
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;
|
let mut i = 0;
|
||||||
while i < worklist.len() {
|
while i < worklist.len() {
|
||||||
let (trait_id, bound_substs) = worklist[i];
|
let (init_trait_ty, init_substs) = worklist[i];
|
||||||
i += 1;
|
i += 1;
|
||||||
|
|
||||||
// Replace any appearance of `self` with the type of the
|
let init_trait_id = ty::ty_to_def_id(init_trait_ty).get();
|
||||||
// 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.
|
// Add all the supertraits of this trait to the worklist.
|
||||||
debug!("finding supertraits for %d:%d", trait_id.crate,
|
let supertraits = ty::trait_supertraits(tcx,
|
||||||
trait_id.node);
|
init_trait_id);
|
||||||
let instantiated_trait_refs = ty::trait_supertraits(
|
for supertraits.each |supertrait| {
|
||||||
tcx, trait_id);
|
debug!("adding supertrait: %?",
|
||||||
for instantiated_trait_refs.each |instantiated_trait_ref| {
|
supertrait.def_id);
|
||||||
debug!("adding supertrait");
|
|
||||||
|
|
||||||
let new_substs = ty::subst_substs(
|
let new_substs = ty::subst_substs(
|
||||||
tcx,
|
tcx,
|
||||||
&instantiated_trait_ref.tpt.substs,
|
&supertrait.tpt.substs,
|
||||||
&rcvr_substs);
|
&init_substs);
|
||||||
|
|
||||||
worklist.push(
|
// Again replacing the self type
|
||||||
(instantiated_trait_ref.def_id, new_substs));
|
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 = {
|
let pos = {
|
||||||
// FIXME #3453 can't use trait_methods.position
|
// FIXME #3453 can't use trait_methods.position
|
||||||
match vec::position(*trait_methods,
|
match vec::position(*trait_methods,
|
||||||
|
@ -381,6 +391,8 @@ impl LookupContext {
|
||||||
{
|
{
|
||||||
Some(pos) => pos,
|
Some(pos) => pos,
|
||||||
None => {
|
None => {
|
||||||
|
debug!("trait doesn't contain method: %?",
|
||||||
|
init_trait_id);
|
||||||
loop; // check next trait or bound
|
loop; // check next trait or bound
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -389,18 +401,21 @@ impl LookupContext {
|
||||||
|
|
||||||
let (rcvr_ty, rcvr_substs) =
|
let (rcvr_ty, rcvr_substs) =
|
||||||
self.create_rcvr_ty_and_substs_for_method(
|
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_ty: rcvr_ty,
|
||||||
rcvr_substs: rcvr_substs,
|
rcvr_substs: rcvr_substs,
|
||||||
num_method_tps: method.tps.len(),
|
num_method_tps: method.tps.len(),
|
||||||
self_mode: get_mode_from_self_type(method.self_ty),
|
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,
|
method_num:pos,
|
||||||
param_num:param_ty.idx,
|
param_num:param_ty.idx,
|
||||||
bound_num:this_bound_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 =
|
let relevant_candidates =
|
||||||
candidates.filter_to_vec(|c| self.is_relevant(self_ty, &c));
|
candidates.filter_to_vec(|c| self.is_relevant(self_ty, &c));
|
||||||
|
|
||||||
|
let relevant_candidates = self.merge_candidates(relevant_candidates);
|
||||||
|
|
||||||
if relevant_candidates.len() == 0 {
|
if relevant_candidates.len() == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -791,6 +808,52 @@ impl LookupContext {
|
||||||
Some(self.confirm_candidate(self_ty, &relevant_candidates[0]))
|
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,
|
fn confirm_candidate(&self,
|
||||||
self_ty: ty::t,
|
self_ty: ty::t,
|
||||||
candidate: &Candidate)
|
candidate: &Candidate)
|
||||||
|
|
|
@ -67,28 +67,43 @@ fn lookup_vtables(vcx: &VtableContext,
|
||||||
let tcx = vcx.tcx();
|
let tcx = vcx.tcx();
|
||||||
let mut result = ~[], i = 0u;
|
let mut result = ~[], i = 0u;
|
||||||
for substs.tps.each |ty| {
|
for substs.tps.each |ty| {
|
||||||
for vec::each(*bounds[i]) |bound| {
|
for ty::iter_bound_traits_and_supertraits(
|
||||||
match *bound {
|
tcx, bounds[i]) |trait_ty| {
|
||||||
ty::bound_trait(i_ty) => {
|
|
||||||
let i_ty = ty::subst(tcx, substs, i_ty);
|
debug!("about to subst: %?, %?",
|
||||||
match lookup_vtable_covariant(vcx, location_info, *ty, i_ty,
|
ty_to_str(tcx, trait_ty),
|
||||||
allow_unsafe, is_early) {
|
ty::substs_to_str(tcx, substs));
|
||||||
Some(vtable) => result.push(vtable),
|
|
||||||
None => {
|
let new_substs = {self_ty: Some(*ty), ..*substs};
|
||||||
vcx.tcx().sess.span_fatal(
|
let trait_ty = ty::subst(tcx, &new_substs, trait_ty);
|
||||||
location_info.span,
|
|
||||||
fmt!("failed to find an implementation of trait \
|
debug!("after subst: %?",
|
||||||
%s for %s",
|
ty_to_str(tcx, trait_ty));
|
||||||
ty_to_str(vcx.tcx(), i_ty),
|
|
||||||
ty_to_str(vcx.tcx(), *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;
|
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
|
@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
|
// Look up the vtable to use when treating an item of type `t` as if it has
|
||||||
// type `trait_ty`. This does allow subtraits.
|
// type `trait_ty`
|
||||||
fn lookup_vtable_covariant(vcx: &VtableContext,
|
fn lookup_vtable(vcx: &VtableContext,
|
||||||
location_info: &LocationInfo,
|
location_info: &LocationInfo,
|
||||||
ty: ty::t,
|
ty: ty::t,
|
||||||
trait_ty: ty::t,
|
trait_ty: ty::t,
|
||||||
allow_unsafe: bool,
|
allow_unsafe: bool,
|
||||||
is_early: bool)
|
is_early: bool)
|
||||||
-> Option<vtable_origin> {
|
-> Option<vtable_origin> {
|
||||||
debug!("lookup_vtable_covariant(ty: %s, trait_ty=%s)",
|
debug!("lookup_vtable(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<vtable_origin> {
|
|
||||||
debug!("lookup_vtable_invariant(ty=%s, trait_ty=%s)",
|
|
||||||
vcx.infcx.ty_to_str(ty), vcx.infcx.ty_to_str(trait_ty));
|
vcx.infcx.ty_to_str(ty), vcx.infcx.ty_to_str(trait_ty));
|
||||||
let _i = indenter();
|
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 {
|
let (trait_id, trait_substs, trait_vstore) = match ty::get(trait_ty).sty {
|
||||||
ty::ty_trait(did, substs, vstore) => (did, substs, vstore),
|
ty::ty_trait(did, substs, vstore) => (did, substs, vstore),
|
||||||
_ => tcx.sess.impossible_case(location_info.span,
|
_ => tcx.sess.impossible_case(location_info.span,
|
||||||
"lookup_vtable_invariant: \
|
"lookup_vtable: \
|
||||||
don't know how to handle a non-trait")
|
don't know how to handle a non-trait")
|
||||||
};
|
};
|
||||||
let ty = match fixup_ty(vcx, location_info, ty, is_early) {
|
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 {
|
match ty::get(ty).sty {
|
||||||
ty::ty_param({idx: n, def_id: did}) => {
|
ty::ty_param({idx: n, def_id: did}) => {
|
||||||
let mut n_bound = 0;
|
let mut n_bound = 0;
|
||||||
for vec::each(*tcx.ty_param_bounds.get(did.node)) |bound| {
|
let bounds = tcx.ty_param_bounds.get(did.node);
|
||||||
match *bound {
|
for ty::iter_bound_traits_and_supertraits(
|
||||||
ty::bound_send | ty::bound_copy | ty::bound_const |
|
tcx, bounds) |ity| {
|
||||||
ty::bound_owned => {
|
debug!("checking bounds trait %?",
|
||||||
/* ignore */
|
vcx.infcx.ty_to_str(ity));
|
||||||
}
|
|
||||||
ty::bound_trait(ity) => {
|
match ty::get(ity).sty {
|
||||||
match ty::get(ity).sty {
|
ty::ty_trait(idid, _, _) => {
|
||||||
ty::ty_trait(idid, _, _) => {
|
if trait_id == idid {
|
||||||
if trait_id == idid {
|
debug!("(checking vtable) @0 \
|
||||||
debug!("(checking vtable) @0 relating \
|
relating ty to trait \
|
||||||
ty to trait ty with did %?",
|
ty with did %?",
|
||||||
idid);
|
idid);
|
||||||
relate_trait_tys(vcx, location_info,
|
relate_trait_tys(vcx, location_info,
|
||||||
trait_ty, ity);
|
trait_ty, ity);
|
||||||
return Some(vtable_param(n, n_bound));
|
let vtable = vtable_param(n, n_bound);
|
||||||
}
|
debug!("found param vtable: %?",
|
||||||
}
|
vtable);
|
||||||
_ => tcx.sess.impossible_case(
|
return Some(vtable);
|
||||||
location_info.span,
|
|
||||||
"lookup_vtable_invariant: in loop, \
|
|
||||||
don't know how to handle a non-trait ity")
|
|
||||||
}
|
}
|
||||||
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.
|
// impl.
|
||||||
let {substs: substs, ty: for_ty} =
|
let {substs: substs, ty: for_ty} =
|
||||||
impl_self_ty(vcx, location_info, im.did);
|
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,
|
match infer::mk_subty(vcx.infcx,
|
||||||
false,
|
false,
|
||||||
location_info.span,
|
location_info.span,
|
||||||
|
@ -369,6 +368,8 @@ fn lookup_vtable_invariant(vcx: &VtableContext,
|
||||||
// to. connect_trait_tps requires these
|
// to. connect_trait_tps requires these
|
||||||
// lists of types to unify pairwise.
|
// lists of types to unify pairwise.
|
||||||
|
|
||||||
|
let im_bs = ty::lookup_item_type(tcx,
|
||||||
|
im.did).bounds;
|
||||||
connect_trait_tps(vcx,
|
connect_trait_tps(vcx,
|
||||||
location_info,
|
location_info,
|
||||||
substs_f.tps,
|
substs_f.tps,
|
||||||
|
@ -493,8 +494,9 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
|
||||||
Some(ref substs) => {
|
Some(ref substs) => {
|
||||||
let def = cx.tcx.def_map.get(ex.id);
|
let def = cx.tcx.def_map.get(ex.id);
|
||||||
let did = ast_util::def_id_of_def(def);
|
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);
|
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) {
|
if has_trait_bounds(*item_ty.bounds) {
|
||||||
for item_ty.bounds.each |bounds| {
|
for item_ty.bounds.each |bounds| {
|
||||||
debug!("early_resolve_expr: looking up vtables for bound \
|
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,
|
ast::expr_field(_, _, _) => ex.id,
|
||||||
_ => ex.callee_id
|
_ => ex.callee_id
|
||||||
};
|
};
|
||||||
|
|
||||||
let substs = fcx.node_ty_substs(callee_id);
|
let substs = fcx.node_ty_substs(callee_id);
|
||||||
let vcx = VtableContext { ccx: fcx.ccx, infcx: fcx.infcx() };
|
let vcx = VtableContext { ccx: fcx.ccx, infcx: fcx.infcx() };
|
||||||
let vtbls = lookup_vtables(&vcx, &location_info_for_expr(ex),
|
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 ty = fcx.expr_ty(src);
|
||||||
let vcx = VtableContext { ccx: fcx.ccx, infcx: fcx.infcx() };
|
let vcx = VtableContext { ccx: fcx.ccx, infcx: fcx.infcx() };
|
||||||
let vtable_opt =
|
let vtable_opt =
|
||||||
lookup_vtable_invariant(&vcx,
|
lookup_vtable(&vcx,
|
||||||
&location_info_for_expr(ex),
|
&location_info_for_expr(ex),
|
||||||
ty,
|
ty,
|
||||||
target_ty,
|
target_ty,
|
||||||
true,
|
true,
|
||||||
is_early);
|
is_early);
|
||||||
match vtable_opt {
|
match vtable_opt {
|
||||||
None => {
|
None => {
|
||||||
// Try the new-style boxed trait; "@int as @Trait".
|
// 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 =
|
let location_info =
|
||||||
&location_info_for_expr(ex);
|
&location_info_for_expr(ex);
|
||||||
let vtable_opt =
|
let vtable_opt =
|
||||||
lookup_vtable_invariant(&vcx,
|
lookup_vtable(&vcx,
|
||||||
location_info,
|
location_info,
|
||||||
mt.ty,
|
mt.ty,
|
||||||
target_ty,
|
target_ty,
|
||||||
true,
|
true,
|
||||||
is_early);
|
is_early);
|
||||||
match vtable_opt {
|
match vtable_opt {
|
||||||
Some(vtable) => {
|
Some(vtable) => {
|
||||||
// Map this expression to that
|
// Map this expression to that
|
||||||
|
|
11
src/test/auxiliary/trait_inheritance_auto_xc_2_aux.rs
Normal file
11
src/test/auxiliary/trait_inheritance_auto_xc_2_aux.rs
Normal file
|
@ -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 } }
|
||||||
|
|
||||||
|
|
7
src/test/auxiliary/trait_inheritance_auto_xc_aux.rs
Normal file
7
src/test/auxiliary/trait_inheritance_auto_xc_aux.rs
Normal file
|
@ -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: Foo Bar Baz> T: Quux { }
|
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
pub trait Foo {
|
||||||
|
fn f() -> int;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct A {
|
||||||
|
x: int
|
||||||
|
}
|
||||||
|
|
||||||
|
impl A : Foo {
|
||||||
|
fn f() -> int { 10 }
|
||||||
|
}
|
|
@ -1,9 +1,31 @@
|
||||||
pub trait MyNum : Add<self,self>, Sub<self,self>, Mul<self,self> {
|
use cmp::Eq;
|
||||||
|
|
||||||
|
pub trait MyNum : Add<self,self>, Sub<self,self>, Mul<self,self>, Eq {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub impl int : MyNum {
|
pub struct MyInt {
|
||||||
pure fn add(other: &int) -> int { self + *other }
|
val: int
|
||||||
pure fn sub(&self, other: &int) -> int { *self - *other }
|
|
||||||
pure fn mul(&self, other: &int) -> int { *self * *other }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub impl MyInt : Add<MyInt, MyInt> {
|
||||||
|
pure fn add(other: &MyInt) -> MyInt { mi(self.val + other.val) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub impl MyInt : Sub<MyInt, MyInt> {
|
||||||
|
pure fn sub(&self, other: &MyInt) -> MyInt { mi(self.val - other.val) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub impl MyInt : Mul<MyInt, MyInt> {
|
||||||
|
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 } }
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
}
|
||||||
|
|
23
src/test/run-pass/trait-inheritance-auto-xc-2.rs
Normal file
23
src/test/run-pass/trait-inheritance-auto-xc-2.rs
Normal file
|
@ -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: Foo Bar Baz> T: Quux { }
|
||||||
|
|
||||||
|
fn f<T: Quux>(a: &T) {
|
||||||
|
assert a.f() == 10;
|
||||||
|
assert a.g() == 20;
|
||||||
|
assert a.h() == 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let a = &A { x: 3 };
|
||||||
|
f(a);
|
||||||
|
}
|
||||||
|
|
24
src/test/run-pass/trait-inheritance-auto-xc.rs
Normal file
24
src/test/run-pass/trait-inheritance-auto-xc.rs
Normal file
|
@ -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<T: Quux>(a: &T) {
|
||||||
|
assert a.f() == 10;
|
||||||
|
assert a.g() == 20;
|
||||||
|
assert a.h() == 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let a = &A { x: 3 };
|
||||||
|
f(a);
|
||||||
|
}
|
||||||
|
|
27
src/test/run-pass/trait-inheritance-auto.rs
Normal file
27
src/test/run-pass/trait-inheritance-auto.rs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
// Testing that this impl turns A into a Quux, because
|
||||||
|
// A is already a Foo Bar Baz
|
||||||
|
impl<T: Foo Bar Baz> 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<T: Quux>(a: &T) {
|
||||||
|
assert a.f() == 10;
|
||||||
|
assert a.g() == 20;
|
||||||
|
assert a.h() == 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let a = &A { x: 3 };
|
||||||
|
f(a);
|
||||||
|
}
|
||||||
|
|
18
src/test/run-pass/trait-inheritance-call-bound-inherited.rs
Normal file
18
src/test/run-pass/trait-inheritance-call-bound-inherited.rs
Normal file
|
@ -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<T:Bar>(a: &T) -> int {
|
||||||
|
a.f()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let a = &A { x: 3 };
|
||||||
|
assert gg(a) == 10;
|
||||||
|
}
|
||||||
|
|
21
src/test/run-pass/trait-inheritance-call-bound-inherited2.rs
Normal file
21
src/test/run-pass/trait-inheritance-call-bound-inherited2.rs
Normal file
|
@ -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<T: Baz>(a: &T) -> int {
|
||||||
|
a.f()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let a = &A { x: 3 };
|
||||||
|
assert gg(a) == 10;
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
33
src/test/run-pass/trait-inheritance-cast.rs
Normal file
33
src/test/run-pass/trait-inheritance-cast.rs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
|
18
src/test/run-pass/trait-inheritance-cross-trait-call-xc.rs
Normal file
18
src/test/run-pass/trait-inheritance-cross-trait-call-xc.rs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
|
17
src/test/run-pass/trait-inheritance-cross-trait-call.rs
Normal file
17
src/test/run-pass/trait-inheritance-cross-trait-call.rs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
|
25
src/test/run-pass/trait-inheritance-diamond.rs
Normal file
25
src/test/run-pass/trait-inheritance-diamond.rs
Normal file
|
@ -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<T: D>(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);
|
||||||
|
}
|
20
src/test/run-pass/trait-inheritance-multiple-inheritors.rs
Normal file
20
src/test/run-pass/trait-inheritance-multiple-inheritors.rs
Normal file
|
@ -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<T: B C>(x: &T) {
|
||||||
|
assert x.a() == 10;
|
||||||
|
assert x.b() == 20;
|
||||||
|
assert x.c() == 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
f(&S { bogus: () })
|
||||||
|
}
|
23
src/test/run-pass/trait-inheritance-multiple-params.rs
Normal file
23
src/test/run-pass/trait-inheritance-multiple-params.rs
Normal file
|
@ -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: A, Y: B, Z: C>(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);
|
||||||
|
}
|
14
src/test/run-pass/trait-inheritance-num.rs
Normal file
14
src/test/run-pass/trait-inheritance-num.rs
Normal file
|
@ -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<T:NumExt>(n: &T) -> bool { *n > from_int(1) }
|
||||||
|
fn greater_than_one_float<T:FloatExt>(n: &T) -> bool { *n > from_int(1) }
|
||||||
|
|
||||||
|
fn main() {}
|
16
src/test/run-pass/trait-inheritance-num0.rs
Normal file
16
src/test/run-pass/trait-inheritance-num0.rs
Normal file
|
@ -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<T:NumExt>(n: &T) -> bool {
|
||||||
|
n.gt(&from_int(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
12
src/test/run-pass/trait-inheritance-num1.rs
Normal file
12
src/test/run-pass/trait-inheritance-num1.rs
Normal file
|
@ -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<T:NumExt>(n: &T) -> bool {
|
||||||
|
*n > from_int(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
96
src/test/run-pass/trait-inheritance-num2.rs
Normal file
96
src/test/run-pass/trait-inheritance-num2.rs
Normal file
|
@ -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<T:FloatExt>(n: T) { io::println(fmt!("%?", n < n)) }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test_float_ext(1f32);
|
||||||
|
}
|
12
src/test/run-pass/trait-inheritance-num3.rs
Normal file
12
src/test/run-pass/trait-inheritance-num3.rs
Normal file
|
@ -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<T:NumExt>(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
|
||||||
|
}
|
15
src/test/run-pass/trait-inheritance-num5.rs
Normal file
15
src/test/run-pass/trait-inheritance-num5.rs
Normal file
|
@ -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:NumExt>() -> T {
|
||||||
|
from_int(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
num_eq_one::<int>(); // you need to actually use the function to trigger the ICE
|
||||||
|
}
|
25
src/test/run-pass/trait-inheritance-overloading-simple.rs
Normal file
25
src/test/run-pass/trait-inheritance-overloading-simple.rs
Normal file
|
@ -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<T:MyNum>(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;
|
||||||
|
}
|
||||||
|
|
|
@ -2,18 +2,19 @@
|
||||||
// aux-build:trait_inheritance_overloading_xc.rs
|
// aux-build:trait_inheritance_overloading_xc.rs
|
||||||
|
|
||||||
extern mod trait_inheritance_overloading_xc;
|
extern mod trait_inheritance_overloading_xc;
|
||||||
use trait_inheritance_overloading_xc::MyNum;
|
use trait_inheritance_overloading_xc::{MyNum, MyInt};
|
||||||
|
|
||||||
fn f<T:Copy MyNum>(x: T, y: T) -> (T, T, T) {
|
fn f<T:Copy MyNum>(x: T, y: T) -> (T, T, T) {
|
||||||
return (x + y, x - y, x * y);
|
return (x + y, x - y, x * y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pure fn mi(v: int) -> MyInt { MyInt { val: v } }
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let (x, y) = (3, 5);
|
let (x, y) = (mi(3), mi(5));
|
||||||
let (a, b, c) = f(x, y);
|
let (a, b, c) = f(x, y);
|
||||||
assert a == 8;
|
assert a == mi(8);
|
||||||
assert b == -2;
|
assert b == mi(-2);
|
||||||
assert c == 15;
|
assert c == mi(15);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,39 @@
|
||||||
trait MyNum : Add<self,self>, Sub<self,self>, Mul<self,self> {
|
use cmp::Eq;
|
||||||
|
|
||||||
|
trait MyNum : Add<self,self>, Sub<self,self>, Mul<self,self>, Eq { }
|
||||||
|
|
||||||
|
struct MyInt { val: int }
|
||||||
|
|
||||||
|
impl MyInt : Add<MyInt, MyInt> {
|
||||||
|
pure fn add(other: &MyInt) -> MyInt { mi(self.val + other.val) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl int : MyNum {
|
impl MyInt : Sub<MyInt, MyInt> {
|
||||||
pure fn add(other: &int) -> int { self + *other }
|
pure fn sub(&self, other: &MyInt) -> MyInt { mi(self.val - other.val) }
|
||||||
pure fn sub(&self, other: &int) -> int { *self - *other }
|
|
||||||
pure fn mul(&self, other: &int) -> int { *self * *other }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MyInt : Mul<MyInt, MyInt> {
|
||||||
|
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<T:Copy MyNum>(x: T, y: T) -> (T, T, T) {
|
fn f<T:Copy MyNum>(x: T, y: T) -> (T, T, T) {
|
||||||
return (x + y, x - y, x * y);
|
return (x + y, x - y, x * y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pure fn mi(v: int) -> MyInt { MyInt { val: v } }
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let (x, y) = (3, 5);
|
let (x, y) = (mi(3), mi(5));
|
||||||
let (a, b, c) = f(x, y);
|
let (a, b, c) = f(x, y);
|
||||||
assert a == 8;
|
assert a == mi(8);
|
||||||
assert b == -2;
|
assert b == mi(-2);
|
||||||
assert c == 15;
|
assert c == mi(15);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,22 @@
|
||||||
trait Foo {
|
trait Foo { fn f() -> int; }
|
||||||
fn f();
|
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<T:Foo>(a: &T) -> int {
|
||||||
|
a.f()
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Bar : Foo {
|
fn gg<T:Bar>(a: &T) -> int {
|
||||||
fn g();
|
a.g()
|
||||||
}
|
|
||||||
|
|
||||||
struct A {
|
|
||||||
x: int
|
|
||||||
}
|
|
||||||
|
|
||||||
impl A : Bar {
|
|
||||||
fn g() { io::println("in g"); }
|
|
||||||
fn f() { io::println("in f"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn h<T:Foo>(a: &T) {
|
|
||||||
a.f();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let a = A { x: 3 };
|
let a = &A { x: 3 };
|
||||||
h(&a);
|
assert ff(a) == 10;
|
||||||
|
assert gg(a) == 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
24
src/test/run-pass/trait-inheritance-static.rs
Normal file
24
src/test/run-pass/trait-inheritance-static.rs
Normal file
|
@ -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:NumExt>() -> T { from_int(1) }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let v: S = greater_than_one();
|
||||||
|
assert v.v == 1;
|
||||||
|
}
|
28
src/test/run-pass/trait-inheritance-static2.rs
Normal file
28
src/test/run-pass/trait-inheritance-static2.rs
Normal file
|
@ -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:NumExt>() -> T { from_int(1) }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let v: S = greater_than_one();
|
||||||
|
assert v.v == 1;
|
||||||
|
}
|
26
src/test/run-pass/trait-inheritance-subst.rs
Normal file
26
src/test/run-pass/trait-inheritance-subst.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
pub trait Add<RHS,Result> {
|
||||||
|
pure fn add(rhs: &RHS) -> Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait MyNum : Add<self,self> { }
|
||||||
|
|
||||||
|
struct MyInt { val: int }
|
||||||
|
|
||||||
|
impl MyInt : Add<MyInt, MyInt> {
|
||||||
|
pure fn add(other: &MyInt) -> MyInt { mi(self.val + other.val) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MyInt : MyNum;
|
||||||
|
|
||||||
|
fn f<T:MyNum>(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
|
||||||
|
}
|
||||||
|
|
36
src/test/run-pass/trait-inheritance-subst2.rs
Normal file
36
src/test/run-pass/trait-inheritance-subst2.rs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
trait Panda<T> {
|
||||||
|
fn chomp(bamboo: &T) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Add<RHS,Result>: Panda<RHS> {
|
||||||
|
fn add(rhs: &RHS) -> Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait MyNum : Add<self,self> { }
|
||||||
|
|
||||||
|
struct MyInt { val: int }
|
||||||
|
|
||||||
|
impl MyInt : Panda<MyInt> {
|
||||||
|
fn chomp(bamboo: &MyInt) -> MyInt {
|
||||||
|
mi(self.val + bamboo.val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MyInt : Add<MyInt, MyInt> {
|
||||||
|
fn add(other: &MyInt) -> MyInt { self.chomp(other) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MyInt : MyNum;
|
||||||
|
|
||||||
|
fn f<T:MyNum>(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;
|
||||||
|
}
|
||||||
|
|
18
src/test/run-pass/trait-inheritance-visibility.rs
Normal file
18
src/test/run-pass/trait-inheritance-visibility.rs
Normal file
|
@ -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: traits::Foo> 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<T: Quux>(x: &T) {
|
||||||
|
assert x.f() == 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
f(&0)
|
||||||
|
}
|
24
src/test/run-pass/trait-inheritance2.rs
Normal file
24
src/test/run-pass/trait-inheritance2.rs
Normal file
|
@ -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<T: Quux Foo Bar Baz>(a: &T) {
|
||||||
|
assert a.f() == 10;
|
||||||
|
assert a.g() == 20;
|
||||||
|
assert a.h() == 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let a = &A { x: 3 };
|
||||||
|
f(a);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue