Skip to content

Commit 1d7881d

Browse files
David Rajchenbach-Tellermrhota
David Rajchenbach-Teller
authored andcommitted
Issue rust-lang#5977 - Generalizing RawNullablePointer to RawForbiddenValue
1 parent 100b309 commit 1d7881d

File tree

3 files changed

+89
-63
lines changed

3 files changed

+89
-63
lines changed

src/librustc_trans/adt.rs

Lines changed: 72 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -82,25 +82,37 @@ pub enum Repr<'tcx> {
8282
/// General-case enums: for each case there is a struct, and they
8383
/// all start with a field for the discriminant.
8484
General(IntType, Vec<Struct<'tcx>>),
85-
/// Two cases distinguished by a nullable pointer: the case with discriminant
86-
/// `nndiscr` must have single field which is known to be nonnull due to its type.
87-
/// The other case is known to be zero sized. Hence we represent the enum
88-
/// as simply a nullable pointer: if not null it indicates the `nndiscr` variant,
89-
/// otherwise it indicates the other case.
90-
RawNullablePointer {
91-
nndiscr: Disr,
92-
nnty: Ty<'tcx>,
93-
nullfields: Vec<Ty<'tcx>>
85+
/// Two cases distinguished by a known-to-be-forbidden value.
86+
///
87+
/// Example: `Option<&T>` (a `&T` cannot be null)
88+
/// Example: `Option<char>` (a `char` is large enough to hold 2^32 - 1,
89+
/// but this value is forbidden by definition)
90+
/// Example: `Result<&T, ()>` (a `&T` cannot be null)
91+
///
92+
/// One of the cases (the "unit case") must be known to be
93+
/// zero-sized (e.g. `None`). The other case (the "payload case")
94+
/// must be known to be a single field that cannot adopt a
95+
/// specific value (in the above examples, 0 for `&T` or 2^32 - 1
96+
/// for `char`).
97+
///
98+
/// We may safely represent the enum by its payload case and
99+
/// differentiate between cases by checking for the forbidden
100+
/// value.
101+
RawForbiddenValue {
102+
/// Unit case (e.g. `None` or `Either((), ())`)
103+
unit_fields: Vec<Ty<'tcx>>,
104+
/// Case holding a payload: the constructor
105+
payload_discr: Disr,
106+
/// Case holding a payload: the type
107+
payload_ty: Ty<'tcx>,
108+
/// A value that the payload can never hold.
109+
forbidden_value: ValueRef,
94110
},
95111
/// Two cases distinguished by a nullable pointer: the case with discriminant
96112
/// `nndiscr` is represented by the struct `nonnull`, where the `discrfield`th
97113
/// field is known to be nonnull due to its type; if that field is null, then
98114
/// it represents the other case, which is inhabited by at most one value
99115
/// (and all other fields are undefined/unused).
100-
///
101-
/// For example, `std::option::Option` instantiated at a safe pointer type
102-
/// is represented such that `None` is a null pointer and `Some` is the
103-
/// identity function.
104116
StructWrappedNullablePointer {
105117
nonnull: Struct<'tcx>,
106118
nndiscr: Disr,
@@ -217,18 +229,26 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
217229
}
218230

219231
if cases.len() == 2 && hint == attr::ReprAny {
220-
// Nullable pointer optimization
221-
let mut discr = 0;
222-
while discr < 2 {
232+
// Two cases, so it might be possible to turn this
233+
// into a `RawForbiddenValue` or a
234+
// `StructWrappedNullablePointer`, if all conditions
235+
// are met.
236+
for discr in 0 .. 2 {
223237
if cases[1 - discr].is_zerolen(cx, t) {
238+
// One of the cases has zero length. We are on the right track.
224239
let st = mk_struct(cx, &cases[discr].tys,
225240
false, t);
241+
242+
// For the moment, we can only apply these
243+
// optimizations to safe pointers.
226244
match cases[discr].find_ptr(cx) {
227245
Some(ref df) if df.len() == 1 && st.fields.len() == 1 => {
228-
return RawNullablePointer {
229-
nndiscr: Disr::from(discr),
230-
nnty: st.fields[0],
231-
nullfields: cases[1 - discr].tys.clone()
246+
let payload_ty = st.fields[0];
247+
return RawForbiddenValue {
248+
payload_discr: Disr::from(discr),
249+
payload_ty: payload_ty,
250+
forbidden_value: C_null(type_of::sizing_type_of(cx, payload_ty)),
251+
unit_fields: cases[1 - discr].tys.clone()
232252
};
233253
}
234254
Some(mut discrfield) => {
@@ -243,8 +263,13 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
243263
}
244264
None => {}
245265
}
266+
// No need to continue the loop. If both cases
267+
// have zero length, we can apply neither
268+
// `RawForbiddenValue` nor
269+
// `StructWrappedNullablePointer`.
270+
break;
271+
246272
}
247-
discr += 1;
248273
}
249274
}
250275

@@ -432,6 +457,8 @@ impl<'tcx> Case<'tcx> {
432457
mk_struct(cx, &self.tys, false, scapegoat).size == 0
433458
}
434459

460+
/// Find a safe pointer that may be used to discriminate in a
461+
/// RawForbiddenValue or StructWrappedNullablePointer.
435462
fn find_ptr<'a>(&self, cx: &CrateContext<'a, 'tcx>) -> Option<DiscrField> {
436463
for (i, &ty) in self.tys.iter().enumerate() {
437464
if let Some(mut path) = find_discr_field_candidate(cx.tcx(), ty, vec![]) {
@@ -643,7 +670,7 @@ pub fn incomplete_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
643670
pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
644671
r: &Repr<'tcx>, llty: &mut Type) {
645672
match *r {
646-
CEnum(..) | General(..) | RawNullablePointer { .. } => { }
673+
CEnum(..) | General(..) | RawForbiddenValue { .. } => { }
647674
Univariant(ref st) | StructWrappedNullablePointer { nonnull: ref st, .. } =>
648675
llty.set_struct_body(&struct_llfields(cx, st, false, false),
649676
st.packed)
@@ -659,8 +686,8 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
659686
r, name, sizing, dst);
660687
match *r {
661688
CEnum(ity, _, _) => ll_inttype(cx, ity),
662-
RawNullablePointer { nnty, .. } =>
663-
type_of::sizing_type_of(cx, nnty),
689+
RawForbiddenValue { payload_ty, .. } =>
690+
type_of::sizing_type_of(cx, payload_ty),
664691
StructWrappedNullablePointer { nonnull: ref st, .. } => {
665692
match name {
666693
None => {
@@ -756,7 +783,7 @@ pub fn trans_switch<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
756783
-> (BranchKind, Option<ValueRef>) {
757784
match *r {
758785
CEnum(..) | General(..) |
759-
RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => {
786+
RawForbiddenValue{ .. } | StructWrappedNullablePointer { .. } => {
760787
(BranchKind::Switch, Some(trans_get_discr(bcx, r, scrutinee, None, range_assert)))
761788
}
762789
Univariant(..) => {
@@ -771,7 +798,7 @@ pub fn is_discr_signed<'tcx>(r: &Repr<'tcx>) -> bool {
771798
CEnum(ity, _, _) => ity.is_signed(),
772799
General(ity, _) => ity.is_signed(),
773800
Univariant(..) => false,
774-
RawNullablePointer { .. } => false,
801+
RawForbiddenValue { payload_ty, .. } => payload_ty.is_signed(),
775802
StructWrappedNullablePointer { .. } => false,
776803
}
777804
}
@@ -792,10 +819,9 @@ pub fn trans_get_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
792819
range_assert)
793820
}
794821
Univariant(..) => C_u8(bcx.ccx(), 0),
795-
RawNullablePointer { nndiscr, nnty, .. } => {
796-
let cmp = if nndiscr == Disr(0) { IntEQ } else { IntNE };
797-
let llptrty = type_of::sizing_type_of(bcx.ccx(), nnty);
798-
ICmp(bcx, cmp, Load(bcx, scrutinee), C_null(llptrty), DebugLoc::None)
822+
RawForbiddenValue { payload_discr, forbidden_value, .. } => {
823+
let cmp = if payload_discr == Disr(0) { IntEQ } else { IntNE };
824+
ICmp(bcx, cmp, Load(bcx, scrutinee), forbidden_value, DebugLoc::None)
799825
}
800826
StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
801827
struct_wrapped_nullable_bitdiscr(bcx, nndiscr, discrfield, scrutinee)
@@ -856,7 +882,7 @@ pub fn trans_case<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr, discr: Disr)
856882
Univariant(..) => {
857883
bug!("no cases for univariants or structs")
858884
}
859-
RawNullablePointer { .. } |
885+
RawForbiddenValue { .. } |
860886
StructWrappedNullablePointer { .. } => {
861887
assert!(discr == Disr(0) || discr == Disr(1));
862888
C_bool(bcx.ccx(), discr != Disr(0))
@@ -881,10 +907,9 @@ pub fn trans_set_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
881907
Univariant(_) => {
882908
assert_eq!(discr, Disr(0));
883909
}
884-
RawNullablePointer { nndiscr, nnty, ..} => {
885-
if discr != nndiscr {
886-
let llptrty = type_of::sizing_type_of(bcx.ccx(), nnty);
887-
Store(bcx, C_null(llptrty), val);
910+
RawForbiddenValue { payload_discr, forbidden_value, ..} => {
911+
if discr != payload_discr {
912+
Store(bcx, forbidden_value, val);
888913
}
889914
}
890915
StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
@@ -936,7 +961,7 @@ pub fn trans_field_ptr_builder<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
936961
General(_, ref cases) => {
937962
struct_field_ptr(bcx, &cases[discr.0 as usize], val, ix + 1, true)
938963
}
939-
RawNullablePointer { nndiscr, ref nullfields, .. } |
964+
RawForbiddenValue { payload_discr: nndiscr, unit_fields: ref nullfields, .. } |
940965
StructWrappedNullablePointer { nndiscr, ref nullfields, .. } if discr != nndiscr => {
941966
// The unit-like case might have a nonzero number of unit-like fields.
942967
// (e.d., Result of Either with (), as one side.)
@@ -947,10 +972,10 @@ pub fn trans_field_ptr_builder<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
947972
if bcx.is_unreachable() { return C_undef(ty.ptr_to()); }
948973
bcx.pointercast(val.value, ty.ptr_to())
949974
}
950-
RawNullablePointer { nndiscr, nnty, .. } => {
951-
assert_eq!(ix, 0);
952-
assert_eq!(discr, nndiscr);
953-
let ty = type_of::type_of(bcx.ccx(), nnty);
975+
RawForbiddenValue { payload_discr, payload_ty, .. } => {
976+
assert_eq!(ix, 0); // By definition, the payload of RawForbiddenValue has a single field.
977+
assert_eq!(discr, payload_discr);
978+
let ty = type_of::type_of(bcx.ccx(), payload_ty);
954979
if bcx.is_unreachable() { return C_undef(ty.ptr_to()); }
955980
bcx.pointercast(val.value, ty.ptr_to())
956981
}
@@ -1102,12 +1127,12 @@ pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, r: &Repr<'tcx>, discr
11021127
let contents = build_const_struct(ccx, st, vals);
11031128
C_struct(ccx, &contents[..], st.packed)
11041129
}
1105-
RawNullablePointer { nndiscr, nnty, .. } => {
1106-
if discr == nndiscr {
1107-
assert_eq!(vals.len(), 1);
1130+
RawForbiddenValue { payload_discr, forbidden_value, .. } => {
1131+
if discr == payload_discr {
1132+
assert_eq!(vals.len(), 1); // By definition, the payload has only a single field.
11081133
vals[0]
11091134
} else {
1110-
C_null(type_of::sizing_type_of(ccx, nnty))
1135+
forbidden_value
11111136
}
11121137
}
11131138
StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => {
@@ -1203,14 +1228,14 @@ fn roundup(x: u64, a: u32) -> u64 { let a = a as u64; ((x + (a - 1)) / a) * a }
12031228
///
12041229
/// (Not to be confused with `common::const_get_elt`, which operates on
12051230
/// raw LLVM-level structs and arrays.)
1206-
pub fn const_get_field(r: &Repr, val: ValueRef, _discr: Disr,
1231+
pub fn const_get_field(r: &Repr, val: ValueRef, discr: Disr,
12071232
ix: usize) -> ValueRef {
12081233
match *r {
12091234
CEnum(..) => bug!("element access in C-like enum const"),
12101235
Univariant(..) => const_struct_field(val, ix),
12111236
General(..) => const_struct_field(val, ix + 1),
1212-
RawNullablePointer { .. } => {
1213-
assert_eq!(ix, 0);
1237+
RawForbiddenValue { .. } => {
1238+
assert_eq!(ix, 0); // By definition, the payload only has a single field.
12141239
val
12151240
},
12161241
StructWrappedNullablePointer{ .. } => const_struct_field(val, ix)

src/librustc_trans/debuginfo/metadata.rs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1295,36 +1295,36 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
12951295
]
12961296
}
12971297
}
1298-
adt::RawNullablePointer { nndiscr: non_null_variant_index, nnty, .. } => {
1298+
adt::RawForbiddenValue { payload_discr: payload_variant_index, payload_ty, .. } => {
12991299
// As far as debuginfo is concerned, the pointer this enum
13001300
// represents is still wrapped in a struct. This is to make the
13011301
// DWARF representation of enums uniform.
13021302

13031303
// First create a description of the artificial wrapper struct:
1304-
let non_null_variant = &adt.variants[non_null_variant_index.0 as usize];
1305-
let non_null_variant_name = non_null_variant.name.as_str();
1304+
let payload_variant = &adt.variants[payload_variant_index.0 as usize];
1305+
let payload_variant_name = payload_variant.name.as_str();
13061306

13071307
// The llvm type and metadata of the pointer
1308-
let non_null_llvm_type = type_of::type_of(cx, nnty);
1309-
let non_null_type_metadata = type_metadata(cx, nnty, self.span);
1308+
let payload_llvm_type = type_of::type_of(cx, payload_ty);
1309+
let payload_type_metadata = type_metadata(cx, payload_ty, self.span);
13101310

13111311
// The type of the artificial struct wrapping the pointer
13121312
let artificial_struct_llvm_type = Type::struct_(cx,
1313-
&[non_null_llvm_type],
1313+
&[payload_llvm_type],
13141314
false);
13151315

13161316
// For the metadata of the wrapper struct, we need to create a
13171317
// MemberDescription of the struct's single field.
13181318
let sole_struct_member_description = MemberDescription {
1319-
name: match non_null_variant.kind {
1319+
name: match payload_variant.kind {
13201320
ty::VariantKind::Tuple => "__0".to_string(),
13211321
ty::VariantKind::Struct => {
1322-
non_null_variant.fields[0].name.to_string()
1322+
payload_variant.fields[0].name.to_string()
13231323
}
13241324
ty::VariantKind::Unit => bug!()
13251325
},
1326-
llvm_type: non_null_llvm_type,
1327-
type_metadata: non_null_type_metadata,
1326+
llvm_type: payload_llvm_type,
1327+
type_metadata: payload_type_metadata,
13281328
offset: FixedMemberOffset { bytes: 0 },
13291329
flags: FLAGS_NONE
13301330
};
@@ -1334,13 +1334,13 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
13341334
.get_unique_type_id_of_enum_variant(
13351335
cx,
13361336
self.enum_type,
1337-
&non_null_variant_name);
1337+
&payload_variant_name);
13381338

13391339
// Now we can create the metadata of the artificial struct
13401340
let artificial_struct_metadata =
13411341
composite_type_metadata(cx,
13421342
artificial_struct_llvm_type,
1343-
&non_null_variant_name,
1343+
&payload_variant_name,
13441344
unique_type_id,
13451345
&[sole_struct_member_description],
13461346
self.containing_scope,
@@ -1349,11 +1349,11 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
13491349

13501350
// Encode the information about the null variant in the union
13511351
// member's name.
1352-
let null_variant_index = (1 - non_null_variant_index.0) as usize;
1353-
let null_variant_name = adt.variants[null_variant_index].name;
1352+
let unit_variant_index = (1 - payload_variant_index.0) as usize;
1353+
let unit_variant_name = adt.variants[unit_variant_index].name;
13541354
let union_member_name = format!("RUST$ENCODED$ENUM${}${}",
13551355
0,
1356-
null_variant_name);
1356+
unit_variant_name);
13571357

13581358
// Finally create the (singleton) list of descriptions of union
13591359
// members.
@@ -1607,7 +1607,7 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
16071607
adt::CEnum(inttype, _, _) => {
16081608
return FinalMetadata(discriminant_type_metadata(inttype))
16091609
},
1610-
adt::RawNullablePointer { .. } |
1610+
adt::RawForbiddenValue { .. } |
16111611
adt::StructWrappedNullablePointer { .. } |
16121612
adt::Univariant(..) => None,
16131613
adt::General(inttype, _) => Some(discriminant_type_metadata(inttype)),

src/librustc_trans/disr.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
/// Representation of single value in a C-style enum.
1112
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
1213
pub struct Disr(pub u64);
1314

0 commit comments

Comments
 (0)