From 94361fba3e9b26ca9040625a5de6d6ed1724f4cf Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 9 Mar 2026 15:38:21 +0100 Subject: [PATCH 1/3] interpret: go back to regular string interpolation for error messages --- compiler/rustc_abi/src/lib.rs | 18 +- .../src/const_eval/dummy_machine.rs | 11 +- .../rustc_const_eval/src/const_eval/error.rs | 101 ++- .../src/const_eval/machine.rs | 33 +- compiler/rustc_const_eval/src/errors.rs | 708 +----------------- .../rustc_const_eval/src/interpret/call.rs | 23 +- .../rustc_const_eval/src/interpret/cast.rs | 9 +- .../src/interpret/eval_context.rs | 20 +- .../src/interpret/intrinsics.rs | 71 +- .../rustc_const_eval/src/interpret/memory.rs | 105 +-- .../src/interpret/validity.rs | 349 ++++++--- compiler/rustc_const_eval/src/lib.rs | 2 - .../src/util/check_validity_requirement.rs | 19 +- compiler/rustc_errors/src/diagnostic_impls.rs | 9 +- .../src/mir/interpret/allocation.rs | 4 +- .../rustc_middle/src/mir/interpret/error.rs | 513 ++++++++----- .../rustc_middle/src/mir/interpret/mod.rs | 27 +- .../rustc_middle/src/mir/interpret/pointer.rs | 6 + compiler/rustc_middle/src/mir/mod.rs | 2 +- compiler/rustc_middle/src/mir/terminator.rs | 114 +-- compiler/rustc_middle/src/ty/layout.rs | 26 +- compiler/rustc_mir_transform/src/errors.rs | 6 +- .../src/known_panics_lint.rs | 2 +- compiler/rustc_target/src/spec/json.rs | 6 +- .../stacked_borrows/diagnostics.rs | 21 +- .../tree_borrows/diagnostics.rs | 4 +- src/tools/miri/src/diagnostics.rs | 46 +- .../tests/pass/alloc-access-tracking.stderr | 4 +- 28 files changed, 808 insertions(+), 1451 deletions(-) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 21ca92d46d1c6..253dff6f8e75c 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1000,20 +1000,6 @@ pub enum AlignFromBytesError { TooLarge(u64), } -impl AlignFromBytesError { - pub fn diag_ident(self) -> &'static str { - match self { - Self::NotPowerOfTwo(_) => "not_power_of_two", - Self::TooLarge(_) => "too_large", - } - } - - pub fn align(self) -> u64 { - let (Self::NotPowerOfTwo(align) | Self::TooLarge(align)) = self; - align - } -} - impl fmt::Debug for AlignFromBytesError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, f) @@ -1023,8 +1009,8 @@ impl fmt::Debug for AlignFromBytesError { impl fmt::Display for AlignFromBytesError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - AlignFromBytesError::NotPowerOfTwo(align) => write!(f, "`{align}` is not a power of 2"), - AlignFromBytesError::TooLarge(align) => write!(f, "`{align}` is too large"), + AlignFromBytesError::NotPowerOfTwo(align) => write!(f, "{align} is not a power of 2"), + AlignFromBytesError::TooLarge(align) => write!(f, "{align} is too large"), } } } diff --git a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs index f41b68bb1f7ed..7c5f867929f04 100644 --- a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs @@ -25,17 +25,8 @@ pub macro throw_machine_stop_str($($tt:tt)*) {{ write!(f, $($tt)*) } } + impl rustc_middle::mir::interpret::MachineStopType for Zst {} - impl rustc_middle::mir::interpret::MachineStopType for Zst { - fn diagnostic_message(&self) -> rustc_errors::DiagMessage { - self.to_string().into() - } - - fn add_args( - self: Box, - _: &mut dyn FnMut(rustc_errors::DiagArgName, rustc_errors::DiagArgValue), - ) {} - } throw_machine_stop!(Zst) }} diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index 41d21bd74f98c..61e8e433d5178 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -1,15 +1,17 @@ -use std::mem; +use std::{fmt, mem}; -use rustc_errors::{Diag, DiagArgName, DiagArgValue, DiagMessage, IntoDiagArg}; +use rustc_errors::Diag; use rustc_middle::mir::AssertKind; -use rustc_middle::mir::interpret::{AllocId, Provenance, ReportedErrorInfo, UndefinedBehaviorInfo}; +use rustc_middle::mir::interpret::{ + AllocId, Provenance, ReportedErrorInfo, UndefinedBehaviorInfo, UnsupportedOpInfo, +}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::ConstInt; use rustc_middle::ty::layout::LayoutError; use rustc_span::{Span, Symbol}; use super::CompileTimeMachine; -use crate::errors::{self, FrameNote, ReportErrorExt}; +use crate::errors::{self, FrameNote}; use crate::interpret::{ CtfeProvenance, ErrorHandled, Frame, InterpCx, InterpErrorInfo, InterpErrorKind, MachineStopType, Pointer, err_inval, err_machine_stop, @@ -40,65 +42,49 @@ pub enum ConstEvalErrKind { ConstMakeGlobalWithOffset(Pointer>), } -impl MachineStopType for ConstEvalErrKind { - fn diagnostic_message(&self) -> DiagMessage { +impl fmt::Display for ConstEvalErrKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use ConstEvalErrKind::*; - use rustc_errors::msg; - match self { - ConstAccessesMutGlobal => "constant accesses mutable global memory".into(), + ConstAccessesMutGlobal => write!(f, "constant accesses mutable global memory"), ModifiedGlobal => { - "modifying a static's initial value from another static's initializer".into() + write!(f, "modifying a static's initial value from another static's initializer") } - Panic { .. } => msg!("evaluation panicked: {$msg}"), + Panic { msg, .. } => write!(f, "evaluation panicked: {msg}"), RecursiveStatic => { - "encountered static that tried to access itself during initialization".into() + write!(f, "encountered static that tried to access itself during initialization") } - AssertFailure(x) => x.diagnostic_message(), + AssertFailure(x) => write!(f, "{x}"), WriteThroughImmutablePointer => { - msg!( + write!( + f, "writing through a pointer that was derived from a shared (immutable) reference" ) } - ConstMakeGlobalPtrAlreadyMadeGlobal { .. } => { - msg!("attempting to call `const_make_global` twice on the same allocation {$alloc}") - } - ConstMakeGlobalPtrIsNonHeap(_) => { - msg!( - "pointer passed to `const_make_global` does not point to a heap allocation: {$ptr}" + ConstMakeGlobalPtrAlreadyMadeGlobal(alloc) => { + write!( + f, + "attempting to call `const_make_global` twice on the same allocation {alloc}" ) } - ConstMakeGlobalWithDanglingPtr(_) => { - msg!("pointer passed to `const_make_global` is dangling: {$ptr}") - } - ConstMakeGlobalWithOffset(_) => { - msg!("making {$ptr} global which does not point to the beginning of an object") - } - } - } - fn add_args(self: Box, adder: &mut dyn FnMut(DiagArgName, DiagArgValue)) { - use ConstEvalErrKind::*; - match *self { - RecursiveStatic - | ConstAccessesMutGlobal - | ModifiedGlobal - | WriteThroughImmutablePointer => {} - AssertFailure(kind) => kind.add_args(adder), - Panic { msg, .. } => { - adder("msg".into(), msg.into_diag_arg(&mut None)); + ConstMakeGlobalPtrIsNonHeap(ptr) => { + write!( + f, + "pointer passed to `const_make_global` does not point to a heap allocation: {ptr}" + ) } - ConstMakeGlobalPtrIsNonHeap(ptr) - | ConstMakeGlobalWithOffset(ptr) - | ConstMakeGlobalWithDanglingPtr(ptr) => { - adder("ptr".into(), format!("{ptr:?}").into_diag_arg(&mut None)); + ConstMakeGlobalWithDanglingPtr(ptr) => { + write!(f, "pointer passed to `const_make_global` is dangling: {ptr}") } - ConstMakeGlobalPtrAlreadyMadeGlobal(alloc) => { - adder("alloc".into(), alloc.into_diag_arg(&mut None)); + ConstMakeGlobalWithOffset(ptr) => { + write!(f, "making {ptr} global which does not point to the beginning of an object") } } } } +impl MachineStopType for ConstEvalErrKind {} + /// The errors become [`InterpErrorKind::MachineStop`] when being raised. impl<'tcx> Into> for ConstEvalErrKind { fn into(self) -> InterpErrorInfo<'tcx> { @@ -207,14 +193,20 @@ where _ => { let (our_span, frames) = get_span_and_frames(); let span = span.substitute_dummy(our_span); - let mut err = tcx.dcx().struct_span_err(our_span, error.diagnostic_message()); - // We allow invalid programs in infallible promoteds since invalid layouts can occur - // anyway (e.g. due to size overflow). And we allow OOM as that can happen any time. - let allowed_in_infallible = matches!( + let mut err = tcx.dcx().struct_span_err(our_span, error.to_string()); + if matches!( error, - InterpErrorKind::ResourceExhaustion(_) | InterpErrorKind::InvalidProgram(_) - ); - + InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ValidationError { + ptr_bytes_warning: true, + .. + }) | InterpErrorKind::Unsupported( + UnsupportedOpInfo::ReadPointerAsInt(..) + | UnsupportedOpInfo::ReadPartialPointer(..) + ) + ) { + err.help("this code performed an operation that depends on the underlying bytes representing a pointer"); + err.help("the absolute address of a pointer is not known at compile-time, so such operations are not supported"); + } if let InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes( Some((alloc_id, _access)), )) = error @@ -229,7 +221,12 @@ where err.subdiagnostic(raw_bytes); } - error.add_args(&mut err); + // We allow invalid programs in infallible promoteds since invalid layouts can occur + // anyway (e.g. due to size overflow). And we allow OOM as that can happen any time. + let allowed_in_infallible = matches!( + error, + InterpErrorKind::ResourceExhaustion(_) | InterpErrorKind::InvalidProgram(_) + ); mk(&mut err, span, frames); let g = err.emit(); diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index e88123334dd52..a19bf0b4da8be 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -5,7 +5,6 @@ use std::hash::Hash; use rustc_abi::{Align, FIRST_VARIANT, Size}; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry}; -use rustc_errors::msg; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem, find_attr}; use rustc_middle::mir::AssertMessage; @@ -24,7 +23,7 @@ use crate::interpret::{ self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, Scalar, compile_time_machine, ensure_monomorphic_enough, err_inval, interp_ok, throw_exhaust, - throw_inval, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, + throw_inval, throw_ub, throw_ub_format, throw_unsup, throw_unsup_format, type_implements_dyn_trait, }; @@ -489,18 +488,9 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { let align = match Align::from_bytes(align) { Ok(a) => a, - Err(err) => throw_ub_custom!( - msg!( - "invalid align passed to `{$name}`: {$align} is {$err_kind -> - [not_power_of_two] not a power of 2 - [too_large] too large - *[other] {\"\"} - }" - ), - name = "const_allocate", - err_kind = err.diag_ident(), - align = err.align() - ), + Err(err) => { + throw_ub_format!("invalid align passed to `const_allocate`: {err}") + } }; let ptr = ecx.allocate_ptr( @@ -519,18 +509,9 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { let size = Size::from_bytes(size); let align = match Align::from_bytes(align) { Ok(a) => a, - Err(err) => throw_ub_custom!( - msg!( - "invalid align passed to `{$name}`: {$align} is {$err_kind -> - [not_power_of_two] not a power of 2 - [too_large] too large - *[other] {\"\"} - }" - ), - name = "const_deallocate", - err_kind = err.diag_ident(), - align = err.align() - ), + Err(err) => { + throw_ub_format!("invalid align passed to `const_deallocate`: {err}") + } }; // If an allocation is created in an another const, diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 07d001e9d847f..1097eecf3121d 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -1,22 +1,11 @@ use std::borrow::Cow; -use std::fmt::Write; -use either::Either; -use rustc_abi::WrappingRange; use rustc_errors::codes::*; use rustc_errors::formatting::DiagMessageAddArg; -use rustc_errors::{ - Diag, DiagArgMap, DiagArgValue, DiagMessage, Diagnostic, EmissionGuarantee, Level, MultiSpan, - Subdiagnostic, format_diag_message, msg, -}; +use rustc_errors::{Diag, DiagArgValue, EmissionGuarantee, MultiSpan, Subdiagnostic, msg}; use rustc_hir::ConstContext; use rustc_macros::{Diagnostic, Subdiagnostic}; -use rustc_middle::mir::interpret::{ - CtfeProvenance, ExpectedKind, InterpErrorKind, InvalidMetaKind, InvalidProgramInfo, - Misalignment, Pointer, PointerKind, ResourceExhaustionInfo, UndefinedBehaviorInfo, - UnsupportedOpInfo, ValidationErrorInfo, -}; -use rustc_middle::ty::{self, Mutability, Ty}; +use rustc_middle::ty::{Mutability, Ty}; use rustc_span::{Span, Symbol}; use crate::interpret::InternKind; @@ -609,699 +598,6 @@ pub struct LiveDrop<'tcx> { pub dropped_at: Span, } -pub trait ReportErrorExt { - /// Returns the diagnostic message for this error. - fn diagnostic_message(&self) -> DiagMessage; - fn add_args(self, diag: &mut Diag<'_, G>); - - fn debug(self) -> String - where - Self: Sized, - { - ty::tls::with(move |tcx| { - let dcx = tcx.dcx(); - let mut diag = dcx.struct_allow(DiagMessage::Str(String::new().into())); - let message = self.diagnostic_message(); - self.add_args(&mut diag); - let s = format_diag_message(&message, &diag.args).into_owned(); - diag.cancel(); - s - }) - } -} - -impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { - fn diagnostic_message(&self) -> DiagMessage { - use UndefinedBehaviorInfo::*; - - match self { - Ub(msg) => msg.clone().into(), - Custom(x) => (x.msg)(), - ValidationError(e) => e.diagnostic_message(), - - Unreachable => "entering unreachable code".into(), - BoundsCheckFailed { .. } => msg!("indexing out of bounds: the len is {$len} but the index is {$index}"), - DivisionByZero => "dividing by zero".into(), - RemainderByZero => "calculating the remainder with a divisor of zero".into(), - DivisionOverflow => "overflow in signed division (dividing MIN by -1)".into(), - RemainderOverflow => "overflow in signed remainder (dividing MIN by -1)".into(), - PointerArithOverflow => "overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize`".into(), - ArithOverflow { .. } => msg!("arithmetic overflow in `{$intrinsic}`"), - ShiftOverflow { .. } => msg!("overflowing shift by {$shift_amount} in `{$intrinsic}`"), - InvalidMeta(InvalidMetaKind::SliceTooBig) => "invalid metadata in wide pointer: slice is bigger than largest supported object".into(), - InvalidMeta(InvalidMetaKind::TooBig) => "invalid metadata in wide pointer: total size is bigger than largest supported object".into(), - UnterminatedCString(_) => "reading a null-terminated string starting at {$pointer} with no null found before end of allocation".into(), - PointerUseAfterFree(_, _) => msg!( - "{$operation -> - [MemoryAccess] memory access failed - [InboundsPointerArithmetic] in-bounds pointer arithmetic failed - *[Dereferenceable] pointer not dereferenceable - }: {$alloc_id} has been freed, so this pointer is dangling" - ), - PointerOutOfBounds { .. } => msg!( - "{$operation -> - [MemoryAccess] memory access failed - [InboundsPointerArithmetic] in-bounds pointer arithmetic failed - *[Dereferenceable] pointer not dereferenceable - }: {$operation -> - [MemoryAccess] attempting to access {$inbounds_size -> - [1] 1 byte - *[x] {$inbounds_size} bytes - } - [InboundsPointerArithmetic] attempting to offset pointer by {$inbounds_size -> - [1] 1 byte - *[x] {$inbounds_size} bytes - } - *[Dereferenceable] pointer must {$inbounds_size -> - [0] point to some allocation - [1] be dereferenceable for 1 byte - *[x] be dereferenceable for {$inbounds_size} bytes - } - }, but got {$pointer} which {$ptr_offset_is_neg -> - [true] points to before the beginning of the allocation - *[false] {$inbounds_size_is_neg -> - [false] {$alloc_size_minus_ptr_offset -> - [0] is at or beyond the end of the allocation of size {$alloc_size -> - [1] 1 byte - *[x] {$alloc_size} bytes - } - [1] is only 1 byte from the end of the allocation - *[x] is only {$alloc_size_minus_ptr_offset} bytes from the end of the allocation - } - *[true] {$ptr_offset_abs -> - [0] is at the beginning of the allocation - *[other] is only {$ptr_offset_abs} bytes from the beginning of the allocation - } - } - }" - ), - DanglingIntPointer { addr: 0, .. } => msg!( - "{$operation -> - [MemoryAccess] memory access failed - [InboundsPointerArithmetic] in-bounds pointer arithmetic failed - *[Dereferenceable] pointer not dereferenceable - }: {$operation -> - [MemoryAccess] attempting to access {$inbounds_size -> - [1] 1 byte - *[x] {$inbounds_size} bytes - } - [InboundsPointerArithmetic] attempting to offset pointer by {$inbounds_size -> - [1] 1 byte - *[x] {$inbounds_size} bytes - } - *[Dereferenceable] pointer must {$inbounds_size -> - [0] point to some allocation - [1] be dereferenceable for 1 byte - *[x] be dereferenceable for {$inbounds_size} bytes - } - }, but got null pointer"), - DanglingIntPointer { .. } => msg!( - "{$operation -> - [MemoryAccess] memory access failed - [InboundsPointerArithmetic] in-bounds pointer arithmetic failed - *[Dereferenceable] pointer not dereferenceable - }: {$operation -> - [MemoryAccess] attempting to access {$inbounds_size -> - [1] 1 byte - *[x] {$inbounds_size} bytes - } - [InboundsPointerArithmetic] attempting to offset pointer by {$inbounds_size -> - [1] 1 byte - *[x] {$inbounds_size} bytes - } - *[Dereferenceable] pointer must {$inbounds_size -> - [0] point to some allocation - [1] be dereferenceable for 1 byte - *[x] be dereferenceable for {$inbounds_size} bytes - } - }, but got {$pointer} which is a dangling pointer (it has no provenance)"), - AlignmentCheckFailed { .. } => msg!( - "{$msg -> - [AccessedPtr] accessing memory - *[other] accessing memory based on pointer - } with alignment {$has}, but alignment {$required} is required" - ), - WriteToReadOnly(_) => msg!("writing to {$allocation} which is read-only"), - DerefFunctionPointer(_) => msg!("accessing {$allocation} which contains a function"), - DerefVTablePointer(_) => msg!("accessing {$allocation} which contains a vtable"), - DerefVaListPointer(_) => msg!("accessing {$allocation} which contains a variable argument list"), - DerefTypeIdPointer(_) => msg!("accessing {$allocation} which contains a `TypeId`"), - InvalidBool(_) => msg!("interpreting an invalid 8-bit value as a bool: 0x{$value}"), - InvalidChar(_) => msg!("interpreting an invalid 32-bit value as a char: 0x{$value}"), - InvalidTag(_) => msg!("enum value has invalid tag: {$tag}"), - InvalidFunctionPointer(_) => msg!("using {$pointer} as function pointer but it does not point to a function"), - InvalidVaListPointer(_) => msg!("using {$pointer} as variable argument list pointer but it does not point to a variable argument list"), - InvalidVTablePointer(_) => msg!("using {$pointer} as vtable pointer but it does not point to a vtable"), - InvalidVTableTrait { .. } => msg!("using vtable for `{$vtable_dyn_type}` but `{$expected_dyn_type}` was expected"), - InvalidStr(_) => msg!("this string is not valid UTF-8: {$err}"), - InvalidUninitBytes(None) => "using uninitialized data, but this operation requires initialized memory".into(), - InvalidUninitBytes(Some(_)) => msg!("reading memory at {$alloc}{$access}, but memory is uninitialized at {$uninit}, and this operation requires initialized memory"), - DeadLocal => "accessing a dead local variable".into(), - ScalarSizeMismatch(_) => msg!("scalar size mismatch: expected {$target_size} bytes but got {$data_size} bytes instead"), - UninhabitedEnumVariantWritten(_) => "writing discriminant of an uninhabited enum variant".into(), - UninhabitedEnumVariantRead(_) => "read discriminant of an uninhabited enum variant".into(), - InvalidNichedEnumVariantWritten { .. } => { - msg!("trying to set discriminant of a {$ty} to the niched variant, but the value does not match") - } - AbiMismatchArgument { .. } => msg!("calling a function whose parameter #{$arg_idx} has type {$callee_ty} passing argument of type {$caller_ty}"), - AbiMismatchReturn { .. } => msg!("calling a function with return type {$callee_ty} passing return place of type {$caller_ty}"), - VaArgOutOfBounds => "more C-variadic arguments read than were passed".into(), - CVariadicMismatch { ..} => "calling a function where the caller and callee disagree on whether the function is C-variadic".into(), - CVariadicFixedCountMismatch { .. } => msg!("calling a C-variadic function with {$caller} fixed arguments, but the function expects {$callee}"), - } - } - - fn add_args(self, diag: &mut Diag<'_, G>) { - use UndefinedBehaviorInfo::*; - match self { - Ub(_) => {} - Custom(custom) => { - (custom.add_args)(&mut |name, value| { - diag.arg(name, value); - }); - } - ValidationError(e) => e.add_args(diag), - - Unreachable - | DivisionByZero - | RemainderByZero - | DivisionOverflow - | RemainderOverflow - | PointerArithOverflow - | InvalidMeta(InvalidMetaKind::SliceTooBig) - | InvalidMeta(InvalidMetaKind::TooBig) - | InvalidUninitBytes(None) - | DeadLocal - | VaArgOutOfBounds - | UninhabitedEnumVariantWritten(_) - | UninhabitedEnumVariantRead(_) => {} - - ArithOverflow { intrinsic } => { - diag.arg("intrinsic", intrinsic); - } - ShiftOverflow { intrinsic, shift_amount } => { - diag.arg("intrinsic", intrinsic); - diag.arg( - "shift_amount", - match shift_amount { - Either::Left(v) => v.to_string(), - Either::Right(v) => v.to_string(), - }, - ); - } - BoundsCheckFailed { len, index } => { - diag.arg("len", len); - diag.arg("index", index); - } - UnterminatedCString(ptr) - | InvalidFunctionPointer(ptr) - | InvalidVaListPointer(ptr) - | InvalidVTablePointer(ptr) => { - diag.arg("pointer", ptr); - } - InvalidVTableTrait { expected_dyn_type, vtable_dyn_type } => { - diag.arg("expected_dyn_type", expected_dyn_type.to_string()); - diag.arg("vtable_dyn_type", vtable_dyn_type.to_string()); - } - PointerUseAfterFree(alloc_id, msg) => { - diag.arg("alloc_id", alloc_id).arg("operation", format!("{:?}", msg)); - } - PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, inbounds_size, msg } => { - diag.arg("alloc_size", alloc_size.bytes()); - diag.arg("pointer", { - let mut out = format!("{:?}", alloc_id); - if ptr_offset > 0 { - write!(out, "+{:#x}", ptr_offset).unwrap(); - } else if ptr_offset < 0 { - write!(out, "-{:#x}", ptr_offset.unsigned_abs()).unwrap(); - } - out - }); - diag.arg("inbounds_size", inbounds_size); - diag.arg("inbounds_size_is_neg", inbounds_size < 0); - diag.arg("inbounds_size_abs", inbounds_size.unsigned_abs()); - diag.arg("ptr_offset", ptr_offset); - diag.arg("ptr_offset_is_neg", ptr_offset < 0); - diag.arg("ptr_offset_abs", ptr_offset.unsigned_abs()); - diag.arg( - "alloc_size_minus_ptr_offset", - alloc_size.bytes().saturating_sub(ptr_offset as u64), - ); - diag.arg("operation", format!("{:?}", msg)); - } - DanglingIntPointer { addr, inbounds_size, msg } => { - if addr != 0 { - diag.arg( - "pointer", - Pointer::>::without_provenance(addr).to_string(), - ); - } - - diag.arg("inbounds_size", inbounds_size); - diag.arg("inbounds_size_is_neg", inbounds_size < 0); - diag.arg("inbounds_size_abs", inbounds_size.unsigned_abs()); - diag.arg("operation", format!("{:?}", msg)); - } - AlignmentCheckFailed(Misalignment { required, has }, msg) => { - diag.arg("required", required.bytes()); - diag.arg("has", has.bytes()); - diag.arg("msg", format!("{msg:?}")); - } - WriteToReadOnly(alloc) - | DerefFunctionPointer(alloc) - | DerefVTablePointer(alloc) - | DerefVaListPointer(alloc) - | DerefTypeIdPointer(alloc) => { - diag.arg("allocation", alloc); - } - InvalidBool(b) => { - diag.arg("value", format!("{b:02x}")); - } - InvalidChar(c) => { - diag.arg("value", format!("{c:08x}")); - } - InvalidTag(tag) => { - diag.arg("tag", format!("{tag:x}")); - } - InvalidStr(err) => { - diag.arg("err", format!("{err}")); - } - InvalidUninitBytes(Some((alloc, info))) => { - diag.arg("alloc", alloc); - diag.arg("access", info.access); - diag.arg("uninit", info.bad); - } - ScalarSizeMismatch(info) => { - diag.arg("target_size", info.target_size); - diag.arg("data_size", info.data_size); - } - InvalidNichedEnumVariantWritten { enum_ty } => { - diag.arg("ty", enum_ty); - } - AbiMismatchArgument { arg_idx, caller_ty, callee_ty } => { - diag.arg("arg_idx", arg_idx + 1); // adjust for 1-indexed lists in output - diag.arg("caller_ty", caller_ty); - diag.arg("callee_ty", callee_ty); - } - AbiMismatchReturn { caller_ty, callee_ty } => { - diag.arg("caller_ty", caller_ty); - diag.arg("callee_ty", callee_ty); - } - CVariadicMismatch { caller_is_c_variadic, callee_is_c_variadic } => { - diag.arg("caller_is_c_variadic", caller_is_c_variadic); - diag.arg("callee_is_c_variadic", callee_is_c_variadic); - } - CVariadicFixedCountMismatch { caller, callee } => { - diag.arg("caller", caller); - diag.arg("callee", callee); - } - } - } -} - -impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { - fn diagnostic_message(&self) -> DiagMessage { - use rustc_middle::mir::interpret::ValidationErrorKind::*; - - match self.kind { - PtrToUninhabited { ptr_kind: PointerKind::Box, .. } => { - msg!("{$front_matter}: encountered a box pointing to uninhabited type {$ty}") - } - PtrToUninhabited { ptr_kind: PointerKind::Ref(_), .. } => { - msg!("{$front_matter}: encountered a reference pointing to uninhabited type {$ty}") - } - - PointerAsInt { .. } => { - msg!("{$front_matter}: encountered a pointer, but {$expected}") - } - PartialPointer => { - msg!("{$front_matter}: encountered a partial pointer or a mix of pointers") - } - MutableRefToImmutable => { - msg!( - "{$front_matter}: encountered mutable reference or box pointing to read-only memory" - ) - } - NullFnPtr { .. } => { - msg!( - "{$front_matter}: encountered a {$maybe -> - [true] maybe-null - *[false] null - } function pointer" - ) - } - NeverVal => { - msg!("{$front_matter}: encountered a value of the never type `!`") - } - NonnullPtrMaybeNull { .. } => { - msg!( - "{$front_matter}: encountered a maybe-null pointer, but expected something that is definitely non-zero" - ) - } - PtrOutOfRange { .. } => { - msg!( - "{$front_matter}: encountered a pointer with unknown absolute address, but expected something that is definitely {$in_range}" - ) - } - OutOfRange { .. } => { - msg!("{$front_matter}: encountered {$value}, but expected something {$in_range}") - } - UnsafeCellInImmutable => { - msg!("{$front_matter}: encountered `UnsafeCell` in read-only memory") - } - UninhabitedVal { .. } => { - msg!("{$front_matter}: encountered a value of uninhabited type `{$ty}`") - } - InvalidEnumTag { .. } => { - msg!("{$front_matter}: encountered {$value}, but expected a valid enum tag") - } - UninhabitedEnumVariant => { - msg!("{$front_matter}: encountered an uninhabited enum variant") - } - Uninit { .. } => { - msg!("{$front_matter}: encountered uninitialized memory, but {$expected}") - } - InvalidVTablePtr { .. } => { - msg!("{$front_matter}: encountered {$value}, but expected a vtable pointer") - } - InvalidMetaWrongTrait { .. } => { - msg!( - "{$front_matter}: wrong trait in wide pointer vtable: expected `{$expected_dyn_type}`, but encountered `{$vtable_dyn_type}`" - ) - } - InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Box } => { - msg!( - "{$front_matter}: encountered invalid box metadata: slice is bigger than largest supported object" - ) - } - InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Ref(_) } => { - msg!( - "{$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object" - ) - } - - InvalidMetaTooLarge { ptr_kind: PointerKind::Box } => { - msg!( - "{$front_matter}: encountered invalid box metadata: total size is bigger than largest supported object" - ) - } - InvalidMetaTooLarge { ptr_kind: PointerKind::Ref(_) } => { - msg!( - "{$front_matter}: encountered invalid reference metadata: total size is bigger than largest supported object" - ) - } - UnalignedPtr { ptr_kind: PointerKind::Ref(_), .. } => { - msg!( - "{$front_matter}: encountered an unaligned reference (required {$required_bytes} byte alignment but found {$found_bytes})" - ) - } - UnalignedPtr { ptr_kind: PointerKind::Box, .. } => { - msg!( - "{$front_matter}: encountered an unaligned box (required {$required_bytes} byte alignment but found {$found_bytes})" - ) - } - - NullPtr { ptr_kind: PointerKind::Box, .. } => { - msg!( - "{$front_matter}: encountered a {$maybe -> - [true] maybe-null - *[false] null - } box" - ) - } - NullPtr { ptr_kind: PointerKind::Ref(_), .. } => { - msg!( - "{$front_matter}: encountered a {$maybe -> - [true] maybe-null - *[false] null - } reference" - ) - } - DanglingPtrNoProvenance { ptr_kind: PointerKind::Box, .. } => { - msg!("{$front_matter}: encountered a dangling box ({$pointer} has no provenance)") - } - DanglingPtrNoProvenance { ptr_kind: PointerKind::Ref(_), .. } => { - msg!( - "{$front_matter}: encountered a dangling reference ({$pointer} has no provenance)" - ) - } - DanglingPtrOutOfBounds { ptr_kind: PointerKind::Box } => { - msg!( - "{$front_matter}: encountered a dangling box (going beyond the bounds of its allocation)" - ) - } - DanglingPtrOutOfBounds { ptr_kind: PointerKind::Ref(_) } => { - msg!( - "{$front_matter}: encountered a dangling reference (going beyond the bounds of its allocation)" - ) - } - DanglingPtrUseAfterFree { ptr_kind: PointerKind::Box } => { - msg!("{$front_matter}: encountered a dangling box (use-after-free)") - } - DanglingPtrUseAfterFree { ptr_kind: PointerKind::Ref(_) } => { - msg!("{$front_matter}: encountered a dangling reference (use-after-free)") - } - InvalidBool { .. } => { - msg!("{$front_matter}: encountered {$value}, but expected a boolean") - } - InvalidChar { .. } => { - msg!( - "{$front_matter}: encountered {$value}, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`)" - ) - } - InvalidFnPtr { .. } => { - msg!("{$front_matter}: encountered {$value}, but expected a function pointer") - } - } - } - - fn add_args(self, err: &mut Diag<'_, G>) { - use rustc_errors::msg; - use rustc_middle::mir::interpret::ValidationErrorKind::*; - - if let PointerAsInt { .. } | PartialPointer = self.kind { - err.help(msg!("this code performed an operation that depends on the underlying bytes representing a pointer")); - err.help(msg!("the absolute address of a pointer is not known at compile-time, so such operations are not supported")); - } - - let message = if let Some(path) = self.path { - format_diag_message( - &msg!("constructing invalid value at {$path}"), - &DiagArgMap::from_iter([("path".into(), DiagArgValue::Str(path.into()))]), - ) - } else { - Cow::Borrowed("constructing invalid value") - }; - - err.arg("front_matter", message); - - fn add_range_arg( - r: WrappingRange, - max_hi: u128, - err: &mut Diag<'_, G>, - ) { - let WrappingRange { start: lo, end: hi } = r; - assert!(hi <= max_hi); - let msg = if lo > hi { - msg!("less or equal to {$hi}, or greater or equal to {$lo}") - } else if lo == hi { - msg!("equal to {$lo}") - } else if lo == 0 { - assert!(hi < max_hi, "should not be printing if the range covers everything"); - msg!("less or equal to {$hi}") - } else if hi == max_hi { - assert!(lo > 0, "should not be printing if the range covers everything"); - msg!("greater or equal to {$lo}") - } else { - msg!("in the range {$lo}..={$hi}") - }; - - let message = format_diag_message( - &msg, - &DiagArgMap::from_iter([ - ("lo".into(), DiagArgValue::Str(lo.to_string().into())), - ("hi".into(), DiagArgValue::Str(hi.to_string().into())), - ]), - ); - err.arg("in_range", message); - } - - match self.kind { - PtrToUninhabited { ty, .. } | UninhabitedVal { ty } => { - err.arg("ty", ty); - } - PointerAsInt { expected } | Uninit { expected } => { - let msg = match expected { - ExpectedKind::Reference => "expected a reference", - ExpectedKind::Box => "expected a box", - ExpectedKind::RawPtr => "expected a raw pointer", - ExpectedKind::InitScalar => "expected initialized scalar value", - ExpectedKind::Bool => "expected a boolean", - ExpectedKind::Char => "expected a unicode scalar value", - ExpectedKind::Float => "expected a floating point number", - ExpectedKind::Int => "expected an integer", - ExpectedKind::FnPtr => "expected a function pointer", - ExpectedKind::EnumTag => "expected a valid enum tag", - ExpectedKind::Str => "expected a string", - }; - err.arg("expected", msg); - } - InvalidEnumTag { value } - | InvalidVTablePtr { value } - | InvalidBool { value } - | InvalidChar { value } - | InvalidFnPtr { value } => { - err.arg("value", value); - } - PtrOutOfRange { range, max_value } => add_range_arg(range, max_value, err), - OutOfRange { range, max_value, value } => { - err.arg("value", value); - add_range_arg(range, max_value, err); - } - UnalignedPtr { required_bytes, found_bytes, .. } => { - err.arg("required_bytes", required_bytes); - err.arg("found_bytes", found_bytes); - } - DanglingPtrNoProvenance { pointer, .. } => { - err.arg("pointer", pointer); - } - InvalidMetaWrongTrait { vtable_dyn_type, expected_dyn_type } => { - err.arg("vtable_dyn_type", vtable_dyn_type.to_string()); - err.arg("expected_dyn_type", expected_dyn_type.to_string()); - } - NullPtr { maybe, .. } | NullFnPtr { maybe } => { - err.arg("maybe", maybe); - } - MutableRefToImmutable - | NonnullPtrMaybeNull - | NeverVal - | UnsafeCellInImmutable - | InvalidMetaSliceTooLarge { .. } - | InvalidMetaTooLarge { .. } - | DanglingPtrUseAfterFree { .. } - | DanglingPtrOutOfBounds { .. } - | UninhabitedEnumVariant - | PartialPointer => {} - } - } -} - -impl ReportErrorExt for UnsupportedOpInfo { - fn diagnostic_message(&self) -> DiagMessage { - match self { - UnsupportedOpInfo::Unsupported(s) => s.clone().into(), - UnsupportedOpInfo::ExternTypeField => { - "`extern type` field does not have a known offset".into() - } - UnsupportedOpInfo::UnsizedLocal => "unsized locals are not supported".into(), - UnsupportedOpInfo::ReadPartialPointer(_) => { - msg!("unable to read parts of a pointer from memory at {$ptr}") - } - UnsupportedOpInfo::ReadPointerAsInt(_) => "unable to turn pointer into integer".into(), - UnsupportedOpInfo::ThreadLocalStatic(_) => { - msg!("cannot access thread local static `{$did}`") - } - UnsupportedOpInfo::ExternStatic(_) => { - msg!("cannot access extern static `{$did}`") - } - } - .into() - } - - fn add_args(self, diag: &mut Diag<'_, G>) { - use UnsupportedOpInfo::*; - - if let ReadPointerAsInt(_) | ReadPartialPointer(_) = self { - diag.help("this code performed an operation that depends on the underlying bytes representing a pointer"); - diag.help("the absolute address of a pointer is not known at compile-time, so such operations are not supported"); - } - match self { - // `ReadPointerAsInt(Some(info))` is never printed anyway, it only serves as an error to - // be further processed by validity checking which then turns it into something nice to - // print. So it's not worth the effort of having diagnostics that can print the `info`. - UnsizedLocal - | UnsupportedOpInfo::ExternTypeField - | Unsupported(_) - | ReadPointerAsInt(_) => {} - ReadPartialPointer(ptr) => { - diag.arg("ptr", ptr); - } - ThreadLocalStatic(did) | ExternStatic(did) => rustc_middle::ty::tls::with(|tcx| { - diag.arg("did", tcx.def_path_str(did)); - }), - } - } -} - -impl<'tcx> ReportErrorExt for InterpErrorKind<'tcx> { - fn diagnostic_message(&self) -> DiagMessage { - match self { - InterpErrorKind::UndefinedBehavior(ub) => ub.diagnostic_message(), - InterpErrorKind::Unsupported(e) => e.diagnostic_message(), - InterpErrorKind::InvalidProgram(e) => e.diagnostic_message(), - InterpErrorKind::ResourceExhaustion(e) => e.diagnostic_message(), - InterpErrorKind::MachineStop(e) => e.diagnostic_message(), - } - } - fn add_args(self, diag: &mut Diag<'_, G>) { - match self { - InterpErrorKind::UndefinedBehavior(ub) => ub.add_args(diag), - InterpErrorKind::Unsupported(e) => e.add_args(diag), - InterpErrorKind::InvalidProgram(e) => e.add_args(diag), - InterpErrorKind::ResourceExhaustion(e) => e.add_args(diag), - InterpErrorKind::MachineStop(e) => e.add_args(&mut |name, value| { - diag.arg(name, value); - }), - } - } -} - -impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> { - fn diagnostic_message(&self) -> DiagMessage { - match self { - InvalidProgramInfo::TooGeneric => "encountered overly generic constant".into(), - InvalidProgramInfo::AlreadyReported(_) => { - "an error has already been reported elsewhere (this should not usually be printed)" - .into() - } - InvalidProgramInfo::Layout(e) => e.diagnostic_message(), - } - } - fn add_args(self, diag: &mut Diag<'_, G>) { - match self { - InvalidProgramInfo::TooGeneric | InvalidProgramInfo::AlreadyReported(_) => {} - InvalidProgramInfo::Layout(e) => { - // The level doesn't matter, `dummy_diag` is consumed without it being used. - let dummy_level = Level::Bug; - let dummy_diag: Diag<'_, ()> = e.into_diagnostic().into_diag(diag.dcx, dummy_level); - for (name, val) in dummy_diag.args.iter() { - diag.arg(name.clone(), val.clone()); - } - dummy_diag.cancel(); - } - } - } -} - -impl ReportErrorExt for ResourceExhaustionInfo { - fn diagnostic_message(&self) -> DiagMessage { - match self { - ResourceExhaustionInfo::StackFrameLimitReached => { - "reached the configured maximum number of stack frames" - } - ResourceExhaustionInfo::MemoryExhausted => { - "tried to allocate more memory than available to compiler" - } - ResourceExhaustionInfo::AddressSpaceFull => { - "there are no more free addresses in the address space" - } - ResourceExhaustionInfo::Interrupted => "compilation was interrupted", - } - .into() - } - fn add_args(self, _: &mut Diag<'_, G>) {} -} - impl rustc_errors::IntoDiagArg for InternKind { fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { DiagArgValue::Str(Cow::Borrowed(match self { diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index b207244349d8f..0381571ef6dae 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -6,7 +6,6 @@ use std::borrow::Cow; use either::{Left, Right}; use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, VariantIdx}; -use rustc_errors::msg; use rustc_hir::def_id::DefId; use rustc_hir::find_attr; use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; @@ -19,7 +18,7 @@ use tracing::{info, instrument, trace}; use super::{ CtfeProvenance, FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, Projectable, Provenance, ReturnAction, ReturnContinuation, Scalar, interp_ok, throw_ub, - throw_ub_custom, + throw_ub_format, }; use crate::enter_trace_span; use crate::interpret::EnteredTraceSpan; @@ -293,7 +292,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } // Find next caller arg. let Some((caller_arg, caller_abi)) = caller_args.next() else { - throw_ub_custom!(msg!("calling a function with fewer arguments than it requires")); + throw_ub_format!("calling a function with fewer arguments than it requires"); }; assert_eq!(caller_arg.layout().layout, caller_abi.layout.layout); // Sadly we cannot assert that `caller_arg.layout().ty` and `caller_abi.layout.ty` are @@ -364,12 +363,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let callee_fn_abi = self.fn_abi_of_instance_no_deduced_attrs(instance, extra_tys)?; if caller_fn_abi.conv != callee_fn_abi.conv { - throw_ub_custom!( - rustc_errors::msg!( - "calling a function with calling convention \"{$callee_conv}\" using calling convention \"{$caller_conv}\"" - ), - callee_conv = format!("{}", callee_fn_abi.conv), - caller_conv = format!("{}", caller_fn_abi.conv), + throw_ub_format!( + "calling a function with calling convention \"{callee_conv}\" using calling convention \"{caller_conv}\"", + callee_conv = callee_fn_abi.conv, + caller_conv = caller_fn_abi.conv, ) } @@ -544,7 +541,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { "mismatch between callee ABI and callee body arguments" ); if caller_args.next().is_some() { - throw_ub_custom!(msg!("calling a function with more arguments than it expected")); + throw_ub_format!("calling a function with more arguments than it expected"); } // Don't forget to check the return type! if !self.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret)? { @@ -744,9 +741,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let vtable_entries = self.vtable_entries(receiver_trait.principal(), dyn_ty); let Some(ty::VtblEntry::Method(fn_inst)) = vtable_entries.get(idx).copied() else { // FIXME(fee1-dead) these could be variants of the UB info enum instead of this - throw_ub_custom!(msg!( - "`dyn` call trying to call something that is not a method" - )); + throw_ub_format!("`dyn` call trying to call something that is not a method"); }; trace!("Virtual call dispatches to {fn_inst:#?}"); // We can also do the lookup based on `def_id` and `dyn_ty`, and check that that @@ -952,7 +947,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } ); if unwinding && self.frame_idx() == 0 { - throw_ub_custom!(msg!("unwinding past the topmost frame of the stack")); + throw_ub_format!("unwinding past the topmost frame of the stack"); } // Get out the return value. Must happen *before* the frame is popped as we have to get the diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 6972a79226f33..faef782534280 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -3,7 +3,6 @@ use std::assert_matches; use rustc_abi::{FieldIdx, Integer}; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_apfloat::{Float, FloatConvert}; -use rustc_errors::msg; use rustc_middle::mir::CastKind; use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; use rustc_middle::ty::adjustment::PointerCoercion; @@ -15,7 +14,7 @@ use tracing::trace; use super::util::ensure_monomorphic_enough; use super::{ FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy, err_inval, interp_ok, throw_ub, - throw_ub_custom, + throw_ub_format, }; use crate::enter_trace_span; use crate::interpret::Writeable; @@ -139,10 +138,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { assert!(dest.layout.is_sized()); assert_eq!(cast_ty, dest.layout.ty); // we otherwise ignore `cast_ty` enirely... if src.layout.size != dest.layout.size { - throw_ub_custom!( - msg!( - "transmuting from {$src_bytes}-byte type to {$dest_bytes}-byte type: `{$src}` -> `{$dest}`" - ), + throw_ub_format!( + "transmuting from {src_bytes}-byte type to {dest_bytes}-byte type: `{src}` -> `{dest}`", src_bytes = src.layout.size.bytes(), dest_bytes = dest.layout.size.bytes(), src = src.layout.ty, diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 956be147d7486..0bfe012bfe7a4 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -2,7 +2,6 @@ use std::debug_assert_matches; use either::{Left, Right}; use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout}; -use rustc_errors::{DiagCtxtHandle, format_diag_message, msg}; use rustc_hir::def_id::DefId; use rustc_hir::limit::Limit; use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo}; @@ -22,9 +21,9 @@ use tracing::{debug, trace}; use super::{ Frame, FrameInfo, GlobalId, InterpErrorInfo, InterpErrorKind, InterpResult, MPlaceTy, Machine, MemPlaceMeta, Memory, OpTy, Place, PlaceTy, PointerArithmetic, Projectable, Provenance, - err_inval, interp_ok, throw_inval, throw_ub, throw_ub_custom, + err_inval, interp_ok, throw_inval, throw_ub, throw_ub_format, }; -use crate::{ReportErrorExt, enter_trace_span, util}; +use crate::{enter_trace_span, util}; pub struct InterpCx<'tcx, M: Machine<'tcx>> { /// Stores the `Machine` instance. @@ -228,17 +227,10 @@ pub(super) fn from_known_layout<'tcx>( /// /// This is NOT the preferred way to render an error; use `report` from `const_eval` instead. /// However, this is useful when error messages appear in ICEs. -pub fn format_interp_error<'tcx>(dcx: DiagCtxtHandle<'_>, e: InterpErrorInfo<'tcx>) -> String { +pub fn format_interp_error<'tcx>(e: InterpErrorInfo<'tcx>) -> String { let (e, backtrace) = e.into_parts(); backtrace.print_backtrace(); - // FIXME(fee1-dead), HACK: we want to use the error as title therefore we can just extract the - // label and arguments from the InterpError. - let mut diag = dcx.struct_allow(""); - let msg = e.diagnostic_message(); - e.add_args(&mut diag); - let msg = format_diag_message(&msg, &diag.args).into_owned(); - diag.cancel(); - msg + e.to_string() } impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { @@ -556,9 +548,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { mir::UnwindAction::Cleanup(block) => Left(mir::Location { block, statement_index: 0 }), mir::UnwindAction::Continue => Right(self.frame_mut().body.span), mir::UnwindAction::Unreachable => { - throw_ub_custom!(msg!( - "unwinding past a stack frame that does not allow unwinding" - )); + throw_ub_format!("unwinding past a stack frame that does not allow unwinding"); } mir::UnwindAction::Terminate(reason) => { self.frame_mut().loc = Right(self.frame_mut().body.span); diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index c9106d691f7ce..7f7c69adf4f84 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -8,7 +8,6 @@ use std::assert_matches; use rustc_abi::{FieldIdx, HasDataLayout, Size, VariantIdx}; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; -use rustc_errors::msg; use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint}; use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty::layout::TyAndLayout; @@ -21,8 +20,8 @@ use super::memory::MemoryKind; use super::util::ensure_monomorphic_enough; use super::{ AllocId, CheckInAllocMsg, ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Pointer, - PointerArithmetic, Projectable, Provenance, Scalar, err_ub_custom, err_unsup_format, interp_ok, - throw_inval, throw_ub, throw_ub_custom, throw_ub_format, throw_unsup_format, + PointerArithmetic, Projectable, Provenance, Scalar, err_ub_format, err_unsup_format, interp_ok, + throw_inval, throw_ub, throw_ub_format, throw_unsup_format, }; use crate::interpret::Writeable; @@ -402,10 +401,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } _ => { // Not into the same allocation -- this is UB. - throw_ub_custom!( - msg!( - "`{$name}` called on two different pointers that are not both derived from the same allocation" - ), + throw_ub_format!( + "`{name}` called on two different pointers that are not both derived from the same allocation", name = intrinsic_name, ); } @@ -425,16 +422,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if overflowed.to_bool()? { // a < b if intrinsic_name == sym::ptr_offset_from_unsigned { - throw_ub_custom!( - msg!( - "`ptr_offset_from_unsigned` called when first pointer has smaller {$is_addr -> - [true] address - *[false] offset - } than second: {$a_offset} < {$b_offset}" - ), + throw_ub_format!( + "`ptr_offset_from_unsigned` called when first pointer has smaller {is_addr} than second: {a_offset} < {b_offset}", a_offset = a_offset, b_offset = b_offset, - is_addr = is_addr, + is_addr = if is_addr { "address" } else { "offset" }, ); } // The signed form of the intrinsic allows this. If we interpret the @@ -442,11 +434,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // seems *positive* or equal to isize::MIN, they were more than isize::MAX apart. let dist = val.to_target_isize(self)?; if dist >= 0 || i128::from(dist) == self.pointer_size().signed_int_min() { - throw_ub_custom!( - msg!( - "`{$name}` called when first pointer is too far before second" - ), - name = intrinsic_name, + throw_ub_format!( + "`{intrinsic_name}` called when first pointer is too far before second" ); } dist @@ -456,11 +445,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // If converting to isize produced a *negative* result, we had an overflow // because they were more than isize::MAX apart. if dist < 0 { - throw_ub_custom!( - msg!( - "`{$name}` called when first pointer is too far ahead of second" - ), - name = intrinsic_name, + throw_ub_format!( + "`{intrinsic_name}` called when first pointer is too far ahead of second" ); } dist @@ -477,14 +463,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { && let Ok((b_alloc_id, ..)) = self.ptr_try_get_alloc_id(b, 0) && a_alloc_id == b_alloc_id { - err_ub_custom!( - msg!("`{$name}` called on two different pointers where the memory range between them is not in-bounds of an allocation"), - name = intrinsic_name, + err_ub_format!( + "`{intrinsic_name}` called on two different pointers where the memory range between them is not in-bounds of an allocation" ) } else { - err_ub_custom!( - msg!("`{$name}` called on two different pointers that are not both derived from the same allocation"), - name = intrinsic_name, + err_ub_format!( + "`{intrinsic_name}` called on two different pointers that are not both derived from the same allocation" ) } })?; @@ -497,9 +481,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) .map_err_kind(|_| { // Make the error more specific. - err_ub_custom!( - msg!("`{$name}` called on two different pointers that are not both derived from the same allocation"), - name = intrinsic_name, + err_ub_format!( + "`{intrinsic_name}` called on two different pointers that are not both derived from the same allocation" ) })?; @@ -779,7 +762,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let op = self.eval_operand(op, None)?; let cond = self.read_scalar(&op)?.to_bool()?; if !cond { - throw_ub_custom!(msg!("`assume` called with `false`")); + throw_ub_format!("`assume` called with `false`"); } interp_ok(()) } @@ -809,7 +792,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let bits_out = match name { sym::ctpop => u128::from(bits.count_ones()), sym::ctlz_nonzero | sym::cttz_nonzero if bits == 0 => { - throw_ub_custom!(msg!("`{$name}` called on 0"), name = name,); + throw_ub_format!("`{name}` called on 0"); } sym::ctlz | sym::ctlz_nonzero => u128::from(bits.leading_zeros()) - extra, sym::cttz | sym::cttz_nonzero => u128::from((bits << extra).trailing_zeros()) - extra, @@ -841,11 +824,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let rem = self.binary_op(BinOp::Rem, a, b)?; // sign does not matter for 0 test, so `to_bits` is fine if rem.to_scalar().to_bits(a.layout.size)? != 0 { - throw_ub_custom!( - msg!("exact_div: {$a} cannot be divided by {$b} without remainder"), - a = format!("{a}"), - b = format!("{b}") - ) + throw_ub_format!("exact_div: {a} cannot be divided by {b} without remainder") } // `Rem` says this is all right, so we can let `Div` do its job. let res = self.binary_op(BinOp::Div, a, b)?; @@ -926,8 +905,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let (size, align) = (layout.size, layout.align.abi); let size = self.compute_size_in_bytes(size, count).ok_or_else(|| { - err_ub_custom!( - msg!("overflow computing total size of `{$name}`"), + err_ub_format!( + "overflow computing total size of `{name}`", name = if nonoverlapping { "copy_nonoverlapping" } else { "copy" } ) })?; @@ -990,9 +969,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max), // but no actual allocation can be big enough for the difference to be noticeable. - let len = self.compute_size_in_bytes(layout.size, count).ok_or_else(|| { - err_ub_custom!(msg!("overflow computing total size of `{$name}`"), name = name) - })?; + let len = self + .compute_size_in_bytes(layout.size, count) + .ok_or_else(|| err_ub_format!("overflow computing total size of `{name}`"))?; let bytes = std::iter::repeat_n(byte, len.bytes_usize()); self.write_bytes_ptr(dst, bytes) diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 8a864f372b9c3..214d653d23c13 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -14,17 +14,16 @@ use std::{assert_matches, fmt, ptr}; use rustc_abi::{Align, HasDataLayout, Size}; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_errors::msg; +use rustc_middle::bug; use rustc_middle::mir::display_allocation; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; -use rustc_middle::{bug, throw_ub_format}; use tracing::{debug, instrument, trace}; use super::{ AllocBytes, AllocId, AllocInit, AllocMap, AllocRange, Allocation, CheckAlignMsg, CheckInAllocMsg, CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, MPlaceTy, Machine, MayLeak, Misalignment, Pointer, PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, - err_ub_custom, interp_ok, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, + err_ub_format, interp_ok, throw_ub, throw_ub_format, throw_unsup, throw_unsup_format, }; use crate::const_eval::ConstEvalErrKind; @@ -310,16 +309,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, Pointer> { let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr, 0)?; if offset.bytes() != 0 { - throw_ub_custom!( - msg!( - "{$kind -> - [dealloc] deallocating - [realloc] reallocating - *[other] {\"\"} - } {$ptr} which does not point to the beginning of an object" - ), - ptr = format!("{ptr:?}"), - kind = "realloc" + throw_ub_format!( + "reallocating {ptr} which does not point to the beginning of an object" ); } @@ -393,19 +384,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { kind: MemoryKind, ) -> InterpResult<'tcx> { let (alloc_id, offset, prov) = self.ptr_get_alloc_id(ptr, 0)?; - trace!("deallocating: {alloc_id:?}"); + trace!("deallocating: {alloc_id}"); if offset.bytes() != 0 { - throw_ub_custom!( - msg!( - "{$kind -> - [dealloc] deallocating - [realloc] reallocating - *[other] {\"\"} - } {$ptr} which does not point to the beginning of an object" - ), - ptr = format!("{ptr:?}"), - kind = "dealloc", + throw_ub_format!( + "deallocating {ptr} which does not point to the beginning of an object" ); } @@ -413,60 +396,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Deallocating global memory -- always an error return Err(match self.tcx.try_get_global_alloc(alloc_id) { Some(GlobalAlloc::Function { .. }) => { - err_ub_custom!( - msg!( - "deallocating {$alloc_id}, which is {$kind -> - [fn] a function - [vtable] a vtable - [static_mem] static memory - *[other] {\"\"} - }" - ), - alloc_id = alloc_id, - kind = "fn", - ) + err_ub_format!("deallocating {alloc_id}, which is a function") } Some(GlobalAlloc::VTable(..)) => { - err_ub_custom!( - msg!( - "deallocating {$alloc_id}, which is {$kind -> - [fn] a function - [vtable] a vtable - [static_mem] static memory - *[other] {\"\"} - }" - ), - alloc_id = alloc_id, - kind = "vtable", - ) + err_ub_format!("deallocating {alloc_id}, which is a vtable") } Some(GlobalAlloc::TypeId { .. }) => { - err_ub_custom!( - msg!( - "deallocating {$alloc_id}, which is {$kind -> - [fn] a function - [vtable] a vtable - [static_mem] static memory - *[other] {\"\"} - }" - ), - alloc_id = alloc_id, - kind = "typeid", - ) + err_ub_format!("deallocating {alloc_id}, which is a type id") } Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => { - err_ub_custom!( - msg!( - "deallocating {$alloc_id}, which is {$kind -> - [fn] a function - [vtable] a vtable - [static_mem] static memory - *[other] {\"\"} - }" - ), - alloc_id = alloc_id, - kind = "static_mem" - ) + err_ub_format!("deallocating {alloc_id}, which is static memory") } None => err_ub!(PointerUseAfterFree(alloc_id, CheckInAllocMsg::MemoryAccess)), }) @@ -474,25 +413,17 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; if alloc.mutability.is_not() { - throw_ub_custom!(msg!("deallocating immutable allocation {$alloc}"), alloc = alloc_id,); + throw_ub_format!("deallocating immutable allocation {alloc_id}"); } if alloc_kind != kind { - throw_ub_custom!( - msg!( - "deallocating {$alloc}, which is {$alloc_kind} memory, using {$kind} deallocation operation" - ), - alloc = alloc_id, - alloc_kind = format!("{alloc_kind}"), - kind = format!("{kind}"), + throw_ub_format!( + "deallocating {alloc_id}, which is {alloc_kind} memory, using {kind} deallocation operation", ); } if let Some((size, align)) = old_size_and_align { if size != alloc.size() || align != alloc.align { - throw_ub_custom!( - msg!( - "incorrect layout on deallocation: {$alloc} has size {$size} and alignment {$align}, but gave size {$size_found} and alignment {$align_found}" - ), - alloc = alloc_id, + throw_ub_format!( + "incorrect layout on deallocation: {alloc_id} has size {size} and alignment {align}, but gave size {size_found} and alignment {align_found}", size = alloc.size().bytes(), align = alloc.align.bytes(), size_found = size.bytes(), @@ -1653,9 +1584,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if (src_offset <= dest_offset && src_offset + size > dest_offset) || (dest_offset <= src_offset && dest_offset + size > src_offset) { - throw_ub_custom!(msg!( - "`copy_nonoverlapping` called on overlapping ranges" - )); + throw_ub_format!("`copy_nonoverlapping` called on overlapping ranges"); } } } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 677bbf5d41e61..41a4cb2f23a1c 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -5,7 +5,7 @@ //! to be const-safe. use std::borrow::Cow; -use std::fmt::Write; +use std::fmt::{self, Write}; use std::hash::Hash; use std::mem; use std::num::NonZero; @@ -20,10 +20,9 @@ use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_middle::bug; -use rustc_middle::mir::interpret::ValidationErrorKind::{self, *}; use rustc_middle::mir::interpret::{ - ExpectedKind, InterpErrorKind, InvalidMetaKind, Misalignment, PointerKind, Provenance, - UnsupportedOpInfo, ValidationErrorInfo, alloc_range, interp_ok, + InterpErrorKind, InvalidMetaKind, Misalignment, Provenance, UnsupportedOpInfo, alloc_range, + interp_ok, }; use rustc_middle::ty::layout::{LayoutCx, TyAndLayout}; use rustc_middle::ty::{self, Ty}; @@ -46,7 +45,7 @@ use super::UndefinedBehaviorInfo::*; use super::UnsupportedOpInfo::*; macro_rules! err_validation_failure { - ($where:expr, $kind: expr) => {{ + ($where:expr, $msg:expr ) => {{ let where_ = &$where; let path = if !where_.is_empty() { let mut path = String::new(); @@ -56,13 +55,20 @@ macro_rules! err_validation_failure { None }; - err_ub!(ValidationError(ValidationErrorInfo { path, kind: $kind })) + #[allow(unused)] + use ValidationErrorKind::*; + let msg = ValidationErrorKind::from($msg); + err_ub!(ValidationError { + path, + ptr_bytes_warning: msg.ptr_bytes_warning(), + msg: msg.to_string(), + }) }}; } macro_rules! throw_validation_failure { - ($where:expr, $kind: expr) => { - do yeet err_validation_failure!($where, $kind) + ($where:expr, $msg:expr ) => { + do yeet err_validation_failure!($where, $msg) }; } @@ -73,32 +79,15 @@ macro_rules! throw_validation_failure { /// can possibly happen: /// /// ```ignore(illustrative) -/// let v = try_validation!(some_fn(), some_path, { -/// Foo | Bar | Baz => { "some failure" }, +/// let v = try_validation!(some_fn(x), some_path, { +/// Foo | Bar | Baz => format!("some failure involving {x}"), /// }); /// ``` /// /// The patterns must be of type `UndefinedBehaviorInfo`. -/// An additional expected parameter can also be added to the failure message: -/// -/// ```ignore(illustrative) -/// let v = try_validation!(some_fn(), some_path, { -/// Foo | Bar | Baz => { "some failure" } expected { "something that wasn't a failure" }, -/// }); -/// ``` -/// -/// An additional nicety is that both parameters actually take format args, so you can just write -/// the format string in directly: -/// -/// ```ignore(illustrative) -/// let v = try_validation!(some_fn(), some_path, { -/// Foo | Bar | Baz => { "{:?}", some_failure } expected { "{}", expected_value }, -/// }); -/// ``` -/// macro_rules! try_validation { ($e:expr, $where:expr, - $( $( $p:pat_param )|+ => $kind: expr ),+ $(,)? + $( $( $p:pat_param )|+ => $msg:expr ),+ $(,)? ) => {{ $e.map_err_kind(|e| { // We catch the error and turn it into a validation failure. We are okay with @@ -108,7 +97,7 @@ macro_rules! try_validation { $($p)|+ => { err_validation_failure!( $where, - $kind + $msg ) } ),+, @@ -118,6 +107,130 @@ macro_rules! try_validation { }}; } +#[derive(Debug, Clone, Copy)] +enum PointerKind { + Ref(Mutability), + Box, +} + +impl fmt::Display for PointerKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let str = match self { + PointerKind::Ref(_) => "reference", + PointerKind::Box => "box", + }; + write!(f, "{str}") + } +} + +#[derive(Debug)] +enum ExpectedKind { + Reference, + Box, + RawPtr, + InitScalar, + Bool, + Char, + Float, + Int, + FnPtr, + Str, +} + +impl fmt::Display for ExpectedKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let str = match self { + ExpectedKind::Reference => "expected a reference", + ExpectedKind::Box => "expected a box", + ExpectedKind::RawPtr => "expected a raw pointer", + ExpectedKind::InitScalar => "expected initialized scalar value", + ExpectedKind::Bool => "expected a boolean", + ExpectedKind::Char => "expected a unicode scalar value", + ExpectedKind::Float => "expected a floating point number", + ExpectedKind::Int => "expected an integer", + ExpectedKind::FnPtr => "expected a function pointer", + ExpectedKind::Str => "expected a string", + }; + write!(f, "{str}") + } +} + +impl From for ExpectedKind { + fn from(x: PointerKind) -> ExpectedKind { + match x { + PointerKind::Box => ExpectedKind::Box, + PointerKind::Ref(_) => ExpectedKind::Reference, + } + } +} + +/// Validation errors that can be emitted in one than one place get a variant here so that +/// we format them consistently. Everything else uses the `String` fallback. +#[derive(Debug)] +enum ValidationErrorKind<'tcx> { + Uninit { + expected: ExpectedKind, + }, + PointerAsInt { + expected: ExpectedKind, + }, + PartialPointer, + InvalidMetaWrongTrait { + /// The vtable that was actually referenced by the wide pointer metadata. + vtable_dyn_type: &'tcx ty::List>, + /// The vtable that was expected at the point in MIR that it was accessed. + expected_dyn_type: &'tcx ty::List>, + }, + GeneralError { + msg: String, + }, +} + +impl<'tcx> ValidationErrorKind<'tcx> { + // We don't do this via `fmt::Display` to so that we can do a move in the `GeneralError` case. + fn to_string(self) -> String { + use ValidationErrorKind::*; + match self { + Uninit { expected } => format!("encountered uninitialized memory, but {expected}"), + PointerAsInt { expected } => format!("encountered a pointer, but {expected}"), + PartialPointer => format!("encountered a partial pointer or a mix of pointers"), + InvalidMetaWrongTrait { vtable_dyn_type, expected_dyn_type } => format!( + "wrong trait in wide pointer vtable: expected `{expected_dyn_type}`, but encountered `{vtable_dyn_type}`" + ), + GeneralError { msg } => msg, + } + } + + fn ptr_bytes_warning(&self) -> bool { + use ValidationErrorKind::*; + matches!(self, PointerAsInt { .. } | PartialPointer) + } +} + +impl<'tcx> From for ValidationErrorKind<'tcx> { + fn from(msg: String) -> Self { + ValidationErrorKind::GeneralError { msg } + } +} + +fn fmt_range(r: WrappingRange, max_hi: u128) -> String { + let WrappingRange { start: lo, end: hi } = r; + assert!(hi <= max_hi); + if lo > hi { + format!("less or equal to {hi}, or greater or equal to {lo}") + } else if lo == hi { + format!("equal to {lo}") + } else if lo == 0 { + assert!(hi < max_hi, "should not be printing if the range covers everything"); + format!("less or equal to {hi}") + } else if hi == max_hi { + assert!(lo > 0, "should not be printing if the range covers everything"); + format!("greater or equal to {lo}") + } else { + format!("in the range {lo}..={hi}") + } +} + /// We want to show a nice path to the invalid field for diagnostics, /// but avoid string operations in the happy case where no error happens. /// So we track a `Vec` where `PathElem` contains all the data we @@ -193,7 +306,6 @@ impl RefTracking } } -// FIXME make this translatable as well? /// Format a path fn write_path(out: &mut String, path: &[PathElem]) { use self::PathElem::*; @@ -459,10 +571,9 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { self.ecx.get_ptr_vtable_ty(vtable, Some(data)), self.path, Ub(DanglingIntPointer{ .. } | InvalidVTablePointer(..)) => - InvalidVTablePtr { value: format!("{vtable}") }, - Ub(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type }) => { - InvalidMetaWrongTrait { vtable_dyn_type, expected_dyn_type } - }, + format!("encountered {vtable}, but expected a vtable pointer"), + Ub(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type }) => + InvalidMetaWrongTrait { expected_dyn_type, vtable_dyn_type }, ); } ty::Slice(..) | ty::Str => { @@ -497,10 +608,13 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { let size_and_align = try_validation!( self.ecx.size_and_align_of_val(&place), self.path, - Ub(InvalidMeta(msg)) => match msg { - InvalidMetaKind::SliceTooBig => InvalidMetaSliceTooLarge { ptr_kind }, - InvalidMetaKind::TooBig => InvalidMetaTooLarge { ptr_kind }, - } + Ub(InvalidMeta(msg)) => format!( + "encountered invalid {ptr_kind} metadata: {}", + match msg { + InvalidMetaKind::SliceTooBig => "slice is bigger than largest supported object", + InvalidMetaKind::TooBig => "total size is bigger than largest supported object", + } + ) ); let (size, align) = size_and_align // for the purpose of validity, consider foreign types to have @@ -508,11 +622,8 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { // alignment should take attributes into account). .unwrap_or_else(|| (place.layout.size, place.layout.align.abi)); + // If we're not allow to dangle, make sure this is dereferenceable. if !self.may_dangle { - // Make sure this is dereferenceable and all. - - // Direct call to `check_ptr_access_align` checks alignment even on CTFE machines. - // Call `check_ptr_access` to avoid checking alignment here. try_validation!( self.ecx.check_ptr_access( place.ptr(), @@ -520,31 +631,31 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { CheckInAllocMsg::Dereferenceable, // will anyway be replaced by validity message ), self.path, - Ub(DanglingIntPointer { addr: 0, .. }) => NullPtr { ptr_kind, maybe: false }, - Ub(DanglingIntPointer { addr: i, .. }) => DanglingPtrNoProvenance { - ptr_kind, - pointer: format!("{}", Pointer::>::without_provenance(i)) - }, - Ub(PointerOutOfBounds { .. }) => DanglingPtrOutOfBounds { - ptr_kind - }, - Ub(PointerUseAfterFree(..)) => DanglingPtrUseAfterFree { - ptr_kind, - }, + Ub(DanglingIntPointer { addr: 0, .. }) => + format!("encountered a null {ptr_kind}"), + Ub(DanglingIntPointer { addr: i, .. }) => + format!( + "encountered a dangling {ptr_kind} ({ptr} has no provenance)", + ptr = Pointer::>::without_provenance(i) + ), + Ub(PointerOutOfBounds { .. }) => + format!("encountered a dangling {ptr_kind} (going beyond the bounds of its allocation)"), + Ub(PointerUseAfterFree(..)) => + format!("encountered a dangling {ptr_kind} (use-after-free)"), ); } - + // Check alignment after dereferenceable (if both are violated, trigger the error above). try_validation!( self.ecx.check_ptr_align( place.ptr(), align, ), self.path, - Ub(AlignmentCheckFailed(Misalignment { required, has }, _msg)) => UnalignedPtr { - ptr_kind, - required_bytes: required.bytes(), - found_bytes: has.bytes() - }, + Ub(AlignmentCheckFailed(Misalignment { required, has }, _msg)) => format!( + "encountered an unaligned {ptr_kind} (required {required_bytes} byte alignment but found {found_bytes})", + required_bytes = required.bytes(), + found_bytes = has.bytes() + ), ); // Make sure this is non-null. This is obviously needed when `may_dangle` is set, @@ -553,12 +664,21 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { let scalar = Scalar::from_maybe_pointer(place.ptr(), self.ecx); if self.ecx.scalar_may_be_null(scalar)? { let maybe = !M::Provenance::OFFSET_IS_ADDR && matches!(scalar, Scalar::Ptr(..)); - throw_validation_failure!(self.path, NullPtr { ptr_kind, maybe }) + throw_validation_failure!( + self.path, + format!( + "encountered a {maybe}null {ptr_kind}", + maybe = if maybe { "maybe-" } else { "" } + ) + ) } // Do not allow references to uninhabited types. if place.layout.is_uninhabited() { let ty = place.layout.ty; - throw_validation_failure!(self.path, PtrToUninhabited { ptr_kind, ty }) + throw_validation_failure!( + self.path, + format!("encountered a {ptr_kind} pointing to uninhabited type {ty}") + ) } // Recursive checking if let Some(ref_tracking) = self.ref_tracking.as_deref_mut() { @@ -586,7 +706,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { // though for zero-sized references this isn't really UB. // A potential future alternative would be to resurrect this as a zero-sized allocation // (which codegen will then compile to an aligned dummy pointer anyway). - throw_validation_failure!(self.path, DanglingPtrUseAfterFree { ptr_kind }); + throw_validation_failure!( + self.path, + format!("encountered a dangling {ptr_kind} (use-after-free)") + ); }; let (size, _align) = global_alloc.size_and_align(*self.ecx.tcx, self.ecx.typing_env); @@ -649,7 +772,12 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { && alloc_actual_mutbl == Mutability::Not { // This can actually occur with transmutes. - throw_validation_failure!(self.path, MutableRefToImmutable); + throw_validation_failure!( + self.path, + format!( + "encountered mutable reference or box pointing to read-only memory" + ) + ); } } } @@ -692,9 +820,8 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { try_validation!( scalar.to_bool(), self.path, - Ub(InvalidBool(..)) => ValidationErrorKind::InvalidBool { - value: format!("{scalar:x}"), - } + Ub(InvalidBool(..)) => + format!("encountered {scalar:x}, but expected a boolean"), ); if self.reset_provenance_and_padding { self.ecx.clear_provenance(value)?; @@ -707,9 +834,9 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { try_validation!( scalar.to_char(), self.path, - Ub(InvalidChar(..)) => ValidationErrorKind::InvalidChar { - value: format!("{scalar:x}"), - } + Ub(InvalidChar(..)) => + format!("encountered {scalar:x}, but expected a valid unicode scalar value \ + (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`)") ); if self.reset_provenance_and_padding { self.ecx.clear_provenance(value)?; @@ -755,7 +882,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { self.ecx.get_ptr_fn(ptr), self.path, Ub(DanglingIntPointer{ .. } | InvalidFunctionPointer(..)) => - InvalidFnPtr { value: format!("{ptr}") }, + format!("encountered {ptr}, but expected a function pointer"), ); // FIXME: Check if the signature matches } else { @@ -764,7 +891,13 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { if self.ecx.scalar_may_be_null(scalar)? { let maybe = !M::Provenance::OFFSET_IS_ADDR && matches!(scalar, Scalar::Ptr(..)); - throw_validation_failure!(self.path, NullFnPtr { maybe }); + throw_validation_failure!( + self.path, + format!( + "encountered a {maybe}null function pointer", + maybe = if maybe { "maybe-" } else { "" } + ) + ); } } if self.reset_provenance_and_padding { @@ -777,7 +910,12 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { } interp_ok(true) } - ty::Never => throw_validation_failure!(self.path, NeverVal), + ty::Never => { + throw_validation_failure!( + self.path, + format!("encountered a value of the never type `!`") + ) + } ty::Foreign(..) | ty::FnDef(..) => { // Nothing to check. interp_ok(true) @@ -826,7 +964,12 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { if start == 1 && end == max_value { // Only null is the niche. So make sure the ptr is NOT null. if self.ecx.scalar_may_be_null(scalar)? { - throw_validation_failure!(self.path, NonnullPtrMaybeNull) + throw_validation_failure!( + self.path, + format!( + "encountered a maybe-null pointer, but expected something that is definitely non-zero" + ) + ) } else { return interp_ok(()); } @@ -834,11 +977,13 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { // Easy. (This is reachable if `enforce_number_validity` is set.) return interp_ok(()); } else { - // Conservatively, we reject, because the pointer *could* have a bad - // value. + // Conservatively, we reject, because the pointer *could* have a bad value. throw_validation_failure!( self.path, - PtrOutOfRange { range: valid_range, max_value } + format!( + "encountered a pointer with unknown absolute address, but expected something that is definitely {in_range}", + in_range = fmt_range(valid_range, max_value) + ) ) } } @@ -849,7 +994,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { } else { throw_validation_failure!( self.path, - OutOfRange { value: format!("{bits}"), range: valid_range, max_value } + format!( + "encountered {bits}, but expected something {in_range}", + in_range = fmt_range(valid_range, max_value) + ) ) } } @@ -1054,10 +1202,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, interp_ok(try_validation!( this.ecx.read_discriminant(val), this.path, - Ub(InvalidTag(val)) => InvalidEnumTag { - value: format!("{val:x}"), - }, - Ub(UninhabitedEnumVariantRead(_)) => UninhabitedEnumVariant, + Ub(InvalidTag(val)) => + format!("encountered {val:x}, but expected a valid enum tag"), + Ub(UninhabitedEnumVariantRead(_)) => + format!("encountered an uninhabited enum variant"), // Uninit / bad provenance are not possible since the field was already previously // checked at its integer type. )) @@ -1104,7 +1252,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, let zst = self.ecx.size_and_align_of_val(val)?.is_some_and(|(s, _a)| s.bytes() == 0); if !zst && !val.layout.ty.is_freeze(*self.ecx.tcx, self.ecx.typing_env) { if !self.in_mutable_memory(val) { - throw_validation_failure!(self.path, UnsafeCellInImmutable); + throw_validation_failure!( + self.path, + format!("encountered `UnsafeCell` in read-only memory") + ); } } } @@ -1154,7 +1305,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, && def.is_unsafe_cell() { if !self.in_mutable_memory(val) { - throw_validation_failure!(self.path, UnsafeCellInImmutable); + throw_validation_failure!( + self.path, + format!("encountered `UnsafeCell` in read-only memory") + ); } } } @@ -1164,11 +1318,14 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, ty::Str => { let mplace = val.assert_mem_place(); // strings are unsized and hence never immediate let len = mplace.len(self.ecx)?; + let expected = ExpectedKind::Str; try_validation!( self.ecx.read_bytes_ptr_strip_provenance(mplace.ptr(), Size::from_bytes(len)), self.path, - Ub(InvalidUninitBytes(..)) => Uninit { expected: ExpectedKind::Str }, - Unsup(ReadPointerAsInt(_)) => PointerAsInt { expected: ExpectedKind::Str } + Ub(InvalidUninitBytes(..)) => + Uninit { expected }, + Unsup(ReadPointerAsInt(_)) => + PointerAsInt { expected }, ); } ty::Array(tys, ..) | ty::Slice(tys) @@ -1199,7 +1356,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, Left(mplace) => mplace, Right(imm) => match *imm { Immediate::Uninit => - throw_validation_failure!(self.path, Uninit { expected }), + throw_validation_failure!( + self.path, + Uninit { expected } + ), Immediate::Scalar(..) | Immediate::ScalarPair(..) => bug!("arrays/slices can never have Scalar/ScalarPair layout"), } @@ -1229,7 +1389,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, if matches!(kind, Ub(InvalidUninitBytes(_))) { err_validation_failure!(self.path, Uninit { expected }) } else { - err_validation_failure!(self.path, PointerAsInt { expected }) + err_validation_failure!(self.path, PointerAsInt {expected}) } } @@ -1292,9 +1452,8 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, self.path, // It's not great to catch errors here, since we can't give a very good path, // but it's better than ICEing. - Ub(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type }) => { - InvalidMetaWrongTrait { vtable_dyn_type, expected_dyn_type } - }, + Ub(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type }) => + InvalidMetaWrongTrait { expected_dyn_type, vtable_dyn_type }, ); } } @@ -1310,7 +1469,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // MyNewtype and then the scalar in there). if val.layout.is_uninhabited() { let ty = val.layout.ty; - throw_validation_failure!(self.path, UninhabitedVal { ty }); + throw_validation_failure!( + self.path, + format!("encountered a value of uninhabited type `{ty}`") + ); } match val.layout.backend_repr { BackendRepr::Scalar(scalar_layout) => { @@ -1383,10 +1545,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | InterpErrorKind::InvalidProgram(_) | InterpErrorKind::Unsupported(UnsupportedOpInfo::ExternTypeField) ) { - bug!( - "Unexpected error during validation: {}", - format_interp_error(self.tcx.dcx(), err) - ); + bug!("Unexpected error during validation: {}", format_interp_error(err)); } err }) diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index ade6f1c094754..a9fff6d593261 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -22,8 +22,6 @@ use std::sync::atomic::AtomicBool; use rustc_middle::ty; use rustc_middle::util::Providers; -pub use self::errors::ReportErrorExt; - pub fn provide(providers: &mut Providers) { const_eval::provide(&mut providers.queries); providers.queries.tag_for_variant = const_eval::tag_for_variant_provider; diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index 384d7858366de..00835a3cc990a 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -2,6 +2,7 @@ use rustc_abi::{BackendRepr, FieldsShape, Scalar, Variants}; use rustc_middle::ty::layout::{ HasTyCtxt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement, }; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{PseudoCanonicalInput, ScalarInt, Ty, TyCtxt}; use rustc_middle::{bug, ty}; use rustc_span::DUMMY_SP; @@ -70,13 +71,19 @@ fn check_validity_requirement_strict<'tcx>( // due to this. // The value we are validating is temporary and discarded at the end of this function, so // there is no point in resetting provenance and padding. - cx.validate_operand( - &allocated.into(), - /*recursive*/ false, - /*reset_provenance_and_padding*/ false, + // This is pretty inefficient: we do the full path tracking and even format an error message + // in case there is a problem, only to entirely throw that away again. For a nightly-only + // option this is fine, but if this is ever meant to be stable we should probably add + // a "fast mode" to validation. + with_no_trimmed_paths!( + cx.validate_operand( + &allocated.into(), + /*recursive*/ false, + /*reset_provenance_and_padding*/ false, + ) + .discard_err() + .is_some() ) - .discard_err() - .is_some() } /// Implements the 'lax' (default) version of the [`check_validity_requirement`] checks; see that diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 42c605d34814f..e50cbbbf06e0b 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -59,15 +59,10 @@ impl Diagnostic<'_, G> for TargetDataLayoutErrors<'_> { } TargetDataLayoutErrors::InvalidAlignment { cause, err } => { Diag::new(dcx, level, msg!( - "invalid alignment for `{$cause}` in \"data-layout\": `{$align}` is {$err_kind -> - [not_power_of_two] not a power of 2 - [too_large] too large - *[other] {\"\"} - }" + "invalid alignment for `{$cause}` in \"data-layout\": {$err}" )) .with_arg("cause", cause) - .with_arg("err_kind", err.diag_ident()) - .with_arg("align", err.align()) + .with_arg("err", err.to_string()) } TargetDataLayoutErrors::InconsistentTargetArchitecture { dl, target } => { Diag::new(dcx, level, msg!( diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 7fa818ba7d3ae..c0dfc18689ba8 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -341,13 +341,13 @@ impl AllocError { } /// The information that makes up a memory access: offset and size. -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub struct AllocRange { pub start: Size, pub size: Size, } -impl fmt::Debug for AllocRange { +impl fmt::Display for AllocRange { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "[{:#x}..{:#x}]", self.start.bytes(), self.end().bytes()) } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 035ffd362a6bb..2c3d114a0f25e 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -4,9 +4,9 @@ use std::borrow::Cow; use std::{convert, fmt, mem, ops}; use either::Either; -use rustc_abi::{Align, Size, VariantIdx, WrappingRange}; +use rustc_abi::{Align, Size, VariantIdx}; use rustc_data_structures::sync::Lock; -use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, ErrorGuaranteed, IntoDiagArg}; +use rustc_errors::{DiagArgValue, ErrorGuaranteed, IntoDiagArg}; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_session::CtfeBacktrace; use rustc_span::def_id::DefId; @@ -14,8 +14,9 @@ use rustc_span::{DUMMY_SP, Span, Symbol}; use super::{AllocId, AllocRange, ConstAllocation, Pointer, Scalar}; use crate::error; +use crate::mir::interpret::CtfeProvenance; use crate::mir::{ConstAlloc, ConstValue}; -use crate::ty::{self, Mutability, Ty, TyCtxt, ValTree, layout, tls}; +use crate::ty::{self, Ty, TyCtxt, ValTree, layout, tls}; #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] pub enum ErrorHandled { @@ -240,20 +241,6 @@ impl<'tcx> From> for InterpErrorInfo<'tcx> { } } -/// Error information for when the program we executed turned out not to actually be a valid -/// program. This cannot happen in stand-alone Miri (except for layout errors that are only detect -/// during monomorphization), 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, - /// Abort in case errors are already reported. - AlreadyReported(ReportedErrorInfo), - /// An error occurred during layout computation. - Layout(layout::LayoutError<'tcx>), -} - /// Details of why a pointer had to be in-bounds. #[derive(Debug, Copy, Clone)] pub enum CheckInAllocMsg { @@ -265,6 +252,17 @@ pub enum CheckInAllocMsg { Dereferenceable, } +impl fmt::Display for CheckInAllocMsg { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use CheckInAllocMsg::*; + match self { + MemoryAccess => write!(f, "memory access failed"), + InboundsPointerArithmetic => write!(f, "in-bounds pointer arithmetic failed"), + Dereferenceable => write!(f, "pointer not dereferenceable"), + } + } +} + /// Details of which pointer is not aligned. #[derive(Debug, Copy, Clone)] pub enum CheckAlignMsg { @@ -314,34 +312,13 @@ pub struct Misalignment { pub required: Align, } -macro_rules! impl_into_diag_arg_through_debug { - ($($ty:ty),*$(,)?) => {$( - impl IntoDiagArg for $ty { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(format!("{self:?}"))) - } - } - )*} -} - -// These types have nice `Debug` output so we can just use them in diagnostics. -impl_into_diag_arg_through_debug! { - AllocId, - Pointer, - AllocRange, -} - /// Error information for when the program caused Undefined Behavior. #[derive(Debug)] pub enum UndefinedBehaviorInfo<'tcx> { /// Free-form case. Only for errors that are never caught! Used by miri Ub(String), - // FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically - // dispatched - /// A custom (free-form) fluent-translated error, created by `err_ub_custom!`. - Custom(crate::error::CustomSubdiagnostic<'tcx>), /// Validation error. - ValidationError(ValidationErrorInfo<'tcx>), + ValidationError { path: Option, msg: String, ptr_bytes_warning: bool }, /// Unreachable code was executed. Unreachable, @@ -446,137 +423,259 @@ pub enum UndefinedBehaviorInfo<'tcx> { CVariadicFixedCountMismatch { caller: u32, callee: u32 }, } -#[derive(Debug, Clone, Copy)] -pub enum PointerKind { - Ref(Mutability), - Box, -} +impl<'tcx> fmt::Display for UndefinedBehaviorInfo<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use UndefinedBehaviorInfo::*; -impl IntoDiagArg for PointerKind { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str( - match self { - Self::Ref(_) => "ref", - Self::Box => "box", + fn fmt_in_alloc_attempt( + f: &mut fmt::Formatter<'_>, + msg: CheckInAllocMsg, + inbounds_size: i64, + ) -> fmt::Result { + let inbounds_size_fmt = if inbounds_size == 1 { + format_args!("1 byte") + } else { + format_args!("{inbounds_size} bytes") + }; + write!(f, "{msg}: ")?; + match msg { + CheckInAllocMsg::MemoryAccess => { + write!(f, "attempting to access {inbounds_size_fmt}") + } + CheckInAllocMsg::InboundsPointerArithmetic => { + write!(f, "attempting to offset pointer by {inbounds_size_fmt}") + } + CheckInAllocMsg::Dereferenceable if inbounds_size == 0 => { + write!(f, "pointer must point to some allocation") + } + CheckInAllocMsg::Dereferenceable => { + write!(f, "pointer must be dereferenceable for {inbounds_size_fmt}") + } } - .into(), - ) + } + + match self { + Ub(msg) => write!(f, "{msg}"), + + ValidationError { path: None, msg, .. } => { + write!(f, "constructing invalid value: {msg}") + } + ValidationError { path: Some(path), msg, .. } => { + write!(f, "constructing invalid value at {path}: {msg}") + } + + Unreachable => write!(f, "entering unreachable code"), + BoundsCheckFailed { len, 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 pointer arithmetic: the total offset in bytes does not fit in an `isize`" + ), + ArithOverflow { intrinsic } => write!(f, "arithmetic overflow in `{intrinsic}`"), + ShiftOverflow { shift_amount, intrinsic } => { + write!(f, "overflowing shift by {shift_amount} in `{intrinsic}`") + } + InvalidMeta(InvalidMetaKind::SliceTooBig) => write!( + f, + "invalid metadata in wide pointer: slice is bigger than largest supported object" + ), + InvalidMeta(InvalidMetaKind::TooBig) => write!( + f, + "invalid metadata in wide pointer: total size is bigger than largest supported object" + ), + UnterminatedCString(ptr) => write!( + f, + "reading a null-terminated string starting at {ptr} with no null found before end of allocation" + ), + PointerUseAfterFree(alloc_id, msg) => { + write!(f, "{msg}: {alloc_id} has been freed, so this pointer is dangling") + } + &PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, inbounds_size, msg } => { + fmt_in_alloc_attempt(f, msg, inbounds_size)?; + write!(f, ", but got ")?; + // Write pointer. Offset might be negative so we cannot use the normal `impl Display + // for Pointer`. + write!(f, "{}", alloc_id)?; + if ptr_offset > 0 { + write!(f, "+{:#x}", ptr_offset)?; + } else if ptr_offset < 0 { + write!(f, "-{:#x}", ptr_offset.unsigned_abs())?; + } + // Write why it is invalid. + write!(f, " which ")?; + if ptr_offset < 0 { + write!(f, "points to before the beginning of the allocation") + } else if inbounds_size < 0 { + // We expected the ptr to have memory to its left, but it does not. + if ptr_offset == 0 { + write!(f, "is at the beginning of the allocation") + } else { + write!(f, "is only {ptr_offset} bytes from the beginning of the allocation") + } + } else { + let ptr_offset = ptr_offset as u64; + let alloc_size = alloc_size.bytes(); + if ptr_offset >= alloc_size { + let size = if alloc_size == 1 { + format_args!("1 byte") + } else { + format_args!("{alloc_size} bytes") + }; + write!(f, "is at or beyond the end of the allocation of size {size}",) + } else { + let dist_to_end = alloc_size - ptr_offset; + let dist = if dist_to_end == 1 { + format_args!("1 byte") + } else { + format_args!("{dist_to_end} bytes") + }; + write!(f, "is only {dist} from the end of the allocation",) + } + } + } + &DanglingIntPointer { addr: 0, inbounds_size, msg } => { + fmt_in_alloc_attempt(f, msg, inbounds_size)?; + write!(f, ", but got null pointer") + } + &DanglingIntPointer { addr, inbounds_size, msg } => { + fmt_in_alloc_attempt(f, msg, inbounds_size)?; + write!( + f, + ", but got {ptr} which is a dangling pointer (it has no provenance)", + ptr = Pointer::>::without_provenance(addr), + ) + } + AlignmentCheckFailed(misalign, msg) => { + write!( + f, + "{acc} with alignment {has}, but alignment {required} is required", + acc = match msg { + CheckAlignMsg::AccessedPtr => "accessing memory", + CheckAlignMsg::BasedOn => "accessing memory based on pointer", + }, + has = misalign.has.bytes(), + required = misalign.required.bytes(), + ) + } + WriteToReadOnly(alloc) => write!(f, "writing to {alloc} which is read-only"), + DerefFunctionPointer(alloc) => { + write!(f, "accessing {alloc} which contains a function") + } + DerefVTablePointer(alloc) => write!(f, "accessing {alloc} which contains a vtable"), + DerefVaListPointer(alloc) => { + write!(f, "accessing {alloc} which contains a variable argument list") + } + DerefTypeIdPointer(alloc) => write!(f, "accessing {alloc} which contains a `TypeId`"), + InvalidBool(value) => { + write!(f, "interpreting an invalid 8-bit value as a bool: 0x{value:02x}") + } + InvalidChar(value) => { + write!(f, "interpreting an invalid 32-bit value as a char: 0x{value:08x}") + } + InvalidTag(tag) => write!(f, "enum value has invalid tag: {tag:x}"), + InvalidFunctionPointer(ptr) => { + write!(f, "using {ptr} as function pointer but it does not point to a function") + } + InvalidVaListPointer(ptr) => write!( + f, + "using {ptr} as variable argument list pointer but it does not point to a variable argument list" + ), + InvalidVTablePointer(ptr) => { + write!(f, "using {ptr} as vtable pointer but it does not point to a vtable") + } + InvalidVTableTrait { vtable_dyn_type, expected_dyn_type } => write!( + f, + "using vtable for `{vtable_dyn_type}` but `{expected_dyn_type}` was expected" + ), + InvalidStr(err) => write!(f, "this string is not valid UTF-8: {err}"), + InvalidUninitBytes(None) => { + write!( + f, + "using uninitialized data, but this operation requires initialized memory" + ) + } + 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.bad, + ), + DeadLocal => write!(f, "accessing a dead local variable"), + ScalarSizeMismatch(mismatch) => write!( + f, + "scalar size mismatch: expected {target_size} bytes but got {data_size} bytes instead", + target_size = mismatch.target_size, + data_size = mismatch.data_size, + ), + UninhabitedEnumVariantWritten(_) => { + write!(f, "writing discriminant of an uninhabited enum variant") + } + UninhabitedEnumVariantRead(_) => { + write!(f, "read discriminant of an uninhabited enum variant") + } + InvalidNichedEnumVariantWritten { enum_ty } => { + write!( + f, + "trying to set discriminant of a {enum_ty} to the niched variant, but the value does not match" + ) + } + AbiMismatchArgument { arg_idx, caller_ty, callee_ty } => write!( + f, + "calling a function whose parameter #{arg_idx} has type {callee_ty} passing argument of type {caller_ty}", + arg_idx = arg_idx + 1, // adjust for 1-indexed lists in output + ), + AbiMismatchReturn { caller_ty, callee_ty } => write!( + f, + "calling a function with return type {callee_ty} passing return place of type {caller_ty}" + ), + VaArgOutOfBounds => write!(f, "more C-variadic arguments read than were passed"), + CVariadicMismatch { .. } => write!( + f, + "calling a function where the caller and callee disagree on whether the function is C-variadic" + ), + CVariadicFixedCountMismatch { caller, callee } => write!( + f, + "calling a C-variadic function with {caller} fixed arguments, but the function expects {callee}" + ), + } } } +/// Error information for when the program we executed turned out not to actually be a valid +/// program. This cannot happen in stand-alone Miri (except for layout errors that are only detect +/// during monomorphization), but it can happen during CTFE/ConstProp where we work on generic code +/// or execution does not have all information available. #[derive(Debug)] -pub struct ValidationErrorInfo<'tcx> { - pub path: Option, - pub kind: ValidationErrorKind<'tcx>, +pub enum InvalidProgramInfo<'tcx> { + /// Resolution can fail if we are in a too generic context. + TooGeneric, + /// Abort in case errors are already reported. + AlreadyReported(ReportedErrorInfo), + /// An error occurred during layout computation. + Layout(layout::LayoutError<'tcx>), } -#[derive(Debug)] -pub enum ExpectedKind { - Reference, - Box, - RawPtr, - InitScalar, - Bool, - Char, - Float, - Int, - FnPtr, - EnumTag, - Str, -} - -impl From for ExpectedKind { - fn from(x: PointerKind) -> ExpectedKind { - match x { - PointerKind::Box => ExpectedKind::Box, - PointerKind::Ref(_) => ExpectedKind::Reference, +impl<'tcx> fmt::Display for InvalidProgramInfo<'tcx> { + 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(e) => write!(f, "{e}"), } } } -#[derive(Debug)] -pub enum ValidationErrorKind<'tcx> { - PointerAsInt { - expected: ExpectedKind, - }, - PartialPointer, - PtrToUninhabited { - ptr_kind: PointerKind, - ty: Ty<'tcx>, - }, - MutableRefToImmutable, - UnsafeCellInImmutable, - NullFnPtr { - /// Records whether this pointer is definitely null or just may be null. - maybe: bool, - }, - NeverVal, - NonnullPtrMaybeNull, - PtrOutOfRange { - range: WrappingRange, - max_value: u128, - }, - OutOfRange { - value: String, - range: WrappingRange, - max_value: u128, - }, - UninhabitedVal { - ty: Ty<'tcx>, - }, - InvalidEnumTag { - value: String, - }, - UninhabitedEnumVariant, - Uninit { - expected: ExpectedKind, - }, - InvalidVTablePtr { - value: String, - }, - InvalidMetaWrongTrait { - /// The vtable that was actually referenced by the wide pointer metadata. - vtable_dyn_type: &'tcx ty::List>, - /// The vtable that was expected at the point in MIR that it was accessed. - expected_dyn_type: &'tcx ty::List>, - }, - InvalidMetaSliceTooLarge { - ptr_kind: PointerKind, - }, - InvalidMetaTooLarge { - ptr_kind: PointerKind, - }, - UnalignedPtr { - ptr_kind: PointerKind, - required_bytes: u64, - found_bytes: u64, - }, - NullPtr { - ptr_kind: PointerKind, - /// Records whether this pointer is definitely null or just may be null. - maybe: bool, - }, - 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 @@ -604,6 +703,37 @@ pub enum UnsupportedOpInfo { ExternStatic(DefId), } +impl fmt::Display for UnsupportedOpInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use UnsupportedOpInfo::*; + match self { + Unsupported(s) => write!(f, "{s}"), + ExternTypeField => { + write!(f, "`extern type` field does not have a known offset") + } + UnsizedLocal => write!(f, "unsized locals are not supported"), + ReadPartialPointer(ptr) => { + write!(f, "unable to read parts of a pointer from memory at {ptr}") + } + ReadPointerAsInt(_) => write!(f, "unable to turn pointer into integer"), + &ThreadLocalStatic(did) => { + write!( + f, + "cannot access thread local static `{did}`", + did = ty::tls::with(|tcx| tcx.def_path_str(did)) + ) + } + &ExternStatic(did) => { + write!( + f, + "cannot access extern static `{did}`", + did = ty::tls::with(|tcx| tcx.def_path_str(did)) + ) + } + } + } +} + /// Error information for when the program exhausted the resources granted to it /// by the interpreter. #[derive(Debug)] @@ -618,15 +748,27 @@ pub enum ResourceExhaustionInfo { Interrupted, } -/// A trait for machine-specific errors (or other "machine stop" conditions). -pub trait MachineStopType: Any + fmt::Debug + Send { - /// The diagnostic message for this error - fn diagnostic_message(&self) -> DiagMessage; - /// 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, adder: &mut dyn FnMut(DiagArgName, DiagArgValue)); +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") + } + Interrupted => write!(f, "compilation was interrupted"), + } + } } +/// A trait for machine-specific errors (or other "machine stop" conditions). +pub trait MachineStopType: Any + fmt::Display + fmt::Debug + Send {} + impl dyn MachineStopType { #[inline(always)] pub fn downcast_ref(&self) -> Option<&T> { @@ -639,11 +781,11 @@ impl dyn MachineStopType { pub enum InterpErrorKind<'tcx> { /// The program caused undefined behavior. UndefinedBehavior(UndefinedBehaviorInfo<'tcx>), + /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...). + InvalidProgram(InvalidProgramInfo<'tcx>), /// The program did something the interpreter does not support (some of these *might* be UB /// but the interpreter is not sure). Unsupported(UnsupportedOpInfo), - /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...). - InvalidProgram(InvalidProgramInfo<'tcx>), /// The program exhausted the interpreter's resources (stack/heap too big, /// execution takes too long, ...). ResourceExhaustion(ResourceExhaustionInfo), @@ -652,6 +794,19 @@ pub enum InterpErrorKind<'tcx> { MachineStop(Box), } +impl<'tcx> fmt::Display for InterpErrorKind<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use InterpErrorKind::*; + match self { + Unsupported(msg) => write!(f, "{msg}"), + InvalidProgram(msg) => write!(f, "{msg}"), + UndefinedBehavior(msg) => write!(f, "{msg}"), + ResourceExhaustion(msg) => write!(f, "{msg}"), + MachineStop(msg) => write!(f, "{msg}"), + } + } +} + impl InterpErrorKind<'_> { /// 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, @@ -704,25 +859,6 @@ macro_rules! err_ub_format { ($($tt:tt)*) => { $crate::err_ub!(Ub(format!($($tt)*))) }; } -#[macro_export] -macro_rules! err_ub_custom { - ($msg:expr $(, $($name:ident = $value:expr),* $(,)?)?) => {{ - $( - let ($($name,)*) = ($($value,)*); - )? - $crate::err_ub!(Custom( - $crate::error::CustomSubdiagnostic { - msg: || $msg, - add_args: Box::new(move |mut set_arg| { - $($( - set_arg(stringify!($name).into(), rustc_errors::IntoDiagArg::into_diag_arg($name, &mut None)); - )*)? - }) - } - )) - }}; -} - #[macro_export] macro_rules! err_exhaust { ($($tt:tt)*) => { @@ -765,11 +901,6 @@ macro_rules! throw_ub_format { ($($tt:tt)*) => { do yeet $crate::err_ub_format!($($tt)*) }; } -#[macro_export] -macro_rules! throw_ub_custom { - ($($tt:tt)*) => { do yeet $crate::err_ub_custom!($($tt)*) }; -} - #[macro_export] macro_rules! throw_exhaust { ($($tt:tt)*) => { do yeet $crate::err_exhaust!($($tt)*) }; diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 654404f9790cb..b3ec4439e3541 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -24,9 +24,9 @@ use rustc_serialize::{Decodable, Encodable}; use tracing::{debug, trace}; // Also make the error macros available from this module. pub use { - err_exhaust, err_inval, err_machine_stop, err_ub, err_ub_custom, err_ub_format, err_unsup, - err_unsup_format, throw_exhaust, throw_inval, throw_machine_stop, throw_ub, throw_ub_custom, - throw_ub_format, throw_unsup, throw_unsup_format, + err_exhaust, err_inval, err_machine_stop, err_ub, err_ub_format, err_unsup, err_unsup_format, + throw_exhaust, throw_inval, throw_machine_stop, throw_ub, throw_ub_format, throw_unsup, + throw_unsup_format, }; pub use self::allocation::{ @@ -35,11 +35,10 @@ pub use self::allocation::{ }; pub use self::error::{ BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, ErrorHandled, EvalStaticInitializerRawResult, - EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, ExpectedKind, - InterpErrorInfo, InterpErrorKind, InterpResult, InvalidMetaKind, InvalidProgramInfo, - MachineStopType, Misalignment, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo, - ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, ValTreeCreationError, - ValidationErrorInfo, ValidationErrorKind, interp_ok, + EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, InterpErrorInfo, + InterpErrorKind, InterpResult, InvalidMetaKind, InvalidProgramInfo, MachineStopType, + Misalignment, ReportedErrorInfo, ResourceExhaustionInfo, ScalarSizeMismatch, + UndefinedBehaviorInfo, UnsupportedOpInfo, ValTreeCreationError, interp_ok, }; pub use self::pointer::{CtfeProvenance, Pointer, PointerArithmetic, Provenance}; pub use self::value::Scalar; @@ -76,16 +75,22 @@ impl<'tcx> GlobalId<'tcx> { #[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct AllocId(pub NonZero); +// AllocId show up in const-eval error messages +impl fmt::Display for AllocId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { write!(f, "a{}", self.0) } else { write!(f, "alloc{}", self.0) } + } +} + // We want the `Debug` output to be readable as it is used by `derive(Debug)` for // all the Miri types. impl fmt::Debug for AllocId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if f.alternate() { write!(f, "a{}", self.0) } else { write!(f, "alloc{}", self.0) } + // Dispatch to `Display` impl. + fmt::Display::fmt(self, f) } } -// No "Display" since AllocIds are not usually user-visible. - #[derive(TyDecodable, TyEncodable)] enum AllocDiscriminant { Alloc, diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index b6bcc87ff0413..e135404f28e7a 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -241,6 +241,12 @@ impl fmt::Debug for Pointer { } } +impl fmt::Display for Pointer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Provenance::fmt(self, f) + } +} + impl fmt::Debug for Pointer> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.provenance { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 2dd205e8468da..b029112ed8a05 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -14,7 +14,7 @@ use rustc_abi::{FieldIdx, VariantIdx}; pub use rustc_ast::{Mutability, Pinnedness}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::graph::dominators::Dominators; -use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, ErrorGuaranteed, IntoDiagArg}; +use rustc_errors::ErrorGuaranteed; use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_hir::{ diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 650c44aa41fbe..7931c80bed6c1 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -326,36 +326,42 @@ impl AssertKind { } } } +} - /// Format the diagnostic message for use in a lint (e.g. when the assertion fails during const-eval). - /// - /// Needs to be kept in sync with the run-time behavior (which is defined by - /// `AssertKind::panic_function` and the lang items mentioned in its docs). - /// Note that we deliberately show more details here than we do at runtime, such as the actual - /// numbers that overflowed -- it is much easier to do so here than at runtime. - pub fn diagnostic_message(&self) -> DiagMessage { +/// Format the diagnostic message for use in a lint (e.g. when the assertion fails during const-eval). +/// +/// Needs to be kept in sync with the run-time behavior (which is defined by +/// `AssertKind::panic_function` and the lang items mentioned in its docs). +/// Note that we deliberately show more details here than we do at runtime, such as the actual +/// numbers that overflowed -- it is much easier to do so here than at runtime. +impl fmt::Display for AssertKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { use AssertKind::*; match self { - BoundsCheck { .. } => { - msg!("index out of bounds: the length is {$len} but the index is {$index}") + BoundsCheck { len, index } => { + write!(f, "index out of bounds: the length is {len:?} but the index is {index:?}") } - Overflow(BinOp::Shl, _, _) => { - msg!("attempt to shift left by `{$val}`, which would overflow") + Overflow(BinOp::Shl, _, val) => { + write!(f, "attempt to shift left by `{val:#?}`, which would overflow") } - Overflow(BinOp::Shr, _, _) => { - msg!("attempt to shift right by `{$val}`, which would overflow") + Overflow(BinOp::Shr, _, val) => { + write!(f, "attempt to shift right by `{val:#?}`, which would overflow") } - Overflow(_, _, _) => { - msg!("attempt to compute `{$left} {$op} {$right}`, which would overflow") + Overflow(binop, left, right) => { + write!( + f, + "attempt to compute `{left:#?} {op} {right:#?}`, which would overflow", + op = binop.to_hir_binop().as_str() + ) } - OverflowNeg(_) => msg!("attempt to negate `{$val}`, which would overflow"), - DivisionByZero(_) => msg!("attempt to divide `{$val}` by zero"), - RemainderByZero(_) => { - msg!("attempt to calculate the remainder of `{$val}` with a divisor of zero") + OverflowNeg(val) => write!(f, "attempt to negate `{val:#?}`, which would overflow"), + DivisionByZero(val) => write!(f, "attempt to divide `{val:#?}` by zero"), + RemainderByZero(val) => { + write!(f, "attempt to calculate the remainder of `{val:#?}` with a divisor of zero") } ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => { - msg!("`async fn` resumed after completion") + write!(f, "`async fn` resumed after completion") } ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => { todo!() @@ -364,84 +370,43 @@ impl AssertKind { bug!("gen blocks can be resumed after they return and will keep returning `None`") } ResumedAfterReturn(CoroutineKind::Coroutine(_)) => { - msg!("coroutine resumed after completion") + write!(f, "coroutine resumed after completion") } ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => { - msg!("`async fn` resumed after panicking") + write!(f, "`async fn` resumed after panicking") } ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => { todo!() } ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => { - msg!("`gen` fn or block cannot be further iterated on after it panicked") + write!(f, "`gen` fn or block cannot be further iterated on after it panicked") } ResumedAfterPanic(CoroutineKind::Coroutine(_)) => { - msg!("coroutine resumed after panicking") + write!(f, "coroutine resumed after panicking") } - NullPointerDereference => msg!("null pointer dereference occurred"), - InvalidEnumConstruction(_) => { - msg!("trying to construct an enum from an invalid value `{$source}`") + NullPointerDereference => write!(f, "null pointer dereference occurred"), + InvalidEnumConstruction(source) => { + write!(f, "trying to construct an enum from an invalid value `{source:#?}`") } ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => { - msg!("`async fn` resumed after async drop") + write!(f, "`async fn` resumed after async drop") } ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => { todo!() } ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => { - msg!("`gen` fn or block cannot be further iterated on after it async dropped") + write!(f, "`gen` fn or block cannot be further iterated on after it async dropped") } ResumedAfterDrop(CoroutineKind::Coroutine(_)) => { - msg!("coroutine resumed after async drop") + write!(f, "coroutine resumed after async drop") } - MisalignedPointerDereference { .. } => msg!( - "misaligned pointer dereference: address must be a multiple of {$required} but is {$found}" + MisalignedPointerDereference { required, found } => write!( + f, + "misaligned pointer dereference: address must be a multiple of {required:#?} but is {found:#?}" ), } } - - pub fn add_args(self, adder: &mut dyn FnMut(DiagArgName, DiagArgValue)) - where - O: fmt::Debug, - { - use AssertKind::*; - - macro_rules! add { - ($name: expr, $value: expr) => { - adder($name.into(), $value.into_diag_arg(&mut None)); - }; - } - - match self { - BoundsCheck { len, index } => { - add!("len", format!("{len:?}")); - add!("index", format!("{index:?}")); - } - Overflow(BinOp::Shl | BinOp::Shr, _, val) - | DivisionByZero(val) - | RemainderByZero(val) - | OverflowNeg(val) => { - add!("val", format!("{val:#?}")); - } - Overflow(binop, left, right) => { - add!("op", binop.to_hir_binop().as_str()); - add!("left", format!("{left:#?}")); - add!("right", format!("{right:#?}")); - } - ResumedAfterReturn(_) - | ResumedAfterPanic(_) - | NullPointerDereference - | ResumedAfterDrop(_) => {} - MisalignedPointerDereference { required, found } => { - add!("required", format!("{required:#?}")); - add!("found", format!("{found:#?}")); - } - InvalidEnumConstruction(source) => { - add!("source", format!("{source:#?}")); - } - } - } } #[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] @@ -510,7 +475,6 @@ impl<'tcx> TerminatorKind<'tcx> { } pub use helper::*; -use rustc_errors::msg; mod helper { use super::*; diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index fe8f8abf7f57f..ebd428168daa5 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -7,9 +7,8 @@ use rustc_abi::{ PointerKind, Primitive, ReprFlags, ReprOptions, Scalar, Size, TagEncoding, TargetDataLayout, TyAbiInterface, VariantIdx, Variants, }; -use rustc_error_messages::DiagMessage; use rustc_errors::{ - Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, msg, + Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, }; use rustc_hir as hir; use rustc_hir::LangItem; @@ -266,29 +265,6 @@ pub enum LayoutError<'tcx> { } impl<'tcx> LayoutError<'tcx> { - pub fn diagnostic_message(&self) -> DiagMessage { - use LayoutError::*; - - match self { - Unknown(_) => msg!("the type `{$ty}` has an unknown layout"), - SizeOverflow(_) => { - msg!("values of the type `{$ty}` are too big for the target architecture") - } - InvalidSimd { kind: SimdLayoutError::TooManyLanes(_), .. } => { - msg!("the SIMD type `{$ty}` has more elements than the limit {$max_lanes}") - } - InvalidSimd { kind: SimdLayoutError::ZeroLength, .. } => { - msg!("the SIMD type `{$ty}` has zero elements") - } - TooGeneric(_) => msg!("the type `{$ty}` does not have a fixed layout"), - NormalizationFailure(_, _) => msg!( - "unable to determine layout for `{$ty}` because `{$failure_ty}` cannot be normalized" - ), - Cycle(_) => msg!("a cycle occurred during layout computation"), - ReferencesError(_) => msg!("the type has an unknown layout"), - } - } - pub fn into_diagnostic(self) -> crate::error::LayoutError<'tcx> { use LayoutError::*; diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 00b67b6564594..daee8de965135 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -145,11 +145,7 @@ impl<'a, P: std::fmt::Debug> Diagnostic<'a, ()> for AssertLint

{ } }, ); - let label = self.assert_kind.diagnostic_message(); - self.assert_kind.add_args(&mut |name, value| { - diag.arg(name, value); - }); - diag.span_label(self.span, label); + diag.span_label(self.span, self.assert_kind.to_string()); diag } } diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index c4994417ed7ea..8d0922db8f40e 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -241,7 +241,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { assert!( !err.kind().formatted_string(), "known panics lint encountered formatting error: {}", - format_interp_error(self.ecx.tcx.dcx(), err), + format_interp_error(err), ); err }) diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index 20fbb687b3080..d8ef2484a95cf 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -42,11 +42,7 @@ impl Target { } let alignment_error = |field_name: &str, error: AlignFromBytesError| -> String { - let msg = match error { - AlignFromBytesError::NotPowerOfTwo(_) => "not a power of 2 number of bytes", - AlignFromBytesError::TooLarge(_) => "too large", - }; - format!("`{}` bits is not a valid value for {field_name}: {msg}", error.align() * 8) + format!("invalid value for {field_name}: {error}") }; macro_rules! forward { diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs index 36e574c8e57f7..6b7c0adc8afb0 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs @@ -37,17 +37,14 @@ impl Creation { let tag = self.retag.new_tag; if let Some(perm) = self.retag.permission { ( - format!( - "{tag:?} was created by a {:?} retag at offsets {:?}", - perm, self.retag.range, - ), + format!("{tag:?} was created by a {perm:?} retag at offsets {}", self.retag.range), self.span.data(), ) } else { assert!(self.retag.range.size == Size::ZERO); ( format!( - "{tag:?} would have been created here, but this is a zero-size retag ({:?}) so the tag in question does not exist anywhere", + "{tag:?} would have been created here, but this is a zero-size retag ({}) so the tag in question does not exist anywhere", self.retag.range, ), self.span.data(), @@ -79,12 +76,12 @@ impl Invalidation { // For a FnEntry retag, our Span points at the caller. // See `DiagnosticCx::log_invalidation`. format!( - "{:?} was later invalidated at offsets {:?} by a {} inside this call", + "{:?} was later invalidated at offsets {} by a {} inside this call", self.tag, self.range, self.cause ) } else { format!( - "{:?} was later invalidated at offsets {:?} by a {}", + "{:?} was later invalidated at offsets {} by a {}", self.tag, self.range, self.cause ) }; @@ -343,7 +340,7 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> { if self.history.root.0.tag() == tag { Some(( format!( - "{tag:?} was created here, as the root tag for {:?}", + "{tag:?} was created here, as the root tag for {}", self.history.id ), self.history.root.1.data(), @@ -383,7 +380,7 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> { let perm = op.permission.expect("`start_grant` must be called before calling `grant_error`"); let action = format!( - "trying to retag from {:?} for {:?} permission at {:?}[{:#x}]", + "trying to retag from {:?} for {:?} permission at {}[{:#x}]", op.orig_tag, perm, self.history.id, @@ -410,7 +407,7 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> { Operation::Dealloc(_) => return self.dealloc_error(stack), }; let action = format!( - "attempting a {access} using {tag:?} at {alloc_id:?}[{offset:#x}]", + "attempting a {access} using {tag:?} at {alloc_id}[{offset:#x}]", access = op.kind, tag = op.tag, alloc_id = self.history.id, @@ -455,7 +452,7 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> { }; err_sb_ub( format!( - "attempting deallocation using {tag:?} at {alloc_id:?}{cause}", + "attempting deallocation using {tag:?} at {alloc_id}{cause}", tag = op.tag, alloc_id = self.history.id, cause = error_cause(stack, op.tag), @@ -488,7 +485,7 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> { } fn operation_summary(operation: &str, alloc_id: AllocId, alloc_range: AllocRange) -> String { - format!("this error occurs as part of {operation} at {alloc_id:?}{alloc_range:?}") + format!("this error occurs as part of {operation} at {alloc_id}{alloc_range}") } fn error_cause(stack: &Stack, prov_extra: ProvenanceExtra) -> &'static str { diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs index 055baca8542fd..093dada405af8 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -157,7 +157,7 @@ impl HistoryData { // to the user. The meaningful one is `access_range`. let access = access_cause.print_as_access(is_foreign); let access_range_text = match access_range { - Some(r) => format!("at offsets {r:?}"), + Some(r) => format!("at offsets {r}"), None => format!("on every location previously accessed by this tag"), }; self.events.push(( @@ -339,7 +339,7 @@ impl TbError<'_> { // all tags through which an access would cause UB. let accessed_is_conflicting = accessed.map(|a| a.tag) == Some(conflicting.tag); let title = format!( - "{cause} through {accessed_str} at {alloc_id:?}[{error_offset:#x}] is forbidden", + "{cause} through {accessed_str} at {alloc_id}[{error_offset:#x}] is forbidden", alloc_id = self.access_info.alloc_id ); let (title, details, conflicting_tag_name) = match self.error_kind { diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 367012e56ad9c..7992819aafd07 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -4,7 +4,7 @@ use std::sync::Mutex; use rustc_abi::{Align, Size}; use rustc_data_structures::fx::{FxBuildHasher, FxHashSet}; -use rustc_errors::{Diag, DiagMessage, Level}; +use rustc_errors::{Diag, Level}; use rustc_span::{DUMMY_SP, Span, SpanData, Symbol}; use crate::borrow_tracker::stacked_borrows::diagnostics::TagHistory; @@ -106,16 +106,7 @@ impl fmt::Debug for TerminationInfo { } } -impl MachineStopType for TerminationInfo { - fn diagnostic_message(&self) -> DiagMessage { - self.to_string().into() - } - fn add_args( - self: Box, - _: &mut dyn FnMut(std::borrow::Cow<'static, str>, rustc_errors::DiagArgValue), - ) { - } -} +impl MachineStopType for TerminationInfo {} /// Miri specific diagnostics pub enum NonHaltingDiagnostic { @@ -354,16 +345,14 @@ pub fn report_result<'tcx>( (title, helps) } else { let title = match res.kind() { - UndefinedBehavior(ValidationError(validation_err)) - if matches!( - validation_err.kind, - ValidationErrorKind::PointerAsInt { .. } | ValidationErrorKind::PartialPointer - ) => - { + UndefinedBehavior(UndefinedBehaviorInfo::ValidationError { + ptr_bytes_warning: true, + .. + }) => { ecx.handle_ice(); // print interpreter backtrace (this is outside the eval `catch_unwind`) bug!( "This validation error should be impossible in Miri: {}", - format_interp_error(ecx.tcx.dcx(), res) + format_interp_error(res) ); } UndefinedBehavior(_) => "Undefined Behavior", @@ -380,10 +369,7 @@ pub fn report_result<'tcx>( ) => "post-monomorphization error", _ => { ecx.handle_ice(); // print interpreter backtrace (this is outside the eval `catch_unwind`) - bug!( - "This error should be impossible in Miri: {}", - format_interp_error(ecx.tcx.dcx(), res) - ); + bug!("This error should be impossible in Miri: {}", format_interp_error(res)); } }; #[rustfmt::skip] @@ -411,10 +397,10 @@ pub fn report_result<'tcx>( match info { PointerUseAfterFree(alloc_id, _) | PointerOutOfBounds { alloc_id, .. } => { if let Some(span) = ecx.machine.allocated_span(*alloc_id) { - helps.push(note_span!(span, "{:?} was allocated here:", alloc_id)); + helps.push(note_span!(span, "{alloc_id} was allocated here:")); } if let Some(span) = ecx.machine.deallocated_span(*alloc_id) { - helps.push(note_span!(span, "{:?} was deallocated here:", alloc_id)); + helps.push(note_span!(span, "{alloc_id} was deallocated here:")); } } AbiMismatchArgument { .. } | AbiMismatchReturn { .. } => { @@ -447,7 +433,7 @@ pub fn report_result<'tcx>( UndefinedBehavior(InvalidUninitBytes(Some((alloc_id, access)))) => { writeln!( extra, - "Uninitialized memory occurred at {alloc_id:?}{range:?}, in this allocation:", + "Uninitialized memory occurred at {alloc_id}{range}, in this allocation:", range = access.bad, ) .unwrap(); @@ -460,7 +446,7 @@ pub fn report_result<'tcx>( if let Some(title) = title { write!(primary_msg, "{title}: ").unwrap(); } - write!(primary_msg, "{}", format_interp_error(ecx.tcx.dcx(), res)).unwrap(); + write!(primary_msg, "{}", format_interp_error(res)).unwrap(); if labels.is_empty() { labels.push(format!( @@ -510,7 +496,7 @@ pub fn report_leaks<'tcx>( let mut any_pruned = false; for (id, kind, alloc) in leaks { let mut title = format!( - "memory leaked: {id:?} ({}, size: {:?}, align: {:?})", + "memory leaked: {id:?} ({}, size: {}, align: {})", kind, alloc.size().bytes(), alloc.align.bytes() @@ -665,17 +651,17 @@ impl<'tcx> MiriMachine<'tcx> { format!("created {tag:?} with {perm} derived from unknown tag"), CreatedPointerTag(tag, Some(perm), Some((alloc_id, range, orig_tag))) => format!( - "created tag {tag:?} with {perm} at {alloc_id:?}{range:?} derived from {orig_tag:?}" + "created tag {tag:?} with {perm} at {alloc_id}{range} derived from {orig_tag:?}" ), PoppedPointerTag(item, cause) => format!("popped tracked tag for item {item:?}{cause}"), TrackingAlloc(id, size, align) => format!( - "now tracking allocation {id:?} of {size} bytes (alignment {align} bytes)", + "now tracking allocation {id} of {size} bytes (alignment {align} bytes)", size = size.bytes(), align = align.bytes(), ), AccessedAlloc(id, range, access_kind) => - format!("{access_kind} at {id:?}[{}..{}]", range.start.bytes(), range.end().bytes()), + format!("{access_kind} at {id}{range}"), FreedAlloc(id) => format!("freed allocation {id:?}"), RejectedIsolatedOp(op) => format!("{op} was made to return an error due to isolation"), ProgressReport { .. } => diff --git a/src/tools/miri/tests/pass/alloc-access-tracking.stderr b/src/tools/miri/tests/pass/alloc-access-tracking.stderr index f1c9241beded7..6c780e78e91b4 100644 --- a/src/tools/miri/tests/pass/alloc-access-tracking.stderr +++ b/src/tools/miri/tests/pass/alloc-access-tracking.stderr @@ -4,13 +4,13 @@ note: now tracking allocation ALLOC of 123 bytes (alignment ALIGN bytes) LL | utils::miri_track_alloc(ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tracking was triggered here -note: write access at ALLOC[0..1] +note: write access at ALLOC[0x0..0x1] --> tests/pass/alloc-access-tracking.rs:LL:CC | LL | *ptr = 42; // Crucially, only a write is printed here, no read! | ^^^^^^^^^ tracking was triggered here -note: read access at ALLOC[0..1] +note: read access at ALLOC[0x0..0x1] --> tests/pass/alloc-access-tracking.rs:LL:CC | LL | assert_eq!(*ptr, 42); From 90f7d7e07475697fa5b9eedd26d74f2a309b4d31 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 9 Mar 2026 15:47:34 +0100 Subject: [PATCH 2/3] clean up some unused leftovers --- compiler/rustc_const_eval/src/errors.rs | 2 -- compiler/rustc_middle/src/error.rs | 26 +--------------------- compiler/rustc_middle/src/ty/consts/int.rs | 9 -------- 3 files changed, 1 insertion(+), 36 deletions(-) diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 1097eecf3121d..3943be0cf15ef 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -383,8 +383,6 @@ pub struct RawBytesNote { pub bytes: String, } -// FIXME(fee1-dead) do not use stringly typed `ConstContext` - #[derive(Diagnostic)] #[diag( r#"cannot match on `{$ty}` in {$kind -> diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index dfb99fb98513d..d173335e9aa28 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -1,8 +1,7 @@ +use std::io; use std::path::Path; -use std::{fmt, io}; use rustc_errors::codes::*; -use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; @@ -114,29 +113,6 @@ pub(super) struct ConstNotUsedTraitAlias { pub span: Span, } -pub struct CustomSubdiagnostic<'a> { - pub msg: fn() -> DiagMessage, - pub add_args: Box, -} - -impl<'a> CustomSubdiagnostic<'a> { - pub fn label(x: fn() -> DiagMessage) -> Self { - Self::label_and_then(x, |_| {}) - } - pub fn label_and_then( - msg: fn() -> DiagMessage, - 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("the type `{$ty}` has an unknown layout")] diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs index eaf67ae23ad26..180ccd9301c49 100644 --- a/compiler/rustc_middle/src/ty/consts/int.rs +++ b/compiler/rustc_middle/src/ty/consts/int.rs @@ -4,7 +4,6 @@ use std::num::NonZero; use rustc_abi::Size; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; -use rustc_errors::{DiagArgValue, IntoDiagArg}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use crate::ty::TyCtxt; @@ -137,14 +136,6 @@ impl std::fmt::Debug for ConstInt { } } -impl IntoDiagArg 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_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::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 From fe1f92af4b010113a5d4b9d596209ee4637d7045 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 9 Mar 2026 17:54:10 +0100 Subject: [PATCH 3/3] de-duplicate LayoutError formatting --- compiler/rustc_codegen_gcc/src/context.rs | 2 +- compiler/rustc_codegen_llvm/src/context.rs | 2 +- .../src/debuginfo/type_names.rs | 2 +- .../rustc_hir_analysis/src/check/check.rs | 3 +-- compiler/rustc_middle/src/error.rs | 27 ------------------- compiler/rustc_middle/src/ty/layout.rs | 26 +----------------- compiler/rustc_middle/src/ty/vtable.rs | 2 +- compiler/rustc_passes/src/abi_test.rs | 13 +++------ compiler/rustc_passes/src/layout_test.rs | 4 +-- 9 files changed, 11 insertions(+), 70 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 5423ee5390fe5..64ab92b3a914b 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -538,7 +538,7 @@ impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> { | LayoutError::InvalidSimd { .. } | LayoutError::ReferencesError(_) = err { - self.tcx.dcx().emit_fatal(respan(span, err.into_diagnostic())) + self.tcx.dcx().span_fatal(span, err.to_string()) } else { self.tcx.dcx().emit_fatal(ssa_errors::FailedToGetLayout { span, ty, err }) } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 28aa12cc83dc5..545a9add911d4 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -1135,7 +1135,7 @@ impl<'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'_, 'tcx> { | LayoutError::ReferencesError(_) | LayoutError::InvalidSimd { .. } = err { - self.tcx.dcx().emit_fatal(Spanned { span, node: err.into_diagnostic() }) + self.tcx.dcx().span_fatal(span, err.to_string()) } else { self.tcx.dcx().emit_fatal(ssa_errors::FailedToGetLayout { span, ty, err }) } diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index d3b2caca4742d..d207cdca4d4bb 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -95,7 +95,7 @@ fn push_debuginfo_type_name<'tcx>( // Computing the layout can still fail here, e.g. if the target architecture // cannot represent the type. See // https://github.com/rust-lang/rust/issues/94961. - tcx.dcx().emit_fatal(e.into_diagnostic()); + tcx.dcx().fatal(e.to_string()); } } } else { diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 1f5eed25029b9..32cfa3003eebe 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -24,7 +24,6 @@ use rustc_middle::ty::{ TypeVisitable, TypeVisitableExt, fold_regions, }; use rustc_session::lint::builtin::UNINHABITED_STATIC; -use rustc_span::Spanned; use rustc_target::spec::{AbiMap, AbiMapping}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits; @@ -216,7 +215,7 @@ fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) { // SIMD types with invalid layout (e.g., zero-length) should emit an error Err(e @ LayoutError::InvalidSimd { .. }) => { let ty_span = tcx.ty_span(def_id); - tcx.dcx().emit_err(Spanned { span: ty_span, node: e.into_diagnostic() }); + tcx.dcx().span_err(ty_span, e.to_string()); return; } // Generic statics are rejected, but we still reach this case. diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index d173335e9aa28..44ac747726a61 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -113,33 +113,6 @@ pub(super) struct ConstNotUsedTraitAlias { pub span: Span, } -#[derive(Diagnostic)] -pub enum LayoutError<'tcx> { - #[diag("the type `{$ty}` has an unknown layout")] - Unknown { ty: Ty<'tcx> }, - - #[diag("the type `{$ty}` does not have a fixed layout")] - TooGeneric { ty: Ty<'tcx> }, - - #[diag("values of the type `{$ty}` are too big for the target architecture")] - Overflow { ty: Ty<'tcx> }, - - #[diag("the SIMD type `{$ty}` has more elements than the limit {$max_lanes}")] - SimdTooManyLanes { ty: Ty<'tcx>, max_lanes: u64 }, - - #[diag("the SIMD type `{$ty}` has zero elements")] - SimdZeroLength { ty: Ty<'tcx> }, - - #[diag("unable to determine layout for `{$ty}` because `{$failure_ty}` cannot be normalized")] - NormalizationFailure { ty: Ty<'tcx>, failure_ty: String }, - - #[diag("a cycle occurred during layout computation")] - Cycle, - - #[diag("the type has an unknown layout")] - ReferencesError, -} - #[derive(Diagnostic)] #[diag("erroneous constant encountered")] pub(crate) struct ErroneousConstant { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index ebd428168daa5..4ca51c078bef5 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -264,30 +264,6 @@ pub enum LayoutError<'tcx> { Cycle(ErrorGuaranteed), } -impl<'tcx> LayoutError<'tcx> { - pub fn into_diagnostic(self) -> crate::error::LayoutError<'tcx> { - use LayoutError::*; - - use crate::error::LayoutError as E; - match self { - Unknown(ty) => E::Unknown { ty }, - SizeOverflow(ty) => E::Overflow { ty }, - InvalidSimd { ty, kind: SimdLayoutError::TooManyLanes(max_lanes) } => { - E::SimdTooManyLanes { ty, max_lanes } - } - InvalidSimd { ty, kind: SimdLayoutError::ZeroLength } => E::SimdZeroLength { ty }, - TooGeneric(ty) => E::TooGeneric { ty }, - NormalizationFailure(ty, e) => { - E::NormalizationFailure { ty, failure_ty: e.get_type_for_failure() } - } - Cycle(_) => E::Cycle, - ReferencesError(_) => E::ReferencesError, - } - } -} - -// FIXME: Once the other errors that embed this error have been converted to translatable -// diagnostics, this Display impl should be removed. impl<'tcx> fmt::Display for LayoutError<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { @@ -1309,7 +1285,7 @@ pub enum FnAbiError<'tcx> { impl<'a, 'b, G: EmissionGuarantee> Diagnostic<'a, G> for FnAbiError<'b> { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { match self { - Self::Layout(e) => e.into_diagnostic().into_diag(dcx, level), + Self::Layout(e) => Diag::new(dcx, level, e.to_string()), } } } diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index edb1eaea30275..8a376e5429459 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -101,7 +101,7 @@ pub(super) fn vtable_allocation_provider<'tcx>( let layout = match tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)) { Ok(layout) => layout, - Err(e) => tcx.dcx().emit_fatal(e.into_diagnostic()), + Err(e) => tcx.dcx().fatal(e.to_string()), }; assert!(layout.is_sized(), "can't create a vtable for an unsized type"); let size = layout.size.bytes(); diff --git a/compiler/rustc_passes/src/abi_test.rs b/compiler/rustc_passes/src/abi_test.rs index 7d1ee1ffbc364..35bfad3a877f2 100644 --- a/compiler/rustc_passes/src/abi_test.rs +++ b/compiler/rustc_passes/src/abi_test.rs @@ -5,7 +5,7 @@ use rustc_hir::find_attr; use rustc_middle::span_bug; use rustc_middle::ty::layout::{FnAbiError, LayoutError}; use rustc_middle::ty::{self, GenericArgs, Instance, Ty, TyCtxt}; -use rustc_span::{Span, Spanned}; +use rustc_span::Span; use rustc_target::callconv::FnAbi; use super::layout_test::ensure_wf; @@ -44,10 +44,7 @@ fn unwrap_fn_abi<'tcx>( match abi { Ok(abi) => abi, Err(FnAbiError::Layout(layout_error)) => { - tcx.dcx().emit_fatal(Spanned { - node: layout_error.into_diagnostic(), - span: tcx.def_span(item_def_id), - }); + tcx.dcx().span_fatal(tcx.def_span(item_def_id), layout_error.to_string()); } } } @@ -65,11 +62,7 @@ fn dump_abi_of_fn_item( Ok(None) => { // Not sure what to do here, but `LayoutError::Unknown` seems reasonable? let ty = tcx.type_of(item_def_id).instantiate_identity(); - tcx.dcx().emit_fatal(Spanned { - node: LayoutError::Unknown(ty).into_diagnostic(), - - span: tcx.def_span(item_def_id), - }); + tcx.dcx().span_fatal(tcx.def_span(item_def_id), LayoutError::Unknown(ty).to_string()); } Err(_guaranteed) => return, }; diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index f4f5ae6c1683f..1a9054a51ca39 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -6,7 +6,7 @@ use rustc_hir::find_attr; use rustc_middle::span_bug; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutError, LayoutOfHelpers}; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::{Span, Spanned}; +use rustc_span::Span; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::TyCtxtInferExt; use rustc_trait_selection::traits; @@ -115,7 +115,7 @@ fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attrs: &[RustcLayout } Err(layout_error) => { - tcx.dcx().emit_err(Spanned { node: layout_error.into_diagnostic(), span }); + tcx.dcx().span_err(span, layout_error.to_string()); } } }