1 use super::{error_to_const_error, CompileTimeEvalContext, CompileTimeInterpreter, 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 ScalarMaybeUndef
, StackPopCleanup
,
8 use rustc_hir
::def
::DefKind
;
10 use rustc_middle
::mir
::interpret
::{ConstEvalErr, ErrorHandled}
;
11 use rustc_middle
::traits
::Reveal
;
12 use rustc_middle
::ty
::{self, subst::Subst, TyCtxt}
;
13 use rustc_span
::source_map
::Span
;
14 use rustc_target
::abi
::{Abi, LayoutOf}
;
15 use std
::convert
::TryInto
;
17 pub fn note_on_undefined_behavior_error() -> &'
static str {
18 "The rules on what exactly is undefined behavior aren't clear, \
19 so this check might be overzealous. Please open an issue on the rustc \
20 repository if you believe it should not be considered undefined behavior."
23 // Returns a pointer to where the result lives
24 fn eval_body_using_ecx
<'mir
, 'tcx
>(
25 ecx
: &mut CompileTimeEvalContext
<'mir
, 'tcx
>,
27 body
: &'mir mir
::Body
<'tcx
>,
28 ) -> InterpResult
<'tcx
, MPlaceTy
<'tcx
>> {
29 debug
!("eval_body_using_ecx: {:?}, {:?}", cid
, ecx
.param_env
);
30 let tcx
= ecx
.tcx
.tcx
;
31 let layout
= ecx
.layout_of(body
.return_ty().subst(tcx
, cid
.instance
.substs
))?
;
32 assert
!(!layout
.is_unsized());
33 let ret
= ecx
.allocate(layout
, MemoryKind
::Stack
);
35 let name
= ty
::tls
::with(|tcx
| tcx
.def_path_str(cid
.instance
.def_id()));
36 let prom
= cid
.promoted
.map_or(String
::new(), |p
| format
!("::promoted[{:?}]", p
));
37 trace
!("eval_body_using_ecx: pushing stack frame for global: {}{}", name
, prom
);
39 // Assert all args (if any) are zero-sized types; `eval_body_using_ecx` doesn't
40 // make sense if the body is expecting nontrivial arguments.
41 // (The alternative would be to use `eval_fn_call` with an args slice.)
42 for arg
in body
.args_iter() {
43 let decl
= body
.local_decls
.get(arg
).expect("arg missing from local_decls");
44 let layout
= ecx
.layout_of(decl
.ty
.subst(tcx
, cid
.instance
.substs
))?
;
45 assert
!(layout
.is_zst())
52 StackPopCleanup
::None { cleanup: false }
,
55 // The main interpreter loop.
59 let intern_kind
= match tcx
.static_mutability(cid
.instance
.def_id()) {
60 Some(m
) => InternKind
::Static(m
),
61 None
if cid
.promoted
.is_some() => InternKind
::Promoted
,
62 _
=> InternKind
::Constant
,
64 intern_const_alloc_recursive(
68 body
.ignore_interior_mut_in_const_validation
,
71 debug
!("eval_body_using_ecx done: {:?}", *ret
);
75 /// The `InterpCx` is only meant to be used to do field and index projections into constants for
76 /// `simd_shuffle` and const patterns in match arms.
78 /// The function containing the `match` that is currently being analyzed may have generic bounds
79 /// that inform us about the generic bounds of the constant. E.g., using an associated constant
80 /// of a function's generic parameter will require knowledge about the bounds on the generic
81 /// parameter. These bounds are passed to `mk_eval_cx` via the `ParamEnv` argument.
82 pub(super) fn mk_eval_cx
<'mir
, 'tcx
>(
85 param_env
: ty
::ParamEnv
<'tcx
>,
86 can_access_statics
: bool
,
87 ) -> CompileTimeEvalContext
<'mir
, 'tcx
> {
88 debug
!("mk_eval_cx: {:?}", param_env
);
92 CompileTimeInterpreter
::new(*tcx
.sess
.const_eval_limit
.get()),
93 MemoryExtra { can_access_statics }
,
97 pub(super) fn op_to_const
<'tcx
>(
98 ecx
: &CompileTimeEvalContext
<'_
, 'tcx
>,
100 ) -> ConstValue
<'tcx
> {
101 // We do not have value optimizations for everything.
102 // Only scalars and slices, since they are very common.
103 // Note that further down we turn scalars of undefined bits back to `ByRef`. These can result
104 // from scalar unions that are initialized with one of their zero sized variants. We could
105 // instead allow `ConstValue::Scalar` to store `ScalarMaybeUndef`, but that would affect all
106 // the usual cases of extracting e.g. a `usize`, without there being a real use case for the
107 // `Undef` situation.
108 let try_as_immediate
= match op
.layout
.abi
{
109 Abi
::Scalar(..) => true,
110 Abi
::ScalarPair(..) => match op
.layout
.ty
.kind
{
111 ty
::Ref(_
, inner
, _
) => match inner
.kind
{
112 ty
::Slice(elem
) => elem
== ecx
.tcx
.types
.u8,
120 let immediate
= if try_as_immediate
{
121 Err(ecx
.read_immediate(op
).expect("normalization works on validated constants"))
123 // It is guaranteed that any non-slice scalar pair is actually ByRef here.
124 // When we come back from raw const eval, we are always by-ref. The only way our op here is
125 // by-val is if we are in const_field, i.e., if this is (a field of) something that we
126 // "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or
127 // structs containing such.
128 op
.try_as_mplace(ecx
)
131 let to_const_value
= |mplace
: MPlaceTy
<'_
>| match mplace
.ptr
{
132 Scalar
::Ptr(ptr
) => {
133 let alloc
= ecx
.tcx
.alloc_map
.lock().unwrap_memory(ptr
.alloc_id
);
134 ConstValue
::ByRef { alloc, offset: ptr.offset }
136 Scalar
::Raw { data, .. }
=> {
137 assert
!(mplace
.layout
.is_zst());
140 mplace
.layout
.align
.abi
.bytes().into(),
141 "this MPlaceTy must come from `try_as_mplace` being used on a zst, so we know what
142 value this integer address must have",
144 ConstValue
::Scalar(Scalar
::zst())
148 Ok(mplace
) => to_const_value(mplace
),
149 // see comment on `let try_as_immediate` above
150 Err(imm
) => match *imm
{
151 Immediate
::Scalar(x
) => match x
{
152 ScalarMaybeUndef
::Scalar(s
) => ConstValue
::Scalar(s
),
153 ScalarMaybeUndef
::Undef
=> to_const_value(op
.assert_mem_place(ecx
)),
155 Immediate
::ScalarPair(a
, b
) => {
156 let (data
, start
) = match a
.not_undef().unwrap() {
157 Scalar
::Ptr(ptr
) => {
158 (ecx
.tcx
.alloc_map
.lock().unwrap_memory(ptr
.alloc_id
), ptr
.offset
.bytes())
160 Scalar
::Raw { .. }
=> (
162 .intern_const_alloc(Allocation
::from_byte_aligned_bytes(b
"" as &[u8])),
166 let len
= b
.to_machine_usize(&ecx
.tcx
.tcx
).unwrap();
167 let start
= start
.try_into().unwrap();
168 let len
: usize = len
.try_into().unwrap();
169 ConstValue
::Slice { data, start, end: start + len }
175 fn validate_and_turn_into_const
<'tcx
>(
177 constant
: RawConst
<'tcx
>,
178 key
: ty
::ParamEnvAnd
<'tcx
, GlobalId
<'tcx
>>,
179 ) -> ::rustc_middle
::mir
::interpret
::ConstEvalResult
<'tcx
> {
181 let def_id
= cid
.instance
.def
.def_id();
182 let is_static
= tcx
.is_static(def_id
);
183 let ecx
= mk_eval_cx(tcx
, tcx
.def_span(key
.value
.instance
.def_id()), key
.param_env
, is_static
);
185 let mplace
= ecx
.raw_const_to_mplace(constant
)?
;
187 // FIXME do not validate promoteds until a decision on
188 // https://github.com/rust-lang/rust/issues/67465 is made
189 if cid
.promoted
.is_none() {
190 let mut ref_tracking
= RefTracking
::new(mplace
);
191 while let Some((mplace
, path
)) = ref_tracking
.todo
.pop() {
192 ecx
.const_validate_operand(
196 /*may_ref_to_static*/ is_static
,
200 // Now that we validated, turn this into a proper constant.
201 // Statics/promoteds are always `ByRef`, for the rest `op_to_const` decides
202 // whether they become immediates.
203 if is_static
|| cid
.promoted
.is_some() {
204 let ptr
= mplace
.ptr
.assert_ptr();
205 Ok(ConstValue
::ByRef
{
206 alloc
: ecx
.tcx
.alloc_map
.lock().unwrap_memory(ptr
.alloc_id
),
210 Ok(op_to_const(&ecx
, mplace
.into()))
214 val
.map_err(|error
| {
215 let err
= error_to_const_error(&ecx
, error
);
216 err
.struct_error(ecx
.tcx
, "it is undefined behavior to use this value", |mut diag
| {
217 diag
.note(note_on_undefined_behavior_error());
223 pub fn const_eval_validated_provider
<'tcx
>(
225 key
: ty
::ParamEnvAnd
<'tcx
, GlobalId
<'tcx
>>,
226 ) -> ::rustc_middle
::mir
::interpret
::ConstEvalResult
<'tcx
> {
227 // see comment in const_eval_raw_provider for what we're doing here
228 if key
.param_env
.reveal
== Reveal
::All
{
230 key
.param_env
.reveal
= Reveal
::UserFacing
;
231 match tcx
.const_eval_validated(key
) {
232 // try again with reveal all as requested
233 Err(ErrorHandled
::TooGeneric
) => {}
235 other
=> return other
,
239 // We call `const_eval` for zero arg intrinsics, too, in order to cache their value.
240 // Catch such calls and evaluate them instead of trying to load a constant's MIR.
241 if let ty
::InstanceDef
::Intrinsic(def_id
) = key
.value
.instance
.def
{
242 let ty
= key
.value
.instance
.ty_env(tcx
, key
.param_env
);
243 let substs
= match ty
.kind
{
244 ty
::FnDef(_
, substs
) => substs
,
245 _
=> bug
!("intrinsic with type {:?}", ty
),
247 return eval_nullary_intrinsic(tcx
, key
.param_env
, def_id
, substs
).map_err(|error
| {
248 let span
= tcx
.def_span(def_id
);
249 let error
= ConstEvalErr { error: error.kind, stacktrace: vec![], span }
;
250 error
.report_as_error(tcx
.at(span
), "could not evaluate nullary intrinsic")
254 tcx
.const_eval_raw(key
).and_then(|val
| validate_and_turn_into_const(tcx
, val
, key
))
257 pub fn const_eval_raw_provider
<'tcx
>(
259 key
: ty
::ParamEnvAnd
<'tcx
, GlobalId
<'tcx
>>,
260 ) -> ::rustc_middle
::mir
::interpret
::ConstEvalRawResult
<'tcx
> {
261 // Because the constant is computed twice (once per value of `Reveal`), we are at risk of
262 // reporting the same error twice here. To resolve this, we check whether we can evaluate the
263 // constant in the more restrictive `Reveal::UserFacing`, which most likely already was
264 // computed. For a large percentage of constants that will already have succeeded. Only
265 // associated constants of generic functions will fail due to not enough monomorphization
266 // information being available.
268 // In case we fail in the `UserFacing` variant, we just do the real computation.
269 if key
.param_env
.reveal
== Reveal
::All
{
271 key
.param_env
.reveal
= Reveal
::UserFacing
;
272 match tcx
.const_eval_raw(key
) {
273 // try again with reveal all as requested
274 Err(ErrorHandled
::TooGeneric
) => {}
276 other
=> return other
,
279 if cfg
!(debug_assertions
) {
280 // Make sure we format the instance even if we do not print it.
281 // This serves as a regression test against an ICE on printing.
282 // The next two lines concatenated contain some discussion:
283 // https://rust-lang.zulipchat.com/#narrow/stream/146212-t-compiler.2Fconst-eval/
284 // subject/anon_const_instance_printing/near/135980032
285 let instance
= key
.value
.instance
.to_string();
286 trace
!("const eval: {:?} ({})", key
, instance
);
290 let def_id
= cid
.instance
.def
.def_id();
292 if def_id
.is_local() && tcx
.has_typeck_tables(def_id
) {
293 if let Some(error_reported
) = tcx
.typeck_tables_of(def_id
).tainted_by_errors
{
294 return Err(ErrorHandled
::Reported(error_reported
));
298 let is_static
= tcx
.is_static(def_id
);
300 let span
= tcx
.def_span(cid
.instance
.def_id());
301 let mut ecx
= InterpCx
::new(
304 CompileTimeInterpreter
::new(*tcx
.sess
.const_eval_limit
.get()),
305 MemoryExtra { can_access_statics: is_static }
,
308 let res
= ecx
.load_mir(cid
.instance
.def
, cid
.promoted
);
309 res
.and_then(|body
| eval_body_using_ecx(&mut ecx
, cid
, &body
))
311 Ok(RawConst { alloc_id: place.ptr.assert_ptr().alloc_id, ty: place.layout.ty }
)
314 let err
= error_to_const_error(&ecx
, error
);
315 // errors in statics are always emitted as fatal errors
317 // Ensure that if the above error was either `TooGeneric` or `Reported`
318 // an error must be reported.
319 let v
= err
.report_as_error(ecx
.tcx
, "could not evaluate static initializer");
321 // If this is `Reveal:All`, then we need to make sure an error is reported but if
322 // this is `Reveal::UserFacing`, then it's expected that we could get a
323 // `TooGeneric` error. When we fall back to `Reveal::All`, then it will either
324 // succeed or we'll report this error then.
325 if key
.param_env
.reveal
== Reveal
::All
{
326 tcx
.sess
.delay_span_bug(
328 &format
!("static eval failure did not emit an error: {:#?}", v
),
333 } else if def_id
.is_local() {
334 // constant defined in this crate, we can figure out a lint level!
335 match tcx
.def_kind(def_id
) {
336 // constants never produce a hard error at the definition site. Anything else is
337 // a backwards compatibility hazard (and will break old versions of winapi for
340 // note that validation may still cause a hard error on this very same constant,
341 // because any code that existed before validation could not have failed
342 // validation thus preventing such a hard error from being a backwards
343 // compatibility hazard
344 Some(DefKind
::Const
| DefKind
::AssocConst
) => {
345 let hir_id
= tcx
.hir().as_local_hir_id(def_id
).unwrap();
347 tcx
.at(tcx
.def_span(def_id
)),
348 "any use of this value will cause an error",
353 // promoting runtime code is only allowed to error if it references broken
354 // constants any other kind of error will be reported to the user as a
355 // deny-by-default lint
357 if let Some(p
) = cid
.promoted
{
358 let span
= tcx
.promoted_mir(def_id
)[p
].span
;
359 if let err_inval
!(ReferencedConstant
) = err
.error
{
362 "evaluation of constant expression failed",
367 "reaching this expression at runtime will panic or abort",
368 tcx
.hir().as_local_hir_id(def_id
).unwrap(),
372 // anything else (array lengths, enum initializers, constant patterns) are
373 // reported as hard errors
375 err
.report_as_error(ecx
.tcx
, "evaluation of constant value failed")
380 // use of broken constant from other crate
381 err
.report_as_error(ecx
.tcx
, "could not evaluate constant")