]> git.proxmox.com Git - rustc.git/blob - src/librustc_middle/mir/interpret/error.rs
New upstream version 1.47.0+dfsg1
[rustc.git] / src / librustc_middle / mir / interpret / error.rs
1 use super::{AllocId, Pointer, RawConst, Scalar};
2
3 use crate::mir::interpret::ConstValue;
4 use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty};
5
6 use rustc_data_structures::sync::Lock;
7 use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorReported};
8 use rustc_macros::HashStable;
9 use rustc_session::CtfeBacktrace;
10 use rustc_span::def_id::DefId;
11 use rustc_target::abi::{Align, Size};
12 use std::{any::Any, backtrace::Backtrace, fmt, mem};
13
14 #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
15 pub enum ErrorHandled {
16 /// Already reported an error for this evaluation, and the compilation is
17 /// *guaranteed* to fail. Warnings/lints *must not* produce `Reported`.
18 Reported(ErrorReported),
19 /// Already emitted a lint for this evaluation.
20 Linted,
21 /// Don't emit an error, the evaluation failed because the MIR was generic
22 /// and the substs didn't fully monomorphize it.
23 TooGeneric,
24 }
25
26 CloneTypeFoldableAndLiftImpls! {
27 ErrorHandled,
28 }
29
30 pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
31 pub type ConstEvalResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
32
33 pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
34 struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
35 }
36
37 /// Packages the kind of error we got from the const code interpreter
38 /// up with a Rust-level backtrace of where the error occurred.
39 /// Thsese should always be constructed by calling `.into()` on
40 /// a `InterpError`. In `librustc_mir::interpret`, we have `throw_err_*`
41 /// macros for this.
42 #[derive(Debug)]
43 pub struct InterpErrorInfo<'tcx> {
44 pub kind: InterpError<'tcx>,
45 backtrace: Option<Box<Backtrace>>,
46 }
47
48 impl fmt::Display for InterpErrorInfo<'_> {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 write!(f, "{}", self.kind)
51 }
52 }
53
54 impl InterpErrorInfo<'_> {
55 pub fn print_backtrace(&self) {
56 if let Some(backtrace) = self.backtrace.as_ref() {
57 print_backtrace(backtrace);
58 }
59 }
60 }
61
62 fn print_backtrace(backtrace: &Backtrace) {
63 eprintln!("\n\nAn error occurred in miri:\n{}", backtrace);
64 }
65
66 impl From<ErrorHandled> for InterpErrorInfo<'_> {
67 fn from(err: ErrorHandled) -> Self {
68 match err {
69 ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {
70 err_inval!(ReferencedConstant)
71 }
72 ErrorHandled::TooGeneric => err_inval!(TooGeneric),
73 }
74 .into()
75 }
76 }
77
78 impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
79 fn from(kind: InterpError<'tcx>) -> Self {
80 let capture_backtrace = tls::with_opt(|tcx| {
81 if let Some(tcx) = tcx {
82 *Lock::borrow(&tcx.sess.ctfe_backtrace)
83 } else {
84 CtfeBacktrace::Disabled
85 }
86 });
87
88 let backtrace = match capture_backtrace {
89 CtfeBacktrace::Disabled => None,
90 CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())),
91 CtfeBacktrace::Immediate => {
92 // Print it now.
93 let backtrace = Backtrace::force_capture();
94 print_backtrace(&backtrace);
95 None
96 }
97 };
98
99 InterpErrorInfo { kind, backtrace }
100 }
101 }
102
103 /// Error information for when the program we executed turned out not to actually be a valid
104 /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
105 /// where we work on generic code or execution does not have all information available.
106 pub enum InvalidProgramInfo<'tcx> {
107 /// Resolution can fail if we are in a too generic context.
108 TooGeneric,
109 /// Cannot compute this constant because it depends on another one
110 /// which already produced an error.
111 ReferencedConstant,
112 /// Abort in case type errors are reached.
113 TypeckError(ErrorReported),
114 /// An error occurred during layout computation.
115 Layout(layout::LayoutError<'tcx>),
116 /// An invalid transmute happened.
117 TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>),
118 }
119
120 impl fmt::Display for InvalidProgramInfo<'_> {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 use InvalidProgramInfo::*;
123 match self {
124 TooGeneric => write!(f, "encountered overly generic constant"),
125 ReferencedConstant => write!(f, "referenced constant has errors"),
126 TypeckError(ErrorReported) => {
127 write!(f, "encountered constants with type errors, stopping evaluation")
128 }
129 Layout(ref err) => write!(f, "{}", err),
130 TransmuteSizeDiff(from_ty, to_ty) => write!(
131 f,
132 "transmuting `{}` to `{}` is not possible, because these types do not have the same size",
133 from_ty, to_ty
134 ),
135 }
136 }
137 }
138
139 /// Details of why a pointer had to be in-bounds.
140 #[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
141 pub enum CheckInAllocMsg {
142 MemoryAccessTest,
143 NullPointerTest,
144 PointerArithmeticTest,
145 InboundsTest,
146 }
147
148 impl fmt::Display for CheckInAllocMsg {
149 /// When this is printed as an error the context looks like this
150 /// "{test name} failed: pointer must be in-bounds at offset..."
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 write!(
153 f,
154 "{}",
155 match *self {
156 CheckInAllocMsg::MemoryAccessTest => "memory access",
157 CheckInAllocMsg::NullPointerTest => "NULL pointer test",
158 CheckInAllocMsg::PointerArithmeticTest => "pointer arithmetic",
159 CheckInAllocMsg::InboundsTest => "inbounds test",
160 }
161 )
162 }
163 }
164
165 /// Details of an access to uninitialized bytes where it is not allowed.
166 #[derive(Debug)]
167 pub struct UninitBytesAccess {
168 /// Location of the original memory access.
169 pub access_ptr: Pointer,
170 /// Size of the original memory access.
171 pub access_size: Size,
172 /// Location of the first uninitialized byte that was accessed.
173 pub uninit_ptr: Pointer,
174 /// Number of consecutive uninitialized bytes that were accessed.
175 pub uninit_size: Size,
176 }
177
178 /// Error information for when the program caused Undefined Behavior.
179 pub enum UndefinedBehaviorInfo<'tcx> {
180 /// Free-form case. Only for errors that are never caught!
181 Ub(String),
182 /// Unreachable code was executed.
183 Unreachable,
184 /// A slice/array index projection went out-of-bounds.
185 BoundsCheckFailed {
186 len: u64,
187 index: u64,
188 },
189 /// Something was divided by 0 (x / 0).
190 DivisionByZero,
191 /// Something was "remainded" by 0 (x % 0).
192 RemainderByZero,
193 /// Overflowing inbounds pointer arithmetic.
194 PointerArithOverflow,
195 /// Invalid metadata in a wide pointer (using `str` to avoid allocations).
196 InvalidMeta(&'static str),
197 /// Invalid drop function in vtable.
198 InvalidDropFn(FnSig<'tcx>),
199 /// Reading a C string that does not end within its allocation.
200 UnterminatedCString(Pointer),
201 /// Dereferencing a dangling pointer after it got freed.
202 PointerUseAfterFree(AllocId),
203 /// Used a pointer outside the bounds it is valid for.
204 PointerOutOfBounds {
205 ptr: Pointer,
206 msg: CheckInAllocMsg,
207 allocation_size: Size,
208 },
209 /// Using an integer as a pointer in the wrong way.
210 DanglingIntPointer(u64, CheckInAllocMsg),
211 /// Used a pointer with bad alignment.
212 AlignmentCheckFailed {
213 required: Align,
214 has: Align,
215 },
216 /// Writing to read-only memory.
217 WriteToReadOnly(AllocId),
218 // Trying to access the data behind a function pointer.
219 DerefFunctionPointer(AllocId),
220 /// The value validity check found a problem.
221 /// Should only be thrown by `validity.rs` and always point out which part of the value
222 /// is the problem.
223 ValidationFailure(String),
224 /// Using a non-boolean `u8` as bool.
225 InvalidBool(u8),
226 /// Using a non-character `u32` as character.
227 InvalidChar(u32),
228 /// The tag of an enum does not encode an actual discriminant.
229 InvalidTag(Scalar),
230 /// Using a pointer-not-to-a-function as function pointer.
231 InvalidFunctionPointer(Pointer),
232 /// Using a string that is not valid UTF-8,
233 InvalidStr(std::str::Utf8Error),
234 /// Using uninitialized data where it is not allowed.
235 InvalidUninitBytes(Option<Box<UninitBytesAccess>>),
236 /// Working with a local that is not currently live.
237 DeadLocal,
238 /// Data size is not equal to target size.
239 ScalarSizeMismatch {
240 target_size: u64,
241 data_size: u64,
242 },
243 }
244
245 impl fmt::Display for UndefinedBehaviorInfo<'_> {
246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247 use UndefinedBehaviorInfo::*;
248 match self {
249 Ub(msg) => write!(f, "{}", msg),
250 Unreachable => write!(f, "entering unreachable code"),
251 BoundsCheckFailed { ref len, ref index } => {
252 write!(f, "indexing out of bounds: the len is {} but the index is {}", len, index)
253 }
254 DivisionByZero => write!(f, "dividing by zero"),
255 RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
256 PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
257 InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg),
258 InvalidDropFn(sig) => write!(
259 f,
260 "invalid drop function signature: got {}, expected exactly one argument which must be a pointer type",
261 sig
262 ),
263 UnterminatedCString(p) => write!(
264 f,
265 "reading a null-terminated string starting at {} with no null found before end of allocation",
266 p,
267 ),
268 PointerUseAfterFree(a) => {
269 write!(f, "pointer to {} was dereferenced after this allocation got freed", a)
270 }
271 PointerOutOfBounds { ptr, msg, allocation_size } => write!(
272 f,
273 "{} failed: pointer must be in-bounds at offset {}, \
274 but is outside bounds of {} which has size {}",
275 msg,
276 ptr.offset.bytes(),
277 ptr.alloc_id,
278 allocation_size.bytes()
279 ),
280 DanglingIntPointer(_, CheckInAllocMsg::NullPointerTest) => {
281 write!(f, "NULL pointer is not allowed for this operation")
282 }
283 DanglingIntPointer(i, msg) => {
284 write!(f, "{} failed: 0x{:x} is not a valid pointer", msg, i)
285 }
286 AlignmentCheckFailed { required, has } => write!(
287 f,
288 "accessing memory with alignment {}, but alignment {} is required",
289 has.bytes(),
290 required.bytes()
291 ),
292 WriteToReadOnly(a) => write!(f, "writing to {} which is read-only", a),
293 DerefFunctionPointer(a) => write!(f, "accessing {} which contains a function", a),
294 ValidationFailure(ref err) => write!(f, "type validation failed: {}", err),
295 InvalidBool(b) => {
296 write!(f, "interpreting an invalid 8-bit value as a bool: 0x{:02x}", b)
297 }
298 InvalidChar(c) => {
299 write!(f, "interpreting an invalid 32-bit value as a char: 0x{:08x}", c)
300 }
301 InvalidTag(val) => write!(f, "enum value has invalid tag: {}", val),
302 InvalidFunctionPointer(p) => {
303 write!(f, "using {} as function pointer but it does not point to a function", p)
304 }
305 InvalidStr(err) => write!(f, "this string is not valid UTF-8: {}", err),
306 InvalidUninitBytes(Some(access)) => write!(
307 f,
308 "reading {} byte{} of memory starting at {}, \
309 but {} byte{} {} uninitialized starting at {}, \
310 and this operation requires initialized memory",
311 access.access_size.bytes(),
312 pluralize!(access.access_size.bytes()),
313 access.access_ptr,
314 access.uninit_size.bytes(),
315 pluralize!(access.uninit_size.bytes()),
316 if access.uninit_size.bytes() != 1 { "are" } else { "is" },
317 access.uninit_ptr,
318 ),
319 InvalidUninitBytes(None) => write!(
320 f,
321 "using uninitialized data, but this operation requires initialized memory"
322 ),
323 DeadLocal => write!(f, "accessing a dead local variable"),
324 ScalarSizeMismatch { target_size, data_size } => write!(
325 f,
326 "scalar size mismatch: expected {} bytes but got {} bytes instead",
327 target_size, data_size
328 ),
329 }
330 }
331 }
332
333 /// Error information for when the program did something that might (or might not) be correct
334 /// to do according to the Rust spec, but due to limitations in the interpreter, the
335 /// operation could not be carried out. These limitations can differ between CTFE and the
336 /// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses.
337 pub enum UnsupportedOpInfo {
338 /// Free-form case. Only for errors that are never caught!
339 Unsupported(String),
340 /// Could not find MIR for a function.
341 NoMirFor(DefId),
342 /// Encountered a pointer where we needed raw bytes.
343 ReadPointerAsBytes,
344 //
345 // The variants below are only reachable from CTFE/const prop, miri will never emit them.
346 //
347 /// Encountered raw bytes where we needed a pointer.
348 ReadBytesAsPointer,
349 /// Accessing thread local statics
350 ThreadLocalStatic(DefId),
351 /// Accessing an unsupported extern static.
352 ReadExternStatic(DefId),
353 }
354
355 impl fmt::Display for UnsupportedOpInfo {
356 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
357 use UnsupportedOpInfo::*;
358 match self {
359 Unsupported(ref msg) => write!(f, "{}", msg),
360 ReadExternStatic(did) => write!(f, "cannot read from extern static ({:?})", did),
361 NoMirFor(did) => write!(f, "no MIR body is available for {:?}", did),
362 ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes",),
363 ReadBytesAsPointer => write!(f, "unable to turn bytes into a pointer"),
364 ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({:?})", did),
365 }
366 }
367 }
368
369 /// Error information for when the program exhausted the resources granted to it
370 /// by the interpreter.
371 pub enum ResourceExhaustionInfo {
372 /// The stack grew too big.
373 StackFrameLimitReached,
374 /// The program ran for too long.
375 ///
376 /// The exact limit is set by the `const_eval_limit` attribute.
377 StepLimitReached,
378 }
379
380 impl fmt::Display for ResourceExhaustionInfo {
381 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
382 use ResourceExhaustionInfo::*;
383 match self {
384 StackFrameLimitReached => {
385 write!(f, "reached the configured maximum number of stack frames")
386 }
387 StepLimitReached => {
388 write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)")
389 }
390 }
391 }
392 }
393
394 /// A trait to work around not having trait object upcasting.
395 pub trait AsAny: Any {
396 fn as_any(&self) -> &dyn Any;
397 }
398 impl<T: Any> AsAny for T {
399 #[inline(always)]
400 fn as_any(&self) -> &dyn Any {
401 self
402 }
403 }
404
405 /// A trait for machine-specific errors (or other "machine stop" conditions).
406 pub trait MachineStopType: AsAny + fmt::Display + Send {}
407 impl MachineStopType for String {}
408
409 impl dyn MachineStopType {
410 #[inline(always)]
411 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
412 self.as_any().downcast_ref()
413 }
414 }
415
416 #[cfg(target_arch = "x86_64")]
417 static_assert_size!(InterpError<'_>, 40);
418
419 pub enum InterpError<'tcx> {
420 /// The program caused undefined behavior.
421 UndefinedBehavior(UndefinedBehaviorInfo<'tcx>),
422 /// The program did something the interpreter does not support (some of these *might* be UB
423 /// but the interpreter is not sure).
424 Unsupported(UnsupportedOpInfo),
425 /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...).
426 InvalidProgram(InvalidProgramInfo<'tcx>),
427 /// The program exhausted the interpreter's resources (stack/heap too big,
428 /// execution takes too long, ...).
429 ResourceExhaustion(ResourceExhaustionInfo),
430 /// Stop execution for a machine-controlled reason. This is never raised by
431 /// the core engine itself.
432 MachineStop(Box<dyn MachineStopType>),
433 }
434
435 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
436
437 impl fmt::Display for InterpError<'_> {
438 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
439 use InterpError::*;
440 match *self {
441 Unsupported(ref msg) => write!(f, "{}", msg),
442 InvalidProgram(ref msg) => write!(f, "{}", msg),
443 UndefinedBehavior(ref msg) => write!(f, "{}", msg),
444 ResourceExhaustion(ref msg) => write!(f, "{}", msg),
445 MachineStop(ref msg) => write!(f, "{}", msg),
446 }
447 }
448 }
449
450 // Forward `Debug` to `Display`, so it does not look awful.
451 impl fmt::Debug for InterpError<'_> {
452 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
453 fmt::Display::fmt(self, f)
454 }
455 }
456
457 impl InterpError<'_> {
458 /// Some errors allocate to be created as they contain free-form strings.
459 /// And sometimes we want to be sure that did not happen as it is a
460 /// waste of resources.
461 pub fn allocates(&self) -> bool {
462 match self {
463 // Zero-sized boxes do not allocate.
464 InterpError::MachineStop(b) => mem::size_of_val::<dyn MachineStopType>(&**b) > 0,
465 InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
466 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure(_))
467 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
468 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some(_))) => {
469 true
470 }
471 _ => false,
472 }
473 }
474 }