Skip to content

Commit a3b83c6

Browse files
committed
rustc: Translate "deriving" for monomorphic intra-crate enums. r=brson
1 parent 675c272 commit a3b83c6

File tree

2 files changed

+214
-38
lines changed

2 files changed

+214
-38
lines changed

src/rustc/middle/trans/deriving.rs

Lines changed: 182 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@
22
// enums and structs only; other types cannot be automatically derived.
33

44
use lib::llvm::llvm;
5-
use middle::trans::base::{finish_fn, get_insn_ctxt, get_item_val};
5+
use middle::trans::base::{GEP_enum, finish_fn, get_insn_ctxt, get_item_val};
66
use middle::trans::base::{new_fn_ctxt, sub_block, top_scope_block};
7-
use middle::trans::build::{Br, CondBr, GEPi, Load, PointerCast, Store};
8-
use middle::trans::build::{ValueRef};
7+
use middle::trans::build::{AddCase, Br, CondBr, GEPi, Load, PointerCast};
8+
use middle::trans::build::{Store, Switch, Unreachable, ValueRef};
99
use middle::trans::callee;
1010
use middle::trans::callee::{ArgVals, Callee, DontAutorefArg, Method};
1111
use middle::trans::callee::{MethodData};
1212
use middle::trans::common;
13-
use middle::trans::common::{C_bool, T_ptr, block, crate_ctxt};
13+
use middle::trans::common::{C_bool, C_int, T_ptr, block, crate_ctxt};
1414
use middle::trans::expr::SaveIn;
1515
use middle::trans::type_of::type_of;
16-
use middle::typeck::method_static;
16+
use middle::typeck::{method_origin, method_static};
1717
use syntax::ast;
1818
use syntax::ast::{def_id, ident, node_id, ty_param};
1919
use syntax::ast_map::path;
@@ -41,9 +41,13 @@ pub fn trans_deriving_impl(ccx: @crate_ctxt, _path: path, _name: ident,
4141
trans_deriving_struct_method(ccx, llfn, impl_def_id,
4242
self_ty.ty);
4343
}
44+
ty::ty_enum(*) => {
45+
trans_deriving_enum_method(ccx, llfn, impl_def_id,
46+
self_ty.ty);
47+
}
4448
_ => {
45-
ccx.tcx.sess.unimpl(~"translation of non-struct deriving \
46-
method");
49+
ccx.tcx.sess.bug(~"translation of non-struct deriving \
50+
method");
4751
}
4852
}
4953
}
@@ -76,40 +80,12 @@ fn trans_deriving_struct_method(ccx: @crate_ctxt, llfn: ValueRef,
7680
// Iterate over every element of the struct.
7781
for ccx.tcx.deriving_struct_methods.get(impl_did).eachi
7882
|i, derived_method_info| {
79-
let target_method_def_id;
80-
match *derived_method_info {
81-
method_static(did) => target_method_def_id = did,
82-
_ => fail ~"derived method didn't resolve to a static method"
83-
}
84-
85-
let fn_expr_ty =
86-
ty::lookup_item_type(ccx.tcx, target_method_def_id).ty;
87-
8883
let llselfval = GEPi(bcx, llselfval, [0, 0, i]);
8984
let llotherval = GEPi(bcx, llotherval, [0, 0, i]);
9085

91-
// XXX: Cross-crate won't work!
92-
let llfn = get_item_val(ccx, target_method_def_id.node);
93-
let cb: &fn(block) -> Callee = |block| {
94-
Callee {
95-
bcx: block,
96-
data: Method(MethodData {
97-
llfn: llfn,
98-
llself: llselfval,
99-
self_ty: struct_field_tys[i].mt.ty,
100-
self_mode: ast::by_copy
101-
})
102-
}
103-
};
104-
105-
bcx = callee::trans_call_inner(bcx,
106-
None,
107-
fn_expr_ty,
108-
ty::mk_bool(ccx.tcx),
109-
cb,
110-
ArgVals(~[llotherval]),
111-
SaveIn(fcx.llretptr),
112-
DontAutorefArg);
86+
let self_ty = struct_field_tys[i].mt.ty;
87+
bcx = call_substructure_method(bcx, derived_method_info, self_ty,
88+
llselfval, llotherval);
11389

11490
// Return immediately if the call returned false.
11591
let next_block = sub_block(top_bcx, ~"next");
@@ -124,3 +100,171 @@ fn trans_deriving_struct_method(ccx: @crate_ctxt, llfn: ValueRef,
124100
finish_fn(fcx, lltop);
125101
}
126102

103+
fn trans_deriving_enum_method(ccx: @crate_ctxt, llfn: ValueRef,
104+
impl_did: def_id, self_ty: ty::t) {
105+
let _icx = ccx.insn_ctxt("trans_deriving_enum_method");
106+
let fcx = new_fn_ctxt(ccx, ~[], llfn, None);
107+
let top_bcx = top_scope_block(fcx, None);
108+
let lltop = top_bcx.llbb;
109+
let mut bcx = top_bcx;
110+
111+
let llselfty = type_of(ccx, self_ty);
112+
let llselfval = PointerCast(bcx, fcx.llenv, T_ptr(llselfty));
113+
let llotherval = llvm::LLVMGetParam(llfn, 2);
114+
115+
let enum_id, enum_substs, enum_variant_infos;
116+
match ty::get(self_ty).sty {
117+
ty::ty_enum(found_enum_id, ref found_enum_substs) => {
118+
enum_id = found_enum_id;
119+
enum_substs = copy *found_enum_substs;
120+
enum_variant_infos = ty::substd_enum_variants(
121+
ccx.tcx, enum_id, &enum_substs);
122+
}
123+
_ => {
124+
ccx.tcx.sess.bug(~"passed non-enum to \
125+
trans_deriving_enum_method");
126+
}
127+
}
128+
129+
// Create the "no match" basic block. This is a basic block that does
130+
// nothing more than return false.
131+
let nomatch_bcx = sub_block(top_bcx, ~"no_match");
132+
Store(nomatch_bcx, C_bool(false), fcx.llretptr);
133+
Br(nomatch_bcx, fcx.llreturn);
134+
135+
// Create the "unreachable" basic block.
136+
let unreachable_bcx = sub_block(top_bcx, ~"unreachable");
137+
Unreachable(unreachable_bcx);
138+
139+
// Get the deriving enum method info.
140+
let deriving_enum_methods = ccx.tcx.deriving_enum_methods.get(impl_did);
141+
let n_variants = deriving_enum_methods.len();
142+
143+
if n_variants != 1 {
144+
// Grab the two discriminants.
145+
let llselfdiscrim = Load(bcx, GEPi(bcx, llselfval, [0, 0]));
146+
let llotherdiscrim = Load(bcx, GEPi(bcx, llotherval, [0, 0]));
147+
148+
// Skip over the discriminants and compute the address of the payload.
149+
let llselfpayload = GEPi(bcx, llselfval, [0, 1]);
150+
let llotherpayload = GEPi(bcx, llotherval, [0, 1]);
151+
152+
// Create basic blocks for the outer switch.
153+
let outer_bcxs = vec::from_fn(
154+
deriving_enum_methods.len(),
155+
|i| sub_block(top_bcx, fmt!("outer_%u", i)));
156+
157+
// For each basic block in the outer switch...
158+
for outer_bcxs.eachi |self_variant_index, bcx| {
159+
// Create the matching basic block for the inner switch.
160+
let top_match_bcx = sub_block(top_bcx, fmt!("maybe_match_%u",
161+
self_variant_index));
162+
let mut match_bcx = top_match_bcx;
163+
164+
// Compare each variant.
165+
for deriving_enum_methods[self_variant_index].eachi
166+
|i, derived_method_info| {
167+
let variant_def_id =
168+
enum_variant_infos[self_variant_index].id;
169+
let llselfval = GEP_enum(match_bcx, llselfpayload, enum_id,
170+
variant_def_id, enum_substs.tps, i);
171+
let llotherval = GEP_enum(match_bcx, llotherpayload,
172+
enum_id, variant_def_id,
173+
enum_substs.tps, i);
174+
175+
let self_ty = enum_variant_infos[self_variant_index].args[i];
176+
match_bcx = call_substructure_method(match_bcx,
177+
derived_method_info,
178+
self_ty,
179+
llselfval,
180+
llotherval);
181+
182+
// Return immediately if the call to the substructure returned
183+
// false.
184+
let next_bcx = sub_block(
185+
top_bcx, fmt!("next_%u_%u", self_variant_index, i));
186+
let llcond = Load(match_bcx, fcx.llretptr);
187+
CondBr(match_bcx, llcond, next_bcx.llbb, fcx.llreturn);
188+
match_bcx = next_bcx;
189+
}
190+
191+
// Finish up the matching block.
192+
Store(match_bcx, C_bool(true), fcx.llretptr);
193+
Br(match_bcx, fcx.llreturn);
194+
195+
// Build the inner switch.
196+
let llswitch = Switch(
197+
*bcx, llotherdiscrim, unreachable_bcx.llbb, n_variants);
198+
for uint::range(0, n_variants) |other_variant_index| {
199+
let discriminant =
200+
enum_variant_infos[other_variant_index].disr_val;
201+
if self_variant_index == other_variant_index {
202+
// This is the potentially-matching case.
203+
AddCase(llswitch,
204+
C_int(ccx, discriminant),
205+
top_match_bcx.llbb);
206+
} else {
207+
// This is always a non-matching case.
208+
AddCase(llswitch,
209+
C_int(ccx, discriminant),
210+
nomatch_bcx.llbb);
211+
}
212+
}
213+
}
214+
215+
// Now build the outer switch.
216+
let llswitch = Switch(top_bcx, llselfdiscrim, unreachable_bcx.llbb,
217+
n_variants);
218+
for outer_bcxs.eachi |self_variant_index, outer_bcx| {
219+
let discriminant =
220+
enum_variant_infos[self_variant_index].disr_val;
221+
AddCase(llswitch, C_int(ccx, discriminant), outer_bcx.llbb);
222+
}
223+
} else {
224+
ccx.tcx.sess.unimpl(~"degenerate enum deriving");
225+
}
226+
227+
// Finish up the function.
228+
finish_fn(fcx, lltop);
229+
}
230+
231+
fn call_substructure_method(bcx: block,
232+
derived_method_info: &method_origin,
233+
self_ty: ty::t,
234+
llselfval: ValueRef,
235+
llotherval: ValueRef) -> block {
236+
let fcx = bcx.fcx;
237+
let ccx = fcx.ccx;
238+
239+
let target_method_def_id;
240+
match *derived_method_info {
241+
method_static(did) => target_method_def_id = did,
242+
_ => fail ~"derived method didn't resolve to a static method"
243+
}
244+
245+
let fn_expr_ty = ty::lookup_item_type(ccx.tcx, target_method_def_id).ty;
246+
247+
// XXX: Cross-crate won't work!
248+
let llfn = get_item_val(ccx, target_method_def_id.node);
249+
let cb: &fn(block) -> Callee = |block| {
250+
Callee {
251+
bcx: block,
252+
data: Method(MethodData {
253+
llfn: llfn,
254+
llself: llselfval,
255+
self_ty: self_ty,
256+
self_mode: ast::by_copy
257+
})
258+
}
259+
};
260+
261+
callee::trans_call_inner(bcx,
262+
None,
263+
fn_expr_ty,
264+
ty::mk_bool(ccx.tcx),
265+
cb,
266+
ArgVals(~[llotherval]),
267+
SaveIn(fcx.llretptr),
268+
DontAutorefArg)
269+
}
270+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
trait MyEq {
2+
pure fn eq(other: &self) -> bool;
3+
}
4+
5+
struct A {
6+
x: int
7+
}
8+
9+
enum B {
10+
C(A),
11+
D(A),
12+
E(A)
13+
}
14+
15+
impl A : MyEq {
16+
pure fn eq(other: &A) -> bool {
17+
unsafe { io::println("in eq"); }
18+
self.x == other.x
19+
}
20+
}
21+
22+
impl B : MyEq;
23+
24+
fn main() {
25+
let c = C(A { x: 15 });
26+
let d = D(A { x: 30 });
27+
let e = C(A { x: 30 });
28+
assert c.eq(&c);
29+
assert !c.eq(&d);
30+
assert !c.eq(&e);
31+
}
32+

0 commit comments

Comments
 (0)