From 1a8969f64be0a7e7a2cd61f04d6c4d94abe35c75 Mon Sep 17 00:00:00 2001 From: Michael Sullivan Date: Wed, 12 Jun 2013 14:22:17 -0700 Subject: [PATCH] Get cross crate default methods working. This fixes the large number of problems that prevented cross crate methods from ever working. It also fixes a couple lingering bugs with polymorphic default methods and cleans up some of the code paths. Closes #4102. Closes #4103. --- src/librustc/metadata/csearch.rs | 2 +- src/librustc/metadata/decoder.rs | 41 +---- src/librustc/middle/trans/callee.rs | 51 ++---- src/librustc/middle/trans/inline.rs | 15 +- src/librustc/middle/trans/meth.rs | 89 ++++------ src/librustc/middle/typeck/check/method.rs | 4 + src/librustc/middle/typeck/coherence.rs | 163 +++++++++--------- .../auxiliary/trait_default_method_xc_aux.rs | 34 ++++ .../trait-default-method-bound-subst.rs | 2 + src/test/run-pass/trait-default-method-xc.rs | 71 ++++++++ 10 files changed, 255 insertions(+), 217 deletions(-) create mode 100644 src/test/auxiliary/trait_default_method_xc_aux.rs create mode 100644 src/test/run-pass/trait-default-method-xc.rs diff --git a/src/librustc/metadata/csearch.rs b/src/librustc/metadata/csearch.rs index 5b154f6836c28..e1cd7caa19c1e 100644 --- a/src/librustc/metadata/csearch.rs +++ b/src/librustc/metadata/csearch.rs @@ -229,7 +229,7 @@ pub fn get_impl_trait(tcx: ty::ctxt, pub fn get_impl_method(cstore: @mut cstore::CStore, def: ast::def_id, mname: ast::ident) - -> ast::def_id { + -> Option { let cdata = cstore::get_crate_data(cstore, def.crate); decoder::get_impl_method(cstore.intr, cdata, def.node, mname) } diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index fdef25b5e711a..7f06953663b3c 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -415,7 +415,7 @@ pub fn get_impl_trait(cdata: cmd, } pub fn get_impl_method(intr: @ident_interner, cdata: cmd, id: ast::node_id, - name: ast::ident) -> ast::def_id { + name: ast::ident) -> Option { let items = reader::get_doc(reader::Doc(cdata.data), tag_items); let mut found = None; for reader::tagged_docs(find_item(id, items), tag_item_impl_method) @@ -425,7 +425,7 @@ pub fn get_impl_method(intr: @ident_interner, cdata: cmd, id: ast::node_id, found = Some(translate_def_id(cdata, m_did)); } } - found.get() + found } pub fn get_symbol(data: @~[u8], id: ast::node_id) -> ~str { @@ -755,40 +755,13 @@ pub fn get_provided_trait_methods(intr: @ident_interner, cdata: cmd, let item = lookup_item(id, data); let mut result = ~[]; - for reader::tagged_docs(item, tag_item_trait_method) |mth| { - if item_method_sort(mth) != 'p' { loop; } - - let did = item_def_id(mth, cdata); - - let type_param_defs = - item_ty_param_defs(mth, tcx, cdata, - tag_items_data_item_ty_param_bounds); - let name = item_name(intr, mth); - let ty = doc_type(mth, tcx, cdata); + for reader::tagged_docs(item, tag_item_trait_method) |mth_id| { + let did = item_def_id(mth_id, cdata); + let mth = lookup_item(did.node, data); - let fty = match ty::get(ty).sty { - ty::ty_bare_fn(ref f) => copy *f, - _ => { - tcx.diag.handler().bug("get_provided_trait_methods(): id \ - has non-function type"); - } - }; + if item_method_sort(mth) != 'p' { loop; } - let transformed_self_ty = doc_transformed_self_ty(mth, tcx, cdata); - let explicit_self = get_explicit_self(mth); - - let ty_method = ty::Method::new( - name, - ty::Generics { - type_param_defs: type_param_defs, - region_param: None - }, - transformed_self_ty, - fty, - explicit_self, - ast::public, - did - ); + let ty_method = get_method(intr, cdata, did.node, tcx); let provided_trait_method_info = ProvidedTraitMethodInfo { ty: ty_method, def_id: did diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index bfbe078c4f524..7fab21b4156cb 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -42,6 +42,7 @@ use middle::trans::type_of; use middle::ty; use middle::subst::Subst; use middle::typeck; +use middle::typeck::coherence::make_substs_for_receiver_types; use util::ppaux::Repr; use core::vec; @@ -253,50 +254,24 @@ pub fn trans_fn_ref_with_vtables( // So, what we need to do is find this substitution and // compose it with the one we already have. - // In order to find the substitution for the trait params, - // we look up the impl in the ast map, find its trait_ref - // id, then look up its trait ref. I feel like there - // should be a better way. - let map_node = session::expect( - ccx.sess, - ccx.tcx.items.find_copy(&source.impl_id.node), - || fmt!("couldn't find node while monomorphizing \ - default method: %?", source.impl_id.node)); - let item = match map_node { - ast_map::node_item(item, _) => item, - _ => ccx.tcx.sess.bug("Not an item") - }; - let ast_trait_ref = match copy item.node { - ast::item_impl(_, Some(tr), _, _) => tr, - _ => ccx.tcx.sess.bug("Not an impl with trait_ref") - }; - let trait_ref = ccx.tcx.trait_refs.get(&ast_trait_ref.ref_id); - - // The substs from the trait_ref only substitues for the - // trait parameters. Our substitution also needs to be - // able to substitute for the actual method type - // params. To do this, we figure out how many method - // parameters there are and pad out the substitution with - // substitution for the variables. - let item_ty = ty::lookup_item_type(tcx, source.method_id); - let num_params = item_ty.generics.type_param_defs.len() - - trait_ref.substs.tps.len(); - let id_subst = do vec::from_fn(num_params) |i| { - ty::mk_param(tcx, i, ast::def_id {crate: 0, node: 0}) - }; - // Merge the two substitions together now. - let first_subst = ty::substs {tps: trait_ref.substs.tps + id_subst, - .. trait_ref.substs}; + let trait_ref = ty::impl_trait_ref(tcx, source.impl_id) + .expect("could not find trait_ref for impl with \ + default methods"); + let method = ty::method(tcx, source.method_id); - // And compose them. + // Compute the first substitution + let first_subst = make_substs_for_receiver_types( + tcx, source.impl_id, trait_ref, method); + + // And compose them let new_substs = first_subst.subst(tcx, &substs); debug!("trans_fn_with_vtables - default method: \ - substs = %s, id_subst = %s, trait_subst = %s, \ + substs = %s, trait_subst = %s, \ first_subst = %s, new_subst = %s", - substs.repr(tcx), - id_subst.repr(tcx), trait_ref.substs.repr(tcx), + substs.repr(tcx), trait_ref.substs.repr(tcx), first_subst.repr(tcx), new_substs.repr(tcx)); + (source.method_id, Some(source.impl_id), new_substs) } }; diff --git a/src/librustc/middle/trans/inline.rs b/src/librustc/middle/trans/inline.rs index b0aedbae79bfd..00afece91979f 100644 --- a/src/librustc/middle/trans/inline.rs +++ b/src/librustc/middle/trans/inline.rs @@ -93,11 +93,16 @@ pub fn maybe_instantiate_inline(ccx: @mut CrateContext, fn_id: ast::def_id, csearch::found(ast::ii_method(impl_did, mth)) => { ccx.stats.n_inlines += 1; ccx.external.insert(fn_id, Some(mth.id)); - let impl_tpt = ty::lookup_item_type(ccx.tcx, impl_did); - let num_type_params = - impl_tpt.generics.type_param_defs.len() + - mth.generics.ty_params.len(); - if translate && num_type_params == 0 { + // If this is a default method, we can't look up the + // impl type. But we aren't going to translate anyways, so don't. + if !translate { return local_def(mth.id); } + + let impl_tpt = ty::lookup_item_type(ccx.tcx, impl_did); + let num_type_params = + impl_tpt.generics.type_param_defs.len() + + mth.generics.ty_params.len(); + + if num_type_params == 0 { let llfn = get_item_val(ccx, mth.id); let path = vec::append( ty::item_path(ccx.tcx, impl_did), diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index c59b3f36779b7..bd79cb1cfeba6 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -383,71 +383,48 @@ pub fn method_with_name_or_default(ccx: @mut CrateContext, name: ast::ident) -> ast::def_id { let imp = ccx.impl_method_cache.find_copy(&(impl_id, name)); match imp { - Some(m) => m, - None => { - let imp = if impl_id.crate == ast::local_crate { - match ccx.tcx.items.get_copy(&impl_id.node) { - ast_map::node_item(@ast::item { - node: ast::item_impl(_, _, _, ref ms), _ - }, _) => { - let did = method_from_methods(*ms, name); - if did.is_some() { - did.get() - } else { - // Look for a default method - let pmm = ccx.tcx.provided_methods; - match pmm.find(&impl_id) { - Some(pmis) => { - for pmis.each |pmi| { - if pmi.method_info.ident == name { - debug!("pmi.method_info.did = %?", pmi.method_info.did); - return pmi.method_info.did; - } - } - fail!() - } - None => fail!() - } - } - } - _ => fail!("method_with_name") - } - } else { - csearch::get_impl_method(ccx.sess.cstore, impl_id, name) - }; + Some(m) => return m, + None => {} + } - ccx.impl_method_cache.insert((impl_id, name), imp); + // None of this feels like it should be the best way to do this. + let mut did = if impl_id.crate == ast::local_crate { + match ccx.tcx.items.get_copy(&impl_id.node) { + ast_map::node_item(@ast::item { + node: ast::item_impl(_, _, _, ref ms), _ + }, _) => { method_from_methods(*ms, name) }, + _ => fail!("method_with_name") + } + } else { + csearch::get_impl_method(ccx.sess.cstore, impl_id, name) + }; - imp + if did.is_none() { + // Look for a default method + let pmm = ccx.tcx.provided_methods; + match pmm.find(&impl_id) { + Some(pmis) => { + for pmis.each |pmi| { + if pmi.method_info.ident == name { + debug!("pmi.method_info.did = %?", + pmi.method_info.did); + did = Some(pmi.method_info.did); + } + } + } + None => {} } } + + let imp = did.expect("could not find method while translating"); + ccx.impl_method_cache.insert((impl_id, name), imp); + imp } pub fn method_ty_param_count(ccx: &CrateContext, m_id: ast::def_id, i_id: ast::def_id) -> uint { debug!("method_ty_param_count: m_id: %?, i_id: %?", m_id, i_id); - if m_id.crate == ast::local_crate { - match ccx.tcx.items.find(&m_id.node) { - Some(&ast_map::node_method(m, _, _)) => m.generics.ty_params.len(), - None => { - match ccx.tcx.provided_method_sources.find(&m_id) { - Some(source) => { - method_ty_param_count( - ccx, source.method_id, source.impl_id) - } - None => fail!() - } - } - Some(&ast_map::node_trait_method(@ast::provided(@ref m), - _, _)) => { - m.generics.ty_params.len() - } - ref e => fail!("method_ty_param_count %?", *e) - } - } else { - csearch::get_type_param_count(ccx.sess.cstore, m_id) - - csearch::get_type_param_count(ccx.sess.cstore, i_id) - } + ty::method(ccx.tcx, m_id).generics.type_param_defs.len() } pub fn trans_monomorphized_callee(bcx: block, diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index b2c9d27241d43..7fc1728a720bc 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -541,6 +541,10 @@ impl<'self> LookupContext<'self> { if !self.impl_dups.insert(impl_info.did) { return; // already visited } + debug!("push_candidates_from_impl: %s %s %s", + self.m_name.repr(self.tcx()), + impl_info.ident.repr(self.tcx()), + impl_info.methods.map(|m| m.ident).repr(self.tcx())); let idx = { match impl_info.methods.position(|m| m.ident == self.m_name) { diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs index 9528c825fbed9..d404ac39a1518 100644 --- a/src/librustc/middle/typeck/coherence.rs +++ b/src/librustc/middle/typeck/coherence.rs @@ -279,7 +279,7 @@ impl CoherenceChecker { trait_ref.repr(self.crate_context.tcx), self.crate_context.tcx.sess.str_of(item.ident)); - self.instantiate_default_methods(item.id, trait_ref); + self.instantiate_default_methods(local_def(item.id), trait_ref); let implementation; if implementation_opt.is_none() { @@ -327,13 +327,13 @@ impl CoherenceChecker { // and trait pair. Then, for each provided method in the trait, inserts a // `ProvidedMethodInfo` instance into the `provided_method_sources` map. pub fn instantiate_default_methods(&self, - impl_id: ast::node_id, + impl_id: ast::def_id, trait_ref: &ty::TraitRef) { let tcx = self.crate_context.tcx; debug!("instantiate_default_methods(impl_id=%?, trait_ref=%s)", impl_id, trait_ref.repr(tcx)); - let impl_poly_type = ty::lookup_item_type(tcx, local_def(impl_id)); + let impl_poly_type = ty::lookup_item_type(tcx, impl_id); for self.each_provided_trait_method(trait_ref.def_id) |trait_method| { // Synthesize an ID. @@ -375,7 +375,7 @@ impl CoherenceChecker { // ID of the method. let source = ProvidedMethodSource { method_id: trait_method.def_id, - impl_id: local_def(impl_id) + impl_id: impl_id }; self.crate_context.tcx.provided_method_sources.insert(new_did, @@ -393,7 +393,7 @@ impl CoherenceChecker { }; let pmm = self.crate_context.tcx.provided_methods; - match pmm.find(&local_def(impl_id)) { + match pmm.find(&impl_id) { Some(&mis) => { // If the trait already has an entry in the // provided_methods_map, we just need to add this @@ -410,7 +410,7 @@ impl CoherenceChecker { for method `%s`", self.crate_context.tcx.sess.str_of( provided_method_info.method_info.ident)); - pmm.insert(local_def(impl_id), + pmm.insert(impl_id, @mut ~[provided_method_info]); } } @@ -733,10 +733,12 @@ impl CoherenceChecker { } // Default methods for ty::provided_trait_methods(tcx, trait_did).each |ident| { + debug!("inserting provided method %s", ident.repr(tcx)); provided_names.insert(*ident); } for (*ty::trait_methods(tcx, trait_did)).each |method| { + debug!("checking for %s", method.ident.repr(tcx)); if provided_names.contains(&method.ident) { loop; } tcx.sess.span_err(trait_ref_span, @@ -785,19 +787,41 @@ impl CoherenceChecker { } } - // Converts an implementation in the AST to an Impl structure. - pub fn create_impl_from_item(&self, item: @item) -> @Impl { - fn add_provided_methods(all_methods: &mut ~[@MethodInfo], - all_provided_methods: &mut ~[@ProvidedMethodInfo], - sess: driver::session::Session) { - for all_provided_methods.each |provided_method| { - debug!( - "(creating impl) adding provided method `%s` to impl", - sess.str_of(provided_method.method_info.ident)); - vec::push(all_methods, provided_method.method_info); + fn add_provided_methods_to_impl( + &self, + all_methods: &mut ~[@MethodInfo], + trait_did: &ast::def_id, + impl_id: &ast::def_id) { + + + match self.crate_context.tcx + .provided_methods + .find(impl_id) { + None => { + debug!("(creating impl) trait with node_id `%d` \ + has no provided methods", trait_did.node); + /* fall through */ + } + Some(&all_provided_methods) => { + debug!("(creating impl) trait with node_id `%d` \ + has provided methods", trait_did.node); + // Add all provided methods. + for all_provided_methods.each |provided_method| { + debug!( + "(creating impl) adding provided method \ + `%s` to impl", + provided_method.method_info + .ident.repr(self.crate_context.tcx)); + vec::push(all_methods, provided_method.method_info); + } + } } - } + + } + + // Converts an implementation in the AST to an Impl structure. + pub fn create_impl_from_item(&self, item: @item) -> @Impl { match item.node { item_impl(_, ref trait_refs, _, ref ast_methods) => { let mut methods = ~[]; @@ -820,27 +844,11 @@ impl CoherenceChecker { // if a method of that name is not inherent to the // impl, use the provided definition in the trait. for trait_refs.iter().advance |trait_ref| { - let trait_did = - self.trait_ref_to_trait_def_id(*trait_ref); - - match self.crate_context.tcx - .provided_methods - .find(&local_def(item.id)) { - None => { - debug!("(creating impl) trait with node_id `%d` \ - has no provided methods", trait_did.node); - /* fall through */ - } - Some(&all_provided) => { - debug!("(creating impl) trait with node_id `%d` \ - has provided methods", trait_did.node); - // Add all provided methods. - add_provided_methods( - &mut methods, - all_provided, - self.crate_context.tcx.sess); - } - } + let trait_did = self.trait_ref_to_trait_def_id(*trait_ref); + self.add_provided_methods_to_impl( + &mut methods, + &trait_did, + &local_def(item.id)); } return @Impl { @@ -917,9 +925,23 @@ impl CoherenceChecker { } } + let mut implementation = *implementation; + // Record all the trait methods. for associated_traits.iter().advance |trait_ref| { - self.add_trait_method(trait_ref.def_id, *implementation); + self.instantiate_default_methods(implementation.did, + &**trait_ref); + // Could we avoid these copies when we don't need them? + let mut methods = /*bad?*/ copy implementation.methods; + self.add_provided_methods_to_impl( + &mut methods, + &trait_ref.def_id, + &implementation.did); + implementation = @Impl { methods: methods, + .. *implementation }; + + + self.add_trait_method(trait_ref.def_id, implementation); } // Add the implementation to the mapping from @@ -937,7 +959,7 @@ impl CoherenceChecker { // `impl Trait for Type`: if associated_traits.is_none() { self.add_inherent_method(base_type_def_id, - *implementation); + implementation); } self.base_type_def_ids.insert(implementation.did, @@ -947,38 +969,6 @@ impl CoherenceChecker { } } - pub fn add_default_methods_for_external_trait(&self, - trait_def_id: ast::def_id) { - let tcx = self.crate_context.tcx; - let pmm = tcx.provided_methods; - - if pmm.contains_key(&trait_def_id) { return; } - - debug!("(adding default methods for trait) processing trait"); - - for csearch::get_provided_trait_methods(tcx, trait_def_id).each - |trait_method_info| { - debug!("(adding default methods for trait) found default method"); - - // Create a new def ID for this provided method. - let parse_sess = &self.crate_context.tcx.sess.parse_sess; - let new_did = local_def(parse::next_node_id(*parse_sess)); - - let provided_method_info = - @ProvidedMethodInfo { - method_info: @MethodInfo { - did: new_did, - n_tps: trait_method_info.ty.generics.type_param_defs.len(), - ident: trait_method_info.ty.ident, - explicit_self: trait_method_info.ty.explicit_self - }, - trait_method_def_id: trait_method_info.def_id - }; - - pmm.insert(trait_def_id, @mut ~[provided_method_info]); - } - } - // Adds implementations and traits from external crates to the coherence // info. pub fn add_external_crates(&self) { @@ -998,9 +988,6 @@ impl CoherenceChecker { crate_store, def_id); } - dl_def(def_trait(def_id)) => { - self.add_default_methods_for_external_trait(def_id); - } dl_def(_) | dl_impl(_) | dl_field => { // Skip this. loop; @@ -1063,12 +1050,11 @@ impl CoherenceChecker { } } -fn subst_receiver_types_in_method_ty(tcx: ty::ctxt, - impl_id: ast::node_id, - trait_ref: &ty::TraitRef, - new_def_id: ast::def_id, - method: &ty::Method) - -> ty::Method { +pub fn make_substs_for_receiver_types(tcx: ty::ctxt, + impl_id: ast::def_id, + trait_ref: &ty::TraitRef, + method: &ty::Method) + -> ty::substs { /*! * Substitutes the values for the receiver's type parameters * that are found in method, leaving the method's type parameters @@ -1079,7 +1065,7 @@ fn subst_receiver_types_in_method_ty(tcx: ty::ctxt, // determine how many type parameters were declared on the impl let num_impl_type_parameters = { - let impl_polytype = ty::lookup_item_type(tcx, local_def(impl_id)); + let impl_polytype = ty::lookup_item_type(tcx, impl_id); impl_polytype.generics.type_param_defs.len() }; @@ -1105,11 +1091,22 @@ fn subst_receiver_types_in_method_ty(tcx: ty::ctxt, } }); - let combined_substs = ty::substs { + return ty::substs { self_r: trait_ref.substs.self_r, self_ty: trait_ref.substs.self_ty, tps: combined_tps }; +} + +fn subst_receiver_types_in_method_ty(tcx: ty::ctxt, + impl_id: ast::def_id, + trait_ref: &ty::TraitRef, + new_def_id: ast::def_id, + method: &ty::Method) + -> ty::Method { + + let combined_substs = make_substs_for_receiver_types( + tcx, impl_id, trait_ref, method); ty::Method::new( method.ident, diff --git a/src/test/auxiliary/trait_default_method_xc_aux.rs b/src/test/auxiliary/trait_default_method_xc_aux.rs new file mode 100644 index 0000000000000..5ee243179df8b --- /dev/null +++ b/src/test/auxiliary/trait_default_method_xc_aux.rs @@ -0,0 +1,34 @@ +#[allow(default_methods)]; + +pub trait A { + fn f(&self) -> int; + fn g(&self) -> int { 10 } + fn h(&self) -> int { 10 } +} + + +impl A for int { + fn f(&self) -> int { 10 } +} + +trait B { + fn thing(&self, x: T, y: U) -> (T, U) { (x, y) } +} + +impl B for int { } +impl B for bool { } + + + +pub trait TestEquality { + fn test_eq(&self, rhs: &Self) -> bool; + fn test_neq(&self, rhs: &Self) -> bool { + !self.test_eq(rhs) + } +} + +impl TestEquality for int { + fn test_eq(&self, rhs: &int) -> bool { + *self == *rhs + } +} diff --git a/src/test/run-pass/trait-default-method-bound-subst.rs b/src/test/run-pass/trait-default-method-bound-subst.rs index adabafc082a0c..9dbbcee0f7707 100644 --- a/src/test/run-pass/trait-default-method-bound-subst.rs +++ b/src/test/run-pass/trait-default-method-bound-subst.rs @@ -15,6 +15,7 @@ trait A { } impl A for int { } +impl A for uint { } fn f>(i: V, j: T, k: U) -> (T, U) { i.g(j, k) @@ -22,4 +23,5 @@ fn f>(i: V, j: T, k: U) -> (T, U) { pub fn main () { assert_eq!(f(0, 1, 2), (1, 2)); + assert_eq!(f(0u, 1, 2), (1, 2)); } diff --git a/src/test/run-pass/trait-default-method-xc.rs b/src/test/run-pass/trait-default-method-xc.rs new file mode 100644 index 0000000000000..e6e5b8605a161 --- /dev/null +++ b/src/test/run-pass/trait-default-method-xc.rs @@ -0,0 +1,71 @@ +// xfail-fast +// aux-build:trait_default_method_xc_aux.rs + +#[allow(default_methods)]; + +extern mod aux(name = "trait_default_method_xc_aux"); +use aux::{A, B, TestEquality}; + + +fn f(i: T) { + assert_eq!(i.g(), 10); +} + + +pub struct thing { x: int } +impl A for thing { + fn f(&self) -> int { 10 } +} + +fn g>(i: V, j: T, k: U) -> (T, U) { + i.thing(j, k) +} + +fn eq(lhs: &T, rhs: &T) -> bool { + lhs.test_eq(rhs) +} +fn neq(lhs: &T, rhs: &T) -> bool { + lhs.test_neq(rhs) +} + + +impl TestEquality for thing { + fn test_eq(&self, rhs: &thing) -> bool { + //self.x.test_eq(&rhs.x) + eq(&self.x, &rhs.x) + } +} + + +fn main () { + // Some tests of random things + f(0); + + let a = thing { x: 0 }; + let b = thing { x: 1 }; + + assert_eq!(0i.g(), 10); + assert_eq!(a.g(), 10); + assert_eq!(a.h(), 10); + + + assert_eq!(0i.thing(3.14, 1), (3.14, 1)); + + assert_eq!(g(0i, 3.14, 1), (3.14, 1)); + assert_eq!(g(false, 3.14, 1), (3.14, 1)); + + let obj = @0i as @A; + assert_eq!(obj.h(), 10); + + + // Trying out a real one + assert!(12.test_neq(&10)); + assert!(!10.test_neq(&10)); + assert!(a.test_neq(&b)); + assert!(!a.test_neq(&a)); + + assert!(neq(&12, &10)); + assert!(!neq(&10, &10)); + assert!(neq(&a, &b)); + assert!(!neq(&a, &a)); +}