about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/place.rs24
-rw-r--r--tests/ui/enum-discriminant/ptr_niche.rs38
-rw-r--r--tests/ui/structs-enums/type-sizes.rs7
3 files changed, 61 insertions, 8 deletions
diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs
index b8fa8c0351b..c38484109d2 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 00000000000..32df08bce63
--- /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<PointerWithRange> = None;
+    assert!(ptr.is_none());
+    let raw = unsafe { mem::transmute::<_, usize>(ptr) };
+    assert!(!(1..=100).contains(&raw));
+
+    let ptr: Option<Option<PointerWithRange>> = 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 1961f10bd0a..a8fadcc1d1e 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::<u8>(), 1 as usize);
     assert_eq!(size_of::<u32>(), 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::<Option<PointerWithRange>>(), size_of::<PointerWithRange>());
+    assert_eq!(size_of::<Option<Option<PointerWithRange>>>(), size_of::<PointerWithRange>());
 }