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