rustc_const_eval/util/
check_validity_requirement.rs1use rustc_abi::{BackendRepr, FieldsShape, Scalar, Variants};
2use rustc_middle::ty::layout::{
3 HasTyCtxt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement,
4};
5use rustc_middle::ty::print::with_no_trimmed_paths;
6use rustc_middle::ty::{PseudoCanonicalInput, ScalarInt, Ty, TyCtxt};
7use rustc_middle::{bug, ty};
8use rustc_span::DUMMY_SP;
9
10use crate::const_eval::{CanAccessMutGlobal, CheckAlignment, CompileTimeMachine};
11use crate::interpret::{InterpCx, MemoryKind};
12
13pub fn check_validity_requirement<'tcx>(
26 tcx: TyCtxt<'tcx>,
27 kind: ValidityRequirement,
28 input: PseudoCanonicalInput<'tcx, Ty<'tcx>>,
29) -> Result<bool, &'tcx LayoutError<'tcx>> {
30 let layout = tcx.layout_of(input)?;
31
32 if kind == ValidityRequirement::Inhabited {
34 return Ok(!layout.is_uninhabited());
35 }
36
37 let layout_cx = LayoutCx::new(tcx, input.typing_env);
38 if kind == ValidityRequirement::Uninit || tcx.sess.opts.unstable_opts.strict_init_checks {
39 Ok(check_validity_requirement_strict(layout, &layout_cx, kind))
40 } else {
41 check_validity_requirement_lax(layout, &layout_cx, kind)
42 }
43}
44
45fn check_validity_requirement_strict<'tcx>(
48 ty: TyAndLayout<'tcx>,
49 cx: &LayoutCx<'tcx>,
50 kind: ValidityRequirement,
51) -> bool {
52 let machine = CompileTimeMachine::new(CanAccessMutGlobal::No, CheckAlignment::Error);
53
54 let mut cx = InterpCx::new(cx.tcx(), DUMMY_SP, cx.typing_env, machine);
55
56 let allocated =
58 cx.allocate(ty, MemoryKind::Stack).expect("OOM: failed to allocate for uninit check");
59
60 if kind == ValidityRequirement::Zero {
61 cx.write_bytes_ptr(
62 allocated.ptr(),
63 std::iter::repeat_n(0_u8, ty.layout.size().bytes_usize()),
64 )
65 .expect("failed to write bytes for zero valid check");
66 }
67
68 {
let _guard = NoTrimmedGuard::new();
cx.validate_operand(&allocated.into(), false,
false).discard_err().is_some()
}with_no_trimmed_paths!(
79 cx.validate_operand(
80 &allocated.into(),
81 false,
82 false,
83 )
84 .discard_err()
85 .is_some()
86 )
87}
88
89fn check_validity_requirement_lax<'tcx>(
92 this: TyAndLayout<'tcx>,
93 cx: &LayoutCx<'tcx>,
94 init_kind: ValidityRequirement,
95) -> Result<bool, &'tcx LayoutError<'tcx>> {
96 let scalar_allows_raw_init = move |s: Scalar| -> bool {
97 match init_kind {
98 ValidityRequirement::Inhabited => {
99 ::rustc_middle::util::bug::bug_fmt(format_args!("ValidityRequirement::Inhabited should have been handled above"))bug!("ValidityRequirement::Inhabited should have been handled above")
100 }
101 ValidityRequirement::Zero => {
102 s.valid_range(cx).contains(0)
104 }
105 ValidityRequirement::UninitMitigated0x01Fill => {
106 let mut val: u128 = 0x01;
108 for _ in 1..s.size(cx).bytes() {
109 val = (val << 8) | 0x01;
111 }
112 s.valid_range(cx).contains(val)
113 }
114 ValidityRequirement::Uninit => {
115 ::rustc_middle::util::bug::bug_fmt(format_args!("ValidityRequirement::Uninit should have been handled above"))bug!("ValidityRequirement::Uninit should have been handled above")
116 }
117 }
118 };
119
120 let valid = !this.is_uninhabited() && match this.backend_repr {
123 BackendRepr::Scalar(s) => scalar_allows_raw_init(s),
124 BackendRepr::ScalarPair(s1, s2) => {
125 scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2)
126 }
127 BackendRepr::SimdVector { element: s, count } => count == 0 || scalar_allows_raw_init(s),
128 BackendRepr::Memory { .. } => true, BackendRepr::SimdScalableVector { element, .. } => scalar_allows_raw_init(element),
130 };
131
132 if !valid {
133 return Ok(false);
135 }
136
137 if let Some(pointee) = this.ty.builtin_deref(false) {
139 let pointee = cx.layout_of(pointee)?;
140 if pointee.align.bytes() > 1 {
142 return Ok(false);
144 }
145 if pointee.size.bytes() > 0 {
146 return Ok(false);
148 }
149 }
150
151 match &this.fields {
153 FieldsShape::Primitive | FieldsShape::Union { .. } => {}
154 FieldsShape::Array { .. } => {
155 }
158 FieldsShape::Arbitrary { offsets, .. } => {
159 for idx in 0..offsets.len() {
160 if !check_validity_requirement_lax(this.field(cx, idx), cx, init_kind)? {
161 return Ok(false);
163 }
164 }
165 }
166 }
167
168 match &this.variants {
169 Variants::Empty => return Ok(false),
170 Variants::Single { .. } => {
171 }
174 Variants::Multiple { .. } => {
175 }
178 }
179
180 Ok(true)
181}
182
183pub(crate) fn validate_scalar_in_layout<'tcx>(
184 tcx: TyCtxt<'tcx>,
185 scalar: ScalarInt,
186 ty: Ty<'tcx>,
187) -> bool {
188 let machine = CompileTimeMachine::new(CanAccessMutGlobal::No, CheckAlignment::Error);
189
190 let typing_env = ty::TypingEnv::fully_monomorphized();
191 let mut cx = InterpCx::new(tcx, DUMMY_SP, typing_env, machine);
192
193 let Ok(layout) = cx.layout_of(ty) else {
194 ::rustc_middle::util::bug::bug_fmt(format_args!("could not compute layout of {0:?}:{1:?}",
scalar, ty))bug!("could not compute layout of {scalar:?}:{ty:?}")
195 };
196
197 let allocated =
199 cx.allocate(layout, MemoryKind::Stack).expect("OOM: failed to allocate for uninit check");
200
201 cx.write_scalar(scalar, &allocated).unwrap();
202
203 cx.validate_operand(
204 &allocated.into(),
205 false,
206 false,
207 )
208 .discard_err()
209 .is_some()
210}