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