Skip to main content

rustc_const_eval/const_eval/
error.rs

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