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