mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 08:44:35 +00:00
Merge pull request #3871 from pcwalton/master
rustc: Translate monomorphic intra-crate automatically-derived method…
This commit is contained in:
commit
17a875b08a
@ -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>;
|
||||
|
@ -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");
|
||||
|
@ -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"));
|
||||
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
|
||||
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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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()}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
32
src/test/run-pass/deriving-simple.rs
Normal file
32
src/test/run-pass/deriving-simple.rs
Normal 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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user