29
29
* that might contain one and adjust GEP indices accordingly. See
30
30
* issue #4578.
31
31
*
32
- * - Rendering `Option<&T>` as a possibly-null `*T` instead of using
33
- * an extra word (and likewise for `@T` and `~T`). Can and probably
34
- * should also apply to any enum with one empty case and one case
35
- * starting with a non-null pointer (e.g., `Result<(), ~str>`).
36
- *
37
32
* - Using smaller integer types for discriminants.
38
33
*
39
34
* - Store nested enums' discriminants in the same word. Rather, if
@@ -54,7 +49,8 @@ use core::libc::c_ulonglong;
54
49
use core:: option:: { Option , Some , None } ;
55
50
use core:: vec;
56
51
57
- use lib:: llvm:: { ValueRef , TypeRef , True } ;
52
+ use lib:: llvm:: { ValueRef , TypeRef , True , IntEQ , IntNE } ;
53
+ use lib:: llvm:: llvm:: LLVMDumpValue ;
58
54
use middle:: trans:: _match;
59
55
use middle:: trans:: build:: * ;
60
56
use middle:: trans:: common:: * ;
@@ -81,7 +77,20 @@ pub enum Repr {
81
77
* General-case enums: for each case there is a struct, and they
82
78
* all start with a field for the discriminant.
83
79
*/
84
- General ( ~[ Struct ] )
80
+ General ( ~[ Struct ] ) ,
81
+ /**
82
+ * Two cases distinguished by a nullable pointer: the case with discriminant
83
+ * `nndiscr` is represented by the struct `nonnull`, where the `ptrfield`th
84
+ * field is known to be nonnull due to its type; if that field is null, then
85
+ * it represents the other case, which is inhabited by at most one value
86
+ * (and all other fields are undefined/unused).
87
+ *
88
+ * For example, `core::option::Option` instantiated at a safe pointer type
89
+ * is represented such that `None` is a null pointer and `Some` is the
90
+ * identity function.
91
+ */
92
+ NullablePointer { nonnull : Struct , nndiscr : int , ptrfield : uint ,
93
+ nullfields : ~[ ty:: t ] }
85
94
}
86
95
87
96
/// For structs, and struct-like parts of anything fancier.
@@ -108,9 +117,16 @@ pub fn represent_type(cx: @CrateContext, t: ty::t) -> @Repr {
108
117
Some ( repr) => return * repr,
109
118
None => { }
110
119
}
111
- let repr = @match ty:: get ( t) . sty {
120
+ let repr = @represent_type_uncached ( cx, t) ;
121
+ debug ! ( "Represented as: %?" , repr)
122
+ cx. adt_reprs . insert ( t, repr) ;
123
+ return repr;
124
+ }
125
+
126
+ fn represent_type_uncached ( cx : @CrateContext , t : ty:: t ) -> Repr {
127
+ match ty:: get ( t) . sty {
112
128
ty:: ty_tup( ref elems) => {
113
- Univariant ( mk_struct ( cx, * elems, false ) , false )
129
+ return Univariant ( mk_struct ( cx, * elems, false ) , false )
114
130
}
115
131
ty:: ty_struct( def_id, ref substs) => {
116
132
let fields = ty:: lookup_struct_fields ( cx. tcx , def_id) ;
@@ -121,45 +137,78 @@ pub fn represent_type(cx: @CrateContext, t: ty::t) -> @Repr {
121
137
let dtor = ty:: ty_dtor ( cx. tcx , def_id) . is_present ( ) ;
122
138
let ftys =
123
139
if dtor { ftys + [ ty:: mk_bool ( cx. tcx ) ] } else { ftys } ;
124
- Univariant ( mk_struct ( cx, ftys, packed) , dtor)
140
+ return Univariant ( mk_struct ( cx, ftys, packed) , dtor)
125
141
}
126
142
ty:: ty_enum( def_id, ref substs) => {
127
143
struct Case { discr : int , tys : ~[ ty:: t ] } ;
144
+ impl Case {
145
+ fn is_zerolen ( & self , cx : @CrateContext ) -> bool {
146
+ mk_struct ( cx, self . tys , false ) . size == 0
147
+ }
148
+ fn find_ptr ( & self ) -> Option < uint > {
149
+ self . tys . position ( |& ty| mono_data_classify ( ty) == MonoNonNull )
150
+ }
151
+ }
128
152
129
153
let cases = do ty:: enum_variants ( cx. tcx , def_id) . map |vi| {
130
154
let arg_tys = do vi. args . map |& raw_ty| {
131
155
ty:: subst ( cx. tcx , substs, raw_ty)
132
156
} ;
133
157
Case { discr : vi. disr_val , tys : arg_tys }
134
158
} ;
159
+
135
160
if cases. len ( ) == 0 {
136
161
// Uninhabitable; represent as unit
137
- Univariant ( mk_struct ( cx, ~[ ] , false ) , false )
138
- } else if cases. all ( |c| c. tys . len ( ) == 0 ) {
162
+ return Univariant ( mk_struct ( cx, ~[ ] , false ) , false ) ;
163
+ }
164
+
165
+ if cases. all ( |c| c. tys . len ( ) == 0 ) {
139
166
// All bodies empty -> intlike
140
167
let discrs = cases. map ( |c| c. discr ) ;
141
- CEnum ( discrs. min ( ) , discrs. max ( ) )
142
- } else if cases. len ( ) == 1 {
168
+ return CEnum ( discrs. min ( ) , discrs. max ( ) ) ;
169
+ }
170
+
171
+ if cases. len ( ) == 1 {
143
172
// Equivalent to a struct/tuple/newtype.
144
173
assert ! ( cases[ 0 ] . discr == 0 ) ;
145
- Univariant ( mk_struct ( cx, cases[ 0 ] . tys , false ) , false )
146
- } else {
147
- // The general case. Since there's at least one
148
- // non-empty body, explicit discriminants should have
149
- // been rejected by a checker before this point.
150
- if !cases. alli ( |i, c| c. discr == ( i as int ) ) {
151
- cx. sess . bug ( fmt ! ( "non-C-like enum %s with specified \
152
- discriminants",
153
- ty:: item_path_str( cx. tcx, def_id) ) )
174
+ return Univariant ( mk_struct ( cx, cases[ 0 ] . tys , false ) , false )
175
+ }
176
+
177
+ // Since there's at least one
178
+ // non-empty body, explicit discriminants should have
179
+ // been rejected by a checker before this point.
180
+ if !cases. alli ( |i, c| c. discr == ( i as int ) ) {
181
+ cx. sess . bug ( fmt ! ( "non-C-like enum %s with specified \
182
+ discriminants",
183
+ ty:: item_path_str( cx. tcx, def_id) ) )
184
+ }
185
+
186
+ if cases. len ( ) == 2 {
187
+ let mut discr = 0 ;
188
+ while discr < 2 {
189
+ if cases[ 1 - discr] . is_zerolen ( cx) {
190
+ match cases[ discr] . find_ptr ( ) {
191
+ Some ( ptrfield) => {
192
+ return NullablePointer {
193
+ nndiscr : discr,
194
+ nonnull : mk_struct ( cx, cases[ discr] . tys , false ) ,
195
+ ptrfield : ptrfield,
196
+ nullfields : copy cases[ 1 - discr] . tys
197
+ }
198
+ }
199
+ None => { }
200
+ }
201
+ }
202
+ discr += 1 ;
154
203
}
155
- let discr = ~[ ty:: mk_int ( cx. tcx ) ] ;
156
- General ( cases. map ( |c| mk_struct ( cx, discr + c. tys , false ) ) )
157
204
}
205
+
206
+ // The general case.
207
+ let discr = ~[ ty:: mk_int ( cx. tcx ) ] ;
208
+ return General ( cases. map ( |c| mk_struct ( cx, discr + c. tys , false ) ) )
158
209
}
159
210
_ => cx. sess . bug ( ~"adt:: represent_type called on non-ADT type")
160
- } ;
161
- cx. adt_reprs . insert ( t, repr) ;
162
- return repr;
211
+ }
163
212
}
164
213
165
214
fn mk_struct ( cx : @CrateContext , tys : & [ ty:: t ] , packed : bool ) -> Struct {
@@ -190,6 +239,7 @@ fn generic_fields_of(cx: @CrateContext, r: &Repr, sizing: bool)
190
239
match * r {
191
240
CEnum ( * ) => ~[ T_enum_discrim ( cx) ] ,
192
241
Univariant ( ref st, _dtor) => struct_llfields ( cx, st, sizing) ,
242
+ NullablePointer { nonnull : ref st, _ } => struct_llfields ( cx, st, sizing) ,
193
243
General ( ref sts) => {
194
244
// To get "the" type of a general enum, we pick the case
195
245
// with the largest alignment (so it will always align
@@ -239,23 +289,40 @@ pub fn trans_switch(bcx: block, r: &Repr, scrutinee: ValueRef)
239
289
CEnum ( * ) | General ( * ) => {
240
290
( _match:: switch, Some ( trans_get_discr ( bcx, r, scrutinee) ) )
241
291
}
292
+ NullablePointer { nonnull : ref nonnull, nndiscr, ptrfield, _ } => {
293
+ ( _match:: switch, Some ( nullable_bitdiscr ( bcx, nonnull, nndiscr, ptrfield, scrutinee) ) )
294
+ }
242
295
Univariant ( * ) => {
243
296
( _match:: single, None )
244
297
}
245
298
}
246
299
}
247
300
301
+
302
+
248
303
/// Obtain the actual discriminant of a value.
249
304
pub fn trans_get_discr ( bcx : block , r : & Repr , scrutinee : ValueRef )
250
305
-> ValueRef {
251
306
match * r {
252
307
CEnum ( min, max) => load_discr ( bcx, scrutinee, min, max) ,
253
308
Univariant ( * ) => C_int ( bcx. ccx ( ) , 0 ) ,
254
309
General ( ref cases) => load_discr ( bcx, scrutinee, 0 ,
255
- ( cases. len ( ) - 1 ) as int )
310
+ ( cases. len ( ) - 1 ) as int ) ,
311
+ NullablePointer { nonnull : ref nonnull, nndiscr, ptrfield, _ } => {
312
+ ZExt ( bcx, nullable_bitdiscr ( bcx, nonnull, nndiscr, ptrfield, scrutinee) ,
313
+ T_enum_discrim ( bcx. ccx ( ) ) )
314
+ }
256
315
}
257
316
}
258
317
318
+ fn nullable_bitdiscr ( bcx : block , nonnull : & Struct , nndiscr : int , ptrfield : uint ,
319
+ scrutinee : ValueRef ) -> ValueRef {
320
+ let cmp = if nndiscr == 0 { IntEQ } else { IntNE } ;
321
+ let llptr = Load ( bcx, GEPi ( bcx, scrutinee, [ 0 , ptrfield] ) ) ;
322
+ let llptrty = type_of:: type_of ( bcx. ccx ( ) , nonnull. fields [ ptrfield] ) ;
323
+ ICmp ( bcx, cmp, llptr, C_null ( llptrty) )
324
+ }
325
+
259
326
/// Helper for cases where the discriminant is simply loaded.
260
327
fn load_discr ( bcx : block , scrutinee : ValueRef , min : int , max : int )
261
328
-> ValueRef {
@@ -286,12 +353,16 @@ pub fn trans_case(bcx: block, r: &Repr, discr: int) -> _match::opt_result {
286
353
CEnum ( * ) => {
287
354
_match:: single_result ( rslt ( bcx, C_int ( bcx. ccx ( ) , discr) ) )
288
355
}
289
- Univariant ( * ) => {
356
+ Univariant ( * ) => {
290
357
bcx. ccx ( ) . sess . bug ( ~"no cases for univariants or structs")
291
358
}
292
359
General ( * ) => {
293
360
_match:: single_result ( rslt ( bcx, C_int ( bcx. ccx ( ) , discr) ) )
294
361
}
362
+ NullablePointer { _ } => {
363
+ assert ! ( discr == 0 || discr == 1 ) ;
364
+ _match:: single_result ( rslt ( bcx, C_i1 ( discr != 0 ) ) )
365
+ }
295
366
}
296
367
}
297
368
@@ -317,6 +388,13 @@ pub fn trans_start_init(bcx: block, r: &Repr, val: ValueRef, discr: int) {
317
388
General ( * ) => {
318
389
Store ( bcx, C_int ( bcx. ccx ( ) , discr) , GEPi ( bcx, val, [ 0 , 0 ] ) )
319
390
}
391
+ NullablePointer { nonnull : ref nonnull, nndiscr, ptrfield, _ } => {
392
+ if discr != nndiscr {
393
+ let llptrptr = GEPi ( bcx, val, [ 0 , ptrfield] ) ;
394
+ let llptrty = type_of:: type_of ( bcx. ccx ( ) , nonnull. fields [ ptrfield] ) ;
395
+ Store ( bcx, C_null ( llptrty) , llptrptr)
396
+ }
397
+ }
320
398
}
321
399
}
322
400
@@ -331,7 +409,10 @@ pub fn num_args(r: &Repr, discr: int) -> uint {
331
409
assert ! ( discr == 0 ) ;
332
410
st. fields . len ( ) - ( if dtor { 1 } else { 0 } )
333
411
}
334
- General ( ref cases) => cases[ discr as uint ] . fields . len ( ) - 1
412
+ General ( ref cases) => cases[ discr as uint ] . fields . len ( ) - 1 ,
413
+ NullablePointer { nonnull : ref nonnull, nndiscr, _ } => {
414
+ if discr == nndiscr { nonnull. fields . len ( ) } else { 0 }
415
+ }
335
416
}
336
417
}
337
418
@@ -352,6 +433,19 @@ pub fn trans_field_ptr(bcx: block, r: &Repr, val: ValueRef, discr: int,
352
433
General ( ref cases) => {
353
434
struct_field_ptr ( bcx, & cases[ discr as uint ] , val, ix + 1 , true )
354
435
}
436
+ NullablePointer { nonnull : ref nonnull, nullfields : ref nullfields, nndiscr, _ } => {
437
+ if ( discr == nndiscr) {
438
+ struct_field_ptr ( bcx, nonnull, val, ix, false )
439
+ } else {
440
+ // The unit-like case might have a nonzero number of unit-like fields.
441
+ // (e.g., Result or Either with () as one side.)
442
+ let llty = type_of:: type_of ( bcx. ccx ( ) , nullfields[ ix] ) ;
443
+ assert ! ( machine:: llsize_of_alloc( bcx. ccx( ) , llty) == 0 ) ;
444
+ // The contents of memory at this pointer can't matter, but use
445
+ // the value that's "reasonable" in case of pointer comparison.
446
+ PointerCast ( bcx, val, T_ptr ( llty) )
447
+ }
448
+ }
355
449
}
356
450
}
357
451
@@ -420,6 +514,18 @@ pub fn trans_const(ccx: @CrateContext, r: &Repr, discr: int,
420
514
~[ C_int ( ccx, discr) ] + vals) ;
421
515
C_struct ( contents + [ padding ( max_sz - case. size ) ] )
422
516
}
517
+ NullablePointer { nonnull : ref nonnull, nndiscr, ptrfield, _ } => {
518
+ if discr == nndiscr {
519
+ C_struct ( build_const_struct ( ccx, nonnull, vals) )
520
+ } else {
521
+ assert ! ( vals. len( ) == 0 ) ;
522
+ let vals = do nonnull. fields . mapi |i, & ty| {
523
+ let llty = type_of:: sizing_type_of ( ccx, ty) ;
524
+ if i == ptrfield { C_null ( llty) } else { C_undef ( llty) }
525
+ } ;
526
+ C_struct ( build_const_struct ( ccx, nonnull, vals) )
527
+ }
528
+ }
423
529
}
424
530
}
425
531
@@ -451,10 +557,14 @@ fn build_const_struct(ccx: @CrateContext, st: &Struct, vals: &[ValueRef])
451
557
cfields. push ( padding ( target_offset - offset) ) ;
452
558
offset = target_offset;
453
559
}
454
- assert ! ( !is_undef( vals[ i] ) ) ;
455
- // If that assert fails, could change it to wrap in a struct?
456
- // (See `const_struct_field` for why real fields must not be undef.)
457
- cfields. push ( vals[ i] ) ;
560
+ let val = if is_undef ( vals[ i] ) {
561
+ let wrapped = C_struct ( [ vals[ i] ] ) ;
562
+ assert ! ( !is_undef( wrapped) ) ;
563
+ wrapped
564
+ } else {
565
+ vals[ i]
566
+ } ;
567
+ cfields. push ( val) ;
458
568
}
459
569
460
570
return cfields;
@@ -475,6 +585,9 @@ pub fn const_get_discrim(ccx: @CrateContext, r: &Repr, val: ValueRef)
475
585
CEnum ( * ) => const_to_int ( val) as int ,
476
586
Univariant ( * ) => 0 ,
477
587
General ( * ) => const_to_int ( const_get_elt ( ccx, val, [ 0 ] ) ) as int ,
588
+ NullablePointer { nndiscr, ptrfield, _ } => {
589
+ if is_null ( const_struct_field ( ccx, val, ptrfield) ) { 1 - nndiscr } else { nndiscr }
590
+ }
478
591
}
479
592
}
480
593
@@ -490,7 +603,8 @@ pub fn const_get_field(ccx: @CrateContext, r: &Repr, val: ValueRef,
490
603
match * r {
491
604
CEnum ( * ) => ccx. sess . bug ( ~"element access in C -like enum const ") ,
492
605
Univariant ( * ) => const_struct_field ( ccx, val, ix) ,
493
- General ( * ) => const_struct_field ( ccx, val, ix + 1 )
606
+ General ( * ) => const_struct_field ( ccx, val, ix + 1 ) ,
607
+ NullablePointer { _ } => const_struct_field ( ccx, val, ix)
494
608
}
495
609
}
496
610
0 commit comments