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