Skip to main content

rustc_const_eval/const_eval/
error.rs

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