@@ -117,25 +117,40 @@ pub enum Repr<'tcx> {
117117 /// (The flag, if nonzero, represents the initialization value to use;
118118 /// if zero, then use no flag at all.)
119119 General ( IntType , Vec < Struct < ' tcx > > , u8 ) ,
120- /// Two cases distinguished by a nullable pointer: the case with discriminant
121- /// `nndiscr` must have single field which is known to be nonnull due to its type.
122- /// The other case is known to be zero sized. Hence we represent the enum
123- /// as simply a nullable pointer: if not null it indicates the `nndiscr` variant,
124- /// otherwise it indicates the other case.
125- RawNullablePointer {
126- nndiscr : Disr ,
127- nnty : Ty < ' tcx > ,
128- nullfields : Vec < Ty < ' tcx > >
120+ /// Two cases distinguised by a known-to-be-forbidden value.
121+ ///
122+ /// Example: `Option<&T>` (a `&T` cannot be null)
123+ /// Example: `Option<char>` (a `char` is large enough to hold 2^32 - 1,
124+ /// but this value is forbidden by definition)
125+ /// Example: `Result<&T, ()>` (a `&T` cannot be null)
126+ ///
127+ /// One of the cases (the "unit case") must be known to be
128+ /// zero-sized (e.g. `None`). The other case (the "payload case")
129+ /// must be known to be a single field that cannot adopt a
130+ /// specific value (in the above examples, 0 for `&T` or 2^32 - 1
131+ /// for `char`).
132+ ///
133+ /// We may safely represent the enum by its payload case and
134+ /// differentiate between cases by checking for the forbidden
135+ /// value.
136+ RawForbiddenValue {
137+ /// Unit case (e.g. `None` or `Either((), ())`)
138+ unit_fields : Vec < Ty < ' tcx > > ,
139+
140+ /// Case holding a payload: the constructor
141+ payload_discr : Disr ,
142+
143+ /// Case holding a payload: the type
144+ payload_ty : Ty < ' tcx > ,
145+
146+ /// A value that the payload can never hold.
147+ forbidden_value : ValueRef ,
129148 } ,
130149 /// Two cases distinguished by a nullable pointer: the case with discriminant
131150 /// `nndiscr` is represented by the struct `nonnull`, where the `discrfield`th
132151 /// field is known to be nonnull due to its type; if that field is null, then
133152 /// it represents the other case, which is inhabited by at most one value
134153 /// (and all other fields are undefined/unused).
135- ///
136- /// For example, `std::option::Option` instantiated at a safe pointer type
137- /// is represented such that `None` is a null pointer and `Some` is the
138- /// identity function.
139154 StructWrappedNullablePointer {
140155 nonnull : Struct < ' tcx > ,
141156 nndiscr : Disr ,
@@ -322,18 +337,26 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
322337 }
323338
324339 if !dtor && cases. len ( ) == 2 && hint == attr:: ReprAny {
325- // Nullable pointer optimization
326- let mut discr = 0 ;
327- while discr < 2 {
340+ // Two cases, so it might be possible to turn this
341+ // into a `RawForbiddenValue` or a
342+ // `StructWrappedNullablePointer`, if all conditions
343+ // are met.
344+ for discr in 0 .. 2 {
328345 if cases[ 1 - discr] . is_zerolen ( cx, t) {
346+ // One of the cases has zero length. We are on the right track.
329347 let st = mk_struct ( cx, & cases[ discr] . tys ,
330348 false , t) ;
349+
350+ // For the moment, we can only apply these
351+ // optimizations to safe pointers.
331352 match cases[ discr] . find_ptr ( cx) {
332353 Some ( ref df) if df. len ( ) == 1 && st. fields . len ( ) == 1 => {
333- return RawNullablePointer {
334- nndiscr : Disr :: from ( discr) ,
335- nnty : st. fields [ 0 ] ,
336- nullfields : cases[ 1 - discr] . tys . clone ( )
354+ let payload_ty = st. fields [ 0 ] ;
355+ return RawForbiddenValue {
356+ payload_discr : Disr :: from ( discr) ,
357+ payload_ty : payload_ty,
358+ forbidden_value : C_null ( type_of:: sizing_type_of ( cx, payload_ty) ) ,
359+ unit_fields : cases[ 1 - discr] . tys . clone ( )
337360 } ;
338361 }
339362 Some ( mut discrfield) => {
@@ -348,8 +371,13 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
348371 }
349372 None => { }
350373 }
374+ // No need to continue the loop. If both cases
375+ // have zero length, we can apply neither
376+ // `RawForbiddenValue` nor
377+ // `StructWrappedNullablePointer`.
378+ break ;
379+
351380 }
352- discr += 1 ;
353381 }
354382 }
355383
@@ -529,6 +557,8 @@ impl<'tcx> Case<'tcx> {
529557 mk_struct ( cx, & self . tys , false , scapegoat) . size == 0
530558 }
531559
560+ /// Find a safe pointer that may be used to discriminate in a
561+ /// RawForbiddenValue or StructWrappedNullablePointer.
532562 fn find_ptr < ' a > ( & self , cx : & CrateContext < ' a , ' tcx > ) -> Option < DiscrField > {
533563 for ( i, & ty) in self . tys . iter ( ) . enumerate ( ) {
534564 if let Some ( mut path) = find_discr_field_candidate ( cx. tcx ( ) , ty, vec ! [ ] ) {
@@ -748,7 +778,7 @@ pub fn incomplete_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
748778pub fn finish_type_of < ' a , ' tcx > ( cx : & CrateContext < ' a , ' tcx > ,
749779 r : & Repr < ' tcx > , llty : & mut Type ) {
750780 match * r {
751- CEnum ( ..) | General ( ..) | RawNullablePointer { .. } => { }
781+ CEnum ( ..) | General ( ..) | RawForbiddenValue { .. } => { }
752782 Univariant ( ref st, _) | StructWrappedNullablePointer { nonnull : ref st, .. } =>
753783 llty. set_struct_body ( & struct_llfields ( cx, st, false , false ) ,
754784 st. packed )
@@ -765,8 +795,8 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
765795 r, name, sizing, dst, delay_drop_flag) ;
766796 match * r {
767797 CEnum ( ity, _, _) => TypeContext :: direct ( ll_inttype ( cx, ity) ) ,
768- RawNullablePointer { nnty , .. } =>
769- TypeContext :: direct ( type_of:: sizing_type_of ( cx, nnty ) ) ,
798+ RawForbiddenValue { payload_ty , .. } =>
799+ TypeContext :: direct ( type_of:: sizing_type_of ( cx, payload_ty ) ) ,
770800 StructWrappedNullablePointer { nonnull : ref st, .. } => {
771801 match name {
772802 None => {
@@ -880,9 +910,8 @@ pub fn trans_switch<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
880910 -> ( _match:: BranchKind , Option < ValueRef > ) {
881911 match * r {
882912 CEnum ( ..) | General ( ..) |
883- RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => {
884- ( _match:: Switch , Some ( trans_get_discr ( bcx, r, scrutinee, None ,
885- range_assert) ) )
913+ RawForbiddenValue { .. } | StructWrappedNullablePointer { .. } => {
914+ ( _match:: Switch , Some ( trans_get_discr ( bcx, r, scrutinee, None , range_assert) ) )
886915 }
887916 Univariant ( ..) => {
888917 // N.B.: Univariant means <= 1 enum variants (*not* == 1 variants).
@@ -896,7 +925,7 @@ pub fn is_discr_signed<'tcx>(r: &Repr<'tcx>) -> bool {
896925 CEnum ( ity, _, _) => ity. is_signed ( ) ,
897926 General ( ity, _, _) => ity. is_signed ( ) ,
898927 Univariant ( ..) => false ,
899- RawNullablePointer { .. } => false ,
928+ RawForbiddenValue { payload_ty , .. } => payload_ty . is_signed ( ) ,
900929 StructWrappedNullablePointer { .. } => false ,
901930 }
902931}
@@ -917,10 +946,9 @@ pub fn trans_get_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
917946 range_assert)
918947 }
919948 Univariant ( ..) => C_u8 ( bcx. ccx ( ) , 0 ) ,
920- RawNullablePointer { nndiscr, nnty, .. } => {
921- let cmp = if nndiscr == Disr ( 0 ) { IntEQ } else { IntNE } ;
922- let llptrty = type_of:: sizing_type_of ( bcx. ccx ( ) , nnty) ;
923- ICmp ( bcx, cmp, Load ( bcx, scrutinee) , C_null ( llptrty) , DebugLoc :: None )
949+ RawForbiddenValue { payload_discr, forbidden_value, .. } => {
950+ let cmp = if payload_discr == Disr ( 0 ) { IntEQ } else { IntNE } ;
951+ ICmp ( bcx, cmp, Load ( bcx, scrutinee) , forbidden_value, DebugLoc :: None )
924952 }
925953 StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
926954 struct_wrapped_nullable_bitdiscr ( bcx, nndiscr, discrfield, scrutinee)
@@ -981,7 +1009,7 @@ pub fn trans_case<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr, discr: Disr)
9811009 Univariant ( ..) => {
9821010 bcx. ccx ( ) . sess ( ) . bug ( "no cases for univariants or structs" )
9831011 }
984- RawNullablePointer { .. } |
1012+ RawForbiddenValue { .. } |
9851013 StructWrappedNullablePointer { .. } => {
9861014 assert ! ( discr == Disr ( 0 ) || discr == Disr ( 1 ) ) ;
9871015 C_bool ( bcx. ccx ( ) , discr != Disr ( 0 ) )
@@ -1015,10 +1043,9 @@ pub fn trans_set_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
10151043 StructGEP ( bcx, val, st. fields . len ( ) - 1 ) ) ;
10161044 }
10171045 }
1018- RawNullablePointer { nndiscr, nnty, ..} => {
1019- if discr != nndiscr {
1020- let llptrty = type_of:: sizing_type_of ( bcx. ccx ( ) , nnty) ;
1021- Store ( bcx, C_null ( llptrty) , val) ;
1046+ RawForbiddenValue { payload_discr, forbidden_value, ..} => {
1047+ if discr != payload_discr {
1048+ Store ( bcx, forbidden_value, val) ;
10221049 }
10231050 }
10241051 StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
@@ -1056,8 +1083,16 @@ pub fn num_args(r: &Repr, discr: Disr) -> usize {
10561083 General ( _, ref cases, dtor) => {
10571084 cases[ discr. 0 as usize ] . fields . len ( ) - 1 - ( if dtor_active ( dtor) { 1 } else { 0 } )
10581085 }
1059- RawNullablePointer { nndiscr, ref nullfields, .. } => {
1060- if discr == nndiscr { 1 } else { nullfields. len ( ) }
1086+ RawForbiddenValue { payload_discr, ref unit_fields, .. } => {
1087+ if discr == payload_discr {
1088+ // By definition of `RawForbiddenValue`, the payload case
1089+ // has exactly one field.
1090+ 1
1091+ } else {
1092+ // In the unit case, we may have any number of fields,
1093+ // including 0.
1094+ unit_fields. len ( )
1095+ }
10611096 }
10621097 StructWrappedNullablePointer { ref nonnull, nndiscr,
10631098 ref nullfields, .. } => {
@@ -1083,7 +1118,7 @@ pub fn trans_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
10831118 General ( _, ref cases, _) => {
10841119 struct_field_ptr ( bcx, & cases[ discr. 0 as usize ] , val, ix + 1 , true )
10851120 }
1086- RawNullablePointer { nndiscr, ref nullfields, .. } |
1121+ RawForbiddenValue { payload_discr : nndiscr, unit_fields : ref nullfields, .. } |
10871122 StructWrappedNullablePointer { nndiscr, ref nullfields, .. } if discr != nndiscr => {
10881123 // The unit-like case might have a nonzero number of unit-like fields.
10891124 // (e.d., Result of Either with (), as one side.)
@@ -1093,10 +1128,10 @@ pub fn trans_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
10931128 // the value that's "reasonable" in case of pointer comparison.
10941129 PointerCast ( bcx, val. value , ty. ptr_to ( ) )
10951130 }
1096- RawNullablePointer { nndiscr , nnty , .. } => {
1097- assert_eq ! ( ix, 0 ) ;
1098- assert_eq ! ( discr, nndiscr ) ;
1099- let ty = type_of:: type_of ( bcx. ccx ( ) , nnty ) ;
1131+ RawForbiddenValue { payload_discr , payload_ty , .. } => {
1132+ assert_eq ! ( ix, 0 ) ; // By definition, the payload of RawForbiddenValue has a single field.
1133+ assert_eq ! ( discr, payload_discr ) ;
1134+ let ty = type_of:: type_of ( bcx. ccx ( ) , payload_ty ) ;
11001135 PointerCast ( bcx, val. value , ty. ptr_to ( ) )
11011136 }
11021137 StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => {
@@ -1345,12 +1380,12 @@ pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, r: &Repr<'tcx>, discr
13451380 let contents = build_const_struct ( ccx, st, vals) ;
13461381 C_struct ( ccx, & contents[ ..] , st. packed )
13471382 }
1348- RawNullablePointer { nndiscr , nnty , .. } => {
1349- if discr == nndiscr {
1350- assert_eq ! ( vals. len( ) , 1 ) ;
1383+ RawForbiddenValue { payload_discr , forbidden_value , .. } => {
1384+ if discr == payload_discr {
1385+ assert_eq ! ( vals. len( ) , 1 ) ; // By definition, the payload has only a single field.
13511386 vals[ 0 ]
13521387 } else {
1353- C_null ( type_of :: sizing_type_of ( ccx , nnty ) )
1388+ forbidden_value
13541389 }
13551390 }
13561391 StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => {
@@ -1457,7 +1492,7 @@ pub fn const_get_discrim(ccx: &CrateContext, r: &Repr, val: ValueRef) -> Disr {
14571492 }
14581493 }
14591494 Univariant ( ..) => Disr ( 0 ) ,
1460- RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => {
1495+ RawForbiddenValue { .. } | StructWrappedNullablePointer { .. } => {
14611496 ccx. sess ( ) . bug ( "const discrim access of non c-like enum" )
14621497 }
14631498 }
@@ -1469,14 +1504,19 @@ pub fn const_get_discrim(ccx: &CrateContext, r: &Repr, val: ValueRef) -> Disr {
14691504/// (Not to be confused with `common::const_get_elt`, which operates on
14701505/// raw LLVM-level structs and arrays.)
14711506pub fn const_get_field ( ccx : & CrateContext , r : & Repr , val : ValueRef ,
1472- _discr : Disr , ix : usize ) -> ValueRef {
1507+ discr : Disr , ix : usize ) -> ValueRef {
14731508 match * r {
14741509 CEnum ( ..) => ccx. sess ( ) . bug ( "element access in C-like enum const" ) ,
14751510 Univariant ( ..) => const_struct_field ( ccx, val, ix) ,
14761511 General ( ..) => const_struct_field ( ccx, val, ix + 1 ) ,
1477- RawNullablePointer { .. } => {
1478- assert_eq ! ( ix, 0 ) ;
1479- val
1512+ RawForbiddenValue { payload_discr, .. } => {
1513+ if discr == payload_discr {
1514+ assert_eq ! ( ix, 0 ) ; // By definition, the payload only has a single field.
1515+ val
1516+ } else {
1517+ // All values are unit.
1518+ C_null ( Type :: nil ( ccx) )
1519+ }
14801520 } ,
14811521 StructWrappedNullablePointer { .. } => const_struct_field ( ccx, val, ix)
14821522 }
0 commit comments