1 use super::{AllocId, CheckInAllocMsg, Pointer, RawConst, ScalarMaybeUndef};
3 use crate::mir::interpret::ConstValue;
4 use crate::ty::layout::LayoutError;
5 use crate::ty::query::TyCtxtAt;
7 use crate::ty::{self, layout, Ty};
9 use backtrace::Backtrace;
10 use rustc_data_structures::sync::Lock;
11 use rustc_errors::{struct_span_err, DiagnosticBuilder, ErrorReported};
13 use rustc_hir::definitions::DefPathData;
14 use rustc_macros::HashStable;
15 use rustc_session::CtfeBacktrace;
16 use rustc_span::{def_id::DefId, Pos, Span};
17 use rustc_target::abi::{Align, Size};
18 use std::{any::Any, fmt, mem};
20 #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, RustcEncodable, RustcDecodable)]
21 pub enum ErrorHandled {
22 /// Already reported an error for this evaluation, and the compilation is
23 /// *guaranteed* to fail. Warnings/lints *must not* produce `Reported`.
24 Reported(ErrorReported),
25 /// Already emitted a lint for this evaluation.
27 /// Don't emit an error, the evaluation failed because the MIR was generic
28 /// and the substs didn't fully monomorphize it.
32 CloneTypeFoldableImpls! {
36 pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
37 pub type ConstEvalResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
40 pub struct ConstEvalErr<'tcx> {
42 pub error: crate::mir::interpret::InterpError<'tcx>,
43 pub stacktrace: Vec<FrameInfo<'tcx>>,
47 pub struct FrameInfo<'tcx> {
48 pub instance: ty::Instance<'tcx>,
50 pub lint_root: Option<hir::HirId>,
53 impl<'tcx> fmt::Display for FrameInfo<'tcx> {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 if tcx.def_key(self.instance.def_id()).disambiguated_data.data
57 == DefPathData::ClosureExpr
59 write!(f, "inside closure")?;
61 write!(f, "inside `{}`", self.instance)?;
63 if !self.span.is_dummy() {
64 let lo = tcx.sess.source_map().lookup_char_pos(self.span.lo());
65 write!(f, " at {}:{}:{}", lo.file.name, lo.line, lo.col.to_usize() + 1)?;
72 impl<'tcx> ConstEvalErr<'tcx> {
77 emit: impl FnOnce(DiagnosticBuilder<'_>),
79 self.struct_generic(tcx, message, emit, None)
82 pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
83 self.struct_error(tcx, message, |mut e| e.emit())
86 pub fn report_as_lint(
90 lint_root: hir::HirId,
96 |mut lint: DiagnosticBuilder<'_>| {
98 if let Some(span) = span {
99 let primary_spans = lint.span.primary_spans().to_vec();
100 // point at the actual error as the primary span
101 lint.replace_span_with(span);
102 // point to the `const` statement as a secondary span
103 // they don't have any label
104 for sp in primary_spans {
106 lint.span_label(sp, "");
116 /// Create a diagnostic for this const eval error.
118 /// Sets the message passed in via `message` and adds span labels with detailed error
119 /// information before handing control back to `emit` to do any final processing.
120 /// It's the caller's responsibility to call emit(), stash(), etc. within the `emit`
121 /// function to dispose of the diagnostic properly.
123 /// If `lint_root.is_some()` report it as a lint, else report it as a hard error.
124 /// (Except that for some errors, we ignore all that -- see `must_error` below.)
129 emit: impl FnOnce(DiagnosticBuilder<'_>),
130 lint_root: Option<hir::HirId>,
132 let must_error = match self.error {
133 err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
134 return ErrorHandled::TooGeneric;
136 err_inval!(TypeckError(error_reported)) => {
137 return ErrorHandled::Reported(error_reported);
139 // We must *always* hard error on these, even if the caller wants just a lint.
140 err_inval!(Layout(LayoutError::SizeOverflow(_))) => true,
143 trace!("reporting const eval failure at {:?}", self.span);
145 let err_msg = match &self.error {
146 InterpError::MachineStop(msg) => {
147 // A custom error (`ConstEvalErrKind` in `librustc_mir/interp/const_eval/error.rs`).
148 // Should be turned into a string by now.
149 msg.downcast_ref::<String>().expect("invalid MachineStop payload").clone()
151 err => err.to_string(),
154 let finish = |mut err: DiagnosticBuilder<'_>, span_msg: Option<String>| {
155 if let Some(span_msg) = span_msg {
156 err.span_label(self.span, span_msg);
158 // Add spans for the stacktrace. Don't print a single-line backtrace though.
159 if self.stacktrace.len() > 1 {
160 for frame_info in &self.stacktrace {
161 err.span_label(frame_info.span, frame_info.to_string());
164 // Let the caller finish the job.
169 // The `message` makes little sense here, this is a more serious error than the
170 // caller thinks anyway.
171 // See <https://github.com/rust-lang/rust/pull/63152>.
172 finish(struct_error(tcx, &err_msg), None);
173 ErrorHandled::Reported(ErrorReported)
176 if let Some(lint_root) = lint_root {
182 .filter_map(|frame| frame.lint_root)
184 .unwrap_or(lint_root);
185 tcx.struct_span_lint_hir(
186 rustc_session::lint::builtin::CONST_ERR,
189 |lint| finish(lint.build(message), Some(err_msg)),
193 // Report as hard error.
194 finish(struct_error(tcx, message), Some(err_msg));
195 ErrorHandled::Reported(ErrorReported)
201 pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
202 struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
205 /// Packages the kind of error we got from the const code interpreter
206 /// up with a Rust-level backtrace of where the error occurred.
207 /// Thsese should always be constructed by calling `.into()` on
208 /// a `InterpError`. In `librustc_mir::interpret`, we have `throw_err_*`
211 pub struct InterpErrorInfo<'tcx> {
212 pub kind: InterpError<'tcx>,
213 backtrace: Option<Box<Backtrace>>,
216 impl fmt::Display for InterpErrorInfo<'_> {
217 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218 write!(f, "{}", self.kind)
222 impl InterpErrorInfo<'_> {
223 pub fn print_backtrace(&mut self) {
224 if let Some(ref mut backtrace) = self.backtrace {
225 print_backtrace(&mut *backtrace);
230 fn print_backtrace(backtrace: &mut Backtrace) {
232 eprintln!("\n\nAn error occurred in miri:\n{:?}", backtrace);
235 impl From<ErrorHandled> for InterpErrorInfo<'_> {
236 fn from(err: ErrorHandled) -> Self {
238 ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {
239 err_inval!(ReferencedConstant)
241 ErrorHandled::TooGeneric => err_inval!(TooGeneric),
247 impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
248 fn from(kind: InterpError<'tcx>) -> Self {
249 let capture_backtrace = tls::with_context_opt(|ctxt| {
250 if let Some(ctxt) = ctxt {
251 *Lock::borrow(&ctxt.tcx.sess.ctfe_backtrace)
253 CtfeBacktrace::Disabled
257 let backtrace = match capture_backtrace {
258 CtfeBacktrace::Disabled => None,
259 CtfeBacktrace::Capture => Some(Box::new(Backtrace::new_unresolved())),
260 CtfeBacktrace::Immediate => {
262 let mut backtrace = Backtrace::new_unresolved();
263 print_backtrace(&mut backtrace);
268 InterpErrorInfo { kind, backtrace }
272 /// Error information for when the program we executed turned out not to actually be a valid
273 /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
274 /// where we work on generic code or execution does not have all information available.
275 pub enum InvalidProgramInfo<'tcx> {
276 /// Resolution can fail if we are in a too generic context.
278 /// Cannot compute this constant because it depends on another one
279 /// which already produced an error.
281 /// Abort in case type errors are reached.
282 TypeckError(ErrorReported),
283 /// An error occurred during layout computation.
284 Layout(layout::LayoutError<'tcx>),
285 /// An invalid transmute happened.
286 TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>),
289 impl fmt::Debug for InvalidProgramInfo<'_> {
290 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291 use InvalidProgramInfo::*;
293 TooGeneric => write!(f, "encountered overly generic constant"),
294 ReferencedConstant => write!(f, "referenced constant has errors"),
295 TypeckError(ErrorReported) => {
296 write!(f, "encountered constants with type errors, stopping evaluation")
298 Layout(ref err) => write!(f, "{}", err),
299 TransmuteSizeDiff(from_ty, to_ty) => write!(
301 "transmuting `{}` to `{}` is not possible, because these types do not have the same size",
308 /// Error information for when the program caused Undefined Behavior.
309 pub enum UndefinedBehaviorInfo {
310 /// Free-form case. Only for errors that are never caught!
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.
321 /// Something was divided by 0 (x / 0).
323 /// Something was "remainded" by 0 (x % 0).
325 /// Overflowing inbounds pointer arithmetic.
326 PointerArithOverflow,
327 /// Invalid metadata in a wide pointer (using `str` to avoid allocations).
328 InvalidMeta(&'static str),
329 /// Reading a C string that does not end within its allocation.
330 UnterminatedCString(Pointer),
331 /// Dereferencing a dangling pointer after it got freed.
332 PointerUseAfterFree(AllocId),
333 /// Used a pointer outside the bounds it is valid for.
336 msg: CheckInAllocMsg,
337 allocation_size: Size,
339 /// Used a pointer with bad alignment.
340 AlignmentCheckFailed {
344 /// Using an integer as a pointer in the wrong way.
345 InvalidIntPointerUsage(u64),
346 /// Writing to read-only memory.
347 WriteToReadOnly(AllocId),
348 /// Using a pointer-not-to-a-function as function pointer.
349 InvalidFunctionPointer(Pointer),
350 // Trying to access the data behind a function pointer.
351 DerefFunctionPointer(AllocId),
352 /// The value validity check found a problem.
353 /// Should only be thrown by `validity.rs` and always point out which part of the value
355 ValidationFailure(String),
356 /// Using a non-boolean `u8` as bool.
358 /// Using a non-character `u32` as character.
360 /// Using uninitialized data where it is not allowed.
361 InvalidUndefBytes(Option<Pointer>),
362 /// Working with a local that is not currently live.
366 impl fmt::Debug for UndefinedBehaviorInfo {
367 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
368 use UndefinedBehaviorInfo::*;
370 Ub(msg) => write!(f, "{}", msg),
371 Unreachable => write!(f, "entering unreachable code"),
372 InvalidDiscriminant(val) => write!(f, "encountering invalid enum discriminant {}", val),
373 BoundsCheckFailed { ref len, ref index } => write!(
375 "indexing out of bounds: the len is {:?} but the index is {:?}",
378 DivisionByZero => write!(f, "dividing by zero"),
379 RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
380 PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
381 InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg),
382 UnterminatedCString(p) => write!(
384 "reading a null-terminated string starting at {:?} with no null found before end of allocation",
387 PointerUseAfterFree(a) => {
388 write!(f, "pointer to {:?} was dereferenced after this allocation got freed", a)
390 PointerOutOfBounds { ptr, msg, allocation_size } => write!(
392 "{} failed: pointer must be in-bounds at offset {}, \
393 but is outside bounds of {} which has size {}",
397 allocation_size.bytes()
399 InvalidIntPointerUsage(0) => write!(f, "invalid use of NULL pointer"),
400 InvalidIntPointerUsage(i) => write!(f, "invalid use of {} as a pointer", i),
401 AlignmentCheckFailed { required, has } => write!(
403 "accessing memory with alignment {}, but alignment {} is required",
407 WriteToReadOnly(a) => write!(f, "writing to {:?} which is read-only", a),
408 InvalidFunctionPointer(p) => {
409 write!(f, "using {:?} as function pointer but it does not point to a function", p)
411 DerefFunctionPointer(a) => write!(f, "accessing {:?} which contains a function", a),
412 ValidationFailure(ref err) => write!(f, "type validation failed: {}", err),
413 InvalidBool(b) => write!(f, "interpreting an invalid 8-bit value as a bool: {}", b),
414 InvalidChar(c) => write!(f, "interpreting an invalid 32-bit value as a char: {}", c),
415 InvalidUndefBytes(Some(p)) => write!(
417 "reading uninitialized memory at {:?}, but this operation requires initialized memory",
420 InvalidUndefBytes(None) => write!(
422 "using uninitialized data, but this operation requires initialized memory"
424 DeadLocal => write!(f, "accessing a dead local variable"),
429 /// Error information for when the program did something that might (or might not) be correct
430 /// to do according to the Rust spec, but due to limitations in the interpreter, the
431 /// operation could not be carried out. These limitations can differ between CTFE and the
432 /// Miri engine, e.g., CTFE does not support casting pointers to "real" integers.
434 /// Currently, we also use this as fall-back error kind for errors that have not been
436 pub enum UnsupportedOpInfo {
437 /// Free-form case. Only for errors that are never caught!
439 /// Accessing an unsupported foreign static.
440 ReadForeignStatic(DefId),
441 /// Could not find MIR for a function.
443 /// Encountered a pointer where we needed raw bytes.
445 /// Encountered raw bytes where we needed a pointer.
449 impl fmt::Debug for UnsupportedOpInfo {
450 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
451 use UnsupportedOpInfo::*;
453 Unsupported(ref msg) => write!(f, "{}", msg),
454 ReadForeignStatic(did) => {
455 write!(f, "cannot read from foreign (extern) static {:?}", did)
457 NoMirFor(did) => write!(f, "no MIR body is available for {:?}", did),
458 ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes",),
459 ReadBytesAsPointer => write!(f, "unable to turn bytes into a pointer"),
464 /// Error information for when the program exhausted the resources granted to it
465 /// by the interpreter.
466 pub enum ResourceExhaustionInfo {
467 /// The stack grew too big.
468 StackFrameLimitReached,
469 /// The program ran for too long.
471 /// The exact limit is set by the `const_eval_limit` attribute.
475 impl fmt::Debug for ResourceExhaustionInfo {
476 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
477 use ResourceExhaustionInfo::*;
479 StackFrameLimitReached => {
480 write!(f, "reached the configured maximum number of stack frames")
482 StepLimitReached => {
483 write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)")
489 /// A trait to work around not having trait object upcasting.
490 pub trait AsAny: Any {
491 fn as_any(&self) -> &dyn Any;
494 impl<T: Any> AsAny for T {
496 fn as_any(&self) -> &dyn Any {
501 /// A trait for machine-specific errors (or other "machine stop" conditions).
502 pub trait MachineStopType: AsAny + fmt::Debug + Send {}
503 impl MachineStopType for String {}
505 impl dyn MachineStopType {
507 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
508 self.as_any().downcast_ref()
512 pub enum InterpError<'tcx> {
513 /// The program caused undefined behavior.
514 UndefinedBehavior(UndefinedBehaviorInfo),
515 /// The program did something the interpreter does not support (some of these *might* be UB
516 /// but the interpreter is not sure).
517 Unsupported(UnsupportedOpInfo),
518 /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...).
519 InvalidProgram(InvalidProgramInfo<'tcx>),
520 /// The program exhausted the interpreter's resources (stack/heap too big,
521 /// execution takes too long, ...).
522 ResourceExhaustion(ResourceExhaustionInfo),
523 /// Stop execution for a machine-controlled reason. This is never raised by
524 /// the core engine itself.
525 MachineStop(Box<dyn MachineStopType>),
528 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
530 impl fmt::Display for InterpError<'_> {
531 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
532 // Forward `Display` to `Debug`.
533 fmt::Debug::fmt(self, f)
537 impl fmt::Debug for InterpError<'_> {
538 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
541 Unsupported(ref msg) => write!(f, "{:?}", msg),
542 InvalidProgram(ref msg) => write!(f, "{:?}", msg),
543 UndefinedBehavior(ref msg) => write!(f, "{:?}", msg),
544 ResourceExhaustion(ref msg) => write!(f, "{:?}", msg),
545 MachineStop(ref msg) => write!(f, "{:?}", msg),
550 impl InterpError<'_> {
551 /// Some errors allocate to be created as they contain free-form strings.
552 /// And sometimes we want to be sure that did not happen as it is a
553 /// waste of resources.
554 pub fn allocates(&self) -> bool {
556 // Zero-sized boxes do not allocate.
557 InterpError::MachineStop(b) => mem::size_of_val::<dyn MachineStopType>(&**b) > 0,
558 InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
559 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure(_))
560 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) => true,