]> git.lizzy.rs Git - rust.git/blob - src/librustc_middle/mir/interpret/error.rs
Rename ArgumentSizeMismatch to ScalarSizeMismatch
[rust.git] / src / librustc_middle / mir / interpret / error.rs
1 use super::{AllocId, CheckInAllocMsg, Pointer, RawConst, ScalarMaybeUndef};
2
3 use crate::mir::interpret::ConstValue;
4 use crate::ty::layout::LayoutError;
5 use crate::ty::query::TyCtxtAt;
6 use crate::ty::tls;
7 use crate::ty::{self, layout, Ty};
8
9 use backtrace::Backtrace;
10 use rustc_data_structures::sync::Lock;
11 use rustc_errors::{struct_span_err, DiagnosticBuilder, ErrorReported};
12 use rustc_hir as hir;
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};
19
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.
26     Linted,
27     /// Don't emit an error, the evaluation failed because the MIR was generic
28     /// and the substs didn't fully monomorphize it.
29     TooGeneric,
30 }
31
32 CloneTypeFoldableImpls! {
33     ErrorHandled,
34 }
35
36 pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
37 pub type ConstEvalResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
38
39 #[derive(Debug)]
40 pub struct ConstEvalErr<'tcx> {
41     pub span: Span,
42     pub error: crate::mir::interpret::InterpError<'tcx>,
43     pub stacktrace: Vec<FrameInfo<'tcx>>,
44 }
45
46 #[derive(Debug)]
47 pub struct FrameInfo<'tcx> {
48     pub instance: ty::Instance<'tcx>,
49     pub span: Span,
50     pub lint_root: Option<hir::HirId>,
51 }
52
53 impl<'tcx> fmt::Display for FrameInfo<'tcx> {
54     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55         ty::tls::with(|tcx| {
56             if tcx.def_key(self.instance.def_id()).disambiguated_data.data
57                 == DefPathData::ClosureExpr
58             {
59                 write!(f, "inside closure")?;
60             } else {
61                 write!(f, "inside `{}`", self.instance)?;
62             }
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)?;
66             }
67             Ok(())
68         })
69     }
70 }
71
72 impl<'tcx> ConstEvalErr<'tcx> {
73     pub fn struct_error(
74         &self,
75         tcx: TyCtxtAt<'tcx>,
76         message: &str,
77         emit: impl FnOnce(DiagnosticBuilder<'_>),
78     ) -> ErrorHandled {
79         self.struct_generic(tcx, message, emit, None)
80     }
81
82     pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
83         self.struct_error(tcx, message, |mut e| e.emit())
84     }
85
86     pub fn report_as_lint(
87         &self,
88         tcx: TyCtxtAt<'tcx>,
89         message: &str,
90         lint_root: hir::HirId,
91         span: Option<Span>,
92     ) -> ErrorHandled {
93         self.struct_generic(
94             tcx,
95             message,
96             |mut lint: DiagnosticBuilder<'_>| {
97                 // Apply the span.
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 {
105                         if sp != span {
106                             lint.span_label(sp, "");
107                         }
108                     }
109                 }
110                 lint.emit();
111             },
112             Some(lint_root),
113         )
114     }
115
116     /// Create a diagnostic for this const eval error.
117     ///
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.
122     ///
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.)
125     fn struct_generic(
126         &self,
127         tcx: TyCtxtAt<'tcx>,
128         message: &str,
129         emit: impl FnOnce(DiagnosticBuilder<'_>),
130         lint_root: Option<hir::HirId>,
131     ) -> ErrorHandled {
132         let must_error = match self.error {
133             err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
134                 return ErrorHandled::TooGeneric;
135             }
136             err_inval!(TypeckError(error_reported)) => {
137                 return ErrorHandled::Reported(error_reported);
138             }
139             // We must *always* hard error on these, even if the caller wants just a lint.
140             err_inval!(Layout(LayoutError::SizeOverflow(_))) => true,
141             _ => false,
142         };
143         trace!("reporting const eval failure at {:?}", self.span);
144
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()
150             }
151             err => err.to_string(),
152         };
153
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);
157             }
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());
162                 }
163             }
164             // Let the caller finish the job.
165             emit(err)
166         };
167
168         if must_error {
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)
174         } else {
175             // Regular case.
176             if let Some(lint_root) = lint_root {
177                 // Report as lint.
178                 let hir_id = self
179                     .stacktrace
180                     .iter()
181                     .rev()
182                     .filter_map(|frame| frame.lint_root)
183                     .next()
184                     .unwrap_or(lint_root);
185                 tcx.struct_span_lint_hir(
186                     rustc_session::lint::builtin::CONST_ERR,
187                     hir_id,
188                     tcx.span,
189                     |lint| finish(lint.build(message), Some(err_msg)),
190                 );
191                 ErrorHandled::Linted
192             } else {
193                 // Report as hard error.
194                 finish(struct_error(tcx, message), Some(err_msg));
195                 ErrorHandled::Reported(ErrorReported)
196             }
197         }
198     }
199 }
200
201 pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
202     struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
203 }
204
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_*`
209 /// macros for this.
210 #[derive(Debug)]
211 pub struct InterpErrorInfo<'tcx> {
212     pub kind: InterpError<'tcx>,
213     backtrace: Option<Box<Backtrace>>,
214 }
215
216 impl fmt::Display for InterpErrorInfo<'_> {
217     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218         write!(f, "{}", self.kind)
219     }
220 }
221
222 impl InterpErrorInfo<'_> {
223     pub fn print_backtrace(&mut self) {
224         if let Some(ref mut backtrace) = self.backtrace {
225             print_backtrace(&mut *backtrace);
226         }
227     }
228 }
229
230 fn print_backtrace(backtrace: &mut Backtrace) {
231     backtrace.resolve();
232     eprintln!("\n\nAn error occurred in miri:\n{:?}", backtrace);
233 }
234
235 impl From<ErrorHandled> for InterpErrorInfo<'_> {
236     fn from(err: ErrorHandled) -> Self {
237         match err {
238             ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {
239                 err_inval!(ReferencedConstant)
240             }
241             ErrorHandled::TooGeneric => err_inval!(TooGeneric),
242         }
243         .into()
244     }
245 }
246
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)
252             } else {
253                 CtfeBacktrace::Disabled
254             }
255         });
256
257         let backtrace = match capture_backtrace {
258             CtfeBacktrace::Disabled => None,
259             CtfeBacktrace::Capture => Some(Box::new(Backtrace::new_unresolved())),
260             CtfeBacktrace::Immediate => {
261                 // Print it now.
262                 let mut backtrace = Backtrace::new_unresolved();
263                 print_backtrace(&mut backtrace);
264                 None
265             }
266         };
267
268         InterpErrorInfo { kind, backtrace }
269     }
270 }
271
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.
277     TooGeneric,
278     /// Cannot compute this constant because it depends on another one
279     /// which already produced an error.
280     ReferencedConstant,
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>),
287 }
288
289 impl fmt::Debug for InvalidProgramInfo<'_> {
290     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291         use InvalidProgramInfo::*;
292         match self {
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")
297             }
298             Layout(ref err) => write!(f, "{}", err),
299             TransmuteSizeDiff(from_ty, to_ty) => write!(
300                 f,
301                 "transmuting `{}` to `{}` is not possible, because these types do not have the same size",
302                 from_ty, to_ty
303             ),
304         }
305     }
306 }
307
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!
311     Ub(String),
312     /// Unreachable code was executed.
313     Unreachable,
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 {
318         len: u64,
319         index: u64,
320     },
321     /// Something was divided by 0 (x / 0).
322     DivisionByZero,
323     /// Something was "remainded" by 0 (x % 0).
324     RemainderByZero,
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.
334     PointerOutOfBounds {
335         ptr: Pointer,
336         msg: CheckInAllocMsg,
337         allocation_size: Size,
338     },
339     /// Used a pointer with bad alignment.
340     AlignmentCheckFailed {
341         required: Align,
342         has: Align,
343     },
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
354     /// is the problem.
355     ValidationFailure(String),
356     /// Using a non-boolean `u8` as bool.
357     InvalidBool(u8),
358     /// Using a non-character `u32` as character.
359     InvalidChar(u32),
360     /// Using uninitialized data where it is not allowed.
361     InvalidUndefBytes(Option<Pointer>),
362     /// Working with a local that is not currently live.
363     DeadLocal,
364     /// Data size is not equal to target size
365     ScalarSizeMismatch {
366         target_size: u64,
367         data_size: u64,
368     },
369 }
370
371 impl fmt::Debug for UndefinedBehaviorInfo {
372     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
373         use UndefinedBehaviorInfo::*;
374         match self {
375             Ub(msg) => write!(f, "{}", msg),
376             Unreachable => write!(f, "entering unreachable code"),
377             InvalidDiscriminant(val) => write!(f, "encountering invalid enum discriminant {}", val),
378             BoundsCheckFailed { ref len, ref index } => write!(
379                 f,
380                 "indexing out of bounds: the len is {:?} but the index is {:?}",
381                 len, index
382             ),
383             DivisionByZero => write!(f, "dividing by zero"),
384             RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
385             PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
386             InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg),
387             UnterminatedCString(p) => write!(
388                 f,
389                 "reading a null-terminated string starting at {:?} with no null found before end of allocation",
390                 p,
391             ),
392             PointerUseAfterFree(a) => {
393                 write!(f, "pointer to {:?} was dereferenced after this allocation got freed", a)
394             }
395             PointerOutOfBounds { ptr, msg, allocation_size } => write!(
396                 f,
397                 "{} failed: pointer must be in-bounds at offset {}, \
398                            but is outside bounds of {} which has size {}",
399                 msg,
400                 ptr.offset.bytes(),
401                 ptr.alloc_id,
402                 allocation_size.bytes()
403             ),
404             InvalidIntPointerUsage(0) => write!(f, "invalid use of NULL pointer"),
405             InvalidIntPointerUsage(i) => write!(f, "invalid use of {} as a pointer", i),
406             AlignmentCheckFailed { required, has } => write!(
407                 f,
408                 "accessing memory with alignment {}, but alignment {} is required",
409                 has.bytes(),
410                 required.bytes()
411             ),
412             WriteToReadOnly(a) => write!(f, "writing to {:?} which is read-only", a),
413             InvalidFunctionPointer(p) => {
414                 write!(f, "using {:?} as function pointer but it does not point to a function", p)
415             }
416             DerefFunctionPointer(a) => write!(f, "accessing {:?} which contains a function", a),
417             ValidationFailure(ref err) => write!(f, "type validation failed: {}", err),
418             InvalidBool(b) => write!(f, "interpreting an invalid 8-bit value as a bool: {}", b),
419             InvalidChar(c) => write!(f, "interpreting an invalid 32-bit value as a char: {}", c),
420             InvalidUndefBytes(Some(p)) => write!(
421                 f,
422                 "reading uninitialized memory at {:?}, but this operation requires initialized memory",
423                 p
424             ),
425             InvalidUndefBytes(None) => write!(
426                 f,
427                 "using uninitialized data, but this operation requires initialized memory"
428             ),
429             DeadLocal => write!(f, "accessing a dead local variable"),
430             ScalarSizeMismatch { target_size, data_size } => write!(
431                 f,
432                 "scalar size mismatch: expected {} bytes but got {} bytes instead",
433                 target_size, data_size
434             ),
435         }
436     }
437 }
438
439 /// Error information for when the program did something that might (or might not) be correct
440 /// to do according to the Rust spec, but due to limitations in the interpreter, the
441 /// operation could not be carried out. These limitations can differ between CTFE and the
442 /// Miri engine, e.g., CTFE does not support casting pointers to "real" integers.
443 ///
444 /// Currently, we also use this as fall-back error kind for errors that have not been
445 /// categorized yet.
446 pub enum UnsupportedOpInfo {
447     /// Free-form case. Only for errors that are never caught!
448     Unsupported(String),
449     /// Accessing an unsupported foreign static.
450     ReadForeignStatic(DefId),
451     /// Could not find MIR for a function.
452     NoMirFor(DefId),
453     /// Encountered a pointer where we needed raw bytes.
454     ReadPointerAsBytes,
455     /// Encountered raw bytes where we needed a pointer.
456     ReadBytesAsPointer,
457 }
458
459 impl fmt::Debug for UnsupportedOpInfo {
460     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
461         use UnsupportedOpInfo::*;
462         match self {
463             Unsupported(ref msg) => write!(f, "{}", msg),
464             ReadForeignStatic(did) => {
465                 write!(f, "cannot read from foreign (extern) static {:?}", did)
466             }
467             NoMirFor(did) => write!(f, "no MIR body is available for {:?}", did),
468             ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes",),
469             ReadBytesAsPointer => write!(f, "unable to turn bytes into a pointer"),
470         }
471     }
472 }
473
474 /// Error information for when the program exhausted the resources granted to it
475 /// by the interpreter.
476 pub enum ResourceExhaustionInfo {
477     /// The stack grew too big.
478     StackFrameLimitReached,
479     /// The program ran for too long.
480     ///
481     /// The exact limit is set by the `const_eval_limit` attribute.
482     StepLimitReached,
483 }
484
485 impl fmt::Debug for ResourceExhaustionInfo {
486     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
487         use ResourceExhaustionInfo::*;
488         match self {
489             StackFrameLimitReached => {
490                 write!(f, "reached the configured maximum number of stack frames")
491             }
492             StepLimitReached => {
493                 write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)")
494             }
495         }
496     }
497 }
498
499 /// A trait to work around not having trait object upcasting.
500 pub trait AsAny: Any {
501     fn as_any(&self) -> &dyn Any;
502 }
503
504 impl<T: Any> AsAny for T {
505     #[inline(always)]
506     fn as_any(&self) -> &dyn Any {
507         self
508     }
509 }
510
511 /// A trait for machine-specific errors (or other "machine stop" conditions).
512 pub trait MachineStopType: AsAny + fmt::Debug + Send {}
513 impl MachineStopType for String {}
514
515 impl dyn MachineStopType {
516     #[inline(always)]
517     pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
518         self.as_any().downcast_ref()
519     }
520 }
521
522 pub enum InterpError<'tcx> {
523     /// The program caused undefined behavior.
524     UndefinedBehavior(UndefinedBehaviorInfo),
525     /// The program did something the interpreter does not support (some of these *might* be UB
526     /// but the interpreter is not sure).
527     Unsupported(UnsupportedOpInfo),
528     /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...).
529     InvalidProgram(InvalidProgramInfo<'tcx>),
530     /// The program exhausted the interpreter's resources (stack/heap too big,
531     /// execution takes too long, ...).
532     ResourceExhaustion(ResourceExhaustionInfo),
533     /// Stop execution for a machine-controlled reason. This is never raised by
534     /// the core engine itself.
535     MachineStop(Box<dyn MachineStopType>),
536 }
537
538 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
539
540 impl fmt::Display for InterpError<'_> {
541     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
542         // Forward `Display` to `Debug`.
543         fmt::Debug::fmt(self, f)
544     }
545 }
546
547 impl fmt::Debug for InterpError<'_> {
548     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
549         use InterpError::*;
550         match *self {
551             Unsupported(ref msg) => write!(f, "{:?}", msg),
552             InvalidProgram(ref msg) => write!(f, "{:?}", msg),
553             UndefinedBehavior(ref msg) => write!(f, "{:?}", msg),
554             ResourceExhaustion(ref msg) => write!(f, "{:?}", msg),
555             MachineStop(ref msg) => write!(f, "{:?}", msg),
556         }
557     }
558 }
559
560 impl InterpError<'_> {
561     /// Some errors allocate to be created as they contain free-form strings.
562     /// And sometimes we want to be sure that did not happen as it is a
563     /// waste of resources.
564     pub fn allocates(&self) -> bool {
565         match self {
566             // Zero-sized boxes do not allocate.
567             InterpError::MachineStop(b) => mem::size_of_val::<dyn MachineStopType>(&**b) > 0,
568             InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
569             | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure(_))
570             | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) => true,
571             _ => false,
572         }
573     }
574 }