1use std::{fmt, mem};
23use rustc_errors::Diag;
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::{Span, Symbol};
1213use super::CompileTimeMachine;
14use crate::errors::{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) -> ConstEvalErrKind {
match self {
ConstEvalErrKind::ConstAccessesMutGlobal =>
ConstEvalErrKind::ConstAccessesMutGlobal,
ConstEvalErrKind::ModifiedGlobal =>
ConstEvalErrKind::ModifiedGlobal,
ConstEvalErrKind::RecursiveStatic =>
ConstEvalErrKind::RecursiveStatic,
ConstEvalErrKind::AssertFailure(__self_0) =>
ConstEvalErrKind::AssertFailure(::core::clone::Clone::clone(__self_0)),
ConstEvalErrKind::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),
},
ConstEvalErrKind::WriteThroughImmutablePointer =>
ConstEvalErrKind::WriteThroughImmutablePointer,
ConstEvalErrKind::ConstMakeGlobalPtrAlreadyMadeGlobal(__self_0) =>
ConstEvalErrKind::ConstMakeGlobalPtrAlreadyMadeGlobal(::core::clone::Clone::clone(__self_0)),
ConstEvalErrKind::ConstMakeGlobalPtrIsNonHeap(__self_0) =>
ConstEvalErrKind::ConstMakeGlobalPtrIsNonHeap(::core::clone::Clone::clone(__self_0)),
ConstEvalErrKind::ConstMakeGlobalWithDanglingPtr(__self_0) =>
ConstEvalErrKind::ConstMakeGlobalWithDanglingPtr(::core::clone::Clone::clone(__self_0)),
ConstEvalErrKind::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 {
ConstEvalErrKind::ConstAccessesMutGlobal =>
::core::fmt::Formatter::write_str(f,
"ConstAccessesMutGlobal"),
ConstEvalErrKind::ModifiedGlobal =>
::core::fmt::Formatter::write_str(f, "ModifiedGlobal"),
ConstEvalErrKind::RecursiveStatic =>
::core::fmt::Formatter::write_str(f, "RecursiveStatic"),
ConstEvalErrKind::AssertFailure(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"AssertFailure", &__self_0),
ConstEvalErrKind::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),
ConstEvalErrKind::WriteThroughImmutablePointer =>
::core::fmt::Formatter::write_str(f,
"WriteThroughImmutablePointer"),
ConstEvalErrKind::ConstMakeGlobalPtrAlreadyMadeGlobal(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"ConstMakeGlobalPtrAlreadyMadeGlobal", &__self_0),
ConstEvalErrKind::ConstMakeGlobalPtrIsNonHeap(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"ConstMakeGlobalPtrIsNonHeap", &__self_0),
ConstEvalErrKind::ConstMakeGlobalWithDanglingPtr(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"ConstMakeGlobalWithDanglingPtr", &__self_0),
ConstEvalErrKind::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 WriteThroughImmutablePointer,
34/// Called `const_make_global` twice.
35ConstMakeGlobalPtrAlreadyMadeGlobal(AllocId),
36/// Called `const_make_global` on a non-heap pointer.
37ConstMakeGlobalPtrIsNonHeap(Pointer<Option<CtfeProvenance>>),
38/// Called `const_make_global` on a dangling pointer.
39ConstMakeGlobalWithDanglingPtr(Pointer<Option<CtfeProvenance>>),
40/// Called `const_make_global` on a pointer that does not start at the
41 /// beginning of an object.
42ConstMakeGlobalWithOffset(Pointer<Option<CtfeProvenance>>),
43}
4445impl fmt::Displayfor ConstEvalErrKind {
46fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47use ConstEvalErrKind::*;
48match self {
49ConstAccessesMutGlobal => f.write_fmt(format_args!("constant accesses mutable global memory"))write!(f, "constant accesses mutable global memory"),
50ModifiedGlobal => {
51f.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")52 }
53Panic { msg, .. } => f.write_fmt(format_args!("evaluation panicked: {0}", msg))write!(f, "evaluation panicked: {msg}"),
54RecursiveStatic => {
55f.write_fmt(format_args!("encountered static that tried to access itself during initialization"))write!(f, "encountered static that tried to access itself during initialization")56 }
57AssertFailure(x) => f.write_fmt(format_args!("{0}", x))write!(f, "{x}"),
58WriteThroughImmutablePointer => {
59f.write_fmt(format_args!("writing through a pointer that was derived from a shared (immutable) reference"))write!(
60f,
61"writing through a pointer that was derived from a shared (immutable) reference"
62)63 }
64ConstMakeGlobalPtrAlreadyMadeGlobal(alloc) => {
65f.write_fmt(format_args!("attempting to call `const_make_global` twice on the same allocation {0}",
alloc))write!(
66f,
67"attempting to call `const_make_global` twice on the same allocation {alloc}"
68)69 }
70ConstMakeGlobalPtrIsNonHeap(ptr) => {
71f.write_fmt(format_args!("pointer passed to `const_make_global` does not point to a heap allocation: {0}",
ptr))write!(
72f,
73"pointer passed to `const_make_global` does not point to a heap allocation: {ptr}"
74)75 }
76ConstMakeGlobalWithDanglingPtr(ptr) => {
77f.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}")78 }
79ConstMakeGlobalWithOffset(ptr) => {
80f.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")81 }
82 }
83 }
84}
8586impl MachineStopTypefor ConstEvalErrKind {}
8788/// The errors become [`InterpErrorKind::MachineStop`] when being raised.
89impl<'tcx> Into<InterpErrorInfo<'tcx>> for ConstEvalErrKind {
90fn into(self) -> InterpErrorInfo<'tcx> {
91::rustc_middle::mir::interpret::InterpErrorKind::MachineStop(Box::new(self))err_machine_stop!(self).into()
92 }
93}
9495pub fn get_span_and_frames<'tcx>(
96 tcx: TyCtxtAt<'tcx>,
97 stack: &[Frame<'tcx, impl Provenance, impl Sized>],
98) -> (Span, Vec<errors::FrameNote>) {
99let mut stacktrace = Frame::generate_stacktrace_from_stack(stack, *tcx);
100// Filter out `requires_caller_location` frames.
101stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*tcx));
102let span = stacktrace.last().map(|f| f.span).unwrap_or(tcx.span);
103104let mut frames = Vec::new();
105106// Add notes to the backtrace. Don't print a single-line backtrace though.
107if stacktrace.len() > 1 {
108// Helper closure to print duplicated lines.
109let mut add_frame = |mut frame: errors::FrameNote| {
110frames.push(errors::FrameNote { times: 0, ..frame.clone() });
111// Don't print [... additional calls ...] if the number of lines is small
112if frame.times < 3 {
113let times = frame.times;
114frame.times = 0;
115frames.extend(std::iter::repeat_n(frame, timesas usize));
116 } else {
117frames.push(frame);
118 }
119 };
120121let mut last_frame: Option<errors::FrameNote> = None;
122for frame_info in &stacktrace {
123let frame = frame_info.as_note(*tcx);
124match last_frame.as_mut() {
125Some(last_frame)
126if last_frame.span == frame.span
127 && last_frame.where_ == frame.where_
128 && last_frame.instance == frame.instance =>
129 {
130 last_frame.times += 1;
131 }
132Some(last_frame) => {
133 add_frame(mem::replace(last_frame, frame));
134 }
135None => {
136 last_frame = Some(frame);
137 }
138 }
139 }
140if let Some(frame) = last_frame {
141add_frame(frame);
142 }
143 }
144145// In `rustc`, we present const-eval errors from the outer-most place first to the inner-most.
146 // So we reverse the frames here. The first frame will be the same as the span from the current
147 // `TyCtxtAt<'_>`, so we remove it as it would be redundant.
148frames.reverse();
149if frames.len() > 0 {
150frames.remove(0);
151 }
152if let Some(last) = frames.last_mut()
153// If the span is not going to be printed, we don't want the span label for `is_last`.
154&& tcx.sess.source_map().span_to_snippet(last.span.source_callsite()).is_ok()
155 {
156last.has_label = true;
157 }
158159 (span, frames)
160}
161162/// Create a diagnostic for a const eval error.
163///
164/// This will use the `mk` function for adding more information to the error.
165/// You can use it to add a stacktrace of current execution according to
166/// `get_span_and_frames` or just give context on where the const eval error happened.
167pub(super) fn report<'tcx, C, F>(
168 ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>,
169 error: InterpErrorKind<'tcx>,
170 span: Span,
171 get_span_and_frames: C,
172 mk: F,
173) -> ErrorHandled174where
175C: FnOnce() -> (Span, Vec<FrameNote>),
176 F: FnOnce(&mut Diag<'_>, Span, Vec<FrameNote>),
177{
178let tcx = ecx.tcx.tcx;
179// Special handling for certain errors
180match error {
181// Don't emit a new diagnostic for these errors, they are already reported elsewhere or
182 // should remain silent.
183::rustc_middle::mir::interpret::InterpErrorKind::InvalidProgram(::rustc_middle::mir::interpret::InvalidProgramInfo::AlreadyReported(info))err_inval!(AlreadyReported(info)) => ErrorHandled::Reported(info, span),
184::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) => {
185 ErrorHandled::TooGeneric(span)
186 }
187::rustc_middle::mir::interpret::InterpErrorKind::InvalidProgram(::rustc_middle::mir::interpret::InvalidProgramInfo::Layout(LayoutError::ReferencesError(guar)))err_inval!(Layout(LayoutError::ReferencesError(guar))) => {
188// This can occur in infallible promoteds e.g. when a non-existent type or field is
189 // encountered.
190ErrorHandled::Reported(ReportedErrorInfo::allowed_in_infallible(guar), span)
191 }
192// Report remaining errors.
193_ => {
194let (our_span, frames) = get_span_and_frames();
195let span = span.substitute_dummy(our_span);
196let mut err = tcx.dcx().struct_span_err(our_span, error.to_string());
197if #[allow(non_exhaustive_omitted_patterns)] match error {
InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ValidationError {
ptr_bytes_warning: true, .. }) |
InterpErrorKind::Unsupported(UnsupportedOpInfo::ReadPointerAsInt(..) |
UnsupportedOpInfo::ReadPartialPointer(..)) => true,
_ => false,
}matches!(
198 error,
199 InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ValidationError {
200 ptr_bytes_warning: true,
201 ..
202 }) | InterpErrorKind::Unsupported(
203 UnsupportedOpInfo::ReadPointerAsInt(..)
204 | UnsupportedOpInfo::ReadPartialPointer(..)
205 )
206 ) {
207err.help("this code performed an operation that depends on the underlying bytes representing a pointer");
208err.help("the absolute address of a pointer is not known at compile-time, so such operations are not supported");
209 }
210if let InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(
211Some((alloc_id, _access)),
212 )) = error213 {
214let bytes = ecx.print_alloc_bytes_for_diagnostics(alloc_id);
215let info = ecx.get_alloc_info(alloc_id);
216let raw_bytes = errors::RawBytesNote {
217 size: info.size.bytes(),
218 align: info.align.bytes(),
219bytes,
220 };
221err.subdiagnostic(raw_bytes);
222 }
223224// We allow invalid programs in infallible promoteds since invalid layouts can occur
225 // anyway (e.g. due to size overflow). And we allow OOM as that can happen any time.
226let allowed_in_infallible = #[allow(non_exhaustive_omitted_patterns)] match error {
InterpErrorKind::ResourceExhaustion(_) |
InterpErrorKind::InvalidProgram(_) => true,
_ => false,
}matches!(
227 error,
228 InterpErrorKind::ResourceExhaustion(_) | InterpErrorKind::InvalidProgram(_)
229 );
230231mk(&mut err, span, frames);
232let g = err.emit();
233let reported = if allowed_in_infallible {
234ReportedErrorInfo::allowed_in_infallible(g)
235 } else {
236ReportedErrorInfo::const_eval_error(g)
237 };
238 ErrorHandled::Reported(reported, span)
239 }
240 }
241}
242243/// Emit a lint from a const-eval situation, with a backtrace.
244// 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!
245#[allow(unused)]
246pub(super) fn lint<'tcx, L>(
247 tcx: TyCtxtAt<'tcx>,
248 machine: &CompileTimeMachine<'tcx>,
249 lint: &'static rustc_session::lint::Lint,
250 decorator: impl FnOnce(Vec<errors::FrameNote>) -> L,
251) where
252L: for<'a> rustc_errors::Diagnostic<'a, ()>,
253{
254let (span, frames) = get_span_and_frames(tcx, &machine.stack);
255256tcx.emit_node_span_lint(lint, machine.best_lint_scope(*tcx), span, decorator(frames));
257}