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