summary refs log tree commit diff
path: root/src/rustc
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2012-10-29 10:37:22 -0700
committerPatrick Walton <pcwalton@mimiga.net>2012-10-29 10:37:22 -0700
commit17a875b08a0a607a97fff0e4fd13be0d2fa4dcb3 (patch)
treee27b4900297909fa29c864fae85a1ab44c99d41e /src/rustc
parent5e5ea0460879972dd9b78516cdee2e65be7fad93 (diff)
parentd5a27a0e0c6e64d1532443a6bd45b3416e7597fe (diff)
downloadrust-17a875b08a0a607a97fff0e4fd13be0d2fa4dcb3.tar.gz
rust-17a875b08a0a607a97fff0e4fd13be0d2fa4dcb3.zip
Merge pull request #3871 from pcwalton/master
 	rustc: Translate monomorphic intra-crate automatically-derived method… 
Diffstat (limited to 'src/rustc')
-rw-r--r--src/rustc/middle/trans/base.rs43
-rw-r--r--src/rustc/middle/trans/callee.rs46
-rw-r--r--src/rustc/middle/trans/deriving.rs117
-rw-r--r--src/rustc/middle/ty.rs22
-rw-r--r--src/rustc/middle/typeck/coherence.rs37
5 files changed, 233 insertions, 32 deletions
diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs
index 822fb4f417a..40792ce45ed 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 2c1e9b21c6d..bd1facdddce 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 5ef412888b9..0e4a72f563d 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<ty::t>,
-                           _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 d6364e5515e..8af38e96999 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<intern_key, t_box>,
@@ -379,7 +385,17 @@ type ctxt =
       provided_method_sources: HashMap<ast::def_id, ProvidedMethodSource>,
       supertraits: HashMap<ast::def_id, @~[InstantiatedTraitRef]>,
       deriving_struct_methods: HashMap<ast::def_id,
-                                       @~[typeck::method_origin]>};
+                                       @~[typeck::method_origin]>,
+
+      // A mapping from the def ID of a method that was automatically derived
+      // to information about it.
+      automatically_derived_methods: HashMap<ast::def_id, DerivedMethodInfo>,
+
+      // A mapping from the def ID of an impl to the IDs of the derived
+      // methods within it.
+      automatically_derived_methods_for_impl:
+            HashMap<ast::def_id, @~[ast::def_id]>
+      };
 
 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 b6b7a4c5f34..d0ec2b3cfc5 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));
                         }
                     }
                 }