rustc: Translate "deriving" for monomorphic intra-crate enums. r=brson
This commit is contained in:
parent
675c272dad
commit
a3b83c6224
|
@ -2,18 +2,18 @@
|
|||
// enums and structs only; other types cannot be automatically derived.
|
||||
|
||||
use lib::llvm::llvm;
|
||||
use middle::trans::base::{finish_fn, get_insn_ctxt, get_item_val};
|
||||
use middle::trans::base::{GEP_enum, 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::build::{AddCase, Br, CondBr, GEPi, Load, PointerCast};
|
||||
use middle::trans::build::{Store, Switch, Unreachable, 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::common::{C_bool, C_int, T_ptr, block, crate_ctxt};
|
||||
use middle::trans::expr::SaveIn;
|
||||
use middle::trans::type_of::type_of;
|
||||
use middle::typeck::method_static;
|
||||
use middle::typeck::{method_origin, method_static};
|
||||
use syntax::ast;
|
||||
use syntax::ast::{def_id, ident, node_id, ty_param};
|
||||
use syntax::ast_map::path;
|
||||
|
@ -41,9 +41,13 @@ pub fn trans_deriving_impl(ccx: @crate_ctxt, _path: path, _name: ident,
|
|||
trans_deriving_struct_method(ccx, llfn, impl_def_id,
|
||||
self_ty.ty);
|
||||
}
|
||||
ty::ty_enum(*) => {
|
||||
trans_deriving_enum_method(ccx, llfn, impl_def_id,
|
||||
self_ty.ty);
|
||||
}
|
||||
_ => {
|
||||
ccx.tcx.sess.unimpl(~"translation of non-struct deriving \
|
||||
method");
|
||||
ccx.tcx.sess.bug(~"translation of non-struct deriving \
|
||||
method");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -76,40 +80,12 @@ fn trans_deriving_struct_method(ccx: @crate_ctxt, llfn: ValueRef,
|
|||
// 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);
|
||||
let self_ty = struct_field_tys[i].mt.ty;
|
||||
bcx = call_substructure_method(bcx, derived_method_info, self_ty,
|
||||
llselfval, llotherval);
|
||||
|
||||
// Return immediately if the call returned false.
|
||||
let next_block = sub_block(top_bcx, ~"next");
|
||||
|
@ -124,3 +100,171 @@ fn trans_deriving_struct_method(ccx: @crate_ctxt, llfn: ValueRef,
|
|||
finish_fn(fcx, lltop);
|
||||
}
|
||||
|
||||
fn trans_deriving_enum_method(ccx: @crate_ctxt, llfn: ValueRef,
|
||||
impl_did: def_id, self_ty: ty::t) {
|
||||
let _icx = ccx.insn_ctxt("trans_deriving_enum_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 enum_id, enum_substs, enum_variant_infos;
|
||||
match ty::get(self_ty).sty {
|
||||
ty::ty_enum(found_enum_id, ref found_enum_substs) => {
|
||||
enum_id = found_enum_id;
|
||||
enum_substs = copy *found_enum_substs;
|
||||
enum_variant_infos = ty::substd_enum_variants(
|
||||
ccx.tcx, enum_id, &enum_substs);
|
||||
}
|
||||
_ => {
|
||||
ccx.tcx.sess.bug(~"passed non-enum to \
|
||||
trans_deriving_enum_method");
|
||||
}
|
||||
}
|
||||
|
||||
// Create the "no match" basic block. This is a basic block that does
|
||||
// nothing more than return false.
|
||||
let nomatch_bcx = sub_block(top_bcx, ~"no_match");
|
||||
Store(nomatch_bcx, C_bool(false), fcx.llretptr);
|
||||
Br(nomatch_bcx, fcx.llreturn);
|
||||
|
||||
// Create the "unreachable" basic block.
|
||||
let unreachable_bcx = sub_block(top_bcx, ~"unreachable");
|
||||
Unreachable(unreachable_bcx);
|
||||
|
||||
// Get the deriving enum method info.
|
||||
let deriving_enum_methods = ccx.tcx.deriving_enum_methods.get(impl_did);
|
||||
let n_variants = deriving_enum_methods.len();
|
||||
|
||||
if n_variants != 1 {
|
||||
// Grab the two discriminants.
|
||||
let llselfdiscrim = Load(bcx, GEPi(bcx, llselfval, [0, 0]));
|
||||
let llotherdiscrim = Load(bcx, GEPi(bcx, llotherval, [0, 0]));
|
||||
|
||||
// Skip over the discriminants and compute the address of the payload.
|
||||
let llselfpayload = GEPi(bcx, llselfval, [0, 1]);
|
||||
let llotherpayload = GEPi(bcx, llotherval, [0, 1]);
|
||||
|
||||
// Create basic blocks for the outer switch.
|
||||
let outer_bcxs = vec::from_fn(
|
||||
deriving_enum_methods.len(),
|
||||
|i| sub_block(top_bcx, fmt!("outer_%u", i)));
|
||||
|
||||
// For each basic block in the outer switch...
|
||||
for outer_bcxs.eachi |self_variant_index, bcx| {
|
||||
// Create the matching basic block for the inner switch.
|
||||
let top_match_bcx = sub_block(top_bcx, fmt!("maybe_match_%u",
|
||||
self_variant_index));
|
||||
let mut match_bcx = top_match_bcx;
|
||||
|
||||
// Compare each variant.
|
||||
for deriving_enum_methods[self_variant_index].eachi
|
||||
|i, derived_method_info| {
|
||||
let variant_def_id =
|
||||
enum_variant_infos[self_variant_index].id;
|
||||
let llselfval = GEP_enum(match_bcx, llselfpayload, enum_id,
|
||||
variant_def_id, enum_substs.tps, i);
|
||||
let llotherval = GEP_enum(match_bcx, llotherpayload,
|
||||
enum_id, variant_def_id,
|
||||
enum_substs.tps, i);
|
||||
|
||||
let self_ty = enum_variant_infos[self_variant_index].args[i];
|
||||
match_bcx = call_substructure_method(match_bcx,
|
||||
derived_method_info,
|
||||
self_ty,
|
||||
llselfval,
|
||||
llotherval);
|
||||
|
||||
// Return immediately if the call to the substructure returned
|
||||
// false.
|
||||
let next_bcx = sub_block(
|
||||
top_bcx, fmt!("next_%u_%u", self_variant_index, i));
|
||||
let llcond = Load(match_bcx, fcx.llretptr);
|
||||
CondBr(match_bcx, llcond, next_bcx.llbb, fcx.llreturn);
|
||||
match_bcx = next_bcx;
|
||||
}
|
||||
|
||||
// Finish up the matching block.
|
||||
Store(match_bcx, C_bool(true), fcx.llretptr);
|
||||
Br(match_bcx, fcx.llreturn);
|
||||
|
||||
// Build the inner switch.
|
||||
let llswitch = Switch(
|
||||
*bcx, llotherdiscrim, unreachable_bcx.llbb, n_variants);
|
||||
for uint::range(0, n_variants) |other_variant_index| {
|
||||
let discriminant =
|
||||
enum_variant_infos[other_variant_index].disr_val;
|
||||
if self_variant_index == other_variant_index {
|
||||
// This is the potentially-matching case.
|
||||
AddCase(llswitch,
|
||||
C_int(ccx, discriminant),
|
||||
top_match_bcx.llbb);
|
||||
} else {
|
||||
// This is always a non-matching case.
|
||||
AddCase(llswitch,
|
||||
C_int(ccx, discriminant),
|
||||
nomatch_bcx.llbb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now build the outer switch.
|
||||
let llswitch = Switch(top_bcx, llselfdiscrim, unreachable_bcx.llbb,
|
||||
n_variants);
|
||||
for outer_bcxs.eachi |self_variant_index, outer_bcx| {
|
||||
let discriminant =
|
||||
enum_variant_infos[self_variant_index].disr_val;
|
||||
AddCase(llswitch, C_int(ccx, discriminant), outer_bcx.llbb);
|
||||
}
|
||||
} else {
|
||||
ccx.tcx.sess.unimpl(~"degenerate enum deriving");
|
||||
}
|
||||
|
||||
// Finish up the function.
|
||||
finish_fn(fcx, lltop);
|
||||
}
|
||||
|
||||
fn call_substructure_method(bcx: block,
|
||||
derived_method_info: &method_origin,
|
||||
self_ty: ty::t,
|
||||
llselfval: ValueRef,
|
||||
llotherval: ValueRef) -> block {
|
||||
let fcx = bcx.fcx;
|
||||
let ccx = fcx.ccx;
|
||||
|
||||
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;
|
||||
|
||||
// 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: self_ty,
|
||||
self_mode: ast::by_copy
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
callee::trans_call_inner(bcx,
|
||||
None,
|
||||
fn_expr_ty,
|
||||
ty::mk_bool(ccx.tcx),
|
||||
cb,
|
||||
ArgVals(~[llotherval]),
|
||||
SaveIn(fcx.llretptr),
|
||||
DontAutorefArg)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
trait MyEq {
|
||||
pure fn eq(other: &self) -> bool;
|
||||
}
|
||||
|
||||
struct A {
|
||||
x: int
|
||||
}
|
||||
|
||||
enum B {
|
||||
C(A),
|
||||
D(A),
|
||||
E(A)
|
||||
}
|
||||
|
||||
impl A : MyEq {
|
||||
pure fn eq(other: &A) -> bool {
|
||||
unsafe { io::println("in eq"); }
|
||||
self.x == other.x
|
||||
}
|
||||
}
|
||||
|
||||
impl B : MyEq;
|
||||
|
||||
fn main() {
|
||||
let c = C(A { x: 15 });
|
||||
let d = D(A { x: 30 });
|
||||
let e = C(A { x: 30 });
|
||||
assert c.eq(&c);
|
||||
assert !c.eq(&d);
|
||||
assert !c.eq(&e);
|
||||
}
|
||||
|
Loading…
Reference in New Issue