mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-06 08:36:52 -04:00
9b64d52d78
Most diagnostic types are only used within their own crate, and so have a `pub(crate)` visibility. We have some diagnostic types that are unnecessarily `pub`. This is bad because (a) information hiding, and (b) if a `pub(crate)` type becomes unused the compiler will warn but it won't warn for a `pub` type. This commit eliminates unnecessary `pub` visibilities for some diagnostic types, and also some related things due to knock-on effects. (I found these types with some ad hoc use of `grep`.)
252 lines
9.8 KiB
Rust
252 lines
9.8 KiB
Rust
use std::{fmt, mem};
|
|
|
|
use rustc_errors::{Diag, E0080};
|
|
use rustc_middle::mir::AssertKind;
|
|
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::{DUMMY_SP, Span, Symbol};
|
|
|
|
use super::CompileTimeMachine;
|
|
use crate::errors::{self, FrameNote};
|
|
use crate::interpret::{
|
|
CtfeProvenance, ErrorHandled, Frame, InterpCx, InterpErrorInfo, InterpErrorKind,
|
|
MachineStopType, Pointer, err_inval, err_machine_stop,
|
|
};
|
|
|
|
/// The CTFE machine has some custom error kinds.
|
|
#[derive(Clone, Debug)]
|
|
pub enum ConstEvalErrKind {
|
|
ConstAccessesMutGlobal,
|
|
ModifiedGlobal,
|
|
RecursiveStatic,
|
|
AssertFailure(AssertKind<ConstInt>),
|
|
Panic {
|
|
msg: Symbol,
|
|
line: u32,
|
|
col: u32,
|
|
file: Symbol,
|
|
},
|
|
WriteThroughImmutablePointer,
|
|
/// Called `const_make_global` twice.
|
|
ConstMakeGlobalPtrAlreadyMadeGlobal(AllocId),
|
|
/// Called `const_make_global` on a non-heap pointer.
|
|
ConstMakeGlobalPtrIsNonHeap(Pointer<Option<CtfeProvenance>>),
|
|
/// Called `const_make_global` on a dangling pointer.
|
|
ConstMakeGlobalWithDanglingPtr(Pointer<Option<CtfeProvenance>>),
|
|
/// Called `const_make_global` on a pointer that does not start at the
|
|
/// beginning of an object.
|
|
ConstMakeGlobalWithOffset(Pointer<Option<CtfeProvenance>>),
|
|
}
|
|
|
|
impl fmt::Display for ConstEvalErrKind {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
use ConstEvalErrKind::*;
|
|
match self {
|
|
ConstAccessesMutGlobal => write!(f, "constant accesses mutable global memory"),
|
|
ModifiedGlobal => {
|
|
write!(f, "modifying a static's initial value from another static's initializer")
|
|
}
|
|
Panic { msg, .. } => write!(f, "evaluation panicked: {msg}"),
|
|
RecursiveStatic => {
|
|
write!(f, "encountered static that tried to access itself during initialization")
|
|
}
|
|
AssertFailure(x) => write!(f, "{x}"),
|
|
WriteThroughImmutablePointer => {
|
|
write!(
|
|
f,
|
|
"writing through a pointer that was derived from a shared (immutable) reference"
|
|
)
|
|
}
|
|
ConstMakeGlobalPtrAlreadyMadeGlobal(alloc) => {
|
|
write!(
|
|
f,
|
|
"attempting to call `const_make_global` twice on the same allocation {alloc}"
|
|
)
|
|
}
|
|
ConstMakeGlobalPtrIsNonHeap(ptr) => {
|
|
write!(
|
|
f,
|
|
"pointer passed to `const_make_global` does not point to a heap allocation: {ptr}"
|
|
)
|
|
}
|
|
ConstMakeGlobalWithDanglingPtr(ptr) => {
|
|
write!(f, "pointer passed to `const_make_global` is dangling: {ptr}")
|
|
}
|
|
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<InterpErrorInfo<'tcx>> for ConstEvalErrKind {
|
|
fn into(self) -> InterpErrorInfo<'tcx> {
|
|
err_machine_stop!(self).into()
|
|
}
|
|
}
|
|
|
|
pub(crate) fn get_span_and_frames<'tcx>(
|
|
tcx: TyCtxtAt<'tcx>,
|
|
stack: &[Frame<'tcx, impl Provenance, impl Sized>],
|
|
) -> (Span, Vec<errors::FrameNote>) {
|
|
let mut stacktrace = Frame::generate_stacktrace_from_stack(stack, *tcx);
|
|
// Filter out `requires_caller_location` frames.
|
|
stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*tcx));
|
|
let span = stacktrace.last().map(|f| f.span).unwrap_or(tcx.span);
|
|
|
|
let mut frames = Vec::new();
|
|
|
|
// Add notes to the backtrace. Don't print a single-line backtrace though.
|
|
if stacktrace.len() > 1 {
|
|
// Helper closure to print duplicated lines.
|
|
let mut add_frame = |mut frame: errors::FrameNote| {
|
|
frames.push(errors::FrameNote { times: 0, ..frame.clone() });
|
|
// Don't print [... additional calls ...] if the number of lines is small
|
|
if frame.times < 3 {
|
|
let times = frame.times;
|
|
frame.times = 0;
|
|
frames.extend(std::iter::repeat_n(frame, times as usize));
|
|
} else {
|
|
frames.push(frame);
|
|
}
|
|
};
|
|
|
|
let mut last_frame: Option<errors::FrameNote> = None;
|
|
for frame_info in &stacktrace {
|
|
let frame = frame_info.as_note(*tcx);
|
|
match last_frame.as_mut() {
|
|
Some(last_frame)
|
|
if last_frame.span == frame.span
|
|
&& last_frame.where_ == frame.where_
|
|
&& last_frame.instance == frame.instance =>
|
|
{
|
|
last_frame.times += 1;
|
|
}
|
|
Some(last_frame) => {
|
|
add_frame(mem::replace(last_frame, frame));
|
|
}
|
|
None => {
|
|
last_frame = Some(frame);
|
|
}
|
|
}
|
|
}
|
|
if let Some(frame) = last_frame {
|
|
add_frame(frame);
|
|
}
|
|
}
|
|
|
|
// In `rustc`, we present const-eval errors from the outer-most place first to the inner-most.
|
|
// So we reverse the frames here. The first frame will be the same as the span from the current
|
|
// `TyCtxtAt<'_>`, so we remove it as it would be redundant.
|
|
frames.reverse();
|
|
if frames.len() > 0 {
|
|
frames.remove(0);
|
|
}
|
|
if let Some(last) = frames.last_mut()
|
|
// If the span is not going to be printed, we don't want the span label for `is_last`.
|
|
&& tcx.sess.source_map().span_to_snippet(last.span.source_callsite()).is_ok()
|
|
{
|
|
last.has_label = true;
|
|
}
|
|
|
|
(span, frames)
|
|
}
|
|
|
|
/// Create a diagnostic for a const eval error.
|
|
///
|
|
/// This will use the `mk` function for adding more information to the error.
|
|
/// You can use it to add a stacktrace of current execution according to
|
|
/// `get_span_and_frames` or just give context on where the const eval error happened.
|
|
pub(super) fn report<'tcx>(
|
|
ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>,
|
|
error: InterpErrorKind<'tcx>,
|
|
mk: impl FnOnce(&mut Diag<'_>, Span, Vec<FrameNote>),
|
|
) -> ErrorHandled {
|
|
let tcx = ecx.tcx.tcx;
|
|
// Special handling for certain errors
|
|
match error {
|
|
// Don't emit a new diagnostic for these errors, they are already reported elsewhere or
|
|
// should remain silent.
|
|
err_inval!(AlreadyReported(info)) => ErrorHandled::Reported(info, DUMMY_SP),
|
|
err_inval!(Layout(LayoutError::TooGeneric(_))) | err_inval!(TooGeneric) => {
|
|
ErrorHandled::TooGeneric(DUMMY_SP)
|
|
}
|
|
err_inval!(Layout(LayoutError::ReferencesError(guar))) => {
|
|
// This can occur in infallible promoteds e.g. when a non-existent type or field is
|
|
// encountered.
|
|
ErrorHandled::Reported(ReportedErrorInfo::allowed_in_infallible(guar), DUMMY_SP)
|
|
}
|
|
// Report remaining errors.
|
|
_ => {
|
|
let (span, frames) = super::get_span_and_frames(ecx.tcx, ecx.stack());
|
|
let mut err = tcx.dcx().struct_span_err(span, error.to_string());
|
|
err.code(E0080);
|
|
if matches!(
|
|
error,
|
|
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
|
|
{
|
|
let bytes = ecx.print_alloc_bytes_for_diagnostics(alloc_id);
|
|
let info = ecx.get_alloc_info(alloc_id);
|
|
let raw_bytes = errors::RawBytesNote {
|
|
size: info.size.bytes(),
|
|
align: info.align.bytes(),
|
|
bytes,
|
|
};
|
|
err.subdiagnostic(raw_bytes);
|
|
}
|
|
|
|
// 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();
|
|
let reported = if allowed_in_infallible {
|
|
ReportedErrorInfo::allowed_in_infallible(g)
|
|
} else {
|
|
ReportedErrorInfo::const_eval_error(g)
|
|
};
|
|
ErrorHandled::Reported(reported, span)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Emit a lint from a const-eval situation, with a backtrace.
|
|
// Even if this is unused, please don't remove it -- chances are we will need to emit a lint during const-eval again in the future!
|
|
#[allow(unused)]
|
|
pub(super) fn lint<'tcx, L>(
|
|
tcx: TyCtxtAt<'tcx>,
|
|
machine: &CompileTimeMachine<'tcx>,
|
|
lint: &'static rustc_session::lint::Lint,
|
|
decorator: impl FnOnce(Vec<errors::FrameNote>) -> L,
|
|
) where
|
|
L: for<'a> rustc_errors::Diagnostic<'a, ()>,
|
|
{
|
|
let (span, frames) = get_span_and_frames(tcx, &machine.stack);
|
|
|
|
tcx.emit_node_span_lint(lint, machine.best_lint_scope(*tcx), span, decorator(frames));
|
|
}
|