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/// The CTFE machine has some custom error kinds.
19#[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
58/// The errors become [`InterpErrorKind::MachineStop`] when being raised.
59impl<'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    // Filter out `requires_caller_location` frames.
71    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    // Add notes to the backtrace. Don't print a single-line backtrace though.
77    if stacktrace.len() > 1 {
78        // Helper closure to print duplicated lines.
79        let mut add_frame = |mut frame: errors::FrameNote| {
80            frames.push(errors::FrameNote { times: 0, ..frame.clone() });
81            // Don't print [... additional calls ...] if the number of lines is small
82            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    // In `rustc`, we present const-eval errors from the outer-most place first to the inner-most.
116    // So we reverse the frames here. The first frame will be the same as the span from the current
117    // `TyCtxtAt<'_>`, so we remove it as it would be redundant.
118    frames.reverse();
119    if frames.len() > 0 {
120        frames.remove(0);
121    }
122    if let Some(last) = frames.last_mut()
123        // If the span is not going to be printed, we don't want the span label for `is_last`.
124        && 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
132/// Create a diagnostic for a const eval error.
133///
134/// This will use the `mk` function for creating the error which will get passed labels according to
135/// the `InterpError` and the span and a stacktrace of current execution according to
136/// `get_span_and_frames`.
137pub(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    // Special handling for certain errors
150    match error {
151        // Don't emit a new diagnostic for these errors, they are already reported elsewhere or
152        // should remain silent.
153        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            // This can occur in infallible promoteds e.g. when a non-existent type or field is
159            // encountered.
160            ErrorHandled::Reported(ReportedErrorInfo::allowed_in_infallible(guar), span)
161        }
162        // Report remaining errors.
163        _ => {
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            // We allow invalid programs in infallible promoteds since invalid layouts can occur
169            // anyway (e.g. due to size overflow). And we allow OOM as that can happen any time.
170            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            // Use *our* span to label the interp error
179            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/// Emit a lint from a const-eval situation, with a backtrace.
192// 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!
193#[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}