2
2
// enums and structs only; other types cannot be automatically derived.
3
3
4
4
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} ;
6
6
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 } ;
9
9
use middle:: trans:: callee;
10
10
use middle:: trans:: callee:: { ArgVals , Callee , DontAutorefArg , Method } ;
11
11
use middle:: trans:: callee:: { MethodData } ;
12
12
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} ;
14
14
use middle:: trans:: expr:: SaveIn ;
15
15
use middle:: trans:: type_of:: type_of;
16
- use middle:: typeck:: method_static;
16
+ use middle:: typeck:: { method_origin , method_static} ;
17
17
use syntax:: ast;
18
18
use syntax:: ast:: { def_id, ident, node_id, ty_param} ;
19
19
use syntax:: ast_map:: path;
@@ -41,9 +41,13 @@ pub fn trans_deriving_impl(ccx: @crate_ctxt, _path: path, _name: ident,
41
41
trans_deriving_struct_method ( ccx, llfn, impl_def_id,
42
42
self_ty. ty ) ;
43
43
}
44
+ ty:: ty_enum( * ) => {
45
+ trans_deriving_enum_method ( ccx, llfn, impl_def_id,
46
+ self_ty. ty ) ;
47
+ }
44
48
_ => {
45
- ccx. tcx . sess . unimpl ( ~"translation of non-struct deriving \
46
- method") ;
49
+ ccx. tcx . sess . bug ( ~"translation of non-struct deriving \
50
+ method") ;
47
51
}
48
52
}
49
53
}
@@ -76,40 +80,12 @@ fn trans_deriving_struct_method(ccx: @crate_ctxt, llfn: ValueRef,
76
80
// Iterate over every element of the struct.
77
81
for ccx. tcx. deriving_struct_methods. get( impl_did) . eachi
78
82
|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
-
88
83
let llselfval = GEPi ( bcx, llselfval, [ 0 , 0 , i] ) ;
89
84
let llotherval = GEPi ( bcx, llotherval, [ 0 , 0 , i] ) ;
90
85
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) ;
113
89
114
90
// Return immediately if the call returned false.
115
91
let next_block = sub_block ( top_bcx, ~"next") ;
@@ -124,3 +100,171 @@ fn trans_deriving_struct_method(ccx: @crate_ctxt, llfn: ValueRef,
124
100
finish_fn ( fcx, lltop) ;
125
101
}
126
102
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
+
0 commit comments