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