rust/compiler/rustc_const_eval/src/errors.rs
Jana Dönszelmann 63c5a84b74
Rollup merge of #142724 - xizheyin:avoid_overwrite_args, r=oli-obk
Add runtime check to avoid overwrite arg in `Diag`

## Origin PR description
At first, I set up a `debug_assert` check for the arg method to make sure that `args` in `Diag` aren't easily overwritten, and I added the `remove_arg()` method, so that if you do need to overwrite an arg, then you can explicitly call `remove_arg()` to remove it first, then call `arg()` to overwrite it.

For the code before the rust-lang/rust#142015 change, it won't compile because it will report an error
```
arg `instance`already exists.
```

This PR also modifies all diagnostics that fail the check to pass the check. There are two cases of check failure:

1. ~~Between *the parent diagnostic and the subdiagnostic*, or *between the subdiagnostics* have the same field between them. In this case, I renamed the conflicting fields.~~
2. ~~For subdiagnostics stored in `Vec`, the rendering may iteratively write the same arg over and over again. In this case, I changed the auto-generation with `derive(SubDiagnostic)` to manually implementing `SubDiagnostic` and manually rendered it with `eagerly_translate()`, similar to https://github.com/rust-lang/rust/issues/142031#issuecomment-2984812090, and after rendering it I manually deleted useless arg with the newly added `remove_arg` method.~~

## Final Decision

After trying and discussing, we made a final decision.

For `#[derive(Subdiagnostic)]`, This PR made two changes:

1. After the subdiagnostic is rendered, remove all args of this subdiagnostic, which allows for usage like `Vec<Subdiag>`.
2. Store `diag.args` before setting arguments, so that you can restore the contents of the main diagnostic after deleting the arguments after subdiagnostic is rendered, to avoid deleting the main diagnostic's arg when they have the same name args.
2025-06-25 22:14:55 +02:00

945 lines
33 KiB
Rust

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,
};
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::fluent_generated as fluent;
use crate::interpret::InternKind;
#[derive(Diagnostic)]
#[diag(const_eval_dangling_ptr_in_final)]
pub(crate) struct DanglingPtrInFinal {
#[primary_span]
pub span: Span,
pub kind: InternKind,
}
#[derive(Diagnostic)]
#[diag(const_eval_nested_static_in_thread_local)]
pub(crate) struct NestedStaticInThreadLocal {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(const_eval_mutable_ptr_in_final)]
pub(crate) struct MutablePtrInFinal {
#[primary_span]
pub span: Span,
pub kind: InternKind,
}
#[derive(Diagnostic)]
#[diag(const_eval_unstable_in_stable_exposed)]
pub(crate) struct UnstableInStableExposed {
pub gate: String,
#[primary_span]
pub span: Span,
#[help(const_eval_is_function_call)]
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(
const_eval_unstable_sugg,
code = "#[rustc_const_unstable(feature = \"...\", issue = \"...\")]\n",
applicability = "has-placeholders"
)]
pub attr_span: Span,
}
#[derive(Diagnostic)]
#[diag(const_eval_thread_local_access, code = E0625)]
pub(crate) struct ThreadLocalAccessErr {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(const_eval_raw_ptr_to_int)]
#[note]
#[note(const_eval_note2)]
pub(crate) struct RawPtrToIntErr {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(const_eval_raw_ptr_comparison)]
#[note]
pub(crate) struct RawPtrComparisonErr {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(const_eval_panic_non_str)]
pub(crate) struct PanicNonStrErr {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(const_eval_max_num_nodes_in_const)]
pub(crate) struct MaxNumNodesInConstErr {
#[primary_span]
pub span: Option<Span>,
pub global_const_id: String,
}
#[derive(Diagnostic)]
#[diag(const_eval_unallowed_fn_pointer_call)]
pub(crate) struct UnallowedFnPointerCall {
#[primary_span]
pub span: Span,
pub kind: ConstContext,
}
#[derive(Diagnostic)]
#[diag(const_eval_unstable_const_fn)]
pub(crate) struct UnstableConstFn {
#[primary_span]
pub span: Span,
pub def_path: String,
}
#[derive(Diagnostic)]
#[diag(const_eval_unstable_const_trait)]
pub(crate) struct UnstableConstTrait {
#[primary_span]
pub span: Span,
pub def_path: String,
}
#[derive(Diagnostic)]
#[diag(const_eval_unstable_intrinsic)]
pub(crate) struct UnstableIntrinsic {
#[primary_span]
pub span: Span,
pub name: Symbol,
pub feature: Symbol,
#[suggestion(
const_eval_unstable_intrinsic_suggestion,
code = "#![feature({feature})]\n",
applicability = "machine-applicable"
)]
pub suggestion: Span,
}
#[derive(Diagnostic)]
#[diag(const_eval_unmarked_const_item_exposed)]
#[help]
pub(crate) struct UnmarkedConstItemExposed {
#[primary_span]
pub span: Span,
pub def_path: String,
}
#[derive(Diagnostic)]
#[diag(const_eval_unmarked_intrinsic_exposed)]
#[help]
pub(crate) struct UnmarkedIntrinsicExposed {
#[primary_span]
pub span: Span,
pub def_path: String,
}
#[derive(Diagnostic)]
#[diag(const_eval_mutable_ref_escaping, code = E0764)]
pub(crate) struct MutableRefEscaping {
#[primary_span]
pub span: Span,
pub kind: ConstContext,
#[note(const_eval_teach_note)]
pub teach: bool,
}
#[derive(Diagnostic)]
#[diag(const_eval_mutable_raw_escaping, code = E0764)]
pub(crate) struct MutableRawEscaping {
#[primary_span]
pub span: Span,
pub kind: ConstContext,
#[note(const_eval_teach_note)]
pub teach: bool,
}
#[derive(Diagnostic)]
#[diag(const_eval_non_const_fmt_macro_call, code = E0015)]
pub(crate) struct NonConstFmtMacroCall {
#[primary_span]
pub span: Span,
pub kind: ConstContext,
pub non_or_conditionally: &'static str,
}
#[derive(Diagnostic)]
#[diag(const_eval_non_const_fn_call, 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(const_eval_non_const_intrinsic)]
pub(crate) struct NonConstIntrinsic {
#[primary_span]
pub span: Span,
pub name: Symbol,
pub kind: ConstContext,
}
#[derive(Diagnostic)]
#[diag(const_eval_unallowed_op_in_const_context)]
pub(crate) struct UnallowedOpInConstContext {
#[primary_span]
pub span: Span,
pub msg: String,
}
#[derive(Diagnostic)]
#[diag(const_eval_unallowed_heap_allocations, code = E0010)]
pub(crate) struct UnallowedHeapAllocations {
#[primary_span]
#[label]
pub span: Span,
pub kind: ConstContext,
#[note(const_eval_teach_note)]
pub teach: bool,
}
#[derive(Diagnostic)]
#[diag(const_eval_unallowed_inline_asm, code = E0015)]
pub(crate) struct UnallowedInlineAsm {
#[primary_span]
pub span: Span,
pub kind: ConstContext,
}
#[derive(Diagnostic)]
#[diag(const_eval_interior_mutable_ref_escaping, code = E0492)]
pub(crate) struct InteriorMutableRefEscaping {
#[primary_span]
#[label]
pub span: Span,
#[help]
pub opt_help: bool,
pub kind: ConstContext,
#[note(const_eval_teach_note)]
pub teach: bool,
}
#[derive(LintDiagnostic)]
#[diag(const_eval_long_running)]
#[note]
pub struct LongRunning {
#[help]
pub item_span: Span,
}
#[derive(Diagnostic)]
#[diag(const_eval_long_running)]
pub struct LongRunningWarn {
#[primary_span]
#[label]
pub span: Span,
#[help]
pub item_span: Span,
// Used for evading `-Z deduplicate-diagnostics`.
pub force_duplicate: usize,
}
#[derive(Subdiagnostic)]
#[note(const_eval_non_const_impl)]
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, fluent::const_eval_frame_note_last);
}
let msg = diag.eagerly_translate(fluent::const_eval_frame_note);
diag.remove_arg("times");
diag.remove_arg("where_");
diag.remove_arg("instance");
diag.span_note(span, msg);
}
}
#[derive(Subdiagnostic)]
#[note(const_eval_raw_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(const_eval_non_const_match_eq, code = E0015)]
#[note]
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(const_eval_non_const_for_loop_into_iter, 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(const_eval_non_const_question_branch, 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(const_eval_non_const_question_from_residual, 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(const_eval_non_const_try_block_from_output, 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(const_eval_non_const_await, 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(const_eval_non_const_closure, 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(Subdiagnostic)]
pub enum NonConstClosureNote {
#[note(const_eval_closure_fndef_not_const)]
FnDef {
#[primary_span]
span: Span,
},
#[note(const_eval_fn_ptr_call)]
FnPtr,
#[note(const_eval_closure_call)]
Closure,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(const_eval_consider_dereferencing, 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(const_eval_non_const_operator, 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(const_eval_non_const_deref_coercion, code = E0015)]
#[note]
pub struct NonConstDerefCoercion<'tcx> {
#[primary_span]
pub span: Span,
pub ty: Ty<'tcx>,
pub kind: ConstContext,
pub target_ty: Ty<'tcx>,
#[note(const_eval_target_note)]
pub deref_target: Option<Span>,
pub non_or_conditionally: &'static str,
}
#[derive(Diagnostic)]
#[diag(const_eval_live_drop, code = E0493)]
pub struct LiveDrop<'tcx> {
#[primary_span]
#[label]
pub span: Span,
pub kind: ConstContext,
pub dropped_ty: Ty<'tcx>,
#[label(const_eval_dropped_at_label)]
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::*;
use crate::fluent_generated::*;
match self {
Ub(msg) => msg.clone().into(),
Custom(x) => (x.msg)(),
ValidationError(e) => e.diagnostic_message(),
Unreachable => const_eval_unreachable,
BoundsCheckFailed { .. } => const_eval_bounds_check_failed,
DivisionByZero => const_eval_division_by_zero,
RemainderByZero => const_eval_remainder_by_zero,
DivisionOverflow => const_eval_division_overflow,
RemainderOverflow => const_eval_remainder_overflow,
PointerArithOverflow => const_eval_pointer_arithmetic_overflow,
ArithOverflow { .. } => const_eval_overflow_arith,
ShiftOverflow { .. } => const_eval_overflow_shift,
InvalidMeta(InvalidMetaKind::SliceTooBig) => const_eval_invalid_meta_slice,
InvalidMeta(InvalidMetaKind::TooBig) => const_eval_invalid_meta,
UnterminatedCString(_) => const_eval_unterminated_c_string,
PointerUseAfterFree(_, _) => const_eval_pointer_use_after_free,
PointerOutOfBounds { .. } => const_eval_pointer_out_of_bounds,
DanglingIntPointer { addr: 0, .. } => const_eval_dangling_null_pointer,
DanglingIntPointer { .. } => const_eval_dangling_int_pointer,
AlignmentCheckFailed { .. } => const_eval_alignment_check_failed,
WriteToReadOnly(_) => const_eval_write_to_read_only,
DerefFunctionPointer(_) => const_eval_deref_function_pointer,
DerefVTablePointer(_) => const_eval_deref_vtable_pointer,
InvalidBool(_) => const_eval_invalid_bool,
InvalidChar(_) => const_eval_invalid_char,
InvalidTag(_) => const_eval_invalid_tag,
InvalidFunctionPointer(_) => const_eval_invalid_function_pointer,
InvalidVTablePointer(_) => const_eval_invalid_vtable_pointer,
InvalidVTableTrait { .. } => const_eval_invalid_vtable_trait,
InvalidStr(_) => const_eval_invalid_str,
InvalidUninitBytes(None) => const_eval_invalid_uninit_bytes_unknown,
InvalidUninitBytes(Some(_)) => const_eval_invalid_uninit_bytes,
DeadLocal => const_eval_dead_local,
ScalarSizeMismatch(_) => const_eval_scalar_size_mismatch,
UninhabitedEnumVariantWritten(_) => const_eval_uninhabited_enum_variant_written,
UninhabitedEnumVariantRead(_) => const_eval_uninhabited_enum_variant_read,
InvalidNichedEnumVariantWritten { .. } => {
const_eval_invalid_niched_enum_variant_written
}
AbiMismatchArgument { .. } => const_eval_incompatible_types,
AbiMismatchReturn { .. } => const_eval_incompatible_return_types,
}
}
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
| 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) | 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>>::from_addr_invalid(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) => {
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.to_string());
}
AbiMismatchArgument { caller_ty, callee_ty }
| AbiMismatchReturn { caller_ty, callee_ty } => {
diag.arg("caller_ty", caller_ty.to_string());
diag.arg("callee_ty", callee_ty.to_string());
}
}
}
}
impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
fn diagnostic_message(&self) -> DiagMessage {
use rustc_middle::mir::interpret::ValidationErrorKind::*;
use crate::fluent_generated::*;
match self.kind {
PtrToUninhabited { ptr_kind: PointerKind::Box, .. } => {
const_eval_validation_box_to_uninhabited
}
PtrToUninhabited { ptr_kind: PointerKind::Ref(_), .. } => {
const_eval_validation_ref_to_uninhabited
}
PointerAsInt { .. } => const_eval_validation_pointer_as_int,
PartialPointer => const_eval_validation_partial_pointer,
ConstRefToMutable => const_eval_validation_const_ref_to_mutable,
ConstRefToExtern => const_eval_validation_const_ref_to_extern,
MutableRefToImmutable => const_eval_validation_mutable_ref_to_immutable,
NullFnPtr => const_eval_validation_null_fn_ptr,
NeverVal => const_eval_validation_never_val,
NullablePtrOutOfRange { .. } => const_eval_validation_nullable_ptr_out_of_range,
PtrOutOfRange { .. } => const_eval_validation_ptr_out_of_range,
OutOfRange { .. } => const_eval_validation_out_of_range,
UnsafeCellInImmutable => const_eval_validation_unsafe_cell,
UninhabitedVal { .. } => const_eval_validation_uninhabited_val,
InvalidEnumTag { .. } => const_eval_validation_invalid_enum_tag,
UninhabitedEnumVariant => const_eval_validation_uninhabited_enum_variant,
Uninit { .. } => const_eval_validation_uninit,
InvalidVTablePtr { .. } => const_eval_validation_invalid_vtable_ptr,
InvalidMetaWrongTrait { .. } => const_eval_validation_invalid_vtable_trait,
InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Box } => {
const_eval_validation_invalid_box_slice_meta
}
InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Ref(_) } => {
const_eval_validation_invalid_ref_slice_meta
}
InvalidMetaTooLarge { ptr_kind: PointerKind::Box } => {
const_eval_validation_invalid_box_meta
}
InvalidMetaTooLarge { ptr_kind: PointerKind::Ref(_) } => {
const_eval_validation_invalid_ref_meta
}
UnalignedPtr { ptr_kind: PointerKind::Ref(_), .. } => {
const_eval_validation_unaligned_ref
}
UnalignedPtr { ptr_kind: PointerKind::Box, .. } => const_eval_validation_unaligned_box,
NullPtr { ptr_kind: PointerKind::Box } => const_eval_validation_null_box,
NullPtr { ptr_kind: PointerKind::Ref(_) } => const_eval_validation_null_ref,
DanglingPtrNoProvenance { ptr_kind: PointerKind::Box, .. } => {
const_eval_validation_dangling_box_no_provenance
}
DanglingPtrNoProvenance { ptr_kind: PointerKind::Ref(_), .. } => {
const_eval_validation_dangling_ref_no_provenance
}
DanglingPtrOutOfBounds { ptr_kind: PointerKind::Box } => {
const_eval_validation_dangling_box_out_of_bounds
}
DanglingPtrOutOfBounds { ptr_kind: PointerKind::Ref(_) } => {
const_eval_validation_dangling_ref_out_of_bounds
}
DanglingPtrUseAfterFree { ptr_kind: PointerKind::Box } => {
const_eval_validation_dangling_box_use_after_free
}
DanglingPtrUseAfterFree { ptr_kind: PointerKind::Ref(_) } => {
const_eval_validation_dangling_ref_use_after_free
}
InvalidBool { .. } => const_eval_validation_invalid_bool,
InvalidChar { .. } => const_eval_validation_invalid_char,
InvalidFnPtr { .. } => const_eval_validation_invalid_fn_ptr,
}
}
fn add_args<G: EmissionGuarantee>(self, err: &mut Diag<'_, G>) {
use rustc_middle::mir::interpret::ValidationErrorKind::*;
use crate::fluent_generated as fluent;
if let PointerAsInt { .. } | PartialPointer = self.kind {
err.help(fluent::const_eval_ptr_as_bytes_1);
err.help(fluent::const_eval_ptr_as_bytes_2);
}
let message = if let Some(path) = self.path {
err.dcx.eagerly_translate_to_string(
fluent::const_eval_validation_front_matter_invalid_value_with_path,
[("path".into(), DiagArgValue::Str(path.into()))].iter().map(|(a, b)| (a, b)),
)
} else {
err.dcx.eagerly_translate_to_string(
fluent::const_eval_validation_front_matter_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 {
fluent::const_eval_range_wrapping
} else if lo == hi {
fluent::const_eval_range_singular
} else if lo == 0 {
assert!(hi < max_hi, "should not be printing if the range covers everything");
fluent::const_eval_range_upper
} else if hi == max_hi {
assert!(lo > 0, "should not be printing if the range covers everything");
fluent::const_eval_range_lower
} else {
fluent::const_eval_range
};
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 => fluent::const_eval_validation_expected_ref,
ExpectedKind::Box => fluent::const_eval_validation_expected_box,
ExpectedKind::RawPtr => fluent::const_eval_validation_expected_raw_ptr,
ExpectedKind::InitScalar => fluent::const_eval_validation_expected_init_scalar,
ExpectedKind::Bool => fluent::const_eval_validation_expected_bool,
ExpectedKind::Char => fluent::const_eval_validation_expected_char,
ExpectedKind::Float => fluent::const_eval_validation_expected_float,
ExpectedKind::Int => fluent::const_eval_validation_expected_int,
ExpectedKind::FnPtr => fluent::const_eval_validation_expected_fn_ptr,
ExpectedKind::EnumTag => fluent::const_eval_validation_expected_enum_tag,
ExpectedKind::Str => fluent::const_eval_validation_expected_str,
};
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);
}
NullablePtrOutOfRange { range, max_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 { .. }
| ConstRefToMutable
| ConstRefToExtern
| MutableRefToImmutable
| NullFnPtr
| NeverVal
| UnsafeCellInImmutable
| InvalidMetaSliceTooLarge { .. }
| InvalidMetaTooLarge { .. }
| DanglingPtrUseAfterFree { .. }
| DanglingPtrOutOfBounds { .. }
| UninhabitedEnumVariant
| PartialPointer => {}
}
}
}
impl ReportErrorExt for UnsupportedOpInfo {
fn diagnostic_message(&self) -> DiagMessage {
use crate::fluent_generated::*;
match self {
UnsupportedOpInfo::Unsupported(s) => s.clone().into(),
UnsupportedOpInfo::ExternTypeField => const_eval_extern_type_field,
UnsupportedOpInfo::UnsizedLocal => const_eval_unsized_local,
UnsupportedOpInfo::OverwritePartialPointer(_) => const_eval_partial_pointer_overwrite,
UnsupportedOpInfo::ReadPartialPointer(_) => const_eval_partial_pointer_copy,
UnsupportedOpInfo::ReadPointerAsInt(_) => const_eval_read_pointer_as_int,
UnsupportedOpInfo::ThreadLocalStatic(_) => const_eval_thread_local_static,
UnsupportedOpInfo::ExternStatic(_) => const_eval_extern_static,
}
}
fn add_args<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
use UnsupportedOpInfo::*;
use crate::fluent_generated::*;
if let ReadPointerAsInt(_) | OverwritePartialPointer(_) | ReadPartialPointer(_) = self {
diag.help(const_eval_ptr_as_bytes_1);
diag.help(const_eval_ptr_as_bytes_2);
}
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(_) => {}
OverwritePartialPointer(ptr) | 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 {
use crate::fluent_generated::*;
match self {
InvalidProgramInfo::TooGeneric => const_eval_too_generic,
InvalidProgramInfo::AlreadyReported(_) => const_eval_already_reported,
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 {
use crate::fluent_generated::*;
match self {
ResourceExhaustionInfo::StackFrameLimitReached => const_eval_stack_frame_limit_reached,
ResourceExhaustionInfo::MemoryExhausted => const_eval_memory_exhausted,
ResourceExhaustionInfo::AddressSpaceFull => const_eval_address_space_full,
ResourceExhaustionInfo::Interrupted => const_eval_interrupted,
}
}
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",
}))
}
}