1 use super::{CompileTimeEvalContext, CompileTimeInterpreter, ConstEvalErr}
;
2 use crate::interpret
::eval_nullary_intrinsic
;
3 use crate::interpret
::{
4 intern_const_alloc_recursive
, Allocation
, ConstAlloc
, ConstValue
, CtfeValidationMode
, GlobalId
,
5 Immediate
, 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
::mir
::pretty
::display_allocation
;
13 use rustc_middle
::traits
::Reveal
;
14 use rustc_middle
::ty
::layout
::LayoutOf
;
15 use rustc_middle
::ty
::print
::with_no_trimmed_paths
;
16 use rustc_middle
::ty
::{self, subst::Subst, EarlyBinder, TyCtxt}
;
17 use rustc_span
::source_map
::Span
;
18 use rustc_target
::abi
::{self, Abi}
;
20 use std
::convert
::TryInto
;
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."
28 // Returns a pointer to where the result lives
29 fn eval_body_using_ecx
<'mir
, 'tcx
>(
30 ecx
: &mut CompileTimeEvalContext
<'mir
, 'tcx
>,
32 body
: &'mir mir
::Body
<'tcx
>,
33 ) -> InterpResult
<'tcx
, MPlaceTy
<'tcx
>> {
34 debug
!("eval_body_using_ecx: {:?}, {:?}", cid
, ecx
.param_env
);
37 cid
.promoted
.is_some()
39 ecx
.tcx
.def_kind(cid
.instance
.def_id()),
44 | DefKind
::InlineConst
47 "Unexpected DefKind: {:?}",
48 ecx
.tcx
.def_kind(cid
.instance
.def_id())
50 let layout
= ecx
.layout_of(EarlyBinder(body
.return_ty()).subst(tcx
, cid
.instance
.substs
))?
;
51 assert
!(!layout
.is_unsized());
52 let ret
= ecx
.allocate(layout
, MemoryKind
::Stack
)?
;
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
))
64 StackPopCleanup
::Root { cleanup: false }
,
67 // The main interpreter loop.
71 let intern_kind
= if cid
.promoted
.is_some() {
74 match tcx
.static_mutability(cid
.instance
.def_id()) {
75 Some(m
) => InternKind
::Static(m
),
76 None
=> InternKind
::Constant
,
79 intern_const_alloc_recursive(ecx
, intern_kind
, &ret
)?
;
81 debug
!("eval_body_using_ecx done: {:?}", *ret
);
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.
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
>(
95 param_env
: ty
::ParamEnv
<'tcx
>,
96 can_access_statics
: bool
,
97 ) -> CompileTimeEvalContext
<'mir
, 'tcx
> {
98 debug
!("mk_eval_cx: {:?}", param_env
);
103 CompileTimeInterpreter
::new(tcx
.const_eval_limit(), can_access_statics
),
107 /// This function converts an interpreter value into a constant that is meant for use in the
109 #[instrument(skip(ecx), level = "debug")]
110 pub(super) fn op_to_const
<'tcx
>(
111 ecx
: &CompileTimeEvalContext
<'_
, 'tcx
>,
113 ) -> ConstValue
<'tcx
> {
114 // We do not have value optimizations for everything.
115 // Only scalars and slices, since they are very common.
116 // Note that further down we turn scalars of uninitialized bits back to `ByRef`. These can result
117 // from scalar unions that are initialized with one of their zero sized variants. We could
118 // instead allow `ConstValue::Scalar` to store `ScalarMaybeUninit`, but that would affect all
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
{
122 Abi
::Scalar(abi
::Scalar
::Initialized { .. }
) => true,
123 Abi
::ScalarPair(..) => match op
.layout
.ty
.kind() {
124 ty
::Ref(_
, inner
, _
) => match *inner
.kind() {
125 ty
::Slice(elem
) => elem
== ecx
.tcx
.types
.u8,
133 let immediate
= if try_as_immediate
{
134 Err(ecx
.read_immediate(op
).expect("normalization works on validated constants"))
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
138 // by-val is if we are in destructure_mir_constant, i.e., if this is (a field of) something that we
139 // "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or
140 // structs containing such.
146 // We know `offset` is relative to the allocation, so we can use `into_parts`.
147 let to_const_value
= |mplace
: &MPlaceTy
<'_
>| {
148 debug
!("to_const_value(mplace: {:?})", mplace
);
149 match mplace
.ptr
.into_parts() {
150 (Some(alloc_id
), offset
) => {
151 let alloc
= ecx
.tcx
.global_alloc(alloc_id
).unwrap_memory();
152 ConstValue
::ByRef { alloc, offset }
155 assert
!(mplace
.layout
.is_zst());
157 offset
.bytes() % mplace
.layout
.align
.abi
.bytes(),
159 "this MPlaceTy must come from a validated constant, thus we can assume the \
160 alignment is correct",
162 ConstValue
::Scalar(Scalar
::ZST
)
167 Ok(ref mplace
) => to_const_value(mplace
),
168 // see comment on `let try_as_immediate` above
169 Err(imm
) => match *imm
{
170 Immediate
::Scalar(x
) => match x
{
171 ScalarMaybeUninit
::Scalar(s
) => ConstValue
::Scalar(s
),
172 ScalarMaybeUninit
::Uninit
=> to_const_value(&op
.assert_mem_place()),
174 Immediate
::ScalarPair(a
, b
) => {
175 debug
!("ScalarPair(a: {:?}, b: {:?})", a
, b
);
176 // We know `offset` is relative to the allocation, so we can use `into_parts`.
178 match ecx
.scalar_to_ptr(a
.check_init().unwrap()).unwrap().into_parts() {
179 (Some(alloc_id
), offset
) => {
180 (ecx
.tcx
.global_alloc(alloc_id
).unwrap_memory(), offset
.bytes())
183 ecx
.tcx
.intern_const_alloc(
184 Allocation
::from_bytes_byte_aligned_immutable(b
"" as &[u8]),
189 let len
= b
.to_machine_usize(ecx
).unwrap();
190 let start
= start
.try_into().unwrap();
191 let len
: usize = len
.try_into().unwrap();
192 ConstValue
::Slice { data, start, end: start + len }
198 #[instrument(skip(tcx), level = "debug")]
199 pub(crate) fn turn_into_const_value
<'tcx
>(
201 constant
: ConstAlloc
<'tcx
>,
202 key
: ty
::ParamEnvAnd
<'tcx
, GlobalId
<'tcx
>>,
203 ) -> ConstValue
<'tcx
> {
205 let def_id
= cid
.instance
.def
.def_id();
206 let is_static
= tcx
.is_static(def_id
);
207 let ecx
= mk_eval_cx(tcx
, tcx
.def_span(key
.value
.instance
.def_id()), key
.param_env
, is_static
);
209 let mplace
= ecx
.raw_const_to_mplace(constant
).expect(
210 "can only fail if layout computation failed, \
211 which should have given a good error before ever invoking this function",
214 !is_static
|| cid
.promoted
.is_some(),
215 "the `eval_to_const_value_raw` query should not be used for statics, use `eval_to_allocation` instead"
218 // Turn this into a proper constant.
219 let const_val
= op_to_const(&ecx
, &mplace
.into());
225 #[instrument(skip(tcx), level = "debug")]
226 pub fn eval_to_const_value_raw_provider
<'tcx
>(
228 key
: ty
::ParamEnvAnd
<'tcx
, GlobalId
<'tcx
>>,
229 ) -> ::rustc_middle
::mir
::interpret
::EvalToConstValueResult
<'tcx
> {
230 assert
!(key
.param_env
.is_const());
231 // see comment in eval_to_allocation_raw_provider for what we're doing here
232 if key
.param_env
.reveal() == Reveal
::All
{
234 key
.param_env
= key
.param_env
.with_user_facing();
235 match tcx
.eval_to_const_value_raw(key
) {
236 // try again with reveal all as requested
237 Err(ErrorHandled
::TooGeneric
) => {}
239 other
=> return other
,
243 // We call `const_eval` for zero arg intrinsics, too, in order to cache their value.
244 // Catch such calls and evaluate them instead of trying to load a constant's MIR.
245 if let ty
::InstanceDef
::Intrinsic(def_id
) = key
.value
.instance
.def
{
246 let ty
= key
.value
.instance
.ty(tcx
, key
.param_env
);
247 let ty
::FnDef(_
, substs
) = ty
.kind() else {
248 bug
!("intrinsic with type {:?}", ty
);
250 return eval_nullary_intrinsic(tcx
, key
.param_env
, def_id
, substs
).map_err(|error
| {
251 let span
= tcx
.def_span(def_id
);
252 let error
= ConstEvalErr { error: error.into_kind(), stacktrace: vec![], span }
;
253 error
.report_as_error(tcx
.at(span
), "could not evaluate nullary intrinsic")
257 tcx
.eval_to_allocation_raw(key
).map(|val
| turn_into_const_value(tcx
, val
, key
))
260 #[instrument(skip(tcx), level = "debug")]
261 pub fn eval_to_allocation_raw_provider
<'tcx
>(
263 key
: ty
::ParamEnvAnd
<'tcx
, GlobalId
<'tcx
>>,
264 ) -> ::rustc_middle
::mir
::interpret
::EvalToAllocationRawResult
<'tcx
> {
265 assert
!(key
.param_env
.is_const());
266 // Because the constant is computed twice (once per value of `Reveal`), we are at risk of
267 // reporting the same error twice here. To resolve this, we check whether we can evaluate the
268 // constant in the more restrictive `Reveal::UserFacing`, which most likely already was
269 // computed. For a large percentage of constants that will already have succeeded. Only
270 // associated constants of generic functions will fail due to not enough monomorphization
271 // information being available.
273 // In case we fail in the `UserFacing` variant, we just do the real computation.
274 if key
.param_env
.reveal() == Reveal
::All
{
276 key
.param_env
= key
.param_env
.with_user_facing();
277 match tcx
.eval_to_allocation_raw(key
) {
278 // try again with reveal all as requested
279 Err(ErrorHandled
::TooGeneric
) => {}
281 other
=> return other
,
284 if cfg
!(debug_assertions
) {
285 // Make sure we format the instance even if we do not print it.
286 // This serves as a regression test against an ICE on printing.
287 // The next two lines concatenated contain some discussion:
288 // https://rust-lang.zulipchat.com/#narrow/stream/146212-t-compiler.2Fconst-eval/
289 // subject/anon_const_instance_printing/near/135980032
290 let instance
= with_no_trimmed_paths
!(key
.value
.instance
.to_string());
291 trace
!("const eval: {:?} ({})", key
, instance
);
295 let def
= cid
.instance
.def
.with_opt_param();
296 let is_static
= tcx
.is_static(def
.did
);
298 let mut ecx
= InterpCx
::new(
300 tcx
.def_span(def
.did
),
302 // Statics (and promoteds inside statics) may access other statics, because unlike consts
303 // they do not have to behave "as if" they were evaluated at runtime.
304 CompileTimeInterpreter
::new(tcx
.const_eval_limit(), /*can_access_statics:*/ is_static
),
307 let res
= ecx
.load_mir(cid
.instance
.def
, cid
.promoted
);
308 match res
.and_then(|body
| eval_body_using_ecx(&mut ecx
, cid
, &body
)) {
310 let err
= ConstEvalErr
::new(&ecx
, error
, None
);
311 // Some CTFE errors raise just a lint, not a hard error; see
312 // <https://github.com/rust-lang/rust/issues/71800>.
313 let is_hard_err
= if let Some(def
) = def
.as_local() {
314 // (Associated) consts only emit a lint, since they might be unused.
315 !matches
!(tcx
.def_kind(def
.did
.to_def_id()), DefKind
::Const
| DefKind
::AssocConst
)
316 // check if the inner InterpError is hard
317 || err
.error
.is_hard_err()
319 // use of broken constant from other crate: always an error
324 let msg
= if is_static
{
325 Cow
::from("could not evaluate static initializer")
327 // If the current item has generics, we'd like to enrich the message with the
328 // instance and its substs: to show the actual compile-time values, in addition to
329 // the expression, leading to the const eval error.
330 let instance
= &key
.value
.instance
;
331 if !instance
.substs
.is_empty() {
332 let instance
= with_no_trimmed_paths
!(instance
.to_string());
333 let msg
= format
!("evaluation of `{}` failed", instance
);
336 Cow
::from("evaluation of constant value failed")
340 Err(err
.report_as_error(ecx
.tcx
.at(ecx
.cur_span()), &msg
))
342 let hir_id
= tcx
.hir().local_def_id_to_hir_id(def
.as_local().unwrap().did
);
343 Err(err
.report_as_lint(
344 tcx
.at(tcx
.def_span(def
.did
)),
345 "any use of this value will cause an error",
352 // Since evaluation had no errors, validate the resulting constant.
353 // This is a separate `try` block to provide more targeted error reporting.
354 let validation
= try
{
355 let mut ref_tracking
= RefTracking
::new(mplace
);
356 let mut inner
= false;
357 while let Some((mplace
, path
)) = ref_tracking
.todo
.pop() {
358 let mode
= match tcx
.static_mutability(cid
.instance
.def_id()) {
359 Some(_
) if cid
.promoted
.is_some() => {
360 // Promoteds in statics are allowed to point to statics.
361 CtfeValidationMode
::Const { inner, allow_static_ptrs: true }
363 Some(_
) => CtfeValidationMode
::Regular
, // a `static`
364 None
=> CtfeValidationMode
::Const { inner, allow_static_ptrs: false }
,
366 ecx
.const_validate_operand(&mplace
.into(), path
, &mut ref_tracking
, mode
)?
;
370 let alloc_id
= mplace
.ptr
.provenance
.unwrap();
371 if let Err(error
) = validation
{
372 // Validation failed, report an error. This is always a hard error.
373 let err
= ConstEvalErr
::new(&ecx
, error
, None
);
374 Err(err
.struct_error(
376 "it is undefined behavior to use this value",
378 diag
.note(note_on_undefined_behavior_error());
380 "the raw bytes of the constant ({}",
383 ecx
.tcx
.global_alloc(alloc_id
).unwrap_memory().inner()
389 // Convert to raw constant
390 Ok(ConstAlloc { alloc_id, ty: mplace.layout.ty }
)