]>
Commit | Line | Data |
---|---|---|
04454e1e | 1 | use super::{CompileTimeEvalContext, CompileTimeInterpreter, ConstEvalErr}; |
dfeec247 XL |
2 | use crate::interpret::eval_nullary_intrinsic; |
3 | use crate::interpret::{ | |
29967ef6 | 4 | intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId, |
f2b60f7d FG |
5 | Immediate, InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, |
6 | RefTracking, StackPopCleanup, | |
dfeec247 | 7 | }; |
3dfed10e | 8 | |
dfeec247 | 9 | use rustc_hir::def::DefKind; |
ba9703b0 | 10 | use rustc_middle::mir; |
3dfed10e | 11 | use rustc_middle::mir::interpret::ErrorHandled; |
c295e0f8 | 12 | use rustc_middle::mir::pretty::display_allocation; |
ba9703b0 | 13 | use rustc_middle::traits::Reveal; |
c295e0f8 | 14 | use rustc_middle::ty::layout::LayoutOf; |
1b1a35ee | 15 | use rustc_middle::ty::print::with_no_trimmed_paths; |
2b03887a | 16 | use rustc_middle::ty::{self, TyCtxt}; |
dfeec247 | 17 | use rustc_span::source_map::Span; |
04454e1e | 18 | use rustc_target::abi::{self, Abi}; |
17df50a5 | 19 | use std::borrow::Cow; |
29967ef6 | 20 | use std::convert::TryInto; |
dfeec247 | 21 | |
064997fb | 22 | const NOTE_ON_UNDEFINED_BEHAVIOR_ERROR: &str = "The rules on what exactly is undefined behavior aren't clear, \ |
dfeec247 | 23 | so this check might be overzealous. Please open an issue on the rustc \ |
064997fb | 24 | repository if you believe it should not be considered undefined behavior."; |
dfeec247 XL |
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); | |
f035d41b | 33 | let tcx = *ecx.tcx; |
fc512014 XL |
34 | assert!( |
35 | cid.promoted.is_some() | |
36 | || matches!( | |
37 | ecx.tcx.def_kind(cid.instance.def_id()), | |
38 | DefKind::Const | |
5e7ed085 | 39 | | DefKind::Static(_) |
fc512014 XL |
40 | | DefKind::ConstParam |
41 | | DefKind::AnonConst | |
3c0e092e | 42 | | DefKind::InlineConst |
fc512014 XL |
43 | | DefKind::AssocConst |
44 | ), | |
45 | "Unexpected DefKind: {:?}", | |
46 | ecx.tcx.def_kind(cid.instance.def_id()) | |
47 | ); | |
064997fb | 48 | let layout = ecx.layout_of(body.bound_return_ty().subst(tcx, cid.instance.substs))?; |
dfeec247 | 49 | assert!(!layout.is_unsized()); |
136023e0 | 50 | let ret = ecx.allocate(layout, MemoryKind::Stack)?; |
dfeec247 | 51 | |
c295e0f8 XL |
52 | trace!( |
53 | "eval_body_using_ecx: pushing stack frame for global: {}{}", | |
5e7ed085 | 54 | with_no_trimmed_paths!(ty::tls::with(|tcx| tcx.def_path_str(cid.instance.def_id()))), |
c295e0f8 XL |
55 | cid.promoted.map_or_else(String::new, |p| format!("::promoted[{:?}]", p)) |
56 | ); | |
dfeec247 | 57 | |
dfeec247 XL |
58 | ecx.push_stack_frame( |
59 | cid.instance, | |
dfeec247 | 60 | body, |
923072b8 | 61 | &ret.into(), |
a2a8927a | 62 | StackPopCleanup::Root { cleanup: false }, |
dfeec247 XL |
63 | )?; |
64 | ||
65 | // The main interpreter loop. | |
66 | ecx.run()?; | |
67 | ||
68 | // Intern the result | |
29967ef6 XL |
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 | } | |
dfeec247 | 76 | }; |
f2b60f7d | 77 | ecx.machine.check_alignment = false; // interning doesn't need to respect alignment |
6a06907d | 78 | intern_const_alloc_recursive(ecx, intern_kind, &ret)?; |
f2b60f7d | 79 | // we leave alignment checks off, since this `ecx` will not be used for further evaluation anyway |
dfeec247 XL |
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 | |
f2b60f7d | 86 | /// `simd_shuffle` and const patterns in match arms. It never performs alignment checks. |
dfeec247 XL |
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>, | |
f035d41b | 94 | root_span: Span, |
dfeec247 XL |
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( | |
f035d41b XL |
100 | tcx, |
101 | root_span, | |
dfeec247 | 102 | param_env, |
f2b60f7d FG |
103 | CompileTimeInterpreter::new( |
104 | tcx.const_eval_limit(), | |
105 | can_access_statics, | |
106 | /*check_alignment:*/ false, | |
107 | ), | |
dfeec247 XL |
108 | ) |
109 | } | |
110 | ||
1b1a35ee XL |
111 | /// This function converts an interpreter value into a constant that is meant for use in the |
112 | /// type system. | |
04454e1e | 113 | #[instrument(skip(ecx), level = "debug")] |
dfeec247 XL |
114 | pub(super) fn op_to_const<'tcx>( |
115 | ecx: &CompileTimeEvalContext<'_, 'tcx>, | |
6a06907d | 116 | op: &OpTy<'tcx>, |
74b04a01 | 117 | ) -> ConstValue<'tcx> { |
dfeec247 XL |
118 | // We do not have value optimizations for everything. |
119 | // Only scalars and slices, since they are very common. | |
f9f354fc | 120 | // Note that further down we turn scalars of uninitialized bits back to `ByRef`. These can result |
dfeec247 | 121 | // from scalar unions that are initialized with one of their zero sized variants. We could |
f9f354fc | 122 | // instead allow `ConstValue::Scalar` to store `ScalarMaybeUninit`, but that would affect all |
dfeec247 XL |
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 { | |
04454e1e | 126 | Abi::Scalar(abi::Scalar::Initialized { .. }) => true, |
1b1a35ee XL |
127 | Abi::ScalarPair(..) => match op.layout.ty.kind() { |
128 | ty::Ref(_, inner, _) => match *inner.kind() { | |
dfeec247 XL |
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 | |
923072b8 | 142 | // by-val is if we are in destructure_mir_constant, i.e., if this is (a field of) something that we |
dfeec247 XL |
143 | // "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or |
144 | // structs containing such. | |
136023e0 | 145 | op.try_as_mplace() |
dfeec247 XL |
146 | }; |
147 | ||
04454e1e FG |
148 | debug!(?immediate); |
149 | ||
136023e0 | 150 | // We know `offset` is relative to the allocation, so we can use `into_parts`. |
04454e1e FG |
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 \ | |
1b1a35ee | 164 | alignment is correct", |
04454e1e | 165 | ); |
064997fb | 166 | ConstValue::ZeroSized |
04454e1e | 167 | } |
dfeec247 XL |
168 | } |
169 | }; | |
74b04a01 | 170 | match immediate { |
6a06907d | 171 | Ok(ref mplace) => to_const_value(mplace), |
dfeec247 | 172 | // see comment on `let try_as_immediate` above |
ba9703b0 | 173 | Err(imm) => match *imm { |
064997fb | 174 | _ if imm.layout.is_zst() => ConstValue::ZeroSized, |
f2b60f7d | 175 | Immediate::Scalar(x) => ConstValue::Scalar(x), |
ba9703b0 | 176 | Immediate::ScalarPair(a, b) => { |
04454e1e | 177 | debug!("ScalarPair(a: {:?}, b: {:?})", a, b); |
136023e0 | 178 | // We know `offset` is relative to the allocation, so we can use `into_parts`. |
064997fb FG |
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 | }; | |
f035d41b | 190 | let len = b.to_machine_usize(ecx).unwrap(); |
ba9703b0 XL |
191 | let start = start.try_into().unwrap(); |
192 | let len: usize = len.try_into().unwrap(); | |
193 | ConstValue::Slice { data, start, end: start + len } | |
194 | } | |
064997fb | 195 | Immediate::Uninit => to_const_value(&op.assert_mem_place()), |
dfeec247 | 196 | }, |
74b04a01 | 197 | } |
dfeec247 XL |
198 | } |
199 | ||
f2b60f7d | 200 | #[instrument(skip(tcx), level = "debug", ret)] |
923072b8 | 201 | pub(crate) fn turn_into_const_value<'tcx>( |
dfeec247 | 202 | tcx: TyCtxt<'tcx>, |
1b1a35ee | 203 | constant: ConstAlloc<'tcx>, |
dfeec247 | 204 | key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, |
1b1a35ee | 205 | ) -> ConstValue<'tcx> { |
dfeec247 XL |
206 | let cid = key.value; |
207 | let def_id = cid.instance.def.def_id(); | |
208 | let is_static = tcx.is_static(def_id); | |
f2b60f7d FG |
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 | ); | |
dfeec247 | 216 | |
1b1a35ee XL |
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 | ); | |
04454e1e | 225 | |
1b1a35ee | 226 | // Turn this into a proper constant. |
f2b60f7d | 227 | op_to_const(&ecx, &mplace.into()) |
dfeec247 XL |
228 | } |
229 | ||
923072b8 | 230 | #[instrument(skip(tcx), level = "debug")] |
1b1a35ee | 231 | pub fn eval_to_const_value_raw_provider<'tcx>( |
dfeec247 XL |
232 | tcx: TyCtxt<'tcx>, |
233 | key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, | |
1b1a35ee | 234 | ) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> { |
5099ac24 | 235 | assert!(key.param_env.is_const()); |
6a06907d | 236 | // see comment in eval_to_allocation_raw_provider for what we're doing here |
f035d41b | 237 | if key.param_env.reveal() == Reveal::All { |
dfeec247 | 238 | let mut key = key; |
f035d41b | 239 | key.param_env = key.param_env.with_user_facing(); |
1b1a35ee | 240 | match tcx.eval_to_const_value_raw(key) { |
dfeec247 XL |
241 | // try again with reveal all as requested |
242 | Err(ErrorHandled::TooGeneric) => {} | |
74b04a01 | 243 | // deduplicate calls |
dfeec247 XL |
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 { | |
3dfed10e | 251 | let ty = key.value.instance.ty(tcx, key.param_env); |
5e7ed085 FG |
252 | let ty::FnDef(_, substs) = ty.kind() else { |
253 | bug!("intrinsic with type {:?}", ty); | |
dfeec247 XL |
254 | }; |
255 | return eval_nullary_intrinsic(tcx, key.param_env, def_id, substs).map_err(|error| { | |
256 | let span = tcx.def_span(def_id); | |
6a06907d | 257 | let error = ConstEvalErr { error: error.into_kind(), stacktrace: vec![], span }; |
dfeec247 XL |
258 | error.report_as_error(tcx.at(span), "could not evaluate nullary intrinsic") |
259 | }); | |
260 | } | |
261 | ||
1b1a35ee | 262 | tcx.eval_to_allocation_raw(key).map(|val| turn_into_const_value(tcx, val, key)) |
dfeec247 XL |
263 | } |
264 | ||
923072b8 | 265 | #[instrument(skip(tcx), level = "debug")] |
1b1a35ee | 266 | pub fn eval_to_allocation_raw_provider<'tcx>( |
dfeec247 XL |
267 | tcx: TyCtxt<'tcx>, |
268 | key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, | |
1b1a35ee | 269 | ) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> { |
5099ac24 | 270 | assert!(key.param_env.is_const()); |
dfeec247 XL |
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. | |
f035d41b | 279 | if key.param_env.reveal() == Reveal::All { |
dfeec247 | 280 | let mut key = key; |
f035d41b | 281 | key.param_env = key.param_env.with_user_facing(); |
1b1a35ee | 282 | match tcx.eval_to_allocation_raw(key) { |
dfeec247 XL |
283 | // try again with reveal all as requested |
284 | Err(ErrorHandled::TooGeneric) => {} | |
74b04a01 | 285 | // deduplicate calls |
dfeec247 XL |
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 | |
5e7ed085 | 295 | let instance = with_no_trimmed_paths!(key.value.instance.to_string()); |
dfeec247 XL |
296 | trace!("const eval: {:?} ({})", key, instance); |
297 | } | |
298 | ||
299 | let cid = key.value; | |
3dfed10e | 300 | let def = cid.instance.def.with_opt_param(); |
3dfed10e | 301 | let is_static = tcx.is_static(def.did); |
dfeec247 | 302 | |
dfeec247 | 303 | let mut ecx = InterpCx::new( |
f035d41b | 304 | tcx, |
3dfed10e | 305 | tcx.def_span(def.did), |
dfeec247 | 306 | key.param_env, |
5869c6ff XL |
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. | |
f2b60f7d FG |
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 | ), | |
dfeec247 XL |
314 | ); |
315 | ||
316 | let res = ecx.load_mir(cid.instance.def, cid.promoted); | |
1b1a35ee XL |
317 | match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) { |
318 | Err(error) => { | |
3dfed10e | 319 | let err = ConstEvalErr::new(&ecx, error, None); |
2b03887a FG |
320 | let msg = if is_static { |
321 | Cow::from("could not evaluate static initializer") | |
dfeec247 | 322 | } else { |
2b03887a FG |
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) | |
5869c6ff | 331 | } else { |
2b03887a FG |
332 | Cow::from("evaluation of constant value failed") |
333 | } | |
334 | }; | |
17df50a5 | 335 | |
2b03887a | 336 | Err(err.report_as_error(ecx.tcx.at(err.span), &msg)) |
1b1a35ee XL |
337 | } |
338 | Ok(mplace) => { | |
5869c6ff XL |
339 | // Since evaluation had no errors, validate the resulting constant. |
340 | // This is a separate `try` block to provide more targeted error reporting. | |
1b1a35ee | 341 | let validation = try { |
fc512014 XL |
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 | }; | |
6a06907d | 353 | ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?; |
fc512014 | 354 | inner = true; |
1b1a35ee XL |
355 | } |
356 | }; | |
136023e0 | 357 | let alloc_id = mplace.ptr.provenance.unwrap(); |
1b1a35ee | 358 | if let Err(error) = validation { |
5869c6ff | 359 | // Validation failed, report an error. This is always a hard error. |
1b1a35ee XL |
360 | let err = ConstEvalErr::new(&ecx, error, None); |
361 | Err(err.struct_error( | |
362 | ecx.tcx, | |
363 | "it is undefined behavior to use this value", | |
5e7ed085 | 364 | |diag| { |
064997fb FG |
365 | if matches!(err.error, InterpError::UndefinedBehavior(_)) { |
366 | diag.note(NOTE_ON_UNDEFINED_BEHAVIOR_ERROR); | |
367 | } | |
cdc7bbd5 XL |
368 | diag.note(&format!( |
369 | "the raw bytes of the constant ({}", | |
370 | display_allocation( | |
371 | *ecx.tcx, | |
5e7ed085 | 372 | ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner() |
cdc7bbd5 XL |
373 | ) |
374 | )); | |
1b1a35ee XL |
375 | }, |
376 | )) | |
377 | } else { | |
378 | // Convert to raw constant | |
136023e0 | 379 | Ok(ConstAlloc { alloc_id, ty: mplace.layout.ty }) |
1b1a35ee XL |
380 | } |
381 | } | |
382 | } | |
dfeec247 | 383 | } |