Skip to main content

rustc_mir_build/thir/pattern/
const_to_pat.rs

1use core::ops::ControlFlow;
2
3use rustc_abi::{FieldIdx, VariantIdx};
4use rustc_apfloat::Float;
5use rustc_data_structures::fx::FxHashSet;
6use rustc_errors::{Diag, msg};
7use rustc_hir as hir;
8use rustc_hir::find_attr;
9use rustc_index::Idx;
10use rustc_infer::infer::TyCtxtInferExt;
11use rustc_infer::traits::Obligation;
12use rustc_middle::mir::interpret::ErrorHandled;
13use rustc_middle::span_bug;
14use rustc_middle::thir::{FieldPat, Pat, PatKind};
15use rustc_middle::ty::{
16    self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitableExt, TypeVisitor, Unnormalized,
17};
18use rustc_span::def_id::DefId;
19use rustc_span::{DUMMY_SP, Span};
20use rustc_trait_selection::traits::ObligationCause;
21use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
22use tracing::{debug, instrument, trace};
23
24use super::PatCtxt;
25use crate::diagnostics::{
26    ConstPatternDependsOnGenericParameter, CouldNotEvalConstPattern, InvalidPattern, NaNPattern,
27    PointerPattern, SuggestEq, TypeNotPartialEq, TypeNotStructural, UnionPattern, UnsizedPattern,
28};
29
30impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> {
31    /// Converts a constant to a pattern (if possible).
32    /// This means aggregate values (like structs and enums) are converted
33    /// to a pattern that matches the value (as if you'd compared via structural equality).
34    ///
35    /// Only type system constants are supported, as we are using valtrees
36    /// as an intermediate step. Unfortunately those don't carry a type
37    /// so we have to carry one ourselves.
38    x;#[instrument(level = "debug", skip(self), ret)]
39    pub(super) fn const_to_pat(
40        &self,
41        c: ty::Const<'tcx>,
42        ty: Ty<'tcx>,
43        id: hir::HirId,
44        span: Span,
45    ) -> Box<Pat<'tcx>> {
46        let mut convert = ConstToPat::new(self, id, span, c);
47
48        match c.kind() {
49            ty::ConstKind::Unevaluated(uv) => convert.unevaluated_to_pat(uv, ty),
50            ty::ConstKind::Value(value) => convert.valtree_to_pat(value),
51            _ => span_bug!(span, "Invalid `ConstKind` for `const_to_pat`: {:?}", c),
52        }
53    }
54}
55
56struct ConstToPat<'tcx> {
57    tcx: TyCtxt<'tcx>,
58    typing_env: ty::TypingEnv<'tcx>,
59    span: Span,
60    id: hir::HirId,
61
62    c: ty::Const<'tcx>,
63}
64
65impl<'tcx> ConstToPat<'tcx> {
66    fn new(pat_ctxt: &PatCtxt<'tcx, '_>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self {
67        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs:67",
                        "rustc_mir_build::thir::pattern::const_to_pat",
                        ::tracing::Level::TRACE,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs"),
                        ::tracing_core::__macro_support::Option::Some(67u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::const_to_pat"),
                        ::tracing_core::field::FieldSet::new(&["pat_ctxt.typeck_results.hir_owner"],
                            ::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};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&pat_ctxt.typeck_results.hir_owner)
                                            as &dyn Value))])
            });
    } else { ; }
};trace!(?pat_ctxt.typeck_results.hir_owner);
68        ConstToPat { tcx: pat_ctxt.tcx, typing_env: pat_ctxt.typing_env, span, id, c }
69    }
70
71    fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
72        ty.is_structural_eq_shallow(self.tcx)
73    }
74
75    /// We errored. Signal that in the pattern, so that follow up errors can be silenced.
76    fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
77        if let ty::ConstKind::Unevaluated(uv) = self.c.kind() {
78            if let ty::UnevaluatedConstKind::Projection { def_id }
79            | ty::UnevaluatedConstKind::Inherent { def_id } = uv.kind
80                && let Some(def_id) = def_id.as_local()
81            {
82                // Include the container item in the output.
83                err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), "");
84            }
85            if let ty::UnevaluatedConstKind::Projection { def_id }
86            | ty::UnevaluatedConstKind::Inherent { def_id }
87            | ty::UnevaluatedConstKind::Free { def_id } = uv.kind
88            {
89                err.span_label(self.tcx.def_span(def_id), rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("constant defined here"))msg!("constant defined here"));
90            }
91        }
92        Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()), extra: None })
93    }
94
95    fn unevaluated_to_pat(
96        &mut self,
97        uv: ty::UnevaluatedConst<'tcx>,
98        ty: Ty<'tcx>,
99    ) -> Box<Pat<'tcx>> {
100        // It's not *technically* correct to be revealing opaque types here as borrowcheck has
101        // not run yet. However, CTFE itself uses `TypingMode::PostAnalysis` unconditionally even
102        // during typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821).
103        // As a result we always use a revealed env when resolving the instance to evaluate.
104        //
105        // FIXME: `const_eval_resolve_for_typeck` should probably just modify the env itself
106        // instead of having this logic here
107        let typing_env =
108            self.tcx.erase_and_anonymize_regions(self.typing_env).with_codegen_normalized(self.tcx);
109        let uv = self.tcx.erase_and_anonymize_regions(uv);
110
111        // FIXME(gca): This will become insufficient once associated constants can be
112        // implemented as `type` consts (project-const-generics#76). At that point it'll
113        // become necessary to just use type system normalization for all const patterns
114        // but that's not yet possible.
115        let mut thir_pat = if uv.kind.is_type_const(self.tcx) {
116            let Ok(normalize) = self
117                .tcx
118                .try_normalize_erasing_regions(self.typing_env, Unnormalized::new_wip(self.c))
119            else {
120                let err = self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span });
121                return self.mk_err(err, ty);
122            };
123
124            let ty::ConstKind::Value(value) = normalize.kind() else {
125                let err = self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span });
126                return self.mk_err(err, ty);
127            };
128            self.valtree_to_pat(value)
129        } else {
130            // try to resolve e.g. associated constants to their definition on an impl, and then
131            // evaluate the const.
132            let valtree = match self.tcx.const_eval_resolve_for_typeck(typing_env, uv, self.span) {
133                Ok(Ok(c)) => c,
134                Err(ErrorHandled::Reported(_, _)) => {
135                    // Let's tell the use where this failing const occurs.
136                    let mut err =
137                        self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span });
138                    // We've emitted an error on the original const, it would be redundant to complain
139                    // on its use as well.
140                    if let ty::ConstKind::Unevaluated(uv) = self.c.kind()
141                        && let ty::UnevaluatedConstKind::Projection { .. }
142                        | ty::UnevaluatedConstKind::Inherent { .. }
143                        | ty::UnevaluatedConstKind::Free { .. } = uv.kind
144                    {
145                        err.downgrade_to_delayed_bug();
146                    }
147                    return self.mk_err(err, ty);
148                }
149                Err(ErrorHandled::TooGeneric(_)) => {
150                    let mut e = self
151                        .tcx
152                        .dcx()
153                        .create_err(ConstPatternDependsOnGenericParameter { span: self.span });
154                    for arg in uv.args {
155                        if let ty::GenericArgKind::Type(ty) = arg.kind()
156                            && let ty::Param(param_ty) = ty.kind()
157                        {
158                            let def_id = self.tcx.hir_enclosing_body_owner(self.id);
159                            let generics = self.tcx.generics_of(def_id);
160                            let param = generics.type_param(*param_ty, self.tcx);
161                            let span = self.tcx.def_span(param.def_id);
162                            e.span_label(span, "constant depends on this generic parameter");
163                            if let Some(ident) = self.tcx.def_ident_span(def_id)
164                                && self.tcx.sess.source_map().is_multiline(ident.between(span))
165                            {
166                                // Display the `fn` name as well in the diagnostic, as the generic isn't
167                                // in the same line and it could be confusing otherwise.
168                                e.span_label(ident, "");
169                            }
170                        }
171                    }
172                    return self.mk_err(e, ty);
173                }
174                Ok(Err(bad_ty)) => {
175                    // The pattern cannot be turned into a valtree.
176                    let e = match bad_ty.kind() {
177                        ty::Adt(def, ..) => {
178                            if !def.is_union() {
    ::core::panicking::panic("assertion failed: def.is_union()")
};assert!(def.is_union());
179                            self.tcx.dcx().create_err(UnionPattern { span: self.span })
180                        }
181                        ty::FnPtr(..) | ty::RawPtr(..) => {
182                            self.tcx.dcx().create_err(PointerPattern { span: self.span })
183                        }
184                        _ => self.tcx.dcx().create_err(InvalidPattern {
185                            span: self.span,
186                            non_sm_ty: bad_ty,
187                            prefix: bad_ty.prefix_string(self.tcx).to_string(),
188                        }),
189                    };
190                    return self.mk_err(e, ty);
191                }
192            };
193
194            // Lower the valtree to a THIR pattern.
195            self.valtree_to_pat(ty::Value { ty, valtree })
196        };
197
198        if !thir_pat.references_error() {
199            // Always check for `PartialEq` if we had no other errors yet.
200            if !type_has_partial_eq_impl(self.tcx, typing_env, ty).has_impl {
201                let mut err = self.tcx.dcx().create_err(TypeNotPartialEq { span: self.span, ty });
202                extend_type_not_partial_eq(self.tcx, typing_env, ty, &mut err);
203                return self.mk_err(err, ty);
204            }
205        }
206
207        // Mark the pattern to indicate that it is the result of lowering a named
208        // constant. This is used for diagnostics.
209        thir_pat.extra.get_or_insert_default().expanded_const = uv.kind.opt_def_id();
210        thir_pat
211    }
212
213    fn lower_field_values_to_fieldpats(
214        &self,
215        values: impl Iterator<Item = ty::Value<'tcx>>,
216    ) -> Vec<FieldPat<'tcx>> {
217        values
218            .enumerate()
219            .map(|(index, value)| FieldPat {
220                field: FieldIdx::new(index),
221                pattern: *self.valtree_to_pat(value),
222            })
223            .collect()
224    }
225
226    // Recursive helper for `to_pat`; invoke that (instead of calling this directly).
227    #[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("valtree_to_pat",
                                    "rustc_mir_build::thir::pattern::const_to_pat",
                                    ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs"),
                                    ::tracing_core::__macro_support::Option::Some(227u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::const_to_pat"),
                                    ::tracing_core::field::FieldSet::new(&["value"],
                                        ::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};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&value)
                                                            as &dyn 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: Box<Pat<'tcx>> = loop {};
            return __tracing_attr_fake_return;
        }
        {
            let span = self.span;
            let tcx = self.tcx;
            let ty::Value { ty, valtree } = value;
            let kind =
                match ty.kind() {
                    ty::Adt(adt_def, _) if !self.type_marked_structural(ty) => {
                        {
                            use ::tracing::__macro_support::Callsite as _;
                            static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                                {
                                    static META: ::tracing::Metadata<'static> =
                                        {
                                            ::tracing_core::metadata::Metadata::new("event compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs:238",
                                                "rustc_mir_build::thir::pattern::const_to_pat",
                                                ::tracing::Level::DEBUG,
                                                ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs"),
                                                ::tracing_core::__macro_support::Option::Some(238u32),
                                                ::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::const_to_pat"),
                                                ::tracing_core::field::FieldSet::new(&["message", "adt_def",
                                                                "value.ty"],
                                                    ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                                ::tracing::metadata::Kind::EVENT)
                                        };
                                    ::tracing::callsite::DefaultCallsite::new(&META)
                                };
                            let enabled =
                                ::tracing::Level::DEBUG <=
                                            ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                        ::tracing::Level::DEBUG <=
                                            ::tracing::level_filters::LevelFilter::current() &&
                                    {
                                        let interest = __CALLSITE.interest();
                                        !interest.is_never() &&
                                            ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                                                interest)
                                    };
                            if enabled {
                                (|value_set: ::tracing::field::ValueSet|
                                            {
                                                let meta = __CALLSITE.metadata();
                                                ::tracing::Event::dispatch(meta, &value_set);
                                                ;
                                            })({
                                        #[allow(unused_imports)]
                                        use ::tracing::field::{debug, display, Value};
                                        let mut iter = __CALLSITE.metadata().fields().iter();
                                        __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                            ::tracing::__macro_support::Option::Some(&format_args!("ADT type in pattern is not `type_marked_structural`")
                                                                    as &dyn Value)),
                                                        (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                            ::tracing::__macro_support::Option::Some(&debug(&adt_def) as
                                                                    &dyn Value)),
                                                        (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                            ::tracing::__macro_support::Option::Some(&debug(&value.ty)
                                                                    as &dyn Value))])
                                    });
                            } else { ; }
                        };
                        let PartialEqImplStatus {
                                is_derived, structural_partial_eq, non_blanket_impl, .. } =
                            type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
                        let (manual_partialeq_impl_span,
                                manual_partialeq_impl_note) =
                            match (structural_partial_eq, non_blanket_impl) {
                                (true, _) => (None, false),
                                (_, Some(def_id)) if def_id.is_local() && !is_derived => {
                                    (Some(tcx.def_span(def_id)), false)
                                }
                                _ => (None, true),
                            };
                        let manual_partialeq_impl =
                            manual_partialeq_impl_note ||
                                manual_partialeq_impl_span.is_some();
                        let is_local = adt_def.did().is_local();
                        let ty_def_span = tcx.def_span(adt_def.did());
                        let suggestion =
                            if let Ok(name) =
                                        tcx.sess.source_map().span_to_snippet(self.span) &&
                                    (is_local || manual_partialeq_impl) {
                                let mut hir_id = self.id;
                                while let hir::Node::Pat(pat) = tcx.parent_hir_node(hir_id)
                                    {
                                    hir_id = pat.hir_id;
                                }
                                match tcx.parent_hir_node(hir_id) {
                                    hir::Node::Arm(hir::Arm { pat, guard: None, .. }) => {
                                        Some(SuggestEq::AddIf {
                                                if_span: pat.span.shrink_to_hi(),
                                                pat_span: self.span,
                                                name,
                                                ty,
                                                manual_partialeq_impl,
                                            })
                                    }
                                    hir::Node::Arm(hir::Arm { guard: Some(guard), .. }) => {
                                        Some(SuggestEq::AddToIf {
                                                span: guard.span.shrink_to_hi(),
                                                pat_span: self.span,
                                                name,
                                                ty,
                                                manual_partialeq_impl,
                                            })
                                    }
                                    hir::Node::Expr(hir::Expr {
                                        kind: hir::ExprKind::Let(let_expr), span, .. }) => {
                                        if let_expr.pat.span == self.span {
                                            Some(SuggestEq::ReplaceWithEq {
                                                    removal: span.until(self.span),
                                                    eq: self.span.between(let_expr.init.span),
                                                    ty,
                                                    manual_partialeq_impl,
                                                })
                                        } else if tcx.sess.edition().at_least_rust_2024() {
                                            Some(SuggestEq::AddToLetChain {
                                                    span: span.shrink_to_hi(),
                                                    pat_span: self.span,
                                                    name,
                                                    ty,
                                                    manual_partialeq_impl,
                                                })
                                        } else { None }
                                    }
                                    hir::Node::LetStmt(let_stmt) if
                                        let Some(init) = let_stmt.init &&
                                                    let Some(els) = let_stmt.els && init.span.ctxt().is_root()
                                            && els.span.ctxt().is_root() => {
                                        Some(SuggestEq::ReplaceLetElseWithIf {
                                                if_span: let_stmt.span.until(let_stmt.pat.span),
                                                eq: let_stmt.pat.span.between(init.span),
                                                else_span: init.span.between(els.span),
                                                ty,
                                                manual_partialeq_impl,
                                            })
                                    }
                                    _ => None,
                                }
                            } else { None };
                        let err =
                            TypeNotStructural {
                                span,
                                ty,
                                ty_def_span,
                                manual_partialeq_impl_span,
                                manual_partialeq_impl_note,
                                is_local,
                                suggestion,
                            };
                        return self.mk_err(tcx.dcx().create_err(err), ty);
                    }
                    ty::Adt(adt_def, args) if adt_def.is_enum() => {
                        let (&variant_index, fields) =
                            valtree.to_branch().split_first().unwrap();
                        let variant_index =
                            VariantIdx::from_u32(variant_index.to_leaf().to_u32());
                        PatKind::Variant {
                            adt_def: *adt_def,
                            args,
                            variant_index,
                            subpatterns: self.lower_field_values_to_fieldpats(fields.iter().map(|ct|
                                        ct.to_value())),
                        }
                    }
                    ty::Adt(def, _) => {
                        if !!def.is_union() {
                            ::core::panicking::panic("assertion failed: !def.is_union()")
                        };
                        PatKind::Leaf {
                            subpatterns: self.lower_field_values_to_fieldpats(valtree.to_branch().iter().map(|ct|
                                        ct.to_value())),
                        }
                    }
                    ty::Tuple(_) =>
                        PatKind::Leaf {
                            subpatterns: self.lower_field_values_to_fieldpats(valtree.to_branch().iter().map(|ct|
                                        ct.to_value())),
                        },
                    ty::Slice(_) =>
                        PatKind::Slice {
                            prefix: valtree.to_branch().iter().map(|val|
                                        *self.valtree_to_pat(val.to_value())).collect(),
                            slice: None,
                            suffix: Box::new([]),
                        },
                    ty::Array(_, _) =>
                        PatKind::Array {
                            prefix: valtree.to_branch().iter().map(|val|
                                        *self.valtree_to_pat(val.to_value())).collect(),
                            slice: None,
                            suffix: Box::new([]),
                        },
                    ty::Str => { PatKind::Constant { value } }
                    ty::Ref(_, pointee_ty, ..) => {
                        if pointee_ty.is_str() || pointee_ty.is_slice() ||
                                pointee_ty.is_sized(tcx, self.typing_env) {
                            PatKind::Deref {
                                pin: hir::Pinnedness::Not,
                                subpattern: self.valtree_to_pat(ty::Value {
                                        ty: *pointee_ty,
                                        valtree,
                                    }),
                            }
                        } else {
                            return self.mk_err(tcx.dcx().create_err(UnsizedPattern {
                                            span,
                                            non_sm_ty: *pointee_ty,
                                        }), ty);
                        }
                    }
                    ty::Float(flt) => {
                        let v = valtree.to_leaf();
                        let is_nan =
                            match flt {
                                ty::FloatTy::F16 => v.to_f16().is_nan(),
                                ty::FloatTy::F32 => v.to_f32().is_nan(),
                                ty::FloatTy::F64 => v.to_f64().is_nan(),
                                ty::FloatTy::F128 => v.to_f128().is_nan(),
                            };
                        if is_nan {
                            return self.mk_err(tcx.dcx().create_err(NaNPattern {
                                            span,
                                        }), ty);
                        } else { PatKind::Constant { value } }
                    }
                    ty::Pat(..) | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_)
                        | ty::RawPtr(..) => {
                        PatKind::Constant { value }
                    }
                    ty::FnPtr(..) => {
                        {
                            ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
                                    format_args!("Valtree construction would never succeed for FnPtr, so this is unreachable.")));
                        }
                    }
                    _ => {
                        let err =
                            InvalidPattern {
                                span,
                                non_sm_ty: ty,
                                prefix: ty.prefix_string(tcx).to_string(),
                            };
                        return self.mk_err(tcx.dcx().create_err(err), ty);
                    }
                };
            Box::new(Pat { span, ty, kind, extra: None })
        }
    }
}#[instrument(skip(self), level = "debug")]
228    fn valtree_to_pat(&self, value: ty::Value<'tcx>) -> Box<Pat<'tcx>> {
229        let span = self.span;
230        let tcx = self.tcx;
231        let ty::Value { ty, valtree } = value;
232
233        let kind = match ty.kind() {
234            // Extremely important check for all ADTs!
235            // Make sure they are eligible to be used in patterns, and if not, emit an error.
236            ty::Adt(adt_def, _) if !self.type_marked_structural(ty) => {
237                // This ADT cannot be used as a constant in patterns.
238                debug!(?adt_def, ?value.ty, "ADT type in pattern is not `type_marked_structural`");
239                let PartialEqImplStatus {
240                    is_derived, structural_partial_eq, non_blanket_impl, ..
241                } = type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
242                let (manual_partialeq_impl_span, manual_partialeq_impl_note) =
243                    match (structural_partial_eq, non_blanket_impl) {
244                        (true, _) => (None, false),
245                        (_, Some(def_id)) if def_id.is_local() && !is_derived => {
246                            (Some(tcx.def_span(def_id)), false)
247                        }
248                        _ => (None, true),
249                    };
250                let manual_partialeq_impl =
251                    manual_partialeq_impl_note || manual_partialeq_impl_span.is_some();
252                let is_local = adt_def.did().is_local();
253                let ty_def_span = tcx.def_span(adt_def.did());
254                let suggestion = if let Ok(name) = tcx.sess.source_map().span_to_snippet(self.span)
255                    && (is_local || manual_partialeq_impl)
256                {
257                    let mut hir_id = self.id;
258                    while let hir::Node::Pat(pat) = tcx.parent_hir_node(hir_id) {
259                        hir_id = pat.hir_id;
260                    }
261                    match tcx.parent_hir_node(hir_id) {
262                        hir::Node::Arm(hir::Arm { pat, guard: None, .. }) => {
263                            // Add an if condition to the match arm.
264                            Some(SuggestEq::AddIf {
265                                if_span: pat.span.shrink_to_hi(),
266                                pat_span: self.span,
267                                name,
268                                ty,
269                                manual_partialeq_impl,
270                            })
271                        }
272                        hir::Node::Arm(hir::Arm { guard: Some(guard), .. }) => {
273                            // Modify the the match arm if condition and add a check for equality.
274                            Some(SuggestEq::AddToIf {
275                                span: guard.span.shrink_to_hi(),
276                                pat_span: self.span,
277                                name,
278                                ty,
279                                manual_partialeq_impl,
280                            })
281                        }
282                        hir::Node::Expr(hir::Expr {
283                            kind: hir::ExprKind::Let(let_expr),
284                            span,
285                            ..
286                        }) => {
287                            if let_expr.pat.span == self.span {
288                                // `if let CONST = expr` -> `if CONST == expr`.
289                                Some(SuggestEq::ReplaceWithEq {
290                                    removal: span.until(self.span),
291                                    eq: self.span.between(let_expr.init.span),
292                                    ty,
293                                    manual_partialeq_impl,
294                                })
295                            } else if tcx.sess.edition().at_least_rust_2024() {
296                                // `if let Some(CONST) = expr` ->
297                                // `if let Some(binding) = expr && binding == CONST`.
298                                Some(SuggestEq::AddToLetChain {
299                                    span: span.shrink_to_hi(),
300                                    pat_span: self.span,
301                                    name,
302                                    ty,
303                                    manual_partialeq_impl,
304                                })
305                            } else {
306                                None
307                            }
308                        }
309                        hir::Node::LetStmt(let_stmt)
310                            if let Some(init) = let_stmt.init
311                                && let Some(els) = let_stmt.els
312                                && init.span.ctxt().is_root()
313                                && els.span.ctxt().is_root() =>
314                        {
315                            // `let PAT = expr else {` -> `if PAT == expr {`.
316                            Some(SuggestEq::ReplaceLetElseWithIf {
317                                if_span: let_stmt.span.until(let_stmt.pat.span),
318                                eq: let_stmt.pat.span.between(init.span),
319                                else_span: init.span.between(els.span),
320                                ty,
321                                manual_partialeq_impl,
322                            })
323                        }
324                        _ => None,
325                    }
326                } else {
327                    None
328                };
329                let err = TypeNotStructural {
330                    span,
331                    ty,
332                    ty_def_span,
333                    manual_partialeq_impl_span,
334                    manual_partialeq_impl_note,
335                    is_local,
336                    suggestion,
337                };
338                return self.mk_err(tcx.dcx().create_err(err), ty);
339            }
340            ty::Adt(adt_def, args) if adt_def.is_enum() => {
341                let (&variant_index, fields) = valtree.to_branch().split_first().unwrap();
342                let variant_index = VariantIdx::from_u32(variant_index.to_leaf().to_u32());
343                PatKind::Variant {
344                    adt_def: *adt_def,
345                    args,
346                    variant_index,
347                    subpatterns: self
348                        .lower_field_values_to_fieldpats(fields.iter().map(|ct| ct.to_value())),
349                }
350            }
351            ty::Adt(def, _) => {
352                assert!(!def.is_union()); // Valtree construction would never succeed for unions.
353                PatKind::Leaf {
354                    subpatterns: self.lower_field_values_to_fieldpats(
355                        valtree.to_branch().iter().map(|ct| ct.to_value()),
356                    ),
357                }
358            }
359            ty::Tuple(_) => PatKind::Leaf {
360                subpatterns: self.lower_field_values_to_fieldpats(
361                    valtree.to_branch().iter().map(|ct| ct.to_value()),
362                ),
363            },
364            ty::Slice(_) => PatKind::Slice {
365                prefix: valtree
366                    .to_branch()
367                    .iter()
368                    .map(|val| *self.valtree_to_pat(val.to_value()))
369                    .collect(),
370                slice: None,
371                suffix: Box::new([]),
372            },
373            ty::Array(_, _) => PatKind::Array {
374                prefix: valtree
375                    .to_branch()
376                    .iter()
377                    .map(|val| *self.valtree_to_pat(val.to_value()))
378                    .collect(),
379                slice: None,
380                suffix: Box::new([]),
381            },
382            ty::Str => {
383                // Constant/literal patterns of type `&str` are lowered to a
384                // `PatKind::Deref` wrapping a `PatKind::Constant` of type `str`.
385                // This pattern node is the `str` constant part.
386                //
387                // Under `feature(deref_patterns)`, string literal patterns can also
388                // have type `str` directly, without the `&`, in order to allow things
389                // like `deref!("...")` to work when the scrutinee is `String`.
390                PatKind::Constant { value }
391            }
392            ty::Ref(_, pointee_ty, ..) => {
393                if pointee_ty.is_str()
394                    || pointee_ty.is_slice()
395                    || pointee_ty.is_sized(tcx, self.typing_env)
396                {
397                    PatKind::Deref {
398                        // This node has type `ty::Ref`, so it's not a pin-deref.
399                        pin: hir::Pinnedness::Not,
400                        // Lower the valtree to a pattern as the pointee type.
401                        // This works because references have the same valtree
402                        // representation as their pointee.
403                        subpattern: self.valtree_to_pat(ty::Value { ty: *pointee_ty, valtree }),
404                    }
405                } else {
406                    return self.mk_err(
407                        tcx.dcx().create_err(UnsizedPattern { span, non_sm_ty: *pointee_ty }),
408                        ty,
409                    );
410                }
411            }
412            ty::Float(flt) => {
413                let v = valtree.to_leaf();
414                let is_nan = match flt {
415                    ty::FloatTy::F16 => v.to_f16().is_nan(),
416                    ty::FloatTy::F32 => v.to_f32().is_nan(),
417                    ty::FloatTy::F64 => v.to_f64().is_nan(),
418                    ty::FloatTy::F128 => v.to_f128().is_nan(),
419                };
420                if is_nan {
421                    // NaNs are not ever equal to anything so they make no sense as patterns.
422                    // Also see <https://github.com/rust-lang/rfcs/pull/3535>.
423                    return self.mk_err(tcx.dcx().create_err(NaNPattern { span }), ty);
424                } else {
425                    PatKind::Constant { value }
426                }
427            }
428            ty::Pat(..) | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => {
429                // The raw pointers we see here have been "vetted" by valtree construction to be
430                // just integers, so we simply allow them.
431                PatKind::Constant { value }
432            }
433            ty::FnPtr(..) => {
434                unreachable!(
435                    "Valtree construction would never succeed for FnPtr, so this is unreachable."
436                )
437            }
438            _ => {
439                let err = InvalidPattern {
440                    span,
441                    non_sm_ty: ty,
442                    prefix: ty.prefix_string(tcx).to_string(),
443                };
444                return self.mk_err(tcx.dcx().create_err(err), ty);
445            }
446        };
447
448        Box::new(Pat { span, ty, kind, extra: None })
449    }
450}
451
452/// Given a type with type parameters, visit every ADT looking for types that need to
453/// `#[derive(PartialEq)]` for it to be a structural type.
454fn extend_type_not_partial_eq<'tcx>(
455    tcx: TyCtxt<'tcx>,
456    typing_env: ty::TypingEnv<'tcx>,
457    ty: Ty<'tcx>,
458    err: &mut Diag<'_>,
459) {
460    /// Collect all types that need to be `StructuralPartialEq`.
461    struct UsedParamsNeedInstantiationVisitor<'tcx> {
462        tcx: TyCtxt<'tcx>,
463        typing_env: ty::TypingEnv<'tcx>,
464        /// The user has written `impl PartialEq for Ty` which means it's non-structural.
465        adts_with_manual_partialeq: FxHashSet<Span>,
466        /// The type has no `PartialEq` implementation, neither manual or derived.
467        adts_without_partialeq: FxHashSet<Span>,
468        /// The user has written `impl PartialEq for Ty` which means it's non-structural,
469        /// but we don't have a span to point at, so we'll just add them as a `note`.
470        manual: FxHashSet<Ty<'tcx>>,
471        /// The type has no `PartialEq` implementation, neither manual or derived, but
472        /// we don't have a span to point at, so we'll just add them as a `note`.
473        without: FxHashSet<Ty<'tcx>>,
474    }
475
476    impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UsedParamsNeedInstantiationVisitor<'tcx> {
477        type Result = ControlFlow<()>;
478        fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
479            match ty.kind() {
480                ty::Dynamic(..) => return ControlFlow::Break(()),
481                // Unsafe binders never implement `PartialEq`, so avoid walking into them
482                // which would require instantiating its binder with placeholders too.
483                ty::UnsafeBinder(..) => return ControlFlow::Break(()),
484                ty::FnPtr(..) => return ControlFlow::Continue(()),
485                ty::Adt(def, _args) => {
486                    let ty_def_id = def.did();
487                    let ty_def_span = self.tcx.def_span(ty_def_id);
488                    let PartialEqImplStatus {
489                        has_impl,
490                        is_derived,
491                        structural_partial_eq,
492                        non_blanket_impl,
493                    } = type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
494                    match (has_impl, is_derived, structural_partial_eq, non_blanket_impl) {
495                        (_, _, true, _) => {}
496                        (true, false, _, Some(def_id)) if def_id.is_local() => {
497                            self.adts_with_manual_partialeq.insert(self.tcx.def_span(def_id));
498                        }
499                        (true, false, _, _) if ty_def_id.is_local() => {
500                            self.adts_with_manual_partialeq.insert(ty_def_span);
501                        }
502                        (false, _, _, _) if ty_def_id.is_local() => {
503                            self.adts_without_partialeq.insert(ty_def_span);
504                        }
505                        (true, false, _, _) => {
506                            self.manual.insert(ty);
507                        }
508                        (false, _, _, _) => {
509                            self.without.insert(ty);
510                        }
511                        _ => {}
512                    };
513                    ty.super_visit_with(self)
514                }
515                _ => ty.super_visit_with(self),
516            }
517        }
518    }
519    let mut v = UsedParamsNeedInstantiationVisitor {
520        tcx,
521        typing_env,
522        adts_with_manual_partialeq: FxHashSet::default(),
523        adts_without_partialeq: FxHashSet::default(),
524        manual: FxHashSet::default(),
525        without: FxHashSet::default(),
526    };
527    if v.visit_ty(ty).is_break() {
528        return;
529    }
530    #[allow(rustc::potential_query_instability)] // Span labels will be sorted by the rendering
531    for span in v.adts_with_manual_partialeq {
532        err.span_note(span, "the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details");
533    }
534    #[allow(rustc::potential_query_instability)] // Span labels will be sorted by the rendering
535    for span in v.adts_without_partialeq {
536        err.span_label(
537            span,
538            "must be annotated with `#[derive(PartialEq)]` to be usable in patterns",
539        );
540    }
541    #[allow(rustc::potential_query_instability)]
542    let mut manual: Vec<_> = v.manual.into_iter().map(|t| t.to_string()).collect();
543    manual.sort();
544    for ty in manual {
545        err.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details",
                ty))
    })format!(
546            "`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details"
547        ));
548    }
549    #[allow(rustc::potential_query_instability)]
550    let mut without: Vec<_> = v.without.into_iter().map(|t| t.to_string()).collect();
551    without.sort();
552    for ty in without {
553        err.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns",
                ty))
    })format!(
554            "`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns"
555        ));
556    }
557}
558
559#[derive(#[automatically_derived]
impl ::core::fmt::Debug for PartialEqImplStatus {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field4_finish(f,
            "PartialEqImplStatus", "has_impl", &self.has_impl, "is_derived",
            &self.is_derived, "structural_partial_eq",
            &self.structural_partial_eq, "non_blanket_impl",
            &&self.non_blanket_impl)
    }
}Debug)]
560struct PartialEqImplStatus {
561    has_impl: bool,
562    is_derived: bool,
563    structural_partial_eq: bool,
564    non_blanket_impl: Option<DefId>,
565}
566
567x;#[instrument(level = "trace", skip(tcx), ret)]
568fn type_has_partial_eq_impl<'tcx>(
569    tcx: TyCtxt<'tcx>,
570    typing_env: ty::TypingEnv<'tcx>,
571    ty: Ty<'tcx>,
572) -> PartialEqImplStatus {
573    let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
574    // double-check there even *is* a semantic `PartialEq` to dispatch to.
575    //
576    // (If there isn't, then we can safely issue a hard
577    // error, because that's never worked, due to compiler
578    // using `PartialEq::eq` in this scenario in the past.)
579    let partial_eq_trait_id = tcx.require_lang_item(hir::LangItem::PartialEq, DUMMY_SP);
580    let structural_partial_eq_trait_id =
581        tcx.require_lang_item(hir::LangItem::StructuralPeq, DUMMY_SP);
582
583    let partial_eq_obligation = Obligation::new(
584        tcx,
585        ObligationCause::dummy(),
586        param_env,
587        ty::TraitRef::new(tcx, partial_eq_trait_id, [ty, ty]),
588    );
589
590    let mut automatically_derived = false;
591    let mut structural_peq = false;
592    let mut impl_def_id = None;
593    for def_id in tcx.non_blanket_impls_for_ty(partial_eq_trait_id, ty) {
594        automatically_derived = find_attr!(tcx, def_id, AutomaticallyDerived);
595        impl_def_id = Some(def_id);
596    }
597    for _ in tcx.non_blanket_impls_for_ty(structural_partial_eq_trait_id, ty) {
598        structural_peq = true;
599    }
600    // This *could* accept a type that isn't actually `PartialEq`, because region bounds get
601    // ignored. However that should be pretty much impossible since consts that do not depend on
602    // generics can only mention the `'static` lifetime, and how would one have a type that's
603    // `PartialEq` for some lifetime but *not* for `'static`? If this ever becomes a problem
604    // we'll need to leave some sort of trace of this requirement in the MIR so that borrowck
605    // can ensure that the type really implements `PartialEq`.
606    // We also do *not* require `const PartialEq`, not even in `const fn`. This violates the model
607    // that patterns can only do things that the code could also do without patterns, but it is
608    // needed for backwards compatibility. The actual pattern matching compares primitive values,
609    // `PartialEq::eq` never gets invoked, so there's no risk of us running non-const code.
610    PartialEqImplStatus {
611        has_impl: infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation),
612        is_derived: automatically_derived,
613        structural_partial_eq: structural_peq,
614        non_blanket_impl: impl_def_id,
615    }
616}