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