Skip to content

Commit 71817da

Browse files
author
David Rajchenbach-Teller
committed
Issue #5977 - Generalizing RawNullablePointer to RawForbiddenValue
1 parent 59b7c90 commit 71817da

File tree

3 files changed

+110
-69
lines changed

3 files changed

+110
-69
lines changed

src/librustc_trans/trans/adt.rs

+93-53
Original file line numberDiff line numberDiff line change
@@ -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>,
748778
pub 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.)
14711506
pub 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
}

src/librustc_trans/trans/debuginfo/metadata.rs

+16-16
Original file line numberDiff line numberDiff line change
@@ -1335,36 +1335,36 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
13351335
]
13361336
}
13371337
}
1338-
adt::RawNullablePointer { nndiscr: non_null_variant_index, nnty, .. } => {
1338+
adt::RawForbiddenValue { payload_discr: payload_variant_index, payload_ty, .. } => {
13391339
// As far as debuginfo is concerned, the pointer this enum
13401340
// represents is still wrapped in a struct. This is to make the
13411341
// DWARF representation of enums uniform.
13421342

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

13471347
// The llvm type and metadata of the pointer
1348-
let non_null_llvm_type = type_of::type_of(cx, nnty);
1349-
let non_null_type_metadata = type_metadata(cx, nnty, self.span);
1348+
let payload_llvm_type = type_of::type_of(cx, payload_ty);
1349+
let payload_type_metadata = type_metadata(cx, payload_ty, self.span);
13501350

13511351
// The type of the artificial struct wrapping the pointer
13521352
let artificial_struct_llvm_type = Type::struct_(cx,
1353-
&[non_null_llvm_type],
1353+
&[payload_llvm_type],
13541354
false);
13551355

13561356
// For the metadata of the wrapper struct, we need to create a
13571357
// MemberDescription of the struct's single field.
13581358
let sole_struct_member_description = MemberDescription {
1359-
name: match non_null_variant.kind() {
1359+
name: match payload_variant.kind() {
13601360
ty::VariantKind::Tuple => "__0".to_string(),
13611361
ty::VariantKind::Struct => {
1362-
non_null_variant.fields[0].name.to_string()
1362+
payload_variant.fields[0].name.to_string()
13631363
}
13641364
ty::VariantKind::Unit => unreachable!()
13651365
},
1366-
llvm_type: non_null_llvm_type,
1367-
type_metadata: non_null_type_metadata,
1366+
llvm_type: payload_llvm_type,
1367+
type_metadata: payload_type_metadata,
13681368
offset: FixedMemberOffset { bytes: 0 },
13691369
flags: FLAGS_NONE
13701370
};
@@ -1374,13 +1374,13 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
13741374
.get_unique_type_id_of_enum_variant(
13751375
cx,
13761376
self.enum_type,
1377-
&non_null_variant_name);
1377+
&payload_variant_name);
13781378

13791379
// Now we can create the metadata of the artificial struct
13801380
let artificial_struct_metadata =
13811381
composite_type_metadata(cx,
13821382
artificial_struct_llvm_type,
1383-
&non_null_variant_name,
1383+
&payload_variant_name,
13841384
unique_type_id,
13851385
&[sole_struct_member_description],
13861386
self.containing_scope,
@@ -1389,11 +1389,11 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
13891389

13901390
// Encode the information about the null variant in the union
13911391
// member's name.
1392-
let null_variant_index = (1 - non_null_variant_index.0) as usize;
1393-
let null_variant_name = adt.variants[null_variant_index].name;
1392+
let unit_variant_index = (1 - payload_variant_index.0) as usize;
1393+
let unit_variant_name = adt.variants[unit_variant_index].name;
13941394
let union_member_name = format!("RUST$ENCODED$ENUM${}${}",
13951395
0,
1396-
null_variant_name);
1396+
unit_variant_name);
13971397

13981398
// Finally create the (singleton) list of descriptions of union
13991399
// members.
@@ -1647,7 +1647,7 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
16471647
adt::CEnum(inttype, _, _) => {
16481648
return FinalMetadata(discriminant_type_metadata(inttype))
16491649
},
1650-
adt::RawNullablePointer { .. } |
1650+
adt::RawForbiddenValue { .. } |
16511651
adt::StructWrappedNullablePointer { .. } |
16521652
adt::Univariant(..) => None,
16531653
adt::General(inttype, _, _) => Some(discriminant_type_metadata(inttype)),

src/librustc_trans/trans/disr.rs

+1
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)