1 use super::{AllocId, CheckInAllocMsg, Pointer, RawConst, ScalarMaybeUndef};
3 use crate::hir::map::definitions::DefPathData;
4 use crate::mir::interpret::ConstValue;
5 use crate::ty::layout::{Align, LayoutError, Size};
6 use crate::ty::query::TyCtxtAt;
8 use crate::ty::{self, layout, Ty};
10 use backtrace::Backtrace;
11 use rustc_data_structures::sync::Lock;
12 use rustc_errors::{struct_span_err, DiagnosticBuilder};
14 use rustc_macros::HashStable;
15 use rustc_session::CtfeBacktrace;
16 use rustc_span::{Pos, Span, def_id::DefId};
17 use std::{any::Any, fmt};
19 #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, RustcEncodable, RustcDecodable)]
20 pub enum ErrorHandled {
21 /// Already reported a lint or an error for this evaluation.
23 /// Don't emit an error, the evaluation failed because the MIR was generic
24 /// and the substs didn't fully monomorphize it.
29 pub fn assert_reported(self) {
31 ErrorHandled::Reported => {}
32 ErrorHandled::TooGeneric => bug!(
33 "MIR interpretation failed without reporting an error \
34 even though it was fully monomorphized"
40 CloneTypeFoldableImpls! {
44 pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
45 pub type ConstEvalResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
48 pub struct ConstEvalErr<'tcx> {
50 pub error: crate::mir::interpret::InterpError<'tcx>,
51 pub stacktrace: Vec<FrameInfo<'tcx>>,
55 pub struct FrameInfo<'tcx> {
56 /// This span is in the caller.
58 pub instance: ty::Instance<'tcx>,
59 pub lint_root: Option<hir::HirId>,
62 impl<'tcx> fmt::Display for FrameInfo<'tcx> {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 if tcx.def_key(self.instance.def_id()).disambiguated_data.data
66 == DefPathData::ClosureExpr
68 write!(f, "inside call to closure")?;
70 write!(f, "inside call to `{}`", self.instance)?;
72 if !self.call_site.is_dummy() {
73 let lo = tcx.sess.source_map().lookup_char_pos(self.call_site.lo());
74 write!(f, " at {}:{}:{}", lo.file.name, lo.line, lo.col.to_usize() + 1)?;
81 impl<'tcx> ConstEvalErr<'tcx> {
86 emit: impl FnOnce(DiagnosticBuilder<'_>),
87 ) -> Result<(), ErrorHandled> {
88 self.struct_generic(tcx, message, emit, None)
91 pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
92 match self.struct_error(tcx, message, |mut e| e.emit()) {
93 Ok(_) => ErrorHandled::Reported,
98 pub fn report_as_lint(
102 lint_root: hir::HirId,
105 match self.struct_generic(
108 |mut lint: DiagnosticBuilder<'_>| {
110 if let Some(span) = span {
111 let primary_spans = lint.span.primary_spans().to_vec();
112 // point at the actual error as the primary span
113 lint.replace_span_with(span);
114 // point to the `const` statement as a secondary span
115 // they don't have any label
116 for sp in primary_spans {
118 lint.span_label(sp, "");
126 Ok(_) => ErrorHandled::Reported,
131 /// Create a diagnostic for this const eval error.
133 /// Sets the message passed in via `message` and adds span labels with detailed error
134 /// information before handing control back to `emit` to do any final processing.
135 /// It's the caller's responsibility to call emit(), stash(), etc. within the `emit`
136 /// function to dispose of the diagnostic properly.
138 /// If `lint_root.is_some()` report it as a lint, else report it as a hard error.
139 /// (Except that for some errors, we ignore all that -- see `must_error` below.)
144 emit: impl FnOnce(DiagnosticBuilder<'_>),
145 lint_root: Option<hir::HirId>,
146 ) -> Result<(), ErrorHandled> {
147 let must_error = match self.error {
148 err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
149 return Err(ErrorHandled::TooGeneric);
151 err_inval!(TypeckError) => return Err(ErrorHandled::Reported),
152 // We must *always* hard error on these, even if the caller wants just a lint.
153 err_inval!(Layout(LayoutError::SizeOverflow(_))) => true,
156 trace!("reporting const eval failure at {:?}", self.span);
158 let err_msg = match &self.error {
159 InterpError::MachineStop(msg) => {
160 // A custom error (`ConstEvalErrKind` in `librustc_mir/interp/const_eval/error.rs`).
161 // Should be turned into a string by now.
162 msg.downcast_ref::<String>().expect("invalid MachineStop payload").clone()
164 err => err.to_string(),
167 let finish = |mut err: DiagnosticBuilder<'_>, span_msg: Option<String>| {
168 if let Some(span_msg) = span_msg {
169 err.span_label(self.span, span_msg);
171 // Add spans for the stacktrace.
172 // Skip the last, which is just the environment of the constant. The stacktrace
173 // is sometimes empty because we create "fake" eval contexts in CTFE to do work
174 // on constant values.
175 if !self.stacktrace.is_empty() {
176 for frame_info in &self.stacktrace[..self.stacktrace.len() - 1] {
177 err.span_label(frame_info.call_site, frame_info.to_string());
180 // Let the caller finish the job.
185 // The `message` makes little sense here, this is a more serious error than the
186 // caller thinks anyway.
187 // See <https://github.com/rust-lang/rust/pull/63152>.
188 finish(struct_error(tcx, &err_msg), None);
191 if let Some(lint_root) = lint_root {
197 .filter_map(|frame| frame.lint_root)
199 .unwrap_or(lint_root);
200 tcx.struct_span_lint_hir(
201 rustc_session::lint::builtin::CONST_ERR,
204 |lint| finish(lint.build(message), Some(err_msg)),
207 // Report as hard error.
208 finish(struct_error(tcx, message), Some(err_msg));
215 pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
216 struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
219 /// Packages the kind of error we got from the const code interpreter
220 /// up with a Rust-level backtrace of where the error occurred.
221 /// Thsese should always be constructed by calling `.into()` on
222 /// a `InterpError`. In `librustc_mir::interpret`, we have `throw_err_*`
225 pub struct InterpErrorInfo<'tcx> {
226 pub kind: InterpError<'tcx>,
227 backtrace: Option<Box<Backtrace>>,
230 impl fmt::Display for InterpErrorInfo<'_> {
231 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
232 write!(f, "{}", self.kind)
236 impl InterpErrorInfo<'_> {
237 pub fn print_backtrace(&mut self) {
238 if let Some(ref mut backtrace) = self.backtrace {
239 print_backtrace(&mut *backtrace);
244 fn print_backtrace(backtrace: &mut Backtrace) {
246 eprintln!("\n\nAn error occurred in miri:\n{:?}", backtrace);
249 impl From<ErrorHandled> for InterpErrorInfo<'_> {
250 fn from(err: ErrorHandled) -> Self {
252 ErrorHandled::Reported => err_inval!(ReferencedConstant),
253 ErrorHandled::TooGeneric => err_inval!(TooGeneric),
259 impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
260 fn from(kind: InterpError<'tcx>) -> Self {
261 let capture_backtrace = tls::with_context_opt(|ctxt| {
262 if let Some(ctxt) = ctxt {
263 *Lock::borrow(&ctxt.tcx.sess.ctfe_backtrace)
265 CtfeBacktrace::Disabled
269 let backtrace = match capture_backtrace {
270 CtfeBacktrace::Disabled => None,
271 CtfeBacktrace::Capture => Some(Box::new(Backtrace::new_unresolved())),
272 CtfeBacktrace::Immediate => {
274 let mut backtrace = Backtrace::new_unresolved();
275 print_backtrace(&mut backtrace);
280 InterpErrorInfo { kind, backtrace }
284 /// Error information for when the program we executed turned out not to actually be a valid
285 /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
286 /// where we work on generic code or execution does not have all information available.
287 pub enum InvalidProgramInfo<'tcx> {
288 /// Resolution can fail if we are in a too generic context.
290 /// Cannot compute this constant because it depends on another one
291 /// which already produced an error.
293 /// Abort in case type errors are reached.
295 /// An error occurred during layout computation.
296 Layout(layout::LayoutError<'tcx>),
297 /// An invalid transmute happened.
298 TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>),
301 impl fmt::Debug for InvalidProgramInfo<'_> {
302 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303 use InvalidProgramInfo::*;
305 TooGeneric => write!(f, "encountered overly generic constant"),
306 ReferencedConstant => write!(f, "referenced constant has errors"),
307 TypeckError => write!(f, "encountered constants with type errors, stopping evaluation"),
308 Layout(ref err) => write!(f, "{}", err),
309 TransmuteSizeDiff(from_ty, to_ty) => write!(
311 "tried to transmute from {:?} to {:?}, but their sizes differed",
318 /// Error information for when the program caused Undefined Behavior.
319 pub enum UndefinedBehaviorInfo {
320 /// Free-form case. Only for errors that are never caught!
322 /// Free-form case for experimental UB. Only for errors that are never caught!
323 UbExperimental(String),
324 /// Unreachable code was executed.
326 /// An enum discriminant was set to a value which was outside the range of valid values.
327 InvalidDiscriminant(ScalarMaybeUndef),
328 /// A slice/array index projection went out-of-bounds.
333 /// Something was divided by 0 (x / 0).
335 /// Something was "remainded" by 0 (x % 0).
337 /// Overflowing inbounds pointer arithmetic.
338 PointerArithOverflow,
339 /// Invalid metadata in a wide pointer (using `str` to avoid allocations).
340 InvalidMeta(&'static str),
341 /// Reading a C string that does not end within its allocation.
342 UnterminatedCString(Pointer),
343 /// Dereferencing a dangling pointer after it got freed.
344 PointerUseAfterFree(AllocId),
345 /// Used a pointer outside the bounds it is valid for.
348 msg: CheckInAllocMsg,
349 allocation_size: Size,
351 /// Used a pointer with bad alignment.
352 AlignmentCheckFailed {
356 /// Using an integer as a pointer in the wrong way.
357 InvalidIntPointerUsage(u64),
358 /// Writing to read-only memory.
359 WriteToReadOnly(AllocId),
360 /// Using a pointer-not-to-a-function as function pointer.
361 InvalidFunctionPointer(Pointer),
362 // Trying to access the data behind a function pointer.
363 DerefFunctionPointer(AllocId),
364 /// The value validity check found a problem.
365 /// Should only be thrown by `validity.rs` and always point out which part of the value
367 ValidationFailure(String),
368 /// Using a non-boolean `u8` as bool.
370 /// Using a non-character `u32` as character.
372 /// Using uninitialized data where it is not allowed.
373 InvalidUndefBytes(Option<Pointer>),
374 /// Working with a local that is not currently live.
376 /// Trying to read from the return place of a function.
380 impl fmt::Debug for UndefinedBehaviorInfo {
381 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
382 use UndefinedBehaviorInfo::*;
384 Ub(msg) | UbExperimental(msg) => write!(f, "{}", msg),
385 Unreachable => write!(f, "entering unreachable code"),
386 InvalidDiscriminant(val) => write!(f, "encountering invalid enum discriminant {}", val),
387 BoundsCheckFailed { ref len, ref index } => write!(
389 "indexing out of bounds: the len is {:?} but the index is {:?}",
392 DivisionByZero => write!(f, "dividing by zero"),
393 RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
394 PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
395 InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg),
396 UnterminatedCString(p) => write!(
398 "reading a null-terminated string starting at {:?} with no null found before end of allocation",
401 PointerUseAfterFree(a) => {
402 write!(f, "pointer to {:?} was dereferenced after this allocation got freed", a)
404 PointerOutOfBounds { ptr, msg, allocation_size } => write!(
406 "{} failed: pointer must be in-bounds at offset {}, \
407 but is outside bounds of {} which has size {}",
411 allocation_size.bytes()
413 InvalidIntPointerUsage(0) => write!(f, "invalid use of NULL pointer"),
414 InvalidIntPointerUsage(i) => write!(f, "invalid use of {} as a pointer", i),
415 AlignmentCheckFailed { required, has } => write!(
417 "accessing memory with alignment {}, but alignment {} is required",
421 WriteToReadOnly(a) => write!(f, "writing to {:?} which is read-only", a),
422 InvalidFunctionPointer(p) => {
423 write!(f, "using {:?} as function pointer but it does not point to a function", p)
425 DerefFunctionPointer(a) => write!(f, "accessing {:?} which contains a function", a),
426 ValidationFailure(ref err) => write!(f, "type validation failed: {}", err),
427 InvalidBool(b) => write!(f, "interpreting an invalid 8-bit value as a bool: {}", b),
428 InvalidChar(c) => write!(f, "interpreting an invalid 32-bit value as a char: {}", c),
429 InvalidUndefBytes(Some(p)) => write!(
431 "reading uninitialized memory at {:?}, but this operation requires initialized memory",
434 InvalidUndefBytes(None) => write!(
436 "using uninitialized data, but this operation requires initialized memory"
438 DeadLocal => write!(f, "accessing a dead local variable"),
439 ReadFromReturnPlace => write!(f, "tried to read from the return place"),
444 /// Error information for when the program did something that might (or might not) be correct
445 /// to do according to the Rust spec, but due to limitations in the interpreter, the
446 /// operation could not be carried out. These limitations can differ between CTFE and the
447 /// Miri engine, e.g., CTFE does not support casting pointers to "real" integers.
449 /// Currently, we also use this as fall-back error kind for errors that have not been
451 pub enum UnsupportedOpInfo {
452 /// Free-form case. Only for errors that are never caught!
454 /// When const-prop encounters a situation it does not support, it raises this error.
455 /// This must not allocate for performance reasons (hence `str`, not `String`).
456 ConstPropUnsupported(&'static str),
457 /// Accessing an unsupported foreign static.
458 ReadForeignStatic(DefId),
459 /// Could not find MIR for a function.
461 /// Modified a static during const-eval.
462 /// FIXME: move this to `ConstEvalErrKind` through a machine hook.
464 /// Encountered a pointer where we needed raw bytes.
466 /// Encountered raw bytes where we needed a pointer.
470 impl fmt::Debug for UnsupportedOpInfo {
471 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
472 use UnsupportedOpInfo::*;
474 Unsupported(ref msg) => write!(f, "{}", msg),
475 ConstPropUnsupported(ref msg) => {
476 write!(f, "Constant propagation encountered an unsupported situation: {}", msg)
478 ReadForeignStatic(did) => {
479 write!(f, "tried to read from foreign (extern) static {:?}", did)
481 NoMirFor(did) => write!(f, "could not load MIR for {:?}", did),
482 ModifiedStatic => write!(
484 "tried to modify a static's initial value from another static's \
488 ReadPointerAsBytes => write!(f, "unable to turn this pointer into raw bytes",),
489 ReadBytesAsPointer => write!(f, "unable to turn these bytes into a pointer"),
494 /// Error information for when the program exhausted the resources granted to it
495 /// by the interpreter.
496 pub enum ResourceExhaustionInfo {
497 /// The stack grew too big.
498 StackFrameLimitReached,
499 /// The program ran into an infinite loop.
503 impl fmt::Debug for ResourceExhaustionInfo {
504 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
505 use ResourceExhaustionInfo::*;
507 StackFrameLimitReached => {
508 write!(f, "reached the configured maximum number of stack frames")
510 InfiniteLoop => write!(
512 "duplicate interpreter state observed here, const evaluation will never \
519 pub enum InterpError<'tcx> {
520 /// The program caused undefined behavior.
521 UndefinedBehavior(UndefinedBehaviorInfo),
522 /// The program did something the interpreter does not support (some of these *might* be UB
523 /// but the interpreter is not sure).
524 Unsupported(UnsupportedOpInfo),
525 /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...).
526 InvalidProgram(InvalidProgramInfo<'tcx>),
527 /// The program exhausted the interpreter's resources (stack/heap too big,
528 /// execution takes too long, ...).
529 ResourceExhaustion(ResourceExhaustionInfo),
530 /// Stop execution for a machine-controlled reason. This is never raised by
531 /// the core engine itself.
532 MachineStop(Box<dyn Any + Send>),
535 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
537 impl fmt::Display for InterpError<'_> {
538 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
539 // Forward `Display` to `Debug`.
540 fmt::Debug::fmt(self, f)
544 impl fmt::Debug for InterpError<'_> {
545 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
548 Unsupported(ref msg) => write!(f, "{:?}", msg),
549 InvalidProgram(ref msg) => write!(f, "{:?}", msg),
550 UndefinedBehavior(ref msg) => write!(f, "{:?}", msg),
551 ResourceExhaustion(ref msg) => write!(f, "{:?}", msg),
552 MachineStop(_) => bug!("unhandled MachineStop"),
557 impl InterpError<'_> {
558 /// Some errors allocate to be created as they contain free-form strings.
559 /// And sometimes we want to be sure that did not happen as it is a
560 /// waste of resources.
561 pub fn allocates(&self) -> bool {
563 InterpError::MachineStop(_)
564 | InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
565 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure(_))
566 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
567 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::UbExperimental(_)) => true,