Merge pull request #3871 from pcwalton/master

rustc: Translate monomorphic intra-crate automatically-derived method…
This commit is contained in:
Patrick Walton 2012-10-29 10:37:22 -07:00
commit 17a875b08a
7 changed files with 265 additions and 32 deletions

View File

@ -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<node_id, ast_node>;

View File

@ -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");

View File

@ -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 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, _) => true,
_ => false
ast_map::node_foreign_item(_,
ast::foreign_abi_rust_intrinsic,
_) => {
must_monomorphise = true;
}
_ => {
must_monomorphise = false;
}
}
} else {
false
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.

View File

@ -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);
}

View File

@ -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()}
}

View File

@ -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));
}
}
}

View File

@ -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);
}