]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_mir/src/const_eval/eval_queries.rs
New upstream version 1.48.0~beta.8+dfsg1
[rustc.git] / compiler / rustc_mir / src / const_eval / eval_queries.rs
1 use super::{CompileTimeEvalContext, CompileTimeInterpreter, ConstEvalErr, MemoryExtra};
2 use crate::interpret::eval_nullary_intrinsic;
3 use crate::interpret::{
4 intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, GlobalId, Immediate,
5 InternKind, InterpCx, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, Scalar,
6 ScalarMaybeUninit, 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::traits::Reveal;
13 use rustc_middle::ty::print::with_no_trimmed_paths;
14 use rustc_middle::ty::{self, subst::Subst, TyCtxt};
15 use rustc_span::source_map::Span;
16 use rustc_target::abi::{Abi, LayoutOf};
17 use std::convert::{TryFrom, TryInto};
18
19 pub fn note_on_undefined_behavior_error() -> &'static str {
20 "The rules on what exactly is undefined behavior aren't clear, \
21 so this check might be overzealous. Please open an issue on the rustc \
22 repository if you believe it should not be considered undefined behavior."
23 }
24
25 // Returns a pointer to where the result lives
26 fn eval_body_using_ecx<'mir, 'tcx>(
27 ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
28 cid: GlobalId<'tcx>,
29 body: &'mir mir::Body<'tcx>,
30 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
31 debug!("eval_body_using_ecx: {:?}, {:?}", cid, ecx.param_env);
32 let tcx = *ecx.tcx;
33 let layout = ecx.layout_of(body.return_ty().subst(tcx, cid.instance.substs))?;
34 assert!(!layout.is_unsized());
35 let ret = ecx.allocate(layout, MemoryKind::Stack);
36
37 let name =
38 with_no_trimmed_paths(|| ty::tls::with(|tcx| tcx.def_path_str(cid.instance.def_id())));
39 let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p));
40 trace!("eval_body_using_ecx: pushing stack frame for global: {}{}", name, prom);
41
42 // Assert all args (if any) are zero-sized types; `eval_body_using_ecx` doesn't
43 // make sense if the body is expecting nontrivial arguments.
44 // (The alternative would be to use `eval_fn_call` with an args slice.)
45 for arg in body.args_iter() {
46 let decl = body.local_decls.get(arg).expect("arg missing from local_decls");
47 let layout = ecx.layout_of(decl.ty.subst(tcx, cid.instance.substs))?;
48 assert!(layout.is_zst())
49 }
50
51 ecx.push_stack_frame(
52 cid.instance,
53 body,
54 Some(ret.into()),
55 StackPopCleanup::None { cleanup: false },
56 )?;
57
58 // The main interpreter loop.
59 ecx.run()?;
60
61 // Intern the result
62 // FIXME: since the DefId of a promoted is the DefId of its owner, this
63 // means that promoteds in statics are actually interned like statics!
64 // However, this is also currently crucial because we promote mutable
65 // non-empty slices in statics to extend their lifetime, and this
66 // ensures that they are put into a mutable allocation.
67 // For other kinds of promoteds in statics (like array initializers), this is rather silly.
68 let intern_kind = match tcx.static_mutability(cid.instance.def_id()) {
69 Some(m) => InternKind::Static(m),
70 None if cid.promoted.is_some() => InternKind::Promoted,
71 _ => InternKind::Constant,
72 };
73 intern_const_alloc_recursive(
74 ecx,
75 intern_kind,
76 ret,
77 body.ignore_interior_mut_in_const_validation,
78 );
79
80 debug!("eval_body_using_ecx done: {:?}", *ret);
81 Ok(ret)
82 }
83
84 /// The `InterpCx` is only meant to be used to do field and index projections into constants for
85 /// `simd_shuffle` and const patterns in match arms.
86 ///
87 /// The function containing the `match` that is currently being analyzed may have generic bounds
88 /// that inform us about the generic bounds of the constant. E.g., using an associated constant
89 /// of a function's generic parameter will require knowledge about the bounds on the generic
90 /// parameter. These bounds are passed to `mk_eval_cx` via the `ParamEnv` argument.
91 pub(super) fn mk_eval_cx<'mir, 'tcx>(
92 tcx: TyCtxt<'tcx>,
93 root_span: Span,
94 param_env: ty::ParamEnv<'tcx>,
95 can_access_statics: bool,
96 ) -> CompileTimeEvalContext<'mir, 'tcx> {
97 debug!("mk_eval_cx: {:?}", param_env);
98 InterpCx::new(
99 tcx,
100 root_span,
101 param_env,
102 CompileTimeInterpreter::new(tcx.sess.const_eval_limit()),
103 MemoryExtra { can_access_statics },
104 )
105 }
106
107 /// This function converts an interpreter value into a constant that is meant for use in the
108 /// type system.
109 pub(super) fn op_to_const<'tcx>(
110 ecx: &CompileTimeEvalContext<'_, 'tcx>,
111 op: OpTy<'tcx>,
112 ) -> ConstValue<'tcx> {
113 // We do not have value optimizations for everything.
114 // Only scalars and slices, since they are very common.
115 // Note that further down we turn scalars of uninitialized bits back to `ByRef`. These can result
116 // from scalar unions that are initialized with one of their zero sized variants. We could
117 // instead allow `ConstValue::Scalar` to store `ScalarMaybeUninit`, but that would affect all
118 // the usual cases of extracting e.g. a `usize`, without there being a real use case for the
119 // `Undef` situation.
120 let try_as_immediate = match op.layout.abi {
121 Abi::Scalar(..) => true,
122 Abi::ScalarPair(..) => match op.layout.ty.kind() {
123 ty::Ref(_, inner, _) => match *inner.kind() {
124 ty::Slice(elem) => elem == ecx.tcx.types.u8,
125 ty::Str => true,
126 _ => false,
127 },
128 _ => false,
129 },
130 _ => false,
131 };
132 let immediate = if try_as_immediate {
133 Err(ecx.read_immediate(op).expect("normalization works on validated constants"))
134 } else {
135 // It is guaranteed that any non-slice scalar pair is actually ByRef here.
136 // When we come back from raw const eval, we are always by-ref. The only way our op here is
137 // by-val is if we are in destructure_const, i.e., if this is (a field of) something that we
138 // "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or
139 // structs containing such.
140 op.try_as_mplace(ecx)
141 };
142
143 let to_const_value = |mplace: MPlaceTy<'_>| match mplace.ptr {
144 Scalar::Ptr(ptr) => {
145 let alloc = ecx.tcx.global_alloc(ptr.alloc_id).unwrap_memory();
146 ConstValue::ByRef { alloc, offset: ptr.offset }
147 }
148 Scalar::Raw { data, .. } => {
149 assert!(mplace.layout.is_zst());
150 assert_eq!(
151 u64::try_from(data).unwrap() % mplace.layout.align.abi.bytes(),
152 0,
153 "this MPlaceTy must come from a validated constant, thus we can assume the \
154 alignment is correct",
155 );
156 ConstValue::Scalar(Scalar::zst())
157 }
158 };
159 match immediate {
160 Ok(mplace) => to_const_value(mplace),
161 // see comment on `let try_as_immediate` above
162 Err(imm) => match *imm {
163 Immediate::Scalar(x) => match x {
164 ScalarMaybeUninit::Scalar(s) => ConstValue::Scalar(s),
165 ScalarMaybeUninit::Uninit => to_const_value(op.assert_mem_place(ecx)),
166 },
167 Immediate::ScalarPair(a, b) => {
168 let (data, start) = match a.check_init().unwrap() {
169 Scalar::Ptr(ptr) => {
170 (ecx.tcx.global_alloc(ptr.alloc_id).unwrap_memory(), ptr.offset.bytes())
171 }
172 Scalar::Raw { .. } => (
173 ecx.tcx
174 .intern_const_alloc(Allocation::from_byte_aligned_bytes(b"" as &[u8])),
175 0,
176 ),
177 };
178 let len = b.to_machine_usize(ecx).unwrap();
179 let start = start.try_into().unwrap();
180 let len: usize = len.try_into().unwrap();
181 ConstValue::Slice { data, start, end: start + len }
182 }
183 },
184 }
185 }
186
187 fn turn_into_const_value<'tcx>(
188 tcx: TyCtxt<'tcx>,
189 constant: ConstAlloc<'tcx>,
190 key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
191 ) -> ConstValue<'tcx> {
192 let cid = key.value;
193 let def_id = cid.instance.def.def_id();
194 let is_static = tcx.is_static(def_id);
195 let ecx = mk_eval_cx(tcx, tcx.def_span(key.value.instance.def_id()), key.param_env, is_static);
196
197 let mplace = ecx.raw_const_to_mplace(constant).expect(
198 "can only fail if layout computation failed, \
199 which should have given a good error before ever invoking this function",
200 );
201 assert!(
202 !is_static || cid.promoted.is_some(),
203 "the `eval_to_const_value_raw` query should not be used for statics, use `eval_to_allocation` instead"
204 );
205 // Turn this into a proper constant.
206 op_to_const(&ecx, mplace.into())
207 }
208
209 pub fn eval_to_const_value_raw_provider<'tcx>(
210 tcx: TyCtxt<'tcx>,
211 key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
212 ) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> {
213 // see comment in const_eval_raw_provider for what we're doing here
214 if key.param_env.reveal() == Reveal::All {
215 let mut key = key;
216 key.param_env = key.param_env.with_user_facing();
217 match tcx.eval_to_const_value_raw(key) {
218 // try again with reveal all as requested
219 Err(ErrorHandled::TooGeneric) => {}
220 // deduplicate calls
221 other => return other,
222 }
223 }
224
225 // We call `const_eval` for zero arg intrinsics, too, in order to cache their value.
226 // Catch such calls and evaluate them instead of trying to load a constant's MIR.
227 if let ty::InstanceDef::Intrinsic(def_id) = key.value.instance.def {
228 let ty = key.value.instance.ty(tcx, key.param_env);
229 let substs = match ty.kind() {
230 ty::FnDef(_, substs) => substs,
231 _ => bug!("intrinsic with type {:?}", ty),
232 };
233 return eval_nullary_intrinsic(tcx, key.param_env, def_id, substs).map_err(|error| {
234 let span = tcx.def_span(def_id);
235 let error = ConstEvalErr { error: error.kind, stacktrace: vec![], span };
236 error.report_as_error(tcx.at(span), "could not evaluate nullary intrinsic")
237 });
238 }
239
240 tcx.eval_to_allocation_raw(key).map(|val| turn_into_const_value(tcx, val, key))
241 }
242
243 pub fn eval_to_allocation_raw_provider<'tcx>(
244 tcx: TyCtxt<'tcx>,
245 key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
246 ) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> {
247 // Because the constant is computed twice (once per value of `Reveal`), we are at risk of
248 // reporting the same error twice here. To resolve this, we check whether we can evaluate the
249 // constant in the more restrictive `Reveal::UserFacing`, which most likely already was
250 // computed. For a large percentage of constants that will already have succeeded. Only
251 // associated constants of generic functions will fail due to not enough monomorphization
252 // information being available.
253
254 // In case we fail in the `UserFacing` variant, we just do the real computation.
255 if key.param_env.reveal() == Reveal::All {
256 let mut key = key;
257 key.param_env = key.param_env.with_user_facing();
258 match tcx.eval_to_allocation_raw(key) {
259 // try again with reveal all as requested
260 Err(ErrorHandled::TooGeneric) => {}
261 // deduplicate calls
262 other => return other,
263 }
264 }
265 if cfg!(debug_assertions) {
266 // Make sure we format the instance even if we do not print it.
267 // This serves as a regression test against an ICE on printing.
268 // The next two lines concatenated contain some discussion:
269 // https://rust-lang.zulipchat.com/#narrow/stream/146212-t-compiler.2Fconst-eval/
270 // subject/anon_const_instance_printing/near/135980032
271 let instance = with_no_trimmed_paths(|| key.value.instance.to_string());
272 trace!("const eval: {:?} ({})", key, instance);
273 }
274
275 let cid = key.value;
276 let def = cid.instance.def.with_opt_param();
277
278 if let Some(def) = def.as_local() {
279 if tcx.has_typeck_results(def.did) {
280 if let Some(error_reported) = tcx.typeck_opt_const_arg(def).tainted_by_errors {
281 return Err(ErrorHandled::Reported(error_reported));
282 }
283 }
284 }
285
286 let is_static = tcx.is_static(def.did);
287
288 let mut ecx = InterpCx::new(
289 tcx,
290 tcx.def_span(def.did),
291 key.param_env,
292 CompileTimeInterpreter::new(tcx.sess.const_eval_limit()),
293 MemoryExtra { can_access_statics: is_static },
294 );
295
296 let res = ecx.load_mir(cid.instance.def, cid.promoted);
297 match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) {
298 Err(error) => {
299 let err = ConstEvalErr::new(&ecx, error, None);
300 // errors in statics are always emitted as fatal errors
301 if is_static {
302 // Ensure that if the above error was either `TooGeneric` or `Reported`
303 // an error must be reported.
304 let v = err.report_as_error(
305 ecx.tcx.at(ecx.cur_span()),
306 "could not evaluate static initializer",
307 );
308
309 // If this is `Reveal:All`, then we need to make sure an error is reported but if
310 // this is `Reveal::UserFacing`, then it's expected that we could get a
311 // `TooGeneric` error. When we fall back to `Reveal::All`, then it will either
312 // succeed or we'll report this error then.
313 if key.param_env.reveal() == Reveal::All {
314 tcx.sess.delay_span_bug(
315 err.span,
316 &format!("static eval failure did not emit an error: {:#?}", v),
317 );
318 }
319
320 Err(v)
321 } else if let Some(def) = def.as_local() {
322 // constant defined in this crate, we can figure out a lint level!
323 match tcx.def_kind(def.did.to_def_id()) {
324 // constants never produce a hard error at the definition site. Anything else is
325 // a backwards compatibility hazard (and will break old versions of winapi for
326 // sure)
327 //
328 // note that validation may still cause a hard error on this very same constant,
329 // because any code that existed before validation could not have failed
330 // validation thus preventing such a hard error from being a backwards
331 // compatibility hazard
332 DefKind::Const | DefKind::AssocConst => {
333 let hir_id = tcx.hir().local_def_id_to_hir_id(def.did);
334 Err(err.report_as_lint(
335 tcx.at(tcx.def_span(def.did)),
336 "any use of this value will cause an error",
337 hir_id,
338 Some(err.span),
339 ))
340 }
341 // promoting runtime code is only allowed to error if it references broken
342 // constants any other kind of error will be reported to the user as a
343 // deny-by-default lint
344 _ => {
345 if let Some(p) = cid.promoted {
346 let span = tcx.promoted_mir_of_opt_const_arg(def.to_global())[p].span;
347 if let err_inval!(ReferencedConstant) = err.error {
348 Err(err.report_as_error(
349 tcx.at(span),
350 "evaluation of constant expression failed",
351 ))
352 } else {
353 Err(err.report_as_lint(
354 tcx.at(span),
355 "reaching this expression at runtime will panic or abort",
356 tcx.hir().local_def_id_to_hir_id(def.did),
357 Some(err.span),
358 ))
359 }
360 // anything else (array lengths, enum initializers, constant patterns) are
361 // reported as hard errors
362 } else {
363 Err(err.report_as_error(
364 ecx.tcx.at(ecx.cur_span()),
365 "evaluation of constant value failed",
366 ))
367 }
368 }
369 }
370 } else {
371 // use of broken constant from other crate
372 Err(err.report_as_error(ecx.tcx.at(ecx.cur_span()), "could not evaluate constant"))
373 }
374 }
375 Ok(mplace) => {
376 // Since evaluation had no errors, valiate the resulting constant:
377 let validation = try {
378 // FIXME do not validate promoteds until a decision on
379 // https://github.com/rust-lang/rust/issues/67465 is made
380 if cid.promoted.is_none() {
381 let mut ref_tracking = RefTracking::new(mplace);
382 while let Some((mplace, path)) = ref_tracking.todo.pop() {
383 ecx.const_validate_operand(
384 mplace.into(),
385 path,
386 &mut ref_tracking,
387 /*may_ref_to_static*/ ecx.memory.extra.can_access_statics,
388 )?;
389 }
390 }
391 };
392 if let Err(error) = validation {
393 // Validation failed, report an error
394 let err = ConstEvalErr::new(&ecx, error, None);
395 Err(err.struct_error(
396 ecx.tcx,
397 "it is undefined behavior to use this value",
398 |mut diag| {
399 diag.note(note_on_undefined_behavior_error());
400 diag.emit();
401 },
402 ))
403 } else {
404 // Convert to raw constant
405 Ok(ConstAlloc { alloc_id: mplace.ptr.assert_ptr().alloc_id, ty: mplace.layout.ty })
406 }
407 }
408 }
409 }