diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index b8fa8c0351b1e..c38484109d2cc 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -1,5 +1,6 @@ use rustc_abi::Primitive::{Int, Pointer}; -use rustc_abi::{Align, FieldsShape, Size, TagEncoding, VariantIdx, Variants}; +use rustc_abi::{Align, BackendRepr, FieldsShape, Size, TagEncoding, VariantIdx, Variants}; +use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Ty}; @@ -385,15 +386,22 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { if variant_index != untagged_variant { let niche = self.project_field(bx, tag_field); let niche_llty = bx.cx().immediate_backend_type(niche.layout); + let BackendRepr::Scalar(scalar) = niche.layout.backend_repr else { + bug!("expected a scalar placeref for the niche"); + }; + // We are supposed to compute `niche_value.wrapping_add(niche_start)` wrapping + // around the `niche`'s type. + // The easiest way to do that is to do wrapping arithmetic on `u128` and then + // masking off any extra bits that occur because we did the arithmetic with too many bits. let niche_value = variant_index.as_u32() - niche_variants.start().as_u32(); let niche_value = (niche_value as u128).wrapping_add(niche_start); - // FIXME(eddyb): check the actual primitive type here. - let niche_llval = if niche_value == 0 { - // HACK(eddyb): using `c_null` as it works on all types. - bx.cx().const_null(niche_llty) - } else { - bx.cx().const_uint_big(niche_llty, niche_value) - }; + let niche_value = niche_value & niche.layout.size.unsigned_int_max(); + + let niche_llval = bx.cx().scalar_to_backend( + Scalar::from_uint(niche_value, niche.layout.size), + scalar, + niche_llty, + ); OperandValue::Immediate(niche_llval).store(bx, niche); } } diff --git a/tests/ui/enum-discriminant/ptr_niche.rs b/tests/ui/enum-discriminant/ptr_niche.rs new file mode 100644 index 0000000000000..32df08bce6345 --- /dev/null +++ b/tests/ui/enum-discriminant/ptr_niche.rs @@ -0,0 +1,38 @@ +//@ run-pass +//! Check that we can codegen setting and getting discriminants, including non-null niches, +//! for enums with a pointer-like ABI. This used to crash llvm. + +#![feature(rustc_attrs)] +use std::{ptr, mem}; + + +#[rustc_layout_scalar_valid_range_start(1)] +#[rustc_layout_scalar_valid_range_end(100)] +#[derive(Copy, Clone)] +struct PointerWithRange(#[allow(dead_code)] *const u8); + + +fn main() { + let val = unsafe { PointerWithRange(ptr::without_provenance(90)) }; + + let ptr = Some(val); + assert!(ptr.is_some()); + let raw = unsafe { mem::transmute::<_, usize>(ptr) }; + assert_eq!(raw, 90); + + let ptr = Some(Some(val)); + assert!(ptr.is_some()); + assert!(ptr.unwrap().is_some()); + let raw = unsafe { mem::transmute::<_, usize>(ptr) }; + assert_eq!(raw, 90); + + let ptr: Option = None; + assert!(ptr.is_none()); + let raw = unsafe { mem::transmute::<_, usize>(ptr) }; + assert!(!(1..=100).contains(&raw)); + + let ptr: Option> = None; + assert!(ptr.is_none()); + let raw = unsafe { mem::transmute::<_, usize>(ptr) }; + assert!(!(1..=100).contains(&raw)); +} diff --git a/tests/ui/structs-enums/type-sizes.rs b/tests/ui/structs-enums/type-sizes.rs index 1961f10bd0aa4..a8fadcc1d1ec6 100644 --- a/tests/ui/structs-enums/type-sizes.rs +++ b/tests/ui/structs-enums/type-sizes.rs @@ -5,6 +5,7 @@ #![allow(dead_code)] #![feature(never_type)] #![feature(pointer_is_aligned_to)] +#![feature(rustc_attrs)] use std::mem::size_of; use std::num::NonZero; @@ -237,6 +238,10 @@ struct VecDummy { len: usize, } +#[rustc_layout_scalar_valid_range_start(1)] +#[rustc_layout_scalar_valid_range_end(100)] +struct PointerWithRange(#[allow(dead_code)] *const u8); + pub fn main() { assert_eq!(size_of::(), 1 as usize); assert_eq!(size_of::(), 4 as usize); @@ -354,4 +359,6 @@ pub fn main() { assert!(ptr::from_ref(&v.a).addr() > ptr::from_ref(&v.b).addr()); + assert_eq!(size_of::>(), size_of::()); + assert_eq!(size_of::>>(), size_of::()); }