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