1use std::{fmt, mem};
23use rustc_errors::{Diag, E0080};
4use rustc_middle::mir::AssertKind;
5use rustc_middle::mir::interpret::{
6AllocId, Provenance, ReportedErrorInfo, UndefinedBehaviorInfo, UnsupportedOpInfo,
7};
8use rustc_middle::query::TyCtxtAt;
9use rustc_middle::ty::ConstInt;
10use rustc_middle::ty::layout::LayoutError;
11use rustc_span::{DUMMY_SP, Span, Symbol};
1213use super::CompileTimeMachine;
14use crate::diagnostics::{self, FrameNote};
15use crate::interpret::{
16CtfeProvenance, ErrorHandled, Frame, InterpCx, InterpErrorInfo, InterpErrorKind,
17MachineStopType, Pointer, err_inval, err_machine_stop,
18};
1920/// The CTFE machine has some custom error kinds.
21#[derive(#[automatically_derived]
impl ::core::clone::Clone for ConstEvalErrKind {
#[inline]
fn clone(&self) -> Self {
match self {
Self::ConstAccessesMutGlobal =>
ConstEvalErrKind::ConstAccessesMutGlobal,
Self::ModifiedGlobal => ConstEvalErrKind::ModifiedGlobal,
Self::RecursiveStatic => ConstEvalErrKind::RecursiveStatic,
Self::AssertFailure(__self_0) =>
ConstEvalErrKind::AssertFailure(::core::clone::Clone::clone(__self_0)),
Self::Panic {
msg: __self_0, line: __self_1, col: __self_2, file: __self_3 }
=>
ConstEvalErrKind::Panic {
msg: ::core::clone::Clone::clone(__self_0),
line: ::core::clone::Clone::clone(__self_1),
col: ::core::clone::Clone::clone(__self_2),
file: ::core::clone::Clone::clone(__self_3),
},
Self::Abort => ConstEvalErrKind::Abort,
Self::WriteThroughImmutablePointer =>
ConstEvalErrKind::WriteThroughImmutablePointer,
Self::ConstMakeGlobalPtrAlreadyMadeGlobal(__self_0) =>
ConstEvalErrKind::ConstMakeGlobalPtrAlreadyMadeGlobal(::core::clone::Clone::clone(__self_0)),
Self::ConstMakeGlobalPtrIsNonHeap(__self_0) =>
ConstEvalErrKind::ConstMakeGlobalPtrIsNonHeap(::core::clone::Clone::clone(__self_0)),
Self::ConstMakeGlobalWithDanglingPtr(__self_0) =>
ConstEvalErrKind::ConstMakeGlobalWithDanglingPtr(::core::clone::Clone::clone(__self_0)),
Self::ConstMakeGlobalWithOffset(__self_0) =>
ConstEvalErrKind::ConstMakeGlobalWithOffset(::core::clone::Clone::clone(__self_0)),
}
}
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for ConstEvalErrKind {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Self::ConstAccessesMutGlobal =>
::core::fmt::Formatter::write_str(f,
"ConstAccessesMutGlobal"),
Self::ModifiedGlobal =>
::core::fmt::Formatter::write_str(f, "ModifiedGlobal"),
Self::RecursiveStatic =>
::core::fmt::Formatter::write_str(f, "RecursiveStatic"),
Self::AssertFailure(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"AssertFailure", &__self_0),
Self::Panic {
msg: __self_0, line: __self_1, col: __self_2, file: __self_3 }
=>
::core::fmt::Formatter::debug_struct_field4_finish(f, "Panic",
"msg", __self_0, "line", __self_1, "col", __self_2, "file",
&__self_3),
Self::Abort => ::core::fmt::Formatter::write_str(f, "Abort"),
Self::WriteThroughImmutablePointer =>
::core::fmt::Formatter::write_str(f,
"WriteThroughImmutablePointer"),
Self::ConstMakeGlobalPtrAlreadyMadeGlobal(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"ConstMakeGlobalPtrAlreadyMadeGlobal", &__self_0),
Self::ConstMakeGlobalPtrIsNonHeap(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"ConstMakeGlobalPtrIsNonHeap", &__self_0),
Self::ConstMakeGlobalWithDanglingPtr(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"ConstMakeGlobalWithDanglingPtr", &__self_0),
Self::ConstMakeGlobalWithOffset(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"ConstMakeGlobalWithOffset", &__self_0),
}
}
}Debug)]
22pub enum ConstEvalErrKind {
23 ConstAccessesMutGlobal,
24 ModifiedGlobal,
25 RecursiveStatic,
26 AssertFailure(AssertKind<ConstInt>),
27 Panic {
28 msg: Symbol,
29 line: u32,
30 col: u32,
31 file: Symbol,
32 },
33 Abort,
34 WriteThroughImmutablePointer,
35/// Called `const_make_global` twice.
36ConstMakeGlobalPtrAlreadyMadeGlobal(AllocId),
37/// Called `const_make_global` on a non-heap pointer.
38ConstMakeGlobalPtrIsNonHeap(Pointer<Option<CtfeProvenance>>),
39/// Called `const_make_global` on a dangling pointer.
40ConstMakeGlobalWithDanglingPtr(Pointer<Option<CtfeProvenance>>),
41/// Called `const_make_global` on a pointer that does not start at the
42 /// beginning of an object.
43ConstMakeGlobalWithOffset(Pointer<Option<CtfeProvenance>>),
44}
4546impl fmt::Displayfor ConstEvalErrKind {
47fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48use ConstEvalErrKind::*;
49match self {
50ConstAccessesMutGlobal => f.write_fmt(format_args!("constant accesses mutable global memory"))write!(f, "constant accesses mutable global memory"),
51ModifiedGlobal => {
52f.write_fmt(format_args!("modifying a static\'s initial value from another static\'s initializer"))write!(f, "modifying a static's initial value from another static's initializer")53 }
54Panic { msg, .. } => f.write_fmt(format_args!("evaluation panicked: {0}", msg))write!(f, "evaluation panicked: {msg}"),
55Abort => f.write_fmt(format_args!("the program aborted execution"))write!(f, "the program aborted execution"),
56RecursiveStatic => {
57f.write_fmt(format_args!("encountered static that tried to access itself during initialization"))write!(f, "encountered static that tried to access itself during initialization")58 }
59AssertFailure(x) => f.write_fmt(format_args!("{0}", x))write!(f, "{x}"),
60WriteThroughImmutablePointer => {
61f.write_fmt(format_args!("writing through a pointer that was derived from a shared (immutable) reference"))write!(
62f,
63"writing through a pointer that was derived from a shared (immutable) reference"
64)65 }
66ConstMakeGlobalPtrAlreadyMadeGlobal(alloc) => {
67f.write_fmt(format_args!("attempting to call `const_make_global` twice on the same allocation {0}",
alloc))write!(
68f,
69"attempting to call `const_make_global` twice on the same allocation {alloc}"
70)71 }
72ConstMakeGlobalPtrIsNonHeap(ptr) => {
73f.write_fmt(format_args!("pointer passed to `const_make_global` does not point to a heap allocation: {0}",
ptr))write!(
74f,
75"pointer passed to `const_make_global` does not point to a heap allocation: {ptr}"
76)77 }
78ConstMakeGlobalWithDanglingPtr(ptr) => {
79f.write_fmt(format_args!("pointer passed to `const_make_global` is dangling: {0}",
ptr))write!(f, "pointer passed to `const_make_global` is dangling: {ptr}")80 }
81ConstMakeGlobalWithOffset(ptr) => {
82f.write_fmt(format_args!("making {0} global which does not point to the beginning of an object",
ptr))write!(f, "making {ptr} global which does not point to the beginning of an object")83 }
84 }
85 }
86}
8788impl MachineStopTypefor ConstEvalErrKind {}
8990/// The errors become [`InterpErrorKind::MachineStop`] when being raised.
91impl<'tcx> Into<InterpErrorInfo<'tcx>> for ConstEvalErrKind {
92fn into(self) -> InterpErrorInfo<'tcx> {
93::rustc_middle::mir::interpret::InterpErrorKind::MachineStop(Box::new(self))err_machine_stop!(self).into()
94 }
95}
9697pub(crate) fn get_span_and_frames<'tcx>(
98 tcx: TyCtxtAt<'tcx>,
99 stack: &[Frame<'tcx, impl Provenance, impl Sized>],
100) -> (Span, Vec<diagnostics::FrameNote>) {
101let mut stacktrace = Frame::generate_stacktrace_from_stack(stack, *tcx);
102// Filter out `requires_caller_location` frames.
103stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*tcx));
104let span = stacktrace.last().map(|f| f.span).unwrap_or(tcx.span);
105106let mut frames = Vec::new();
107108// Add notes to the backtrace. Don't print a single-line backtrace though.
109if stacktrace.len() > 1 {
110// Helper closure to print duplicated lines.
111let mut add_frame = |mut frame: diagnostics::FrameNote| {
112frames.push(diagnostics::FrameNote { times: 0, ..frame.clone() });
113// Don't print [... additional calls ...] if the number of lines is small
114if frame.times < 3 {
115let times = frame.times;
116frame.times = 0;
117frames.extend(std::iter::repeat_n(frame, timesas usize));
118 } else {
119frames.push(frame);
120 }
121 };
122123let mut last_frame: Option<diagnostics::FrameNote> = None;
124for frame_info in &stacktrace {
125let frame = frame_info.as_note(*tcx);
126match last_frame.as_mut() {
127Some(last_frame)
128if last_frame.span == frame.span
129 && last_frame.where_ == frame.where_
130 && last_frame.instance == frame.instance =>
131 {
132 last_frame.times += 1;
133 }
134Some(last_frame) => {
135 add_frame(mem::replace(last_frame, frame));
136 }
137None => {
138 last_frame = Some(frame);
139 }
140 }
141 }
142if let Some(frame) = last_frame {
143add_frame(frame);
144 }
145 }
146147// In `rustc`, we present const-eval errors from the outer-most place first to the inner-most.
148 // So we reverse the frames here. The first frame will be the same as the span from the current
149 // `TyCtxtAt<'_>`, so we remove it as it would be redundant.
150frames.reverse();
151if frames.len() > 0 {
152frames.remove(0);
153 }
154if let Some(last) = frames.last_mut()
155// If the span is not going to be printed, we don't want the span label for `is_last`.
156&& tcx.sess.source_map().span_to_snippet(last.span.source_callsite()).is_ok()
157 {
158last.has_label = true;
159 }
160161 (span, frames)
162}
163164/// Create a diagnostic for a const eval error.
165///
166/// This will use the `mk` function for adding more information to the error.
167/// You can use it to add a stacktrace of current execution according to
168/// `get_span_and_frames` or just give context on where the const eval error happened.
169pub(super) fn report<'tcx>(
170 ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>,
171 error: InterpErrorKind<'tcx>,
172 mk: impl FnOnce(&mut Diag<'_>, Span, Vec<FrameNote>),
173) -> ErrorHandled {
174let tcx = ecx.tcx.tcx;
175// Special handling for certain errors
176match error {
177// Don't emit a new diagnostic for these errors, they are already reported elsewhere or
178 // should remain silent.
179::rustc_middle::mir::interpret::InterpErrorKind::InvalidProgram(::rustc_middle::mir::interpret::InvalidProgramInfo::AlreadyReported(info))err_inval!(AlreadyReported(info)) => ErrorHandled::Reported(info, DUMMY_SP),
180::rustc_middle::mir::interpret::InterpErrorKind::InvalidProgram(::rustc_middle::mir::interpret::InvalidProgramInfo::Layout(LayoutError::TooGeneric(_)))err_inval!(Layout(LayoutError::TooGeneric(_))) | ::rustc_middle::mir::interpret::InterpErrorKind::InvalidProgram(::rustc_middle::mir::interpret::InvalidProgramInfo::TooGeneric)err_inval!(TooGeneric) => {
181 ErrorHandled::TooGeneric(DUMMY_SP)
182 }
183::rustc_middle::mir::interpret::InterpErrorKind::InvalidProgram(::rustc_middle::mir::interpret::InvalidProgramInfo::Layout(LayoutError::ReferencesError(guar)))err_inval!(Layout(LayoutError::ReferencesError(guar))) => {
184// This can occur in infallible promoteds e.g. when a non-existent type or field is
185 // encountered.
186ErrorHandled::Reported(ReportedErrorInfo::allowed_in_infallible(guar), DUMMY_SP)
187 }
188// Report remaining errors.
189_ => {
190let (span, frames) = super::get_span_and_frames(ecx.tcx, ecx.stack());
191let mut err = tcx.dcx().struct_span_err(span, error.to_string());
192err.code(E0080);
193if #[allow(non_exhaustive_omitted_patterns)] match error {
InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ValidationError {
ptr_bytes_warning: true, .. }) |
InterpErrorKind::Unsupported(UnsupportedOpInfo::ReadPointerAsInt(..) |
UnsupportedOpInfo::ReadPartialPointer(..)) => true,
_ => false,
}matches!(
194 error,
195 InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ValidationError {
196 ptr_bytes_warning: true,
197 ..
198 }) | InterpErrorKind::Unsupported(
199 UnsupportedOpInfo::ReadPointerAsInt(..)
200 | UnsupportedOpInfo::ReadPartialPointer(..)
201 )
202 ) {
203err.help("this code performed an operation that depends on the underlying bytes representing a pointer");
204err.help("the absolute address of a pointer is not known at compile-time, so such operations are not supported");
205 }
206if let InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(
207Some((alloc_id, _access)),
208 )) = error209 {
210let bytes = ecx.print_alloc_bytes_for_diagnostics(alloc_id);
211let info = ecx.get_alloc_info(alloc_id);
212let raw_bytes = diagnostics::RawBytesNote {
213 size: info.size.bytes(),
214 align: info.align.bytes(),
215bytes,
216 };
217err.subdiagnostic(raw_bytes);
218 }
219220// We allow invalid programs in infallible promoteds since invalid layouts can occur
221 // anyway (e.g. due to size overflow). And we allow OOM as that can happen any time.
222let allowed_in_infallible = #[allow(non_exhaustive_omitted_patterns)] match error {
InterpErrorKind::ResourceExhaustion(_) |
InterpErrorKind::InvalidProgram(_) => true,
_ => false,
}matches!(
223 error,
224 InterpErrorKind::ResourceExhaustion(_) | InterpErrorKind::InvalidProgram(_)
225 );
226227mk(&mut err, span, frames);
228let guar = err.emit_err();
229let reported = if allowed_in_infallible {
230ReportedErrorInfo::allowed_in_infallible(guar)
231 } else {
232ReportedErrorInfo::const_eval_error(guar)
233 };
234 ErrorHandled::Reported(reported, span)
235 }
236 }
237}
238239/// Emit a lint from a const-eval situation, with a backtrace.
240// 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!
241#[allow(unused)]
242pub(super) fn lint<'tcx, L>(
243 tcx: TyCtxtAt<'tcx>,
244 machine: &CompileTimeMachine<'tcx>,
245 lint: &'static rustc_lint_defs::Lint,
246 decorator: impl FnOnce(Vec<diagnostics::FrameNote>) -> L,
247) where
248L: for<'a> rustc_errors::Diagnostic<'a>,
249{
250let (span, frames) = get_span_and_frames(tcx, &machine.stack);
251252tcx.emit_node_span_lint(lint, machine.best_lint_scope(*tcx), span, decorator(frames));
253}