diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map.rs index e63de5aefebe3..38ccb64899ca9 100644 --- a/src/libsyntax/ast_map.rs +++ b/src/libsyntax/ast_map.rs @@ -74,7 +74,7 @@ enum ast_node { // Destructor for a class node_dtor(~[ty_param], @class_dtor, def_id, @path), node_block(blk), - node_struct_ctor(@struct_def, @item, @path) + node_struct_ctor(@struct_def, @item, @path), } type map = std::map::HashMap; diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index 822fb4f417a8e..40792ce45ed4f 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -42,6 +42,7 @@ use util::ppaux; use util::ppaux::{ty_to_str, ty_to_short_str}; use syntax::diagnostic::expect; use util::common::indenter; +use ty::DerivedMethodInfo; use build::*; use shape::*; @@ -1843,7 +1844,7 @@ fn trans_item(ccx: @crate_ctxt, item: ast::item) { match ms_opt { None => { deriving::trans_deriving_impl(ccx, *path, item.ident, tps, - None, item.id); + item.id); } Some(ms) => { meth::trans_impl(ccx, *path, item.ident, ms, tps, None, @@ -2079,6 +2080,20 @@ fn get_item_val(ccx: @crate_ctxt, id: ast::node_id) -> ValueRef { match ccx.item_vals.find(id) { Some(v) => v, None => { + // First, check whether we need to automatically generate a method + // via the deriving mechanism. + match ccx.tcx.automatically_derived_methods.find(local_def(id)) { + None => {} // Continue. + Some(ref derived_method_info) => { + // XXX: Mark as internal if necessary. + let llfn = register_deriving_method( + ccx, id, derived_method_info); + ccx.item_vals.insert(id, llfn); + return llfn; + } + } + + // Failing that, look for an item. let mut exprt = false; let val = match ccx.tcx.items.get(id) { ast_map::node_item(i, pth) => { @@ -2226,6 +2241,32 @@ fn register_method(ccx: @crate_ctxt, id: ast::node_id, pth: @ast_map::path, llfn } +fn register_deriving_method(ccx: @crate_ctxt, + id: ast::node_id, + derived_method_info: &DerivedMethodInfo) -> + ValueRef { + // Find the path of the item. + let path, span; + match ccx.tcx.items.get(derived_method_info.containing_impl.node) { + ast_map::node_item(item, found_path) => { + path = found_path; + span = item.span; + } + _ => { + ccx.tcx.sess.bug(~"derived method info containing impl didn't \ + refer to an item"); + } + } + + let path = vec::append(*path, ~[ + ast_map::path_name(derived_method_info.method_info.ident) + ]); + let mty = ty::lookup_item_type(ccx.tcx, local_def(id)).ty; + let llfn = register_fn_full(ccx, span, path, id, mty); + // XXX: Inline hint. + llfn +} + // The constant translation pass. fn trans_constant(ccx: @crate_ctxt, it: @ast::item) { let _icx = ccx.insn_ctxt("trans_constant"); diff --git a/src/rustc/middle/trans/callee.rs b/src/rustc/middle/trans/callee.rs index 2c1e9b21c6d4f..bd1facdddce70 100644 --- a/src/rustc/middle/trans/callee.rs +++ b/src/rustc/middle/trans/callee.rs @@ -210,23 +210,30 @@ fn trans_fn_ref_with_vtables( // intrinsic, or is a default method. In particular, if we see an // intrinsic that is inlined from a different crate, we want to reemit the // intrinsic instead of trying to call it in the other crate. - let must_monomorphise = type_params.len() > 0 || - opt_impl_did.is_some() || { - if def_id.crate == ast::local_crate { - let map_node = session::expect( - ccx.sess, - ccx.tcx.items.find(def_id.node), - || fmt!("local item should be in ast map")); - - match map_node { - ast_map::node_foreign_item( - _, ast::foreign_abi_rust_intrinsic, _) => true, - _ => false + let must_monomorphise; + if type_params.len() > 0 || opt_impl_did.is_some() { + must_monomorphise = true; + } else if ccx.tcx.automatically_derived_methods.contains_key(def_id) { + must_monomorphise = false; + } else if def_id.crate == ast::local_crate { + let map_node = session::expect( + ccx.sess, + ccx.tcx.items.find(def_id.node), + || fmt!("local item should be in ast map")); + + match map_node { + ast_map::node_foreign_item(_, + ast::foreign_abi_rust_intrinsic, + _) => { + must_monomorphise = true; + } + _ => { + must_monomorphise = false; } - } else { - false } - }; + } else { + must_monomorphise = false; + } // Create a monomorphic verison of generic functions if must_monomorphise { @@ -434,6 +441,15 @@ fn trans_call_inner( _ => {} } + // Uncomment this to debug calls. + /* + io::println(fmt!("calling: %s", bcx.val_str(llfn))); + for llargs.each |llarg| { + io::println(fmt!("arg: %s", bcx.val_str(*llarg))); + } + io::println("---"); + */ + // If the block is terminated, then one or more of the args // has type _|_. Since that means it diverges, the code for // the call itself is unreachable. diff --git a/src/rustc/middle/trans/deriving.rs b/src/rustc/middle/trans/deriving.rs index 5ef412888b9cc..0e4a72f563d31 100644 --- a/src/rustc/middle/trans/deriving.rs +++ b/src/rustc/middle/trans/deriving.rs @@ -1,21 +1,126 @@ // Translation of automatically-derived trait implementations. This handles // enums and structs only; other types cannot be automatically derived. -use middle::trans::base::get_insn_ctxt; -use middle::trans::common::crate_ctxt; -use syntax::ast::{ident, node_id, ty_param}; +use lib::llvm::llvm; +use middle::trans::base::{finish_fn, get_insn_ctxt, get_item_val}; +use middle::trans::base::{new_fn_ctxt, sub_block, top_scope_block}; +use middle::trans::build::{Br, CondBr, GEPi, Load, PointerCast, Store}; +use middle::trans::build::{ValueRef}; +use middle::trans::callee; +use middle::trans::callee::{ArgVals, Callee, DontAutorefArg, Method}; +use middle::trans::callee::{MethodData}; +use middle::trans::common; +use middle::trans::common::{C_bool, T_ptr, block, crate_ctxt}; +use middle::trans::expr::SaveIn; +use middle::trans::type_of::type_of; +use middle::typeck::method_static; +use syntax::ast; +use syntax::ast::{def_id, ident, node_id, ty_param}; use syntax::ast_map::path; +use syntax::ast_util; +use syntax::ast_util::local_def; /// The main "translation" pass for automatically-derived impls. Generates /// code for monomorphic methods only. Other methods will be generated when /// they are invoked with specific type parameters; see /// `trans::base::lval_static_fn()` or `trans::base::monomorphic_fn()`. pub fn trans_deriving_impl(ccx: @crate_ctxt, _path: path, _name: ident, - tps: ~[ty_param], _self_ty: Option, - _id: node_id) { + tps: ~[ty_param], id: node_id) { let _icx = ccx.insn_ctxt("deriving::trans_deriving_impl"); if tps.len() > 0 { return; } - // XXX: Unimplemented. + let impl_def_id = local_def(id); + let self_ty = ty::lookup_item_type(ccx.tcx, impl_def_id); + let method_dids = ccx.tcx.automatically_derived_methods_for_impl.get( + impl_def_id); + + for method_dids.each |method_did| { + let llfn = get_item_val(ccx, method_did.node); + match ty::get(self_ty.ty).sty { + ty::ty_class(*) => { + trans_deriving_struct_method(ccx, llfn, impl_def_id, + self_ty.ty); + } + _ => { + ccx.tcx.sess.unimpl(~"translation of non-struct deriving \ + method"); + } + } + } +} + +fn trans_deriving_struct_method(ccx: @crate_ctxt, llfn: ValueRef, + impl_did: def_id, self_ty: ty::t) { + let _icx = ccx.insn_ctxt("trans_deriving_struct_method"); + let fcx = new_fn_ctxt(ccx, ~[], llfn, None); + let top_bcx = top_scope_block(fcx, None); + let lltop = top_bcx.llbb; + let mut bcx = top_bcx; + + let llselfty = type_of(ccx, self_ty); + let llselfval = PointerCast(bcx, fcx.llenv, T_ptr(llselfty)); + let llotherval = llvm::LLVMGetParam(llfn, 2); + + let struct_field_tys; + match ty::get(self_ty).sty { + ty::ty_class(struct_id, ref struct_substs) => { + struct_field_tys = ty::class_items_as_fields( + ccx.tcx, struct_id, struct_substs); + } + _ => { + ccx.tcx.sess.bug(~"passed non-struct to \ + trans_deriving_struct_method"); + } + } + + // Iterate over every element of the struct. + for ccx.tcx.deriving_struct_methods.get(impl_did).eachi + |i, derived_method_info| { + let target_method_def_id; + match *derived_method_info { + method_static(did) => target_method_def_id = did, + _ => fail ~"derived method didn't resolve to a static method" + } + + let fn_expr_ty = + ty::lookup_item_type(ccx.tcx, target_method_def_id).ty; + + let llselfval = GEPi(bcx, llselfval, [0, 0, i]); + let llotherval = GEPi(bcx, llotherval, [0, 0, i]); + + // XXX: Cross-crate won't work! + let llfn = get_item_val(ccx, target_method_def_id.node); + let cb: &fn(block) -> Callee = |block| { + Callee { + bcx: block, + data: Method(MethodData { + llfn: llfn, + llself: llselfval, + self_ty: struct_field_tys[i].mt.ty, + self_mode: ast::by_copy + }) + } + }; + + bcx = callee::trans_call_inner(bcx, + None, + fn_expr_ty, + ty::mk_bool(ccx.tcx), + cb, + ArgVals(~[llotherval]), + SaveIn(fcx.llretptr), + DontAutorefArg); + + // Return immediately if the call returned false. + let next_block = sub_block(top_bcx, ~"next"); + let llcond = Load(bcx, fcx.llretptr); + CondBr(bcx, llcond, next_block.llbb, fcx.llreturn); + bcx = next_block; + } + + Store(bcx, C_bool(true), fcx.llretptr); + Br(bcx, fcx.llreturn); + + finish_fn(fcx, lltop); } diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index d6364e5515ed1..8af38e96999f3 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -200,6 +200,7 @@ export provided_trait_methods; export trait_supertraits; export AutoAdjustment; export AutoRef, AutoRefKind, AutoSlice, AutoPtr; +export DerivedMethodInfo; // Data types @@ -333,6 +334,11 @@ struct InstantiatedTraitRef { tpt: ty_param_substs_and_ty } +struct DerivedMethodInfo { + method_info: @middle::resolve::MethodInfo, + containing_impl: ast::def_id +} + type ctxt = @{diag: syntax::diagnostic::span_handler, interner: HashMap, @@ -379,7 +385,17 @@ type ctxt = provided_method_sources: HashMap, supertraits: HashMap, deriving_struct_methods: HashMap}; + @~[typeck::method_origin]>, + + // A mapping from the def ID of a method that was automatically derived + // to information about it. + automatically_derived_methods: HashMap, + + // A mapping from the def ID of an impl to the IDs of the derived + // methods within it. + automatically_derived_methods_for_impl: + HashMap + }; enum tbox_flag { has_params = 1, @@ -942,7 +958,9 @@ fn mk_ctxt(s: session::Session, legacy_boxed_traits: HashMap(), provided_method_sources: HashMap(), supertraits: HashMap(), - deriving_struct_methods: HashMap()} + deriving_struct_methods: HashMap(), + automatically_derived_methods: HashMap(), + automatically_derived_methods_for_impl: HashMap()} } diff --git a/src/rustc/middle/typeck/coherence.rs b/src/rustc/middle/typeck/coherence.rs index b6b7a4c5f3486..d0ec2b3cfc554 100644 --- a/src/rustc/middle/typeck/coherence.rs +++ b/src/rustc/middle/typeck/coherence.rs @@ -9,10 +9,10 @@ use metadata::csearch::{get_impls_for_mod}; use metadata::cstore::{CStore, iter_crate_data}; use metadata::decoder::{dl_def, dl_field, dl_impl}; use middle::resolve::{Impl, MethodInfo}; -use middle::ty::{ProvidedMethodSource, get, lookup_item_type, subst, t}; -use middle::ty::{ty_box, ty_uniq, ty_ptr, ty_rptr, ty_enum}; -use middle::ty::{ty_class, ty_nil, ty_bot, ty_bool, ty_int, ty_uint}; -use middle::ty::{ty_float, ty_estr, ty_evec, ty_rec}; +use middle::ty::{DerivedMethodInfo, ProvidedMethodSource, get}; +use middle::ty::{lookup_item_type, subst, t, ty_bot, ty_box, ty_class}; +use middle::ty::{ty_bool, ty_enum, ty_int, ty_nil, ty_ptr, ty_rptr, ty_uint}; +use middle::ty::{ty_float, ty_estr, ty_evec, ty_rec, ty_uniq}; use middle::ty::{ty_fn, ty_trait, ty_tup, ty_infer}; use middle::ty::{ty_param, ty_self, ty_type, ty_opaque_box}; use middle::ty::{ty_opaque_closure_ptr, ty_unboxed_vec, type_is_ty_var}; @@ -592,17 +592,33 @@ impl CoherenceChecker { } fn add_automatically_derived_methods_from_trait( - all_methods: &mut ~[@MethodInfo], trait_did: def_id, self_ty: ty::t) { + all_methods: &mut ~[@MethodInfo], + trait_did: def_id, + self_ty: ty::t, + impl_did: def_id) { let tcx = self.crate_context.tcx; + let new_method_dids = dvec::DVec(); for (*ty::trait_methods(tcx, trait_did)).each |method| { // Generate a def ID for each node. let new_def_id = local_def(tcx.sess.next_node_id()); - all_methods.push(@{ + let method_info = @{ did: new_def_id, n_tps: method.tps.len(), ident: method.ident, self_type: method.self_ty - }); + }; + all_methods.push(method_info); + + // Note that this method was automatically derived so that trans + // can handle it differently. + let derived_method_info = DerivedMethodInfo { + method_info: method_info, + containing_impl: impl_did + }; + tcx.automatically_derived_methods.insert(new_def_id, + derived_method_info); + + new_method_dids.push(new_def_id); // Additionally, generate the type for the derived method and add // it to the type cache. @@ -615,6 +631,10 @@ impl CoherenceChecker { ty: ty::subst(tcx, &substs, ty::mk_fn(tcx, method.fty)) }); } + + let new_method_dids = @dvec::unwrap(move new_method_dids); + tcx.automatically_derived_methods_for_impl.insert(impl_did, + new_method_dids); } // Converts an implementation in the AST to an Impl structure. @@ -651,7 +671,8 @@ impl CoherenceChecker { let trait_did = self.trait_ref_to_trait_def_id(*trait_ref); self.add_automatically_derived_methods_from_trait( - &mut methods, trait_did, self_ty.ty); + &mut methods, trait_did, self_ty.ty, + local_def(item.id)); } } } diff --git a/src/test/run-pass/deriving-simple.rs b/src/test/run-pass/deriving-simple.rs new file mode 100644 index 0000000000000..04167893507a9 --- /dev/null +++ b/src/test/run-pass/deriving-simple.rs @@ -0,0 +1,32 @@ +trait MyEq { + pure fn eq(other: &self) -> bool; +} + +struct A { + x: int +} + +struct B { + x: A, + y: A, + z: A +} + +impl A : MyEq { + pure fn eq(other: &A) -> bool { + unsafe { io::println(fmt!("eq %d %d", self.x, other.x)); } + self.x == other.x + } +} + +impl B : MyEq; + +fn main() { + let b = B { x: A { x: 1 }, y: A { x: 2 }, z: A { x: 3 } }; + let c = B { x: A { x: 1 }, y: A { x: 3 }, z: A { x: 4 } }; + assert b.eq(&b); + assert c.eq(&c); + assert !b.eq(&c); + assert !c.eq(&b); +} +