1 use super::{CheckInAllocMsg, Pointer, RawConst, ScalarMaybeUndef};
3 use crate::hir::map::definitions::DefPathData;
5 use crate::ty::layout::{Align, LayoutError, Size};
6 use crate::ty::query::TyCtxtAt;
7 use crate::ty::{self, layout, Ty};
9 use backtrace::Backtrace;
10 use hir::GeneratorKind;
11 use rustc_errors::{struct_span_err, DiagnosticBuilder};
13 use rustc_macros::HashStable;
14 use rustc_span::symbol::Symbol;
15 use rustc_span::{Pos, Span};
16 use rustc_target::spec::abi::Abi;
17 use std::{any::Any, env, fmt};
19 use rustc_error_codes::*;
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<&'tcx ty::Const<'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 ) -> Result<DiagnosticBuilder<'tcx>, ErrorHandled> {
89 self.struct_generic(tcx, message, None)
92 pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
93 let err = self.struct_error(tcx, message);
97 ErrorHandled::Reported
103 pub fn report_as_lint(
107 lint_root: hir::HirId,
110 let lint = self.struct_generic(tcx, message, Some(lint_root));
113 if let Some(span) = span {
114 let primary_spans = lint.span.primary_spans().to_vec();
115 // point at the actual error as the primary span
116 lint.replace_span_with(span);
117 // point to the `const` statement as a secondary span
118 // they don't have any label
119 for sp in primary_spans {
121 lint.span_label(sp, "");
126 ErrorHandled::Reported
136 lint_root: Option<hir::HirId>,
137 ) -> Result<DiagnosticBuilder<'tcx>, ErrorHandled> {
138 let must_error = match self.error {
139 InterpError::MachineStop(_) => bug!("CTFE does not stop"),
140 err_inval!(Layout(LayoutError::Unknown(_)))
141 | err_inval!(TooGeneric) => {
142 return Err(ErrorHandled::TooGeneric);
144 err_inval!(TypeckError) => return Err(ErrorHandled::Reported),
145 err_inval!(Layout(LayoutError::SizeOverflow(_))) => true,
148 trace!("reporting const eval failure at {:?}", self.span);
149 let mut err = if let (Some(lint_root), false) = (lint_root, must_error) {
154 .filter_map(|frame| frame.lint_root)
156 .unwrap_or(lint_root);
157 tcx.struct_span_lint_hir(
158 rustc_session::lint::builtin::CONST_ERR,
163 } else if must_error {
164 struct_error(tcx, &self.error.to_string())
166 struct_error(tcx, message)
169 err.span_label(self.span, self.error.to_string());
171 // Skip the last, which is just the environment of the constant. The stacktrace
172 // is sometimes empty because we create "fake" eval contexts in CTFE to do work
173 // on constant values.
174 if self.stacktrace.len() > 0 {
175 for frame_info in &self.stacktrace[..self.stacktrace.len() - 1] {
176 err.span_label(frame_info.call_site, frame_info.to_string());
183 pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
184 struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
187 /// Packages the kind of error we got from the const code interpreter
188 /// up with a Rust-level backtrace of where the error occurred.
189 /// Thsese should always be constructed by calling `.into()` on
190 /// a `InterpError`. In `librustc_mir::interpret`, we have `throw_err_*`
193 pub struct InterpErrorInfo<'tcx> {
194 pub kind: InterpError<'tcx>,
195 backtrace: Option<Box<Backtrace>>,
198 impl fmt::Display for InterpErrorInfo<'_> {
199 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200 write!(f, "{}", self.kind)
204 impl InterpErrorInfo<'_> {
205 pub fn print_backtrace(&mut self) {
206 if let Some(ref mut backtrace) = self.backtrace {
207 print_backtrace(&mut *backtrace);
212 fn print_backtrace(backtrace: &mut Backtrace) {
214 eprintln!("\n\nAn error occurred in miri:\n{:?}", backtrace);
217 impl From<ErrorHandled> for InterpErrorInfo<'tcx> {
218 fn from(err: ErrorHandled) -> Self {
220 ErrorHandled::Reported => err_inval!(ReferencedConstant),
221 ErrorHandled::TooGeneric => err_inval!(TooGeneric),
227 impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
228 fn from(kind: InterpError<'tcx>) -> Self {
229 let backtrace = match env::var("RUSTC_CTFE_BACKTRACE") {
230 // Matching `RUST_BACKTRACE` -- we treat "0" the same as "not present".
231 Ok(ref val) if val != "0" => {
232 let mut backtrace = Backtrace::new_unresolved();
234 if val == "immediate" {
236 print_backtrace(&mut backtrace);
239 Some(Box::new(backtrace))
244 InterpErrorInfo { kind, backtrace }
248 #[derive(Clone, RustcEncodable, RustcDecodable, HashStable, PartialEq)]
249 pub enum PanicInfo<O> {
250 Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
251 BoundsCheck { len: O, index: O },
252 Overflow(mir::BinOp),
256 ResumedAfterReturn(GeneratorKind),
257 ResumedAfterPanic(GeneratorKind),
260 /// Type for MIR `Assert` terminator error messages.
261 pub type AssertMessage<'tcx> = PanicInfo<mir::Operand<'tcx>>;
263 impl<O> PanicInfo<O> {
264 /// Getting a description does not require `O` to be printable, and does not
265 /// require allocation.
266 /// The caller is expected to handle `Panic` and `BoundsCheck` separately.
267 pub fn description(&self) -> &'static str {
270 Overflow(mir::BinOp::Add) => "attempt to add with overflow",
271 Overflow(mir::BinOp::Sub) => "attempt to subtract with overflow",
272 Overflow(mir::BinOp::Mul) => "attempt to multiply with overflow",
273 Overflow(mir::BinOp::Div) => "attempt to divide with overflow",
274 Overflow(mir::BinOp::Rem) => "attempt to calculate the remainder with overflow",
275 OverflowNeg => "attempt to negate with overflow",
276 Overflow(mir::BinOp::Shr) => "attempt to shift right with overflow",
277 Overflow(mir::BinOp::Shl) => "attempt to shift left with overflow",
278 Overflow(op) => bug!("{:?} cannot overflow", op),
279 DivisionByZero => "attempt to divide by zero",
280 RemainderByZero => "attempt to calculate the remainder with a divisor of zero",
281 ResumedAfterReturn(GeneratorKind::Gen) => "generator resumed after completion",
282 ResumedAfterReturn(GeneratorKind::Async(_)) => "`async fn` resumed after completion",
283 ResumedAfterPanic(GeneratorKind::Gen) => "generator resumed after panicking",
284 ResumedAfterPanic(GeneratorKind::Async(_)) => "`async fn` resumed after panicking",
285 Panic { .. } | BoundsCheck { .. } => bug!("Unexpected PanicInfo"),
290 impl<O: fmt::Debug> fmt::Debug for PanicInfo<O> {
291 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
294 Panic { ref msg, line, col, ref file } => {
295 write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col)
297 BoundsCheck { ref len, ref index } => {
298 write!(f, "index out of bounds: the len is {:?} but the index is {:?}", len, index)
300 _ => write!(f, "{}", self.description()),
305 /// Error information for when the program we executed turned out not to actually be a valid
306 /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
307 /// where we work on generic code or execution does not have all information available.
308 pub enum InvalidProgramInfo<'tcx> {
309 /// Resolution can fail if we are in a too generic context.
311 /// Cannot compute this constant because it depends on another one
312 /// which already produced an error.
314 /// Abort in case type errors are reached.
316 /// An error occurred during layout computation.
317 Layout(layout::LayoutError<'tcx>),
320 impl fmt::Debug for InvalidProgramInfo<'tcx> {
321 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
322 use InvalidProgramInfo::*;
324 TooGeneric => write!(f, "encountered overly generic constant"),
325 ReferencedConstant => write!(f, "referenced constant has errors"),
326 TypeckError => write!(f, "encountered constants with type errors, stopping evaluation"),
327 Layout(ref err) => write!(f, "{}", err),
332 /// Error information for when the program caused Undefined Behavior.
333 pub enum UndefinedBehaviorInfo {
334 /// Free-form case. Only for errors that are never caught!
336 /// Free-form case for experimental UB. Only for errors that are never caught!
337 UbExperimental(String),
338 /// Unreachable code was executed.
340 /// An enum discriminant was set to a value which was outside the range of valid values.
341 InvalidDiscriminant(ScalarMaybeUndef),
342 /// A slice/array index projection went out-of-bounds.
343 BoundsCheckFailed { len: u64, index: u64 },
344 /// Something was divided by 0 (x / 0).
346 /// Something was "remainded" by 0 (x % 0).
348 /// Overflowing inbounds pointer arithmetic.
349 PointerArithOverflow,
352 impl fmt::Debug for UndefinedBehaviorInfo {
353 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354 use UndefinedBehaviorInfo::*;
356 Ub(msg) | UbExperimental(msg) => write!(f, "{}", msg),
357 Unreachable => write!(f, "entering unreachable code"),
358 InvalidDiscriminant(val) => write!(f, "encountering invalid enum discriminant {}", val),
359 BoundsCheckFailed { ref len, ref index } => write!(
361 "indexing out of bounds: the len is {:?} but the index is {:?}",
364 DivisionByZero => write!(f, "dividing by zero"),
365 RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
366 PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
371 /// Error information for when the program did something that might (or might not) be correct
372 /// to do according to the Rust spec, but due to limitations in the interpreter, the
373 /// operation could not be carried out. These limitations can differ between CTFE and the
374 /// Miri engine, e.g., CTFE does not support casting pointers to "real" integers.
376 /// Currently, we also use this as fall-back error kind for errors that have not been
378 pub enum UnsupportedOpInfo<'tcx> {
379 /// Free-form case. Only for errors that are never caught!
382 /// When const-prop encounters a situation it does not support, it raises this error.
383 /// This must not allocate for performance reasons.
384 ConstPropUnsupported(&'tcx str),
386 // -- Everything below is not categorized yet --
387 FunctionAbiMismatch(Abi, Abi),
388 FunctionArgMismatch(Ty<'tcx>, Ty<'tcx>),
389 FunctionRetMismatch(Ty<'tcx>, Ty<'tcx>),
390 FunctionArgCountMismatch,
391 UnterminatedCString(Pointer),
392 DanglingPointerDeref,
395 InvalidFunctionPointer,
399 msg: CheckInAllocMsg,
400 allocation_size: Size,
402 InvalidNullPointerUsage,
407 ReadUndefBytes(Size),
409 InvalidBoolOp(mir::BinOp),
410 UnimplementedTraitSelection,
411 CalledClosureAsFunction,
413 DerefFunctionPointer,
418 AlignmentCheckFailed {
422 ValidationFailure(String),
423 VtableForArgumentlessMethod,
424 ModifiedConstantMemory,
426 TypeNotPrimitive(Ty<'tcx>),
427 ReallocatedWrongMemoryKind(String, String),
428 DeallocatedWrongMemoryKind(String, String),
429 ReallocateNonBasePtr,
430 DeallocateNonBasePtr,
431 IncorrectAllocationInformation(Size, Size, Align, Align),
433 HeapAllocNonPowerOfTwoAlignment(u64),
434 ReadFromReturnPointer,
435 PathNotFound(Vec<String>),
436 TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>),
439 impl fmt::Debug for UnsupportedOpInfo<'tcx> {
440 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
441 use UnsupportedOpInfo::*;
443 PointerOutOfBounds { ptr, msg, allocation_size } => write!(
445 "{} failed: pointer must be in-bounds at offset {}, \
446 but is outside bounds of allocation {} which has size {}",
450 allocation_size.bytes()
452 ValidationFailure(ref err) => write!(f, "type validation failed: {}", err),
453 NoMirFor(ref func) => write!(f, "no MIR for `{}`", func),
454 FunctionAbiMismatch(caller_abi, callee_abi) => write!(
456 "tried to call a function with ABI {:?} using caller ABI {:?}",
457 callee_abi, caller_abi
459 FunctionArgMismatch(caller_ty, callee_ty) => write!(
461 "tried to call a function with argument of type {:?} \
462 passing data of type {:?}",
465 TransmuteSizeDiff(from_ty, to_ty) => write!(
467 "tried to transmute from {:?} to {:?}, but their sizes differed",
470 FunctionRetMismatch(caller_ty, callee_ty) => write!(
472 "tried to call a function with return type {:?} \
473 passing return place of type {:?}",
476 FunctionArgCountMismatch => {
477 write!(f, "tried to call a function with incorrect number of arguments")
479 ReallocatedWrongMemoryKind(ref old, ref new) => {
480 write!(f, "tried to reallocate memory from `{}` to `{}`", old, new)
482 DeallocatedWrongMemoryKind(ref old, ref new) => {
483 write!(f, "tried to deallocate `{}` memory but gave `{}` as the kind", old, new)
486 write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c)
488 AlignmentCheckFailed { required, has } => write!(
490 "tried to access memory with alignment {}, but alignment {} is required",
494 TypeNotPrimitive(ty) => write!(f, "expected primitive type, got {}", ty),
495 PathNotFound(ref path) => write!(f, "cannot find path {:?}", path),
496 IncorrectAllocationInformation(size, size2, align, align2) => write!(
498 "incorrect alloc info: expected size {} and align {}, \
499 got size {} and align {}",
505 InvalidMemoryAccess => write!(f, "tried to access memory through an invalid pointer"),
506 DanglingPointerDeref => write!(f, "dangling pointer was dereferenced"),
507 DoubleFree => write!(f, "tried to deallocate dangling pointer"),
508 InvalidFunctionPointer => {
509 write!(f, "tried to use a function pointer after offsetting it")
511 InvalidBool => write!(f, "invalid boolean value read"),
512 InvalidNullPointerUsage => write!(f, "invalid use of NULL pointer"),
513 ReadPointerAsBytes => write!(
515 "a raw memory access tried to access part of a pointer value as raw \
518 ReadBytesAsPointer => {
519 write!(f, "a memory access tried to interpret some bytes as a pointer")
521 ReadForeignStatic => write!(f, "tried to read from foreign (extern) static"),
522 InvalidPointerMath => write!(
524 "attempted to do invalid arithmetic on pointers that would leak base \
525 addresses, e.g., comparing pointers into different allocations"
527 DeadLocal => write!(f, "tried to access a dead local variable"),
528 DerefFunctionPointer => write!(f, "tried to dereference a function pointer"),
529 ExecuteMemory => write!(f, "tried to treat a memory pointer as a function pointer"),
530 OutOfTls => write!(f, "reached the maximum number of representable TLS keys"),
531 TlsOutOfBounds => write!(f, "accessed an invalid (unallocated) TLS key"),
532 CalledClosureAsFunction => {
533 write!(f, "tried to call a closure through a function pointer")
535 VtableForArgumentlessMethod => {
536 write!(f, "tried to call a vtable function without arguments")
538 ModifiedConstantMemory => write!(f, "tried to modify constant memory"),
539 ModifiedStatic => write!(
541 "tried to modify a static's initial value from another static's \
544 ReallocateNonBasePtr => write!(
546 "tried to reallocate with a pointer not to the beginning of an \
549 DeallocateNonBasePtr => write!(
551 "tried to deallocate with a pointer not to the beginning of an \
554 HeapAllocZeroBytes => write!(f, "tried to re-, de- or allocate zero bytes on the heap"),
555 ReadFromReturnPointer => write!(f, "tried to read from the return pointer"),
556 UnimplementedTraitSelection => {
557 write!(f, "there were unresolved type arguments during trait selection")
559 InvalidBoolOp(_) => write!(f, "invalid boolean operation"),
560 UnterminatedCString(_) => write!(
562 "attempted to get length of a null-terminated string, but no null \
563 found before end of allocation"
565 ReadUndefBytes(_) => write!(f, "attempted to read undefined bytes"),
566 HeapAllocNonPowerOfTwoAlignment(_) => write!(
568 "tried to re-, de-, or allocate heap memory with alignment that is \
571 Unsupported(ref msg) => write!(f, "{}", msg),
572 ConstPropUnsupported(ref msg) => {
573 write!(f, "Constant propagation encountered an unsupported situation: {}", msg)
579 /// Error information for when the program exhausted the resources granted to it
580 /// by the interpreter.
581 pub enum ResourceExhaustionInfo {
582 /// The stack grew too big.
583 StackFrameLimitReached,
584 /// The program ran into an infinite loop.
588 impl fmt::Debug for ResourceExhaustionInfo {
589 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
590 use ResourceExhaustionInfo::*;
592 StackFrameLimitReached => {
593 write!(f, "reached the configured maximum number of stack frames")
595 InfiniteLoop => write!(
597 "duplicate interpreter state observed here, const evaluation will never \
604 pub enum InterpError<'tcx> {
605 /// The program panicked.
606 Panic(PanicInfo<u64>),
607 /// The program caused undefined behavior.
608 UndefinedBehavior(UndefinedBehaviorInfo),
609 /// The program did something the interpreter does not support (some of these *might* be UB
610 /// but the interpreter is not sure).
611 Unsupported(UnsupportedOpInfo<'tcx>),
612 /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...).
613 InvalidProgram(InvalidProgramInfo<'tcx>),
614 /// The program exhausted the interpreter's resources (stack/heap too big,
615 /// execution takes too long, ...).
616 ResourceExhaustion(ResourceExhaustionInfo),
617 /// Stop execution for a machine-controlled reason. This is never raised by
618 /// the core engine itself.
619 MachineStop(Box<dyn Any + Send>),
622 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
624 impl fmt::Display for InterpError<'_> {
625 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
626 // Forward `Display` to `Debug`.
627 write!(f, "{:?}", self)
631 impl fmt::Debug for InterpError<'_> {
632 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
635 Unsupported(ref msg) => write!(f, "{:?}", msg),
636 InvalidProgram(ref msg) => write!(f, "{:?}", msg),
637 UndefinedBehavior(ref msg) => write!(f, "{:?}", msg),
638 ResourceExhaustion(ref msg) => write!(f, "{:?}", msg),
639 Panic(ref msg) => write!(f, "{:?}", msg),
640 MachineStop(_) => write!(f, "machine caused execution to stop"),