1use core::ops::ControlFlow;
2
3use rustc_abi::{FieldIdx, VariantIdx};
4use rustc_apfloat::Float;
5use rustc_attr_data_structures::{AttributeKind, find_attr};
6use rustc_data_structures::fx::FxHashSet;
7use rustc_errors::Diag;
8use rustc_hir as hir;
9use rustc_index::Idx;
10use rustc_infer::infer::TyCtxtInferExt;
11use rustc_infer::traits::Obligation;
12use rustc_middle::mir::interpret::ErrorHandled;
13use rustc_middle::thir::{FieldPat, Pat, PatKind};
14use rustc_middle::ty::{
15 self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitableExt, TypeVisitor, ValTree,
16};
17use rustc_middle::{mir, span_bug};
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::errors::{
26 ConstPatternDependsOnGenericParameter, CouldNotEvalConstPattern, InvalidPattern, NaNPattern,
27 PointerPattern, TypeNotPartialEq, TypeNotStructural, UnionPattern, UnsizedPattern,
28};
29
30impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
31 #[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(cv) => convert.valtree_to_pat(cv.valtree, cv.ty),
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 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 fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
77 if let ty::ConstKind::Unevaluated(uv) = self.c.kind() {
78 let def_kind = self.tcx.def_kind(uv.def);
79 if let hir::def::DefKind::AssocConst = def_kind
80 && let Some(def_id) = uv.def.as_local()
81 {
82 err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), "");
84 }
85 if let hir::def::DefKind::Const | hir::def::DefKind::AssocConst = def_kind {
86 err.span_label(
87 self.tcx.def_span(uv.def),
88 crate::fluent_generated::mir_build_const_defined_here,
89 );
90 }
91 }
92 Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()) })
93 }
94
95 fn unevaluated_to_pat(
96 &mut self,
97 uv: ty::UnevaluatedConst<'tcx>,
98 ty: Ty<'tcx>,
99 ) -> Box<Pat<'tcx>> {
100 let typing_env =
108 self.tcx.erase_regions(self.typing_env).with_post_analysis_normalized(self.tcx);
109 let uv = self.tcx.erase_regions(uv);
110
111 let valtree = match self.tcx.const_eval_resolve_for_typeck(typing_env, uv, self.span) {
114 Ok(Ok(c)) => c,
115 Err(ErrorHandled::Reported(_, _)) => {
116 let mut err =
118 self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span });
119 if let ty::ConstKind::Unevaluated(uv) = self.c.kind()
122 && let hir::def::DefKind::Const | hir::def::DefKind::AssocConst =
123 self.tcx.def_kind(uv.def)
124 {
125 err.downgrade_to_delayed_bug();
126 }
127 return self.mk_err(err, ty);
128 }
129 Err(ErrorHandled::TooGeneric(_)) => {
130 let mut e = self
131 .tcx
132 .dcx()
133 .create_err(ConstPatternDependsOnGenericParameter { span: self.span });
134 for arg in uv.args {
135 if let ty::GenericArgKind::Type(ty) = arg.kind()
136 && let ty::Param(param_ty) = ty.kind()
137 {
138 let def_id = self.tcx.hir_enclosing_body_owner(self.id);
139 let generics = self.tcx.generics_of(def_id);
140 let param = generics.type_param(*param_ty, self.tcx);
141 let span = self.tcx.def_span(param.def_id);
142 e.span_label(span, "constant depends on this generic parameter");
143 if let Some(ident) = self.tcx.def_ident_span(def_id)
144 && self.tcx.sess.source_map().is_multiline(ident.between(span))
145 {
146 e.span_label(ident, "");
149 }
150 }
151 }
152 return self.mk_err(e, ty);
153 }
154 Ok(Err(bad_ty)) => {
155 let e = match bad_ty.kind() {
157 ty::Adt(def, ..) => {
158 assert!(def.is_union());
159 self.tcx.dcx().create_err(UnionPattern { span: self.span })
160 }
161 ty::FnPtr(..) | ty::RawPtr(..) => {
162 self.tcx.dcx().create_err(PointerPattern { span: self.span })
163 }
164 _ => self.tcx.dcx().create_err(InvalidPattern {
165 span: self.span,
166 non_sm_ty: bad_ty,
167 prefix: bad_ty.prefix_string(self.tcx).to_string(),
168 }),
169 };
170 return self.mk_err(e, ty);
171 }
172 };
173
174 let inlined_const_as_pat = self.valtree_to_pat(valtree, ty);
176
177 if !inlined_const_as_pat.references_error() {
178 if !type_has_partial_eq_impl(self.tcx, typing_env, ty).has_impl {
180 let mut err = self.tcx.dcx().create_err(TypeNotPartialEq { span: self.span, ty });
181 extend_type_not_partial_eq(self.tcx, typing_env, ty, &mut err);
182 return self.mk_err(err, ty);
183 }
184 }
185
186 let kind = PatKind::ExpandedConstant { subpattern: inlined_const_as_pat, def_id: uv.def };
189 Box::new(Pat { kind, ty, span: self.span })
190 }
191
192 fn field_pats(
193 &self,
194 vals: impl Iterator<Item = (ValTree<'tcx>, Ty<'tcx>)>,
195 ) -> Vec<FieldPat<'tcx>> {
196 vals.enumerate()
197 .map(|(idx, (val, ty))| {
198 let field = FieldIdx::new(idx);
199 let ty = self.tcx.normalize_erasing_regions(self.typing_env, ty);
201 FieldPat { field, pattern: *self.valtree_to_pat(val, ty) }
202 })
203 .collect()
204 }
205
206 #[instrument(skip(self), level = "debug")]
209 fn valtree_to_pat(&self, cv: ValTree<'tcx>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
210 let span = self.span;
211 let tcx = self.tcx;
212 let kind = match ty.kind() {
213 ty::Adt(adt_def, _) if !self.type_marked_structural(ty) => {
214 debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty);
217 let PartialEqImplStatus {
218 is_derived, structural_partial_eq, non_blanket_impl, ..
219 } = type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
220 let (manual_partialeq_impl_span, manual_partialeq_impl_note) =
221 match (structural_partial_eq, non_blanket_impl) {
222 (true, _) => (None, false),
223 (_, Some(def_id)) if def_id.is_local() && !is_derived => {
224 (Some(tcx.def_span(def_id)), false)
225 }
226 _ => (None, true),
227 };
228 let ty_def_span = tcx.def_span(adt_def.did());
229 let err = TypeNotStructural {
230 span,
231 ty,
232 ty_def_span,
233 manual_partialeq_impl_span,
234 manual_partialeq_impl_note,
235 };
236 return self.mk_err(tcx.dcx().create_err(err), ty);
237 }
238 ty::Adt(adt_def, args) if adt_def.is_enum() => {
239 let (&variant_index, fields) = cv.unwrap_branch().split_first().unwrap();
240 let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().to_u32());
241 PatKind::Variant {
242 adt_def: *adt_def,
243 args,
244 variant_index,
245 subpatterns: self.field_pats(
246 fields.iter().copied().zip(
247 adt_def.variants()[variant_index]
248 .fields
249 .iter()
250 .map(|field| field.ty(tcx, args)),
251 ),
252 ),
253 }
254 }
255 ty::Adt(def, args) => {
256 assert!(!def.is_union()); PatKind::Leaf {
258 subpatterns: self.field_pats(cv.unwrap_branch().iter().copied().zip(
259 def.non_enum_variant().fields.iter().map(|field| field.ty(tcx, args)),
260 )),
261 }
262 }
263 ty::Tuple(fields) => PatKind::Leaf {
264 subpatterns: self.field_pats(cv.unwrap_branch().iter().copied().zip(fields.iter())),
265 },
266 ty::Slice(elem_ty) => PatKind::Slice {
267 prefix: cv
268 .unwrap_branch()
269 .iter()
270 .map(|val| *self.valtree_to_pat(*val, *elem_ty))
271 .collect(),
272 slice: None,
273 suffix: Box::new([]),
274 },
275 ty::Array(elem_ty, _) => PatKind::Array {
276 prefix: cv
277 .unwrap_branch()
278 .iter()
279 .map(|val| *self.valtree_to_pat(*val, *elem_ty))
280 .collect(),
281 slice: None,
282 suffix: Box::new([]),
283 },
284 ty::Str => {
285 let ref_str_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty);
290 PatKind::Constant {
291 value: mir::Const::Ty(ref_str_ty, ty::Const::new_value(tcx, cv, ref_str_ty)),
292 }
293 }
294 ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() {
295 ty::Str => PatKind::Constant {
298 value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)),
299 },
300 _ => {
304 if !pointee_ty.is_sized(tcx, self.typing_env) && !pointee_ty.is_slice() {
305 return self.mk_err(
306 tcx.dcx().create_err(UnsizedPattern { span, non_sm_ty: *pointee_ty }),
307 ty,
308 );
309 } else {
310 PatKind::Deref { subpattern: self.valtree_to_pat(cv, *pointee_ty) }
312 }
313 }
314 },
315 ty::Float(flt) => {
316 let v = cv.unwrap_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 return self.mk_err(tcx.dcx().create_err(NaNPattern { span }), ty);
327 } else {
328 PatKind::Constant {
329 value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)),
330 }
331 }
332 }
333 ty::Pat(..) | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => {
334 PatKind::Constant { value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)) }
337 }
338 ty::FnPtr(..) => {
339 unreachable!(
340 "Valtree construction would never succeed for FnPtr, so this is unreachable."
341 )
342 }
343 _ => {
344 let err = InvalidPattern {
345 span,
346 non_sm_ty: ty,
347 prefix: ty.prefix_string(tcx).to_string(),
348 };
349 return self.mk_err(tcx.dcx().create_err(err), ty);
350 }
351 };
352
353 Box::new(Pat { span, ty, kind })
354 }
355}
356
357fn extend_type_not_partial_eq<'tcx>(
360 tcx: TyCtxt<'tcx>,
361 typing_env: ty::TypingEnv<'tcx>,
362 ty: Ty<'tcx>,
363 err: &mut Diag<'_>,
364) {
365 struct UsedParamsNeedInstantiationVisitor<'tcx> {
367 tcx: TyCtxt<'tcx>,
368 typing_env: ty::TypingEnv<'tcx>,
369 adts_with_manual_partialeq: FxHashSet<Span>,
371 adts_without_partialeq: FxHashSet<Span>,
373 manual: FxHashSet<Ty<'tcx>>,
376 without: FxHashSet<Ty<'tcx>>,
379 }
380
381 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UsedParamsNeedInstantiationVisitor<'tcx> {
382 type Result = ControlFlow<()>;
383 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
384 match ty.kind() {
385 ty::Dynamic(..) => return ControlFlow::Break(()),
386 ty::UnsafeBinder(..) => return ControlFlow::Break(()),
389 ty::FnPtr(..) => return ControlFlow::Continue(()),
390 ty::Adt(def, _args) => {
391 let ty_def_id = def.did();
392 let ty_def_span = self.tcx.def_span(ty_def_id);
393 let PartialEqImplStatus {
394 has_impl,
395 is_derived,
396 structural_partial_eq,
397 non_blanket_impl,
398 } = type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
399 match (has_impl, is_derived, structural_partial_eq, non_blanket_impl) {
400 (_, _, true, _) => {}
401 (true, false, _, Some(def_id)) if def_id.is_local() => {
402 self.adts_with_manual_partialeq.insert(self.tcx.def_span(def_id));
403 }
404 (true, false, _, _) if ty_def_id.is_local() => {
405 self.adts_with_manual_partialeq.insert(ty_def_span);
406 }
407 (false, _, _, _) if ty_def_id.is_local() => {
408 self.adts_without_partialeq.insert(ty_def_span);
409 }
410 (true, false, _, _) => {
411 self.manual.insert(ty);
412 }
413 (false, _, _, _) => {
414 self.without.insert(ty);
415 }
416 _ => {}
417 };
418 ty.super_visit_with(self)
419 }
420 _ => ty.super_visit_with(self),
421 }
422 }
423 }
424 let mut v = UsedParamsNeedInstantiationVisitor {
425 tcx,
426 typing_env,
427 adts_with_manual_partialeq: FxHashSet::default(),
428 adts_without_partialeq: FxHashSet::default(),
429 manual: FxHashSet::default(),
430 without: FxHashSet::default(),
431 };
432 if v.visit_ty(ty).is_break() {
433 return;
434 }
435 #[allow(rustc::potential_query_instability)] for span in v.adts_with_manual_partialeq {
437 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");
438 }
439 #[allow(rustc::potential_query_instability)] for span in v.adts_without_partialeq {
441 err.span_label(
442 span,
443 "must be annotated with `#[derive(PartialEq)]` to be usable in patterns",
444 );
445 }
446 #[allow(rustc::potential_query_instability)]
447 let mut manual: Vec<_> = v.manual.into_iter().map(|t| t.to_string()).collect();
448 manual.sort();
449 for ty in manual {
450 err.note(format!(
451 "`{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"
452 ));
453 }
454 #[allow(rustc::potential_query_instability)]
455 let mut without: Vec<_> = v.without.into_iter().map(|t| t.to_string()).collect();
456 without.sort();
457 for ty in without {
458 err.note(format!(
459 "`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns"
460 ));
461 }
462}
463
464#[derive(Debug)]
465struct PartialEqImplStatus {
466 has_impl: bool,
467 is_derived: bool,
468 structural_partial_eq: bool,
469 non_blanket_impl: Option<DefId>,
470}
471
472#[instrument(level = "trace", skip(tcx), ret)]
473fn type_has_partial_eq_impl<'tcx>(
474 tcx: TyCtxt<'tcx>,
475 typing_env: ty::TypingEnv<'tcx>,
476 ty: Ty<'tcx>,
477) -> PartialEqImplStatus {
478 let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
479 let partial_eq_trait_id = tcx.require_lang_item(hir::LangItem::PartialEq, DUMMY_SP);
485 let structural_partial_eq_trait_id =
486 tcx.require_lang_item(hir::LangItem::StructuralPeq, DUMMY_SP);
487
488 let partial_eq_obligation = Obligation::new(
489 tcx,
490 ObligationCause::dummy(),
491 param_env,
492 ty::TraitRef::new(tcx, partial_eq_trait_id, [ty, ty]),
493 );
494
495 let mut automatically_derived = false;
496 let mut structural_peq = false;
497 let mut impl_def_id = None;
498 for def_id in tcx.non_blanket_impls_for_ty(partial_eq_trait_id, ty) {
499 automatically_derived =
500 find_attr!(tcx.get_all_attrs(def_id), AttributeKind::AutomaticallyDerived(..));
501 impl_def_id = Some(def_id);
502 }
503 for _ in tcx.non_blanket_impls_for_ty(structural_partial_eq_trait_id, ty) {
504 structural_peq = true;
505 }
506 PartialEqImplStatus {
517 has_impl: infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation),
518 is_derived: automatically_derived,
519 structural_partial_eq: structural_peq,
520 non_blanket_impl: impl_def_id,
521 }
522}