Skip to content

Commit db9f8db

Browse files
committed
librustc: Implement deriving with a unit return type. r=tjc
1 parent c6d0117 commit db9f8db

File tree

5 files changed

+235
-67
lines changed

5 files changed

+235
-67
lines changed

src/librustc/middle/trans/base.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2275,6 +2275,8 @@ fn register_deriving_method(ccx: @crate_ctxt,
22752275
}
22762276

22772277
let path = vec::append(*path, ~[
2278+
ast_map::path_mod(
2279+
ccx.sess.parse_sess.interner.intern(@fmt!("__derived%d__", id))),
22782280
ast_map::path_name(derived_method_info.method_info.ident)
22792281
]);
22802282
let mty = ty::lookup_item_type(ccx.tcx, local_def(id)).ty;

src/librustc/middle/trans/deriving.rs

Lines changed: 160 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,38 @@ use syntax::ast_map::path;
2121
use syntax::ast_util;
2222
use syntax::ast_util::local_def;
2323

24+
/// The kind of deriving method this is.
25+
enum DerivingKind {
26+
BoolKind, // fn f(&self, other: &other) -> bool
27+
UnitKind, // fn f(&self) -> ()
28+
}
29+
30+
impl DerivingKind {
31+
static fn of_item(ccx: @crate_ctxt, method_did: ast::def_id)
32+
-> DerivingKind {
33+
let item_type = ty::lookup_item_type(ccx.tcx, method_did).ty;
34+
match ty::get(item_type).sty {
35+
ty::ty_fn(ref f) => {
36+
match ty::get(f.sig.output).sty {
37+
ty::ty_bool => BoolKind,
38+
ty::ty_nil => UnitKind,
39+
_ => {
40+
// FIXME (#3957): Report this earlier.
41+
ccx.tcx.sess.fatal(~"attempt to automatically derive \
42+
derive an implementation of a \
43+
function returning something \
44+
other than bool or ()");
45+
}
46+
}
47+
}
48+
_ => {
49+
ccx.tcx.sess.bug(~"DerivingKind::of_item(): method def ID \
50+
didn't have a function type");
51+
}
52+
}
53+
}
54+
}
55+
2456
/// The main "translation" pass for automatically-derived impls. Generates
2557
/// code for monomorphic methods only. Other methods will be generated when
2658
/// they are invoked with specific type parameters; see
@@ -36,15 +68,16 @@ pub fn trans_deriving_impl(ccx: @crate_ctxt, _path: path, _name: ident,
3668
impl_def_id);
3769

3870
for method_dids.each |method_did| {
71+
let kind = DerivingKind::of_item(ccx, *method_did);
3972
let llfn = get_item_val(ccx, method_did.node);
4073
match ty::get(self_ty.ty).sty {
4174
ty::ty_class(*) => {
4275
trans_deriving_struct_method(ccx, llfn, impl_def_id,
43-
self_ty.ty);
76+
self_ty.ty, kind);
4477
}
4578
ty::ty_enum(*) => {
4679
trans_deriving_enum_method(ccx, llfn, impl_def_id,
47-
self_ty.ty);
80+
self_ty.ty, kind);
4881
}
4982
_ => {
5083
ccx.tcx.sess.bug(~"translation of non-struct deriving \
@@ -54,8 +87,11 @@ pub fn trans_deriving_impl(ccx: @crate_ctxt, _path: path, _name: ident,
5487
}
5588
}
5689

57-
fn trans_deriving_struct_method(ccx: @crate_ctxt, llfn: ValueRef,
58-
impl_did: def_id, self_ty: ty::t) {
90+
fn trans_deriving_struct_method(ccx: @crate_ctxt,
91+
llfn: ValueRef,
92+
impl_did: def_id,
93+
self_ty: ty::t,
94+
kind: DerivingKind) {
5995
let _icx = ccx.insn_ctxt("trans_deriving_struct_method");
6096
let fcx = new_fn_ctxt(ccx, ~[], llfn, None);
6197
let top_bcx = top_scope_block(fcx, None);
@@ -64,7 +100,14 @@ fn trans_deriving_struct_method(ccx: @crate_ctxt, llfn: ValueRef,
64100

65101
let llselfty = type_of(ccx, self_ty);
66102
let llselfval = PointerCast(bcx, fcx.llenv, T_ptr(llselfty));
67-
let llotherval = llvm::LLVMGetParam(llfn, 2);
103+
104+
// If there is an "other" value, then get it. The "other" value is the
105+
// value we're comparing against in the case of Eq and Ord.
106+
let llotherval_opt;
107+
match kind {
108+
BoolKind => llotherval_opt = Some(llvm::LLVMGetParam(llfn, 2)),
109+
UnitKind => llotherval_opt = None
110+
}
68111

69112
let struct_field_tys;
70113
match ty::get(self_ty).sty {
@@ -82,27 +125,45 @@ fn trans_deriving_struct_method(ccx: @crate_ctxt, llfn: ValueRef,
82125
for ccx.tcx.deriving_struct_methods.get(impl_did).eachi
83126
|i, derived_method_info| {
84127
let llselfval = GEPi(bcx, llselfval, [0, 0, i]);
85-
let llotherval = GEPi(bcx, llotherval, [0, 0, i]);
128+
129+
let llotherval_opt = llotherval_opt.map(
130+
|llotherval| GEPi(bcx, *llotherval, [0, 0, i]));
86131

87132
let self_ty = struct_field_tys[i].mt.ty;
88133
bcx = call_substructure_method(bcx, derived_method_info, self_ty,
89-
llselfval, llotherval);
134+
llselfval, llotherval_opt);
135+
136+
// If this derived method is of boolean kind, return immediately if
137+
// the call to the substructure method returned false.
138+
match kind {
139+
BoolKind => {
140+
let next_block = sub_block(top_bcx, ~"next");
141+
let llcond = Load(bcx, fcx.llretptr);
142+
CondBr(bcx, llcond, next_block.llbb, fcx.llreturn);
143+
bcx = next_block;
144+
}
145+
UnitKind => {} // Unconditionally continue.
146+
}
147+
}
90148

91-
// Return immediately if the call returned false.
92-
let next_block = sub_block(top_bcx, ~"next");
93-
let llcond = Load(bcx, fcx.llretptr);
94-
CondBr(bcx, llcond, next_block.llbb, fcx.llreturn);
95-
bcx = next_block;
149+
// Store true if necessary.
150+
match kind {
151+
BoolKind => Store(bcx, C_bool(true), fcx.llretptr),
152+
UnitKind => {}
96153
}
97154

98-
Store(bcx, C_bool(true), fcx.llretptr);
99155
Br(bcx, fcx.llreturn);
100156

101157
finish_fn(fcx, lltop);
102158
}
103159

104-
fn trans_deriving_enum_method(ccx: @crate_ctxt, llfn: ValueRef,
105-
impl_did: def_id, self_ty: ty::t) {
160+
// This could have been combined with trans_deriving_struct_method, but it
161+
// would probably be too big and hard to understand.
162+
fn trans_deriving_enum_method(ccx: @crate_ctxt,
163+
llfn: ValueRef,
164+
impl_did: def_id,
165+
self_ty: ty::t,
166+
kind: DerivingKind) {
106167
let _icx = ccx.insn_ctxt("trans_deriving_enum_method");
107168
let fcx = new_fn_ctxt(ccx, ~[], llfn, None);
108169
let top_bcx = top_scope_block(fcx, None);
@@ -111,7 +172,12 @@ fn trans_deriving_enum_method(ccx: @crate_ctxt, llfn: ValueRef,
111172

112173
let llselfty = type_of(ccx, self_ty);
113174
let llselfval = PointerCast(bcx, fcx.llenv, T_ptr(llselfty));
114-
let llotherval = llvm::LLVMGetParam(llfn, 2);
175+
176+
let llotherval_opt;
177+
match kind {
178+
UnitKind => llotherval_opt = None,
179+
BoolKind => llotherval_opt = Some(llvm::LLVMGetParam(llfn, 2))
180+
}
115181

116182
let enum_id, enum_substs, enum_variant_infos;
117183
match ty::get(self_ty).sty {
@@ -127,11 +193,18 @@ fn trans_deriving_enum_method(ccx: @crate_ctxt, llfn: ValueRef,
127193
}
128194
}
129195

130-
// Create the "no match" basic block. This is a basic block that does
131-
// nothing more than return false.
132-
let nomatch_bcx = sub_block(top_bcx, ~"no_match");
133-
Store(nomatch_bcx, C_bool(false), fcx.llretptr);
134-
Br(nomatch_bcx, fcx.llreturn);
196+
// Create the "no match" basic block, if necessary. This is a basic block
197+
// that does nothing more than return false.
198+
let nomatch_bcx_opt;
199+
match kind {
200+
BoolKind => {
201+
let nomatch_bcx = sub_block(top_bcx, ~"no_match");
202+
Store(nomatch_bcx, C_bool(false), fcx.llretptr);
203+
Br(nomatch_bcx, fcx.llreturn);
204+
nomatch_bcx_opt = Some(nomatch_bcx);
205+
}
206+
UnitKind => nomatch_bcx_opt = None
207+
}
135208

136209
// Create the "unreachable" basic block.
137210
let unreachable_bcx = sub_block(top_bcx, ~"unreachable");
@@ -144,11 +217,13 @@ fn trans_deriving_enum_method(ccx: @crate_ctxt, llfn: ValueRef,
144217
if n_variants != 1 {
145218
// Grab the two discriminants.
146219
let llselfdiscrim = Load(bcx, GEPi(bcx, llselfval, [0, 0]));
147-
let llotherdiscrim = Load(bcx, GEPi(bcx, llotherval, [0, 0]));
220+
let llotherdiscrim_opt = llotherval_opt.map(
221+
|llotherval| Load(bcx, GEPi(bcx, *llotherval, [0, 0])));
148222

149223
// Skip over the discriminants and compute the address of the payload.
150224
let llselfpayload = GEPi(bcx, llselfval, [0, 1]);
151-
let llotherpayload = GEPi(bcx, llotherval, [0, 1]);
225+
let llotherpayload_opt = llotherval_opt.map(
226+
|llotherval| GEPi(bcx, *llotherval, [0, 1]));
152227

153228
// Create basic blocks for the outer switch.
154229
let outer_bcxs = vec::from_fn(
@@ -169,46 +244,71 @@ fn trans_deriving_enum_method(ccx: @crate_ctxt, llfn: ValueRef,
169244
enum_variant_infos[self_variant_index].id;
170245
let llselfval = GEP_enum(match_bcx, llselfpayload, enum_id,
171246
variant_def_id, enum_substs.tps, i);
172-
let llotherval = GEP_enum(match_bcx, llotherpayload,
173-
enum_id, variant_def_id,
174-
enum_substs.tps, i);
247+
248+
let llotherval_opt = llotherpayload_opt.map(|llotherpayload|
249+
GEP_enum(match_bcx, *llotherpayload, enum_id,
250+
variant_def_id, enum_substs.tps, i));
175251

176252
let self_ty = enum_variant_infos[self_variant_index].args[i];
177253
match_bcx = call_substructure_method(match_bcx,
178254
derived_method_info,
179255
self_ty,
180256
llselfval,
181-
llotherval);
182-
183-
// Return immediately if the call to the substructure returned
184-
// false.
185-
let next_bcx = sub_block(
186-
top_bcx, fmt!("next_%u_%u", self_variant_index, i));
187-
let llcond = Load(match_bcx, fcx.llretptr);
188-
CondBr(match_bcx, llcond, next_bcx.llbb, fcx.llreturn);
189-
match_bcx = next_bcx;
257+
llotherval_opt);
258+
259+
// If this is a boolean-kind deriving method, then return
260+
// immediately if the call to the substructure returned false.
261+
match kind {
262+
BoolKind => {
263+
let next_bcx = sub_block(top_bcx,
264+
fmt!("next_%u_%u",
265+
self_variant_index,
266+
i));
267+
let llcond = Load(match_bcx, fcx.llretptr);
268+
CondBr(match_bcx,
269+
llcond,
270+
next_bcx.llbb,
271+
fcx.llreturn);
272+
match_bcx = next_bcx;
273+
}
274+
UnitKind => {}
275+
}
276+
}
277+
278+
// Store true in the return pointer if this is a boolean-kind
279+
// deriving method.
280+
match kind {
281+
BoolKind => Store(match_bcx, C_bool(true), fcx.llretptr),
282+
UnitKind => {}
190283
}
191284

192285
// Finish up the matching block.
193-
Store(match_bcx, C_bool(true), fcx.llretptr);
194286
Br(match_bcx, fcx.llreturn);
195287

196-
// Build the inner switch.
197-
let llswitch = Switch(
198-
*bcx, llotherdiscrim, unreachable_bcx.llbb, n_variants);
199-
for uint::range(0, n_variants) |other_variant_index| {
200-
let discriminant =
201-
enum_variant_infos[other_variant_index].disr_val;
202-
if self_variant_index == other_variant_index {
203-
// This is the potentially-matching case.
204-
AddCase(llswitch,
205-
C_int(ccx, discriminant),
206-
top_match_bcx.llbb);
207-
} else {
208-
// This is always a non-matching case.
209-
AddCase(llswitch,
210-
C_int(ccx, discriminant),
211-
nomatch_bcx.llbb);
288+
// If this is a boolean-kind derived method, build the inner
289+
// switch. Otherwise, just jump to the matching case.
290+
match llotherdiscrim_opt {
291+
None => Br(*bcx, top_match_bcx.llbb),
292+
Some(copy llotherdiscrim) => {
293+
let llswitch = Switch(*bcx,
294+
llotherdiscrim,
295+
unreachable_bcx.llbb,
296+
n_variants);
297+
for uint::range(0, n_variants) |other_variant_index| {
298+
let discriminant =
299+
enum_variant_infos[other_variant_index].disr_val;
300+
if self_variant_index == other_variant_index {
301+
// This is the potentially-matching case.
302+
AddCase(llswitch,
303+
C_int(ccx, discriminant),
304+
top_match_bcx.llbb);
305+
} else {
306+
// This is always a non-matching case.
307+
AddCase(llswitch,
308+
C_int(ccx, discriminant),
309+
nomatch_bcx_opt.get().llbb);
310+
}
311+
}
212312
}
213313
}
214314
}
@@ -233,7 +333,7 @@ fn call_substructure_method(bcx: block,
233333
derived_field_info: &DerivedFieldInfo,
234334
self_ty: ty::t,
235335
llselfval: ValueRef,
236-
llotherval: ValueRef) -> block {
336+
llotherval_opt: Option<ValueRef>) -> block {
237337
let fcx = bcx.fcx;
238338
let ccx = fcx.ccx;
239339

@@ -273,12 +373,18 @@ fn call_substructure_method(bcx: block,
273373
}
274374
};
275375

376+
let arg_values;
377+
match llotherval_opt {
378+
None => arg_values = ArgVals(~[]),
379+
Some(copy llotherval) => arg_values = ArgVals(~[llotherval])
380+
}
381+
276382
callee::trans_call_inner(bcx,
277383
None,
278384
fn_expr_tpbt.ty,
279385
ty::mk_bool(ccx.tcx),
280386
cb,
281-
ArgVals(~[llotherval]),
387+
move arg_values,
282388
SaveIn(fcx.llretptr),
283389
DontAutorefArg)
284390
}

0 commit comments

Comments
 (0)