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
,
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}
;
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."
25 // Returns a pointer to where the result lives
26 fn eval_body_using_ecx
<'mir
, 'tcx
>(
27 ecx
: &mut CompileTimeEvalContext
<'mir
, 'tcx
>,
29 body
: &'mir mir
::Body
<'tcx
>,
30 ) -> InterpResult
<'tcx
, MPlaceTy
<'tcx
>> {
31 debug
!("eval_body_using_ecx: {:?}, {:?}", cid
, ecx
.param_env
);
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
);
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
);
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())
55 StackPopCleanup
::None { cleanup: false }
,
58 // The main interpreter loop.
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
,
73 intern_const_alloc_recursive(
77 body
.ignore_interior_mut_in_const_validation
,
80 debug
!("eval_body_using_ecx done: {:?}", *ret
);
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.
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
>(
94 param_env
: ty
::ParamEnv
<'tcx
>,
95 can_access_statics
: bool
,
96 ) -> CompileTimeEvalContext
<'mir
, 'tcx
> {
97 debug
!("mk_eval_cx: {:?}", param_env
);
102 CompileTimeInterpreter
::new(tcx
.sess
.const_eval_limit()),
103 MemoryExtra { can_access_statics }
,
107 /// This function converts an interpreter value into a constant that is meant for use in the
109 pub(super) fn op_to_const
<'tcx
>(
110 ecx
: &CompileTimeEvalContext
<'_
, '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,
132 let immediate
= if try_as_immediate
{
133 Err(ecx
.read_immediate(op
).expect("normalization works on validated constants"))
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
)
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 }
148 Scalar
::Raw { data, .. }
=> {
149 assert
!(mplace
.layout
.is_zst());
151 u64::try_from(data
).unwrap() % mplace
.layout
.align
.abi
.bytes(),
153 "this MPlaceTy must come from a validated constant, thus we can assume the \
154 alignment is correct",
156 ConstValue
::Scalar(Scalar
::zst())
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
)),
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())
172 Scalar
::Raw { .. }
=> (
174 .intern_const_alloc(Allocation
::from_byte_aligned_bytes(b
"" as &[u8])),
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 }
187 fn turn_into_const_value
<'tcx
>(
189 constant
: ConstAlloc
<'tcx
>,
190 key
: ty
::ParamEnvAnd
<'tcx
, GlobalId
<'tcx
>>,
191 ) -> ConstValue
<'tcx
> {
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
);
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",
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"
205 // Turn this into a proper constant.
206 op_to_const(&ecx
, mplace
.into())
209 pub fn eval_to_const_value_raw_provider
<'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
{
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
) => {}
221 other
=> return other
,
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
),
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")
240 tcx
.eval_to_allocation_raw(key
).map(|val
| turn_into_const_value(tcx
, val
, key
))
243 pub fn eval_to_allocation_raw_provider
<'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.
254 // In case we fail in the `UserFacing` variant, we just do the real computation.
255 if key
.param_env
.reveal() == Reveal
::All
{
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
) => {}
262 other
=> return other
,
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
);
276 let def
= cid
.instance
.def
.with_opt_param();
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
));
286 let is_static
= tcx
.is_static(def
.did
);
288 let mut ecx
= InterpCx
::new(
290 tcx
.def_span(def
.did
),
292 CompileTimeInterpreter
::new(tcx
.sess
.const_eval_limit()),
293 MemoryExtra { can_access_statics: is_static }
,
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
)) {
299 let err
= ConstEvalErr
::new(&ecx
, error
, None
);
300 // errors in statics are always emitted as fatal errors
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",
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(
316 &format
!("static eval failure did not emit an error: {:#?}", 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
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",
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
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(
350 "evaluation of constant expression failed",
353 Err(err
.report_as_lint(
355 "reaching this expression at runtime will panic or abort",
356 tcx
.hir().local_def_id_to_hir_id(def
.did
),
360 // anything else (array lengths, enum initializers, constant patterns) are
361 // reported as hard errors
363 Err(err
.report_as_error(
364 ecx
.tcx
.at(ecx
.cur_span()),
365 "evaluation of constant value failed",
371 // use of broken constant from other crate
372 Err(err
.report_as_error(ecx
.tcx
.at(ecx
.cur_span()), "could not evaluate constant"))
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(
387 /*may_ref_to_static*/ ecx
.memory
.extra
.can_access_statics
,
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(
397 "it is undefined behavior to use this value",
399 diag
.note(note_on_undefined_behavior_error());
404 // Convert to raw constant
405 Ok(ConstAlloc { alloc_id: mplace.ptr.assert_ptr().alloc_id, ty: mplace.layout.ty }
)