| use std::borrow::Cow; |
| use std::fmt::Write; |
| |
| use either::Either; |
| use rustc_abi::WrappingRange; |
| use rustc_errors::codes::*; |
| use rustc_errors::{ |
| Diag, DiagArgValue, DiagMessage, Diagnostic, EmissionGuarantee, Level, MultiSpan, |
| Subdiagnostic, msg, |
| }; |
| use rustc_hir::ConstContext; |
| use rustc_macros::{Diagnostic, LintDiagnostic, 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_span::{Span, Symbol}; |
| |
| use crate::interpret::InternKind; |
| |
| #[derive(Diagnostic)] |
| #[diag( |
| r#"encountered dangling pointer in final value of {$kind -> |
| [static] static |
| [static_mut] mutable static |
| [const] constant |
| [promoted] promoted |
| *[other] {""} |
| }"# |
| )] |
| pub(crate) struct DanglingPtrInFinal { |
| #[primary_span] |
| pub span: Span, |
| pub kind: InternKind, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag( |
| "#[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead" |
| )] |
| pub(crate) struct NestedStaticInThreadLocal { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag( |
| r#"encountered mutable pointer in final value of {$kind -> |
| [static] static |
| [static_mut] mutable static |
| [const] constant |
| [promoted] promoted |
| *[other] {""} |
| }"# |
| )] |
| pub(crate) struct MutablePtrInFinal { |
| #[primary_span] |
| pub span: Span, |
| pub kind: InternKind, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag("encountered `const_allocate` pointer in final value that was not made global")] |
| #[note( |
| "use `const_make_global` to turn allocated pointers into immutable globals before returning" |
| )] |
| pub(crate) struct ConstHeapPtrInFinal { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag( |
| r#"encountered partial pointer in final value of {$kind -> |
| [static] static |
| [static_mut] mutable static |
| [const] constant |
| [promoted] promoted |
| *[other] {""} |
| }"# |
| )] |
| #[note( |
| "while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value" |
| )] |
| pub(crate) struct PartialPtrInFinal { |
| #[primary_span] |
| pub span: Span, |
| pub kind: InternKind, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag( |
| "const function that might be (indirectly) exposed to stable cannot use `#[feature({$gate})]`" |
| )] |
| pub(crate) struct UnstableInStableExposed { |
| pub gate: String, |
| #[primary_span] |
| pub span: Span, |
| #[help( |
| "mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features" |
| )] |
| pub is_function_call: bool, |
| /// Need to duplicate the field so that fluent also provides it as a variable... |
| pub is_function_call2: bool, |
| #[suggestion( |
| "if the {$is_function_call2 -> |
| [true] caller |
| *[false] function |
| } is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]`", |
| code = "#[rustc_const_unstable(feature = \"...\", issue = \"...\")]\n", |
| applicability = "has-placeholders" |
| )] |
| pub attr_span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag("thread-local statics cannot be accessed at compile-time", code = E0625)] |
| pub(crate) struct ThreadLocalAccessErr { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag("pointers cannot be cast to integers during const eval")] |
| #[note("at compile-time, pointers do not have an integer value")] |
| #[note( |
| "avoiding this restriction via `transmute`, `union`, or raw pointers leads to compile-time undefined behavior" |
| )] |
| pub(crate) struct RawPtrToIntErr { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag("pointers cannot be reliably compared during const eval")] |
| #[note("see issue #53020 <https://github.com/rust-lang/rust/issues/53020> for more information")] |
| pub(crate) struct RawPtrComparisonErr { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag("argument to `panic!()` in a const context must have type `&str`")] |
| pub(crate) struct PanicNonStrErr { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag( |
| r#"function pointer calls are not allowed in {$kind -> |
| [const] constant |
| [static] static |
| [const_fn] constant function |
| *[other] {""} |
| }s"# |
| )] |
| pub(crate) struct UnallowedFnPointerCall { |
| #[primary_span] |
| pub span: Span, |
| pub kind: ConstContext, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag("`{$def_path}` is not yet stable as a const fn")] |
| pub(crate) struct UnstableConstFn { |
| #[primary_span] |
| pub span: Span, |
| pub def_path: String, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag("`{$def_path}` is not yet stable as a const trait")] |
| pub(crate) struct UnstableConstTrait { |
| #[primary_span] |
| pub span: Span, |
| pub def_path: String, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag("`{$name}` is not yet stable as a const intrinsic")] |
| pub(crate) struct UnstableIntrinsic { |
| #[primary_span] |
| pub span: Span, |
| pub name: Symbol, |
| pub feature: Symbol, |
| #[suggestion( |
| "add `#![feature({$feature})]` to the crate attributes to enable", |
| code = "#![feature({feature})]\n", |
| applicability = "machine-applicable" |
| )] |
| pub suggestion: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag("`{$def_path}` cannot be (indirectly) exposed to stable")] |
| #[help( |
| "either mark the callee as `#[rustc_const_stable_indirect]`, or the caller as `#[rustc_const_unstable]`" |
| )] |
| pub(crate) struct UnmarkedConstItemExposed { |
| #[primary_span] |
| pub span: Span, |
| pub def_path: String, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag("intrinsic `{$def_path}` cannot be (indirectly) exposed to stable")] |
| #[help( |
| "mark the caller as `#[rustc_const_unstable]`, or mark the intrinsic `#[rustc_intrinsic_const_stable_indirect]` (but this requires team approval)" |
| )] |
| pub(crate) struct UnmarkedIntrinsicExposed { |
| #[primary_span] |
| pub span: Span, |
| pub def_path: String, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag("mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed", code = E0764)] |
| #[note( |
| "temporaries in constants and statics can have their lifetime extended until the end of the program" |
| )] |
| #[note("to avoid accidentally creating global mutable state, such temporaries must be immutable")] |
| #[help( |
| "if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`" |
| )] |
| pub(crate) struct MutableBorrowEscaping { |
| #[primary_span] |
| #[label("this mutable borrow refers to such a temporary")] |
| pub span: Span, |
| pub kind: ConstContext, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag( |
| r#"cannot call {$non_or_conditionally}-const formatting macro in {$kind -> |
| [const] constant |
| [static] static |
| [const_fn] constant function |
| *[other] {""} |
| }s"#, |
| code = E0015, |
| )] |
| pub(crate) struct NonConstFmtMacroCall { |
| #[primary_span] |
| pub span: Span, |
| pub kind: ConstContext, |
| pub non_or_conditionally: &'static str, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(r#"cannot call {$non_or_conditionally}-const {$def_descr} `{$def_path_str}` in {$kind -> |
| [const] constant |
| [static] static |
| [const_fn] constant function |
| *[other] {""} |
| }s"#, code = E0015)] |
| pub(crate) struct NonConstFnCall { |
| #[primary_span] |
| pub span: Span, |
| pub def_path_str: String, |
| pub def_descr: &'static str, |
| pub kind: ConstContext, |
| pub non_or_conditionally: &'static str, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag( |
| r#"cannot call non-const intrinsic `{$name}` in {$kind -> |
| [const] constant |
| [static] static |
| [const_fn] constant function |
| *[other] {""} |
| }s"# |
| )] |
| pub(crate) struct NonConstIntrinsic { |
| #[primary_span] |
| pub span: Span, |
| pub name: Symbol, |
| pub kind: ConstContext, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag("{$msg}")] |
| pub(crate) struct UnallowedOpInConstContext { |
| #[primary_span] |
| pub span: Span, |
| pub msg: String, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(r#"inline assembly is not allowed in {$kind -> |
| [const] constant |
| [static] static |
| [const_fn] constant function |
| *[other] {""} |
| }s"#, code = E0015)] |
| pub(crate) struct UnallowedInlineAsm { |
| #[primary_span] |
| pub span: Span, |
| pub kind: ConstContext, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag("interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed", code = E0492)] |
| #[note( |
| "temporaries in constants and statics can have their lifetime extended until the end of the program" |
| )] |
| #[note("to avoid accidentally creating global mutable state, such temporaries must be immutable")] |
| #[help( |
| "if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`" |
| )] |
| pub(crate) struct InteriorMutableBorrowEscaping { |
| #[primary_span] |
| #[label("this borrow of an interior mutable value refers to such a temporary")] |
| pub span: Span, |
| pub kind: ConstContext, |
| } |
| |
| #[derive(LintDiagnostic)] |
| #[diag("constant evaluation is taking a long time")] |
| #[note( |
| "this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval. |
| If your compilation actually takes a long time, you can safely allow the lint" |
| )] |
| pub struct LongRunning { |
| #[help("the constant being evaluated")] |
| pub item_span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag("constant evaluation is taking a long time")] |
| pub struct LongRunningWarn { |
| #[primary_span] |
| #[label("the const evaluator is currently interpreting this expression")] |
| pub span: Span, |
| #[help("the constant being evaluated")] |
| pub item_span: Span, |
| // Used for evading `-Z deduplicate-diagnostics`. |
| pub force_duplicate: usize, |
| } |
| |
| #[derive(Subdiagnostic)] |
| #[note("impl defined here, but it is not `const`")] |
| pub(crate) struct NonConstImplNote { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Clone)] |
| pub struct FrameNote { |
| pub span: Span, |
| pub times: i32, |
| pub where_: &'static str, |
| pub instance: String, |
| pub has_label: bool, |
| } |
| |
| impl Subdiagnostic for FrameNote { |
| fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) { |
| diag.arg("times", self.times); |
| diag.arg("where_", self.where_); |
| diag.arg("instance", self.instance); |
| let mut span: MultiSpan = self.span.into(); |
| if self.has_label && !self.span.is_dummy() { |
| span.push_span_label(self.span, msg!("the failure occurred here")); |
| } |
| let msg = diag.eagerly_translate(msg!( |
| r#"{$times -> |
| [0] inside {$where_ -> |
| [closure] closure |
| [instance] `{$instance}` |
| *[other] {""} |
| } |
| *[other] [... {$times} additional calls inside {$where_ -> |
| [closure] closure |
| [instance] `{$instance}` |
| *[other] {""} |
| } ...] |
| }"# |
| )); |
| diag.remove_arg("times"); |
| diag.remove_arg("where_"); |
| diag.remove_arg("instance"); |
| diag.span_note(span, msg); |
| } |
| } |
| |
| #[derive(Subdiagnostic)] |
| #[note(r#"the raw bytes of the constant (size: {$size}, align: {$align}) {"{"}{$bytes}{"}"}"#)] |
| pub struct RawBytesNote { |
| pub size: u64, |
| pub align: u64, |
| pub bytes: String, |
| } |
| |
| // FIXME(fee1-dead) do not use stringly typed `ConstContext` |
| |
| #[derive(Diagnostic)] |
| #[diag( |
| r#"cannot match on `{$ty}` in {$kind -> |
| [const] constant |
| [static] static |
| [const_fn] constant function |
| *[other] {""} |
| }s"# |
| )] |
| #[note("`{$ty}` cannot be compared in compile-time, and therefore cannot be used in `match`es")] |
| pub struct NonConstMatchEq<'tcx> { |
| #[primary_span] |
| pub span: Span, |
| pub ty: Ty<'tcx>, |
| pub kind: ConstContext, |
| pub non_or_conditionally: &'static str, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(r#"cannot use `for` loop on `{$ty}` in {$kind -> |
| [const] constant |
| [static] static |
| [const_fn] constant function |
| *[other] {""} |
| }s"#, code = E0015)] |
| pub struct NonConstForLoopIntoIter<'tcx> { |
| #[primary_span] |
| pub span: Span, |
| pub ty: Ty<'tcx>, |
| pub kind: ConstContext, |
| pub non_or_conditionally: &'static str, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(r#"`?` is not allowed on `{$ty}` in {$kind -> |
| [const] constant |
| [static] static |
| [const_fn] constant function |
| *[other] {""} |
| }s"#, code = E0015)] |
| pub struct NonConstQuestionBranch<'tcx> { |
| #[primary_span] |
| pub span: Span, |
| pub ty: Ty<'tcx>, |
| pub kind: ConstContext, |
| pub non_or_conditionally: &'static str, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(r#"`?` is not allowed on `{$ty}` in {$kind -> |
| [const] constant |
| [static] static |
| [const_fn] constant function |
| *[other] {""} |
| }s"#, code = E0015)] |
| pub struct NonConstQuestionFromResidual<'tcx> { |
| #[primary_span] |
| pub span: Span, |
| pub ty: Ty<'tcx>, |
| pub kind: ConstContext, |
| pub non_or_conditionally: &'static str, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(r#"`try` block cannot convert `{$ty}` to the result in {$kind -> |
| [const] constant |
| [static] static |
| [const_fn] constant function |
| *[other] {""} |
| }s"#, code = E0015)] |
| pub struct NonConstTryBlockFromOutput<'tcx> { |
| #[primary_span] |
| pub span: Span, |
| pub ty: Ty<'tcx>, |
| pub kind: ConstContext, |
| pub non_or_conditionally: &'static str, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(r#"cannot convert `{$ty}` into a future in {$kind -> |
| [const] constant |
| [static] static |
| [const_fn] constant function |
| *[other] {""} |
| }s"#, code = E0015)] |
| pub struct NonConstAwait<'tcx> { |
| #[primary_span] |
| pub span: Span, |
| pub ty: Ty<'tcx>, |
| pub kind: ConstContext, |
| pub non_or_conditionally: &'static str, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(r#"cannot call {$non_or_conditionally}-const closure in {$kind -> |
| [const] constant |
| [static] static |
| [const_fn] constant function |
| *[other] {""} |
| }s"#, code = E0015)] |
| pub struct NonConstClosure { |
| #[primary_span] |
| pub span: Span, |
| pub kind: ConstContext, |
| #[subdiagnostic] |
| pub note: Option<NonConstClosureNote>, |
| pub non_or_conditionally: &'static str, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(r#"calling const c-variadic functions is unstable in {$kind -> |
| [const] constant |
| [static] static |
| [const_fn] constant function |
| *[other] {""} |
| }s"#, code = E0015)] |
| pub struct NonConstCVariadicCall { |
| #[primary_span] |
| pub span: Span, |
| pub kind: ConstContext, |
| } |
| |
| #[derive(Subdiagnostic)] |
| pub enum NonConstClosureNote { |
| #[note("function defined here, but it is not `const`")] |
| FnDef { |
| #[primary_span] |
| span: Span, |
| }, |
| #[note( |
| r#"function pointers need an RFC before allowed to be called in {$kind -> |
| [const] constant |
| [static] static |
| [const_fn] constant function |
| *[other] {""} |
| }s"# |
| )] |
| FnPtr, |
| #[note( |
| r#"closures need an RFC before allowed to be called in {$kind -> |
| [const] constant |
| [static] static |
| [const_fn] constant function |
| *[other] {""} |
| }s"# |
| )] |
| Closure, |
| } |
| |
| #[derive(Subdiagnostic)] |
| #[multipart_suggestion("consider dereferencing here", applicability = "machine-applicable")] |
| pub struct ConsiderDereferencing { |
| pub deref: String, |
| #[suggestion_part(code = "{deref}")] |
| pub span: Span, |
| #[suggestion_part(code = "{deref}")] |
| pub rhs_span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(r#"cannot call {$non_or_conditionally}-const operator in {$kind -> |
| [const] constant |
| [static] static |
| [const_fn] constant function |
| *[other] {""} |
| }s"#, code = E0015)] |
| pub struct NonConstOperator { |
| #[primary_span] |
| pub span: Span, |
| pub kind: ConstContext, |
| #[subdiagnostic] |
| pub sugg: Option<ConsiderDereferencing>, |
| pub non_or_conditionally: &'static str, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(r#"cannot perform {$non_or_conditionally}-const deref coercion on `{$ty}` in {$kind -> |
| [const] constant |
| [static] static |
| [const_fn] constant function |
| *[other] {""} |
| }s"#, code = E0015)] |
| #[note("attempting to deref into `{$target_ty}`")] |
| pub struct NonConstDerefCoercion<'tcx> { |
| #[primary_span] |
| pub span: Span, |
| pub ty: Ty<'tcx>, |
| pub kind: ConstContext, |
| pub target_ty: Ty<'tcx>, |
| #[note("deref defined here")] |
| pub deref_target: Option<Span>, |
| pub non_or_conditionally: &'static str, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag("destructor of `{$dropped_ty}` cannot be evaluated at compile-time", code = E0493)] |
| pub struct LiveDrop<'tcx> { |
| #[primary_span] |
| #[label( |
| r#"the destructor for this type cannot be evaluated in {$kind -> |
| [const] constant |
| [static] static |
| [const_fn] constant function |
| *[other] {""} |
| }s"# |
| )] |
| pub span: Span, |
| pub kind: ConstContext, |
| pub dropped_ty: Ty<'tcx>, |
| #[label("value is dropped here")] |
| pub dropped_at: Span, |
| } |
| |
| pub trait ReportErrorExt { |
| /// Returns the diagnostic message for this error. |
| fn diagnostic_message(&self) -> DiagMessage; |
| fn add_args<G: EmissionGuarantee>(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 = dcx.eagerly_translate_to_string(message, diag.args.iter()); |
| 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<G: EmissionGuarantee>(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::<Option<CtfeProvenance>>::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<G: EmissionGuarantee>(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 { |
| err.dcx.eagerly_translate_to_string( |
| msg!("constructing invalid value at {$path}"), |
| [("path".into(), DiagArgValue::Str(path.into()))].iter().map(|(a, b)| (a, b)), |
| ) |
| } else { |
| err.dcx.eagerly_translate_to_string(msg!("constructing invalid value"), [].into_iter()) |
| }; |
| |
| err.arg("front_matter", message); |
| |
| fn add_range_arg<G: EmissionGuarantee>( |
| 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 args = [ |
| ("lo".into(), DiagArgValue::Str(lo.to_string().into())), |
| ("hi".into(), DiagArgValue::Str(hi.to_string().into())), |
| ]; |
| let args = args.iter().map(|(a, b)| (a, b)); |
| let message = err.dcx.eagerly_translate_to_string(msg, args); |
| 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 => msg!("expected a reference"), |
| ExpectedKind::Box => msg!("expected a box"), |
| ExpectedKind::RawPtr => msg!("expected a raw pointer"), |
| ExpectedKind::InitScalar => msg!("expected initialized scalar value"), |
| ExpectedKind::Bool => msg!("expected a boolean"), |
| ExpectedKind::Char => msg!("expected a unicode scalar value"), |
| ExpectedKind::Float => msg!("expected a floating point number"), |
| ExpectedKind::Int => msg!("expected an integer"), |
| ExpectedKind::FnPtr => msg!("expected a function pointer"), |
| ExpectedKind::EnumTag => msg!("expected a valid enum tag"), |
| ExpectedKind::Str => msg!("expected a string"), |
| }; |
| let msg = err.dcx.eagerly_translate_to_string(msg, [].into_iter()); |
| 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<G: EmissionGuarantee>(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<G: EmissionGuarantee>(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<G: EmissionGuarantee>(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<G: EmissionGuarantee>(self, _: &mut Diag<'_, G>) {} |
| } |
| |
| impl rustc_errors::IntoDiagArg for InternKind { |
| fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue { |
| DiagArgValue::Str(Cow::Borrowed(match self { |
| InternKind::Static(Mutability::Not) => "static", |
| InternKind::Static(Mutability::Mut) => "static_mut", |
| InternKind::Constant => "const", |
| InternKind::Promoted => "promoted", |
| })) |
| } |
| } |