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::{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 =
57        cx.allocate(ty, MemoryKind::Stack).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_n(0_u8, 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.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
188    let allocated =
190        cx.allocate(layout, MemoryKind::Stack).expect("OOM: failed to allocate for uninit check");
191
192    cx.write_scalar(scalar, &allocated).unwrap();
193
194    cx.validate_operand(
195        &allocated.into(),
196        false,
197        false,
198    )
199    .discard_err()
200    .is_some()
201}