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_index::Idx;
9use rustc_infer::infer::TyCtxtInferExt;
10use rustc_infer::traits::Obligation;
11use rustc_middle::mir::interpret::ErrorHandled;
12use rustc_middle::thir::{FieldPat, Pat, PatKind};
13use rustc_middle::ty::{
14 self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitableExt, TypeVisitor, ValTree,
15};
16use rustc_middle::{mir, span_bug};
17use rustc_span::def_id::DefId;
18use rustc_span::{DUMMY_SP, Span, sym};
19use rustc_trait_selection::traits::ObligationCause;
20use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
21use tracing::{debug, instrument, trace};
22
23use super::PatCtxt;
24use crate::errors::{
25 ConstPatternDependsOnGenericParameter, CouldNotEvalConstPattern, InvalidPattern, NaNPattern,
26 PointerPattern, TypeNotPartialEq, TypeNotStructural, UnionPattern, UnsizedPattern,
27};
28
29impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
30 #[instrument(level = "debug", skip(self), ret)]
38 pub(super) fn const_to_pat(
39 &self,
40 c: ty::Const<'tcx>,
41 ty: Ty<'tcx>,
42 id: hir::HirId,
43 span: Span,
44 ) -> Box<Pat<'tcx>> {
45 let mut convert = ConstToPat::new(self, id, span, c);
46
47 match c.kind() {
48 ty::ConstKind::Unevaluated(uv) => convert.unevaluated_to_pat(uv, ty),
49 ty::ConstKind::Value(cv) => convert.valtree_to_pat(cv.valtree, cv.ty),
50 _ => span_bug!(span, "Invalid `ConstKind` for `const_to_pat`: {:?}", c),
51 }
52 }
53}
54
55struct ConstToPat<'tcx> {
56 tcx: TyCtxt<'tcx>,
57 typing_env: ty::TypingEnv<'tcx>,
58 span: Span,
59 id: hir::HirId,
60
61 c: ty::Const<'tcx>,
62}
63
64impl<'tcx> ConstToPat<'tcx> {
65 fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self {
66 trace!(?pat_ctxt.typeck_results.hir_owner);
67 ConstToPat { tcx: pat_ctxt.tcx, typing_env: pat_ctxt.typing_env, span, id, c }
68 }
69
70 fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
71 ty.is_structural_eq_shallow(self.tcx)
72 }
73
74 fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
76 if let ty::ConstKind::Unevaluated(uv) = self.c.kind() {
77 let def_kind = self.tcx.def_kind(uv.def);
78 if let hir::def::DefKind::AssocConst = def_kind
79 && let Some(def_id) = uv.def.as_local()
80 {
81 err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), "");
83 }
84 if let hir::def::DefKind::Const | hir::def::DefKind::AssocConst = def_kind {
85 err.span_label(
86 self.tcx.def_span(uv.def),
87 crate::fluent_generated::mir_build_const_defined_here,
88 );
89 }
90 }
91 Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()) })
92 }
93
94 fn unevaluated_to_pat(
95 &mut self,
96 uv: ty::UnevaluatedConst<'tcx>,
97 ty: Ty<'tcx>,
98 ) -> Box<Pat<'tcx>> {
99 let typing_env =
107 self.tcx.erase_regions(self.typing_env).with_post_analysis_normalized(self.tcx);
108 let uv = self.tcx.erase_regions(uv);
109
110 let valtree = match self.tcx.const_eval_resolve_for_typeck(typing_env, uv, self.span) {
113 Ok(Ok(c)) => c,
114 Err(ErrorHandled::Reported(_, _)) => {
115 let mut err =
117 self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span });
118 if let ty::ConstKind::Unevaluated(uv) = self.c.kind()
121 && let hir::def::DefKind::Const | hir::def::DefKind::AssocConst =
122 self.tcx.def_kind(uv.def)
123 {
124 err.downgrade_to_delayed_bug();
125 }
126 return self.mk_err(err, ty);
127 }
128 Err(ErrorHandled::TooGeneric(_)) => {
129 let mut e = self
130 .tcx
131 .dcx()
132 .create_err(ConstPatternDependsOnGenericParameter { span: self.span });
133 for arg in uv.args {
134 if let ty::GenericArgKind::Type(ty) = arg.kind()
135 && let ty::Param(param_ty) = ty.kind()
136 {
137 let def_id = self.tcx.hir_enclosing_body_owner(self.id);
138 let generics = self.tcx.generics_of(def_id);
139 let param = generics.type_param(*param_ty, self.tcx);
140 let span = self.tcx.def_span(param.def_id);
141 e.span_label(span, "constant depends on this generic parameter");
142 if let Some(ident) = self.tcx.def_ident_span(def_id)
143 && self.tcx.sess.source_map().is_multiline(ident.between(span))
144 {
145 e.span_label(ident, "");
148 }
149 }
150 }
151 return self.mk_err(e, ty);
152 }
153 Ok(Err(bad_ty)) => {
154 let e = match bad_ty.kind() {
156 ty::Adt(def, ..) => {
157 assert!(def.is_union());
158 self.tcx.dcx().create_err(UnionPattern { span: self.span })
159 }
160 ty::FnPtr(..) | ty::RawPtr(..) => {
161 self.tcx.dcx().create_err(PointerPattern { span: self.span })
162 }
163 _ => self.tcx.dcx().create_err(InvalidPattern {
164 span: self.span,
165 non_sm_ty: bad_ty,
166 prefix: bad_ty.prefix_string(self.tcx).to_string(),
167 }),
168 };
169 return self.mk_err(e, ty);
170 }
171 };
172
173 let inlined_const_as_pat = self.valtree_to_pat(valtree, ty);
175
176 if !inlined_const_as_pat.references_error() {
177 if !type_has_partial_eq_impl(self.tcx, typing_env, ty).has_impl {
179 let mut err = self.tcx.dcx().create_err(TypeNotPartialEq { span: self.span, ty });
180 extend_type_not_partial_eq(self.tcx, typing_env, ty, &mut err);
181 return self.mk_err(err, ty);
182 }
183 }
184
185 let kind = PatKind::ExpandedConstant { subpattern: inlined_const_as_pat, def_id: uv.def };
188 Box::new(Pat { kind, ty, span: self.span })
189 }
190
191 fn field_pats(
192 &self,
193 vals: impl Iterator<Item = (ValTree<'tcx>, Ty<'tcx>)>,
194 ) -> Vec<FieldPat<'tcx>> {
195 vals.enumerate()
196 .map(|(idx, (val, ty))| {
197 let field = FieldIdx::new(idx);
198 let ty = self.tcx.normalize_erasing_regions(self.typing_env, ty);
200 FieldPat { field, pattern: *self.valtree_to_pat(val, ty) }
201 })
202 .collect()
203 }
204
205 #[instrument(skip(self), level = "debug")]
208 fn valtree_to_pat(&self, cv: ValTree<'tcx>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
209 let span = self.span;
210 let tcx = self.tcx;
211 let kind = match ty.kind() {
212 ty::Adt(adt_def, _) if !self.type_marked_structural(ty) => {
213 debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty);
216 let PartialEqImplStatus {
217 is_derived, structural_partial_eq, non_blanket_impl, ..
218 } = type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
219 let (manual_partialeq_impl_span, manual_partialeq_impl_note) =
220 match (structural_partial_eq, non_blanket_impl) {
221 (true, _) => (None, false),
222 (_, Some(def_id)) if def_id.is_local() && !is_derived => {
223 (Some(tcx.def_span(def_id)), false)
224 }
225 _ => (None, true),
226 };
227 let ty_def_span = tcx.def_span(adt_def.did());
228 let err = TypeNotStructural {
229 span,
230 ty,
231 ty_def_span,
232 manual_partialeq_impl_span,
233 manual_partialeq_impl_note,
234 };
235 return self.mk_err(tcx.dcx().create_err(err), ty);
236 }
237 ty::Adt(adt_def, args) if adt_def.is_enum() => {
238 let (&variant_index, fields) = cv.unwrap_branch().split_first().unwrap();
239 let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().to_u32());
240 PatKind::Variant {
241 adt_def: *adt_def,
242 args,
243 variant_index,
244 subpatterns: self.field_pats(
245 fields.iter().copied().zip(
246 adt_def.variants()[variant_index]
247 .fields
248 .iter()
249 .map(|field| field.ty(tcx, args)),
250 ),
251 ),
252 }
253 }
254 ty::Adt(def, args) => {
255 assert!(!def.is_union()); PatKind::Leaf {
257 subpatterns: self.field_pats(cv.unwrap_branch().iter().copied().zip(
258 def.non_enum_variant().fields.iter().map(|field| field.ty(tcx, args)),
259 )),
260 }
261 }
262 ty::Tuple(fields) => PatKind::Leaf {
263 subpatterns: self.field_pats(cv.unwrap_branch().iter().copied().zip(fields.iter())),
264 },
265 ty::Slice(elem_ty) => PatKind::Slice {
266 prefix: cv
267 .unwrap_branch()
268 .iter()
269 .map(|val| *self.valtree_to_pat(*val, *elem_ty))
270 .collect(),
271 slice: None,
272 suffix: Box::new([]),
273 },
274 ty::Array(elem_ty, _) => PatKind::Array {
275 prefix: cv
276 .unwrap_branch()
277 .iter()
278 .map(|val| *self.valtree_to_pat(*val, *elem_ty))
279 .collect(),
280 slice: None,
281 suffix: Box::new([]),
282 },
283 ty::Str => {
284 let ref_str_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty);
289 PatKind::Constant {
290 value: mir::Const::Ty(ref_str_ty, ty::Const::new_value(tcx, cv, ref_str_ty)),
291 }
292 }
293 ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() {
294 ty::Str => PatKind::Constant {
297 value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)),
298 },
299 _ => {
303 if !pointee_ty.is_sized(tcx, self.typing_env) && !pointee_ty.is_slice() {
304 return self.mk_err(
305 tcx.dcx().create_err(UnsizedPattern { span, non_sm_ty: *pointee_ty }),
306 ty,
307 );
308 } else {
309 PatKind::Deref { subpattern: self.valtree_to_pat(cv, *pointee_ty) }
311 }
312 }
313 },
314 ty::Float(flt) => {
315 let v = cv.unwrap_leaf();
316 let is_nan = match flt {
317 ty::FloatTy::F16 => v.to_f16().is_nan(),
318 ty::FloatTy::F32 => v.to_f32().is_nan(),
319 ty::FloatTy::F64 => v.to_f64().is_nan(),
320 ty::FloatTy::F128 => v.to_f128().is_nan(),
321 };
322 if is_nan {
323 return self.mk_err(tcx.dcx().create_err(NaNPattern { span }), ty);
326 } else {
327 PatKind::Constant {
328 value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)),
329 }
330 }
331 }
332 ty::Pat(..) | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => {
333 PatKind::Constant { value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)) }
336 }
337 ty::FnPtr(..) => {
338 unreachable!(
339 "Valtree construction would never succeed for FnPtr, so this is unreachable."
340 )
341 }
342 _ => {
343 let err = InvalidPattern {
344 span,
345 non_sm_ty: ty,
346 prefix: ty.prefix_string(tcx).to_string(),
347 };
348 return self.mk_err(tcx.dcx().create_err(err), ty);
349 }
350 };
351
352 Box::new(Pat { span, ty, kind })
353 }
354}
355
356fn extend_type_not_partial_eq<'tcx>(
359 tcx: TyCtxt<'tcx>,
360 typing_env: ty::TypingEnv<'tcx>,
361 ty: Ty<'tcx>,
362 err: &mut Diag<'_>,
363) {
364 struct UsedParamsNeedInstantiationVisitor<'tcx> {
366 tcx: TyCtxt<'tcx>,
367 typing_env: ty::TypingEnv<'tcx>,
368 adts_with_manual_partialeq: FxHashSet<Span>,
370 adts_without_partialeq: FxHashSet<Span>,
372 manual: FxHashSet<Ty<'tcx>>,
375 without: FxHashSet<Ty<'tcx>>,
378 }
379
380 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UsedParamsNeedInstantiationVisitor<'tcx> {
381 type Result = ControlFlow<()>;
382 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
383 match ty.kind() {
384 ty::Dynamic(..) => return ControlFlow::Break(()),
385 ty::UnsafeBinder(..) => return ControlFlow::Break(()),
388 ty::FnPtr(..) => return ControlFlow::Continue(()),
389 ty::Adt(def, _args) => {
390 let ty_def_id = def.did();
391 let ty_def_span = self.tcx.def_span(ty_def_id);
392 let PartialEqImplStatus {
393 has_impl,
394 is_derived,
395 structural_partial_eq,
396 non_blanket_impl,
397 } = type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
398 match (has_impl, is_derived, structural_partial_eq, non_blanket_impl) {
399 (_, _, true, _) => {}
400 (true, false, _, Some(def_id)) if def_id.is_local() => {
401 self.adts_with_manual_partialeq.insert(self.tcx.def_span(def_id));
402 }
403 (true, false, _, _) if ty_def_id.is_local() => {
404 self.adts_with_manual_partialeq.insert(ty_def_span);
405 }
406 (false, _, _, _) if ty_def_id.is_local() => {
407 self.adts_without_partialeq.insert(ty_def_span);
408 }
409 (true, false, _, _) => {
410 self.manual.insert(ty);
411 }
412 (false, _, _, _) => {
413 self.without.insert(ty);
414 }
415 _ => {}
416 };
417 ty.super_visit_with(self)
418 }
419 _ => ty.super_visit_with(self),
420 }
421 }
422 }
423 let mut v = UsedParamsNeedInstantiationVisitor {
424 tcx,
425 typing_env,
426 adts_with_manual_partialeq: FxHashSet::default(),
427 adts_without_partialeq: FxHashSet::default(),
428 manual: FxHashSet::default(),
429 without: FxHashSet::default(),
430 };
431 if v.visit_ty(ty).is_break() {
432 return;
433 }
434 #[allow(rustc::potential_query_instability)] for span in v.adts_with_manual_partialeq {
436 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");
437 }
438 #[allow(rustc::potential_query_instability)] for span in v.adts_without_partialeq {
440 err.span_label(
441 span,
442 "must be annotated with `#[derive(PartialEq)]` to be usable in patterns",
443 );
444 }
445 #[allow(rustc::potential_query_instability)]
446 let mut manual: Vec<_> = v.manual.into_iter().map(|t| t.to_string()).collect();
447 manual.sort();
448 for ty in manual {
449 err.note(format!(
450 "`{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"
451 ));
452 }
453 #[allow(rustc::potential_query_instability)]
454 let mut without: Vec<_> = v.without.into_iter().map(|t| t.to_string()).collect();
455 without.sort();
456 for ty in without {
457 err.note(format!(
458 "`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns"
459 ));
460 }
461}
462
463#[derive(Debug)]
464struct PartialEqImplStatus {
465 has_impl: bool,
466 is_derived: bool,
467 structural_partial_eq: bool,
468 non_blanket_impl: Option<DefId>,
469}
470
471#[instrument(level = "trace", skip(tcx), ret)]
472fn type_has_partial_eq_impl<'tcx>(
473 tcx: TyCtxt<'tcx>,
474 typing_env: ty::TypingEnv<'tcx>,
475 ty: Ty<'tcx>,
476) -> PartialEqImplStatus {
477 let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
478 let partial_eq_trait_id = tcx.require_lang_item(hir::LangItem::PartialEq, DUMMY_SP);
484 let structural_partial_eq_trait_id =
485 tcx.require_lang_item(hir::LangItem::StructuralPeq, DUMMY_SP);
486
487 let partial_eq_obligation = Obligation::new(
488 tcx,
489 ObligationCause::dummy(),
490 param_env,
491 ty::TraitRef::new(tcx, partial_eq_trait_id, [ty, ty]),
492 );
493
494 let mut automatically_derived = false;
495 let mut structural_peq = false;
496 let mut impl_def_id = None;
497 for def_id in tcx.non_blanket_impls_for_ty(partial_eq_trait_id, ty) {
498 automatically_derived = tcx.has_attr(def_id, sym::automatically_derived);
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 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}