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::{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 treat_byte_string_as_slice: bool,
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 {
70 tcx: pat_ctxt.tcx,
71 typing_env: pat_ctxt.typing_env,
72 span,
73 id,
74 treat_byte_string_as_slice: pat_ctxt
75 .typeck_results
76 .treat_byte_string_as_slice
77 .contains(&id.local_id),
78 c,
79 }
80 }
81
82 fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
83 ty.is_structural_eq_shallow(self.tcx)
84 }
85
86 fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
88 if let ty::ConstKind::Unevaluated(uv) = self.c.kind() {
89 let def_kind = self.tcx.def_kind(uv.def);
90 if let hir::def::DefKind::AssocConst = def_kind
91 && let Some(def_id) = uv.def.as_local()
92 {
93 err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), "");
95 }
96 if let hir::def::DefKind::Const | hir::def::DefKind::AssocConst = def_kind {
97 err.span_label(
98 self.tcx.def_span(uv.def),
99 crate::fluent_generated::mir_build_const_defined_here,
100 );
101 }
102 }
103 Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()) })
104 }
105
106 fn unevaluated_to_pat(
107 &mut self,
108 uv: ty::UnevaluatedConst<'tcx>,
109 ty: Ty<'tcx>,
110 ) -> Box<Pat<'tcx>> {
111 trace!(self.treat_byte_string_as_slice);
112
113 let typing_env =
121 self.tcx.erase_regions(self.typing_env).with_post_analysis_normalized(self.tcx);
122 let uv = self.tcx.erase_regions(uv);
123
124 let valtree = match self.tcx.const_eval_resolve_for_typeck(typing_env, uv, self.span) {
127 Ok(Ok(c)) => c,
128 Err(ErrorHandled::Reported(_, _)) => {
129 let mut err =
131 self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span });
132 if let ty::ConstKind::Unevaluated(uv) = self.c.kind()
135 && let hir::def::DefKind::Const | hir::def::DefKind::AssocConst =
136 self.tcx.def_kind(uv.def)
137 {
138 err.downgrade_to_delayed_bug();
139 }
140 return self.mk_err(err, ty);
141 }
142 Err(ErrorHandled::TooGeneric(_)) => {
143 let mut e = self
144 .tcx
145 .dcx()
146 .create_err(ConstPatternDependsOnGenericParameter { span: self.span });
147 for arg in uv.args {
148 if let ty::GenericArgKind::Type(ty) = arg.unpack()
149 && let ty::Param(param_ty) = ty.kind()
150 {
151 let def_id = self.tcx.hir().enclosing_body_owner(self.id);
152 let generics = self.tcx.generics_of(def_id);
153 let param = generics.type_param(*param_ty, self.tcx);
154 let span = self.tcx.def_span(param.def_id);
155 e.span_label(span, "constant depends on this generic parameter");
156 if let Some(ident) = self.tcx.def_ident_span(def_id)
157 && self.tcx.sess.source_map().is_multiline(ident.between(span))
158 {
159 e.span_label(ident, "");
162 }
163 }
164 }
165 return self.mk_err(e, ty);
166 }
167 Ok(Err(bad_ty)) => {
168 let e = match bad_ty.kind() {
170 ty::Adt(def, ..) => {
171 assert!(def.is_union());
172 self.tcx.dcx().create_err(UnionPattern { span: self.span })
173 }
174 ty::FnPtr(..) | ty::RawPtr(..) => {
175 self.tcx.dcx().create_err(PointerPattern { span: self.span })
176 }
177 _ => self.tcx.dcx().create_err(InvalidPattern {
178 span: self.span,
179 non_sm_ty: bad_ty,
180 prefix: bad_ty.prefix_string(self.tcx).to_string(),
181 }),
182 };
183 return self.mk_err(e, ty);
184 }
185 };
186
187 let inlined_const_as_pat = self.valtree_to_pat(valtree, ty);
189
190 if !inlined_const_as_pat.references_error() {
191 if !type_has_partial_eq_impl(self.tcx, typing_env, ty).has_impl {
193 let mut err = self.tcx.dcx().create_err(TypeNotPartialEq { span: self.span, ty });
194 extend_type_not_partial_eq(self.tcx, typing_env, ty, &mut err);
195 return self.mk_err(err, ty);
196 }
197 }
198
199 inlined_const_as_pat
200 }
201
202 fn field_pats(
203 &self,
204 vals: impl Iterator<Item = (ValTree<'tcx>, Ty<'tcx>)>,
205 ) -> Vec<FieldPat<'tcx>> {
206 vals.enumerate()
207 .map(|(idx, (val, ty))| {
208 let field = FieldIdx::new(idx);
209 let ty = self.tcx.normalize_erasing_regions(self.typing_env, ty);
211 FieldPat { field, pattern: *self.valtree_to_pat(val, ty) }
212 })
213 .collect()
214 }
215
216 #[instrument(skip(self), level = "debug")]
219 fn valtree_to_pat(&self, cv: ValTree<'tcx>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
220 let span = self.span;
221 let tcx = self.tcx;
222 let kind = match ty.kind() {
223 ty::Adt(adt_def, _) if !self.type_marked_structural(ty) => {
224 debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty);
227 let PartialEqImplStatus {
228 is_derived, structural_partial_eq, non_blanket_impl, ..
229 } = type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
230 let (manual_partialeq_impl_span, manual_partialeq_impl_note) =
231 match (structural_partial_eq, non_blanket_impl) {
232 (true, _) => (None, false),
233 (_, Some(def_id)) if def_id.is_local() && !is_derived => {
234 (Some(tcx.def_span(def_id)), false)
235 }
236 _ => (None, true),
237 };
238 let ty_def_span = tcx.def_span(adt_def.did());
239 let err = TypeNotStructural {
240 span,
241 ty,
242 ty_def_span,
243 manual_partialeq_impl_span,
244 manual_partialeq_impl_note,
245 };
246 return self.mk_err(tcx.dcx().create_err(err), ty);
247 }
248 ty::Adt(adt_def, args) if adt_def.is_enum() => {
249 let (&variant_index, fields) = cv.unwrap_branch().split_first().unwrap();
250 let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().to_u32());
251 PatKind::Variant {
252 adt_def: *adt_def,
253 args,
254 variant_index,
255 subpatterns: self.field_pats(
256 fields.iter().copied().zip(
257 adt_def.variants()[variant_index]
258 .fields
259 .iter()
260 .map(|field| field.ty(tcx, args)),
261 ),
262 ),
263 }
264 }
265 ty::Adt(def, args) => {
266 assert!(!def.is_union()); PatKind::Leaf {
268 subpatterns: self.field_pats(cv.unwrap_branch().iter().copied().zip(
269 def.non_enum_variant().fields.iter().map(|field| field.ty(tcx, args)),
270 )),
271 }
272 }
273 ty::Tuple(fields) => PatKind::Leaf {
274 subpatterns: self.field_pats(cv.unwrap_branch().iter().copied().zip(fields.iter())),
275 },
276 ty::Slice(elem_ty) => PatKind::Slice {
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::Array(elem_ty, _) => PatKind::Array {
286 prefix: cv
287 .unwrap_branch()
288 .iter()
289 .map(|val| *self.valtree_to_pat(*val, *elem_ty))
290 .collect(),
291 slice: None,
292 suffix: Box::new([]),
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 let pointee_ty = match *pointee_ty.kind() {
317 ty::Array(elem_ty, _) if self.treat_byte_string_as_slice => {
318 Ty::new_slice(tcx, elem_ty)
319 }
320 _ => *pointee_ty,
321 };
322 let subpattern = self.valtree_to_pat(cv, pointee_ty);
324 PatKind::Deref { subpattern }
325 }
326 }
327 },
328 ty::Float(flt) => {
329 let v = cv.unwrap_leaf();
330 let is_nan = match flt {
331 ty::FloatTy::F16 => v.to_f16().is_nan(),
332 ty::FloatTy::F32 => v.to_f32().is_nan(),
333 ty::FloatTy::F64 => v.to_f64().is_nan(),
334 ty::FloatTy::F128 => v.to_f128().is_nan(),
335 };
336 if is_nan {
337 return self.mk_err(tcx.dcx().create_err(NaNPattern { span }), ty);
340 } else {
341 PatKind::Constant {
342 value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)),
343 }
344 }
345 }
346 ty::Pat(..) | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => {
347 PatKind::Constant { value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)) }
350 }
351 ty::FnPtr(..) => {
352 unreachable!(
353 "Valtree construction would never succeed for FnPtr, so this is unreachable."
354 )
355 }
356 _ => {
357 let err = InvalidPattern {
358 span,
359 non_sm_ty: ty,
360 prefix: ty.prefix_string(tcx).to_string(),
361 };
362 return self.mk_err(tcx.dcx().create_err(err), ty);
363 }
364 };
365
366 Box::new(Pat { span, ty, kind })
367 }
368}
369
370fn extend_type_not_partial_eq<'tcx>(
373 tcx: TyCtxt<'tcx>,
374 typing_env: ty::TypingEnv<'tcx>,
375 ty: Ty<'tcx>,
376 err: &mut Diag<'_>,
377) {
378 struct UsedParamsNeedInstantiationVisitor<'tcx> {
380 tcx: TyCtxt<'tcx>,
381 typing_env: ty::TypingEnv<'tcx>,
382 adts_with_manual_partialeq: FxHashSet<Span>,
384 adts_without_partialeq: FxHashSet<Span>,
386 manual: FxHashSet<Ty<'tcx>>,
389 without: FxHashSet<Ty<'tcx>>,
392 }
393
394 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UsedParamsNeedInstantiationVisitor<'tcx> {
395 type Result = ControlFlow<()>;
396 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
397 match ty.kind() {
398 ty::Dynamic(..) => return ControlFlow::Break(()),
399 ty::FnPtr(..) => return ControlFlow::Continue(()),
400 ty::Adt(def, _args) => {
401 let ty_def_id = def.did();
402 let ty_def_span = self.tcx.def_span(ty_def_id);
403 let PartialEqImplStatus {
404 has_impl,
405 is_derived,
406 structural_partial_eq,
407 non_blanket_impl,
408 } = type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
409 match (has_impl, is_derived, structural_partial_eq, non_blanket_impl) {
410 (_, _, true, _) => {}
411 (true, false, _, Some(def_id)) if def_id.is_local() => {
412 self.adts_with_manual_partialeq.insert(self.tcx.def_span(def_id));
413 }
414 (true, false, _, _) if ty_def_id.is_local() => {
415 self.adts_with_manual_partialeq.insert(ty_def_span);
416 }
417 (false, _, _, _) if ty_def_id.is_local() => {
418 self.adts_without_partialeq.insert(ty_def_span);
419 }
420 (true, false, _, _) => {
421 self.manual.insert(ty);
422 }
423 (false, _, _, _) => {
424 self.without.insert(ty);
425 }
426 _ => {}
427 };
428 ty.super_visit_with(self)
429 }
430 _ => ty.super_visit_with(self),
431 }
432 }
433 }
434 let mut v = UsedParamsNeedInstantiationVisitor {
435 tcx,
436 typing_env,
437 adts_with_manual_partialeq: FxHashSet::default(),
438 adts_without_partialeq: FxHashSet::default(),
439 manual: FxHashSet::default(),
440 without: FxHashSet::default(),
441 };
442 if v.visit_ty(ty).is_break() {
443 return;
444 }
445 #[allow(rustc::potential_query_instability)] for span in v.adts_with_manual_partialeq {
447 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");
448 }
449 #[allow(rustc::potential_query_instability)] for span in v.adts_without_partialeq {
451 err.span_label(
452 span,
453 "must be annotated with `#[derive(PartialEq)]` to be usable in patterns",
454 );
455 }
456 #[allow(rustc::potential_query_instability)]
457 let mut manual: Vec<_> = v.manual.into_iter().map(|t| t.to_string()).collect();
458 manual.sort();
459 for ty in manual {
460 err.note(format!(
461 "`{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"
462 ));
463 }
464 #[allow(rustc::potential_query_instability)]
465 let mut without: Vec<_> = v.without.into_iter().map(|t| t.to_string()).collect();
466 without.sort();
467 for ty in without {
468 err.note(format!(
469 "`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns"
470 ));
471 }
472}
473
474#[derive(Debug)]
475struct PartialEqImplStatus {
476 has_impl: bool,
477 is_derived: bool,
478 structural_partial_eq: bool,
479 non_blanket_impl: Option<DefId>,
480}
481
482#[instrument(level = "trace", skip(tcx), ret)]
483fn type_has_partial_eq_impl<'tcx>(
484 tcx: TyCtxt<'tcx>,
485 typing_env: ty::TypingEnv<'tcx>,
486 ty: Ty<'tcx>,
487) -> PartialEqImplStatus {
488 let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
489 let partial_eq_trait_id = tcx.require_lang_item(hir::LangItem::PartialEq, None);
495 let structural_partial_eq_trait_id = tcx.require_lang_item(hir::LangItem::StructuralPeq, None);
496
497 let partial_eq_obligation = Obligation::new(
498 tcx,
499 ObligationCause::dummy(),
500 param_env,
501 ty::TraitRef::new(tcx, partial_eq_trait_id, [ty, ty]),
502 );
503
504 let mut automatically_derived = false;
505 let mut structural_peq = false;
506 let mut impl_def_id = None;
507 for def_id in tcx.non_blanket_impls_for_ty(partial_eq_trait_id, ty) {
508 automatically_derived = tcx.has_attr(def_id, sym::automatically_derived);
509 impl_def_id = Some(def_id);
510 }
511 for _ in tcx.non_blanket_impls_for_ty(structural_partial_eq_trait_id, ty) {
512 structural_peq = true;
513 }
514 PartialEqImplStatus {
525 has_impl: infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation),
526 is_derived: automatically_derived,
527 structural_partial_eq: structural_peq,
528 non_blanket_impl: impl_def_id,
529 }
530}