1mod check_match;
4mod const_to_pat;
5mod migration;
6
7use std::assert_matches::assert_matches;
8use std::cmp::Ordering;
9use std::sync::Arc;
10
11use rustc_abi::{FieldIdx, Integer};
12use rustc_errors::codes::*;
13use rustc_hir::def::{CtorOf, DefKind, Res};
14use rustc_hir::pat_util::EnumerateAndAdjustIterator;
15use rustc_hir::{self as hir, RangeEnd};
16use rustc_index::Idx;
17use rustc_middle::mir::interpret::LitToConstInput;
18use rustc_middle::thir::{
19 Ascription, DerefPatBorrowMode, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary,
20};
21use rustc_middle::ty::adjustment::{PatAdjust, PatAdjustment};
22use rustc_middle::ty::layout::IntegerExt;
23use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt};
24use rustc_middle::{bug, span_bug};
25use rustc_span::ErrorGuaranteed;
26use tracing::{debug, instrument};
27
28pub(crate) use self::check_match::check_match;
29use self::migration::PatMigration;
30use crate::errors::*;
31
32struct PatCtxt<'tcx> {
34 tcx: TyCtxt<'tcx>,
35 typing_env: ty::TypingEnv<'tcx>,
36 typeck_results: &'tcx ty::TypeckResults<'tcx>,
37
38 rust_2024_migration: Option<PatMigration<'tcx>>,
40}
41
42#[instrument(level = "debug", skip(tcx, typing_env, typeck_results), ret)]
43pub(super) fn pat_from_hir<'tcx>(
44 tcx: TyCtxt<'tcx>,
45 typing_env: ty::TypingEnv<'tcx>,
46 typeck_results: &'tcx ty::TypeckResults<'tcx>,
47 pat: &'tcx hir::Pat<'tcx>,
48 let_stmt_type: Option<&hir::Ty<'tcx>>,
50) -> Box<Pat<'tcx>> {
51 let mut pcx = PatCtxt {
52 tcx,
53 typing_env,
54 typeck_results,
55 rust_2024_migration: typeck_results
56 .rust_2024_migration_desugared_pats()
57 .get(pat.hir_id)
58 .map(PatMigration::new),
59 };
60
61 let mut thir_pat = pcx.lower_pattern(pat);
62
63 if let Some(let_stmt_type) = let_stmt_type
66 && let Some(&user_ty) = typeck_results.user_provided_types().get(let_stmt_type.hir_id)
67 {
68 debug!(?user_ty);
69 let annotation = CanonicalUserTypeAnnotation {
70 user_ty: Box::new(user_ty),
71 span: let_stmt_type.span,
72 inferred_ty: typeck_results.node_type(let_stmt_type.hir_id),
73 };
74 thir_pat
75 .extra
76 .get_or_insert_default()
77 .ascriptions
78 .push(Ascription { annotation, variance: ty::Covariant });
79 }
80
81 if let Some(m) = pcx.rust_2024_migration {
82 m.emit(tcx, pat.hir_id);
83 }
84
85 thir_pat
86}
87
88impl<'tcx> PatCtxt<'tcx> {
89 fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tcx>> {
90 let adjustments: &[PatAdjustment<'tcx>] =
91 self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
92
93 let mut opt_old_mode_span = None;
97 if let Some(s) = &mut self.rust_2024_migration
98 && adjustments.iter().any(|adjust| adjust.kind == PatAdjust::BuiltinDeref)
99 {
100 opt_old_mode_span = s.visit_implicit_derefs(pat.span, adjustments);
101 }
102
103 let unadjusted_pat = match pat.kind {
123 hir::PatKind::Ref(inner, _, _)
124 if self.typeck_results.skipped_ref_pats().contains(pat.hir_id) =>
125 {
126 self.lower_pattern(inner)
127 }
128 _ => self.lower_pattern_unadjusted(pat),
129 };
130
131 let adjusted_pat = adjustments.iter().rev().fold(unadjusted_pat, |thir_pat, adjust| {
132 debug!("{:?}: wrapping pattern with adjustment {:?}", thir_pat, adjust);
133 let span = thir_pat.span;
134 let kind = match adjust.kind {
135 PatAdjust::BuiltinDeref => PatKind::Deref { subpattern: thir_pat },
136 PatAdjust::OverloadedDeref => {
137 let borrow = self.typeck_results.deref_pat_borrow_mode(adjust.source, pat);
138 PatKind::DerefPattern { subpattern: thir_pat, borrow }
139 }
140 PatAdjust::PinDeref => PatKind::Deref { subpattern: thir_pat },
141 };
142 Box::new(Pat { span, ty: adjust.source, kind, extra: None })
143 });
144
145 if let Some(s) = &mut self.rust_2024_migration
146 && adjustments.iter().any(|adjust| adjust.kind == PatAdjust::BuiltinDeref)
147 {
148 s.leave_ref(opt_old_mode_span);
149 }
150
151 adjusted_pat
152 }
153
154 fn lower_pattern_range_endpoint(
155 &mut self,
156 pat: &'tcx hir::Pat<'tcx>, expr: Option<&'tcx hir::PatExpr<'tcx>>,
158 ascriptions: &mut Vec<Ascription<'tcx>>,
160 ) -> Result<Option<PatRangeBoundary<'tcx>>, ErrorGuaranteed> {
161 assert_matches!(pat.kind, hir::PatKind::Range(..));
162
163 let Some(expr) = expr else { return Ok(None) };
166
167 let endpoint_pat: Box<Pat<'tcx>> = self.lower_pat_expr(pat, expr);
170 let box Pat { ref kind, extra, .. } = endpoint_pat;
171
172 if let Some(extra) = extra {
174 ascriptions.extend(extra.ascriptions);
175 }
176
177 let PatKind::Constant { value } = kind else {
179 let msg =
180 format!("found bad range pattern endpoint `{expr:?}` outside of error recovery");
181 return Err(self.tcx.dcx().span_delayed_bug(expr.span, msg));
182 };
183 Ok(Some(PatRangeBoundary::Finite(value.valtree)))
184 }
185
186 fn error_on_literal_overflow(
192 &self,
193 expr: Option<&'tcx hir::PatExpr<'tcx>>,
194 ty: Ty<'tcx>,
195 ) -> Result<(), ErrorGuaranteed> {
196 use rustc_ast::ast::LitKind;
197
198 let Some(expr) = expr else {
199 return Ok(());
200 };
201 let span = expr.span;
202
203 let hir::PatExprKind::Lit { lit, negated } = expr.kind else {
207 return Ok(());
208 };
209 let LitKind::Int(lit_val, _) = lit.node else {
210 return Ok(());
211 };
212 let (min, max): (i128, u128) = match ty.kind() {
213 ty::Int(ity) => {
214 let size = Integer::from_int_ty(&self.tcx, *ity).size();
215 (size.signed_int_min(), size.signed_int_max() as u128)
216 }
217 ty::Uint(uty) => {
218 let size = Integer::from_uint_ty(&self.tcx, *uty).size();
219 (0, size.unsigned_int_max())
220 }
221 _ => {
222 return Ok(());
223 }
224 };
225 if (negated && lit_val > max + 1) || (!negated && lit_val > max) {
228 return Err(self.tcx.dcx().emit_err(LiteralOutOfRange { span, ty, min, max }));
229 }
230 Ok(())
231 }
232
233 fn lower_pattern_range(
234 &mut self,
235 pat: &'tcx hir::Pat<'tcx>,
236 lo_expr: Option<&'tcx hir::PatExpr<'tcx>>,
237 hi_expr: Option<&'tcx hir::PatExpr<'tcx>>,
238 end: RangeEnd,
239 ) -> Result<Box<Pat<'tcx>>, ErrorGuaranteed> {
240 let ty = self.typeck_results.node_type(pat.hir_id);
241 let span = pat.span;
242
243 if lo_expr.is_none() && hi_expr.is_none() {
244 let msg = "found twice-open range pattern (`..`) outside of error recovery";
245 self.tcx.dcx().span_bug(span, msg);
246 }
247
248 let mut ascriptions = vec![];
250 let mut lower_endpoint =
251 |expr| self.lower_pattern_range_endpoint(pat, expr, &mut ascriptions);
252
253 let lo = lower_endpoint(lo_expr)?.unwrap_or(PatRangeBoundary::NegInfinity);
254 let hi = lower_endpoint(hi_expr)?.unwrap_or(PatRangeBoundary::PosInfinity);
255
256 let cmp = lo.compare_with(hi, ty, self.tcx);
257 let mut kind = PatKind::Range(Arc::new(PatRange { lo, hi, end, ty }));
258 match (end, cmp) {
259 (RangeEnd::Excluded, Some(Ordering::Less)) => {}
261 (RangeEnd::Included, Some(Ordering::Less)) => {}
263 (RangeEnd::Included, Some(Ordering::Equal)) if lo.is_finite() && hi.is_finite() => {
265 let value = ty::Value { ty, valtree: lo.as_finite().unwrap() };
266 kind = PatKind::Constant { value };
267 }
268 (RangeEnd::Included, Some(Ordering::Equal)) if !lo.is_finite() => {}
270 (RangeEnd::Included, Some(Ordering::Equal)) if !hi.is_finite() => {}
273 _ => {
275 self.error_on_literal_overflow(lo_expr, ty)?;
277 self.error_on_literal_overflow(hi_expr, ty)?;
278 let e = match end {
279 RangeEnd::Included => {
280 self.tcx.dcx().emit_err(LowerRangeBoundMustBeLessThanOrEqualToUpper {
281 span,
282 teach: self.tcx.sess.teach(E0030),
283 })
284 }
285 RangeEnd::Excluded if lo_expr.is_none() => {
286 self.tcx.dcx().emit_err(UpperRangeBoundCannotBeMin { span })
287 }
288 RangeEnd::Excluded => {
289 self.tcx.dcx().emit_err(LowerRangeBoundMustBeLessThanUpper { span })
290 }
291 };
292 return Err(e);
293 }
294 }
295 let mut thir_pat = Box::new(Pat { ty, span, kind, extra: None });
296
297 thir_pat.extra.get_or_insert_default().ascriptions.extend(ascriptions);
301 Ok(thir_pat)
307 }
308
309 #[instrument(skip(self), level = "debug")]
310 fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tcx>> {
311 let ty = self.typeck_results.node_type(pat.hir_id);
312 let span = pat.span;
313
314 let kind = match pat.kind {
318 hir::PatKind::Missing => PatKind::Missing,
319
320 hir::PatKind::Wild => PatKind::Wild,
321
322 hir::PatKind::Never => PatKind::Never,
323
324 hir::PatKind::Expr(value) => return self.lower_pat_expr(pat, value),
325
326 hir::PatKind::Range(lo_expr, hi_expr, end) => {
327 match self.lower_pattern_range(pat, lo_expr, hi_expr, end) {
328 Ok(thir_pat) => return thir_pat,
329 Err(e) => PatKind::Error(e),
330 }
331 }
332
333 hir::PatKind::Deref(subpattern) => {
334 let borrow = self.typeck_results.deref_pat_borrow_mode(ty, subpattern);
335 PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), borrow }
336 }
337 hir::PatKind::Ref(subpattern, _, _) => {
338 let opt_old_mode_span =
340 self.rust_2024_migration.as_mut().and_then(|s| s.visit_explicit_deref());
341 let subpattern = self.lower_pattern(subpattern);
342 if let Some(s) = &mut self.rust_2024_migration {
343 s.leave_ref(opt_old_mode_span);
344 }
345 PatKind::Deref { subpattern }
346 }
347 hir::PatKind::Box(subpattern) => PatKind::DerefPattern {
348 subpattern: self.lower_pattern(subpattern),
349 borrow: DerefPatBorrowMode::Box,
350 },
351
352 hir::PatKind::Slice(prefix, slice, suffix) => {
353 return self.slice_or_array_pattern(pat, prefix, slice, suffix);
354 }
355
356 hir::PatKind::Tuple(pats, ddpos) => {
357 let ty::Tuple(tys) = ty.kind() else {
358 span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", ty);
359 };
360 let subpatterns = self.lower_tuple_subpats(pats, tys.len(), ddpos);
361 PatKind::Leaf { subpatterns }
362 }
363
364 hir::PatKind::Binding(explicit_ba, id, ident, sub) => {
365 let mut thir_pat_span = span;
366 if let Some(ident_span) = ident.span.find_ancestor_inside(span) {
367 thir_pat_span = span.with_hi(ident_span.hi());
368 }
369
370 let mode = *self
371 .typeck_results
372 .pat_binding_modes()
373 .get(pat.hir_id)
374 .expect("missing binding mode");
375
376 if let Some(s) = &mut self.rust_2024_migration {
377 s.visit_binding(pat.span, mode, explicit_ba, ident);
378 }
379
380 let var_ty = ty;
383 let mut thir_pat_ty = ty;
384 if let hir::ByRef::Yes(pinnedness, _) = mode.0 {
385 match pinnedness {
386 hir::Pinnedness::Pinned
387 if let Some(pty) = ty.pinned_ty()
388 && let &ty::Ref(_, rty, _) = pty.kind() =>
389 {
390 thir_pat_ty = rty;
391 }
392 hir::Pinnedness::Not if let &ty::Ref(_, rty, _) = ty.kind() => {
393 thir_pat_ty = rty;
394 }
395 _ => bug!("`ref {}` has wrong type {}", ident, ty),
396 }
397 };
398
399 let kind = PatKind::Binding {
400 mode,
401 name: ident.name,
402 var: LocalVarId(id),
403 ty: var_ty,
404 subpattern: self.lower_opt_pattern(sub),
405 is_primary: id == pat.hir_id,
406 is_shorthand: false,
407 };
408 return Box::new(Pat { ty: thir_pat_ty, span: thir_pat_span, kind, extra: None });
411 }
412
413 hir::PatKind::TupleStruct(ref qpath, pats, ddpos) => {
414 let res = self.typeck_results.qpath_res(qpath, pat.hir_id);
415 let ty::Adt(adt_def, _) = ty.kind() else {
416 span_bug!(pat.span, "tuple struct pattern not applied to an ADT {:?}", ty);
417 };
418 let variant_def = adt_def.variant_of_res(res);
419 let subpatterns = self.lower_tuple_subpats(pats, variant_def.fields.len(), ddpos);
420 return self.lower_variant_or_leaf(pat, None, res, subpatterns);
421 }
422
423 hir::PatKind::Struct(ref qpath, fields, _) => {
424 let res = self.typeck_results.qpath_res(qpath, pat.hir_id);
425 let subpatterns = fields
426 .iter()
427 .map(|field| {
428 let mut pattern = *self.lower_pattern(field.pat);
429 if let PatKind::Binding { ref mut is_shorthand, .. } = pattern.kind {
430 *is_shorthand = field.is_shorthand;
431 }
432 let field = self.typeck_results.field_index(field.hir_id);
433 FieldPat { field, pattern }
434 })
435 .collect();
436
437 return self.lower_variant_or_leaf(pat, None, res, subpatterns);
438 }
439
440 hir::PatKind::Or(pats) => PatKind::Or { pats: self.lower_patterns(pats) },
441
442 hir::PatKind::Guard(pat, _) => self.lower_pattern(pat).kind,
444
445 hir::PatKind::Err(guar) => PatKind::Error(guar),
446 };
447
448 Box::new(Pat { span, ty, kind, extra: None })
451 }
452
453 fn lower_tuple_subpats(
454 &mut self,
455 pats: &'tcx [hir::Pat<'tcx>],
456 expected_len: usize,
457 gap_pos: hir::DotDotPos,
458 ) -> Vec<FieldPat<'tcx>> {
459 pats.iter()
460 .enumerate_and_adjust(expected_len, gap_pos)
461 .map(|(i, subpattern)| FieldPat {
462 field: FieldIdx::new(i),
463 pattern: *self.lower_pattern(subpattern),
464 })
465 .collect()
466 }
467
468 fn lower_patterns(&mut self, pats: &'tcx [hir::Pat<'tcx>]) -> Box<[Pat<'tcx>]> {
469 pats.iter().map(|p| *self.lower_pattern(p)).collect()
470 }
471
472 fn lower_opt_pattern(&mut self, pat: Option<&'tcx hir::Pat<'tcx>>) -> Option<Box<Pat<'tcx>>> {
473 pat.map(|p| self.lower_pattern(p))
474 }
475
476 fn slice_or_array_pattern(
477 &mut self,
478 pat: &'tcx hir::Pat<'tcx>,
479 prefix: &'tcx [hir::Pat<'tcx>],
480 slice: Option<&'tcx hir::Pat<'tcx>>,
481 suffix: &'tcx [hir::Pat<'tcx>],
482 ) -> Box<Pat<'tcx>> {
483 let ty = self.typeck_results.node_type(pat.hir_id);
484 let span = pat.span;
485
486 let prefix = self.lower_patterns(prefix);
487 let slice = self.lower_opt_pattern(slice);
488 let suffix = self.lower_patterns(suffix);
489 let kind = match ty.kind() {
490 ty::Slice(..) => PatKind::Slice { prefix, slice, suffix },
492 ty::Array(_, len) => {
494 let len = len
495 .try_to_target_usize(self.tcx)
496 .expect("expected len of array pat to be definite");
497 assert!(len >= prefix.len() as u64 + suffix.len() as u64);
498 PatKind::Array { prefix, slice, suffix }
499 }
500 _ => span_bug!(span, "bad slice pattern type {ty:?}"),
501 };
502 Box::new(Pat { ty, span, kind, extra: None })
503 }
504
505 fn lower_variant_or_leaf(
506 &mut self,
507 pat: &'tcx hir::Pat<'tcx>,
508 expr: Option<&'tcx hir::PatExpr<'tcx>>,
509 res: Res,
510 subpatterns: Vec<FieldPat<'tcx>>,
511 ) -> Box<Pat<'tcx>> {
512 assert_matches!(
514 (pat.kind, expr),
515 (hir::PatKind::Expr(..) | hir::PatKind::Range(..), Some(_))
516 | (hir::PatKind::Struct(..) | hir::PatKind::TupleStruct(..), None)
517 );
518
519 let (hir_id, span) = match expr {
522 Some(expr) => (expr.hir_id, expr.span),
523 None => (pat.hir_id, pat.span),
524 };
525 let ty = self.typeck_results.node_type(hir_id);
526
527 let res = match res {
528 Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_id) => {
529 let variant_id = self.tcx.parent(variant_ctor_id);
530 Res::Def(DefKind::Variant, variant_id)
531 }
532 res => res,
533 };
534
535 let kind = match res {
536 Res::Def(DefKind::Variant, variant_id) => {
537 let enum_id = self.tcx.parent(variant_id);
538 let adt_def = self.tcx.adt_def(enum_id);
539 if adt_def.is_enum() {
540 let args = match ty.kind() {
541 ty::Adt(_, args) | ty::FnDef(_, args) => args,
542 ty::Error(e) => {
543 return Box::new(Pat {
545 ty,
546 span,
547 kind: PatKind::Error(*e),
548 extra: None,
549 });
550 }
551 _ => bug!("inappropriate type for def: {:?}", ty),
552 };
553 PatKind::Variant {
554 adt_def,
555 args,
556 variant_index: adt_def.variant_index_with_id(variant_id),
557 subpatterns,
558 }
559 } else {
560 PatKind::Leaf { subpatterns }
561 }
562 }
563
564 Res::Def(
565 DefKind::Struct
566 | DefKind::Ctor(CtorOf::Struct, ..)
567 | DefKind::Union
568 | DefKind::TyAlias
569 | DefKind::AssocTy,
570 _,
571 )
572 | Res::SelfTyParam { .. }
573 | Res::SelfTyAlias { .. }
574 | Res::SelfCtor(..) => PatKind::Leaf { subpatterns },
575 _ => {
576 let e = match res {
577 Res::Def(DefKind::ConstParam, def_id) => {
578 let const_span = self.tcx.def_span(def_id);
579 self.tcx.dcx().emit_err(ConstParamInPattern { span, const_span })
580 }
581 Res::Def(DefKind::Static { .. }, def_id) => {
582 let static_span = self.tcx.def_span(def_id);
583 self.tcx.dcx().emit_err(StaticInPattern { span, static_span })
584 }
585 _ => self.tcx.dcx().emit_err(NonConstPath { span }),
586 };
587 PatKind::Error(e)
588 }
589 };
590 let mut thir_pat = Box::new(Pat { ty, span, kind, extra: None });
591
592 if let Some(user_ty) = self.user_args_applied_to_ty_of_hir_id(hir_id) {
593 debug!(?thir_pat, ?user_ty, ?span, "lower_variant_or_leaf: applying ascription");
594 let annotation = CanonicalUserTypeAnnotation {
595 user_ty: Box::new(user_ty),
596 span,
597 inferred_ty: self.typeck_results.node_type(hir_id),
598 };
599 thir_pat
600 .extra
601 .get_or_insert_default()
602 .ascriptions
603 .push(Ascription { annotation, variance: ty::Covariant });
604 }
605
606 thir_pat
607 }
608
609 fn user_args_applied_to_ty_of_hir_id(
610 &self,
611 hir_id: hir::HirId,
612 ) -> Option<ty::CanonicalUserType<'tcx>> {
613 crate::thir::util::user_args_applied_to_ty_of_hir_id(self.tcx, self.typeck_results, hir_id)
614 }
615
616 #[instrument(skip(self), level = "debug")]
620 fn lower_path(
621 &mut self,
622 pat: &'tcx hir::Pat<'tcx>, expr: &'tcx hir::PatExpr<'tcx>,
624 qpath: &hir::QPath<'_>,
625 ) -> Box<Pat<'tcx>> {
626 assert_matches!(pat.kind, hir::PatKind::Expr(..) | hir::PatKind::Range(..));
627
628 let id = expr.hir_id;
629 let span = expr.span;
630 let ty = self.typeck_results.node_type(id);
631 let res = self.typeck_results.qpath_res(qpath, id);
632
633 let (def_id, user_ty) = match res {
634 Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => {
635 (def_id, self.typeck_results.user_provided_types().get(id))
636 }
637
638 _ => {
639 return self.lower_variant_or_leaf(pat, Some(expr), res, vec![]);
642 }
643 };
644
645 let args = self.typeck_results.node_args(id);
647 let c = ty::Const::new_unevaluated(self.tcx, ty::UnevaluatedConst { def: def_id, args });
650 let mut pattern = self.const_to_pat(c, ty, id, span);
651
652 if let Some(&user_ty) = user_ty {
655 let annotation = CanonicalUserTypeAnnotation {
656 user_ty: Box::new(user_ty),
657 span,
658 inferred_ty: self.typeck_results.node_type(id),
659 };
660 pattern
663 .extra
664 .get_or_insert_default()
665 .ascriptions
666 .push(Ascription { annotation, variance: ty::Contravariant });
667 }
668
669 pattern
670 }
671
672 fn lower_pat_expr(
676 &mut self,
677 pat: &'tcx hir::Pat<'tcx>, expr: &'tcx hir::PatExpr<'tcx>,
679 ) -> Box<Pat<'tcx>> {
680 assert_matches!(pat.kind, hir::PatKind::Expr(..) | hir::PatKind::Range(..));
681 match &expr.kind {
682 hir::PatExprKind::Path(qpath) => self.lower_path(pat, expr, qpath),
683 hir::PatExprKind::Lit { lit, negated } => {
684 let pat_ty = self.typeck_results.node_type(pat.hir_id);
694 let lit_input = LitToConstInput { lit: lit.node, ty: pat_ty, neg: *negated };
695 let constant = self.tcx.at(expr.span).lit_to_const(lit_input);
696 self.const_to_pat(constant, pat_ty, expr.hir_id, lit.span)
697 }
698 }
699 }
700}