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