rustc_const_eval/const_eval/
error.rs
1use std::mem;
2
3use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, Diagnostic, IntoDiagArg};
4use rustc_middle::mir::AssertKind;
5use rustc_middle::mir::interpret::{Provenance, ReportedErrorInfo};
6use rustc_middle::query::TyCtxtAt;
7use rustc_middle::ty::layout::LayoutError;
8use rustc_middle::ty::{ConstInt, TyCtxt};
9use rustc_span::{Span, Symbol};
10
11use super::CompileTimeMachine;
12use crate::errors::{self, FrameNote, ReportErrorExt};
13use crate::interpret::{
14 ErrorHandled, Frame, InterpErrorInfo, InterpErrorKind, MachineStopType, err_inval,
15 err_machine_stop,
16};
17
18#[derive(Clone, Debug)]
20pub enum ConstEvalErrKind {
21 ConstAccessesMutGlobal,
22 ModifiedGlobal,
23 RecursiveStatic,
24 AssertFailure(AssertKind<ConstInt>),
25 Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
26 WriteThroughImmutablePointer,
27}
28
29impl MachineStopType for ConstEvalErrKind {
30 fn diagnostic_message(&self) -> DiagMessage {
31 use ConstEvalErrKind::*;
32
33 use crate::fluent_generated::*;
34 match self {
35 ConstAccessesMutGlobal => const_eval_const_accesses_mut_global,
36 ModifiedGlobal => const_eval_modified_global,
37 Panic { .. } => const_eval_panic,
38 RecursiveStatic => const_eval_recursive_static,
39 AssertFailure(x) => x.diagnostic_message(),
40 WriteThroughImmutablePointer => const_eval_write_through_immutable_pointer,
41 }
42 }
43 fn add_args(self: Box<Self>, adder: &mut dyn FnMut(DiagArgName, DiagArgValue)) {
44 use ConstEvalErrKind::*;
45 match *self {
46 RecursiveStatic
47 | ConstAccessesMutGlobal
48 | ModifiedGlobal
49 | WriteThroughImmutablePointer => {}
50 AssertFailure(kind) => kind.add_args(adder),
51 Panic { msg, .. } => {
52 adder("msg".into(), msg.into_diag_arg(&mut None));
53 }
54 }
55 }
56}
57
58impl<'tcx> Into<InterpErrorInfo<'tcx>> for ConstEvalErrKind {
60 fn into(self) -> InterpErrorInfo<'tcx> {
61 err_machine_stop!(self).into()
62 }
63}
64
65pub fn get_span_and_frames<'tcx>(
66 tcx: TyCtxtAt<'tcx>,
67 stack: &[Frame<'tcx, impl Provenance, impl Sized>],
68) -> (Span, Vec<errors::FrameNote>) {
69 let mut stacktrace = Frame::generate_stacktrace_from_stack(stack);
70 stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*tcx));
72 let span = stacktrace.last().map(|f| f.span).unwrap_or(tcx.span);
73
74 let mut frames = Vec::new();
75
76 if stacktrace.len() > 1 {
78 let mut add_frame = |mut frame: errors::FrameNote| {
80 frames.push(errors::FrameNote { times: 0, ..frame.clone() });
81 if frame.times < 3 {
83 let times = frame.times;
84 frame.times = 0;
85 frames.extend(std::iter::repeat(frame).take(times as usize));
86 } else {
87 frames.push(frame);
88 }
89 };
90
91 let mut last_frame: Option<errors::FrameNote> = None;
92 for frame_info in &stacktrace {
93 let frame = frame_info.as_note(*tcx);
94 match last_frame.as_mut() {
95 Some(last_frame)
96 if last_frame.span == frame.span
97 && last_frame.where_ == frame.where_
98 && last_frame.instance == frame.instance =>
99 {
100 last_frame.times += 1;
101 }
102 Some(last_frame) => {
103 add_frame(mem::replace(last_frame, frame));
104 }
105 None => {
106 last_frame = Some(frame);
107 }
108 }
109 }
110 if let Some(frame) = last_frame {
111 add_frame(frame);
112 }
113 }
114
115 frames.reverse();
119 if frames.len() > 0 {
120 frames.remove(0);
121 }
122 if let Some(last) = frames.last_mut()
123 && tcx.sess.source_map().span_to_snippet(last.span.source_callsite()).is_ok()
125 {
126 last.has_label = true;
127 }
128
129 (span, frames)
130}
131
132pub(super) fn report<'tcx, C, F, E>(
138 tcx: TyCtxt<'tcx>,
139 error: InterpErrorKind<'tcx>,
140 span: Span,
141 get_span_and_frames: C,
142 mk: F,
143) -> ErrorHandled
144where
145 C: FnOnce() -> (Span, Vec<FrameNote>),
146 F: FnOnce(Span, Vec<FrameNote>) -> E,
147 E: Diagnostic<'tcx>,
148{
149 match error {
151 err_inval!(AlreadyReported(info)) => ErrorHandled::Reported(info, span),
154 err_inval!(Layout(LayoutError::TooGeneric(_))) | err_inval!(TooGeneric) => {
155 ErrorHandled::TooGeneric(span)
156 }
157 err_inval!(Layout(LayoutError::ReferencesError(guar))) => {
158 ErrorHandled::Reported(ReportedErrorInfo::allowed_in_infallible(guar), span)
161 }
162 _ => {
164 let (our_span, frames) = get_span_and_frames();
165 let span = span.substitute_dummy(our_span);
166 let err = mk(span, frames);
167 let mut err = tcx.dcx().create_err(err);
168 let allowed_in_infallible = matches!(
171 error,
172 InterpErrorKind::ResourceExhaustion(_) | InterpErrorKind::InvalidProgram(_)
173 );
174
175 let msg = error.diagnostic_message();
176 error.add_args(&mut err);
177
178 err.span_label(our_span, msg);
180 let g = err.emit();
181 let reported = if allowed_in_infallible {
182 ReportedErrorInfo::allowed_in_infallible(g)
183 } else {
184 ReportedErrorInfo::const_eval_error(g)
185 };
186 ErrorHandled::Reported(reported, span)
187 }
188 }
189}
190
191#[allow(unused)]
194pub(super) fn lint<'tcx, L>(
195 tcx: TyCtxtAt<'tcx>,
196 machine: &CompileTimeMachine<'tcx>,
197 lint: &'static rustc_session::lint::Lint,
198 decorator: impl FnOnce(Vec<errors::FrameNote>) -> L,
199) where
200 L: for<'a> rustc_errors::LintDiagnostic<'a, ()>,
201{
202 let (span, frames) = get_span_and_frames(tcx, &machine.stack);
203
204 tcx.emit_node_span_lint(lint, machine.best_lint_scope(*tcx), span, decorator(frames));
205}