Skip to main content

rustc_const_eval/check_consts/
check.rs

1//! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations.
2
3use std::borrow::Cow;
4use std::num::NonZero;
5use std::ops::Deref;
6use std::{assert_matches, mem};
7
8use rustc_attr_ir::lang_items::LangItem;
9use rustc_attr_ir::{ConstStability, StabilityLevel, find_attr};
10use rustc_errors::{Diag, ErrorGuaranteed};
11use rustc_hir as hir;
12use rustc_hir::def::DefKind;
13use rustc_hir::def_id::DefId;
14use rustc_index::bit_set::DenseBitSet;
15use rustc_infer::infer::TyCtxtInferExt;
16use rustc_middle::mir::visit::Visitor;
17use rustc_middle::mir::*;
18use rustc_middle::ty::adjustment::PointerCoercion;
19use rustc_middle::ty::{self, Ty, TypeVisitableExt};
20use rustc_mir_dataflow::Analysis;
21use rustc_mir_dataflow::impls::{MaybeStorageLive, always_storage_live_locals};
22use rustc_span::{Span, Symbol, span_bug, sym};
23use rustc_trait_selection::traits::{
24    Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt,
25};
26use tracing::{instrument, trace};
27
28use super::ops::{self, NonConstOp, Status};
29use super::qualifs::{self, HasMutInterior, NeedsDrop, NeedsNonConstDrop};
30use super::resolver::FlowSensitiveAnalysis;
31use super::{ConstCx, Qualif};
32use crate::check_consts::is_fn_or_trait_safe_to_expose_on_stable;
33use crate::diagnostics;
34
35type QualifResults<'mir, 'tcx, Q> =
36    rustc_mir_dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'tcx, Q>>;
37
38#[derive(#[automatically_derived]
impl ::core::marker::Copy for ConstConditionsHold { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for ConstConditionsHold { }
#[automatically_derived]
impl ::core::clone::Clone for ConstConditionsHold {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for ConstConditionsHold { }
#[automatically_derived]
impl ::core::cmp::PartialEq for ConstConditionsHold {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
            ::core::intrinsics::discriminant_value(other)
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for ConstConditionsHold { }Eq, #[automatically_derived]
impl ::core::fmt::Debug for ConstConditionsHold {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                ConstConditionsHold::Yes => "Yes",
                ConstConditionsHold::No => "No",
            })
    }
}Debug)]
39enum ConstConditionsHold {
40    Yes,
41    No,
42}
43
44#[derive(#[automatically_derived]
impl<'mir, 'tcx> ::core::default::Default for Qualifs<'mir, 'tcx> {
    #[inline]
    fn default() -> Self {
        Self {
            has_mut_interior: ::core::default::Default::default(),
            needs_drop: ::core::default::Default::default(),
            needs_non_const_drop: ::core::default::Default::default(),
        }
    }
}Default)]
45pub(crate) struct Qualifs<'mir, 'tcx> {
46    has_mut_interior: Option<QualifResults<'mir, 'tcx, HasMutInterior>>,
47    needs_drop: Option<QualifResults<'mir, 'tcx, NeedsDrop>>,
48    needs_non_const_drop: Option<QualifResults<'mir, 'tcx, NeedsNonConstDrop>>,
49}
50
51impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
52    /// Does `Q` hold for the `local` at the given `Location`?
53    ///
54    /// Only updates the cursor if absolutely necessary.
55    fn in_local<Q: Qualif>(
56        qualif_results: &mut Option<QualifResults<'mir, 'tcx, Q>>,
57        ccx: &'mir ConstCx<'mir, 'tcx>,
58        local: Local,
59        location: Location,
60    ) -> bool {
61        let ty = ccx.body.local_decls[local].ty;
62        // Peeking into opaque types causes cycles if the current function declares said opaque
63        // type. Thus we avoid short circuiting on the type and instead run the more expensive
64        // analysis that looks at the actual usage within this function.
65        if !ty.has_opaque_types() && !Q::in_any_value_of_ty(ccx, ty) {
66            return false;
67        }
68
69        let qualif_results = qualif_results.get_or_insert_with(|| {
70            let ConstCx { tcx, body, .. } = *ccx;
71
72            FlowSensitiveAnalysis::new(ccx)
73                .iterate_to_fixpoint(tcx, body, None)
74                .into_results_cursor(body)
75        });
76
77        qualif_results.seek_before_primary_effect(location);
78        qualif_results.get().contains(local)
79    }
80
81    fn in_return_place(
82        &mut self,
83        ccx: &'mir ConstCx<'mir, 'tcx>,
84        tainted_by_errors: Option<ErrorGuaranteed>,
85    ) -> ConstQualifs {
86        // FIXME(explicit_tail_calls): uhhhh I think we can return without return now, does it change anything
87
88        // Find the `Return` terminator if one exists.
89        //
90        // If no `Return` terminator exists, this MIR is divergent. Just return the conservative
91        // qualifs for the return type.
92        let return_block = ccx
93            .body
94            .basic_blocks
95            .iter_enumerated()
96            .find(|(_, block)| #[allow(non_exhaustive_omitted_patterns)] match block.terminator().kind {
    TerminatorKind::Return => true,
    _ => false,
}matches!(block.terminator().kind, TerminatorKind::Return))
97            .map(|(bb, _)| bb);
98
99        let Some(return_block) = return_block else {
100            return qualifs::in_any_value_of_ty(ccx, ccx.body.return_ty(), tainted_by_errors);
101        };
102
103        let return_loc = ccx.body.terminator_loc(return_block);
104
105        ConstQualifs {
106            needs_drop: Self::in_local(&mut self.needs_drop, ccx, RETURN_PLACE, return_loc),
107            needs_non_const_drop: Self::in_local(
108                &mut self.needs_non_const_drop,
109                ccx,
110                RETURN_PLACE,
111                return_loc,
112            ),
113            has_mut_interior: Self::in_local(
114                &mut self.has_mut_interior,
115                ccx,
116                RETURN_PLACE,
117                return_loc,
118            ),
119            tainted_by_errors,
120        }
121    }
122}
123
124pub struct Checker<'mir, 'tcx> {
125    ccx: &'mir ConstCx<'mir, 'tcx>,
126    qualifs: Qualifs<'mir, 'tcx>,
127
128    /// The span of the current statement.
129    span: Span,
130
131    /// A set that stores for each local whether it is "transient", i.e. guaranteed to be dead
132    /// when this MIR body returns.
133    transient_locals: Option<DenseBitSet<Local>>,
134
135    error_emitted: Option<ErrorGuaranteed>,
136    secondary_errors: Vec<Diag<'tcx>>,
137}
138
139impl<'mir, 'tcx> Deref for Checker<'mir, 'tcx> {
140    type Target = ConstCx<'mir, 'tcx>;
141
142    fn deref(&self) -> &Self::Target {
143        self.ccx
144    }
145}
146
147impl<'mir, 'tcx> Checker<'mir, 'tcx> {
148    pub fn new(ccx: &'mir ConstCx<'mir, 'tcx>) -> Self {
149        Checker {
150            span: ccx.body.span,
151            ccx,
152            qualifs: Default::default(),
153            transient_locals: None,
154            error_emitted: None,
155            secondary_errors: Vec::new(),
156        }
157    }
158
159    pub fn check_body(&mut self) {
160        let ConstCx { tcx, body, .. } = *self.ccx;
161        let def_id = self.ccx.def_id();
162
163        // `async` functions cannot be `const fn`. This is checked during AST lowering, so there's
164        // no need to emit duplicate errors here.
165        if self.ccx.is_async() || body.coroutine.is_some() {
166            tcx.dcx().span_delayed_bug(body.span, "`async` functions cannot be `const fn`");
167            return;
168        }
169
170        if !{
        {
            'done:
                {
                for i in ::rustc_attr_ir::HasAttrs::get_attrs(def_id, &tcx) {
                    #[allow(unused_imports)]
                    use ::rustc_attr_ir::AttributeKind::*;
                    let i: &::rustc_attr_ir::Attribute = i;
                    match i {
                        ::rustc_attr_ir::Attribute::Parsed(RustcDoNotConstCheck) =>
                            {
                            break 'done Some(());
                        }
                        ::rustc_attr_ir::Attribute::Unparsed(..) =>
                            {}
                            #[deny(unreachable_patterns)]
                            _ => {}
                    }
                }
                None
            }
        }
    }.is_some()find_attr!(tcx, def_id, RustcDoNotConstCheck) {
171            self.visit_body(body);
172        }
173
174        // If we got through const-checking without emitting any "primary" errors, emit any
175        // "secondary" errors if they occurred. Otherwise, cancel the "secondary" errors.
176        let secondary_errors = mem::take(&mut self.secondary_errors);
177        if self.error_emitted.is_none() {
178            for error in secondary_errors {
179                self.error_emitted = Some(error.emit_err());
180            }
181        } else {
182            if !self.tcx.dcx().has_errors().is_some() {
    ::core::panicking::panic("assertion failed: self.tcx.dcx().has_errors().is_some()")
};assert!(self.tcx.dcx().has_errors().is_some());
183            for error in secondary_errors {
184                error.cancel();
185            }
186        }
187    }
188
189    fn local_is_transient(&mut self, local: Local) -> bool {
190        let ccx = self.ccx;
191        self.transient_locals
192            .get_or_insert_with(|| {
193                // A local is "transient" if it is guaranteed dead at all `Return`.
194                // So first compute the say of "maybe live" locals at each program point.
195                let always_live_locals = &always_storage_live_locals(&ccx.body);
196                let mut maybe_storage_live =
197                    MaybeStorageLive::new(Cow::Borrowed(always_live_locals))
198                        .iterate_to_fixpoint(ccx.tcx, &ccx.body, None)
199                        .into_results_cursor(&ccx.body);
200
201                // And then check all `Return` in the MIR, and if a local is "maybe live" at a
202                // `Return` then it is definitely not transient.
203                let mut transient = DenseBitSet::new_filled(ccx.body.local_decls.len());
204                // Make sure to only visit reachable blocks, the dataflow engine can ICE otherwise.
205                for (bb, data) in traversal::reachable(&ccx.body) {
206                    if data.terminator().kind == TerminatorKind::Return {
207                        let location = ccx.body.terminator_loc(bb);
208                        maybe_storage_live.seek_after_primary_effect(location);
209                        // If a local may be live here, it is definitely not transient.
210                        transient.subtract(maybe_storage_live.get());
211                    }
212                }
213
214                transient
215            })
216            .contains(local)
217    }
218
219    pub fn qualifs_in_return_place(&mut self) -> ConstQualifs {
220        self.qualifs.in_return_place(self.ccx, self.error_emitted)
221    }
222
223    /// Emits an error if an expression cannot be evaluated in the current context.
224    pub fn check_op(&mut self, op: impl NonConstOp<'tcx>) {
225        self.check_op_spanned(op, self.span);
226    }
227
228    /// Emits an error at the given `span` if an expression cannot be evaluated in the current
229    /// context.
230    pub fn check_op_spanned<O: NonConstOp<'tcx>>(&mut self, op: O, span: Span) {
231        let gate = match op.status_in_item(self.ccx) {
232            Status::Unstable {
233                gate,
234                safe_to_expose_on_stable,
235                is_function_call,
236                gate_already_checked,
237            } if gate_already_checked || self.tcx.features().enabled(gate) => {
238                if gate_already_checked {
239                    if !!safe_to_expose_on_stable {
    {
        ::core::panicking::panic_fmt(format_args!("setting `gate_already_checked` without `safe_to_expose_on_stable` makes no sense"));
    }
};assert!(
240                        !safe_to_expose_on_stable,
241                        "setting `gate_already_checked` without `safe_to_expose_on_stable` makes no sense"
242                    );
243                }
244                // Generally this is allowed since the feature gate is enabled -- except
245                // if this function wants to be safe-to-expose-on-stable.
246                if !safe_to_expose_on_stable
247                    && self.enforce_recursive_const_stability()
248                    && !super::rustc_allow_const_fn_unstable(self.tcx, self.def_id(), gate)
249                {
250                    // Avoid suggesting to add `rustc_const_unstable` if the attribute is already
251                    // present. Need to directly check raw attributes as
252                    // `tcx.lookup_const_stability` also includes inherited stability.
253                    let already_unstable = {
        {
            'done:
                {
                for i in
                    ::rustc_attr_ir::HasAttrs::get_attrs(self.def_id(),
                        &self.tcx) {
                    #[allow(unused_imports)]
                    use ::rustc_attr_ir::AttributeKind::*;
                    let i: &::rustc_attr_ir::Attribute = i;
                    match i {
                        ::rustc_attr_ir::Attribute::Parsed(RustcConstStability {
                            stability, .. }) if stability.is_const_unstable() => {
                            break 'done Some(());
                        }
                        ::rustc_attr_ir::Attribute::Unparsed(..) =>
                            {}
                            #[deny(unreachable_patterns)]
                            _ => {}
                    }
                }
                None
            }
        }
    }.is_some()find_attr!(self.tcx, self.def_id(), RustcConstStability { stability, .. } if stability.is_const_unstable());
254                    emit_unstable_in_stable_exposed_error(
255                        self.ccx,
256                        span,
257                        gate,
258                        is_function_call,
259                        already_unstable,
260                    );
261                }
262
263                return;
264            }
265
266            Status::Unstable { gate, .. } => Some(gate),
267            Status::Forbidden => None,
268        };
269
270        if self.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you {
271            self.tcx.sess.miri_unleashed_feature(span, gate);
272            return;
273        }
274
275        let err = op.build_error(self.ccx, span);
276        if !err.is_error() {
    ::core::panicking::panic("assertion failed: err.is_error()")
};assert!(err.is_error());
277
278        match op.importance() {
279            ops::DiagImportance::Primary => {
280                let guar = err.emit_err();
281                self.error_emitted = Some(guar);
282            }
283
284            ops::DiagImportance::Secondary => {
285                self.secondary_errors.push(err);
286                self.tcx.dcx().span_delayed_bug(
287                    span,
288                    "compilation must fail when there is a secondary const checker error",
289                );
290            }
291        }
292    }
293
294    fn check_static(&mut self, def_id: DefId, span: Span) {
295        if self.tcx.is_thread_local_static(def_id) {
296            self.tcx.dcx().span_bug(span, "tls access is checked in `Rvalue::ThreadLocalRef`");
297        }
298        if let Some(def_id) = def_id.as_local()
299            && let Err(guar) = self.tcx.ensure_result().check_well_formed(hir::OwnerId { def_id })
300        {
301            self.error_emitted = Some(guar);
302        }
303    }
304
305    /// Returns whether this place can possibly escape the evaluation of the current const/static
306    /// initializer. The check assumes that all already existing pointers and references point to
307    /// non-escaping places.
308    fn place_may_escape(&mut self, place: &Place<'_>) -> bool {
309        let is_transient = match self.const_kind() {
310            // In a const fn all borrows are transient or point to the places given via
311            // references in the arguments (so we already checked them with
312            // TransientMutBorrow/MutBorrow as appropriate).
313            // The borrow checker guarantees that no new non-transient borrows are created.
314            // NOTE: Once we have heap allocations during CTFE we need to figure out
315            // how to prevent `const fn` to create long-lived allocations that point
316            // to mutable memory.
317            hir::ConstContext::ConstFn => true,
318            _ => {
319                // For indirect places, we are not creating a new permanent borrow, it's just as
320                // transient as the already existing one.
321                // Locals with StorageDead do not live beyond the evaluation and can
322                // thus safely be borrowed without being able to be leaked to the final
323                // value of the constant.
324                // Note: This is only sound if every local that has a `StorageDead` has a
325                // `StorageDead` in every control flow path leading to a `return` terminator.
326                // If anything slips through, there's no safety net -- safe code can create
327                // references to variants of `!Freeze` enums as long as that variant is `Freeze`, so
328                // interning can't protect us here. (There *is* a safety net for mutable references
329                // though, interning will ICE if we miss something here.)
330                place.is_indirect() || self.local_is_transient(place.local)
331            }
332        };
333        // Transient places cannot possibly escape because the place doesn't exist any more at the
334        // end of evaluation.
335        !is_transient
336    }
337
338    /// Returns whether there are const-conditions.
339    fn revalidate_conditional_constness(
340        &mut self,
341        callee: DefId,
342        callee_args: ty::GenericArgsRef<'tcx>,
343        call_span: Span,
344    ) -> Option<ConstConditionsHold> {
345        let tcx = self.tcx;
346        if !tcx.is_conditionally_const(callee) {
347            return None;
348        }
349
350        let const_conditions = tcx.const_conditions(callee).instantiate(tcx, callee_args);
351        if const_conditions.is_empty() {
352            return None;
353        }
354
355        let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(self.body.typing_env(tcx));
356        let ocx = ObligationCtxt::new(&infcx);
357
358        let body_id = self.body.source.def_id().expect_local();
359        let host_polarity = match self.const_kind() {
360            hir::ConstContext::ConstFn => ty::BoundConstness::Maybe,
361            hir::ConstContext::Static(_) | hir::ConstContext::Const { .. } => {
362                ty::BoundConstness::Const
363            }
364        };
365        let const_conditions = const_conditions.into_iter().map(|(c, s)| {
366            (ocx.normalize(&ObligationCause::misc(call_span, body_id), param_env, c), s)
367        });
368        ocx.register_obligations(const_conditions.into_iter().map(|(trait_ref, span)| {
369            Obligation::new(
370                tcx,
371                ObligationCause::new(
372                    call_span,
373                    body_id,
374                    ObligationCauseCode::WhereClause(callee, span),
375                ),
376                param_env,
377                trait_ref.to_host_effect_clause(tcx, host_polarity),
378            )
379        }));
380
381        let errors = ocx.evaluate_obligations_error_on_ambiguity();
382        if errors.no_errors() {
383            Some(ConstConditionsHold::Yes)
384        } else {
385            tcx.dcx()
386                .span_delayed_bug(call_span, "this should have reported a [const] error in HIR");
387            Some(ConstConditionsHold::No)
388        }
389    }
390
391    pub fn check_drop_terminator(
392        &mut self,
393        dropped_place: Place<'tcx>,
394        location: Location,
395        terminator_span: Span,
396    ) {
397        let ty_of_dropped_place = dropped_place.ty(self.body, self.tcx).ty;
398
399        let needs_drop = if let Some(local) = dropped_place.as_local() {
400            Qualifs::in_local(&mut self.qualifs.needs_drop, self.ccx, local, location)
401        } else {
402            qualifs::NeedsDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place)
403        };
404        // If this type doesn't need a drop at all, then there's nothing to enforce.
405        if !needs_drop {
406            return;
407        }
408
409        let mut err_span = self.span;
410        let needs_non_const_drop = if let Some(local) = dropped_place.as_local() {
411            // Use the span where the local was declared as the span of the drop error.
412            err_span = self.body.local_decls[local].source_info.span;
413            Qualifs::in_local(&mut self.qualifs.needs_non_const_drop, self.ccx, local, location)
414        } else {
415            qualifs::NeedsNonConstDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place)
416        };
417
418        self.check_op_spanned(
419            ops::LiveDrop {
420                dropped_at: terminator_span,
421                dropped_ty: ty_of_dropped_place,
422                needs_non_const_drop,
423            },
424            err_span,
425        );
426    }
427
428    /// Check the const stability of the given item (fn or trait).
429    fn check_callee_stability(&mut self, def_id: DefId) {
430        match self.tcx.lookup_const_stability(def_id) {
431            Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => {
432                // All good.
433            }
434            None => {
435                // This doesn't need a separate const-stability check -- const-stability equals
436                // regular stability, and regular stability is checked separately.
437                // However, we *do* have to worry about *recursive* const stability.
438                if self.enforce_recursive_const_stability()
439                    && !is_fn_or_trait_safe_to_expose_on_stable(self.tcx, def_id)
440                {
441                    self.dcx().emit_err(diagnostics::UnmarkedConstItemExposed {
442                        span: self.span,
443                        def_path: self.tcx.def_path_str(def_id),
444                    });
445                }
446            }
447            Some(ConstStability {
448                level: StabilityLevel::Unstable { implied_by: implied_feature, issue, .. },
449                feature,
450                ..
451            }) => {
452                // An unstable const fn/trait with a feature gate.
453                let callee_safe_to_expose_on_stable =
454                    is_fn_or_trait_safe_to_expose_on_stable(self.tcx, def_id);
455
456                // We only honor `span.allows_unstable` aka `#[allow_internal_unstable]` if
457                // the callee is safe to expose, to avoid bypassing recursive stability.
458                // This is not ideal since it means the user sees an error, not the macro
459                // author, but that's also the case if one forgets to set
460                // `#[allow_internal_unstable]` in the first place. Note that this cannot be
461                // integrated in the check below since we want to enforce
462                // `callee_safe_to_expose_on_stable` even if
463                // `!self.enforce_recursive_const_stability()`.
464                if (self.span.allows_unstable(feature)
465                    || implied_feature.is_some_and(|f| self.span.allows_unstable(f)))
466                    && callee_safe_to_expose_on_stable
467                {
468                    return;
469                }
470
471                // We can't use `check_op` to check whether the feature is enabled because
472                // the logic is a bit different than elsewhere: local functions don't need
473                // the feature gate, and there might be an "implied" gate that also suffices
474                // to allow this.
475                let feature_enabled = def_id.is_local()
476                    || self.tcx.features().enabled(feature)
477                    || implied_feature.is_some_and(|f| self.tcx.features().enabled(f))
478                    || {
479                        // When we're compiling the compiler itself we may pull in
480                        // crates from crates.io, but those crates may depend on other
481                        // crates also pulled in from crates.io. We want to ideally be
482                        // able to compile everything without requiring upstream
483                        // modifications, so in the case that this looks like a
484                        // `rustc_private` crate (e.g., a compiler crate) and we also have
485                        // the `-Z force-unstable-if-unmarked` flag present (we're
486                        // compiling a compiler crate), then let this missing feature
487                        // annotation slide.
488                        // This matches what we do in `eval_stability_allow_unstable` for
489                        // regular stability.
490                        feature == sym::rustc_private
491                            && issue == NonZero::new(27812)
492                            && self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked
493                    };
494                // Even if the feature is enabled, we still need check_op to double-check
495                // this if the callee is not safe to expose on stable.
496                if !feature_enabled || !callee_safe_to_expose_on_stable {
497                    self.check_op(ops::CallUnstable {
498                        def_id,
499                        feature,
500                        feature_enabled,
501                        safe_to_expose_on_stable: callee_safe_to_expose_on_stable,
502                        is_function_call: self.tcx.def_kind(def_id) != DefKind::Trait,
503                    });
504                }
505            }
506        }
507    }
508}
509
510impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
511    fn visit_basic_block_data(&mut self, bb: BasicBlock, block: &BasicBlockData<'tcx>) {
512        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_const_eval/src/check_consts/check.rs:512",
                        "rustc_const_eval::check_consts::check",
                        ::tracing::Level::TRACE,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_const_eval/src/check_consts/check.rs"),
                        ::tracing_core::__macro_support::Option::Some(512u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_const_eval::check_consts::check"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::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(&format_args!("visit_basic_block_data: bb={0:?} is_cleanup={1:?}",
                                                    bb, block.is_cleanup) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};trace!("visit_basic_block_data: bb={:?} is_cleanup={:?}", bb, block.is_cleanup);
513
514        // We don't const-check basic blocks on the cleanup path since we never unwind during
515        // const-eval: a panic causes an immediate compile error. In other words, cleanup blocks
516        // are unreachable during const-eval.
517        //
518        // We can't be more conservative (e.g., by const-checking cleanup blocks anyways) because
519        // locals that would never be dropped during normal execution are sometimes dropped during
520        // unwinding, which means backwards-incompatible live-drop errors.
521        if block.is_cleanup {
522            return;
523        }
524
525        self.super_basic_block_data(bb, block);
526    }
527
528    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
529        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_const_eval/src/check_consts/check.rs:529",
                        "rustc_const_eval::check_consts::check",
                        ::tracing::Level::TRACE,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_const_eval/src/check_consts/check.rs"),
                        ::tracing_core::__macro_support::Option::Some(529u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_const_eval::check_consts::check"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::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(&format_args!("visit_rvalue: rvalue={0:?} location={1:?}",
                                                    rvalue, location) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
530
531        self.super_rvalue(rvalue, location);
532
533        match rvalue {
534            Rvalue::ThreadLocalRef(_) => self.check_op(ops::ThreadLocalAccess),
535
536            Rvalue::Use(..)
537            | Rvalue::CopyForDeref(..)
538            | Rvalue::Repeat(..)
539            | Rvalue::Discriminant(..) => {}
540
541            Rvalue::Aggregate(kind, ..) => {
542                if let AggregateKind::Coroutine(def_id, ..) = kind.as_ref()
543                    && let Some(coroutine_kind) = self.tcx.coroutine_kind(*def_id)
544                {
545                    self.check_op(ops::Coroutine(coroutine_kind));
546                }
547            }
548
549            Rvalue::Ref(_, BorrowKind::Mut { .. }, place)
550            | Rvalue::RawPtr(RawPtrKind::Mut, place) => {
551                // Inside mutable statics, we allow arbitrary mutable references.
552                // We've allowed `static mut FOO = &mut [elements];` for a long time (the exact
553                // reasons why are lost to history), and there is no reason to restrict that to
554                // arrays and slices.
555                let is_allowed =
556                    self.const_kind() == hir::ConstContext::Static(hir::Mutability::Mut);
557
558                if !is_allowed && self.place_may_escape(place) {
559                    self.check_op(ops::EscapingMutBorrow);
560                }
561            }
562
563            Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Fake(_), place)
564            | Rvalue::RawPtr(RawPtrKind::Const, place) => {
565                let borrowed_place_has_mut_interior = qualifs::in_place::<HasMutInterior, _>(
566                    self.ccx,
567                    &mut |local| {
568                        Qualifs::in_local(
569                            &mut self.qualifs.has_mut_interior,
570                            self.ccx,
571                            local,
572                            location,
573                        )
574                    },
575                    place.as_ref(),
576                );
577
578                if borrowed_place_has_mut_interior && self.place_may_escape(place) {
579                    self.check_op(ops::EscapingCellBorrow);
580                }
581            }
582
583            Rvalue::Reborrow(..) => {
584                // FIXME(reborrow): figure out if this is relevant at all.
585            }
586
587            Rvalue::RawPtr(RawPtrKind::FakeForPtrMetadata, place) => {
588                // These are only inserted for slice length, so the place must already be indirect.
589                // This implies we do not have to worry about whether the borrow escapes.
590                if !place.is_indirect() {
591                    self.tcx.dcx().span_delayed_bug(
592                        self.body.source_info(location).span,
593                        "fake borrows are always indirect",
594                    );
595                }
596            }
597
598            Rvalue::Cast(
599                CastKind::IntToInt
600                | CastKind::FloatToInt
601                | CastKind::FloatToFloat
602                | CastKind::IntToFloat
603                | CastKind::PtrToPtr
604                | CastKind::FnPtrToPtr
605                | CastKind::Transmute
606                | CastKind::BoxDerefTransmute
607                | CastKind::PointerCoercion(
608                    PointerCoercion::MutToConstPointer
609                    | PointerCoercion::ArrayToPointer
610                    | PointerCoercion::UnsafeFnPointer
611                    | PointerCoercion::ClosureFnPointer(_)
612                    | PointerCoercion::ReifyFnPointer(_)
613                    | PointerCoercion::Unsize,
614                    _,
615                ),
616                _,
617                _,
618            ) => {
619                // Operations that are fully supported by const-eval.
620            }
621            // Special checks for special casts
622            Rvalue::Cast(CastKind::PointerExposeProvenance, _, _) => {
623                self.check_op(ops::RawPtrToIntCast);
624            }
625            Rvalue::Cast(CastKind::PointerWithExposedProvenance, _, _) => {
626                // Since no pointer can ever get exposed (rejected above), this is easy to support.
627            }
628            Rvalue::Cast(kind @ CastKind::Subtype, _, _) => {
629                ::rustc_span::macros::bug_impl(Some(self.span),
    format_args!("invalid CastKind for this MIR phase: {0:?}", kind),
    Location::caller());span_bug!(self.span, "invalid CastKind for this MIR phase: {kind:?}");
630            }
631
632            Rvalue::UnaryOp(op, operand) => {
633                let ty = operand.ty(self.body, self.tcx);
634                match op {
635                    UnOp::Not | UnOp::Neg => {
636                        if is_int_bool_float_or_char(ty) {
637                            // Int, bool, float, and char operations are fine.
638                        } else {
639                            ::rustc_span::macros::bug_impl(Some(self.span),
    format_args!("non-primitive type in `Rvalue::UnaryOp{0:?}`: {1:?}", op,
        ty), Location::caller());span_bug!(
640                                self.span,
641                                "non-primitive type in `Rvalue::UnaryOp{op:?}`: {ty:?}",
642                            );
643                        }
644                    }
645                    UnOp::PtrMetadata => {
646                        // Getting the metadata from a pointer is always const.
647                        // We already validated the type is valid in the validator.
648                    }
649                }
650            }
651
652            Rvalue::BinaryOp(op, (lhs, rhs)) => {
653                let lhs_ty = lhs.ty(self.body, self.tcx);
654                let rhs_ty = rhs.ty(self.body, self.tcx);
655
656                if is_int_bool_float_or_char(lhs_ty) && is_int_bool_float_or_char(rhs_ty) {
657                    // Int, bool, float, and char operations are fine.
658                } else if lhs_ty.is_fn_ptr() || lhs_ty.is_raw_ptr() {
659                    {
    match op {
        BinOp::Eq | BinOp::Ne | BinOp::Le | BinOp::Lt | BinOp::Ge | BinOp::Gt
            | BinOp::Offset => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val,
                "BinOp::Eq | BinOp::Ne | BinOp::Le | BinOp::Lt | BinOp::Ge | BinOp::Gt |\nBinOp::Offset",
                ::core::option::Option::None);
        }
    }
};assert_matches!(
660                        op,
661                        BinOp::Eq
662                            | BinOp::Ne
663                            | BinOp::Le
664                            | BinOp::Lt
665                            | BinOp::Ge
666                            | BinOp::Gt
667                            | BinOp::Offset
668                    );
669
670                    self.check_op(ops::RawPtrComparison);
671                } else {
672                    ::rustc_span::macros::bug_impl(Some(self.span),
    format_args!("non-primitive type in `Rvalue::BinaryOp`: {0:?} ⚬ {1:?}",
        lhs_ty, rhs_ty), Location::caller());span_bug!(
673                        self.span,
674                        "non-primitive type in `Rvalue::BinaryOp`: {:?} ⚬ {:?}",
675                        lhs_ty,
676                        rhs_ty
677                    );
678                }
679            }
680
681            Rvalue::WrapUnsafeBinder(..) => {
682                // Unsafe binders are always trivial to create.
683            }
684        }
685    }
686
687    fn visit_operand(&mut self, op: &Operand<'tcx>, location: Location) {
688        self.super_operand(op, location);
689        if let Operand::Constant(c) = op
690            && let Some(def_id) = c.check_static_ptr(self.tcx)
691        {
692            self.check_static(def_id, self.span);
693        }
694    }
695
696    fn visit_source_info(&mut self, source_info: &SourceInfo) {
697        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_const_eval/src/check_consts/check.rs:697",
                        "rustc_const_eval::check_consts::check",
                        ::tracing::Level::TRACE,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_const_eval/src/check_consts/check.rs"),
                        ::tracing_core::__macro_support::Option::Some(697u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_const_eval::check_consts::check"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::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(&format_args!("visit_source_info: source_info={0:?}",
                                                    source_info) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};trace!("visit_source_info: source_info={:?}", source_info);
698        self.span = source_info.span;
699    }
700
701    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
702        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_const_eval/src/check_consts/check.rs:702",
                        "rustc_const_eval::check_consts::check",
                        ::tracing::Level::TRACE,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_const_eval/src/check_consts/check.rs"),
                        ::tracing_core::__macro_support::Option::Some(702u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_const_eval::check_consts::check"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::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(&format_args!("visit_statement: statement={0:?} location={1:?}",
                                                    statement, location) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};trace!("visit_statement: statement={:?} location={:?}", statement, location);
703
704        self.super_statement(statement, location);
705
706        match statement.kind {
707            StatementKind::Assign(..)
708            | StatementKind::SetDiscriminant { .. }
709            | StatementKind::FakeRead(..)
710            | StatementKind::StorageLive(_)
711            | StatementKind::StorageDead(_)
712            | StatementKind::PlaceMention(..)
713            | StatementKind::AscribeUserType(..)
714            | StatementKind::Coverage(..)
715            | StatementKind::Intrinsic(..)
716            | StatementKind::ConstEvalCounter
717            | StatementKind::BackwardIncompatibleDropHint { .. }
718            | StatementKind::Nop => {}
719        }
720    }
721
722    {}
#[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("visit_terminator",
                                    "rustc_const_eval::check_consts::check",
                                    ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("/rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_const_eval/src/check_consts/check.rs"),
                                    ::tracing_core::__macro_support::Option::Some(722u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_const_eval::check_consts::check"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("terminator")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("terminator");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("location")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("location");
                                                        NAME.as_str()
                                                    }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&terminator)
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&location)
                                                            as &dyn ::tracing::field::Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: () = loop {};
            return __tracing_attr_fake_return;
        }
        {
            self.super_terminator(terminator, location);
            match &terminator.kind {
                TerminatorKind::Call { func, args, fn_span, .. } |
                    TerminatorKind::TailCall { func, args, fn_span, .. } => {
                    let call_source =
                        match terminator.kind {
                            TerminatorKind::Call { call_source, .. } => call_source,
                            TerminatorKind::TailCall { .. } => CallSource::Normal,
                            _ =>
                                ::core::panicking::panic("internal error: entered unreachable code"),
                        };
                    let ConstCx { tcx, body, .. } = *self.ccx;
                    let fn_ty = func.ty(body, tcx);
                    let (callee, fn_args) =
                        match *fn_ty.kind() {
                            ty::FnDef(def_id, fn_args) =>
                                (def_id, fn_args.no_bound_vars().unwrap()),
                            ty::FnPtr(..) => {
                                self.check_op(ops::FnCallIndirect);
                                return;
                            }
                            _ => {
                                ::rustc_span::macros::bug_impl(Some(terminator.source_info.span),
                                    format_args!("invalid callee of type {0:?}", fn_ty),
                                    Location::caller())
                            }
                        };
                    let has_const_conditions =
                        self.revalidate_conditional_constness(callee, fn_args,
                            *fn_span);
                    if let Some(trait_did) = tcx.trait_of_assoc(callee) {
                        {
                            use ::tracing::__macro_support::Callsite as _;
                            static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                                {
                                    static META: ::tracing::Metadata<'static> =
                                        {
                                            ::tracing_core::metadata::Metadata::new("event /rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_const_eval/src/check_consts/check.rs:761",
                                                "rustc_const_eval::check_consts::check",
                                                ::tracing::Level::TRACE,
                                                ::tracing_core::__macro_support::Option::Some("/rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_const_eval/src/check_consts/check.rs"),
                                                ::tracing_core::__macro_support::Option::Some(761u32),
                                                ::tracing_core::__macro_support::Option::Some("rustc_const_eval::check_consts::check"),
                                                ::tracing_core::field::FieldSet::new(&["message"],
                                                    ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                                ::tracing::metadata::Kind::EVENT)
                                        };
                                    ::tracing::callsite::DefaultCallsite::new(&META)
                                };
                            let enabled =
                                ::tracing::Level::TRACE <=
                                            ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                        ::tracing::Level::TRACE <=
                                            ::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(&format_args!("attempting to call a trait method")
                                                                    as &dyn ::tracing::field::Value))])
                                    });
                            } else { ; }
                        };
                        let is_const =
                            #[allow(non_exhaustive_omitted_patterns)] match tcx.constness(callee)
                                {
                                hir::Constness::Const { always: false } => true,
                                _ => false,
                            };
                        if is_const &&
                                has_const_conditions == Some(ConstConditionsHold::Yes) {
                            self.check_op(ops::ConditionallyConstCall {
                                    callee,
                                    args: fn_args,
                                    span: *fn_span,
                                    call_source,
                                });
                            self.check_callee_stability(trait_did);
                        } else {
                            self.check_op(ops::FnCallNonConst {
                                    callee,
                                    args: fn_args,
                                    span: *fn_span,
                                    call_source,
                                });
                        }
                        return;
                    }
                    if has_const_conditions.is_some() {
                        self.check_op(ops::ConditionallyConstCall {
                                callee,
                                args: fn_args,
                                span: *fn_span,
                                call_source,
                            });
                    }
                    if self.tcx.fn_sig(callee).skip_binder().c_variadic() {
                        self.check_op(ops::FnCallCVariadic)
                    }
                    if tcx.is_lang_item(callee, LangItem::BeginPanic) {
                        match args[0].node.ty(&self.ccx.body.local_decls,
                                    tcx).kind() {
                            ty::Ref(_, ty, _) if ty.is_str() => {}
                            _ => self.check_op(ops::PanicNonStr),
                        }
                        return;
                    }
                    if tcx.is_lang_item(callee, LangItem::PanicDisplay) {
                        if let ty::Ref(_, ty, _) =
                                        args[0].node.ty(&self.ccx.body.local_decls, tcx).kind() &&
                                    let ty::Ref(_, ty, _) = ty.kind() && ty.is_str()
                            {} else { self.check_op(ops::PanicNonStr); }
                        return;
                    }
                    if let Some(intrinsic) = tcx.intrinsic(callee) {
                        if !tcx.is_const_fn(callee) {
                            self.check_op(ops::IntrinsicNonConst {
                                    name: intrinsic.name,
                                });
                            return;
                        }
                        let usable_fallback_body =
                            !intrinsic.must_be_overridden &&
                                !{
                                            {
                                                'done:
                                                    {
                                                    for i in
                                                        ::rustc_attr_ir::HasAttrs::get_attrs(callee, &self.tcx) {
                                                        #[allow(unused_imports)]
                                                        use ::rustc_attr_ir::AttributeKind::*;
                                                        let i: &::rustc_attr_ir::Attribute = i;
                                                        match i {
                                                            ::rustc_attr_ir::Attribute::Parsed(RustcDoNotConstCheck) =>
                                                                {
                                                                break 'done Some(());
                                                            }
                                                            ::rustc_attr_ir::Attribute::Unparsed(..) =>
                                                                {}
                                                                #[deny(unreachable_patterns)]
                                                                _ => {}
                                                        }
                                                    }
                                                    None
                                                }
                                            }
                                        }.is_some();
                        let const_stable_indirect =
                            is_fn_or_trait_safe_to_expose_on_stable(tcx, callee) &&
                                (usable_fallback_body || intrinsic.const_stable_indirect);
                        match tcx.lookup_const_stability(callee) {
                            None => {
                                if !const_stable_indirect &&
                                        self.enforce_recursive_const_stability() {
                                    self.dcx().emit_err(diagnostics::UnmarkedIntrinsicExposed {
                                            span: self.span,
                                            def_path: self.tcx.def_path_str(callee),
                                        });
                                }
                            }
                            Some(ConstStability {
                                level: StabilityLevel::Unstable { .. }, feature, .. }) => {
                                if self.span.allows_unstable(feature) &&
                                        const_stable_indirect {
                                    return;
                                }
                                self.check_op(ops::IntrinsicUnstable {
                                        name: intrinsic.name,
                                        feature,
                                        const_stable_indirect,
                                    });
                            }
                            Some(ConstStability { level: StabilityLevel::Stable { .. },
                                .. }) => {}
                        }
                        return;
                    }
                    if !tcx.is_const_fn(callee) {
                        self.check_op(ops::FnCallNonConst {
                                callee,
                                args: fn_args,
                                span: *fn_span,
                                call_source,
                            });
                        return;
                    }
                    self.check_callee_stability(callee);
                }
                TerminatorKind::Drop { place: dropped_place, .. } => {
                    if super::post_drop_elaboration::checking_enabled(self.ccx)
                        {
                        return;
                    }
                    self.check_drop_terminator(*dropped_place, location,
                        terminator.source_info.span);
                }
                TerminatorKind::InlineAsm { .. } =>
                    self.check_op(ops::InlineAsm),
                TerminatorKind::Yield { .. } => {
                    self.check_op(ops::Coroutine(self.tcx.coroutine_kind(self.body.source.def_id()).expect("Only expected to have a yield in a coroutine")));
                }
                TerminatorKind::CoroutineDrop => {
                    ::rustc_span::macros::bug_impl(Some(self.body.source_info(location).span),
                        format_args!("We should not encounter TerminatorKind::CoroutineDrop after coroutine transform"),
                        Location::caller());
                }
                TerminatorKind::UnwindTerminate(_) => {
                    ::rustc_span::macros::bug_impl(Some(self.span),
                        format_args!("`Terminate` terminator outside of cleanup block"),
                        Location::caller())
                }
                TerminatorKind::Assert { .. } | TerminatorKind::FalseEdge { ..
                    } | TerminatorKind::FalseUnwind { .. } |
                    TerminatorKind::Goto { .. } | TerminatorKind::UnwindResume |
                    TerminatorKind::Return | TerminatorKind::SwitchInt { .. } |
                    TerminatorKind::Unreachable => {}
            }
        }
    }
}#[instrument(level = "debug", skip(self))]
723    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
724        self.super_terminator(terminator, location);
725
726        match &terminator.kind {
727            TerminatorKind::Call { func, args, fn_span, .. }
728            | TerminatorKind::TailCall { func, args, fn_span, .. } => {
729                let call_source = match terminator.kind {
730                    TerminatorKind::Call { call_source, .. } => call_source,
731                    TerminatorKind::TailCall { .. } => CallSource::Normal,
732                    _ => unreachable!(),
733                };
734
735                let ConstCx { tcx, body, .. } = *self.ccx;
736
737                let fn_ty = func.ty(body, tcx);
738
739                let (callee, fn_args) = match *fn_ty.kind() {
740                    ty::FnDef(def_id, fn_args) => (def_id, fn_args.no_bound_vars().unwrap()),
741
742                    ty::FnPtr(..) => {
743                        self.check_op(ops::FnCallIndirect);
744                        // We can get here without an error in miri-unleashed mode... might as well
745                        // skip the rest of the checks as well then.
746                        return;
747                    }
748                    _ => {
749                        span_bug!(terminator.source_info.span, "invalid callee of type {:?}", fn_ty)
750                    }
751                };
752
753                let has_const_conditions =
754                    self.revalidate_conditional_constness(callee, fn_args, *fn_span);
755
756                // Attempting to call a trait method?
757                if let Some(trait_did) = tcx.trait_of_assoc(callee) {
758                    // We can't determine the actual callee (the underlying impl of the trait) here, so we have
759                    // to do different checks than usual.
760
761                    trace!("attempting to call a trait method");
762                    let is_const =
763                        matches!(tcx.constness(callee), hir::Constness::Const { always: false });
764
765                    // Only consider a trait to be const if the const conditions hold.
766                    // Otherwise, it's really misleading to call something "conditionally"
767                    // const when it's very obviously not conditionally const.
768                    if is_const && has_const_conditions == Some(ConstConditionsHold::Yes) {
769                        // Trait calls are always conditionally-const.
770                        self.check_op(ops::ConditionallyConstCall {
771                            callee,
772                            args: fn_args,
773                            span: *fn_span,
774                            call_source,
775                        });
776                        self.check_callee_stability(trait_did);
777                    } else {
778                        // Not even a const trait.
779                        self.check_op(ops::FnCallNonConst {
780                            callee,
781                            args: fn_args,
782                            span: *fn_span,
783                            call_source,
784                        });
785                    }
786                    // That's all we can check here.
787                    return;
788                }
789
790                // Even if we know the callee, ensure we can use conditionally-const calls.
791                if has_const_conditions.is_some() {
792                    self.check_op(ops::ConditionallyConstCall {
793                        callee,
794                        args: fn_args,
795                        span: *fn_span,
796                        call_source,
797                    });
798                }
799
800                if self.tcx.fn_sig(callee).skip_binder().c_variadic() {
801                    self.check_op(ops::FnCallCVariadic)
802                }
803
804                // At this point, we are calling a function, `callee`, whose `DefId` is known...
805
806                // `begin_panic` and `panic_display` functions accept generic
807                // types other than str. Check to enforce that only str can be used in
808                // const-eval.
809
810                // const-eval of the `begin_panic` fn assumes the argument is `&str`
811                if tcx.is_lang_item(callee, LangItem::BeginPanic) {
812                    match args[0].node.ty(&self.ccx.body.local_decls, tcx).kind() {
813                        ty::Ref(_, ty, _) if ty.is_str() => {}
814                        _ => self.check_op(ops::PanicNonStr),
815                    }
816                    // Allow this call, skip all the checks below.
817                    return;
818                }
819
820                // const-eval of `panic_display` assumes the argument is `&&str`
821                if tcx.is_lang_item(callee, LangItem::PanicDisplay) {
822                    if let ty::Ref(_, ty, _) =
823                        args[0].node.ty(&self.ccx.body.local_decls, tcx).kind()
824                        && let ty::Ref(_, ty, _) = ty.kind()
825                        && ty.is_str()
826                    {
827                    } else {
828                        self.check_op(ops::PanicNonStr);
829                    }
830                    // Allow this call, skip all the checks below.
831                    return;
832                }
833
834                // Intrinsics are language primitives, not regular calls, so treat them separately.
835                if let Some(intrinsic) = tcx.intrinsic(callee) {
836                    if !tcx.is_const_fn(callee) {
837                        // Non-const intrinsic.
838                        self.check_op(ops::IntrinsicNonConst { name: intrinsic.name });
839                        // If we allowed this, we're in miri-unleashed mode, so we might
840                        // as well skip the remaining checks.
841                        return;
842                    }
843                    // In addition to the usual check, we also require that the intrinsic either has
844                    // a fallback body, or the special `#[rustc_intrinsic_const_stable_indirect]`.
845                    let usable_fallback_body = !intrinsic.must_be_overridden
846                        && !find_attr!(self.tcx, callee, RustcDoNotConstCheck);
847                    let const_stable_indirect =
848                        is_fn_or_trait_safe_to_expose_on_stable(tcx, callee)
849                            && (usable_fallback_body || intrinsic.const_stable_indirect);
850                    match tcx.lookup_const_stability(callee) {
851                        None => {
852                            // This doesn't need a separate const-stability check -- const-stability equals
853                            // regular stability, and regular stability is checked separately.
854                            // However, we *do* have to worry about *recursive* const stability.
855                            if !const_stable_indirect && self.enforce_recursive_const_stability() {
856                                self.dcx().emit_err(diagnostics::UnmarkedIntrinsicExposed {
857                                    span: self.span,
858                                    def_path: self.tcx.def_path_str(callee),
859                                });
860                            }
861                        }
862                        Some(ConstStability {
863                            level: StabilityLevel::Unstable { .. },
864                            feature,
865                            ..
866                        }) => {
867                            // We only honor `span.allows_unstable` aka `#[allow_internal_unstable]`
868                            // if the callee is safe to expose, to avoid bypassing recursive stability.
869                            // This is not ideal since it means the user sees an error, not the macro
870                            // author, but that's also the case if one forgets to set
871                            // `#[allow_internal_unstable]` in the first place.
872                            if self.span.allows_unstable(feature) && const_stable_indirect {
873                                return;
874                            }
875
876                            self.check_op(ops::IntrinsicUnstable {
877                                name: intrinsic.name,
878                                feature,
879                                const_stable_indirect,
880                            });
881                        }
882                        Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => {
883                            // All good. Note that a `#[rustc_const_stable]` intrinsic (meaning it
884                            // can be *directly* invoked from stable const code) does not always
885                            // have the `#[rustc_intrinsic_const_stable_indirect]` attribute (which controls
886                            // exposing an intrinsic indirectly); we accept this call anyway.
887                        }
888                    }
889                    // This completes the checks for intrinsics.
890                    return;
891                }
892
893                if !tcx.is_const_fn(callee) {
894                    self.check_op(ops::FnCallNonConst {
895                        callee,
896                        args: fn_args,
897                        span: *fn_span,
898                        call_source,
899                    });
900                    // If we allowed this, we're in miri-unleashed mode, so we might
901                    // as well skip the remaining checks.
902                    return;
903                }
904
905                // Finally, stability for regular function calls -- this is the big one.
906                self.check_callee_stability(callee);
907            }
908
909            // Forbid all `Drop` terminators unless the place being dropped is a local with no
910            // projections that cannot be `NeedsNonConstDrop`.
911            TerminatorKind::Drop { place: dropped_place, .. } => {
912                // If we are checking live drops after drop-elaboration, don't emit duplicate
913                // errors here.
914                if super::post_drop_elaboration::checking_enabled(self.ccx) {
915                    return;
916                }
917
918                self.check_drop_terminator(*dropped_place, location, terminator.source_info.span);
919            }
920
921            TerminatorKind::InlineAsm { .. } => self.check_op(ops::InlineAsm),
922
923            TerminatorKind::Yield { .. } => {
924                self.check_op(ops::Coroutine(
925                    self.tcx
926                        .coroutine_kind(self.body.source.def_id())
927                        .expect("Only expected to have a yield in a coroutine"),
928                ));
929            }
930
931            TerminatorKind::CoroutineDrop => {
932                span_bug!(
933                    self.body.source_info(location).span,
934                    "We should not encounter TerminatorKind::CoroutineDrop after coroutine transform"
935                );
936            }
937
938            TerminatorKind::UnwindTerminate(_) => {
939                // Cleanup blocks are skipped for const checking (see `visit_basic_block_data`).
940                span_bug!(self.span, "`Terminate` terminator outside of cleanup block")
941            }
942
943            TerminatorKind::Assert { .. }
944            | TerminatorKind::FalseEdge { .. }
945            | TerminatorKind::FalseUnwind { .. }
946            | TerminatorKind::Goto { .. }
947            | TerminatorKind::UnwindResume
948            | TerminatorKind::Return
949            | TerminatorKind::SwitchInt { .. }
950            | TerminatorKind::Unreachable => {}
951        }
952    }
953}
954
955fn is_int_bool_float_or_char(ty: Ty<'_>) -> bool {
956    ty.is_bool() || ty.is_integral() || ty.is_char() || ty.is_floating_point()
957}
958
959fn emit_unstable_in_stable_exposed_error(
960    ccx: &ConstCx<'_, '_>,
961    span: Span,
962    gate: Symbol,
963    is_function_call: bool,
964    already_unstable: bool,
965) -> ErrorGuaranteed {
966    let attr_span = ccx.tcx.def_span(ccx.def_id()).shrink_to_lo();
967
968    ccx.dcx().emit_err(diagnostics::UnstableInStableExposed {
969        gate: gate.to_string(),
970        span,
971        suggest_const_unstable: (!already_unstable).then_some(attr_span),
972        is_function_call,
973        is_function_call2: is_function_call,
974    })
975}