]> git.lizzy.rs Git - rust.git/blob - src/librustc/mir/interpret/error.rs
Rollup merge of #69959 - alexcrichton:fix-panic-in-print, r=Mark-Simulacrum
[rust.git] / src / librustc / mir / interpret / error.rs
1 use super::{AllocId, CheckInAllocMsg, Pointer, RawConst, ScalarMaybeUndef};
2
3 use crate::hir::map::definitions::DefPathData;
4 use crate::mir::interpret::ConstValue;
5 use crate::ty::layout::{Align, LayoutError, Size};
6 use crate::ty::query::TyCtxtAt;
7 use crate::ty::tls;
8 use crate::ty::{self, layout, Ty};
9
10 use backtrace::Backtrace;
11 use rustc_data_structures::sync::Lock;
12 use rustc_errors::{struct_span_err, DiagnosticBuilder};
13 use rustc_hir as hir;
14 use rustc_macros::HashStable;
15 use rustc_session::CtfeBacktrace;
16 use rustc_span::{def_id::DefId, Pos, Span};
17 use std::{any::Any, fmt};
18
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.
22     Reported,
23     /// Don't emit an error, the evaluation failed because the MIR was generic
24     /// and the substs didn't fully monomorphize it.
25     TooGeneric,
26 }
27
28 impl ErrorHandled {
29     pub fn assert_reported(self) {
30         match self {
31             ErrorHandled::Reported => {}
32             ErrorHandled::TooGeneric => bug!(
33                 "MIR interpretation failed without reporting an error \
34                  even though it was fully monomorphized"
35             ),
36         }
37     }
38 }
39
40 CloneTypeFoldableImpls! {
41     ErrorHandled,
42 }
43
44 pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
45 pub type ConstEvalResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
46
47 #[derive(Debug)]
48 pub struct ConstEvalErr<'tcx> {
49     pub span: Span,
50     pub error: crate::mir::interpret::InterpError<'tcx>,
51     pub stacktrace: Vec<FrameInfo<'tcx>>,
52 }
53
54 #[derive(Debug)]
55 pub struct FrameInfo<'tcx> {
56     /// This span is in the caller.
57     pub call_site: Span,
58     pub instance: ty::Instance<'tcx>,
59     pub lint_root: Option<hir::HirId>,
60 }
61
62 impl<'tcx> fmt::Display for FrameInfo<'tcx> {
63     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64         ty::tls::with(|tcx| {
65             if tcx.def_key(self.instance.def_id()).disambiguated_data.data
66                 == DefPathData::ClosureExpr
67             {
68                 write!(f, "inside call to closure")?;
69             } else {
70                 write!(f, "inside call to `{}`", self.instance)?;
71             }
72             if !self.call_site.is_dummy() {
73                 let lo = tcx.sess.source_map().lookup_char_pos(self.call_site.lo());
74                 write!(f, " at {}:{}:{}", lo.file.name, lo.line, lo.col.to_usize() + 1)?;
75             }
76             Ok(())
77         })
78     }
79 }
80
81 impl<'tcx> ConstEvalErr<'tcx> {
82     pub fn struct_error(
83         &self,
84         tcx: TyCtxtAt<'tcx>,
85         message: &str,
86         emit: impl FnOnce(DiagnosticBuilder<'_>),
87     ) -> Result<(), ErrorHandled> {
88         self.struct_generic(tcx, message, emit, None)
89     }
90
91     pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
92         match self.struct_error(tcx, message, |mut e| e.emit()) {
93             Ok(_) => ErrorHandled::Reported,
94             Err(x) => x,
95         }
96     }
97
98     pub fn report_as_lint(
99         &self,
100         tcx: TyCtxtAt<'tcx>,
101         message: &str,
102         lint_root: hir::HirId,
103         span: Option<Span>,
104     ) -> ErrorHandled {
105         match self.struct_generic(
106             tcx,
107             message,
108             |mut lint: DiagnosticBuilder<'_>| {
109                 // Apply the span.
110                 if let Some(span) = span {
111                     let primary_spans = lint.span.primary_spans().to_vec();
112                     // point at the actual error as the primary span
113                     lint.replace_span_with(span);
114                     // point to the `const` statement as a secondary span
115                     // they don't have any label
116                     for sp in primary_spans {
117                         if sp != span {
118                             lint.span_label(sp, "");
119                         }
120                     }
121                 }
122                 lint.emit();
123             },
124             Some(lint_root),
125         ) {
126             Ok(_) => ErrorHandled::Reported,
127             Err(err) => err,
128         }
129     }
130
131     /// Create a diagnostic for this const eval error.
132     ///
133     /// Sets the message passed in via `message` and adds span labels with detailed error
134     /// information before handing control back to `emit` to do any final processing.
135     /// It's the caller's responsibility to call emit(), stash(), etc. within the `emit`
136     /// function to dispose of the diagnostic properly.
137     ///
138     /// If `lint_root.is_some()` report it as a lint, else report it as a hard error.
139     /// (Except that for some errors, we ignore all that -- see `must_error` below.)
140     fn struct_generic(
141         &self,
142         tcx: TyCtxtAt<'tcx>,
143         message: &str,
144         emit: impl FnOnce(DiagnosticBuilder<'_>),
145         lint_root: Option<hir::HirId>,
146     ) -> Result<(), ErrorHandled> {
147         let must_error = match self.error {
148             err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
149                 return Err(ErrorHandled::TooGeneric);
150             }
151             err_inval!(TypeckError) => return Err(ErrorHandled::Reported),
152             // We must *always* hard error on these, even if the caller wants just a lint.
153             err_inval!(Layout(LayoutError::SizeOverflow(_))) => true,
154             _ => false,
155         };
156         trace!("reporting const eval failure at {:?}", self.span);
157
158         let err_msg = match &self.error {
159             InterpError::MachineStop(msg) => {
160                 // A custom error (`ConstEvalErrKind` in `librustc_mir/interp/const_eval/error.rs`).
161                 // Should be turned into a string by now.
162                 msg.downcast_ref::<String>().expect("invalid MachineStop payload").clone()
163             }
164             err => err.to_string(),
165         };
166
167         let finish = |mut err: DiagnosticBuilder<'_>, span_msg: Option<String>| {
168             if let Some(span_msg) = span_msg {
169                 err.span_label(self.span, span_msg);
170             }
171             // Add spans for the stacktrace.
172             // Skip the last, which is just the environment of the constant.  The stacktrace
173             // is sometimes empty because we create "fake" eval contexts in CTFE to do work
174             // on constant values.
175             if !self.stacktrace.is_empty() {
176                 for frame_info in &self.stacktrace[..self.stacktrace.len() - 1] {
177                     err.span_label(frame_info.call_site, frame_info.to_string());
178                 }
179             }
180             // Let the caller finish the job.
181             emit(err)
182         };
183
184         if must_error {
185             // The `message` makes little sense here, this is a more serious error than the
186             // caller thinks anyway.
187             // See <https://github.com/rust-lang/rust/pull/63152>.
188             finish(struct_error(tcx, &err_msg), None);
189         } else {
190             // Regular case.
191             if let Some(lint_root) = lint_root {
192                 // Report as lint.
193                 let hir_id = self
194                     .stacktrace
195                     .iter()
196                     .rev()
197                     .filter_map(|frame| frame.lint_root)
198                     .next()
199                     .unwrap_or(lint_root);
200                 tcx.struct_span_lint_hir(
201                     rustc_session::lint::builtin::CONST_ERR,
202                     hir_id,
203                     tcx.span,
204                     |lint| finish(lint.build(message), Some(err_msg)),
205                 );
206             } else {
207                 // Report as hard error.
208                 finish(struct_error(tcx, message), Some(err_msg));
209             }
210         }
211         Ok(())
212     }
213 }
214
215 pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
216     struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
217 }
218
219 /// Packages the kind of error we got from the const code interpreter
220 /// up with a Rust-level backtrace of where the error occurred.
221 /// Thsese should always be constructed by calling `.into()` on
222 /// a `InterpError`. In `librustc_mir::interpret`, we have `throw_err_*`
223 /// macros for this.
224 #[derive(Debug)]
225 pub struct InterpErrorInfo<'tcx> {
226     pub kind: InterpError<'tcx>,
227     backtrace: Option<Box<Backtrace>>,
228 }
229
230 impl fmt::Display for InterpErrorInfo<'_> {
231     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
232         write!(f, "{}", self.kind)
233     }
234 }
235
236 impl InterpErrorInfo<'_> {
237     pub fn print_backtrace(&mut self) {
238         if let Some(ref mut backtrace) = self.backtrace {
239             print_backtrace(&mut *backtrace);
240         }
241     }
242 }
243
244 fn print_backtrace(backtrace: &mut Backtrace) {
245     backtrace.resolve();
246     eprintln!("\n\nAn error occurred in miri:\n{:?}", backtrace);
247 }
248
249 impl From<ErrorHandled> for InterpErrorInfo<'_> {
250     fn from(err: ErrorHandled) -> Self {
251         match err {
252             ErrorHandled::Reported => err_inval!(ReferencedConstant),
253             ErrorHandled::TooGeneric => err_inval!(TooGeneric),
254         }
255         .into()
256     }
257 }
258
259 impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
260     fn from(kind: InterpError<'tcx>) -> Self {
261         let capture_backtrace = tls::with_context_opt(|ctxt| {
262             if let Some(ctxt) = ctxt {
263                 *Lock::borrow(&ctxt.tcx.sess.ctfe_backtrace)
264             } else {
265                 CtfeBacktrace::Disabled
266             }
267         });
268
269         let backtrace = match capture_backtrace {
270             CtfeBacktrace::Disabled => None,
271             CtfeBacktrace::Capture => Some(Box::new(Backtrace::new_unresolved())),
272             CtfeBacktrace::Immediate => {
273                 // Print it now.
274                 let mut backtrace = Backtrace::new_unresolved();
275                 print_backtrace(&mut backtrace);
276                 None
277             }
278         };
279
280         InterpErrorInfo { kind, backtrace }
281     }
282 }
283
284 /// Error information for when the program we executed turned out not to actually be a valid
285 /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
286 /// where we work on generic code or execution does not have all information available.
287 pub enum InvalidProgramInfo<'tcx> {
288     /// Resolution can fail if we are in a too generic context.
289     TooGeneric,
290     /// Cannot compute this constant because it depends on another one
291     /// which already produced an error.
292     ReferencedConstant,
293     /// Abort in case type errors are reached.
294     TypeckError,
295     /// An error occurred during layout computation.
296     Layout(layout::LayoutError<'tcx>),
297     /// An invalid transmute happened.
298     TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>),
299 }
300
301 impl fmt::Debug for InvalidProgramInfo<'_> {
302     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303         use InvalidProgramInfo::*;
304         match self {
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),
309             TransmuteSizeDiff(from_ty, to_ty) => write!(
310                 f,
311                 "tried to transmute from {:?} to {:?}, but their sizes differed",
312                 from_ty, to_ty
313             ),
314         }
315     }
316 }
317
318 /// Error information for when the program caused Undefined Behavior.
319 pub enum UndefinedBehaviorInfo {
320     /// Free-form case. Only for errors that are never caught!
321     Ub(String),
322     /// Free-form case for experimental UB. Only for errors that are never caught!
323     UbExperimental(String),
324     /// Unreachable code was executed.
325     Unreachable,
326     /// An enum discriminant was set to a value which was outside the range of valid values.
327     InvalidDiscriminant(ScalarMaybeUndef),
328     /// A slice/array index projection went out-of-bounds.
329     BoundsCheckFailed {
330         len: u64,
331         index: u64,
332     },
333     /// Something was divided by 0 (x / 0).
334     DivisionByZero,
335     /// Something was "remainded" by 0 (x % 0).
336     RemainderByZero,
337     /// Overflowing inbounds pointer arithmetic.
338     PointerArithOverflow,
339     /// Invalid metadata in a wide pointer (using `str` to avoid allocations).
340     InvalidMeta(&'static str),
341     /// Reading a C string that does not end within its allocation.
342     UnterminatedCString(Pointer),
343     /// Dereferencing a dangling pointer after it got freed.
344     PointerUseAfterFree(AllocId),
345     /// Used a pointer outside the bounds it is valid for.
346     PointerOutOfBounds {
347         ptr: Pointer,
348         msg: CheckInAllocMsg,
349         allocation_size: Size,
350     },
351     /// Used a pointer with bad alignment.
352     AlignmentCheckFailed {
353         required: Align,
354         has: Align,
355     },
356     /// Using an integer as a pointer in the wrong way.
357     InvalidIntPointerUsage(u64),
358     /// Writing to read-only memory.
359     WriteToReadOnly(AllocId),
360     /// Using a pointer-not-to-a-function as function pointer.
361     InvalidFunctionPointer(Pointer),
362     // Trying to access the data behind a function pointer.
363     DerefFunctionPointer(AllocId),
364     /// The value validity check found a problem.
365     /// Should only be thrown by `validity.rs` and always point out which part of the value
366     /// is the problem.
367     ValidationFailure(String),
368     /// Using a non-boolean `u8` as bool.
369     InvalidBool(u8),
370     /// Using a non-character `u32` as character.
371     InvalidChar(u32),
372     /// Using uninitialized data where it is not allowed.
373     InvalidUndefBytes(Option<Pointer>),
374     /// Working with a local that is not currently live.
375     DeadLocal,
376     /// Trying to read from the return place of a function.
377     ReadFromReturnPlace,
378 }
379
380 impl fmt::Debug for UndefinedBehaviorInfo {
381     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
382         use UndefinedBehaviorInfo::*;
383         match self {
384             Ub(msg) | UbExperimental(msg) => write!(f, "{}", msg),
385             Unreachable => write!(f, "entering unreachable code"),
386             InvalidDiscriminant(val) => write!(f, "encountering invalid enum discriminant {}", val),
387             BoundsCheckFailed { ref len, ref index } => write!(
388                 f,
389                 "indexing out of bounds: the len is {:?} but the index is {:?}",
390                 len, index
391             ),
392             DivisionByZero => write!(f, "dividing by zero"),
393             RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
394             PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
395             InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg),
396             UnterminatedCString(p) => write!(
397                 f,
398                 "reading a null-terminated string starting at {:?} with no null found before end of allocation",
399                 p,
400             ),
401             PointerUseAfterFree(a) => {
402                 write!(f, "pointer to {:?} was dereferenced after this allocation got freed", a)
403             }
404             PointerOutOfBounds { ptr, msg, allocation_size } => write!(
405                 f,
406                 "{} failed: pointer must be in-bounds at offset {}, \
407                            but is outside bounds of {} which has size {}",
408                 msg,
409                 ptr.offset.bytes(),
410                 ptr.alloc_id,
411                 allocation_size.bytes()
412             ),
413             InvalidIntPointerUsage(0) => write!(f, "invalid use of NULL pointer"),
414             InvalidIntPointerUsage(i) => write!(f, "invalid use of {} as a pointer", i),
415             AlignmentCheckFailed { required, has } => write!(
416                 f,
417                 "accessing memory with alignment {}, but alignment {} is required",
418                 has.bytes(),
419                 required.bytes()
420             ),
421             WriteToReadOnly(a) => write!(f, "writing to {:?} which is read-only", a),
422             InvalidFunctionPointer(p) => {
423                 write!(f, "using {:?} as function pointer but it does not point to a function", p)
424             }
425             DerefFunctionPointer(a) => write!(f, "accessing {:?} which contains a function", a),
426             ValidationFailure(ref err) => write!(f, "type validation failed: {}", err),
427             InvalidBool(b) => write!(f, "interpreting an invalid 8-bit value as a bool: {}", b),
428             InvalidChar(c) => write!(f, "interpreting an invalid 32-bit value as a char: {}", c),
429             InvalidUndefBytes(Some(p)) => write!(
430                 f,
431                 "reading uninitialized memory at {:?}, but this operation requires initialized memory",
432                 p
433             ),
434             InvalidUndefBytes(None) => write!(
435                 f,
436                 "using uninitialized data, but this operation requires initialized memory"
437             ),
438             DeadLocal => write!(f, "accessing a dead local variable"),
439             ReadFromReturnPlace => write!(f, "tried to read from the return place"),
440         }
441     }
442 }
443
444 /// Error information for when the program did something that might (or might not) be correct
445 /// to do according to the Rust spec, but due to limitations in the interpreter, the
446 /// operation could not be carried out. These limitations can differ between CTFE and the
447 /// Miri engine, e.g., CTFE does not support casting pointers to "real" integers.
448 ///
449 /// Currently, we also use this as fall-back error kind for errors that have not been
450 /// categorized yet.
451 pub enum UnsupportedOpInfo {
452     /// Free-form case. Only for errors that are never caught!
453     Unsupported(String),
454     /// When const-prop encounters a situation it does not support, it raises this error.
455     /// This must not allocate for performance reasons (hence `str`, not `String`).
456     ConstPropUnsupported(&'static str),
457     /// Accessing an unsupported foreign static.
458     ReadForeignStatic(DefId),
459     /// Could not find MIR for a function.
460     NoMirFor(DefId),
461     /// Modified a static during const-eval.
462     /// FIXME: move this to `ConstEvalErrKind` through a machine hook.
463     ModifiedStatic,
464     /// Encountered a pointer where we needed raw bytes.
465     ReadPointerAsBytes,
466     /// Encountered raw bytes where we needed a pointer.
467     ReadBytesAsPointer,
468 }
469
470 impl fmt::Debug for UnsupportedOpInfo {
471     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
472         use UnsupportedOpInfo::*;
473         match self {
474             Unsupported(ref msg) => write!(f, "{}", msg),
475             ConstPropUnsupported(ref msg) => {
476                 write!(f, "Constant propagation encountered an unsupported situation: {}", msg)
477             }
478             ReadForeignStatic(did) => {
479                 write!(f, "tried to read from foreign (extern) static {:?}", did)
480             }
481             NoMirFor(did) => write!(f, "could not load MIR for {:?}", did),
482             ModifiedStatic => write!(
483                 f,
484                 "tried to modify a static's initial value from another static's \
485                     initializer"
486             ),
487
488             ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes",),
489             ReadBytesAsPointer => write!(f, "unable to turn bytes into a pointer"),
490         }
491     }
492 }
493
494 /// Error information for when the program exhausted the resources granted to it
495 /// by the interpreter.
496 pub enum ResourceExhaustionInfo {
497     /// The stack grew too big.
498     StackFrameLimitReached,
499     /// The program ran into an infinite loop.
500     InfiniteLoop,
501 }
502
503 impl fmt::Debug for ResourceExhaustionInfo {
504     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
505         use ResourceExhaustionInfo::*;
506         match self {
507             StackFrameLimitReached => {
508                 write!(f, "reached the configured maximum number of stack frames")
509             }
510             InfiniteLoop => write!(
511                 f,
512                 "duplicate interpreter state observed here, const evaluation will never \
513                     terminate"
514             ),
515         }
516     }
517 }
518
519 pub enum InterpError<'tcx> {
520     /// The program caused undefined behavior.
521     UndefinedBehavior(UndefinedBehaviorInfo),
522     /// The program did something the interpreter does not support (some of these *might* be UB
523     /// but the interpreter is not sure).
524     Unsupported(UnsupportedOpInfo),
525     /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...).
526     InvalidProgram(InvalidProgramInfo<'tcx>),
527     /// The program exhausted the interpreter's resources (stack/heap too big,
528     /// execution takes too long, ...).
529     ResourceExhaustion(ResourceExhaustionInfo),
530     /// Stop execution for a machine-controlled reason. This is never raised by
531     /// the core engine itself.
532     MachineStop(Box<dyn Any + Send>),
533 }
534
535 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
536
537 impl fmt::Display for InterpError<'_> {
538     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
539         // Forward `Display` to `Debug`.
540         fmt::Debug::fmt(self, f)
541     }
542 }
543
544 impl fmt::Debug for InterpError<'_> {
545     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
546         use InterpError::*;
547         match *self {
548             Unsupported(ref msg) => write!(f, "{:?}", msg),
549             InvalidProgram(ref msg) => write!(f, "{:?}", msg),
550             UndefinedBehavior(ref msg) => write!(f, "{:?}", msg),
551             ResourceExhaustion(ref msg) => write!(f, "{:?}", msg),
552             MachineStop(_) => bug!("unhandled MachineStop"),
553         }
554     }
555 }
556
557 impl InterpError<'_> {
558     /// Some errors allocate to be created as they contain free-form strings.
559     /// And sometimes we want to be sure that did not happen as it is a
560     /// waste of resources.
561     pub fn allocates(&self) -> bool {
562         match self {
563             InterpError::MachineStop(_)
564             | InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
565             | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure(_))
566             | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
567             | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::UbExperimental(_)) => true,
568             _ => false,
569         }
570     }
571 }