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;
8 use crate::ty::{self, layout, Ty};
10 use backtrace::Backtrace;
11 use rustc_errors::{struct_span_err, DiagnosticBuilder};
13 use rustc_macros::HashStable;
14 use rustc_span::{Pos, Span};
15 use rustc_target::spec::abi::Abi;
16 use std::{any::Any, env, fmt};
18 #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, RustcEncodable, RustcDecodable)]
19 pub enum ErrorHandled {
20 /// Already reported a lint or an error for this evaluation.
22 /// Don't emit an error, the evaluation failed because the MIR was generic
23 /// and the substs didn't fully monomorphize it.
28 pub fn assert_reported(self) {
30 ErrorHandled::Reported => {}
31 ErrorHandled::TooGeneric => bug!(
32 "MIR interpretation failed without reporting an error \
33 even though it was fully monomorphized"
39 CloneTypeFoldableImpls! {
43 pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
44 pub type ConstEvalResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
47 pub struct ConstEvalErr<'tcx> {
49 pub error: crate::mir::interpret::InterpError<'tcx>,
50 pub stacktrace: Vec<FrameInfo<'tcx>>,
54 pub struct FrameInfo<'tcx> {
55 /// This span is in the caller.
57 pub instance: ty::Instance<'tcx>,
58 pub lint_root: Option<hir::HirId>,
61 impl<'tcx> fmt::Display for FrameInfo<'tcx> {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 if tcx.def_key(self.instance.def_id()).disambiguated_data.data
65 == DefPathData::ClosureExpr
67 write!(f, "inside call to closure")?;
69 write!(f, "inside call to `{}`", self.instance)?;
71 if !self.call_site.is_dummy() {
72 let lo = tcx.sess.source_map().lookup_char_pos(self.call_site.lo());
73 write!(f, " at {}:{}:{}", lo.file.name, lo.line, lo.col.to_usize() + 1)?;
80 impl<'tcx> ConstEvalErr<'tcx> {
85 emit: impl FnOnce(DiagnosticBuilder<'_>),
86 ) -> Result<(), ErrorHandled> {
87 self.struct_generic(tcx, message, emit, None)
90 pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
91 match self.struct_error(tcx, message, |mut e| e.emit()) {
92 Ok(_) => ErrorHandled::Reported,
97 pub fn report_as_lint(
101 lint_root: hir::HirId,
104 match self.struct_generic(
107 |mut lint: DiagnosticBuilder<'_>| {
109 if let Some(span) = span {
110 let primary_spans = lint.span.primary_spans().to_vec();
111 // point at the actual error as the primary span
112 lint.replace_span_with(span);
113 // point to the `const` statement as a secondary span
114 // they don't have any label
115 for sp in primary_spans {
117 lint.span_label(sp, "");
125 Ok(_) => ErrorHandled::Reported,
130 /// Create a diagnostic for this const eval error.
132 /// Sets the message passed in via `message` and adds span labels with detailed error
133 /// information before handing control back to `emit` to do any final processing.
134 /// It's the caller's responsibility to call emit(), stash(), etc. within the `emit`
135 /// function to dispose of the diagnostic properly.
137 /// If `lint_root.is_some()` report it as a lint, else report it as a hard error.
138 /// (Except that for some errors, we ignore all that -- see `must_error` below.)
143 emit: impl FnOnce(DiagnosticBuilder<'_>),
144 lint_root: Option<hir::HirId>,
145 ) -> Result<(), ErrorHandled> {
146 let must_error = match self.error {
147 err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
148 return Err(ErrorHandled::TooGeneric);
150 err_inval!(TypeckError) => return Err(ErrorHandled::Reported),
151 // We must *always* hard error on these, even if the caller wants just a lint.
152 err_inval!(Layout(LayoutError::SizeOverflow(_))) => true,
155 trace!("reporting const eval failure at {:?}", self.span);
157 let err_msg = match &self.error {
158 InterpError::MachineStop(msg) => {
159 // A custom error (`ConstEvalErrKind` in `librustc_mir/interp/const_eval/error.rs`).
160 // Should be turned into a string by now.
161 msg.downcast_ref::<String>().expect("invalid MachineStop payload").clone()
163 err => err.to_string(),
166 let finish = |mut err: DiagnosticBuilder<'_>, span_msg: Option<String>| {
167 if let Some(span_msg) = span_msg {
168 err.span_label(self.span, span_msg);
170 // Add spans for the stacktrace.
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.is_empty() {
175 for frame_info in &self.stacktrace[..self.stacktrace.len() - 1] {
176 err.span_label(frame_info.call_site, frame_info.to_string());
179 // Let the caller finish the job.
184 // The `message` makes little sense here, this is a more serious error than the
185 // caller thinks anyway.
186 // See <https://github.com/rust-lang/rust/pull/63152>.
187 finish(struct_error(tcx, &err_msg), None);
190 if let Some(lint_root) = lint_root {
196 .filter_map(|frame| frame.lint_root)
198 .unwrap_or(lint_root);
199 tcx.struct_span_lint_hir(
200 rustc_session::lint::builtin::CONST_ERR,
203 |lint| finish(lint.build(message), Some(err_msg)),
206 // Report as hard error.
207 finish(struct_error(tcx, message), Some(err_msg));
214 pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
215 struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
218 /// Packages the kind of error we got from the const code interpreter
219 /// up with a Rust-level backtrace of where the error occurred.
220 /// Thsese should always be constructed by calling `.into()` on
221 /// a `InterpError`. In `librustc_mir::interpret`, we have `throw_err_*`
224 pub struct InterpErrorInfo<'tcx> {
225 pub kind: InterpError<'tcx>,
226 backtrace: Option<Box<Backtrace>>,
229 impl fmt::Display for InterpErrorInfo<'_> {
230 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231 write!(f, "{}", self.kind)
235 impl InterpErrorInfo<'_> {
236 pub fn print_backtrace(&mut self) {
237 if let Some(ref mut backtrace) = self.backtrace {
238 print_backtrace(&mut *backtrace);
243 fn print_backtrace(backtrace: &mut Backtrace) {
245 eprintln!("\n\nAn error occurred in miri:\n{:?}", backtrace);
248 impl From<ErrorHandled> for InterpErrorInfo<'_> {
249 fn from(err: ErrorHandled) -> Self {
251 ErrorHandled::Reported => err_inval!(ReferencedConstant),
252 ErrorHandled::TooGeneric => err_inval!(TooGeneric),
258 impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
259 fn from(kind: InterpError<'tcx>) -> Self {
260 let backtrace = match env::var("RUSTC_CTFE_BACKTRACE") {
261 // Matching `RUST_BACKTRACE` -- we treat "0" the same as "not present".
262 Ok(ref val) if val != "0" => {
263 let mut backtrace = Backtrace::new_unresolved();
265 if val == "immediate" {
267 print_backtrace(&mut backtrace);
270 Some(Box::new(backtrace))
275 InterpErrorInfo { kind, backtrace }
279 /// Error information for when the program we executed turned out not to actually be a valid
280 /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
281 /// where we work on generic code or execution does not have all information available.
282 pub enum InvalidProgramInfo<'tcx> {
283 /// Resolution can fail if we are in a too generic context.
285 /// Cannot compute this constant because it depends on another one
286 /// which already produced an error.
288 /// Abort in case type errors are reached.
290 /// An error occurred during layout computation.
291 Layout(layout::LayoutError<'tcx>),
294 impl fmt::Debug for InvalidProgramInfo<'_> {
295 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296 use InvalidProgramInfo::*;
298 TooGeneric => write!(f, "encountered overly generic constant"),
299 ReferencedConstant => write!(f, "referenced constant has errors"),
300 TypeckError => write!(f, "encountered constants with type errors, stopping evaluation"),
301 Layout(ref err) => write!(f, "{}", err),
306 /// Error information for when the program caused Undefined Behavior.
307 pub enum UndefinedBehaviorInfo {
308 /// Free-form case. Only for errors that are never caught!
310 /// Free-form case for experimental UB. Only for errors that are never caught!
311 UbExperimental(String),
312 /// Unreachable code was executed.
314 /// An enum discriminant was set to a value which was outside the range of valid values.
315 InvalidDiscriminant(ScalarMaybeUndef),
316 /// A slice/array index projection went out-of-bounds.
317 BoundsCheckFailed { len: u64, index: u64 },
318 /// Something was divided by 0 (x / 0).
320 /// Something was "remainded" by 0 (x % 0).
322 /// Overflowing inbounds pointer arithmetic.
323 PointerArithOverflow,
324 /// Invalid metadata in a wide pointer (using `str` to avoid allocations).
325 InvalidMeta(&'static str),
328 impl fmt::Debug for UndefinedBehaviorInfo {
329 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330 use UndefinedBehaviorInfo::*;
332 Ub(msg) | UbExperimental(msg) => write!(f, "{}", msg),
333 Unreachable => write!(f, "entering unreachable code"),
334 InvalidDiscriminant(val) => write!(f, "encountering invalid enum discriminant {}", val),
335 BoundsCheckFailed { ref len, ref index } => write!(
337 "indexing out of bounds: the len is {:?} but the index is {:?}",
340 DivisionByZero => write!(f, "dividing by zero"),
341 RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
342 PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
343 InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg),
348 /// Error information for when the program did something that might (or might not) be correct
349 /// to do according to the Rust spec, but due to limitations in the interpreter, the
350 /// operation could not be carried out. These limitations can differ between CTFE and the
351 /// Miri engine, e.g., CTFE does not support casting pointers to "real" integers.
353 /// Currently, we also use this as fall-back error kind for errors that have not been
355 pub enum UnsupportedOpInfo<'tcx> {
356 /// Free-form case. Only for errors that are never caught!
359 /// When const-prop encounters a situation it does not support, it raises this error.
360 /// This must not allocate for performance reasons (hence `str`, not `String`).
361 ConstPropUnsupported(&'static str),
363 // -- Everything below is not categorized yet --
364 FunctionAbiMismatch(Abi, Abi),
365 FunctionArgMismatch(Ty<'tcx>, Ty<'tcx>),
366 FunctionRetMismatch(Ty<'tcx>, Ty<'tcx>),
367 FunctionArgCountMismatch,
368 UnterminatedCString(Pointer),
369 DanglingPointerDeref,
372 InvalidFunctionPointer,
376 msg: CheckInAllocMsg,
377 allocation_size: Size,
379 InvalidNullPointerUsage,
384 ReadUndefBytes(Size),
386 InvalidBoolOp(mir::BinOp),
387 UnimplementedTraitSelection,
388 CalledClosureAsFunction,
390 DerefFunctionPointer,
395 AlignmentCheckFailed {
399 ValidationFailure(String),
400 VtableForArgumentlessMethod,
401 ModifiedConstantMemory,
403 TypeNotPrimitive(Ty<'tcx>),
404 ReallocatedWrongMemoryKind(String, String),
405 DeallocatedWrongMemoryKind(String, String),
406 ReallocateNonBasePtr,
407 DeallocateNonBasePtr,
408 IncorrectAllocationInformation(Size, Size, Align, Align),
410 HeapAllocNonPowerOfTwoAlignment(u64),
411 ReadFromReturnPointer,
412 PathNotFound(Vec<String>),
413 TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>),
416 impl fmt::Debug for UnsupportedOpInfo<'tcx> {
417 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
418 use UnsupportedOpInfo::*;
420 PointerOutOfBounds { ptr, msg, allocation_size } => write!(
422 "{} failed: pointer must be in-bounds at offset {}, \
423 but is outside bounds of allocation {} which has size {}",
427 allocation_size.bytes()
429 ValidationFailure(ref err) => write!(f, "type validation failed: {}", err),
430 NoMirFor(ref func) => write!(f, "no MIR for `{}`", func),
431 FunctionAbiMismatch(caller_abi, callee_abi) => write!(
433 "tried to call a function with ABI {:?} using caller ABI {:?}",
434 callee_abi, caller_abi
436 FunctionArgMismatch(caller_ty, callee_ty) => write!(
438 "tried to call a function with argument of type {:?} \
439 passing data of type {:?}",
442 TransmuteSizeDiff(from_ty, to_ty) => write!(
444 "tried to transmute from {:?} to {:?}, but their sizes differed",
447 FunctionRetMismatch(caller_ty, callee_ty) => write!(
449 "tried to call a function with return type {:?} \
450 passing return place of type {:?}",
453 FunctionArgCountMismatch => {
454 write!(f, "tried to call a function with incorrect number of arguments")
456 ReallocatedWrongMemoryKind(ref old, ref new) => {
457 write!(f, "tried to reallocate memory from `{}` to `{}`", old, new)
459 DeallocatedWrongMemoryKind(ref old, ref new) => {
460 write!(f, "tried to deallocate `{}` memory but gave `{}` as the kind", old, new)
463 write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c)
465 AlignmentCheckFailed { required, has } => write!(
467 "tried to access memory with alignment {}, but alignment {} is required",
471 TypeNotPrimitive(ty) => write!(f, "expected primitive type, got {}", ty),
472 PathNotFound(ref path) => write!(f, "cannot find path {:?}", path),
473 IncorrectAllocationInformation(size, size2, align, align2) => write!(
475 "incorrect alloc info: expected size {} and align {}, \
476 got size {} and align {}",
482 InvalidMemoryAccess => write!(f, "tried to access memory through an invalid pointer"),
483 DanglingPointerDeref => write!(f, "dangling pointer was dereferenced"),
484 DoubleFree => write!(f, "tried to deallocate dangling pointer"),
485 InvalidFunctionPointer => {
486 write!(f, "tried to use a function pointer after offsetting it")
488 InvalidBool => write!(f, "invalid boolean value read"),
489 InvalidNullPointerUsage => write!(f, "invalid use of NULL pointer"),
490 ReadPointerAsBytes => write!(
492 "a raw memory access tried to access part of a pointer value as raw \
495 ReadBytesAsPointer => {
496 write!(f, "a memory access tried to interpret some bytes as a pointer")
498 ReadForeignStatic => write!(f, "tried to read from foreign (extern) static"),
499 InvalidPointerMath => write!(
501 "attempted to do invalid arithmetic on pointers that would leak base \
502 addresses, e.g., comparing pointers into different allocations"
504 DeadLocal => write!(f, "tried to access a dead local variable"),
505 DerefFunctionPointer => write!(f, "tried to dereference a function pointer"),
506 ExecuteMemory => write!(f, "tried to treat a memory pointer as a function pointer"),
507 OutOfTls => write!(f, "reached the maximum number of representable TLS keys"),
508 TlsOutOfBounds => write!(f, "accessed an invalid (unallocated) TLS key"),
509 CalledClosureAsFunction => {
510 write!(f, "tried to call a closure through a function pointer")
512 VtableForArgumentlessMethod => {
513 write!(f, "tried to call a vtable function without arguments")
515 ModifiedConstantMemory => write!(f, "tried to modify constant memory"),
516 ModifiedStatic => write!(
518 "tried to modify a static's initial value from another static's \
521 ReallocateNonBasePtr => write!(
523 "tried to reallocate with a pointer not to the beginning of an \
526 DeallocateNonBasePtr => write!(
528 "tried to deallocate with a pointer not to the beginning of an \
531 HeapAllocZeroBytes => write!(f, "tried to re-, de- or allocate zero bytes on the heap"),
532 ReadFromReturnPointer => write!(f, "tried to read from the return pointer"),
533 UnimplementedTraitSelection => {
534 write!(f, "there were unresolved type arguments during trait selection")
536 InvalidBoolOp(_) => write!(f, "invalid boolean operation"),
537 UnterminatedCString(_) => write!(
539 "attempted to get length of a null-terminated string, but no null \
540 found before end of allocation"
542 ReadUndefBytes(_) => write!(f, "attempted to read undefined bytes"),
543 HeapAllocNonPowerOfTwoAlignment(_) => write!(
545 "tried to re-, de-, or allocate heap memory with alignment that is \
548 Unsupported(ref msg) => write!(f, "{}", msg),
549 ConstPropUnsupported(ref msg) => {
550 write!(f, "Constant propagation encountered an unsupported situation: {}", msg)
556 /// Error information for when the program exhausted the resources granted to it
557 /// by the interpreter.
558 pub enum ResourceExhaustionInfo {
559 /// The stack grew too big.
560 StackFrameLimitReached,
561 /// The program ran into an infinite loop.
565 impl fmt::Debug for ResourceExhaustionInfo {
566 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
567 use ResourceExhaustionInfo::*;
569 StackFrameLimitReached => {
570 write!(f, "reached the configured maximum number of stack frames")
572 InfiniteLoop => write!(
574 "duplicate interpreter state observed here, const evaluation will never \
581 pub enum InterpError<'tcx> {
582 /// The program caused undefined behavior.
583 UndefinedBehavior(UndefinedBehaviorInfo),
584 /// The program did something the interpreter does not support (some of these *might* be UB
585 /// but the interpreter is not sure).
586 Unsupported(UnsupportedOpInfo<'tcx>),
587 /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...).
588 InvalidProgram(InvalidProgramInfo<'tcx>),
589 /// The program exhausted the interpreter's resources (stack/heap too big,
590 /// execution takes too long, ...).
591 ResourceExhaustion(ResourceExhaustionInfo),
592 /// Stop execution for a machine-controlled reason. This is never raised by
593 /// the core engine itself.
594 MachineStop(Box<dyn Any + Send>),
597 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
599 impl fmt::Display for InterpError<'_> {
600 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
601 // Forward `Display` to `Debug`.
602 write!(f, "{:?}", self)
606 impl fmt::Debug for InterpError<'_> {
607 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
610 Unsupported(ref msg) => write!(f, "{:?}", msg),
611 InvalidProgram(ref msg) => write!(f, "{:?}", msg),
612 UndefinedBehavior(ref msg) => write!(f, "{:?}", msg),
613 ResourceExhaustion(ref msg) => write!(f, "{:?}", msg),
614 MachineStop(_) => bug!("unhandled MachineStop"),
619 impl InterpError<'_> {
620 /// Some errors allocate to be created as they contain free-form strings.
621 /// And sometimes we want to be sure that did not happen as it is a
622 /// waste of resources.
623 pub fn allocates(&self) -> bool {
625 InterpError::MachineStop(_)
626 | InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
627 | InterpError::Unsupported(UnsupportedOpInfo::ValidationFailure(_))
628 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
629 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::UbExperimental(_)) => true,