1 use super::{CheckInAllocMsg, Pointer, RawConst, ScalarMaybeUndef};
3 use crate::hir::map::definitions::DefPathData;
5 use crate::mir::interpret::ConstValue;
6 use crate::ty::layout::{Align, LayoutError, Size};
7 use crate::ty::query::TyCtxtAt;
9 use crate::ty::{self, layout, Ty};
11 use backtrace::Backtrace;
12 use rustc_data_structures::sync::Lock;
13 use rustc_errors::{struct_span_err, DiagnosticBuilder};
15 use rustc_macros::HashStable;
16 use rustc_session::CtfeBacktrace;
17 use rustc_span::{Pos, Span};
18 use rustc_target::spec::abi::Abi;
19 use std::{any::Any, fmt};
21 #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, RustcEncodable, RustcDecodable)]
22 pub enum ErrorHandled {
23 /// Already reported a lint or an error for this evaluation.
25 /// Don't emit an error, the evaluation failed because the MIR was generic
26 /// and the substs didn't fully monomorphize it.
31 pub fn assert_reported(self) {
33 ErrorHandled::Reported => {}
34 ErrorHandled::TooGeneric => bug!(
35 "MIR interpretation failed without reporting an error \
36 even though it was fully monomorphized"
42 CloneTypeFoldableImpls! {
46 pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
47 pub type ConstEvalResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
50 pub struct ConstEvalErr<'tcx> {
52 pub error: crate::mir::interpret::InterpError<'tcx>,
53 pub stacktrace: Vec<FrameInfo<'tcx>>,
57 pub struct FrameInfo<'tcx> {
58 /// This span is in the caller.
60 pub instance: ty::Instance<'tcx>,
61 pub lint_root: Option<hir::HirId>,
64 impl<'tcx> fmt::Display for FrameInfo<'tcx> {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 if tcx.def_key(self.instance.def_id()).disambiguated_data.data
68 == DefPathData::ClosureExpr
70 write!(f, "inside call to closure")?;
72 write!(f, "inside call to `{}`", self.instance)?;
74 if !self.call_site.is_dummy() {
75 let lo = tcx.sess.source_map().lookup_char_pos(self.call_site.lo());
76 write!(f, " at {}:{}:{}", lo.file.name, lo.line, lo.col.to_usize() + 1)?;
83 impl<'tcx> ConstEvalErr<'tcx> {
88 emit: impl FnOnce(DiagnosticBuilder<'_>),
89 ) -> Result<(), ErrorHandled> {
90 self.struct_generic(tcx, message, emit, None)
93 pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
94 match self.struct_error(tcx, message, |mut e| e.emit()) {
95 Ok(_) => ErrorHandled::Reported,
100 pub fn report_as_lint(
104 lint_root: hir::HirId,
107 match self.struct_generic(
110 |mut lint: DiagnosticBuilder<'_>| {
112 if let Some(span) = span {
113 let primary_spans = lint.span.primary_spans().to_vec();
114 // point at the actual error as the primary span
115 lint.replace_span_with(span);
116 // point to the `const` statement as a secondary span
117 // they don't have any label
118 for sp in primary_spans {
120 lint.span_label(sp, "");
128 Ok(_) => ErrorHandled::Reported,
133 /// Create a diagnostic for this const eval error.
135 /// Sets the message passed in via `message` and adds span labels with detailed error
136 /// information before handing control back to `emit` to do any final processing.
137 /// It's the caller's responsibility to call emit(), stash(), etc. within the `emit`
138 /// function to dispose of the diagnostic properly.
140 /// If `lint_root.is_some()` report it as a lint, else report it as a hard error.
141 /// (Except that for some errors, we ignore all that -- see `must_error` below.)
146 emit: impl FnOnce(DiagnosticBuilder<'_>),
147 lint_root: Option<hir::HirId>,
148 ) -> Result<(), ErrorHandled> {
149 let must_error = match self.error {
150 err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
151 return Err(ErrorHandled::TooGeneric);
153 err_inval!(TypeckError) => return Err(ErrorHandled::Reported),
154 // We must *always* hard error on these, even if the caller wants just a lint.
155 err_inval!(Layout(LayoutError::SizeOverflow(_))) => true,
158 trace!("reporting const eval failure at {:?}", self.span);
160 let err_msg = match &self.error {
161 InterpError::MachineStop(msg) => {
162 // A custom error (`ConstEvalErrKind` in `librustc_mir/interp/const_eval/error.rs`).
163 // Should be turned into a string by now.
164 msg.downcast_ref::<String>().expect("invalid MachineStop payload").clone()
166 err => err.to_string(),
169 let finish = |mut err: DiagnosticBuilder<'_>, span_msg: Option<String>| {
170 if let Some(span_msg) = span_msg {
171 err.span_label(self.span, span_msg);
173 // Add spans for the stacktrace.
174 // Skip the last, which is just the environment of the constant. The stacktrace
175 // is sometimes empty because we create "fake" eval contexts in CTFE to do work
176 // on constant values.
177 if !self.stacktrace.is_empty() {
178 for frame_info in &self.stacktrace[..self.stacktrace.len() - 1] {
179 err.span_label(frame_info.call_site, frame_info.to_string());
182 // Let the caller finish the job.
187 // The `message` makes little sense here, this is a more serious error than the
188 // caller thinks anyway.
189 // See <https://github.com/rust-lang/rust/pull/63152>.
190 finish(struct_error(tcx, &err_msg), None);
193 if let Some(lint_root) = lint_root {
199 .filter_map(|frame| frame.lint_root)
201 .unwrap_or(lint_root);
202 tcx.struct_span_lint_hir(
203 rustc_session::lint::builtin::CONST_ERR,
206 |lint| finish(lint.build(message), Some(err_msg)),
209 // Report as hard error.
210 finish(struct_error(tcx, message), Some(err_msg));
217 pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
218 struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
221 /// Packages the kind of error we got from the const code interpreter
222 /// up with a Rust-level backtrace of where the error occurred.
223 /// Thsese should always be constructed by calling `.into()` on
224 /// a `InterpError`. In `librustc_mir::interpret`, we have `throw_err_*`
227 pub struct InterpErrorInfo<'tcx> {
228 pub kind: InterpError<'tcx>,
229 backtrace: Option<Box<Backtrace>>,
232 impl fmt::Display for InterpErrorInfo<'_> {
233 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234 write!(f, "{}", self.kind)
238 impl InterpErrorInfo<'_> {
239 pub fn print_backtrace(&mut self) {
240 if let Some(ref mut backtrace) = self.backtrace {
241 print_backtrace(&mut *backtrace);
246 fn print_backtrace(backtrace: &mut Backtrace) {
248 eprintln!("\n\nAn error occurred in miri:\n{:?}", backtrace);
251 impl From<ErrorHandled> for InterpErrorInfo<'_> {
252 fn from(err: ErrorHandled) -> Self {
254 ErrorHandled::Reported => err_inval!(ReferencedConstant),
255 ErrorHandled::TooGeneric => err_inval!(TooGeneric),
261 impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
262 fn from(kind: InterpError<'tcx>) -> Self {
263 let capture_backtrace = tls::with_context_opt(|ctxt| {
264 if let Some(ctxt) = ctxt {
265 *Lock::borrow(&ctxt.tcx.sess.ctfe_backtrace)
267 CtfeBacktrace::Disabled
271 let backtrace = match capture_backtrace {
272 CtfeBacktrace::Disabled => None,
273 CtfeBacktrace::Capture => Some(Box::new(Backtrace::new_unresolved())),
274 CtfeBacktrace::Immediate => {
276 let mut backtrace = Backtrace::new_unresolved();
277 print_backtrace(&mut backtrace);
282 InterpErrorInfo { kind, backtrace }
286 /// Error information for when the program we executed turned out not to actually be a valid
287 /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
288 /// where we work on generic code or execution does not have all information available.
289 pub enum InvalidProgramInfo<'tcx> {
290 /// Resolution can fail if we are in a too generic context.
292 /// Cannot compute this constant because it depends on another one
293 /// which already produced an error.
295 /// Abort in case type errors are reached.
297 /// An error occurred during layout computation.
298 Layout(layout::LayoutError<'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),
313 /// Error information for when the program caused Undefined Behavior.
314 pub enum UndefinedBehaviorInfo {
315 /// Free-form case. Only for errors that are never caught!
317 /// Free-form case for experimental UB. Only for errors that are never caught!
318 UbExperimental(String),
319 /// Unreachable code was executed.
321 /// An enum discriminant was set to a value which was outside the range of valid values.
322 InvalidDiscriminant(ScalarMaybeUndef),
323 /// A slice/array index projection went out-of-bounds.
324 BoundsCheckFailed { len: u64, index: u64 },
325 /// Something was divided by 0 (x / 0).
327 /// Something was "remainded" by 0 (x % 0).
329 /// Overflowing inbounds pointer arithmetic.
330 PointerArithOverflow,
331 /// Invalid metadata in a wide pointer (using `str` to avoid allocations).
332 InvalidMeta(&'static str),
335 impl fmt::Debug for UndefinedBehaviorInfo {
336 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
337 use UndefinedBehaviorInfo::*;
339 Ub(msg) | UbExperimental(msg) => write!(f, "{}", msg),
340 Unreachable => write!(f, "entering unreachable code"),
341 InvalidDiscriminant(val) => write!(f, "encountering invalid enum discriminant {}", val),
342 BoundsCheckFailed { ref len, ref index } => write!(
344 "indexing out of bounds: the len is {:?} but the index is {:?}",
347 DivisionByZero => write!(f, "dividing by zero"),
348 RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
349 PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
350 InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg),
355 /// Error information for when the program did something that might (or might not) be correct
356 /// to do according to the Rust spec, but due to limitations in the interpreter, the
357 /// operation could not be carried out. These limitations can differ between CTFE and the
358 /// Miri engine, e.g., CTFE does not support casting pointers to "real" integers.
360 /// Currently, we also use this as fall-back error kind for errors that have not been
362 pub enum UnsupportedOpInfo<'tcx> {
363 /// Free-form case. Only for errors that are never caught!
366 /// When const-prop encounters a situation it does not support, it raises this error.
367 /// This must not allocate for performance reasons (hence `str`, not `String`).
368 ConstPropUnsupported(&'static str),
370 // -- Everything below is not categorized yet --
371 FunctionAbiMismatch(Abi, Abi),
372 FunctionArgMismatch(Ty<'tcx>, Ty<'tcx>),
373 FunctionRetMismatch(Ty<'tcx>, Ty<'tcx>),
374 FunctionArgCountMismatch,
375 UnterminatedCString(Pointer),
376 DanglingPointerDeref,
379 InvalidFunctionPointer,
383 msg: CheckInAllocMsg,
384 allocation_size: Size,
386 InvalidNullPointerUsage,
391 ReadUndefBytes(Size),
393 InvalidBoolOp(mir::BinOp),
394 UnimplementedTraitSelection,
395 CalledClosureAsFunction,
397 DerefFunctionPointer,
402 AlignmentCheckFailed {
406 ValidationFailure(String),
407 VtableForArgumentlessMethod,
408 ModifiedConstantMemory,
410 TypeNotPrimitive(Ty<'tcx>),
411 ReallocatedWrongMemoryKind(String, String),
412 DeallocatedWrongMemoryKind(String, String),
413 ReallocateNonBasePtr,
414 DeallocateNonBasePtr,
415 IncorrectAllocationInformation(Size, Size, Align, Align),
417 HeapAllocNonPowerOfTwoAlignment(u64),
418 ReadFromReturnPointer,
419 PathNotFound(Vec<String>),
420 TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>),
423 impl fmt::Debug for UnsupportedOpInfo<'tcx> {
424 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
425 use UnsupportedOpInfo::*;
427 PointerOutOfBounds { ptr, msg, allocation_size } => write!(
429 "{} failed: pointer must be in-bounds at offset {}, \
430 but is outside bounds of allocation {} which has size {}",
434 allocation_size.bytes()
436 ValidationFailure(ref err) => write!(f, "type validation failed: {}", err),
437 NoMirFor(ref func) => write!(f, "no MIR for `{}`", func),
438 FunctionAbiMismatch(caller_abi, callee_abi) => write!(
440 "tried to call a function with ABI {:?} using caller ABI {:?}",
441 callee_abi, caller_abi
443 FunctionArgMismatch(caller_ty, callee_ty) => write!(
445 "tried to call a function with argument of type {:?} \
446 passing data of type {:?}",
449 TransmuteSizeDiff(from_ty, to_ty) => write!(
451 "tried to transmute from {:?} to {:?}, but their sizes differed",
454 FunctionRetMismatch(caller_ty, callee_ty) => write!(
456 "tried to call a function with return type {:?} \
457 passing return place of type {:?}",
460 FunctionArgCountMismatch => {
461 write!(f, "tried to call a function with incorrect number of arguments")
463 ReallocatedWrongMemoryKind(ref old, ref new) => {
464 write!(f, "tried to reallocate memory from `{}` to `{}`", old, new)
466 DeallocatedWrongMemoryKind(ref old, ref new) => {
467 write!(f, "tried to deallocate `{}` memory but gave `{}` as the kind", old, new)
470 write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c)
472 AlignmentCheckFailed { required, has } => write!(
474 "tried to access memory with alignment {}, but alignment {} is required",
478 TypeNotPrimitive(ty) => write!(f, "expected primitive type, got {}", ty),
479 PathNotFound(ref path) => write!(f, "cannot find path {:?}", path),
480 IncorrectAllocationInformation(size, size2, align, align2) => write!(
482 "incorrect alloc info: expected size {} and align {}, \
483 got size {} and align {}",
489 InvalidMemoryAccess => write!(f, "tried to access memory through an invalid pointer"),
490 DanglingPointerDeref => write!(f, "dangling pointer was dereferenced"),
491 DoubleFree => write!(f, "tried to deallocate dangling pointer"),
492 InvalidFunctionPointer => {
493 write!(f, "tried to use a function pointer after offsetting it")
495 InvalidBool => write!(f, "invalid boolean value read"),
496 InvalidNullPointerUsage => write!(f, "invalid use of NULL pointer"),
497 ReadPointerAsBytes => write!(
499 "a raw memory access tried to access part of a pointer value as raw \
502 ReadBytesAsPointer => {
503 write!(f, "a memory access tried to interpret some bytes as a pointer")
505 ReadForeignStatic => write!(f, "tried to read from foreign (extern) static"),
506 InvalidPointerMath => write!(
508 "attempted to do invalid arithmetic on pointers that would leak base \
509 addresses, e.g., comparing pointers into different allocations"
511 DeadLocal => write!(f, "tried to access a dead local variable"),
512 DerefFunctionPointer => write!(f, "tried to dereference a function pointer"),
513 ExecuteMemory => write!(f, "tried to treat a memory pointer as a function pointer"),
514 OutOfTls => write!(f, "reached the maximum number of representable TLS keys"),
515 TlsOutOfBounds => write!(f, "accessed an invalid (unallocated) TLS key"),
516 CalledClosureAsFunction => {
517 write!(f, "tried to call a closure through a function pointer")
519 VtableForArgumentlessMethod => {
520 write!(f, "tried to call a vtable function without arguments")
522 ModifiedConstantMemory => write!(f, "tried to modify constant memory"),
523 ModifiedStatic => write!(
525 "tried to modify a static's initial value from another static's \
528 ReallocateNonBasePtr => write!(
530 "tried to reallocate with a pointer not to the beginning of an \
533 DeallocateNonBasePtr => write!(
535 "tried to deallocate with a pointer not to the beginning of an \
538 HeapAllocZeroBytes => write!(f, "tried to re-, de- or allocate zero bytes on the heap"),
539 ReadFromReturnPointer => write!(f, "tried to read from the return pointer"),
540 UnimplementedTraitSelection => {
541 write!(f, "there were unresolved type arguments during trait selection")
543 InvalidBoolOp(_) => write!(f, "invalid boolean operation"),
544 UnterminatedCString(_) => write!(
546 "attempted to get length of a null-terminated string, but no null \
547 found before end of allocation"
549 ReadUndefBytes(_) => write!(f, "attempted to read undefined bytes"),
550 HeapAllocNonPowerOfTwoAlignment(_) => write!(
552 "tried to re-, de-, or allocate heap memory with alignment that is \
555 Unsupported(ref msg) => write!(f, "{}", msg),
556 ConstPropUnsupported(ref msg) => {
557 write!(f, "Constant propagation encountered an unsupported situation: {}", msg)
563 /// Error information for when the program exhausted the resources granted to it
564 /// by the interpreter.
565 pub enum ResourceExhaustionInfo {
566 /// The stack grew too big.
567 StackFrameLimitReached,
568 /// The program ran into an infinite loop.
572 impl fmt::Debug for ResourceExhaustionInfo {
573 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
574 use ResourceExhaustionInfo::*;
576 StackFrameLimitReached => {
577 write!(f, "reached the configured maximum number of stack frames")
579 InfiniteLoop => write!(
581 "duplicate interpreter state observed here, const evaluation will never \
588 pub enum InterpError<'tcx> {
589 /// The program caused undefined behavior.
590 UndefinedBehavior(UndefinedBehaviorInfo),
591 /// The program did something the interpreter does not support (some of these *might* be UB
592 /// but the interpreter is not sure).
593 Unsupported(UnsupportedOpInfo<'tcx>),
594 /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...).
595 InvalidProgram(InvalidProgramInfo<'tcx>),
596 /// The program exhausted the interpreter's resources (stack/heap too big,
597 /// execution takes too long, ...).
598 ResourceExhaustion(ResourceExhaustionInfo),
599 /// Stop execution for a machine-controlled reason. This is never raised by
600 /// the core engine itself.
601 MachineStop(Box<dyn Any + Send>),
604 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
606 impl fmt::Display for InterpError<'_> {
607 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
608 // Forward `Display` to `Debug`.
609 write!(f, "{:?}", self)
613 impl fmt::Debug for InterpError<'_> {
614 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
617 Unsupported(ref msg) => write!(f, "{:?}", msg),
618 InvalidProgram(ref msg) => write!(f, "{:?}", msg),
619 UndefinedBehavior(ref msg) => write!(f, "{:?}", msg),
620 ResourceExhaustion(ref msg) => write!(f, "{:?}", msg),
621 MachineStop(_) => bug!("unhandled MachineStop"),
626 impl InterpError<'_> {
627 /// Some errors allocate to be created as they contain free-form strings.
628 /// And sometimes we want to be sure that did not happen as it is a
629 /// waste of resources.
630 pub fn allocates(&self) -> bool {
632 InterpError::MachineStop(_)
633 | InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
634 | InterpError::Unsupported(UnsupportedOpInfo::ValidationFailure(_))
635 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
636 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::UbExperimental(_)) => true,