Skip to main content

rustc_const_eval/check_consts/
ops.rs

1//! Concrete error types for all operations which may be invalid in a certain const context.
2
3use hir::ConstContext;
4use rustc_errors::codes::*;
5use rustc_errors::{Applicability, Diag, MultiSpan, msg};
6use rustc_hir as hir;
7use rustc_hir::attrs::lang_items::LangItem;
8use rustc_hir::def_id::DefId;
9use rustc_infer::infer::TyCtxtInferExt;
10use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
11use rustc_middle::mir::CallSource;
12use rustc_middle::span_bug;
13use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths};
14use rustc_middle::ty::{
15    self, AssocContainer, Closure, FnDef, FnPtr, GenericArgKind, GenericArgsRef, Param, TraitRef,
16    Ty, suggest_constraining_type_param,
17};
18use rustc_session::diagnostics::add_feature_diagnostics;
19use rustc_span::{BytePos, Pos, Span, Symbol, sym};
20use rustc_trait_selection::error_reporting::traits::call_kind::{
21    CallDesugaringKind, CallKind, call_kind,
22};
23use rustc_trait_selection::traits::SelectionContext;
24use tracing::debug;
25
26use super::ConstCx;
27use crate::diagnostics;
28
29#[derive(#[automatically_derived]
impl ::core::clone::Clone for Status {
    #[inline]
    fn clone(&self) -> Status {
        let _: ::core::clone::AssertParamIsClone<Symbol>;
        let _: ::core::clone::AssertParamIsClone<bool>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Status { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for Status {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Status::Unstable {
                gate: __self_0,
                gate_already_checked: __self_1,
                safe_to_expose_on_stable: __self_2,
                is_function_call: __self_3 } =>
                ::core::fmt::Formatter::debug_struct_field4_finish(f,
                    "Unstable", "gate", __self_0, "gate_already_checked",
                    __self_1, "safe_to_expose_on_stable", __self_2,
                    "is_function_call", &__self_3),
            Status::Forbidden =>
                ::core::fmt::Formatter::write_str(f, "Forbidden"),
        }
    }
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for Status {
    #[inline]
    fn eq(&self, other: &Status) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr &&
            match (self, other) {
                (Status::Unstable {
                    gate: __self_0,
                    gate_already_checked: __self_1,
                    safe_to_expose_on_stable: __self_2,
                    is_function_call: __self_3 }, Status::Unstable {
                    gate: __arg1_0,
                    gate_already_checked: __arg1_1,
                    safe_to_expose_on_stable: __arg1_2,
                    is_function_call: __arg1_3 }) =>
                    __self_1 == __arg1_1 && __self_2 == __arg1_2 &&
                            __self_3 == __arg1_3 && __self_0 == __arg1_0,
                _ => true,
            }
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for Status {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<Symbol>;
        let _: ::core::cmp::AssertParamIsEq<bool>;
    }
}Eq)]
30pub enum Status {
31    Unstable {
32        /// The feature that must be enabled to use this operation.
33        gate: Symbol,
34        /// Whether the feature gate was already checked (because the logic is a bit more
35        /// complicated than just checking a single gate).
36        gate_already_checked: bool,
37        /// Whether it is allowed to use this operation from stable `const fn`.
38        /// This will usually be `false`.
39        safe_to_expose_on_stable: bool,
40        /// We indicate whether this is a function call, since we can use targeted
41        /// diagnostics for "callee is not safe to expose om stable".
42        is_function_call: bool,
43    },
44    Forbidden,
45}
46
47#[derive(#[automatically_derived]
impl ::core::clone::Clone for DiagImportance {
    #[inline]
    fn clone(&self) -> DiagImportance { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for DiagImportance { }Copy)]
48pub enum DiagImportance {
49    /// An operation that must be removed for const-checking to pass.
50    Primary,
51
52    /// An operation that causes const-checking to fail, but is usually a side-effect of a `Primary` operation elsewhere.
53    Secondary,
54}
55
56/// An operation that is *not allowed* in a const context.
57pub trait NonConstOp<'tcx>: std::fmt::Debug {
58    /// Returns an enum indicating whether this operation can be enabled with a feature gate.
59    fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
60        Status::Forbidden
61    }
62
63    fn importance(&self) -> DiagImportance {
64        DiagImportance::Primary
65    }
66
67    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx>;
68}
69
70/// A function call where the callee is a pointer.
71#[derive(#[automatically_derived]
impl ::core::fmt::Debug for FnCallIndirect {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "FnCallIndirect")
    }
}Debug)]
72pub(crate) struct FnCallIndirect;
73impl<'tcx> NonConstOp<'tcx> for FnCallIndirect {
74    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
75        ccx.dcx().create_err(diagnostics::UnallowedFnPointerCall { span, kind: ccx.const_kind() })
76    }
77}
78
79/// A c-variadic function call.
80#[derive(#[automatically_derived]
impl ::core::fmt::Debug for FnCallCVariadic {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "FnCallCVariadic")
    }
}Debug)]
81pub(crate) struct FnCallCVariadic;
82impl<'tcx> NonConstOp<'tcx> for FnCallCVariadic {
83    fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
84        Status::Unstable {
85            gate: sym::const_c_variadic,
86            gate_already_checked: false,
87            safe_to_expose_on_stable: false,
88            is_function_call: true,
89        }
90    }
91
92    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
93        ccx.tcx.sess.create_feature_err(
94            diagnostics::NonConstCVariadicCall { span, kind: ccx.const_kind() },
95            sym::const_c_variadic,
96        )
97    }
98}
99
100/// A call to a function that is in a trait, or has trait bounds that make it conditionally-const.
101#[derive(#[automatically_derived]
impl<'tcx> ::core::fmt::Debug for ConditionallyConstCall<'tcx> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field4_finish(f,
            "ConditionallyConstCall", "callee", &self.callee, "args",
            &self.args, "span", &self.span, "call_source", &&self.call_source)
    }
}Debug)]
102pub(crate) struct ConditionallyConstCall<'tcx> {
103    pub callee: DefId,
104    pub args: GenericArgsRef<'tcx>,
105    pub span: Span,
106    pub call_source: CallSource,
107}
108
109impl<'tcx> NonConstOp<'tcx> for ConditionallyConstCall<'tcx> {
110    fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
111        // We use the `const_trait_impl` gate for all conditionally-const calls.
112        Status::Unstable {
113            gate: sym::const_trait_impl,
114            gate_already_checked: false,
115            safe_to_expose_on_stable: false,
116            // We don't want the "mark the callee as `#[rustc_const_stable_indirect]`" hint
117            is_function_call: false,
118        }
119    }
120
121    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> {
122        let mut diag = build_error_for_const_call(
123            ccx,
124            self.callee,
125            self.args,
126            self.span,
127            self.call_source,
128            "conditionally",
129            |_, _, _| {},
130        );
131
132        // Override code and mention feature.
133        diag.code(E0658);
134        add_feature_diagnostics(&mut diag, ccx.tcx.sess, sym::const_trait_impl);
135
136        diag
137    }
138}
139
140/// A function call where the callee is not marked as `const`.
141#[derive(#[automatically_derived]
impl<'tcx> ::core::fmt::Debug for FnCallNonConst<'tcx> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field4_finish(f,
            "FnCallNonConst", "callee", &self.callee, "args", &self.args,
            "span", &self.span, "call_source", &&self.call_source)
    }
}Debug, #[automatically_derived]
impl<'tcx> ::core::clone::Clone for FnCallNonConst<'tcx> {
    #[inline]
    fn clone(&self) -> FnCallNonConst<'tcx> {
        let _: ::core::clone::AssertParamIsClone<DefId>;
        let _: ::core::clone::AssertParamIsClone<GenericArgsRef<'tcx>>;
        let _: ::core::clone::AssertParamIsClone<Span>;
        let _: ::core::clone::AssertParamIsClone<CallSource>;
        *self
    }
}Clone, #[automatically_derived]
impl<'tcx> ::core::marker::Copy for FnCallNonConst<'tcx> { }Copy)]
142pub(crate) struct FnCallNonConst<'tcx> {
143    pub callee: DefId,
144    pub args: GenericArgsRef<'tcx>,
145    pub span: Span,
146    pub call_source: CallSource,
147}
148
149impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
150    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> {
151        let tcx = ccx.tcx;
152        let caller = ccx.def_id();
153
154        let mut err = build_error_for_const_call(
155            ccx,
156            self.callee,
157            self.args,
158            self.span,
159            self.call_source,
160            "non",
161            |err, self_ty, trait_id| {
162                // FIXME(const_trait_impl): Do we need any of this on the non-const codepath?
163
164                let trait_ref = TraitRef::from_assoc(tcx, trait_id, self.args);
165
166                match self_ty.kind() {
167                    Param(param_ty) => {
168                        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_const_eval/src/check_consts/ops.rs:168",
                        "rustc_const_eval::check_consts::ops",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_const_eval/src/check_consts/ops.rs"),
                        ::tracing_core::__macro_support::Option::Some(168u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_const_eval::check_consts::ops"),
                        ::tracing_core::field::FieldSet::new(&[{
                                            const NAME:
                                                ::tracing::__macro_support::FieldName<{
                                                    ::tracing::__macro_support::FieldName::len("param_ty")
                                                }> =
                                                ::tracing::__macro_support::FieldName::new("param_ty");
                                            NAME.as_str()
                                        }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&param_ty)
                                            as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!(?param_ty);
169                        if let Some(generics) = tcx.hir_node_by_def_id(caller).generics() {
170                            let constraint = {
    let _guard = NoTrimmedGuard::new();
    ::alloc::__export::must_use({
            ::alloc::fmt::format(format_args!("[const] {0}",
                    trait_ref.print_trait_sugared()))
        })
}with_no_trimmed_paths!(format!(
171                                "[const] {}",
172                                trait_ref.print_trait_sugared(),
173                            ));
174                            suggest_constraining_type_param(
175                                tcx,
176                                generics,
177                                err,
178                                param_ty.name.as_str(),
179                                &constraint,
180                                Some(trait_ref.def_id),
181                                None,
182                            );
183                        }
184                    }
185                    ty::Adt(..) => {
186                        let (infcx, param_env) =
187                            tcx.infer_ctxt().build_with_typing_env(ccx.typing_env);
188                        let obligation =
189                            Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref);
190                        let mut selcx = SelectionContext::new(&infcx);
191                        let implsrc = selcx.select(&obligation);
192                        if let Ok(Some(ImplSource::UserDefined(data))) = implsrc {
193                            // FIXME(const_trait_impl) revisit this
194                            if !tcx.is_const_trait_impl(data.impl_def_id) {
195                                let span = tcx.def_span(data.impl_def_id);
196                                err.subdiagnostic(diagnostics::NonConstImplNote { span });
197                            }
198                        }
199                    }
200                    _ => {}
201                }
202            },
203        );
204
205        if let ConstContext::Static(_) = ccx.const_kind() {
206            err.note(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("consider wrapping this expression in `std::sync::LazyLock::new(|| ...)`"))msg!(
207                "consider wrapping this expression in `std::sync::LazyLock::new(|| ...)`"
208            ));
209        }
210
211        err
212    }
213}
214
215/// Build an error message reporting that a function call is not const (or only
216/// conditionally const). In case that this call is desugared (like an operator
217/// or sugar from something like a `for` loop), try to build a better error message
218/// that doesn't call it a method.
219fn build_error_for_const_call<'tcx>(
220    ccx: &ConstCx<'_, 'tcx>,
221    callee: DefId,
222    args: ty::GenericArgsRef<'tcx>,
223    span: Span,
224    call_source: CallSource,
225    non_or_conditionally: &'static str,
226    note_trait_if_possible: impl FnOnce(&mut Diag<'tcx>, Ty<'tcx>, DefId),
227) -> Diag<'tcx> {
228    let tcx = ccx.tcx;
229
230    let call_kind =
231        call_kind(tcx, ccx.typing_env, callee, args, span, call_source.from_hir_call(), None);
232
233    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_const_eval/src/check_consts/ops.rs:233",
                        "rustc_const_eval::check_consts::ops",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_const_eval/src/check_consts/ops.rs"),
                        ::tracing_core::__macro_support::Option::Some(233u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_const_eval::check_consts::ops"),
                        ::tracing_core::field::FieldSet::new(&[{
                                            const NAME:
                                                ::tracing::__macro_support::FieldName<{
                                                    ::tracing::__macro_support::FieldName::len("call_kind")
                                                }> =
                                                ::tracing::__macro_support::FieldName::new("call_kind");
                                            NAME.as_str()
                                        }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&call_kind)
                                            as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!(?call_kind);
234
235    let mut err = match call_kind {
236        CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => {
237            macro_rules! error {
238                ($err:ident) => {
239                    tcx.dcx().create_err(diagnostics::$err {
240                        span,
241                        ty: self_ty,
242                        kind: ccx.const_kind(),
243                        non_or_conditionally,
244                    })
245                };
246            }
247
248            // Don't point at the trait if this is a desugaring...
249            // FIXME(const_trait_impl): we could perhaps do this for `Iterator`.
250            match kind {
251                CallDesugaringKind::ForLoopIntoIter
252                | CallDesugaringKind::ForLoopIntoAsyncIter
253                | CallDesugaringKind::ForLoopNext => {
254                    tcx.dcx().create_err(diagnostics::NonConstForLoopIntoIter {
        span,
        ty: self_ty,
        kind: ccx.const_kind(),
        non_or_conditionally,
    })error!(NonConstForLoopIntoIter)
255                }
256                CallDesugaringKind::QuestionBranch => {
257                    tcx.dcx().create_err(diagnostics::NonConstQuestionBranch {
        span,
        ty: self_ty,
        kind: ccx.const_kind(),
        non_or_conditionally,
    })error!(NonConstQuestionBranch)
258                }
259                CallDesugaringKind::QuestionFromResidual => {
260                    tcx.dcx().create_err(diagnostics::NonConstQuestionFromResidual {
        span,
        ty: self_ty,
        kind: ccx.const_kind(),
        non_or_conditionally,
    })error!(NonConstQuestionFromResidual)
261                }
262                CallDesugaringKind::TryBlockFromOutput => {
263                    tcx.dcx().create_err(diagnostics::NonConstTryBlockFromOutput {
        span,
        ty: self_ty,
        kind: ccx.const_kind(),
        non_or_conditionally,
    })error!(NonConstTryBlockFromOutput)
264                }
265                CallDesugaringKind::Await => {
266                    tcx.dcx().create_err(diagnostics::NonConstAwait {
        span,
        ty: self_ty,
        kind: ccx.const_kind(),
        non_or_conditionally,
    })error!(NonConstAwait)
267                }
268            }
269        }
270        CallKind::FnCall { fn_trait_id, self_ty } => {
271            let kind = ccx.const_kind();
272            let note = match self_ty.kind() {
273                FnDef(def_id, ..) => {
274                    let span = tcx.def_span(*def_id);
275                    if ccx.tcx.is_const_fn(*def_id) {
276                        ::rustc_middle::util::bug::span_bug_fmt(span,
    format_args!("calling const FnDef errored when it shouldn\'t"));span_bug!(span, "calling const FnDef errored when it shouldn't");
277                    }
278
279                    Some(diagnostics::NonConstClosureNote::FnDef { span })
280                }
281                FnPtr(..) => Some(diagnostics::NonConstClosureNote::FnPtr { kind }),
282                Closure(..) => Some(diagnostics::NonConstClosureNote::Closure { kind }),
283                _ => None,
284            };
285
286            let mut err = tcx.dcx().create_err(diagnostics::NonConstClosure {
287                span,
288                kind: ccx.const_kind(),
289                note,
290                non_or_conditionally,
291            });
292
293            note_trait_if_possible(&mut err, self_ty, fn_trait_id);
294            err
295        }
296        CallKind::Operator { trait_id, self_ty, .. } => {
297            let mut err = if let CallSource::MatchCmp = call_source {
298                tcx.dcx().create_err(diagnostics::NonConstMatchEq {
299                    span,
300                    kind: ccx.const_kind(),
301                    ty: self_ty,
302                    non_or_conditionally,
303                })
304            } else {
305                let mut sugg = None;
306
307                if ccx.tcx.is_lang_item(trait_id, LangItem::PartialEq) {
308                    match (args[0].kind(), args[1].kind()) {
309                        (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
310                            if self_ty == rhs_ty
311                                && self_ty.is_ref()
312                                && self_ty.peel_refs().is_primitive() =>
313                        {
314                            let mut num_refs = 0;
315                            let mut tmp_ty = self_ty;
316                            while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() {
317                                num_refs += 1;
318                                tmp_ty = *inner_ty;
319                            }
320                            let deref = "*".repeat(num_refs);
321
322                            if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span)
323                                && let Some(eq_idx) = call_str.find("==")
324                                && let Some(rhs_idx) =
325                                    call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace())
326                            {
327                                let rhs_pos = span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx);
328                                let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
329                                sugg = Some(diagnostics::ConsiderDereferencing {
330                                    deref,
331                                    span: span.shrink_to_lo(),
332                                    rhs_span,
333                                });
334                            }
335                        }
336                        _ => {}
337                    }
338                }
339                tcx.dcx().create_err(diagnostics::NonConstOperator {
340                    span,
341                    kind: ccx.const_kind(),
342                    sugg,
343                    non_or_conditionally,
344                })
345            };
346
347            note_trait_if_possible(&mut err, self_ty, trait_id);
348            err
349        }
350        CallKind::DerefCoercion { deref_target_span, deref_target_ty, self_ty } => {
351            // Check first whether the source is accessible (issue #87060)
352            let target = if let Some(deref_target_span) = deref_target_span
353                && tcx.sess.source_map().is_span_accessible(deref_target_span)
354            {
355                Some(deref_target_span)
356            } else {
357                None
358            };
359
360            let mut err = tcx.dcx().create_err(diagnostics::NonConstDerefCoercion {
361                span,
362                ty: self_ty,
363                kind: ccx.const_kind(),
364                target_ty: deref_target_ty,
365                deref_target: target,
366                non_or_conditionally,
367            });
368
369            note_trait_if_possible(&mut err, self_ty, tcx.require_lang_item(LangItem::Deref, span));
370            err
371        }
372        _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::FmtArgumentsNew) => {
373            ccx.dcx().create_err(diagnostics::NonConstFmtMacroCall {
374                span,
375                kind: ccx.const_kind(),
376                non_or_conditionally,
377            })
378        }
379        _ => {
380            let def_descr = ccx.tcx.def_descr(callee);
381            let mut err = ccx.dcx().create_err(diagnostics::NonConstFnCall {
382                span,
383                def_descr,
384                def_path_str: ccx.tcx.def_path_str_with_args(callee, args),
385                kind: ccx.const_kind(),
386                non_or_conditionally,
387            });
388            if let Some(item) = ccx.tcx.opt_associated_item(callee) {
389                if let AssocContainer::Trait = item.container
390                    && let parent = item.container_id(ccx.tcx)
391                    && !ccx.tcx.is_const_trait(parent)
392                {
393                    let assoc_span = ccx.tcx.def_span(callee);
394                    let assoc_name = ccx.tcx.item_name(callee);
395                    let mut span: MultiSpan = ccx.tcx.def_span(parent).into();
396                    span.push_span_label(assoc_span, ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("this {0} is not const", def_descr))
    })format!("this {def_descr} is not const"));
397                    let trait_descr = ccx.tcx.def_descr(parent);
398                    let trait_span = ccx.tcx.def_span(parent);
399                    let trait_name = ccx.tcx.item_name(parent);
400                    span.push_span_label(trait_span, ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("this {0} is not const",
                trait_descr))
    })format!("this {trait_descr} is not const"));
401                    err.span_note(
402                        span,
403                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0} `{1}` is not const because {2} `{3}` is not const",
                def_descr, assoc_name, trait_descr, trait_name))
    })format!(
404                            "{def_descr} `{assoc_name}` is not const because {trait_descr} \
405                            `{trait_name}` is not const",
406                        ),
407                    );
408                    if let Some(parent) = parent.as_local()
409                        && ccx.tcx.sess.is_nightly_build()
410                    {
411                        if !ccx.tcx.features().const_trait_impl() {
412                            err.help(
413                                "add `#![feature(const_trait_impl)]` to the crate attributes to \
414                                 enable const traits",
415                            );
416                        }
417                        let span = ccx.tcx.hir_expect_item(parent).vis_span;
418                        let span = ccx.tcx.sess.source_map().span_extend_while_whitespace(span);
419                        err.span_suggestion_verbose(
420                            span.shrink_to_hi(),
421                            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("consider making trait `{0}` const",
                trait_name))
    })format!("consider making trait `{trait_name}` const"),
422                            "const ".to_owned(),
423                            Applicability::MaybeIncorrect,
424                        );
425                    } else if !ccx.tcx.sess.is_nightly_build() {
426                        err.help("const traits are not yet supported on stable Rust");
427                    }
428                }
429            } else if !#[allow(non_exhaustive_omitted_patterns)] match ccx.tcx.constness(callee) {
    hir::Constness::Const { always: false } => true,
    _ => false,
}matches!(ccx.tcx.constness(callee), hir::Constness::Const { always: false })
430            {
431                let name = ccx.tcx.item_name(callee);
432                err.span_note(
433                    ccx.tcx.def_span(callee),
434                    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0} `{1}` is not const", def_descr,
                name))
    })format!("{def_descr} `{name}` is not const"),
435                );
436            }
437            err
438        }
439    };
440
441    err.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("calls in {0}s are limited to constant functions, tuple structs and tuple variants",
                ccx.const_kind()))
    })format!(
442        "calls in {}s are limited to constant functions, tuple structs and tuple variants",
443        ccx.const_kind(),
444    ));
445
446    err
447}
448
449/// A call to an `#[unstable]` const fn, `#[rustc_const_unstable]` function or trait.
450///
451/// Contains the name of the feature that would allow the use of this function/trait.
452#[derive(#[automatically_derived]
impl ::core::fmt::Debug for CallUnstable {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field5_finish(f, "CallUnstable",
            "def_id", &self.def_id, "feature", &self.feature,
            "feature_enabled", &self.feature_enabled,
            "safe_to_expose_on_stable", &self.safe_to_expose_on_stable,
            "is_function_call", &&self.is_function_call)
    }
}Debug)]
453pub(crate) struct CallUnstable {
454    pub def_id: DefId,
455    pub feature: Symbol,
456    /// If this is true, then the feature is enabled, but we need to still check if it is safe to
457    /// expose on stable.
458    pub feature_enabled: bool,
459    pub safe_to_expose_on_stable: bool,
460    /// true if `def_id` is the function we are calling, false if `def_id` is an unstable trait.
461    pub is_function_call: bool,
462}
463
464impl<'tcx> NonConstOp<'tcx> for CallUnstable {
465    fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
466        Status::Unstable {
467            gate: self.feature,
468            gate_already_checked: self.feature_enabled,
469            safe_to_expose_on_stable: self.safe_to_expose_on_stable,
470            is_function_call: self.is_function_call,
471        }
472    }
473
474    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
475        if !!self.feature_enabled {
    ::core::panicking::panic("assertion failed: !self.feature_enabled")
};assert!(!self.feature_enabled);
476        let mut err = if self.is_function_call {
477            ccx.dcx().create_err(diagnostics::UnstableConstFn {
478                span,
479                def_path: ccx.tcx.def_path_str(self.def_id),
480            })
481        } else {
482            ccx.dcx().create_err(diagnostics::UnstableConstTrait {
483                span,
484                def_path: ccx.tcx.def_path_str(self.def_id),
485            })
486        };
487        ccx.tcx.disabled_nightly_features(&mut err, [(String::new(), self.feature)]);
488        err
489    }
490}
491
492/// A call to an intrinsic that is just not const-callable at all.
493#[derive(#[automatically_derived]
impl ::core::fmt::Debug for IntrinsicNonConst {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(f,
            "IntrinsicNonConst", "name", &&self.name)
    }
}Debug)]
494pub(crate) struct IntrinsicNonConst {
495    pub name: Symbol,
496}
497
498impl<'tcx> NonConstOp<'tcx> for IntrinsicNonConst {
499    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
500        ccx.dcx().create_err(diagnostics::NonConstIntrinsic {
501            span,
502            name: self.name,
503            kind: ccx.const_kind(),
504        })
505    }
506}
507
508/// A call to an intrinsic that is just not const-callable at all.
509#[derive(#[automatically_derived]
impl ::core::fmt::Debug for IntrinsicUnstable {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f,
            "IntrinsicUnstable", "name", &self.name, "feature", &self.feature,
            "const_stable_indirect", &&self.const_stable_indirect)
    }
}Debug)]
510pub(crate) struct IntrinsicUnstable {
511    pub name: Symbol,
512    pub feature: Symbol,
513    pub const_stable_indirect: bool,
514}
515
516impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable {
517    fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
518        Status::Unstable {
519            gate: self.feature,
520            gate_already_checked: false,
521            safe_to_expose_on_stable: self.const_stable_indirect,
522            // We do *not* want to suggest to mark the intrinsic as `const_stable_indirect`,
523            // that's not a trivial change!
524            is_function_call: false,
525        }
526    }
527
528    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
529        ccx.dcx().create_err(diagnostics::UnstableIntrinsic {
530            span,
531            name: self.name,
532            feature: self.feature,
533            suggestion: ccx.tcx.crate_level_attribute_injection_span(),
534        })
535    }
536}
537
538#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Coroutine {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Coroutine",
            &&self.0)
    }
}Debug)]
539pub(crate) struct Coroutine(pub hir::CoroutineKind);
540impl<'tcx> NonConstOp<'tcx> for Coroutine {
541    fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
542        match self.0 {
543            hir::CoroutineKind::Desugared(
544                hir::CoroutineDesugaring::Async,
545                hir::CoroutineSource::Block,
546            )
547            // FIXME(coroutines): eventually we want to gate const coroutine coroutines behind a
548            // different feature.
549            | hir::CoroutineKind::Coroutine(_) => Status::Unstable {
550                gate: sym::const_async_blocks,
551                gate_already_checked: false,
552                safe_to_expose_on_stable: false,
553                is_function_call: false,
554            },
555            _ => Status::Forbidden,
556        }
557    }
558
559    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
560        let msg = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0} are not allowed in {1}s",
                self.0.to_plural_string(), ccx.const_kind()))
    })format!("{} are not allowed in {}s", self.0.to_plural_string(), ccx.const_kind());
561        if let Status::Unstable { gate, .. } = self.status_in_item(ccx) {
562            ccx.tcx
563                .sess
564                .create_feature_err(diagnostics::UnallowedOpInConstContext { span, msg }, gate)
565        } else {
566            ccx.dcx().create_err(diagnostics::UnallowedOpInConstContext { span, msg })
567        }
568    }
569}
570
571#[derive(#[automatically_derived]
impl ::core::fmt::Debug for InlineAsm {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "InlineAsm")
    }
}Debug)]
572pub(crate) struct InlineAsm;
573impl<'tcx> NonConstOp<'tcx> for InlineAsm {
574    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
575        ccx.dcx().create_err(diagnostics::UnallowedInlineAsm { span, kind: ccx.const_kind() })
576    }
577}
578
579#[derive(#[automatically_derived]
impl<'tcx> ::core::fmt::Debug for LiveDrop<'tcx> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f, "LiveDrop",
            "dropped_at", &self.dropped_at, "dropped_ty", &self.dropped_ty,
            "needs_non_const_drop", &&self.needs_non_const_drop)
    }
}Debug)]
580pub(crate) struct LiveDrop<'tcx> {
581    pub dropped_at: Span,
582    pub dropped_ty: Ty<'tcx>,
583    pub needs_non_const_drop: bool,
584}
585impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> {
586    fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
587        if self.needs_non_const_drop {
588            Status::Forbidden
589        } else {
590            Status::Unstable {
591                gate: sym::const_destruct,
592                gate_already_checked: false,
593                safe_to_expose_on_stable: false,
594                is_function_call: false,
595            }
596        }
597    }
598
599    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
600        let mut err = if self.needs_non_const_drop {
601            ccx.dcx().create_err(diagnostics::LiveDrop {
602                span,
603                dropped_ty: self.dropped_ty,
604                kind: ccx.const_kind(),
605                dropped_at: self.dropped_at,
606            })
607        } else {
608            ccx.tcx.sess.create_feature_err(
609                diagnostics::LiveDrop {
610                    span,
611                    dropped_ty: self.dropped_ty,
612                    kind: ccx.const_kind(),
613                    dropped_at: self.dropped_at,
614                },
615                sym::const_destruct,
616            )
617        };
618
619        // If the dropped type is a type parameter, suggest adding a `[const] Destruct` bound.
620        // The suggestion is only offered on nightly, since `[const]` bounds are unstable.
621        if let Param(param_ty) = self.dropped_ty.kind()
622            && ccx.tcx.sess.is_nightly_build()
623        {
624            let tcx = ccx.tcx;
625            let caller = ccx.def_id();
626            if let Some(generics) = tcx.hir_node_by_def_id(caller).generics() {
627                let destruct_def_id = tcx.lang_items().destruct_trait();
628                suggest_constraining_type_param(
629                    tcx,
630                    generics,
631                    &mut err,
632                    param_ty.name.as_str(),
633                    "[const] Destruct",
634                    destruct_def_id,
635                    None,
636                );
637            }
638        }
639
640        err
641    }
642}
643
644#[derive(#[automatically_derived]
impl ::core::fmt::Debug for EscapingCellBorrow {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "EscapingCellBorrow")
    }
}Debug)]
645/// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow might escape to
646/// the final value of the constant, and thus we cannot allow this (for now). We may allow
647/// it in the future for static items.
648pub(crate) struct EscapingCellBorrow;
649impl<'tcx> NonConstOp<'tcx> for EscapingCellBorrow {
650    fn importance(&self) -> DiagImportance {
651        // Most likely the code will try to do mutation with these borrows, which
652        // triggers its own errors. Only show this one if that does not happen.
653        DiagImportance::Secondary
654    }
655    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
656        ccx.dcx()
657            .create_err(diagnostics::InteriorMutableBorrowEscaping { span, kind: ccx.const_kind() })
658    }
659}
660
661#[derive(#[automatically_derived]
impl ::core::fmt::Debug for EscapingMutBorrow {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "EscapingMutBorrow")
    }
}Debug)]
662/// This op is for `&mut` borrows in the trailing expression of a constant
663/// which uses the "enclosing scopes rule" to leak its locals into anonymous
664/// static or const items.
665pub(crate) struct EscapingMutBorrow;
666
667impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow {
668    fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
669        Status::Forbidden
670    }
671
672    fn importance(&self) -> DiagImportance {
673        // Most likely the code will try to do mutation with these borrows, which
674        // triggers its own errors. Only show this one if that does not happen.
675        DiagImportance::Secondary
676    }
677
678    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
679        ccx.dcx().create_err(diagnostics::MutableBorrowEscaping { span, kind: ccx.const_kind() })
680    }
681}
682
683/// A call to a `panic()` lang item where the first argument is _not_ a `&str`.
684#[derive(#[automatically_derived]
impl ::core::fmt::Debug for PanicNonStr {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "PanicNonStr")
    }
}Debug)]
685pub(crate) struct PanicNonStr;
686impl<'tcx> NonConstOp<'tcx> for PanicNonStr {
687    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
688        ccx.dcx().create_err(diagnostics::PanicNonStrErr { span })
689    }
690}
691
692/// Comparing raw pointers for equality.
693/// Not currently intended to ever be allowed, even behind a feature gate: operation depends on
694/// allocation base addresses that are not known at compile-time.
695#[derive(#[automatically_derived]
impl ::core::fmt::Debug for RawPtrComparison {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "RawPtrComparison")
    }
}Debug)]
696pub(crate) struct RawPtrComparison;
697impl<'tcx> NonConstOp<'tcx> for RawPtrComparison {
698    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
699        // FIXME(const_trait_impl): revert to span_bug?
700        ccx.dcx().create_err(diagnostics::RawPtrComparisonErr { span })
701    }
702}
703
704/// Casting raw pointer or function pointer to an integer.
705/// Not currently intended to ever be allowed, even behind a feature gate: operation depends on
706/// allocation base addresses that are not known at compile-time.
707#[derive(#[automatically_derived]
impl ::core::fmt::Debug for RawPtrToIntCast {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "RawPtrToIntCast")
    }
}Debug)]
708pub(crate) struct RawPtrToIntCast;
709impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast {
710    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
711        ccx.dcx().create_err(diagnostics::RawPtrToIntErr { span })
712    }
713}
714
715/// An access to a thread-local `static`.
716#[derive(#[automatically_derived]
impl ::core::fmt::Debug for ThreadLocalAccess {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "ThreadLocalAccess")
    }
}Debug)]
717pub(crate) struct ThreadLocalAccess;
718impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess {
719    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
720        ccx.dcx().create_err(diagnostics::ThreadLocalAccessErr { span })
721    }
722}