Skip to main content

rustc_const_eval/const_eval/
error.rs

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