]> git.lizzy.rs Git - rust.git/blob - src/librustc/mir/interpret/error.rs
Rollup merge of #61181 - GuillaumeGomez:fix-theme-checker, r=kinnison
[rust.git] / src / librustc / mir / interpret / error.rs
1 use std::{fmt, env};
2
3 use crate::hir;
4 use crate::hir::map::definitions::DefPathData;
5 use crate::mir;
6 use crate::ty::{self, Ty, layout};
7 use crate::ty::layout::{Size, Align, LayoutError};
8 use rustc_target::spec::abi::Abi;
9 use rustc_macros::HashStable;
10
11 use super::{RawConst, Pointer, CheckInAllocMsg, ScalarMaybeUndef};
12
13 use backtrace::Backtrace;
14
15 use crate::ty::query::TyCtxtAt;
16 use errors::DiagnosticBuilder;
17
18 use syntax_pos::{Pos, Span};
19 use syntax::symbol::Symbol;
20
21 #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable)]
22 pub enum ErrorHandled {
23     /// Already reported a lint or an error for this evaluation.
24     Reported,
25     /// Don't emit an error, the evaluation failed because the MIR was generic
26     /// and the substs didn't fully monomorphize it.
27     TooGeneric,
28 }
29
30 impl ErrorHandled {
31     pub fn assert_reported(self) {
32         match self {
33             ErrorHandled::Reported => {},
34             ErrorHandled::TooGeneric => bug!("MIR interpretation failed without reporting an error \
35                                               even though it was fully monomorphized"),
36         }
37     }
38 }
39
40 pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
41 pub type ConstEvalResult<'tcx> = Result<&'tcx ty::Const<'tcx>, ErrorHandled>;
42
43 #[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
44 pub struct ConstEvalErr<'tcx> {
45     pub span: Span,
46     pub error: crate::mir::interpret::InterpError<'tcx, u64>,
47     pub stacktrace: Vec<FrameInfo<'tcx>>,
48 }
49
50 #[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
51 pub struct FrameInfo<'tcx> {
52     /// This span is in the caller.
53     pub call_site: Span,
54     pub instance: ty::Instance<'tcx>,
55     pub lint_root: Option<hir::HirId>,
56 }
57
58 impl<'tcx> fmt::Display for FrameInfo<'tcx> {
59     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60         ty::tls::with(|tcx| {
61             if tcx.def_key(self.instance.def_id()).disambiguated_data.data
62                 == DefPathData::ClosureExpr
63             {
64                 write!(f, "inside call to closure")?;
65             } else {
66                 write!(f, "inside call to `{}`", self.instance)?;
67             }
68             if !self.call_site.is_dummy() {
69                 let lo = tcx.sess.source_map().lookup_char_pos(self.call_site.lo());
70                 write!(f, " at {}:{}:{}", lo.file.name, lo.line, lo.col.to_usize() + 1)?;
71             }
72             Ok(())
73         })
74     }
75 }
76
77 impl<'tcx> ConstEvalErr<'tcx> {
78     pub fn struct_error(
79         &self,
80         tcx: TyCtxtAt<'tcx>,
81         message: &str,
82     ) -> Result<DiagnosticBuilder<'tcx>, ErrorHandled> {
83         self.struct_generic(tcx, message, None)
84     }
85
86     pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
87         let err = self.struct_error(tcx, message);
88         match err {
89             Ok(mut err) => {
90                 err.emit();
91                 ErrorHandled::Reported
92             },
93             Err(err) => err,
94         }
95     }
96
97     pub fn report_as_lint(
98         &self,
99         tcx: TyCtxtAt<'tcx>,
100         message: &str,
101         lint_root: hir::HirId,
102         span: Option<Span>,
103     ) -> ErrorHandled {
104         let lint = self.struct_generic(
105             tcx,
106             message,
107             Some(lint_root),
108         );
109         match lint {
110             Ok(mut lint) => {
111                 if let Some(span) = span {
112                     let primary_spans = lint.span.primary_spans().to_vec();
113                     // point at the actual error as the primary span
114                     lint.replace_span_with(span);
115                     // point to the `const` statement as a secondary span
116                     // they don't have any label
117                     for sp in primary_spans {
118                         if sp != span {
119                             lint.span_label(sp, "");
120                         }
121                     }
122                 }
123                 lint.emit();
124                 ErrorHandled::Reported
125             },
126             Err(err) => err,
127         }
128     }
129
130     fn struct_generic(
131         &self,
132         tcx: TyCtxtAt<'tcx>,
133         message: &str,
134         lint_root: Option<hir::HirId>,
135     ) -> Result<DiagnosticBuilder<'tcx>, ErrorHandled> {
136         match self.error {
137             InterpError::Layout(LayoutError::Unknown(_)) |
138             InterpError::TooGeneric => return Err(ErrorHandled::TooGeneric),
139             InterpError::Layout(LayoutError::SizeOverflow(_)) |
140             InterpError::TypeckError => return Err(ErrorHandled::Reported),
141             _ => {},
142         }
143         trace!("reporting const eval failure at {:?}", self.span);
144         let mut err = if let Some(lint_root) = lint_root {
145             let hir_id = self.stacktrace
146                 .iter()
147                 .rev()
148                 .filter_map(|frame| frame.lint_root)
149                 .next()
150                 .unwrap_or(lint_root);
151             tcx.struct_span_lint_hir(
152                 crate::rustc::lint::builtin::CONST_ERR,
153                 hir_id,
154                 tcx.span,
155                 message,
156             )
157         } else {
158             struct_error(tcx, message)
159         };
160         err.span_label(self.span, self.error.to_string());
161         // Skip the last, which is just the environment of the constant.  The stacktrace
162         // is sometimes empty because we create "fake" eval contexts in CTFE to do work
163         // on constant values.
164         if self.stacktrace.len() > 0 {
165             for frame_info in &self.stacktrace[..self.stacktrace.len()-1] {
166                 err.span_label(frame_info.call_site, frame_info.to_string());
167             }
168         }
169         Ok(err)
170     }
171 }
172
173 pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
174     struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
175 }
176
177 /// Packages the kind of error we got from the const code interpreter
178 /// up with a Rust-level backtrace of where the error occured.
179 /// Thsese should always be constructed by calling `.into()` on
180 /// a `InterpError`. In `librustc_mir::interpret`, we have the `err!`
181 /// macro for this
182 #[derive(Debug, Clone)]
183 pub struct InterpErrorInfo<'tcx> {
184     pub kind: InterpError<'tcx, u64>,
185     backtrace: Option<Box<Backtrace>>,
186 }
187
188 impl<'tcx> InterpErrorInfo<'tcx> {
189     pub fn print_backtrace(&mut self) {
190         if let Some(ref mut backtrace) = self.backtrace {
191             print_backtrace(&mut *backtrace);
192         }
193     }
194 }
195
196 fn print_backtrace(backtrace: &mut Backtrace) {
197     backtrace.resolve();
198     eprintln!("\n\nAn error occurred in miri:\n{:?}", backtrace);
199 }
200
201 impl<'tcx> From<InterpError<'tcx, u64>> for InterpErrorInfo<'tcx> {
202     fn from(kind: InterpError<'tcx, u64>) -> Self {
203         let backtrace = match env::var("RUST_CTFE_BACKTRACE") {
204             // Matching `RUST_BACKTRACE` -- we treat "0" the same as "not present".
205             Ok(ref val) if val != "0" => {
206                 let mut backtrace = Backtrace::new_unresolved();
207
208                 if val == "immediate" {
209                     // Print it now.
210                     print_backtrace(&mut backtrace);
211                     None
212                 } else {
213                     Some(Box::new(backtrace))
214                 }
215             },
216             _ => None,
217         };
218         InterpErrorInfo {
219             kind,
220             backtrace,
221         }
222     }
223 }
224
225 pub type AssertMessage<'tcx> = InterpError<'tcx, mir::Operand<'tcx>>;
226
227 #[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
228 pub enum InterpError<'tcx, O> {
229     /// This variant is used by machines to signal their own errors that do not
230     /// match an existing variant.
231     MachineError(String),
232
233     /// Not actually an interpreter error -- used to signal that execution has exited
234     /// with the given status code.  Used by Miri, but not by CTFE.
235     Exit(i32),
236
237     FunctionAbiMismatch(Abi, Abi),
238     FunctionArgMismatch(Ty<'tcx>, Ty<'tcx>),
239     FunctionRetMismatch(Ty<'tcx>, Ty<'tcx>),
240     FunctionArgCountMismatch,
241     NoMirFor(String),
242     UnterminatedCString(Pointer),
243     DanglingPointerDeref,
244     DoubleFree,
245     InvalidMemoryAccess,
246     InvalidFunctionPointer,
247     InvalidBool,
248     InvalidDiscriminant(ScalarMaybeUndef),
249     PointerOutOfBounds {
250         ptr: Pointer,
251         msg: CheckInAllocMsg,
252         allocation_size: Size,
253     },
254     InvalidNullPointerUsage,
255     ReadPointerAsBytes,
256     ReadBytesAsPointer,
257     ReadForeignStatic,
258     InvalidPointerMath,
259     ReadUndefBytes(Size),
260     DeadLocal,
261     InvalidBoolOp(mir::BinOp),
262     Unimplemented(String),
263     DerefFunctionPointer,
264     ExecuteMemory,
265     BoundsCheck { len: O, index: O },
266     Overflow(mir::BinOp),
267     OverflowNeg,
268     DivisionByZero,
269     RemainderByZero,
270     Intrinsic(String),
271     InvalidChar(u128),
272     StackFrameLimitReached,
273     OutOfTls,
274     TlsOutOfBounds,
275     AbiViolation(String),
276     AlignmentCheckFailed {
277         required: Align,
278         has: Align,
279     },
280     ValidationFailure(String),
281     CalledClosureAsFunction,
282     VtableForArgumentlessMethod,
283     ModifiedConstantMemory,
284     ModifiedStatic,
285     AssumptionNotHeld,
286     InlineAsm,
287     TypeNotPrimitive(Ty<'tcx>),
288     ReallocatedWrongMemoryKind(String, String),
289     DeallocatedWrongMemoryKind(String, String),
290     ReallocateNonBasePtr,
291     DeallocateNonBasePtr,
292     IncorrectAllocationInformation(Size, Size, Align, Align),
293     Layout(layout::LayoutError<'tcx>),
294     HeapAllocZeroBytes,
295     HeapAllocNonPowerOfTwoAlignment(u64),
296     Unreachable,
297     Panic {
298         msg: Symbol,
299         line: u32,
300         col: u32,
301         file: Symbol,
302     },
303     ReadFromReturnPointer,
304     PathNotFound(Vec<String>),
305     UnimplementedTraitSelection,
306     /// Abort in case type errors are reached
307     TypeckError,
308     /// Resolution can fail if we are in a too generic context
309     TooGeneric,
310     /// Cannot compute this constant because it depends on another one
311     /// which already produced an error
312     ReferencedConstant,
313     GeneratorResumedAfterReturn,
314     GeneratorResumedAfterPanic,
315     InfiniteLoop,
316 }
317
318 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
319
320 impl<'tcx, O> InterpError<'tcx, O> {
321     pub fn description(&self) -> &str {
322         use self::InterpError::*;
323         match *self {
324             MachineError(ref inner) => inner,
325             Exit(..) =>
326                 "exited",
327             FunctionAbiMismatch(..) | FunctionArgMismatch(..) | FunctionRetMismatch(..)
328             | FunctionArgCountMismatch =>
329                 "tried to call a function through a function pointer of incompatible type",
330             InvalidMemoryAccess =>
331                 "tried to access memory through an invalid pointer",
332             DanglingPointerDeref =>
333                 "dangling pointer was dereferenced",
334             DoubleFree =>
335                 "tried to deallocate dangling pointer",
336             InvalidFunctionPointer =>
337                 "tried to use a function pointer after offsetting it",
338             InvalidBool =>
339                 "invalid boolean value read",
340             InvalidDiscriminant(..) =>
341                 "invalid enum discriminant value read",
342             PointerOutOfBounds { .. } =>
343                 "pointer offset outside bounds of allocation",
344             InvalidNullPointerUsage =>
345                 "invalid use of NULL pointer",
346             ValidationFailure(..) =>
347                 "type validation failed",
348             ReadPointerAsBytes =>
349                 "a raw memory access tried to access part of a pointer value as raw bytes",
350             ReadBytesAsPointer =>
351                 "a memory access tried to interpret some bytes as a pointer",
352             ReadForeignStatic =>
353                 "tried to read from foreign (extern) static",
354             InvalidPointerMath =>
355                 "attempted to do invalid arithmetic on pointers that would leak base addresses, \
356                 e.g., comparing pointers into different allocations",
357             ReadUndefBytes(_) =>
358                 "attempted to read undefined bytes",
359             DeadLocal =>
360                 "tried to access a dead local variable",
361             InvalidBoolOp(_) =>
362                 "invalid boolean operation",
363             Unimplemented(ref msg) => msg,
364             DerefFunctionPointer =>
365                 "tried to dereference a function pointer",
366             ExecuteMemory =>
367                 "tried to treat a memory pointer as a function pointer",
368             BoundsCheck{..} =>
369                 "array index out of bounds",
370             Intrinsic(..) =>
371                 "intrinsic failed",
372             NoMirFor(..) =>
373                 "mir not found",
374             InvalidChar(..) =>
375                 "tried to interpret an invalid 32-bit value as a char",
376             StackFrameLimitReached =>
377                 "reached the configured maximum number of stack frames",
378             OutOfTls =>
379                 "reached the maximum number of representable TLS keys",
380             TlsOutOfBounds =>
381                 "accessed an invalid (unallocated) TLS key",
382             AbiViolation(ref msg) => msg,
383             AlignmentCheckFailed{..} =>
384                 "tried to execute a misaligned read or write",
385             CalledClosureAsFunction =>
386                 "tried to call a closure through a function pointer",
387             VtableForArgumentlessMethod =>
388                 "tried to call a vtable function without arguments",
389             ModifiedConstantMemory =>
390                 "tried to modify constant memory",
391             ModifiedStatic =>
392                 "tried to modify a static's initial value from another static's initializer",
393             AssumptionNotHeld =>
394                 "`assume` argument was false",
395             InlineAsm =>
396                 "miri does not support inline assembly",
397             TypeNotPrimitive(_) =>
398                 "expected primitive type, got nonprimitive",
399             ReallocatedWrongMemoryKind(_, _) =>
400                 "tried to reallocate memory from one kind to another",
401             DeallocatedWrongMemoryKind(_, _) =>
402                 "tried to deallocate memory of the wrong kind",
403             ReallocateNonBasePtr =>
404                 "tried to reallocate with a pointer not to the beginning of an existing object",
405             DeallocateNonBasePtr =>
406                 "tried to deallocate with a pointer not to the beginning of an existing object",
407             IncorrectAllocationInformation(..) =>
408                 "tried to deallocate or reallocate using incorrect alignment or size",
409             Layout(_) =>
410                 "rustc layout computation failed",
411             UnterminatedCString(_) =>
412                 "attempted to get length of a null terminated string, but no null found before end \
413                 of allocation",
414             HeapAllocZeroBytes =>
415                 "tried to re-, de- or allocate zero bytes on the heap",
416             HeapAllocNonPowerOfTwoAlignment(_) =>
417                 "tried to re-, de-, or allocate heap memory with alignment that is not a power of \
418                 two",
419             Unreachable =>
420                 "entered unreachable code",
421             Panic { .. } =>
422                 "the evaluated program panicked",
423             ReadFromReturnPointer =>
424                 "tried to read from the return pointer",
425             PathNotFound(_) =>
426                 "a path could not be resolved, maybe the crate is not loaded",
427             UnimplementedTraitSelection =>
428                 "there were unresolved type arguments during trait selection",
429             TypeckError =>
430                 "encountered constants with type errors, stopping evaluation",
431             TooGeneric =>
432                 "encountered overly generic constant",
433             ReferencedConstant =>
434                 "referenced constant has errors",
435             Overflow(mir::BinOp::Add) => "attempt to add with overflow",
436             Overflow(mir::BinOp::Sub) => "attempt to subtract with overflow",
437             Overflow(mir::BinOp::Mul) => "attempt to multiply with overflow",
438             Overflow(mir::BinOp::Div) => "attempt to divide with overflow",
439             Overflow(mir::BinOp::Rem) => "attempt to calculate the remainder with overflow",
440             OverflowNeg => "attempt to negate with overflow",
441             Overflow(mir::BinOp::Shr) => "attempt to shift right with overflow",
442             Overflow(mir::BinOp::Shl) => "attempt to shift left with overflow",
443             Overflow(op) => bug!("{:?} cannot overflow", op),
444             DivisionByZero => "attempt to divide by zero",
445             RemainderByZero => "attempt to calculate the remainder with a divisor of zero",
446             GeneratorResumedAfterReturn => "generator resumed after completion",
447             GeneratorResumedAfterPanic => "generator resumed after panicking",
448             InfiniteLoop =>
449                 "duplicate interpreter state observed here, const evaluation will never terminate",
450         }
451     }
452 }
453
454 impl<'tcx> fmt::Display for InterpErrorInfo<'tcx> {
455     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
456         write!(f, "{}", self.kind)
457     }
458 }
459
460 impl<'tcx> fmt::Display for InterpError<'tcx, u64> {
461     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
462         write!(f, "{:?}", self)
463     }
464 }
465
466 impl<'tcx, O: fmt::Debug> fmt::Debug for InterpError<'tcx, O> {
467     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
468         use self::InterpError::*;
469         match *self {
470             PointerOutOfBounds { ptr, msg, allocation_size } => {
471                 write!(f, "{} failed: pointer must be in-bounds at offset {}, \
472                           but is outside bounds of allocation {} which has size {}",
473                     msg, ptr.offset.bytes(), ptr.alloc_id, allocation_size.bytes())
474             },
475             ValidationFailure(ref err) => {
476                 write!(f, "type validation failed: {}", err)
477             }
478             NoMirFor(ref func) => write!(f, "no mir for `{}`", func),
479             FunctionAbiMismatch(caller_abi, callee_abi) =>
480                 write!(f, "tried to call a function with ABI {:?} using caller ABI {:?}",
481                     callee_abi, caller_abi),
482             FunctionArgMismatch(caller_ty, callee_ty) =>
483                 write!(f, "tried to call a function with argument of type {:?} \
484                            passing data of type {:?}",
485                     callee_ty, caller_ty),
486             FunctionRetMismatch(caller_ty, callee_ty) =>
487                 write!(f, "tried to call a function with return type {:?} \
488                            passing return place of type {:?}",
489                     callee_ty, caller_ty),
490             FunctionArgCountMismatch =>
491                 write!(f, "tried to call a function with incorrect number of arguments"),
492             BoundsCheck { ref len, ref index } =>
493                 write!(f, "index out of bounds: the len is {:?} but the index is {:?}", len, index),
494             ReallocatedWrongMemoryKind(ref old, ref new) =>
495                 write!(f, "tried to reallocate memory from {} to {}", old, new),
496             DeallocatedWrongMemoryKind(ref old, ref new) =>
497                 write!(f, "tried to deallocate {} memory but gave {} as the kind", old, new),
498             Intrinsic(ref err) =>
499                 write!(f, "{}", err),
500             InvalidChar(c) =>
501                 write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c),
502             AlignmentCheckFailed { required, has } =>
503                write!(f, "tried to access memory with alignment {}, but alignment {} is required",
504                       has.bytes(), required.bytes()),
505             TypeNotPrimitive(ty) =>
506                 write!(f, "expected primitive type, got {}", ty),
507             Layout(ref err) =>
508                 write!(f, "rustc layout computation failed: {:?}", err),
509             PathNotFound(ref path) =>
510                 write!(f, "Cannot find path {:?}", path),
511             MachineError(ref inner) =>
512                 write!(f, "{}", inner),
513             IncorrectAllocationInformation(size, size2, align, align2) =>
514                 write!(f, "incorrect alloc info: expected size {} and align {}, \
515                            got size {} and align {}",
516                     size.bytes(), align.bytes(), size2.bytes(), align2.bytes()),
517             Panic { ref msg, line, col, ref file } =>
518                 write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col),
519             InvalidDiscriminant(val) =>
520                 write!(f, "encountered invalid enum discriminant {}", val),
521             Exit(code) =>
522                 write!(f, "exited with status code {}", code),
523             _ => write!(f, "{}", self.description()),
524         }
525     }
526 }