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