Auto merge of #111677 - fee1-dead-contrib:rustc_const_eval-translatable, r=oli-obk,RalfJung
Use translatable diagnostics in `rustc_const_eval` This PR: * adds a `no_span` parameter to `note` / `help` attributes when using `Subdiagnostic` to allow adding notes/helps without using a span * has minor tweaks and changes to error messages
This commit is contained in:
commit
33c3d10128
93 changed files with 2385 additions and 1128 deletions
|
@ -1,3 +1,38 @@
|
|||
middle_adjust_for_foreign_abi_error =
|
||||
target architecture {$arch} does not support `extern {$abi}` ABI
|
||||
|
||||
middle_assert_async_resume_after_panic = `async fn` resumed after panicking
|
||||
|
||||
middle_assert_async_resume_after_return = `async fn` resumed after completion
|
||||
|
||||
middle_assert_divide_by_zero =
|
||||
attempt to divide `{$val}` by zero
|
||||
|
||||
middle_assert_generator_resume_after_panic = generator resumed after panicking
|
||||
|
||||
middle_assert_generator_resume_after_return = generator resumed after completion
|
||||
|
||||
middle_assert_misaligned_ptr_deref =
|
||||
misaligned pointer dereference: address must be a multiple of {$required} but is {$found}
|
||||
|
||||
middle_assert_op_overflow =
|
||||
attempt to compute `{$left} {$op} {$right}`, which would overflow
|
||||
|
||||
middle_assert_overflow_neg =
|
||||
attempt to negate `{$val}`, which would overflow
|
||||
|
||||
middle_assert_remainder_by_zero =
|
||||
attempt to calculate the remainder of `{$val}` with a divisor of zero
|
||||
|
||||
middle_assert_shl_overflow =
|
||||
attempt to shift left by `{$val}`, which would overflow
|
||||
|
||||
middle_assert_shr_overflow =
|
||||
attempt to shift right by `{$val}`, which would overflow
|
||||
|
||||
middle_bounds_check =
|
||||
index out of bounds: the length is {$len} but the index is {$index}
|
||||
|
||||
middle_cannot_be_normalized =
|
||||
unable to determine layout for `{$ty}` because `{$failure_ty}` cannot be normalized
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
|
||||
use rustc_errors::{DiagnosticArgValue, DiagnosticMessage};
|
||||
use rustc_macros::Diagnostic;
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
|
@ -88,3 +92,54 @@ pub(super) struct ConstNotUsedTraitAlias {
|
|||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
pub struct CustomSubdiagnostic<'a> {
|
||||
pub msg: fn() -> DiagnosticMessage,
|
||||
pub add_args:
|
||||
Box<dyn FnOnce(&mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>)) + 'a>,
|
||||
}
|
||||
|
||||
impl<'a> CustomSubdiagnostic<'a> {
|
||||
pub fn label(x: fn() -> DiagnosticMessage) -> Self {
|
||||
Self::label_and_then(x, |_| {})
|
||||
}
|
||||
pub fn label_and_then<
|
||||
F: FnOnce(&mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>)) + 'a,
|
||||
>(
|
||||
msg: fn() -> DiagnosticMessage,
|
||||
f: F,
|
||||
) -> Self {
|
||||
Self { msg, add_args: Box::new(move |x| f(x)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for CustomSubdiagnostic<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("CustomSubdiagnostic").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
pub enum LayoutError<'tcx> {
|
||||
#[diag(middle_unknown_layout)]
|
||||
Unknown { ty: Ty<'tcx> },
|
||||
|
||||
#[diag(middle_values_too_big)]
|
||||
Overflow { ty: Ty<'tcx> },
|
||||
|
||||
#[diag(middle_cannot_be_normalized)]
|
||||
NormalizationFailure { ty: Ty<'tcx>, failure_ty: String },
|
||||
|
||||
#[diag(middle_cycle)]
|
||||
Cycle,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(middle_adjust_for_foreign_abi_error)]
|
||||
pub struct UnsupportedFnAbi {
|
||||
pub arch: Symbol,
|
||||
pub abi: &'static str,
|
||||
}
|
||||
|
||||
/// Used by `rustc_const_eval`
|
||||
pub use crate::fluent_generated::middle_adjust_for_foreign_abi_error;
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#![feature(associated_type_bounds)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(control_flow_enum)]
|
||||
#![feature(trait_upcasting)]
|
||||
#![feature(trusted_step)]
|
||||
#![feature(try_blocks)]
|
||||
#![feature(try_reserve_kind)]
|
||||
|
@ -86,7 +87,7 @@ mod macros;
|
|||
|
||||
#[macro_use]
|
||||
pub mod arena;
|
||||
pub(crate) mod error;
|
||||
pub mod error;
|
||||
pub mod hir;
|
||||
pub mod infer;
|
||||
pub mod lint;
|
||||
|
|
|
@ -296,25 +296,13 @@ impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> {
|
|||
Allocation::from_bytes(slice, Align::ONE, Mutability::Not)
|
||||
}
|
||||
|
||||
/// Try to create an Allocation of `size` bytes, failing if there is not enough memory
|
||||
/// available to the compiler to do so.
|
||||
///
|
||||
/// If `panic_on_fail` is true, this will never return `Err`.
|
||||
pub fn uninit<'tcx>(size: Size, align: Align, panic_on_fail: bool) -> InterpResult<'tcx, Self> {
|
||||
let bytes = Bytes::zeroed(size, align).ok_or_else(|| {
|
||||
// This results in an error that can happen non-deterministically, since the memory
|
||||
// available to the compiler can change between runs. Normally queries are always
|
||||
// deterministic. However, we can be non-deterministic here because all uses of const
|
||||
// evaluation (including ConstProp!) will make compilation fail (via hard error
|
||||
// or ICE) upon encountering a `MemoryExhausted` error.
|
||||
if panic_on_fail {
|
||||
panic!("Allocation::uninit called with panic_on_fail had allocation failure")
|
||||
}
|
||||
ty::tls::with(|tcx| {
|
||||
tcx.sess.delay_span_bug(DUMMY_SP, "exhausted memory during interpretation")
|
||||
});
|
||||
InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted)
|
||||
})?;
|
||||
fn uninit_inner<R>(size: Size, align: Align, fail: impl FnOnce() -> R) -> Result<Self, R> {
|
||||
// This results in an error that can happen non-deterministically, since the memory
|
||||
// available to the compiler can change between runs. Normally queries are always
|
||||
// deterministic. However, we can be non-deterministic here because all uses of const
|
||||
// evaluation (including ConstProp!) will make compilation fail (via hard error
|
||||
// or ICE) upon encountering a `MemoryExhausted` error.
|
||||
let bytes = Bytes::zeroed(size, align).ok_or_else(fail)?;
|
||||
|
||||
Ok(Allocation {
|
||||
bytes,
|
||||
|
@ -325,6 +313,28 @@ impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> {
|
|||
extra: (),
|
||||
})
|
||||
}
|
||||
|
||||
/// Try to create an Allocation of `size` bytes, failing if there is not enough memory
|
||||
/// available to the compiler to do so.
|
||||
pub fn try_uninit<'tcx>(size: Size, align: Align) -> InterpResult<'tcx, Self> {
|
||||
Self::uninit_inner(size, align, || {
|
||||
ty::tls::with(|tcx| {
|
||||
tcx.sess.delay_span_bug(DUMMY_SP, "exhausted memory during interpretation")
|
||||
});
|
||||
InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted).into()
|
||||
})
|
||||
}
|
||||
|
||||
/// Try to create an Allocation of `size` bytes, panics if there is not enough memory
|
||||
/// available to the compiler to do so.
|
||||
pub fn uninit(size: Size, align: Align) -> Self {
|
||||
match Self::uninit_inner(size, align, || {
|
||||
panic!("Allocation::uninit called with panic_on_fail had allocation failure");
|
||||
}) {
|
||||
Ok(x) => x,
|
||||
Err(x) => x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Bytes: AllocBytes> Allocation<AllocId, (), Bytes> {
|
||||
|
|
|
@ -5,11 +5,15 @@ use crate::query::TyCtxtAt;
|
|||
use crate::ty::{layout, tls, Ty, ValTree};
|
||||
|
||||
use rustc_data_structures::sync::Lock;
|
||||
use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorGuaranteed};
|
||||
use rustc_errors::{
|
||||
struct_span_err, DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed,
|
||||
IntoDiagnosticArg,
|
||||
};
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_session::CtfeBacktrace;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_target::abi::{call, Align, Size};
|
||||
use rustc_target::abi::{call, Align, Size, WrappingRange};
|
||||
use std::borrow::Cow;
|
||||
use std::{any::Any, backtrace::Backtrace, fmt};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
|
||||
|
@ -91,20 +95,53 @@ pub struct InterpErrorInfo<'tcx>(Box<InterpErrorInfoInner<'tcx>>);
|
|||
#[derive(Debug)]
|
||||
struct InterpErrorInfoInner<'tcx> {
|
||||
kind: InterpError<'tcx>,
|
||||
backtrace: InterpErrorBacktrace,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InterpErrorBacktrace {
|
||||
backtrace: Option<Box<Backtrace>>,
|
||||
}
|
||||
|
||||
impl fmt::Display for InterpErrorInfo<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0.kind)
|
||||
impl InterpErrorBacktrace {
|
||||
pub fn new() -> InterpErrorBacktrace {
|
||||
let capture_backtrace = tls::with_opt(|tcx| {
|
||||
if let Some(tcx) = tcx {
|
||||
*Lock::borrow(&tcx.sess.ctfe_backtrace)
|
||||
} else {
|
||||
CtfeBacktrace::Disabled
|
||||
}
|
||||
});
|
||||
|
||||
let backtrace = match capture_backtrace {
|
||||
CtfeBacktrace::Disabled => None,
|
||||
CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())),
|
||||
CtfeBacktrace::Immediate => {
|
||||
// Print it now.
|
||||
let backtrace = Backtrace::force_capture();
|
||||
print_backtrace(&backtrace);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
InterpErrorBacktrace { backtrace }
|
||||
}
|
||||
|
||||
pub fn print_backtrace(&self) {
|
||||
if let Some(backtrace) = self.backtrace.as_ref() {
|
||||
print_backtrace(backtrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> InterpErrorInfo<'tcx> {
|
||||
pub fn print_backtrace(&self) {
|
||||
if let Some(backtrace) = self.0.backtrace.as_ref() {
|
||||
print_backtrace(backtrace);
|
||||
}
|
||||
pub fn from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self {
|
||||
Self(Box::new(InterpErrorInfoInner { kind, backtrace }))
|
||||
}
|
||||
|
||||
pub fn into_parts(self) -> (InterpError<'tcx>, InterpErrorBacktrace) {
|
||||
let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace }) = self;
|
||||
(kind, backtrace)
|
||||
}
|
||||
|
||||
pub fn into_kind(self) -> InterpError<'tcx> {
|
||||
|
@ -130,32 +167,17 @@ impl From<ErrorGuaranteed> for InterpErrorInfo<'_> {
|
|||
|
||||
impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
|
||||
fn from(kind: InterpError<'tcx>) -> Self {
|
||||
let capture_backtrace = tls::with_opt(|tcx| {
|
||||
if let Some(tcx) = tcx {
|
||||
*Lock::borrow(&tcx.sess.ctfe_backtrace)
|
||||
} else {
|
||||
CtfeBacktrace::Disabled
|
||||
}
|
||||
});
|
||||
|
||||
let backtrace = match capture_backtrace {
|
||||
CtfeBacktrace::Disabled => None,
|
||||
CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())),
|
||||
CtfeBacktrace::Immediate => {
|
||||
// Print it now.
|
||||
let backtrace = Backtrace::force_capture();
|
||||
print_backtrace(&backtrace);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
InterpErrorInfo(Box::new(InterpErrorInfoInner { kind, backtrace }))
|
||||
InterpErrorInfo(Box::new(InterpErrorInfoInner {
|
||||
kind,
|
||||
backtrace: InterpErrorBacktrace::new(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Error information for when the program we executed turned out not to actually be a valid
|
||||
/// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
|
||||
/// where we work on generic code or execution does not have all information available.
|
||||
#[derive(Debug)]
|
||||
pub enum InvalidProgramInfo<'tcx> {
|
||||
/// Resolution can fail if we are in a too generic context.
|
||||
TooGeneric,
|
||||
|
@ -174,25 +196,6 @@ pub enum InvalidProgramInfo<'tcx> {
|
|||
UninitUnsizedLocal,
|
||||
}
|
||||
|
||||
impl fmt::Display for InvalidProgramInfo<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use InvalidProgramInfo::*;
|
||||
match self {
|
||||
TooGeneric => write!(f, "encountered overly generic constant"),
|
||||
AlreadyReported(_) => {
|
||||
write!(
|
||||
f,
|
||||
"an error has already been reported elsewhere (this should not usually be printed)"
|
||||
)
|
||||
}
|
||||
Layout(ref err) => write!(f, "{err}"),
|
||||
FnAbiAdjustForForeignAbi(ref err) => write!(f, "{err}"),
|
||||
SizeOfUnsizedType(ty) => write!(f, "size_of called on unsized type `{ty}`"),
|
||||
UninitUnsizedLocal => write!(f, "unsized local is used while uninitialized"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Details of why a pointer had to be in-bounds.
|
||||
#[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
|
||||
pub enum CheckInAllocMsg {
|
||||
|
@ -208,26 +211,25 @@ pub enum CheckInAllocMsg {
|
|||
InboundsTest,
|
||||
}
|
||||
|
||||
impl fmt::Display for CheckInAllocMsg {
|
||||
/// When this is printed as an error the context looks like this:
|
||||
/// "{msg}{pointer} is a dangling pointer".
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match *self {
|
||||
CheckInAllocMsg::DerefTest => "dereferencing pointer failed: ",
|
||||
CheckInAllocMsg::MemoryAccessTest => "memory access failed: ",
|
||||
CheckInAllocMsg::PointerArithmeticTest => "out-of-bounds pointer arithmetic: ",
|
||||
CheckInAllocMsg::OffsetFromTest => "out-of-bounds offset_from: ",
|
||||
CheckInAllocMsg::InboundsTest => "out-of-bounds pointer use: ",
|
||||
}
|
||||
)
|
||||
#[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
|
||||
pub enum InvalidMetaKind {
|
||||
/// Size of a `[T]` is too big
|
||||
SliceTooBig,
|
||||
/// Size of a DST is too big
|
||||
TooBig,
|
||||
}
|
||||
|
||||
impl IntoDiagnosticArg for InvalidMetaKind {
|
||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||
DiagnosticArgValue::Str(Cow::Borrowed(match self {
|
||||
InvalidMetaKind::SliceTooBig => "slice_too_big",
|
||||
InvalidMetaKind::TooBig => "too_big",
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Details of an access to uninitialized bytes where it is not allowed.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct UninitBytesAccess {
|
||||
/// Range of the original memory access.
|
||||
pub access: AllocRange,
|
||||
|
@ -242,17 +244,32 @@ pub struct ScalarSizeMismatch {
|
|||
pub data_size: u64,
|
||||
}
|
||||
|
||||
macro_rules! impl_into_diagnostic_arg_through_debug {
|
||||
($($ty:ty),*$(,)?) => {$(
|
||||
impl IntoDiagnosticArg for $ty {
|
||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||
DiagnosticArgValue::Str(Cow::Owned(format!("{self:?}")))
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
// These types have nice `Debug` output so we can just use them in diagnostics.
|
||||
impl_into_diagnostic_arg_through_debug! {
|
||||
AllocId,
|
||||
Pointer,
|
||||
AllocRange,
|
||||
}
|
||||
|
||||
/// Error information for when the program caused Undefined Behavior.
|
||||
pub enum UndefinedBehaviorInfo {
|
||||
/// Free-form case. Only for errors that are never caught!
|
||||
#[derive(Debug)]
|
||||
pub enum UndefinedBehaviorInfo<'a> {
|
||||
/// Free-form case. Only for errors that are never caught! Used by miri
|
||||
Ub(String),
|
||||
/// Unreachable code was executed.
|
||||
Unreachable,
|
||||
/// A slice/array index projection went out-of-bounds.
|
||||
BoundsCheckFailed {
|
||||
len: u64,
|
||||
index: u64,
|
||||
},
|
||||
BoundsCheckFailed { len: u64, index: u64 },
|
||||
/// Something was divided by 0 (x / 0).
|
||||
DivisionByZero,
|
||||
/// Something was "remainded" by 0 (x % 0).
|
||||
|
@ -263,8 +280,8 @@ pub enum UndefinedBehaviorInfo {
|
|||
RemainderOverflow,
|
||||
/// Overflowing inbounds pointer arithmetic.
|
||||
PointerArithOverflow,
|
||||
/// Invalid metadata in a wide pointer (using `str` to avoid allocations).
|
||||
InvalidMeta(&'static str),
|
||||
/// Invalid metadata in a wide pointer
|
||||
InvalidMeta(InvalidMetaKind),
|
||||
/// Reading a C string that does not end within its allocation.
|
||||
UnterminatedCString(Pointer),
|
||||
/// Dereferencing a dangling pointer after it got freed.
|
||||
|
@ -281,25 +298,13 @@ pub enum UndefinedBehaviorInfo {
|
|||
/// Using an integer as a pointer in the wrong way.
|
||||
DanglingIntPointer(u64, CheckInAllocMsg),
|
||||
/// Used a pointer with bad alignment.
|
||||
AlignmentCheckFailed {
|
||||
required: Align,
|
||||
has: Align,
|
||||
},
|
||||
AlignmentCheckFailed { required: Align, has: Align },
|
||||
/// Writing to read-only memory.
|
||||
WriteToReadOnly(AllocId),
|
||||
// Trying to access the data behind a function pointer.
|
||||
/// Trying to access the data behind a function pointer.
|
||||
DerefFunctionPointer(AllocId),
|
||||
// Trying to access the data behind a vtable pointer.
|
||||
/// Trying to access the data behind a vtable pointer.
|
||||
DerefVTablePointer(AllocId),
|
||||
/// The value validity check found a problem.
|
||||
/// Should only be thrown by `validity.rs` and always point out which part of the value
|
||||
/// is the problem.
|
||||
ValidationFailure {
|
||||
/// The "path" to the value in question, e.g. `.0[5].field` for a struct
|
||||
/// field in the 6th element of an array that is the first element of a tuple.
|
||||
path: Option<String>,
|
||||
msg: String,
|
||||
},
|
||||
/// Using a non-boolean `u8` as bool.
|
||||
InvalidBool(u8),
|
||||
/// Using a non-character `u32` as character.
|
||||
|
@ -320,110 +325,100 @@ pub enum UndefinedBehaviorInfo {
|
|||
ScalarSizeMismatch(ScalarSizeMismatch),
|
||||
/// A discriminant of an uninhabited enum variant is written.
|
||||
UninhabitedEnumVariantWritten,
|
||||
/// Validation error.
|
||||
Validation(ValidationErrorInfo<'a>),
|
||||
// FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically
|
||||
// dispatched
|
||||
/// A custom (free-form) error, created by `err_ub_custom!`.
|
||||
Custom(crate::error::CustomSubdiagnostic<'a>),
|
||||
}
|
||||
|
||||
impl fmt::Display for UndefinedBehaviorInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use UndefinedBehaviorInfo::*;
|
||||
match self {
|
||||
Ub(msg) => write!(f, "{msg}"),
|
||||
Unreachable => write!(f, "entering unreachable code"),
|
||||
BoundsCheckFailed { ref len, ref index } => {
|
||||
write!(f, "indexing out of bounds: the len is {len} but the index is {index}")
|
||||
}
|
||||
DivisionByZero => write!(f, "dividing by zero"),
|
||||
RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
|
||||
DivisionOverflow => write!(f, "overflow in signed division (dividing MIN by -1)"),
|
||||
RemainderOverflow => write!(f, "overflow in signed remainder (dividing MIN by -1)"),
|
||||
PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
|
||||
InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {msg}"),
|
||||
UnterminatedCString(p) => write!(
|
||||
f,
|
||||
"reading a null-terminated string starting at {p:?} with no null found before end of allocation",
|
||||
),
|
||||
PointerUseAfterFree(a) => {
|
||||
write!(f, "pointer to {a:?} was dereferenced after this allocation got freed")
|
||||
}
|
||||
PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size: Size::ZERO, msg } => {
|
||||
write!(
|
||||
f,
|
||||
"{msg}{alloc_id:?} has size {alloc_size}, so pointer at offset {ptr_offset} is out-of-bounds",
|
||||
alloc_size = alloc_size.bytes(),
|
||||
)
|
||||
}
|
||||
PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size, msg } => write!(
|
||||
f,
|
||||
"{msg}{alloc_id:?} has size {alloc_size}, so pointer to {ptr_size} byte{ptr_size_p} starting at offset {ptr_offset} is out-of-bounds",
|
||||
alloc_size = alloc_size.bytes(),
|
||||
ptr_size = ptr_size.bytes(),
|
||||
ptr_size_p = pluralize!(ptr_size.bytes()),
|
||||
),
|
||||
DanglingIntPointer(i, msg) => {
|
||||
write!(
|
||||
f,
|
||||
"{msg}{pointer} is a dangling pointer (it has no provenance)",
|
||||
pointer = Pointer::<Option<AllocId>>::from_addr_invalid(*i),
|
||||
)
|
||||
}
|
||||
AlignmentCheckFailed { required, has } => write!(
|
||||
f,
|
||||
"accessing memory with alignment {has}, but alignment {required} is required",
|
||||
has = has.bytes(),
|
||||
required = required.bytes()
|
||||
),
|
||||
WriteToReadOnly(a) => write!(f, "writing to {a:?} which is read-only"),
|
||||
DerefFunctionPointer(a) => write!(f, "accessing {a:?} which contains a function"),
|
||||
DerefVTablePointer(a) => write!(f, "accessing {a:?} which contains a vtable"),
|
||||
ValidationFailure { path: None, msg } => {
|
||||
write!(f, "constructing invalid value: {msg}")
|
||||
}
|
||||
ValidationFailure { path: Some(path), msg } => {
|
||||
write!(f, "constructing invalid value at {path}: {msg}")
|
||||
}
|
||||
InvalidBool(b) => {
|
||||
write!(f, "interpreting an invalid 8-bit value as a bool: 0x{b:02x}")
|
||||
}
|
||||
InvalidChar(c) => {
|
||||
write!(f, "interpreting an invalid 32-bit value as a char: 0x{c:08x}")
|
||||
}
|
||||
InvalidTag(val) => write!(f, "enum value has invalid tag: {val:x}"),
|
||||
InvalidFunctionPointer(p) => {
|
||||
write!(f, "using {p:?} as function pointer but it does not point to a function")
|
||||
}
|
||||
InvalidVTablePointer(p) => {
|
||||
write!(f, "using {p:?} as vtable pointer but it does not point to a vtable")
|
||||
}
|
||||
InvalidStr(err) => write!(f, "this string is not valid UTF-8: {err}"),
|
||||
InvalidUninitBytes(Some((alloc, info))) => write!(
|
||||
f,
|
||||
"reading memory at {alloc:?}{access:?}, \
|
||||
but memory is uninitialized at {uninit:?}, \
|
||||
and this operation requires initialized memory",
|
||||
access = info.access,
|
||||
uninit = info.uninit,
|
||||
),
|
||||
InvalidUninitBytes(None) => write!(
|
||||
f,
|
||||
"using uninitialized data, but this operation requires initialized memory"
|
||||
),
|
||||
DeadLocal => write!(f, "accessing a dead local variable"),
|
||||
ScalarSizeMismatch(self::ScalarSizeMismatch { target_size, data_size }) => write!(
|
||||
f,
|
||||
"scalar size mismatch: expected {target_size} bytes but got {data_size} bytes instead",
|
||||
),
|
||||
UninhabitedEnumVariantWritten => {
|
||||
write!(f, "writing discriminant of an uninhabited enum")
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum PointerKind {
|
||||
Ref,
|
||||
Box,
|
||||
}
|
||||
|
||||
impl IntoDiagnosticArg for PointerKind {
|
||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||
DiagnosticArgValue::Str(
|
||||
match self {
|
||||
Self::Ref => "ref",
|
||||
Self::Box => "box",
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ValidationErrorInfo<'tcx> {
|
||||
pub path: Option<String>,
|
||||
pub kind: ValidationErrorKind<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ExpectedKind {
|
||||
Reference,
|
||||
Box,
|
||||
RawPtr,
|
||||
InitScalar,
|
||||
Bool,
|
||||
Char,
|
||||
Float,
|
||||
Int,
|
||||
FnPtr,
|
||||
}
|
||||
|
||||
impl From<PointerKind> for ExpectedKind {
|
||||
fn from(x: PointerKind) -> ExpectedKind {
|
||||
match x {
|
||||
PointerKind::Box => ExpectedKind::Box,
|
||||
PointerKind::Ref => ExpectedKind::Reference,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ValidationErrorKind<'tcx> {
|
||||
PtrToUninhabited { ptr_kind: PointerKind, ty: Ty<'tcx> },
|
||||
PtrToStatic { ptr_kind: PointerKind },
|
||||
PtrToMut { ptr_kind: PointerKind },
|
||||
ExpectedNonPtr { value: String },
|
||||
MutableRefInConst,
|
||||
NullFnPtr,
|
||||
NeverVal,
|
||||
NullablePtrOutOfRange { range: WrappingRange, max_value: u128 },
|
||||
PtrOutOfRange { range: WrappingRange, max_value: u128 },
|
||||
OutOfRange { value: String, range: WrappingRange, max_value: u128 },
|
||||
UnsafeCell,
|
||||
UninhabitedVal { ty: Ty<'tcx> },
|
||||
InvalidEnumTag { value: String },
|
||||
UninitEnumTag,
|
||||
UninitStr,
|
||||
Uninit { expected: ExpectedKind },
|
||||
UninitVal,
|
||||
InvalidVTablePtr { value: String },
|
||||
InvalidMetaSliceTooLarge { ptr_kind: PointerKind },
|
||||
InvalidMetaTooLarge { ptr_kind: PointerKind },
|
||||
UnalignedPtr { ptr_kind: PointerKind, required_bytes: u64, found_bytes: u64 },
|
||||
NullPtr { ptr_kind: PointerKind },
|
||||
DanglingPtrNoProvenance { ptr_kind: PointerKind, pointer: String },
|
||||
DanglingPtrOutOfBounds { ptr_kind: PointerKind },
|
||||
DanglingPtrUseAfterFree { ptr_kind: PointerKind },
|
||||
InvalidBool { value: String },
|
||||
InvalidChar { value: String },
|
||||
InvalidFnPtr { value: String },
|
||||
}
|
||||
|
||||
/// Error information for when the program did something that might (or might not) be correct
|
||||
/// to do according to the Rust spec, but due to limitations in the interpreter, the
|
||||
/// operation could not be carried out. These limitations can differ between CTFE and the
|
||||
/// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses.
|
||||
#[derive(Debug)]
|
||||
pub enum UnsupportedOpInfo {
|
||||
/// Free-form case. Only for errors that are never caught!
|
||||
// FIXME still use translatable diagnostics
|
||||
Unsupported(String),
|
||||
//
|
||||
// The variants below are only reachable from CTFE/const prop, miri will never emit them.
|
||||
|
@ -442,26 +437,9 @@ pub enum UnsupportedOpInfo {
|
|||
ReadExternStatic(DefId),
|
||||
}
|
||||
|
||||
impl fmt::Display for UnsupportedOpInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use UnsupportedOpInfo::*;
|
||||
match self {
|
||||
Unsupported(ref msg) => write!(f, "{msg}"),
|
||||
PartialPointerOverwrite(ptr) => {
|
||||
write!(f, "unable to overwrite parts of a pointer in memory at {ptr:?}")
|
||||
}
|
||||
PartialPointerCopy(ptr) => {
|
||||
write!(f, "unable to copy parts of a pointer from memory at {ptr:?}")
|
||||
}
|
||||
ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes"),
|
||||
ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({did:?})"),
|
||||
ReadExternStatic(did) => write!(f, "cannot read from extern static ({did:?})"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error information for when the program exhausted the resources granted to it
|
||||
/// by the interpreter.
|
||||
#[derive(Debug)]
|
||||
pub enum ResourceExhaustionInfo {
|
||||
/// The stack grew too big.
|
||||
StackFrameLimitReached,
|
||||
|
@ -471,47 +449,30 @@ pub enum ResourceExhaustionInfo {
|
|||
AddressSpaceFull,
|
||||
}
|
||||
|
||||
impl fmt::Display for ResourceExhaustionInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use ResourceExhaustionInfo::*;
|
||||
match self {
|
||||
StackFrameLimitReached => {
|
||||
write!(f, "reached the configured maximum number of stack frames")
|
||||
}
|
||||
MemoryExhausted => {
|
||||
write!(f, "tried to allocate more memory than available to compiler")
|
||||
}
|
||||
AddressSpaceFull => {
|
||||
write!(f, "there are no more free addresses in the address space")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait to work around not having trait object upcasting.
|
||||
pub trait AsAny: Any {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
}
|
||||
impl<T: Any> AsAny for T {
|
||||
#[inline(always)]
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for machine-specific errors (or other "machine stop" conditions).
|
||||
pub trait MachineStopType: AsAny + fmt::Display + Send {}
|
||||
pub trait MachineStopType: Any + fmt::Debug + Send {
|
||||
/// The diagnostic message for this error
|
||||
fn diagnostic_message(&self) -> DiagnosticMessage;
|
||||
/// Add diagnostic arguments by passing name and value pairs to `adder`, which are passed to
|
||||
/// fluent for formatting the translated diagnostic message.
|
||||
fn add_args(
|
||||
self: Box<Self>,
|
||||
adder: &mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>),
|
||||
);
|
||||
}
|
||||
|
||||
impl dyn MachineStopType {
|
||||
#[inline(always)]
|
||||
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
|
||||
self.as_any().downcast_ref()
|
||||
let x: &dyn Any = self;
|
||||
x.downcast_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum InterpError<'tcx> {
|
||||
/// The program caused undefined behavior.
|
||||
UndefinedBehavior(UndefinedBehaviorInfo),
|
||||
UndefinedBehavior(UndefinedBehaviorInfo<'tcx>),
|
||||
/// The program did something the interpreter does not support (some of these *might* be UB
|
||||
/// but the interpreter is not sure).
|
||||
Unsupported(UnsupportedOpInfo),
|
||||
|
@ -527,26 +488,6 @@ pub enum InterpError<'tcx> {
|
|||
|
||||
pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
|
||||
|
||||
impl fmt::Display for InterpError<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use InterpError::*;
|
||||
match *self {
|
||||
Unsupported(ref msg) => write!(f, "{msg}"),
|
||||
InvalidProgram(ref msg) => write!(f, "{msg}"),
|
||||
UndefinedBehavior(ref msg) => write!(f, "{msg}"),
|
||||
ResourceExhaustion(ref msg) => write!(f, "{msg}"),
|
||||
MachineStop(ref msg) => write!(f, "{msg}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Forward `Debug` to `Display`, so it does not look awful.
|
||||
impl fmt::Debug for InterpError<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl InterpError<'_> {
|
||||
/// Some errors do string formatting even if the error is never printed.
|
||||
/// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors,
|
||||
|
@ -555,7 +496,7 @@ impl InterpError<'_> {
|
|||
matches!(
|
||||
self,
|
||||
InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
|
||||
| InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure { .. })
|
||||
| InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Validation { .. })
|
||||
| InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
|
||||
)
|
||||
}
|
||||
|
|
|
@ -89,6 +89,30 @@ macro_rules! throw_machine_stop {
|
|||
($($tt:tt)*) => { do yeet err_machine_stop!($($tt)*) };
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! err_ub_custom {
|
||||
($msg:expr $(, $($name:ident = $value:expr),* $(,)?)?) => {{
|
||||
$(
|
||||
let ($($name,)*) = ($($value,)*);
|
||||
)?
|
||||
err_ub!(Custom(
|
||||
rustc_middle::error::CustomSubdiagnostic {
|
||||
msg: || $msg,
|
||||
add_args: Box::new(move |mut set_arg| {
|
||||
$($(
|
||||
set_arg(stringify!($name).into(), rustc_errors::IntoDiagnosticArg::into_diagnostic_arg($name));
|
||||
)*)?
|
||||
})
|
||||
}
|
||||
))
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! throw_ub_custom {
|
||||
($($tt:tt)*) => { do yeet err_ub_custom!($($tt)*) };
|
||||
}
|
||||
|
||||
mod allocation;
|
||||
mod error;
|
||||
mod pointer;
|
||||
|
@ -119,9 +143,10 @@ use crate::ty::{self, Instance, Ty, TyCtxt};
|
|||
|
||||
pub use self::error::{
|
||||
struct_error, CheckInAllocMsg, ErrorHandled, EvalToAllocationRawResult, EvalToConstValueResult,
|
||||
EvalToValTreeResult, InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo,
|
||||
MachineStopType, ReportedErrorInfo, ResourceExhaustionInfo, ScalarSizeMismatch,
|
||||
UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo,
|
||||
EvalToValTreeResult, ExpectedKind, InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind,
|
||||
InvalidProgramInfo, MachineStopType, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo,
|
||||
ScalarSizeMismatch, UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo,
|
||||
ValidationErrorInfo, ValidationErrorKind,
|
||||
};
|
||||
|
||||
pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar};
|
||||
|
|
|
@ -375,7 +375,8 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
|
|||
#[inline(always)]
|
||||
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
|
||||
pub fn assert_bits(self, target_size: Size) -> u128 {
|
||||
self.to_bits(target_size).unwrap()
|
||||
self.to_bits(target_size)
|
||||
.unwrap_or_else(|_| panic!("assertion failed: {self:?} fits {target_size:?}"))
|
||||
}
|
||||
|
||||
pub fn to_bool(self) -> InterpResult<'tcx, bool> {
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::ty::{AdtDef, InstanceDef, ScalarInt, UserTypeAnnotationIndex};
|
|||
use crate::ty::{GenericArg, InternalSubsts, SubstsRef};
|
||||
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_errors::{DiagnosticArgValue, DiagnosticMessage, ErrorGuaranteed, IntoDiagnosticArg};
|
||||
use rustc_hir::def::{CtorKind, Namespace};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
|
||||
use rustc_hir::{self, GeneratorKind, ImplicitSelfKind};
|
||||
|
@ -1371,55 +1371,61 @@ impl<O> AssertKind<O> {
|
|||
_ => write!(f, "\"{}\"", self.description()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<O: fmt::Debug> fmt::Debug for AssertKind<O> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
pub fn diagnostic_message(&self) -> DiagnosticMessage {
|
||||
use crate::fluent_generated::*;
|
||||
use AssertKind::*;
|
||||
|
||||
match self {
|
||||
BoundsCheck { ref len, ref index } => write!(
|
||||
f,
|
||||
"index out of bounds: the length is {:?} but the index is {:?}",
|
||||
len, index
|
||||
),
|
||||
OverflowNeg(op) => write!(f, "attempt to negate `{:#?}`, which would overflow", op),
|
||||
DivisionByZero(op) => write!(f, "attempt to divide `{:#?}` by zero", op),
|
||||
RemainderByZero(op) => write!(
|
||||
f,
|
||||
"attempt to calculate the remainder of `{:#?}` with a divisor of zero",
|
||||
op
|
||||
),
|
||||
Overflow(BinOp::Add, l, r) => {
|
||||
write!(f, "attempt to compute `{:#?} + {:#?}`, which would overflow", l, r)
|
||||
BoundsCheck { .. } => middle_bounds_check,
|
||||
Overflow(BinOp::Shl, _, _) => middle_assert_shl_overflow,
|
||||
Overflow(BinOp::Shr, _, _) => middle_assert_shr_overflow,
|
||||
Overflow(_, _, _) => middle_assert_op_overflow,
|
||||
OverflowNeg(_) => middle_assert_overflow_neg,
|
||||
DivisionByZero(_) => middle_assert_divide_by_zero,
|
||||
RemainderByZero(_) => middle_assert_remainder_by_zero,
|
||||
ResumedAfterReturn(GeneratorKind::Async(_)) => middle_assert_async_resume_after_return,
|
||||
ResumedAfterReturn(GeneratorKind::Gen) => middle_assert_generator_resume_after_return,
|
||||
ResumedAfterPanic(GeneratorKind::Async(_)) => middle_assert_async_resume_after_panic,
|
||||
ResumedAfterPanic(GeneratorKind::Gen) => middle_assert_generator_resume_after_panic,
|
||||
|
||||
MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_args(self, adder: &mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>))
|
||||
where
|
||||
O: fmt::Debug,
|
||||
{
|
||||
use AssertKind::*;
|
||||
|
||||
macro_rules! add {
|
||||
($name: expr, $value: expr) => {
|
||||
adder($name.into(), $value.into_diagnostic_arg());
|
||||
};
|
||||
}
|
||||
|
||||
match self {
|
||||
BoundsCheck { len, index } => {
|
||||
add!("len", format!("{len:?}"));
|
||||
add!("index", format!("{index:?}"));
|
||||
}
|
||||
Overflow(BinOp::Sub, l, r) => {
|
||||
write!(f, "attempt to compute `{:#?} - {:#?}`, which would overflow", l, r)
|
||||
Overflow(BinOp::Shl | BinOp::Shr, _, val)
|
||||
| DivisionByZero(val)
|
||||
| RemainderByZero(val)
|
||||
| OverflowNeg(val) => {
|
||||
add!("val", format!("{val:#?}"));
|
||||
}
|
||||
Overflow(BinOp::Mul, l, r) => {
|
||||
write!(f, "attempt to compute `{:#?} * {:#?}`, which would overflow", l, r)
|
||||
}
|
||||
Overflow(BinOp::Div, l, r) => {
|
||||
write!(f, "attempt to compute `{:#?} / {:#?}`, which would overflow", l, r)
|
||||
}
|
||||
Overflow(BinOp::Rem, l, r) => write!(
|
||||
f,
|
||||
"attempt to compute the remainder of `{:#?} % {:#?}`, which would overflow",
|
||||
l, r
|
||||
),
|
||||
Overflow(BinOp::Shr, _, r) => {
|
||||
write!(f, "attempt to shift right by `{:#?}`, which would overflow", r)
|
||||
}
|
||||
Overflow(BinOp::Shl, _, r) => {
|
||||
write!(f, "attempt to shift left by `{:#?}`, which would overflow", r)
|
||||
Overflow(binop, left, right) => {
|
||||
add!("op", binop.to_hir_binop().as_str());
|
||||
add!("left", format!("{left:#?}"));
|
||||
add!("right", format!("{right:#?}"));
|
||||
}
|
||||
ResumedAfterReturn(_) | ResumedAfterPanic(_) => {}
|
||||
MisalignedPointerDereference { required, found } => {
|
||||
write!(
|
||||
f,
|
||||
"misaligned pointer dereference: address must be a multiple of {:?} but is {:?}",
|
||||
required, found
|
||||
)
|
||||
add!("required", format!("{required:#?}"));
|
||||
add!("found", format!("{found:#?}"));
|
||||
}
|
||||
_ => write!(f, "{}", self.description()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -846,7 +846,7 @@ fn write_allocation_newline(
|
|||
/// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there
|
||||
/// is only one line). Note that your prefix should contain a trailing space as the lines are
|
||||
/// printed directly after it.
|
||||
fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(
|
||||
pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
alloc: &Allocation<Prov, Extra, Bytes>,
|
||||
w: &mut dyn std::fmt::Write,
|
||||
|
|
|
@ -801,7 +801,8 @@ pub enum UnwindAction {
|
|||
}
|
||||
|
||||
/// Information about an assertion failure.
|
||||
#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq, TypeFoldable, TypeVisitable)]
|
||||
#[derive(Clone, Hash, HashStable, PartialEq, Debug)]
|
||||
#[derive(TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)]
|
||||
pub enum AssertKind<O> {
|
||||
BoundsCheck { len: O, index: O },
|
||||
Overflow(BinOp, O, O),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use rustc_apfloat::ieee::{Double, Single};
|
||||
use rustc_apfloat::Float;
|
||||
use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
|
||||
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
|
||||
use rustc_target::abi::Size;
|
||||
use std::fmt;
|
||||
|
@ -113,6 +114,14 @@ impl std::fmt::Debug for ConstInt {
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoDiagnosticArg for ConstInt {
|
||||
// FIXME this simply uses the Debug impl, but we could probably do better by converting both
|
||||
// to an inherent method that returns `Cow`.
|
||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||
DiagnosticArgValue::Str(format!("{self:?}").into())
|
||||
}
|
||||
}
|
||||
|
||||
/// The raw bytes of a simple value.
|
||||
///
|
||||
/// This is a packed struct in order to allow this type to be optimally embedded in enums
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use crate::fluent_generated as fluent;
|
||||
use crate::error::UnsupportedFnAbi;
|
||||
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use crate::query::TyCtxtAt;
|
||||
use crate::ty::normalize_erasing_regions::NormalizationError;
|
||||
use crate::ty::{self, ReprOptions, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_error_messages::DiagnosticMessage;
|
||||
use rustc_errors::{DiagnosticBuilder, Handler, IntoDiagnostic};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
|
@ -14,7 +15,7 @@ use rustc_target::abi::call::FnAbi;
|
|||
use rustc_target::abi::*;
|
||||
use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy, Target};
|
||||
|
||||
use std::cmp::{self};
|
||||
use std::cmp;
|
||||
use std::fmt;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::ops::Bound;
|
||||
|
@ -214,29 +215,29 @@ pub enum LayoutError<'tcx> {
|
|||
Cycle,
|
||||
}
|
||||
|
||||
impl IntoDiagnostic<'_, !> for LayoutError<'_> {
|
||||
fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> {
|
||||
let mut diag = handler.struct_fatal("");
|
||||
|
||||
impl<'tcx> LayoutError<'tcx> {
|
||||
pub fn diagnostic_message(&self) -> DiagnosticMessage {
|
||||
use crate::fluent_generated::*;
|
||||
use LayoutError::*;
|
||||
match self {
|
||||
LayoutError::Unknown(ty) => {
|
||||
diag.set_arg("ty", ty);
|
||||
diag.set_primary_message(fluent::middle_unknown_layout);
|
||||
}
|
||||
LayoutError::SizeOverflow(ty) => {
|
||||
diag.set_arg("ty", ty);
|
||||
diag.set_primary_message(fluent::middle_values_too_big);
|
||||
}
|
||||
LayoutError::NormalizationFailure(ty, e) => {
|
||||
diag.set_arg("ty", ty);
|
||||
diag.set_arg("failure_ty", e.get_type_for_failure());
|
||||
diag.set_primary_message(fluent::middle_cannot_be_normalized);
|
||||
}
|
||||
LayoutError::Cycle => {
|
||||
diag.set_primary_message(fluent::middle_cycle);
|
||||
}
|
||||
Unknown(_) => middle_unknown_layout,
|
||||
SizeOverflow(_) => middle_values_too_big,
|
||||
NormalizationFailure(_, _) => middle_cannot_be_normalized,
|
||||
Cycle => middle_cycle,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_diagnostic(self) -> crate::error::LayoutError<'tcx> {
|
||||
use crate::error::LayoutError as E;
|
||||
use LayoutError::*;
|
||||
match self {
|
||||
Unknown(ty) => E::Unknown { ty },
|
||||
SizeOverflow(ty) => E::Overflow { ty },
|
||||
NormalizationFailure(ty, e) => {
|
||||
E::NormalizationFailure { ty, failure_ty: e.get_type_for_failure() }
|
||||
}
|
||||
Cycle => E::Cycle,
|
||||
}
|
||||
diag
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -330,11 +331,8 @@ impl<'tcx> SizeSkeleton<'tcx> {
|
|||
Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(tail) })
|
||||
}
|
||||
_ => bug!(
|
||||
"SizeSkeleton::compute({}): layout errored ({}), yet \
|
||||
tail `{}` is not a type parameter or a projection",
|
||||
ty,
|
||||
err,
|
||||
tail
|
||||
"SizeSkeleton::compute({ty}): layout errored ({err:?}), yet \
|
||||
tail `{tail}` is not a type parameter or a projection",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -940,12 +938,8 @@ where
|
|||
TyMaybeWithLayout::Ty(field_ty) => {
|
||||
cx.tcx().layout_of(cx.param_env().and(field_ty)).unwrap_or_else(|e| {
|
||||
bug!(
|
||||
"failed to get layout for `{}`: {},\n\
|
||||
despite it being a field (#{}) of an existing layout: {:#?}",
|
||||
field_ty,
|
||||
e,
|
||||
i,
|
||||
this
|
||||
"failed to get layout for `{field_ty}`: {e:?},\n\
|
||||
despite it being a field (#{i}) of an existing layout: {this:#?}",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -1262,21 +1256,18 @@ impl From<call::AdjustForForeignAbiError> for FnAbiError<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> fmt::Display for FnAbiError<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
impl<'a, 'b> IntoDiagnostic<'a, !> for FnAbiError<'b> {
|
||||
fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, !> {
|
||||
match self {
|
||||
Self::Layout(err) => err.fmt(f),
|
||||
Self::AdjustForForeignAbi(err) => err.fmt(f),
|
||||
Self::Layout(e) => e.into_diagnostic().into_diagnostic(handler),
|
||||
Self::AdjustForForeignAbi(call::AdjustForForeignAbiError::Unsupported {
|
||||
arch,
|
||||
abi,
|
||||
}) => UnsupportedFnAbi { arch, abi: abi.name() }.into_diagnostic(handler),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDiagnostic<'_, !> for FnAbiError<'_> {
|
||||
fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> {
|
||||
handler.struct_fatal(self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(eddyb) maybe use something like this for an unified `fn_abi_of`, not
|
||||
// just for error handling.
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -73,7 +73,7 @@ pub(super) fn vtable_allocation_provider<'tcx>(
|
|||
let ptr_align = tcx.data_layout.pointer_align.abi;
|
||||
|
||||
let vtable_size = ptr_size * u64::try_from(vtable_entries.len()).unwrap();
|
||||
let mut vtable = Allocation::uninit(vtable_size, ptr_align, /* panic_on_fail */ true).unwrap();
|
||||
let mut vtable = Allocation::uninit(vtable_size, ptr_align);
|
||||
|
||||
// No need to do any alignment checks on the memory accesses below, because we know the
|
||||
// allocation is correctly aligned as we created it above. Also we're only offsetting by
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue