Skip to content

Commit c3f1129

Browse files
committed
improve TagEncoding::Niche docs and sanity check
1 parent 76f3ff6 commit c3f1129

File tree

4 files changed

+58
-4
lines changed

4 files changed

+58
-4
lines changed

compiler/rustc_abi/src/lib.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -1528,14 +1528,22 @@ pub enum TagEncoding<VariantIdx: Idx> {
15281528
/// The variant `untagged_variant` contains a niche at an arbitrary
15291529
/// offset (field `tag_field` of the enum), which for a variant with
15301530
/// discriminant `d` is set to
1531-
/// `(d - niche_variants.start).wrapping_add(niche_start)`.
1531+
/// `(d - niche_variants.start).wrapping_add(niche_start)`
1532+
/// (this is wrapping arithmetic using the type of the niche field).
15321533
///
15331534
/// For example, `Option<(usize, &T)>` is represented such that
15341535
/// `None` has a null pointer for the second tuple field, and
15351536
/// `Some` is the identity function (with a non-null reference).
1537+
///
1538+
/// Other variants that are not `untagged_variant` and that are outside the `niche_variants`
1539+
/// range cannot be represented; they must be uninhabited.
15361540
Niche {
15371541
untagged_variant: VariantIdx,
1542+
/// This range *may* contain `untagged_variant`; that is then just a "dead value" and
1543+
/// not used to encode anything.
15381544
niche_variants: RangeInclusive<VariantIdx>,
1545+
/// This is inbounds of the unsigned type of the niche field
1546+
/// (i.e., all bits beyond the niche field size are 0).
15391547
niche_start: u128,
15401548
},
15411549
}

compiler/rustc_ty_utils/src/layout/invariant.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use std::assert_matches::assert_matches;
22

3-
use rustc_abi::{BackendRepr, FieldsShape, Scalar, Size, Variants};
3+
use rustc_abi::{BackendRepr, FieldsShape, Scalar, Size, TagEncoding, Variants};
44
use rustc_middle::bug;
55
use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, TyAndLayout};
66

77
/// Enforce some basic invariants on layouts.
8-
pub(super) fn partially_check_layout<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) {
8+
pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) {
99
let tcx = cx.tcx();
1010

1111
// Type-level uninhabitedness should always imply ABI uninhabitedness.
@@ -241,7 +241,11 @@ pub(super) fn partially_check_layout<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLa
241241

242242
check_layout_abi(cx, layout);
243243

244-
if let Variants::Multiple { variants, .. } = &layout.variants {
244+
if let Variants::Multiple { variants, tag, tag_encoding, .. } = &layout.variants {
245+
if let TagEncoding::Niche { niche_start, .. } = tag_encoding {
246+
let niche_size = tag.size(cx);
247+
assert!(*niche_start <= niche_size.unsigned_int_max());
248+
}
245249
for variant in variants.iter() {
246250
// No nested "multiple".
247251
assert_matches!(variant.variants, Variants::Single { .. });
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Validity makes this fail at the wrong place.
2+
//@compile-flags: -Zmiri-disable-validation
3+
use std::mem;
4+
5+
// This enum has untagged variant idx 1, with niche_variants being 0..=2
6+
// and niche_start being 2.
7+
// That means the untagged variants is in the niche variant range!
8+
// However, using the corresponding value (2+1 = 3) is not a valid encoding of this variant.
9+
#[derive(Copy, Clone, PartialEq)]
10+
enum Foo {
11+
Var1,
12+
Var2(bool),
13+
Var3,
14+
}
15+
16+
fn main() {
17+
unsafe {
18+
assert!(Foo::Var2(false) == mem::transmute(0u8));
19+
assert!(Foo::Var2(true) == mem::transmute(1u8));
20+
assert!(Foo::Var1 == mem::transmute(2u8));
21+
assert!(Foo::Var3 == mem::transmute(4u8));
22+
23+
let invalid: Foo = mem::transmute(3u8);
24+
assert!(matches!(invalid, Foo::Var2(_)));
25+
//~^ ERROR: invalid tag
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: Undefined Behavior: enum value has invalid tag: 0x03
2+
--> tests/fail/enum-untagged-variant-invalid-encoding.rs:LL:CC
3+
|
4+
LL | assert!(matches!(invalid, Foo::Var2(_)));
5+
| ^^^^^^^ enum value has invalid tag: 0x03
6+
|
7+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
8+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9+
= note: BACKTRACE:
10+
= note: inside `main` at tests/fail/enum-untagged-variant-invalid-encoding.rs:LL:CC
11+
12+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
13+
14+
error: aborting due to 1 previous error
15+

0 commit comments

Comments
 (0)