]>
Commit | Line | Data |
---|---|---|
9c376795 | 1 | use crate::const_eval::CheckAlignment; |
487cf647 | 2 | use std::borrow::Cow; |
487cf647 FG |
3 | |
4 | use either::{Left, Right}; | |
3dfed10e | 5 | |
dfeec247 | 6 | use rustc_hir::def::DefKind; |
ba9703b0 | 7 | use rustc_middle::mir; |
3dfed10e | 8 | use rustc_middle::mir::interpret::ErrorHandled; |
c295e0f8 | 9 | use rustc_middle::mir::pretty::display_allocation; |
ba9703b0 | 10 | use rustc_middle::traits::Reveal; |
c295e0f8 | 11 | use rustc_middle::ty::layout::LayoutOf; |
1b1a35ee | 12 | use rustc_middle::ty::print::with_no_trimmed_paths; |
2b03887a | 13 | use rustc_middle::ty::{self, TyCtxt}; |
dfeec247 | 14 | use rustc_span::source_map::Span; |
04454e1e | 15 | use rustc_target::abi::{self, Abi}; |
487cf647 FG |
16 | |
17 | use super::{CompileTimeEvalContext, CompileTimeInterpreter, ConstEvalErr}; | |
18 | use crate::interpret::eval_nullary_intrinsic; | |
19 | use crate::interpret::{ | |
20 | intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId, | |
21 | Immediate, InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, | |
22 | RefTracking, StackPopCleanup, | |
23 | }; | |
dfeec247 | 24 | |
064997fb | 25 | const NOTE_ON_UNDEFINED_BEHAVIOR_ERROR: &str = "The rules on what exactly is undefined behavior aren't clear, \ |
dfeec247 | 26 | so this check might be overzealous. Please open an issue on the rustc \ |
064997fb | 27 | repository if you believe it should not be considered undefined behavior."; |
dfeec247 XL |
28 | |
29 | // Returns a pointer to where the result lives | |
30 | fn eval_body_using_ecx<'mir, 'tcx>( | |
31 | ecx: &mut CompileTimeEvalContext<'mir, 'tcx>, | |
32 | cid: GlobalId<'tcx>, | |
33 | body: &'mir mir::Body<'tcx>, | |
34 | ) -> InterpResult<'tcx, MPlaceTy<'tcx>> { | |
35 | debug!("eval_body_using_ecx: {:?}, {:?}", cid, ecx.param_env); | |
f035d41b | 36 | let tcx = *ecx.tcx; |
fc512014 XL |
37 | assert!( |
38 | cid.promoted.is_some() | |
39 | || matches!( | |
40 | ecx.tcx.def_kind(cid.instance.def_id()), | |
41 | DefKind::Const | |
5e7ed085 | 42 | | DefKind::Static(_) |
fc512014 XL |
43 | | DefKind::ConstParam |
44 | | DefKind::AnonConst | |
3c0e092e | 45 | | DefKind::InlineConst |
fc512014 XL |
46 | | DefKind::AssocConst |
47 | ), | |
48 | "Unexpected DefKind: {:?}", | |
49 | ecx.tcx.def_kind(cid.instance.def_id()) | |
50 | ); | |
064997fb | 51 | let layout = ecx.layout_of(body.bound_return_ty().subst(tcx, cid.instance.substs))?; |
487cf647 | 52 | assert!(layout.is_sized()); |
136023e0 | 53 | let ret = ecx.allocate(layout, MemoryKind::Stack)?; |
dfeec247 | 54 | |
c295e0f8 XL |
55 | trace!( |
56 | "eval_body_using_ecx: pushing stack frame for global: {}{}", | |
9ffffee4 | 57 | with_no_trimmed_paths!(ecx.tcx.def_path_str(cid.instance.def_id())), |
c295e0f8 XL |
58 | cid.promoted.map_or_else(String::new, |p| format!("::promoted[{:?}]", p)) |
59 | ); | |
dfeec247 | 60 | |
dfeec247 XL |
61 | ecx.push_stack_frame( |
62 | cid.instance, | |
dfeec247 | 63 | body, |
923072b8 | 64 | &ret.into(), |
a2a8927a | 65 | StackPopCleanup::Root { cleanup: false }, |
dfeec247 XL |
66 | )?; |
67 | ||
68 | // The main interpreter loop. | |
487cf647 | 69 | while ecx.step()? {} |
dfeec247 XL |
70 | |
71 | // Intern the result | |
29967ef6 XL |
72 | let intern_kind = if cid.promoted.is_some() { |
73 | InternKind::Promoted | |
74 | } else { | |
75 | match tcx.static_mutability(cid.instance.def_id()) { | |
76 | Some(m) => InternKind::Static(m), | |
77 | None => InternKind::Constant, | |
78 | } | |
dfeec247 | 79 | }; |
9c376795 | 80 | ecx.machine.check_alignment = CheckAlignment::No; // interning doesn't need to respect alignment |
6a06907d | 81 | intern_const_alloc_recursive(ecx, intern_kind, &ret)?; |
f2b60f7d | 82 | // we leave alignment checks off, since this `ecx` will not be used for further evaluation anyway |
dfeec247 XL |
83 | |
84 | debug!("eval_body_using_ecx done: {:?}", *ret); | |
85 | Ok(ret) | |
86 | } | |
87 | ||
88 | /// The `InterpCx` is only meant to be used to do field and index projections into constants for | |
f2b60f7d | 89 | /// `simd_shuffle` and const patterns in match arms. It never performs alignment checks. |
dfeec247 XL |
90 | /// |
91 | /// The function containing the `match` that is currently being analyzed may have generic bounds | |
92 | /// that inform us about the generic bounds of the constant. E.g., using an associated constant | |
93 | /// of a function's generic parameter will require knowledge about the bounds on the generic | |
94 | /// parameter. These bounds are passed to `mk_eval_cx` via the `ParamEnv` argument. | |
95 | pub(super) fn mk_eval_cx<'mir, 'tcx>( | |
96 | tcx: TyCtxt<'tcx>, | |
f035d41b | 97 | root_span: Span, |
dfeec247 XL |
98 | param_env: ty::ParamEnv<'tcx>, |
99 | can_access_statics: bool, | |
100 | ) -> CompileTimeEvalContext<'mir, 'tcx> { | |
101 | debug!("mk_eval_cx: {:?}", param_env); | |
102 | InterpCx::new( | |
f035d41b XL |
103 | tcx, |
104 | root_span, | |
dfeec247 | 105 | param_env, |
9c376795 | 106 | CompileTimeInterpreter::new(tcx.const_eval_limit(), can_access_statics, CheckAlignment::No), |
dfeec247 XL |
107 | ) |
108 | } | |
109 | ||
1b1a35ee XL |
110 | /// This function converts an interpreter value into a constant that is meant for use in the |
111 | /// type system. | |
04454e1e | 112 | #[instrument(skip(ecx), level = "debug")] |
dfeec247 XL |
113 | pub(super) fn op_to_const<'tcx>( |
114 | ecx: &CompileTimeEvalContext<'_, 'tcx>, | |
6a06907d | 115 | op: &OpTy<'tcx>, |
74b04a01 | 116 | ) -> ConstValue<'tcx> { |
dfeec247 XL |
117 | // We do not have value optimizations for everything. |
118 | // Only scalars and slices, since they are very common. | |
f9f354fc | 119 | // Note that further down we turn scalars of uninitialized bits back to `ByRef`. These can result |
dfeec247 | 120 | // from scalar unions that are initialized with one of their zero sized variants. We could |
f9f354fc | 121 | // instead allow `ConstValue::Scalar` to store `ScalarMaybeUninit`, but that would affect all |
dfeec247 XL |
122 | // the usual cases of extracting e.g. a `usize`, without there being a real use case for the |
123 | // `Undef` situation. | |
124 | let try_as_immediate = match op.layout.abi { | |
04454e1e | 125 | Abi::Scalar(abi::Scalar::Initialized { .. }) => true, |
1b1a35ee XL |
126 | Abi::ScalarPair(..) => match op.layout.ty.kind() { |
127 | ty::Ref(_, inner, _) => match *inner.kind() { | |
dfeec247 XL |
128 | ty::Slice(elem) => elem == ecx.tcx.types.u8, |
129 | ty::Str => true, | |
130 | _ => false, | |
131 | }, | |
132 | _ => false, | |
133 | }, | |
134 | _ => false, | |
135 | }; | |
136 | let immediate = if try_as_immediate { | |
487cf647 | 137 | Right(ecx.read_immediate(op).expect("normalization works on validated constants")) |
dfeec247 XL |
138 | } else { |
139 | // It is guaranteed that any non-slice scalar pair is actually ByRef here. | |
140 | // When we come back from raw const eval, we are always by-ref. The only way our op here is | |
923072b8 | 141 | // by-val is if we are in destructure_mir_constant, i.e., if this is (a field of) something that we |
dfeec247 XL |
142 | // "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or |
143 | // structs containing such. | |
487cf647 | 144 | op.as_mplace_or_imm() |
dfeec247 XL |
145 | }; |
146 | ||
04454e1e FG |
147 | debug!(?immediate); |
148 | ||
136023e0 | 149 | // We know `offset` is relative to the allocation, so we can use `into_parts`. |
04454e1e FG |
150 | let to_const_value = |mplace: &MPlaceTy<'_>| { |
151 | debug!("to_const_value(mplace: {:?})", mplace); | |
152 | match mplace.ptr.into_parts() { | |
153 | (Some(alloc_id), offset) => { | |
154 | let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory(); | |
155 | ConstValue::ByRef { alloc, offset } | |
156 | } | |
157 | (None, offset) => { | |
158 | assert!(mplace.layout.is_zst()); | |
159 | assert_eq!( | |
160 | offset.bytes() % mplace.layout.align.abi.bytes(), | |
161 | 0, | |
162 | "this MPlaceTy must come from a validated constant, thus we can assume the \ | |
1b1a35ee | 163 | alignment is correct", |
04454e1e | 164 | ); |
064997fb | 165 | ConstValue::ZeroSized |
04454e1e | 166 | } |
dfeec247 XL |
167 | } |
168 | }; | |
74b04a01 | 169 | match immediate { |
487cf647 | 170 | Left(ref mplace) => to_const_value(mplace), |
dfeec247 | 171 | // see comment on `let try_as_immediate` above |
487cf647 | 172 | Right(imm) => match *imm { |
064997fb | 173 | _ if imm.layout.is_zst() => ConstValue::ZeroSized, |
f2b60f7d | 174 | Immediate::Scalar(x) => ConstValue::Scalar(x), |
ba9703b0 | 175 | Immediate::ScalarPair(a, b) => { |
04454e1e | 176 | debug!("ScalarPair(a: {:?}, b: {:?})", a, b); |
136023e0 | 177 | // We know `offset` is relative to the allocation, so we can use `into_parts`. |
064997fb FG |
178 | let (data, start) = match a.to_pointer(ecx).unwrap().into_parts() { |
179 | (Some(alloc_id), offset) => { | |
180 | (ecx.tcx.global_alloc(alloc_id).unwrap_memory(), offset.bytes()) | |
181 | } | |
182 | (None, _offset) => ( | |
9ffffee4 | 183 | ecx.tcx.mk_const_alloc(Allocation::from_bytes_byte_aligned_immutable( |
064997fb FG |
184 | b"" as &[u8], |
185 | )), | |
186 | 0, | |
187 | ), | |
188 | }; | |
9ffffee4 | 189 | let len = b.to_target_usize(ecx).unwrap(); |
ba9703b0 XL |
190 | let start = start.try_into().unwrap(); |
191 | let len: usize = len.try_into().unwrap(); | |
192 | ConstValue::Slice { data, start, end: start + len } | |
193 | } | |
064997fb | 194 | Immediate::Uninit => to_const_value(&op.assert_mem_place()), |
dfeec247 | 195 | }, |
74b04a01 | 196 | } |
dfeec247 XL |
197 | } |
198 | ||
f2b60f7d | 199 | #[instrument(skip(tcx), level = "debug", ret)] |
923072b8 | 200 | pub(crate) fn turn_into_const_value<'tcx>( |
dfeec247 | 201 | tcx: TyCtxt<'tcx>, |
1b1a35ee | 202 | constant: ConstAlloc<'tcx>, |
dfeec247 | 203 | key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, |
1b1a35ee | 204 | ) -> ConstValue<'tcx> { |
dfeec247 XL |
205 | let cid = key.value; |
206 | let def_id = cid.instance.def.def_id(); | |
207 | let is_static = tcx.is_static(def_id); | |
353b0b11 | 208 | // This is just accessing an already computed constant, so no need to check alignment here. |
f2b60f7d FG |
209 | let ecx = mk_eval_cx( |
210 | tcx, | |
211 | tcx.def_span(key.value.instance.def_id()), | |
212 | key.param_env, | |
213 | /*can_access_statics:*/ is_static, | |
214 | ); | |
dfeec247 | 215 | |
1b1a35ee XL |
216 | let mplace = ecx.raw_const_to_mplace(constant).expect( |
217 | "can only fail if layout computation failed, \ | |
218 | which should have given a good error before ever invoking this function", | |
219 | ); | |
220 | assert!( | |
221 | !is_static || cid.promoted.is_some(), | |
222 | "the `eval_to_const_value_raw` query should not be used for statics, use `eval_to_allocation` instead" | |
223 | ); | |
04454e1e | 224 | |
1b1a35ee | 225 | // Turn this into a proper constant. |
f2b60f7d | 226 | op_to_const(&ecx, &mplace.into()) |
dfeec247 XL |
227 | } |
228 | ||
923072b8 | 229 | #[instrument(skip(tcx), level = "debug")] |
1b1a35ee | 230 | pub fn eval_to_const_value_raw_provider<'tcx>( |
dfeec247 XL |
231 | tcx: TyCtxt<'tcx>, |
232 | key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, | |
1b1a35ee | 233 | ) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> { |
5099ac24 | 234 | assert!(key.param_env.is_const()); |
6a06907d | 235 | // see comment in eval_to_allocation_raw_provider for what we're doing here |
f035d41b | 236 | if key.param_env.reveal() == Reveal::All { |
dfeec247 | 237 | let mut key = key; |
f035d41b | 238 | key.param_env = key.param_env.with_user_facing(); |
1b1a35ee | 239 | match tcx.eval_to_const_value_raw(key) { |
dfeec247 XL |
240 | // try again with reveal all as requested |
241 | Err(ErrorHandled::TooGeneric) => {} | |
74b04a01 | 242 | // deduplicate calls |
dfeec247 XL |
243 | other => return other, |
244 | } | |
245 | } | |
246 | ||
247 | // We call `const_eval` for zero arg intrinsics, too, in order to cache their value. | |
248 | // Catch such calls and evaluate them instead of trying to load a constant's MIR. | |
249 | if let ty::InstanceDef::Intrinsic(def_id) = key.value.instance.def { | |
3dfed10e | 250 | let ty = key.value.instance.ty(tcx, key.param_env); |
5e7ed085 FG |
251 | let ty::FnDef(_, substs) = ty.kind() else { |
252 | bug!("intrinsic with type {:?}", ty); | |
dfeec247 XL |
253 | }; |
254 | return eval_nullary_intrinsic(tcx, key.param_env, def_id, substs).map_err(|error| { | |
255 | let span = tcx.def_span(def_id); | |
6a06907d | 256 | let error = ConstEvalErr { error: error.into_kind(), stacktrace: vec![], span }; |
487cf647 | 257 | error.report(tcx.at(span), "could not evaluate nullary intrinsic") |
dfeec247 XL |
258 | }); |
259 | } | |
260 | ||
1b1a35ee | 261 | tcx.eval_to_allocation_raw(key).map(|val| turn_into_const_value(tcx, val, key)) |
dfeec247 XL |
262 | } |
263 | ||
923072b8 | 264 | #[instrument(skip(tcx), level = "debug")] |
1b1a35ee | 265 | pub fn eval_to_allocation_raw_provider<'tcx>( |
dfeec247 XL |
266 | tcx: TyCtxt<'tcx>, |
267 | key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, | |
1b1a35ee | 268 | ) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> { |
5099ac24 | 269 | assert!(key.param_env.is_const()); |
dfeec247 XL |
270 | // Because the constant is computed twice (once per value of `Reveal`), we are at risk of |
271 | // reporting the same error twice here. To resolve this, we check whether we can evaluate the | |
272 | // constant in the more restrictive `Reveal::UserFacing`, which most likely already was | |
273 | // computed. For a large percentage of constants that will already have succeeded. Only | |
274 | // associated constants of generic functions will fail due to not enough monomorphization | |
275 | // information being available. | |
276 | ||
277 | // In case we fail in the `UserFacing` variant, we just do the real computation. | |
f035d41b | 278 | if key.param_env.reveal() == Reveal::All { |
dfeec247 | 279 | let mut key = key; |
f035d41b | 280 | key.param_env = key.param_env.with_user_facing(); |
1b1a35ee | 281 | match tcx.eval_to_allocation_raw(key) { |
dfeec247 XL |
282 | // try again with reveal all as requested |
283 | Err(ErrorHandled::TooGeneric) => {} | |
74b04a01 | 284 | // deduplicate calls |
dfeec247 XL |
285 | other => return other, |
286 | } | |
287 | } | |
288 | if cfg!(debug_assertions) { | |
289 | // Make sure we format the instance even if we do not print it. | |
290 | // This serves as a regression test against an ICE on printing. | |
291 | // The next two lines concatenated contain some discussion: | |
292 | // https://rust-lang.zulipchat.com/#narrow/stream/146212-t-compiler.2Fconst-eval/ | |
293 | // subject/anon_const_instance_printing/near/135980032 | |
5e7ed085 | 294 | let instance = with_no_trimmed_paths!(key.value.instance.to_string()); |
dfeec247 XL |
295 | trace!("const eval: {:?} ({})", key, instance); |
296 | } | |
297 | ||
298 | let cid = key.value; | |
3dfed10e | 299 | let def = cid.instance.def.with_opt_param(); |
3dfed10e | 300 | let is_static = tcx.is_static(def.did); |
dfeec247 | 301 | |
dfeec247 | 302 | let mut ecx = InterpCx::new( |
f035d41b | 303 | tcx, |
3dfed10e | 304 | tcx.def_span(def.did), |
dfeec247 | 305 | key.param_env, |
5869c6ff XL |
306 | // Statics (and promoteds inside statics) may access other statics, because unlike consts |
307 | // they do not have to behave "as if" they were evaluated at runtime. | |
f2b60f7d FG |
308 | CompileTimeInterpreter::new( |
309 | tcx.const_eval_limit(), | |
310 | /*can_access_statics:*/ is_static, | |
9c376795 FG |
311 | if tcx.sess.opts.unstable_opts.extra_const_ub_checks { |
312 | CheckAlignment::Error | |
313 | } else { | |
314 | CheckAlignment::FutureIncompat | |
315 | }, | |
f2b60f7d | 316 | ), |
dfeec247 XL |
317 | ); |
318 | ||
319 | let res = ecx.load_mir(cid.instance.def, cid.promoted); | |
1b1a35ee XL |
320 | match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) { |
321 | Err(error) => { | |
3dfed10e | 322 | let err = ConstEvalErr::new(&ecx, error, None); |
2b03887a FG |
323 | let msg = if is_static { |
324 | Cow::from("could not evaluate static initializer") | |
dfeec247 | 325 | } else { |
2b03887a FG |
326 | // If the current item has generics, we'd like to enrich the message with the |
327 | // instance and its substs: to show the actual compile-time values, in addition to | |
328 | // the expression, leading to the const eval error. | |
329 | let instance = &key.value.instance; | |
330 | if !instance.substs.is_empty() { | |
331 | let instance = with_no_trimmed_paths!(instance.to_string()); | |
332 | let msg = format!("evaluation of `{}` failed", instance); | |
333 | Cow::from(msg) | |
5869c6ff | 334 | } else { |
2b03887a FG |
335 | Cow::from("evaluation of constant value failed") |
336 | } | |
337 | }; | |
17df50a5 | 338 | |
487cf647 | 339 | Err(err.report(ecx.tcx.at(err.span), &msg)) |
1b1a35ee XL |
340 | } |
341 | Ok(mplace) => { | |
5869c6ff XL |
342 | // Since evaluation had no errors, validate the resulting constant. |
343 | // This is a separate `try` block to provide more targeted error reporting. | |
1b1a35ee | 344 | let validation = try { |
fc512014 XL |
345 | let mut ref_tracking = RefTracking::new(mplace); |
346 | let mut inner = false; | |
347 | while let Some((mplace, path)) = ref_tracking.todo.pop() { | |
348 | let mode = match tcx.static_mutability(cid.instance.def_id()) { | |
349 | Some(_) if cid.promoted.is_some() => { | |
350 | // Promoteds in statics are allowed to point to statics. | |
351 | CtfeValidationMode::Const { inner, allow_static_ptrs: true } | |
352 | } | |
353 | Some(_) => CtfeValidationMode::Regular, // a `static` | |
354 | None => CtfeValidationMode::Const { inner, allow_static_ptrs: false }, | |
355 | }; | |
6a06907d | 356 | ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?; |
fc512014 | 357 | inner = true; |
1b1a35ee XL |
358 | } |
359 | }; | |
136023e0 | 360 | let alloc_id = mplace.ptr.provenance.unwrap(); |
1b1a35ee | 361 | if let Err(error) = validation { |
5869c6ff | 362 | // Validation failed, report an error. This is always a hard error. |
1b1a35ee | 363 | let err = ConstEvalErr::new(&ecx, error, None); |
487cf647 | 364 | Err(err.report_decorated( |
1b1a35ee XL |
365 | ecx.tcx, |
366 | "it is undefined behavior to use this value", | |
5e7ed085 | 367 | |diag| { |
064997fb FG |
368 | if matches!(err.error, InterpError::UndefinedBehavior(_)) { |
369 | diag.note(NOTE_ON_UNDEFINED_BEHAVIOR_ERROR); | |
370 | } | |
cdc7bbd5 XL |
371 | diag.note(&format!( |
372 | "the raw bytes of the constant ({}", | |
373 | display_allocation( | |
374 | *ecx.tcx, | |
5e7ed085 | 375 | ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner() |
cdc7bbd5 XL |
376 | ) |
377 | )); | |
1b1a35ee XL |
378 | }, |
379 | )) | |
380 | } else { | |
381 | // Convert to raw constant | |
136023e0 | 382 | Ok(ConstAlloc { alloc_id, ty: mplace.layout.ty }) |
1b1a35ee XL |
383 | } |
384 | } | |
385 | } | |
dfeec247 | 386 | } |