rustc_const_eval/const_eval/
error.rs1use std::mem;
2
3use rustc_errors::{Diag, DiagArgName, DiagArgValue, DiagMessage, IntoDiagArg};
4use rustc_middle::mir::AssertKind;
5use rustc_middle::mir::interpret::{AllocId, Provenance, ReportedErrorInfo, UndefinedBehaviorInfo};
6use rustc_middle::query::TyCtxtAt;
7use rustc_middle::ty::ConstInt;
8use rustc_middle::ty::layout::LayoutError;
9use rustc_span::{Span, Symbol};
10
11use super::CompileTimeMachine;
12use crate::errors::{self, FrameNote, ReportErrorExt};
13use crate::interpret::{
14 CtfeProvenance, ErrorHandled, Frame, InterpCx, InterpErrorInfo, InterpErrorKind,
15 MachineStopType, Pointer, err_inval, err_machine_stop,
16};
17
18#[derive(Clone, Debug)]
20pub enum ConstEvalErrKind {
21 ConstAccessesMutGlobal,
22 ModifiedGlobal,
23 RecursiveStatic,
24 AssertFailure(AssertKind<ConstInt>),
25 Panic {
26 msg: Symbol,
27 line: u32,
28 col: u32,
29 file: Symbol,
30 },
31 WriteThroughImmutablePointer,
32 ConstMakeGlobalPtrAlreadyMadeGlobal(AllocId),
34 ConstMakeGlobalPtrIsNonHeap(Pointer<Option<CtfeProvenance>>),
36 ConstMakeGlobalWithDanglingPtr(Pointer<Option<CtfeProvenance>>),
38 ConstMakeGlobalWithOffset(Pointer<Option<CtfeProvenance>>),
41}
42
43impl MachineStopType for ConstEvalErrKind {
44 fn diagnostic_message(&self) -> DiagMessage {
45 use ConstEvalErrKind::*;
46
47 use crate::fluent_generated::*;
48 match self {
49 ConstAccessesMutGlobal => const_eval_const_accesses_mut_global,
50 ModifiedGlobal => const_eval_modified_global,
51 Panic { .. } => const_eval_panic,
52 RecursiveStatic => const_eval_recursive_static,
53 AssertFailure(x) => x.diagnostic_message(),
54 WriteThroughImmutablePointer => const_eval_write_through_immutable_pointer,
55 ConstMakeGlobalPtrAlreadyMadeGlobal { .. } => {
56 const_eval_const_make_global_ptr_already_made_global
57 }
58 ConstMakeGlobalPtrIsNonHeap(_) => const_eval_const_make_global_ptr_is_non_heap,
59 ConstMakeGlobalWithDanglingPtr(_) => const_eval_const_make_global_with_dangling_ptr,
60 ConstMakeGlobalWithOffset(_) => const_eval_const_make_global_with_offset,
61 }
62 }
63 fn add_args(self: Box<Self>, adder: &mut dyn FnMut(DiagArgName, DiagArgValue)) {
64 use ConstEvalErrKind::*;
65 match *self {
66 RecursiveStatic
67 | ConstAccessesMutGlobal
68 | ModifiedGlobal
69 | WriteThroughImmutablePointer => {}
70 AssertFailure(kind) => kind.add_args(adder),
71 Panic { msg, .. } => {
72 adder("msg".into(), msg.into_diag_arg(&mut None));
73 }
74 ConstMakeGlobalPtrIsNonHeap(ptr)
75 | ConstMakeGlobalWithOffset(ptr)
76 | ConstMakeGlobalWithDanglingPtr(ptr) => {
77 adder("ptr".into(), format!("{ptr:?}").into_diag_arg(&mut None));
78 }
79 ConstMakeGlobalPtrAlreadyMadeGlobal(alloc) => {
80 adder("alloc".into(), alloc.into_diag_arg(&mut None));
81 }
82 }
83 }
84}
85
86impl<'tcx> Into<InterpErrorInfo<'tcx>> for ConstEvalErrKind {
88 fn into(self) -> InterpErrorInfo<'tcx> {
89 err_machine_stop!(self).into()
90 }
91}
92
93pub fn get_span_and_frames<'tcx>(
94 tcx: TyCtxtAt<'tcx>,
95 stack: &[Frame<'tcx, impl Provenance, impl Sized>],
96) -> (Span, Vec<errors::FrameNote>) {
97 let mut stacktrace = Frame::generate_stacktrace_from_stack(stack);
98 stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*tcx));
100 let span = stacktrace.last().map(|f| f.span).unwrap_or(tcx.span);
101
102 let mut frames = Vec::new();
103
104 if stacktrace.len() > 1 {
106 let mut add_frame = |mut frame: errors::FrameNote| {
108 frames.push(errors::FrameNote { times: 0, ..frame.clone() });
109 if frame.times < 3 {
111 let times = frame.times;
112 frame.times = 0;
113 frames.extend(std::iter::repeat(frame).take(times as usize));
114 } else {
115 frames.push(frame);
116 }
117 };
118
119 let mut last_frame: Option<errors::FrameNote> = None;
120 for frame_info in &stacktrace {
121 let frame = frame_info.as_note(*tcx);
122 match last_frame.as_mut() {
123 Some(last_frame)
124 if last_frame.span == frame.span
125 && last_frame.where_ == frame.where_
126 && last_frame.instance == frame.instance =>
127 {
128 last_frame.times += 1;
129 }
130 Some(last_frame) => {
131 add_frame(mem::replace(last_frame, frame));
132 }
133 None => {
134 last_frame = Some(frame);
135 }
136 }
137 }
138 if let Some(frame) = last_frame {
139 add_frame(frame);
140 }
141 }
142
143 frames.reverse();
147 if frames.len() > 0 {
148 frames.remove(0);
149 }
150 if let Some(last) = frames.last_mut()
151 && tcx.sess.source_map().span_to_snippet(last.span.source_callsite()).is_ok()
153 {
154 last.has_label = true;
155 }
156
157 (span, frames)
158}
159
160pub(super) fn report<'tcx, C, F>(
166 ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>,
167 error: InterpErrorKind<'tcx>,
168 span: Span,
169 get_span_and_frames: C,
170 mk: F,
171) -> ErrorHandled
172where
173 C: FnOnce() -> (Span, Vec<FrameNote>),
174 F: FnOnce(&mut Diag<'_>, Span, Vec<FrameNote>),
175{
176 let tcx = ecx.tcx.tcx;
177 match error {
179 err_inval!(AlreadyReported(info)) => ErrorHandled::Reported(info, span),
182 err_inval!(Layout(LayoutError::TooGeneric(_))) | err_inval!(TooGeneric) => {
183 ErrorHandled::TooGeneric(span)
184 }
185 err_inval!(Layout(LayoutError::ReferencesError(guar))) => {
186 ErrorHandled::Reported(ReportedErrorInfo::allowed_in_infallible(guar), span)
189 }
190 _ => {
192 let (our_span, frames) = get_span_and_frames();
193 let span = span.substitute_dummy(our_span);
194 let mut err = tcx.dcx().struct_span_err(our_span, error.diagnostic_message());
195 let allowed_in_infallible = matches!(
198 error,
199 InterpErrorKind::ResourceExhaustion(_) | InterpErrorKind::InvalidProgram(_)
200 );
201
202 if let InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(
203 Some((alloc_id, _access)),
204 )) = error
205 {
206 let bytes = ecx.print_alloc_bytes_for_diagnostics(alloc_id);
207 let info = ecx.get_alloc_info(alloc_id);
208 let raw_bytes = errors::RawBytesNote {
209 size: info.size.bytes(),
210 align: info.align.bytes(),
211 bytes,
212 };
213 err.subdiagnostic(raw_bytes);
214 }
215
216 error.add_args(&mut err);
217
218 mk(&mut err, span, frames);
219 let g = err.emit();
220 let reported = if allowed_in_infallible {
221 ReportedErrorInfo::allowed_in_infallible(g)
222 } else {
223 ReportedErrorInfo::const_eval_error(g)
224 };
225 ErrorHandled::Reported(reported, span)
226 }
227 }
228}
229
230#[allow(unused)]
233pub(super) fn lint<'tcx, L>(
234 tcx: TyCtxtAt<'tcx>,
235 machine: &CompileTimeMachine<'tcx>,
236 lint: &'static rustc_session::lint::Lint,
237 decorator: impl FnOnce(Vec<errors::FrameNote>) -> L,
238) where
239 L: for<'a> rustc_errors::LintDiagnostic<'a, ()>,
240{
241 let (span, frames) = get_span_and_frames(tcx, &machine.stack);
242
243 tcx.emit_node_span_lint(lint, machine.best_lint_scope(*tcx), span, decorator(frames));
244}