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