Rollup merge of #131698 - the8472:remove-set-discriminant-hack, r=RalfJung
use stores of the correct size to set discriminants Resolves an old HACK /FIXME. Note that I haven't worked much with codegen so I'm not sure if I'm using the functions correctly and I was surprised seeing out-of-range values being fed into `const_uint_big` but apparently they're wrapped implicitly? By making it explicit we can pass in-range values instead.
This commit is contained in:
commit
3029e09e2f
3 changed files with 61 additions and 8 deletions
|
@ -1,5 +1,6 @@
|
||||||
use rustc_abi::Primitive::{Int, Pointer};
|
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::mir::tcx::PlaceTy;
|
||||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
|
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty};
|
||||||
|
@ -385,15 +386,22 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
|
||||||
if variant_index != untagged_variant {
|
if variant_index != untagged_variant {
|
||||||
let niche = self.project_field(bx, tag_field);
|
let niche = self.project_field(bx, tag_field);
|
||||||
let niche_llty = bx.cx().immediate_backend_type(niche.layout);
|
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 = variant_index.as_u32() - niche_variants.start().as_u32();
|
||||||
let niche_value = (niche_value as u128).wrapping_add(niche_start);
|
let niche_value = (niche_value as u128).wrapping_add(niche_start);
|
||||||
// FIXME(eddyb): check the actual primitive type here.
|
let niche_value = niche_value & niche.layout.size.unsigned_int_max();
|
||||||
let niche_llval = if niche_value == 0 {
|
|
||||||
// HACK(eddyb): using `c_null` as it works on all types.
|
let niche_llval = bx.cx().scalar_to_backend(
|
||||||
bx.cx().const_null(niche_llty)
|
Scalar::from_uint(niche_value, niche.layout.size),
|
||||||
} else {
|
scalar,
|
||||||
bx.cx().const_uint_big(niche_llty, niche_value)
|
niche_llty,
|
||||||
};
|
);
|
||||||
OperandValue::Immediate(niche_llval).store(bx, niche);
|
OperandValue::Immediate(niche_llval).store(bx, niche);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
38
tests/ui/enum-discriminant/ptr_niche.rs
Normal file
38
tests/ui/enum-discriminant/ptr_niche.rs
Normal file
|
@ -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));
|
||||||
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
#![feature(never_type)]
|
#![feature(never_type)]
|
||||||
#![feature(pointer_is_aligned_to)]
|
#![feature(pointer_is_aligned_to)]
|
||||||
|
#![feature(rustc_attrs)]
|
||||||
|
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::num::NonZero;
|
use std::num::NonZero;
|
||||||
|
@ -237,6 +238,10 @@ struct VecDummy {
|
||||||
len: usize,
|
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() {
|
pub fn main() {
|
||||||
assert_eq!(size_of::<u8>(), 1 as usize);
|
assert_eq!(size_of::<u8>(), 1 as usize);
|
||||||
assert_eq!(size_of::<u32>(), 4 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!(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>());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue