Skip to content

Commit 3d94e25

Browse files
committed
Properly emit a slice of Invariant, add tests
1 parent 97ce1fb commit 3d94e25

File tree

11 files changed

+169
-58
lines changed

11 files changed

+169
-58
lines changed

compiler/rustc_codegen_ssa/src/mir/operand.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,22 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
100100
let b_llval = bx.const_usize((end - start) as u64);
101101
OperandValue::Pair(a_llval, b_llval)
102102
}
103+
ConstValue::CustomSlice { data, length } => {
104+
let Abi::ScalarPair(a_scalar, _) = layout.abi else {
105+
bug!("from_const: invalid ScalarPair layout: {:#?}", layout);
106+
};
107+
let a = Scalar::from_pointer(
108+
Pointer::new(bx.tcx().create_memory_alloc(data), Size::ZERO),
109+
&bx.tcx(),
110+
);
111+
let a_llval = bx.scalar_to_backend(
112+
a,
113+
a_scalar,
114+
bx.scalar_pair_element_backend_type(layout, 0, true),
115+
);
116+
let b_llval = bx.const_usize(length as u64);
117+
OperandValue::Pair(a_llval, b_llval)
118+
}
103119
ConstValue::ByRef { alloc, offset } => {
104120
return bx.load_operand(bx.from_const_alloc(layout, alloc, offset));
105121
}

compiler/rustc_const_eval/src/interpret/intrinsics.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use std::convert::TryFrom;
66

77
use rustc_hir::def_id::DefId;
8+
use rustc_hir::lang_items::LangItem;
89
use rustc_middle::mir::{
910
self,
1011
interpret::{ConstValue, GlobalId, InterpResult, Scalar},
@@ -106,8 +107,8 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>(
106107
},
107108
sym::validity_invariants_of => {
108109
ensure_monomorphic_enough(tcx, tp_ty)?;
109-
let alloc = validity_invariants_of::alloc_validity_invariants_of(tcx, tp_ty);
110-
ConstValue::Slice { data: alloc, start: 0, end: alloc.inner().len() }
110+
let (data, length) = validity_invariants_of::alloc_validity_invariants_of(tcx, tp_ty);
111+
ConstValue::CustomSlice { data, length }
111112
}
112113
other => bug!("`{}` is not a zero arg intrinsic", other),
113114
})
@@ -176,7 +177,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
176177
sym::needs_drop => self.tcx.types.bool,
177178
sym::type_id => self.tcx.types.u64,
178179
sym::type_name => self.tcx.mk_static_str(),
179-
sym::validity_invariants_of => self.tcx.mk_static_bytes(),
180+
sym::validity_invariants_of => {
181+
let item = self.tcx.require_lang_item(LangItem::ValidityInvariant, None);
182+
let ty = self.tcx.type_of(item);
183+
self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, self.tcx.mk_slice(ty))
184+
}
180185
_ => bug!(),
181186
};
182187
let val =

compiler/rustc_const_eval/src/interpret/intrinsics/validity_invariants_of.rs

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
use rustc_data_structures::fx::FxHashMap;
22
use rustc_hir::lang_items::LangItem;
33
use rustc_middle::mir::interpret::{AllocRange, Allocation, ConstAllocation, Scalar as MirScalar};
4+
use rustc_middle::mir::Mutability;
45
use rustc_middle::ty::layout::LayoutCx;
56
use rustc_middle::ty::{ParamEnv, ParamEnvAnd};
67
use rustc_middle::ty::{Ty, TyCtxt};
78
use rustc_target::abi::{
8-
Abi, FieldsShape, HasDataLayout, Integer, Primitive, Scalar, Size, TyAndLayout, WrappingRange,
9+
Abi, FieldsShape, HasDataLayout, Integer, Primitive, Scalar, Size, TyAndLayout, WrappingRange, Variants,
910
};
1011

1112
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -25,7 +26,12 @@ struct InvariantKey {
2526
}
2627

2728
// FIXME: Don't add duplicate invariants (maybe use a HashMap?)
28-
fn add_invariants<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, invs: &mut FxHashMap<InvariantKey, WrappingRange>, offset: Size) {
29+
fn add_invariants<'tcx>(
30+
tcx: TyCtxt<'tcx>,
31+
ty: Ty<'tcx>,
32+
invs: &mut FxHashMap<InvariantKey, WrappingRange>,
33+
offset: Size,
34+
) {
2935
let x = tcx.layout_of(ParamEnvAnd { param_env: ParamEnv::reveal_all(), value: ty });
3036

3137
if let Ok(layout) = x {
@@ -46,8 +52,14 @@ fn add_invariants<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, invs: &mut FxHashMap<In
4652
let _: Result<_, _> = invs.try_insert(InvariantKey { offset, size }, valid_range);
4753
}
4854

55+
//dbg!(&ty, &layout);
56+
if !matches!(layout.layout.variants(), Variants::Single { .. }) {
57+
// We *don't* want to look for fields inside enums.
58+
return;
59+
}
60+
4961
let param_env = ParamEnv::reveal_all();
50-
let unwrap = LayoutCx { tcx, param_env };
62+
let layout_cx = LayoutCx { tcx, param_env };
5163

5264
match layout.layout.fields() {
5365
FieldsShape::Primitive => {}
@@ -57,13 +69,13 @@ fn add_invariants<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, invs: &mut FxHashMap<In
5769
// That would lead to false negatives, though.
5870
for idx in 0..*count {
5971
let off = offset + *stride * idx;
60-
let f = layout.field(&unwrap, idx as usize);
72+
let f = layout.field(&layout_cx, idx as usize);
6173
add_invariants(tcx, f.ty, invs, off);
6274
}
6375
}
6476
FieldsShape::Arbitrary { offsets, .. } => {
6577
for (idx, &field_offset) in offsets.iter().enumerate() {
66-
let f = layout.field(&unwrap, idx);
78+
let f = layout.field(&layout_cx, idx);
6779
if f.ty == ty {
6880
// Some types contain themselves as fields, such as
6981
// &mut [T]
@@ -90,7 +102,7 @@ fn get_layout_of_invariant<'tcx>(tcx: TyCtxt<'tcx>) -> TyAndLayout<'tcx, Ty<'tcx
90102
pub(crate) fn alloc_validity_invariants_of<'tcx>(
91103
tcx: TyCtxt<'tcx>,
92104
ty: Ty<'tcx>,
93-
) -> ConstAllocation<'tcx> {
105+
) -> (ConstAllocation<'tcx>, usize) {
94106
let mut invs = FxHashMap::default();
95107

96108
let layout = tcx.data_layout();
@@ -126,22 +138,17 @@ pub(crate) fn alloc_validity_invariants_of<'tcx>(
126138

127139
let offset_range = AllocRange { start: offset + start_off, size: Size::from_bytes(16) };
128140
alloc
129-
.write_scalar(
130-
&tcx,
131-
offset_range,
132-
MirScalar::from_u128(invariant.1.start).into(),
133-
)
141+
.write_scalar(&tcx, offset_range, MirScalar::from_u128(invariant.1.start).into())
134142
.unwrap();
135143

136144
let offset_range = AllocRange { start: offset + end_off, size: Size::from_bytes(16) };
137145
alloc
138-
.write_scalar(
139-
&tcx,
140-
offset_range,
141-
MirScalar::from_u128(invariant.1.end).into(),
142-
)
146+
.write_scalar(&tcx, offset_range, MirScalar::from_u128(invariant.1.end).into())
143147
.unwrap();
144148
}
145149

146-
tcx.intern_const_alloc(alloc)
150+
// The allocation is not mutable, we just needed write_scalar.
151+
alloc.mutability = Mutability::Not;
152+
153+
(tcx.intern_const_alloc(alloc), invs.len())
147154
}

compiler/rustc_const_eval/src/interpret/operand.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
692692
self,
693693
))
694694
}
695+
ConstValue::CustomSlice { data, length } => {
696+
// We rely on mutability being set correctly in `data` to prevent writes
697+
// where none should happen.
698+
let ptr = Pointer::new(
699+
self.tcx.create_memory_alloc(data),
700+
Size::ZERO, // offset: 0
701+
);
702+
Operand::Immediate(Immediate::new_slice(
703+
Scalar::from_pointer(self.global_base_pointer(ptr)?, &*self.tcx),
704+
u64::try_from(length).unwrap(),
705+
self,
706+
))
707+
}
695708
};
696709
Ok(OpTy { op, layout })
697710
}

compiler/rustc_middle/src/mir/interpret/value.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ pub enum ConstValue<'tcx> {
3737
/// Used only for `&[u8]` and `&str`
3838
Slice { data: ConstAllocation<'tcx>, start: usize, end: usize },
3939

40+
/// Like `Slice`, but for types that aren't 1 byte long.
41+
CustomSlice { data: ConstAllocation<'tcx>, length: usize },
42+
4043
/// A value not represented/representable by `Scalar` or `Slice`
4144
ByRef {
4245
/// The backing memory of the value, may contain more memory than needed for just the value
@@ -61,6 +64,9 @@ impl<'a, 'tcx> Lift<'tcx> for ConstValue<'a> {
6164
ConstValue::ByRef { alloc, offset } => {
6265
ConstValue::ByRef { alloc: tcx.lift(alloc)?, offset }
6366
}
67+
ConstValue::CustomSlice { data, length } => {
68+
ConstValue::CustomSlice { data: tcx.lift(data)?, length }
69+
}
6470
})
6571
}
6672
}
@@ -69,7 +75,9 @@ impl<'tcx> ConstValue<'tcx> {
6975
#[inline]
7076
pub fn try_to_scalar(&self) -> Option<Scalar<AllocId>> {
7177
match *self {
72-
ConstValue::ByRef { .. } | ConstValue::Slice { .. } => None,
78+
ConstValue::ByRef { .. }
79+
| ConstValue::Slice { .. }
80+
| ConstValue::CustomSlice { .. } => None,
7381
ConstValue::Scalar(val) => Some(val),
7482
}
7583
}

compiler/rustc_middle/src/mir/pretty.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
451451
let fmt_val = |val: &ConstValue<'tcx>| match val {
452452
ConstValue::Scalar(s) => format!("Scalar({:?})", s),
453453
ConstValue::Slice { .. } => format!("Slice(..)"),
454+
ConstValue::CustomSlice { .. } => format!("CustomSlice(..)"),
454455
ConstValue::ByRef { .. } => format!("ByRef(..)"),
455456
};
456457

@@ -679,7 +680,9 @@ pub fn write_allocations<'tcx>(
679680
ConstValue::Scalar(interpret::Scalar::Int { .. }) => {
680681
Either::Left(Either::Right(std::iter::empty()))
681682
}
682-
ConstValue::ByRef { alloc, .. } | ConstValue::Slice { data: alloc, .. } => {
683+
ConstValue::ByRef { alloc, .. }
684+
| ConstValue::Slice { data: alloc, .. }
685+
| ConstValue::CustomSlice { data: alloc, .. } => {
683686
Either::Right(alloc_ids_from_alloc(alloc))
684687
}
685688
}

compiler/rustc_middle/src/ty/context.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2339,11 +2339,6 @@ impl<'tcx> TyCtxt<'tcx> {
23392339
self.mk_imm_ref(self.lifetimes.re_static, self.types.str_)
23402340
}
23412341

2342-
#[inline]
2343-
pub fn mk_static_bytes(self) -> Ty<'tcx> {
2344-
self.mk_imm_ref(self.lifetimes.re_static, self.mk_slice(self.types.u8))
2345-
}
2346-
23472342
#[inline]
23482343
pub fn mk_adt(self, def: AdtDef<'tcx>, substs: SubstsRef<'tcx>) -> Ty<'tcx> {
23492344
// Take a copy of substs so that we own the vectors inside.

compiler/rustc_typeck/src/check/intrinsic.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::require_same_types;
99

1010
use rustc_errors::struct_span_err;
1111
use rustc_hir as hir;
12+
use rustc_hir::lang_items::LangItem;
1213
use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
1314
use rustc_middle::ty::subst::Subst;
1415
use rustc_middle::ty::{self, TyCtxt};
@@ -192,7 +193,12 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
192193
sym::needs_drop => (1, Vec::new(), tcx.types.bool),
193194

194195
sym::type_name => (1, Vec::new(), tcx.mk_static_str()),
195-
sym::validity_invariants_of => (1, Vec::new(), tcx.mk_static_bytes()),
196+
sym::validity_invariants_of => {
197+
let item = tcx.require_lang_item(LangItem::ValidityInvariant, None);
198+
let ty = tcx.type_of(item);
199+
let slice = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_slice(ty));
200+
(1, Vec::new(), slice)
201+
}
196202
sym::type_id => (1, Vec::new(), tcx.types.u64),
197203
sym::offset | sym::arith_offset => (
198204
1,

library/core/src/intrinsics.rs

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1976,6 +1976,13 @@ extern "rust-intrinsic" {
19761976
/// [`std::hint::black_box`]: crate::hint::black_box
19771977
#[rustc_const_unstable(feature = "const_black_box", issue = "none")]
19781978
pub fn black_box<T>(dummy: T) -> T;
1979+
1980+
/// Returns a list of invaraints that must be valid in order for T to be valid.
1981+
///
1982+
/// This is used internally to allow for runtime assertions inside `MaybeUninit::assume_init`
1983+
#[cfg(not(bootstrap))]
1984+
#[rustc_const_unstable(feature = "const_intrinsic_validity_invariants_of", issue = "none")]
1985+
pub fn validity_invariants_of<T>() -> &'static [Invariant];
19791986
}
19801987

19811988
// Some functions are defined here because they accidentally got made
@@ -2138,10 +2145,10 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
21382145
}
21392146
}
21402147

2141-
#[derive(Debug, Clone, Copy)]
2148+
#[derive(Debug, Clone, Copy, PartialEq)]
21422149
#[repr(u8)]
21432150
#[allow(dead_code)] // https://github.com/rust-lang/rust/issues/85677
2144-
enum InvariantSize {
2151+
pub enum InvariantSize {
21452152
U8 = 0,
21462153
U16 = 1,
21472154
U32 = 2,
@@ -2156,35 +2163,20 @@ enum InvariantSize {
21562163
// Be sure to keep the fields in order (offset, size, start, end), validity_invariants_of.rs needs
21572164
// that.
21582165
#[cfg(not(bootstrap))]
2159-
#[derive(Debug, Clone, Copy)]
2166+
#[derive(Debug, Clone, Copy, PartialEq)]
21602167
#[lang = "ValidityInvariant"]
2161-
struct Invariant {
2168+
pub struct Invariant {
21622169
/// The offset in bytes from the start of the struct
2163-
offset: usize,
2170+
pub offset: usize,
21642171
/// The size/type of the invariant.
2165-
size: InvariantSize,
2172+
pub size: InvariantSize,
21662173
/// The start point of the valid range of this field. Is allowed to be > valid_range_end, see
21672174
/// <https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/abi/struct.WrappingRange.html>
21682175
/// which this follows the semantics of.
2169-
valid_range_start: u128,
2176+
pub valid_range_start: u128,
21702177

21712178
/// The end point of the range.
2172-
valid_range_end: u128,
2173-
}
2174-
2175-
#[cfg(not(bootstrap))]
2176-
/// Returns a list of all validity invariants of the type.
2177-
const fn validity_invariants_of<T>() -> &'static [Invariant] {
2178-
extern "rust-intrinsic" {
2179-
#[rustc_const_unstable(feature = "validity_invariants_of", issue = "none")]
2180-
pub fn validity_invariants_of<T>() -> &'static [u8];
2181-
}
2182-
2183-
let invariants: &'static [u8] = validity_invariants_of::<T>();
2184-
let sz = invariants.len() / core::mem::size_of::<Invariant>();
2185-
2186-
// SAFETY: we know this is valid because the intrinsic promises an aligned slice.
2187-
unsafe { core::slice::from_raw_parts(invariants.as_ptr().cast(), sz) }
2179+
pub valid_range_end: u128,
21882180
}
21892181

21902182
/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
@@ -2445,15 +2437,15 @@ where
24452437
}
24462438

24472439
#[cfg(bootstrap)]
2448-
pub(crate) const unsafe fn assert_validity_of<T>(_: *const T) -> bool {
2440+
pub const unsafe fn assert_validity_of<T>(_: *const T) -> bool {
24492441
true
24502442
}
24512443

24522444
#[cfg(not(bootstrap))]
24532445
/// Asserts that the value at `value` is a valid T.
24542446
///
2455-
/// Best effort, and is UB if the value is invalid.
2456-
pub(crate) unsafe fn assert_validity_of<T>(value: *const T) -> bool {
2447+
/// Best effort, can miss some UB, and is UB if the value is invalid.
2448+
pub unsafe fn assert_validity_of<T>(value: *const T) -> bool {
24572449
// We have to do this, since we call assert_validity_of inside MaybeUninit::assume_init
24582450
// and if we had used ptr::read_unaligned, that would be a recursive call.
24592451
#[repr(packed)]
@@ -2493,11 +2485,11 @@ pub(crate) unsafe fn assert_validity_of<T>(value: *const T) -> bool {
24932485

24942486
if start > end {
24952487
if !((start..=max).contains(&value) || (0..=end).contains(&value)) {
2496-
return false;
2488+
panic!("read value {value} which was not in range 0..={end} or {start}..={max} at offset {off} in type {}", core::any::type_name::<T>());
24972489
}
24982490
} else {
24992491
if !(start..=end).contains(&value) {
2500-
return false;
2492+
panic!("read value {value} which was not in range {start}..={end} at offset {off} in type {}", core::any::type_name::<T>());
25012493
}
25022494
}
25032495
}

library/core/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,6 @@
156156
#![feature(const_slice_from_ref)]
157157
#![feature(const_slice_index)]
158158
#![feature(const_is_char_boundary)]
159-
#![cfg_attr(not(bootstrap), feature(validity_invariants_of))]
160-
//
161159
// Language features:
162160
#![feature(abi_unadjusted)]
163161
#![feature(allow_internal_unsafe)]

0 commit comments

Comments
 (0)