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