Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
improve TagEncoding::Niche docs and sanity check
  • Loading branch information
RalfJung committed Nov 30, 2024
commit ce95a44db65f7f595812a52df6b2f0bc479bd290
24 changes: 19 additions & 5 deletions compiler/rustc_abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1215,6 +1215,15 @@ impl Scalar {
Scalar::Union { .. } => true,
}
}

/// Returns `true` if this is a signed integer scalar
#[inline]
pub fn is_signed(&self) -> bool {
match self.primitive() {
Primitive::Int(_, signed) => signed,
_ => false,
}
}
}

// NOTE: This struct is generic over the FieldIdx for rust-analyzer usage.
Expand Down Expand Up @@ -1401,10 +1410,7 @@ impl BackendRepr {
#[inline]
pub fn is_signed(&self) -> bool {
match self {
BackendRepr::Scalar(scal) => match scal.primitive() {
Primitive::Int(_, signed) => signed,
_ => false,
},
BackendRepr::Scalar(scal) => scal.is_signed(),
_ => panic!("`is_signed` on non-scalar ABI {self:?}"),
}
}
Expand Down Expand Up @@ -1528,14 +1534,22 @@ pub enum TagEncoding<VariantIdx: Idx> {
/// The variant `untagged_variant` contains a niche at an arbitrary
/// offset (field `tag_field` of the enum), which for a variant with
/// discriminant `d` is set to
/// `(d - niche_variants.start).wrapping_add(niche_start)`.
/// `(d - niche_variants.start).wrapping_add(niche_start)`
/// (this is wrapping arithmetic using the type of the niche field).
///
/// For example, `Option<(usize, &T)>` is represented such that
/// `None` has a null pointer for the second tuple field, and
/// `Some` is the identity function (with a non-null reference).
///
/// Other variants that are not `untagged_variant` and that are outside the `niche_variants`
/// range cannot be represented; they must be uninhabited.
Niche {
untagged_variant: VariantIdx,
/// This range *may* contain `untagged_variant`; that is then just a "dead value" and
/// not used to encode anything.
niche_variants: RangeInclusive<VariantIdx>,
/// This is inbounds of the type of the niche field
/// (not sign-extended, i.e., all bits beyond the niche field size are 0).
niche_start: u128,
},
}
Expand Down
16 changes: 13 additions & 3 deletions compiler/rustc_ty_utils/src/layout/invariant.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use std::assert_matches::assert_matches;

use rustc_abi::{BackendRepr, FieldsShape, Scalar, Size, Variants};
use rustc_abi::{BackendRepr, FieldsShape, Scalar, Size, TagEncoding, Variants};
use rustc_middle::bug;
use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, TyAndLayout};

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

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

check_layout_abi(cx, layout);

if let Variants::Multiple { variants, .. } = &layout.variants {
if let Variants::Multiple { variants, tag, tag_encoding, .. } = &layout.variants {
if let TagEncoding::Niche { niche_start, untagged_variant, niche_variants } = tag_encoding {
let niche_size = tag.size(cx);
assert!(*niche_start <= niche_size.unsigned_int_max());
for (idx, variant) in variants.iter_enumerated() {
// Ensure all inhabited variants are accounted for.
if !variant.is_uninhabited() {
assert!(idx == *untagged_variant || niche_variants.contains(&idx));
}
}
}
for variant in variants.iter() {
// No nested "multiple".
assert_matches!(variant.variants, Variants::Single { .. });
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Validity makes this fail at the wrong place.
//@compile-flags: -Zmiri-disable-validation
use std::mem;

// This enum has untagged variant idx 1, with niche_variants being 0..=2
// and niche_start being 2.
// That means the untagged variants is in the niche variant range!
// However, using the corresponding value (2+1 = 3) is not a valid encoding of this variant.
#[derive(Copy, Clone, PartialEq)]
enum Foo {
Var1,
Var2(bool),
Var3,
}

fn main() {
unsafe {
assert!(Foo::Var2(false) == mem::transmute(0u8));
assert!(Foo::Var2(true) == mem::transmute(1u8));
assert!(Foo::Var1 == mem::transmute(2u8));
assert!(Foo::Var3 == mem::transmute(4u8));

let invalid: Foo = mem::transmute(3u8);
assert!(matches!(invalid, Foo::Var2(_)));
//~^ ERROR: invalid tag
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error: Undefined Behavior: enum value has invalid tag: 0x03
--> tests/fail/enum-untagged-variant-invalid-encoding.rs:LL:CC
|
LL | assert!(matches!(invalid, Foo::Var2(_)));
| ^^^^^^^ enum value has invalid tag: 0x03
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at tests/fail/enum-untagged-variant-invalid-encoding.rs:LL:CC

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

error: aborting due to 1 previous error