1 use super::{RawConst, Pointer, CheckInAllocMsg, ScalarMaybeUndef};
4 use crate::hir::map::definitions::DefPathData;
6 use crate::ty::{self, Ty, layout};
7 use crate::ty::layout::{Size, Align, LayoutError};
8 use crate::ty::query::TyCtxtAt;
10 use backtrace::Backtrace;
11 use errors::DiagnosticBuilder;
12 use rustc_macros::HashStable;
13 use rustc_target::spec::abi::Abi;
14 use syntax_pos::{Pos, Span};
15 use syntax::symbol::Symbol;
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!("MIR interpretation failed without reporting an error \
33 even though it was fully monomorphized"),
38 CloneTypeFoldableImpls! {
42 pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
43 pub type ConstEvalResult<'tcx> = Result<&'tcx ty::Const<'tcx>, ErrorHandled>;
45 #[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
46 pub struct ConstEvalErr<'tcx> {
48 pub error: crate::mir::interpret::InterpError<'tcx>,
49 pub stacktrace: Vec<FrameInfo<'tcx>>,
52 #[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
53 pub struct FrameInfo<'tcx> {
54 /// This span is in the caller.
56 pub instance: ty::Instance<'tcx>,
57 pub lint_root: Option<hir::HirId>,
60 impl<'tcx> fmt::Display for FrameInfo<'tcx> {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 if tcx.def_key(self.instance.def_id()).disambiguated_data.data
64 == DefPathData::ClosureExpr
66 write!(f, "inside call to closure")?;
68 write!(f, "inside call to `{}`", self.instance)?;
70 if !self.call_site.is_dummy() {
71 let lo = tcx.sess.source_map().lookup_char_pos(self.call_site.lo());
72 write!(f, " at {}:{}:{}", lo.file.name, lo.line, lo.col.to_usize() + 1)?;
79 impl<'tcx> ConstEvalErr<'tcx> {
84 ) -> Result<DiagnosticBuilder<'tcx>, ErrorHandled> {
85 self.struct_generic(tcx, message, None)
88 pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
89 let err = self.struct_error(tcx, message);
93 ErrorHandled::Reported
99 pub fn report_as_lint(
103 lint_root: hir::HirId,
106 let lint = self.struct_generic(
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 err_inval!(Layout(LayoutError::Unknown(_))) |
140 err_inval!(TooGeneric) =>
141 return Err(ErrorHandled::TooGeneric),
142 err_inval!(TypeckError) =>
143 return Err(ErrorHandled::Reported),
144 err_inval!(Layout(LayoutError::SizeOverflow(_))) => true,
147 trace!("reporting const eval failure at {:?}", self.span);
148 let mut err = if let (Some(lint_root), false) = (lint_root, must_error) {
149 let hir_id = self.stacktrace
152 .filter_map(|frame| frame.lint_root)
154 .unwrap_or(lint_root);
155 tcx.struct_span_lint_hir(
156 crate::rustc::lint::builtin::CONST_ERR,
161 } else if must_error {
162 struct_error(tcx, &self.error.to_string())
164 struct_error(tcx, message)
167 err.span_label(self.span, self.error.to_string());
169 // Skip the last, which is just the environment of the constant. The stacktrace
170 // is sometimes empty because we create "fake" eval contexts in CTFE to do work
171 // on constant values.
172 if self.stacktrace.len() > 0 {
173 for frame_info in &self.stacktrace[..self.stacktrace.len()-1] {
174 err.span_label(frame_info.call_site, frame_info.to_string());
181 pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
182 struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
185 /// Packages the kind of error we got from the const code interpreter
186 /// up with a Rust-level backtrace of where the error occured.
187 /// Thsese should always be constructed by calling `.into()` on
188 /// a `InterpError`. In `librustc_mir::interpret`, we have `throw_err_*`
190 #[derive(Debug, Clone)]
191 pub struct InterpErrorInfo<'tcx> {
192 pub kind: InterpError<'tcx>,
193 backtrace: Option<Box<Backtrace>>,
197 impl fmt::Display for InterpErrorInfo<'_> {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 write!(f, "{}", self.kind)
203 impl InterpErrorInfo<'_> {
204 pub fn print_backtrace(&mut self) {
205 if let Some(ref mut backtrace) = self.backtrace {
206 print_backtrace(&mut *backtrace);
211 fn print_backtrace(backtrace: &mut Backtrace) {
213 eprintln!("\n\nAn error occurred in miri:\n{:?}", backtrace);
216 impl From<ErrorHandled> for InterpErrorInfo<'tcx> {
217 fn from(err: ErrorHandled) -> Self {
219 ErrorHandled::Reported => err_inval!(ReferencedConstant),
220 ErrorHandled::TooGeneric => err_inval!(TooGeneric),
225 impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
226 fn from(kind: InterpError<'tcx>) -> Self {
227 let backtrace = match env::var("RUSTC_CTFE_BACKTRACE") {
228 // Matching `RUST_BACKTRACE` -- we treat "0" the same as "not present".
229 Ok(ref val) if val != "0" => {
230 let mut backtrace = Backtrace::new_unresolved();
232 if val == "immediate" {
234 print_backtrace(&mut backtrace);
237 Some(Box::new(backtrace))
249 #[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
250 pub enum PanicInfo<O> {
261 Overflow(mir::BinOp),
265 GeneratorResumedAfterReturn,
266 GeneratorResumedAfterPanic,
269 /// Type for MIR `Assert` terminator error messages.
270 pub type AssertMessage<'tcx> = PanicInfo<mir::Operand<'tcx>>;
272 impl<O> PanicInfo<O> {
273 /// Getting a description does not require `O` to be printable, and does not
274 /// require allocation.
275 /// The caller is expected to handle `Panic` and `BoundsCheck` separately.
276 pub fn description(&self) -> &'static str {
279 Overflow(mir::BinOp::Add) =>
280 "attempt to add with overflow",
281 Overflow(mir::BinOp::Sub) =>
282 "attempt to subtract with overflow",
283 Overflow(mir::BinOp::Mul) =>
284 "attempt to multiply with overflow",
285 Overflow(mir::BinOp::Div) =>
286 "attempt to divide with overflow",
287 Overflow(mir::BinOp::Rem) =>
288 "attempt to calculate the remainder with overflow",
290 "attempt to negate with overflow",
291 Overflow(mir::BinOp::Shr) =>
292 "attempt to shift right with overflow",
293 Overflow(mir::BinOp::Shl) =>
294 "attempt to shift left with overflow",
296 bug!("{:?} cannot overflow", op),
298 "attempt to divide by zero",
300 "attempt to calculate the remainder with a divisor of zero",
301 GeneratorResumedAfterReturn =>
302 "generator resumed after completion",
303 GeneratorResumedAfterPanic =>
304 "generator resumed after panicking",
305 Panic { .. } | BoundsCheck { .. } =>
306 bug!("Unexpected PanicInfo"),
311 impl<O: fmt::Debug> fmt::Debug for PanicInfo<O> {
312 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
315 Panic { ref msg, line, col, ref file } =>
316 write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col),
317 BoundsCheck { ref len, ref index } =>
318 write!(f, "index out of bounds: the len is {:?} but the index is {:?}", len, index),
320 write!(f, "{}", self.description()),
325 /// Error information for when the program we executed turned out not to actually be a valid
326 /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
327 /// where we work on generic code or execution does not have all information available.
328 #[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
329 pub enum InvalidProgramInfo<'tcx> {
330 /// Resolution can fail if we are in a too generic context.
332 /// Cannot compute this constant because it depends on another one
333 /// which already produced an error.
335 /// Abort in case type errors are reached.
337 /// An error occurred during layout computation.
338 Layout(layout::LayoutError<'tcx>),
341 impl fmt::Debug for InvalidProgramInfo<'tcx> {
342 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343 use InvalidProgramInfo::*;
346 write!(f, "encountered overly generic constant"),
347 ReferencedConstant =>
348 write!(f, "referenced constant has errors"),
350 write!(f, "encountered constants with type errors, stopping evaluation"),
352 write!(f, "{}", err),
357 /// Error information for when the program caused Undefined Behavior.
358 #[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
359 pub enum UndefinedBehaviorInfo {
360 /// Free-form case. Only for errors that are never caught!
362 /// Free-form case for experimental UB. Only for errors that are never caught!
363 UbExperimental(String),
364 /// Unreachable code was executed.
368 impl fmt::Debug for UndefinedBehaviorInfo {
369 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370 use UndefinedBehaviorInfo::*;
372 Ub(msg) | UbExperimental(msg) =>
373 write!(f, "{}", msg),
375 write!(f, "entered unreachable code"),
380 /// Error information for when the program did something that might (or might not) be correct
381 /// to do according to the Rust spec, but due to limitations in the interpreter, the
382 /// operation could not be carried out. These limitations can differ between CTFE and the
383 /// Miri engine, e.g., CTFE does not support casting pointers to "real" integers.
385 /// Currently, we also use this as fall-back error kind for errors that have not been
387 #[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
388 pub enum UnsupportedOpInfo<'tcx> {
389 /// Free-form case. Only for errors that are never caught!
392 /// FIXME(#64506) Error used to work around accessing projections of
393 /// uninhabited types.
396 // -- Everything below is not categorized yet --
397 FunctionAbiMismatch(Abi, Abi),
398 FunctionArgMismatch(Ty<'tcx>, Ty<'tcx>),
399 FunctionRetMismatch(Ty<'tcx>, Ty<'tcx>),
400 FunctionArgCountMismatch,
401 UnterminatedCString(Pointer),
402 DanglingPointerDeref,
405 InvalidFunctionPointer,
407 InvalidDiscriminant(ScalarMaybeUndef),
410 msg: CheckInAllocMsg,
411 allocation_size: Size,
413 InvalidNullPointerUsage,
418 ReadUndefBytes(Size),
420 InvalidBoolOp(mir::BinOp),
421 UnimplementedTraitSelection,
422 CalledClosureAsFunction,
424 DerefFunctionPointer,
429 AlignmentCheckFailed {
433 ValidationFailure(String),
434 VtableForArgumentlessMethod,
435 ModifiedConstantMemory,
437 TypeNotPrimitive(Ty<'tcx>),
438 ReallocatedWrongMemoryKind(String, String),
439 DeallocatedWrongMemoryKind(String, String),
440 ReallocateNonBasePtr,
441 DeallocateNonBasePtr,
442 IncorrectAllocationInformation(Size, Size, Align, Align),
444 HeapAllocNonPowerOfTwoAlignment(u64),
445 ReadFromReturnPointer,
446 PathNotFound(Vec<String>),
449 impl fmt::Debug for UnsupportedOpInfo<'tcx> {
450 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
451 use UnsupportedOpInfo::*;
453 PointerOutOfBounds { ptr, msg, allocation_size } => {
454 write!(f, "{} failed: pointer must be in-bounds at offset {}, \
455 but is outside bounds of allocation {} which has size {}",
456 msg, ptr.offset.bytes(), ptr.alloc_id, allocation_size.bytes())
458 ValidationFailure(ref err) => {
459 write!(f, "type validation failed: {}", err)
461 NoMirFor(ref func) => write!(f, "no MIR for `{}`", func),
462 FunctionAbiMismatch(caller_abi, callee_abi) =>
463 write!(f, "tried to call a function with ABI {:?} using caller ABI {:?}",
464 callee_abi, caller_abi),
465 FunctionArgMismatch(caller_ty, callee_ty) =>
466 write!(f, "tried to call a function with argument of type {:?} \
467 passing data of type {:?}",
468 callee_ty, caller_ty),
469 FunctionRetMismatch(caller_ty, callee_ty) =>
470 write!(f, "tried to call a function with return type {:?} \
471 passing return place of type {:?}",
472 callee_ty, caller_ty),
473 FunctionArgCountMismatch =>
474 write!(f, "tried to call a function with incorrect number of arguments"),
475 ReallocatedWrongMemoryKind(ref old, ref new) =>
476 write!(f, "tried to reallocate memory from `{}` to `{}`", old, new),
477 DeallocatedWrongMemoryKind(ref old, ref new) =>
478 write!(f, "tried to deallocate `{}` memory but gave `{}` as the kind", old, new),
480 write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c),
481 AlignmentCheckFailed { required, has } =>
482 write!(f, "tried to access memory with alignment {}, but alignment {} is required",
483 has.bytes(), required.bytes()),
484 TypeNotPrimitive(ty) =>
485 write!(f, "expected primitive type, got {}", ty),
486 PathNotFound(ref path) =>
487 write!(f, "cannot find path {:?}", path),
488 IncorrectAllocationInformation(size, size2, align, align2) =>
489 write!(f, "incorrect alloc info: expected size {} and align {}, \
490 got size {} and align {}",
491 size.bytes(), align.bytes(), size2.bytes(), align2.bytes()),
492 InvalidDiscriminant(val) =>
493 write!(f, "encountered invalid enum discriminant {}", val),
494 InvalidMemoryAccess =>
495 write!(f, "tried to access memory through an invalid pointer"),
496 DanglingPointerDeref =>
497 write!(f, "dangling pointer was dereferenced"),
499 write!(f, "tried to deallocate dangling pointer"),
500 InvalidFunctionPointer =>
501 write!(f, "tried to use a function pointer after offsetting it"),
503 write!(f, "invalid boolean value read"),
504 InvalidNullPointerUsage =>
505 write!(f, "invalid use of NULL pointer"),
506 ReadPointerAsBytes =>
507 write!(f, "a raw memory access tried to access part of a pointer value as raw \
509 ReadBytesAsPointer =>
510 write!(f, "a memory access tried to interpret some bytes as a pointer"),
512 write!(f, "tried to read from foreign (extern) static"),
513 InvalidPointerMath =>
514 write!(f, "attempted to do invalid arithmetic on pointers that would leak base \
515 addresses, e.g., comparing pointers into different allocations"),
517 write!(f, "tried to access a dead local variable"),
518 DerefFunctionPointer =>
519 write!(f, "tried to dereference a function pointer"),
521 write!(f, "tried to treat a memory pointer as a function pointer"),
523 write!(f, "reached the maximum number of representable TLS keys"),
525 write!(f, "accessed an invalid (unallocated) TLS key"),
526 CalledClosureAsFunction =>
527 write!(f, "tried to call a closure through a function pointer"),
528 VtableForArgumentlessMethod =>
529 write!(f, "tried to call a vtable function without arguments"),
530 ModifiedConstantMemory =>
531 write!(f, "tried to modify constant memory"),
533 write!(f, "tried to modify a static's initial value from another static's \
535 ReallocateNonBasePtr =>
536 write!(f, "tried to reallocate with a pointer not to the beginning of an \
538 DeallocateNonBasePtr =>
539 write!(f, "tried to deallocate with a pointer not to the beginning of an \
541 HeapAllocZeroBytes =>
542 write!(f, "tried to re-, de- or allocate zero bytes on the heap"),
543 ReadFromReturnPointer =>
544 write!(f, "tried to read from the return pointer"),
545 UnimplementedTraitSelection =>
546 write!(f, "there were unresolved type arguments during trait selection"),
548 write!(f, "invalid boolean operation"),
549 UnterminatedCString(_) =>
550 write!(f, "attempted to get length of a null-terminated string, but no null \
551 found before end of allocation"),
553 write!(f, "attempted to read undefined bytes"),
554 HeapAllocNonPowerOfTwoAlignment(_) =>
555 write!(f, "tried to re-, de-, or allocate heap memory with alignment that is \
556 not a power of two"),
557 Unsupported(ref msg) =>
558 write!(f, "{}", msg),
560 write!(f, "tried to use an uninhabited value"),
565 /// Error information for when the program exhausted the resources granted to it
566 /// by the interpreter.
567 #[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
568 pub enum ResourceExhaustionInfo {
569 /// The stack grew too big.
570 StackFrameLimitReached,
571 /// The program ran into an infinite loop.
575 impl fmt::Debug for ResourceExhaustionInfo {
576 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
577 use ResourceExhaustionInfo::*;
579 StackFrameLimitReached =>
580 write!(f, "reached the configured maximum number of stack frames"),
582 write!(f, "duplicate interpreter state observed here, const evaluation will never \
588 #[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
589 pub enum InterpError<'tcx> {
590 /// The program panicked.
591 Panic(PanicInfo<u64>),
592 /// The program caused undefined behavior.
593 UndefinedBehavior(UndefinedBehaviorInfo),
594 /// The program did something the interpreter does not support (some of these *might* be UB
595 /// but the interpreter is not sure).
596 Unsupported(UnsupportedOpInfo<'tcx>),
597 /// The program was invalid (ill-typed, not sufficiently monomorphized, ...).
598 InvalidProgram(InvalidProgramInfo<'tcx>),
599 /// The program exhausted the interpreter's resources (stack/heap too big,
600 /// execution takes too long, ..).
601 ResourceExhaustion(ResourceExhaustionInfo),
602 /// Not actually an interpreter error -- used to signal that execution has exited
603 /// with the given status code. Used by Miri, but not by CTFE.
607 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
609 impl fmt::Display for InterpError<'_> {
610 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
611 // Forward `Display` to `Debug`.
612 write!(f, "{:?}", self)
616 impl fmt::Debug for InterpError<'_> {
617 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
620 Unsupported(ref msg) =>
621 write!(f, "{:?}", msg),
622 InvalidProgram(ref msg) =>
623 write!(f, "{:?}", msg),
624 UndefinedBehavior(ref msg) =>
625 write!(f, "{:?}", msg),
626 ResourceExhaustion(ref msg) =>
627 write!(f, "{:?}", msg),
629 write!(f, "{:?}", msg),
631 write!(f, "exited with status code {}", code),