]>
Commit | Line | Data |
---|---|---|
487cf647 FG |
1 | use std::borrow::Cow; |
2 | use std::convert::TryInto; | |
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: {}{}", | |
5e7ed085 | 57 | with_no_trimmed_paths!(ty::tls::with(|tcx| 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 | }; |
f2b60f7d | 80 | ecx.machine.check_alignment = false; // 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, |
f2b60f7d FG |
106 | CompileTimeInterpreter::new( |
107 | tcx.const_eval_limit(), | |
108 | can_access_statics, | |
109 | /*check_alignment:*/ false, | |
110 | ), | |
dfeec247 XL |
111 | ) |
112 | } | |
113 | ||
1b1a35ee XL |
114 | /// This function converts an interpreter value into a constant that is meant for use in the |
115 | /// type system. | |
04454e1e | 116 | #[instrument(skip(ecx), level = "debug")] |
dfeec247 XL |
117 | pub(super) fn op_to_const<'tcx>( |
118 | ecx: &CompileTimeEvalContext<'_, 'tcx>, | |
6a06907d | 119 | op: &OpTy<'tcx>, |
74b04a01 | 120 | ) -> ConstValue<'tcx> { |
dfeec247 XL |
121 | // We do not have value optimizations for everything. |
122 | // Only scalars and slices, since they are very common. | |
f9f354fc | 123 | // Note that further down we turn scalars of uninitialized bits back to `ByRef`. These can result |
dfeec247 | 124 | // from scalar unions that are initialized with one of their zero sized variants. We could |
f9f354fc | 125 | // instead allow `ConstValue::Scalar` to store `ScalarMaybeUninit`, but that would affect all |
dfeec247 XL |
126 | // the usual cases of extracting e.g. a `usize`, without there being a real use case for the |
127 | // `Undef` situation. | |
128 | let try_as_immediate = match op.layout.abi { | |
04454e1e | 129 | Abi::Scalar(abi::Scalar::Initialized { .. }) => true, |
1b1a35ee XL |
130 | Abi::ScalarPair(..) => match op.layout.ty.kind() { |
131 | ty::Ref(_, inner, _) => match *inner.kind() { | |
dfeec247 XL |
132 | ty::Slice(elem) => elem == ecx.tcx.types.u8, |
133 | ty::Str => true, | |
134 | _ => false, | |
135 | }, | |
136 | _ => false, | |
137 | }, | |
138 | _ => false, | |
139 | }; | |
140 | let immediate = if try_as_immediate { | |
487cf647 | 141 | Right(ecx.read_immediate(op).expect("normalization works on validated constants")) |
dfeec247 XL |
142 | } else { |
143 | // It is guaranteed that any non-slice scalar pair is actually ByRef here. | |
144 | // When we come back from raw const eval, we are always by-ref. The only way our op here is | |
923072b8 | 145 | // by-val is if we are in destructure_mir_constant, i.e., if this is (a field of) something that we |
dfeec247 XL |
146 | // "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or |
147 | // structs containing such. | |
487cf647 | 148 | op.as_mplace_or_imm() |
dfeec247 XL |
149 | }; |
150 | ||
04454e1e FG |
151 | debug!(?immediate); |
152 | ||
136023e0 | 153 | // We know `offset` is relative to the allocation, so we can use `into_parts`. |
04454e1e FG |
154 | let to_const_value = |mplace: &MPlaceTy<'_>| { |
155 | debug!("to_const_value(mplace: {:?})", mplace); | |
156 | match mplace.ptr.into_parts() { | |
157 | (Some(alloc_id), offset) => { | |
158 | let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory(); | |
159 | ConstValue::ByRef { alloc, offset } | |
160 | } | |
161 | (None, offset) => { | |
162 | assert!(mplace.layout.is_zst()); | |
163 | assert_eq!( | |
164 | offset.bytes() % mplace.layout.align.abi.bytes(), | |
165 | 0, | |
166 | "this MPlaceTy must come from a validated constant, thus we can assume the \ | |
1b1a35ee | 167 | alignment is correct", |
04454e1e | 168 | ); |
064997fb | 169 | ConstValue::ZeroSized |
04454e1e | 170 | } |
dfeec247 XL |
171 | } |
172 | }; | |
74b04a01 | 173 | match immediate { |
487cf647 | 174 | Left(ref mplace) => to_const_value(mplace), |
dfeec247 | 175 | // see comment on `let try_as_immediate` above |
487cf647 | 176 | Right(imm) => match *imm { |
064997fb | 177 | _ if imm.layout.is_zst() => ConstValue::ZeroSized, |
f2b60f7d | 178 | Immediate::Scalar(x) => ConstValue::Scalar(x), |
ba9703b0 | 179 | Immediate::ScalarPair(a, b) => { |
04454e1e | 180 | debug!("ScalarPair(a: {:?}, b: {:?})", a, b); |
136023e0 | 181 | // We know `offset` is relative to the allocation, so we can use `into_parts`. |
064997fb FG |
182 | let (data, start) = match a.to_pointer(ecx).unwrap().into_parts() { |
183 | (Some(alloc_id), offset) => { | |
184 | (ecx.tcx.global_alloc(alloc_id).unwrap_memory(), offset.bytes()) | |
185 | } | |
186 | (None, _offset) => ( | |
187 | ecx.tcx.intern_const_alloc(Allocation::from_bytes_byte_aligned_immutable( | |
188 | b"" as &[u8], | |
189 | )), | |
190 | 0, | |
191 | ), | |
192 | }; | |
f035d41b | 193 | let len = b.to_machine_usize(ecx).unwrap(); |
ba9703b0 XL |
194 | let start = start.try_into().unwrap(); |
195 | let len: usize = len.try_into().unwrap(); | |
196 | ConstValue::Slice { data, start, end: start + len } | |
197 | } | |
064997fb | 198 | Immediate::Uninit => to_const_value(&op.assert_mem_place()), |
dfeec247 | 199 | }, |
74b04a01 | 200 | } |
dfeec247 XL |
201 | } |
202 | ||
f2b60f7d | 203 | #[instrument(skip(tcx), level = "debug", ret)] |
923072b8 | 204 | pub(crate) fn turn_into_const_value<'tcx>( |
dfeec247 | 205 | tcx: TyCtxt<'tcx>, |
1b1a35ee | 206 | constant: ConstAlloc<'tcx>, |
dfeec247 | 207 | key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, |
1b1a35ee | 208 | ) -> ConstValue<'tcx> { |
dfeec247 XL |
209 | let cid = key.value; |
210 | let def_id = cid.instance.def.def_id(); | |
211 | let is_static = tcx.is_static(def_id); | |
f2b60f7d FG |
212 | // This is just accessing an already computed constant, so no need to check alginment here. |
213 | let ecx = mk_eval_cx( | |
214 | tcx, | |
215 | tcx.def_span(key.value.instance.def_id()), | |
216 | key.param_env, | |
217 | /*can_access_statics:*/ is_static, | |
218 | ); | |
dfeec247 | 219 | |
1b1a35ee XL |
220 | let mplace = ecx.raw_const_to_mplace(constant).expect( |
221 | "can only fail if layout computation failed, \ | |
222 | which should have given a good error before ever invoking this function", | |
223 | ); | |
224 | assert!( | |
225 | !is_static || cid.promoted.is_some(), | |
226 | "the `eval_to_const_value_raw` query should not be used for statics, use `eval_to_allocation` instead" | |
227 | ); | |
04454e1e | 228 | |
1b1a35ee | 229 | // Turn this into a proper constant. |
f2b60f7d | 230 | op_to_const(&ecx, &mplace.into()) |
dfeec247 XL |
231 | } |
232 | ||
923072b8 | 233 | #[instrument(skip(tcx), level = "debug")] |
1b1a35ee | 234 | pub fn eval_to_const_value_raw_provider<'tcx>( |
dfeec247 XL |
235 | tcx: TyCtxt<'tcx>, |
236 | key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, | |
1b1a35ee | 237 | ) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> { |
5099ac24 | 238 | assert!(key.param_env.is_const()); |
6a06907d | 239 | // see comment in eval_to_allocation_raw_provider for what we're doing here |
f035d41b | 240 | if key.param_env.reveal() == Reveal::All { |
dfeec247 | 241 | let mut key = key; |
f035d41b | 242 | key.param_env = key.param_env.with_user_facing(); |
1b1a35ee | 243 | match tcx.eval_to_const_value_raw(key) { |
dfeec247 XL |
244 | // try again with reveal all as requested |
245 | Err(ErrorHandled::TooGeneric) => {} | |
74b04a01 | 246 | // deduplicate calls |
dfeec247 XL |
247 | other => return other, |
248 | } | |
249 | } | |
250 | ||
251 | // We call `const_eval` for zero arg intrinsics, too, in order to cache their value. | |
252 | // Catch such calls and evaluate them instead of trying to load a constant's MIR. | |
253 | if let ty::InstanceDef::Intrinsic(def_id) = key.value.instance.def { | |
3dfed10e | 254 | let ty = key.value.instance.ty(tcx, key.param_env); |
5e7ed085 FG |
255 | let ty::FnDef(_, substs) = ty.kind() else { |
256 | bug!("intrinsic with type {:?}", ty); | |
dfeec247 XL |
257 | }; |
258 | return eval_nullary_intrinsic(tcx, key.param_env, def_id, substs).map_err(|error| { | |
259 | let span = tcx.def_span(def_id); | |
6a06907d | 260 | let error = ConstEvalErr { error: error.into_kind(), stacktrace: vec![], span }; |
487cf647 | 261 | error.report(tcx.at(span), "could not evaluate nullary intrinsic") |
dfeec247 XL |
262 | }); |
263 | } | |
264 | ||
1b1a35ee | 265 | tcx.eval_to_allocation_raw(key).map(|val| turn_into_const_value(tcx, val, key)) |
dfeec247 XL |
266 | } |
267 | ||
923072b8 | 268 | #[instrument(skip(tcx), level = "debug")] |
1b1a35ee | 269 | pub fn eval_to_allocation_raw_provider<'tcx>( |
dfeec247 XL |
270 | tcx: TyCtxt<'tcx>, |
271 | key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, | |
1b1a35ee | 272 | ) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> { |
5099ac24 | 273 | assert!(key.param_env.is_const()); |
dfeec247 XL |
274 | // Because the constant is computed twice (once per value of `Reveal`), we are at risk of |
275 | // reporting the same error twice here. To resolve this, we check whether we can evaluate the | |
276 | // constant in the more restrictive `Reveal::UserFacing`, which most likely already was | |
277 | // computed. For a large percentage of constants that will already have succeeded. Only | |
278 | // associated constants of generic functions will fail due to not enough monomorphization | |
279 | // information being available. | |
280 | ||
281 | // In case we fail in the `UserFacing` variant, we just do the real computation. | |
f035d41b | 282 | if key.param_env.reveal() == Reveal::All { |
dfeec247 | 283 | let mut key = key; |
f035d41b | 284 | key.param_env = key.param_env.with_user_facing(); |
1b1a35ee | 285 | match tcx.eval_to_allocation_raw(key) { |
dfeec247 XL |
286 | // try again with reveal all as requested |
287 | Err(ErrorHandled::TooGeneric) => {} | |
74b04a01 | 288 | // deduplicate calls |
dfeec247 XL |
289 | other => return other, |
290 | } | |
291 | } | |
292 | if cfg!(debug_assertions) { | |
293 | // Make sure we format the instance even if we do not print it. | |
294 | // This serves as a regression test against an ICE on printing. | |
295 | // The next two lines concatenated contain some discussion: | |
296 | // https://rust-lang.zulipchat.com/#narrow/stream/146212-t-compiler.2Fconst-eval/ | |
297 | // subject/anon_const_instance_printing/near/135980032 | |
5e7ed085 | 298 | let instance = with_no_trimmed_paths!(key.value.instance.to_string()); |
dfeec247 XL |
299 | trace!("const eval: {:?} ({})", key, instance); |
300 | } | |
301 | ||
302 | let cid = key.value; | |
3dfed10e | 303 | let def = cid.instance.def.with_opt_param(); |
3dfed10e | 304 | let is_static = tcx.is_static(def.did); |
dfeec247 | 305 | |
dfeec247 | 306 | let mut ecx = InterpCx::new( |
f035d41b | 307 | tcx, |
3dfed10e | 308 | tcx.def_span(def.did), |
dfeec247 | 309 | key.param_env, |
5869c6ff XL |
310 | // Statics (and promoteds inside statics) may access other statics, because unlike consts |
311 | // they do not have to behave "as if" they were evaluated at runtime. | |
f2b60f7d FG |
312 | CompileTimeInterpreter::new( |
313 | tcx.const_eval_limit(), | |
314 | /*can_access_statics:*/ is_static, | |
315 | /*check_alignment:*/ tcx.sess.opts.unstable_opts.extra_const_ub_checks, | |
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 | } |