]> git.lizzy.rs Git - rust.git/blob - src/librustc/mir/interpret/error.rs
7108f69d39ed47105bffc3fde2c21475f71fdf08
[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::InvalidProgram(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 `throw_err_*`
188 /// macros 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 PanicInfo<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> = PanicInfo<mir::Operand<'tcx>>;
261
262 impl<O> PanicInfo<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 PanicInfo::*;
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 PanicInfo"),
297         }
298     }
299 }
300
301 impl<O: fmt::Debug> fmt::Debug for PanicInfo<O> {
302     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303         use PanicInfo::*;
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     /// An error occurred during layout computation.
325     Layout(layout::LayoutError<'tcx>),
326 }
327
328 impl fmt::Debug for InvalidProgramInfo<'tcx> {
329     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330         use InvalidProgramInfo::*;
331         match self {
332             TooGeneric =>
333                 write!(f, "encountered overly generic constant"),
334             ReferencedConstant =>
335                 write!(f, "referenced constant has errors"),
336             TypeckError =>
337                 write!(f, "encountered constants with type errors, stopping evaluation"),
338             Layout(ref err) =>
339                 write!(f, "rustc layout computation failed: {:?}", err),
340         }
341     }
342 }
343
344 #[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
345 pub enum UndefinedBehaviourInfo {
346     /// Handle cases which for which we do not have a fixed variant.
347     Ub(String),
348     /// Unreachable code was executed.
349     Unreachable,
350 }
351
352 impl fmt::Debug for UndefinedBehaviourInfo {
353     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354         use UndefinedBehaviourInfo::*;
355         match self {
356             Ub(ref msg) =>
357                 write!(f, "{}", msg),
358             Unreachable =>
359                 write!(f, "entered unreachable code"),
360         }
361     }
362 }
363
364 #[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
365 pub enum UnsupportedOpInfo<'tcx> {
366     /// Handle cases which for which we do not have a fixed variant.
367     Unimplemented(String),
368
369     // -- Everything below is not classified yet --
370     FunctionAbiMismatch(Abi, Abi),
371     FunctionArgMismatch(Ty<'tcx>, Ty<'tcx>),
372     FunctionRetMismatch(Ty<'tcx>, Ty<'tcx>),
373     FunctionArgCountMismatch,
374     UnterminatedCString(Pointer),
375     DanglingPointerDeref,
376     DoubleFree,
377     InvalidMemoryAccess,
378     InvalidFunctionPointer,
379     InvalidBool,
380     InvalidDiscriminant(ScalarMaybeUndef),
381     PointerOutOfBounds {
382         ptr: Pointer,
383         msg: CheckInAllocMsg,
384         allocation_size: Size,
385     },
386     InvalidNullPointerUsage,
387     ReadPointerAsBytes,
388     ReadBytesAsPointer,
389     ReadForeignStatic,
390     InvalidPointerMath,
391     ReadUndefBytes(Size),
392     DeadLocal,
393     InvalidBoolOp(mir::BinOp),
394     InlineAsm,
395     UnimplementedTraitSelection,
396     CalledClosureAsFunction,
397     NoMirFor(String),
398     /// This variant is used by machines to signal their own errors that do not
399     /// match an existing variant.
400     MachineError(String),
401     DerefFunctionPointer,
402     ExecuteMemory,
403     Intrinsic(String),
404     InvalidChar(u128),
405     OutOfTls,
406     TlsOutOfBounds,
407     AbiViolation(String),
408     AlignmentCheckFailed {
409         required: Align,
410         has: Align,
411     },
412     ValidationFailure(String),
413     VtableForArgumentlessMethod,
414     ModifiedConstantMemory,
415     ModifiedStatic,
416     AssumptionNotHeld,
417     TypeNotPrimitive(Ty<'tcx>),
418     ReallocatedWrongMemoryKind(String, String),
419     DeallocatedWrongMemoryKind(String, String),
420     ReallocateNonBasePtr,
421     DeallocateNonBasePtr,
422     IncorrectAllocationInformation(Size, Size, Align, Align),
423     HeapAllocZeroBytes,
424     HeapAllocNonPowerOfTwoAlignment(u64),
425     ReadFromReturnPointer,
426     PathNotFound(Vec<String>),
427 }
428
429 impl fmt::Debug for UnsupportedOpInfo<'tcx> {
430     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
431         use UnsupportedOpInfo::*;
432         match self {
433             PointerOutOfBounds { ptr, msg, allocation_size } => {
434                 write!(f, "{} failed: pointer must be in-bounds at offset {}, \
435                           but is outside bounds of allocation {} which has size {}",
436                     msg, ptr.offset.bytes(), ptr.alloc_id, allocation_size.bytes())
437             },
438             ValidationFailure(ref err) => {
439                 write!(f, "type validation failed: {}", err)
440             }
441             NoMirFor(ref func) => write!(f, "no mir for `{}`", func),
442             FunctionAbiMismatch(caller_abi, callee_abi) =>
443                 write!(f, "tried to call a function with ABI {:?} using caller ABI {:?}",
444                     callee_abi, caller_abi),
445             FunctionArgMismatch(caller_ty, callee_ty) =>
446                 write!(f, "tried to call a function with argument of type {:?} \
447                            passing data of type {:?}",
448                     callee_ty, caller_ty),
449             FunctionRetMismatch(caller_ty, callee_ty) =>
450                 write!(f, "tried to call a function with return type {:?} \
451                            passing return place of type {:?}",
452                     callee_ty, caller_ty),
453             FunctionArgCountMismatch =>
454                 write!(f, "tried to call a function with incorrect number of arguments"),
455             ReallocatedWrongMemoryKind(ref old, ref new) =>
456                 write!(f, "tried to reallocate memory from {} to {}", old, new),
457             DeallocatedWrongMemoryKind(ref old, ref new) =>
458                 write!(f, "tried to deallocate {} memory but gave {} as the kind", old, new),
459             InvalidChar(c) =>
460                 write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c),
461             AlignmentCheckFailed { required, has } =>
462                write!(f, "tried to access memory with alignment {}, but alignment {} is required",
463                       has.bytes(), required.bytes()),
464             TypeNotPrimitive(ty) =>
465                 write!(f, "expected primitive type, got {}", ty),
466             PathNotFound(ref path) =>
467                 write!(f, "Cannot find path {:?}", path),
468             IncorrectAllocationInformation(size, size2, align, align2) =>
469                 write!(f, "incorrect alloc info: expected size {} and align {}, \
470                            got size {} and align {}",
471                     size.bytes(), align.bytes(), size2.bytes(), align2.bytes()),
472             InvalidDiscriminant(val) =>
473                 write!(f, "encountered invalid enum discriminant {}", val),
474             InvalidMemoryAccess =>
475                 write!(f, "tried to access memory through an invalid pointer"),
476             DanglingPointerDeref =>
477                 write!(f, "dangling pointer was dereferenced"),
478             DoubleFree =>
479                 write!(f, "tried to deallocate dangling pointer"),
480             InvalidFunctionPointer =>
481                 write!(f, "tried to use a function pointer after offsetting it"),
482             InvalidBool =>
483                 write!(f, "invalid boolean value read"),
484             InvalidNullPointerUsage =>
485                 write!(f, "invalid use of NULL pointer"),
486             ReadPointerAsBytes =>
487                 write!(f, "a raw memory access tried to access part of a pointer value as raw \
488                     bytes"),
489             ReadBytesAsPointer =>
490                 write!(f, "a memory access tried to interpret some bytes as a pointer"),
491             ReadForeignStatic =>
492                 write!(f, "tried to read from foreign (extern) static"),
493             InvalidPointerMath =>
494                 write!(f, "attempted to do invalid arithmetic on pointers that would leak base \
495                     addresses, e.g., comparing pointers into different allocations"),
496             DeadLocal =>
497                 write!(f, "tried to access a dead local variable"),
498             DerefFunctionPointer =>
499                 write!(f, "tried to dereference a function pointer"),
500             ExecuteMemory =>
501                 write!(f, "tried to treat a memory pointer as a function pointer"),
502             OutOfTls =>
503                 write!(f, "reached the maximum number of representable TLS keys"),
504             TlsOutOfBounds =>
505                 write!(f, "accessed an invalid (unallocated) TLS key"),
506             CalledClosureAsFunction =>
507                 write!(f, "tried to call a closure through a function pointer"),
508             VtableForArgumentlessMethod =>
509                 write!(f, "tried to call a vtable function without arguments"),
510             ModifiedConstantMemory =>
511                 write!(f, "tried to modify constant memory"),
512             ModifiedStatic =>
513                 write!(f, "tried to modify a static's initial value from another static's \
514                     initializer"),
515             AssumptionNotHeld =>
516                 write!(f, "`assume` argument was false"),
517             InlineAsm =>
518                 write!(f, "miri does not support inline assembly"),
519             ReallocateNonBasePtr =>
520                 write!(f, "tried to reallocate with a pointer not to the beginning of an \
521                     existing object"),
522             DeallocateNonBasePtr =>
523                 write!(f, "tried to deallocate with a pointer not to the beginning of an \
524                     existing object"),
525             HeapAllocZeroBytes =>
526                 write!(f, "tried to re-, de- or allocate zero bytes on the heap"),
527             ReadFromReturnPointer =>
528                 write!(f, "tried to read from the return pointer"),
529             UnimplementedTraitSelection =>
530                 write!(f, "there were unresolved type arguments during trait selection"),
531             InvalidBoolOp(_) =>
532                 write!(f, "invalid boolean operation"),
533             UnterminatedCString(_) =>
534                 write!(f, "attempted to get length of a null terminated string, but no null \
535                     found before end of allocation"),
536             ReadUndefBytes(_) =>
537                 write!(f, "attempted to read undefined bytes"),
538             HeapAllocNonPowerOfTwoAlignment(_) =>
539                 write!(f, "tried to re-, de-, or allocate heap memory with alignment that is \
540                     not a power of two"),
541             MachineError(ref msg) |
542             Unimplemented(ref msg) |
543             AbiViolation(ref msg) |
544             Intrinsic(ref msg) =>
545                 write!(f, "{}", msg),
546         }
547     }
548 }
549
550 #[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
551 pub enum ResourceExhaustionInfo {
552     StackFrameLimitReached,
553     InfiniteLoop,
554 }
555
556 impl fmt::Debug for ResourceExhaustionInfo {
557     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
558         use ResourceExhaustionInfo::*;
559         match self {
560             StackFrameLimitReached =>
561                 write!(f, "reached the configured maximum number of stack frames"),
562             InfiniteLoop =>
563                 write!(f, "duplicate interpreter state observed here, const evaluation will never \
564                     terminate"),
565         }
566     }
567 }
568
569 #[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
570 pub enum InterpError<'tcx> {
571     /// The program panicked.
572     Panic(PanicInfo<u64>),
573     /// The program caused undefined behavior.
574     UndefinedBehaviour(UndefinedBehaviourInfo),
575     /// The program did something the interpreter does not support (some of these *might* be UB
576     /// but the interpreter is not sure).
577     Unsupported(UnsupportedOpInfo<'tcx>),
578     /// The program was invalid (ill-typed, not sufficiently monomorphized, ...).
579     InvalidProgram(InvalidProgramInfo<'tcx>),
580     /// The program exhausted the interpreter's resources (stack/heap too big,
581     /// execution takes too long, ..).
582     ResourceExhaustion(ResourceExhaustionInfo),
583     /// Not actually an interpreter error -- used to signal that execution has exited
584     /// with the given status code.  Used by Miri, but not by CTFE.
585     Exit(i32),
586 }
587
588 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
589
590 impl fmt::Display for InterpError<'_> {
591     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
592         // Forward `Display` to `Debug`
593         write!(f, "{:?}", self)
594     }
595 }
596
597 impl fmt::Debug for InterpError<'_> {
598     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
599         use InterpError::*;
600         match *self {
601             Unsupported(ref msg) =>
602                 write!(f, "{:?}", msg),
603             InvalidProgram(ref msg) =>
604                 write!(f, "{:?}", msg),
605             UndefinedBehaviour(ref msg) =>
606                 write!(f, "{:?}", msg),
607             ResourceExhaustion(ref msg) =>
608                 write!(f, "{:?}", msg),
609             Panic(ref msg) =>
610                 write!(f, "{:?}", msg),
611             Exit(code) =>
612                 write!(f, "exited with status code {}", code),
613         }
614     }
615 }