diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index dcbed92d350b7..5e80314035652 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -807,12 +807,22 @@ pub enum PatKind<'tcx> { subpatterns: Vec>, }, - /// `box P`, `&P`, `&mut P`, etc. + /// Explicit or implicit `&P` or `&mut P`, for some subpattern `P`. + /// + /// Implicit `&`/`&mut` patterns can be inserted by match-ergonomics. + /// + /// With `feature(pin_ergonomics)`, this can also be `&pin const P` or + /// `&pin mut P`, as indicated by the `pin` field. Deref { + #[type_visitable(ignore)] + pin: hir::Pinnedness, subpattern: Box>, }, - /// Deref pattern, written `box P` for now. + /// Explicit or implicit `deref!(..)` pattern, under `feature(deref_patterns)`. + /// Represents a call to `Deref` or `DerefMut`, or a deref-move of `Box`. + /// + /// `box P` patterns also lower to this, under `feature(box_patterns)`. DerefPattern { subpattern: Box>, /// Whether the pattern scrutinee needs to be borrowed in order to call `Deref::deref` or diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index c06d5c5f0ebb0..8e0e3aa294b85 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -268,7 +268,7 @@ pub(crate) fn for_each_immediate_subpat<'a, 'tcx>( | PatKind::Error(_) => {} PatKind::Binding { subpattern: Some(subpattern), .. } - | PatKind::Deref { subpattern } + | PatKind::Deref { subpattern, .. } | PatKind::DerefPattern { subpattern, .. } => callback(subpattern), PatKind::Variant { subpatterns, .. } | PatKind::Leaf { subpatterns } => { diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index e80e29415e6f0..a3662b2768cd3 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use rustc_abi::FieldIdx; use rustc_middle::mir::*; +use rustc_middle::span_bug; use rustc_middle::thir::*; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; @@ -314,23 +315,24 @@ impl<'tcx> MatchPairTree<'tcx> { None } - // FIXME: Pin-patterns should probably have their own pattern kind, - // instead of overloading `PatKind::Deref` via the pattern type. - PatKind::Deref { ref subpattern } - if let Some(ref_ty) = pattern.ty.pinned_ty() - && ref_ty.is_ref() => - { + PatKind::Deref { pin: Pinnedness::Pinned, ref subpattern } => { + let pinned_ref_ty = match pattern.ty.pinned_ty() { + Some(p_ty) if p_ty.is_ref() => p_ty, + _ => span_bug!(pattern.span, "bad type for pinned deref: {:?}", pattern.ty), + }; MatchPairTree::for_pattern( - place_builder.field(FieldIdx::ZERO, ref_ty).deref(), + // Project into the `Pin(_)` struct, then deref the inner `&` or `&mut`. + place_builder.field(FieldIdx::ZERO, pinned_ref_ty).deref(), subpattern, cx, &mut subpairs, extra_data, ); + None } - PatKind::Deref { ref subpattern } + PatKind::Deref { pin: Pinnedness::Not, ref subpattern } | PatKind::DerefPattern { ref subpattern, borrow: DerefPatBorrowMode::Box } => { MatchPairTree::for_pattern( place_builder.deref(), diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 2f9486c2d552e..d04322c320b4c 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -10,7 +10,7 @@ use std::mem; use std::sync::Arc; use itertools::{Itertools, Position}; -use rustc_abi::{FIRST_VARIANT, VariantIdx}; +use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; use rustc_data_structures::debug_assert_matches; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -909,7 +909,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | PatKind::Never | PatKind::Error(_) => {} - PatKind::Deref { ref subpattern } => { + PatKind::Deref { pin: Pinnedness::Pinned, ref subpattern } => { + // Project into the `Pin(_)` struct, then deref the inner `&` or `&mut`. + visit_subpat(self, subpattern, &user_tys.leaf(FieldIdx::ZERO).deref(), f); + } + PatKind::Deref { pin: Pinnedness::Not, ref subpattern } => { visit_subpat(self, subpattern, &user_tys.deref(), f); } diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 7fbf8cd11466a..0a0e0d06061eb 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -294,8 +294,12 @@ impl<'tcx> ConstToPat<'tcx> { || pointee_ty.is_slice() || pointee_ty.is_sized(tcx, self.typing_env) { - // References have the same valtree representation as their pointee. PatKind::Deref { + // This node has type `ty::Ref`, so it's not a pin-deref. + pin: hir::Pinnedness::Not, + // Lower the valtree to a pattern as the pointee type. + // This works because references have the same valtree + // representation as their pointee. subpattern: self.valtree_to_pat(ty::Value { ty: *pointee_ty, valtree }), } } else { diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index d0abb6396145d..3641561567bce 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -132,12 +132,16 @@ impl<'tcx> PatCtxt<'tcx> { debug!("{:?}: wrapping pattern with adjustment {:?}", thir_pat, adjust); let span = thir_pat.span; let kind = match adjust.kind { - PatAdjust::BuiltinDeref => PatKind::Deref { subpattern: thir_pat }, + PatAdjust::BuiltinDeref => { + PatKind::Deref { pin: hir::Pinnedness::Not, subpattern: thir_pat } + } PatAdjust::OverloadedDeref => { let borrow = self.typeck_results.deref_pat_borrow_mode(adjust.source, pat); PatKind::DerefPattern { subpattern: thir_pat, borrow } } - PatAdjust::PinDeref => PatKind::Deref { subpattern: thir_pat }, + PatAdjust::PinDeref => { + PatKind::Deref { pin: hir::Pinnedness::Pinned, subpattern: thir_pat } + } }; Box::new(Pat { span, ty: adjust.source, kind, extra: None }) }); @@ -334,7 +338,7 @@ impl<'tcx> PatCtxt<'tcx> { let borrow = self.typeck_results.deref_pat_borrow_mode(ty, subpattern); PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), borrow } } - hir::PatKind::Ref(subpattern, _, _) => { + hir::PatKind::Ref(subpattern, pin, _) => { // Track the default binding mode for the Rust 2024 migration suggestion. let opt_old_mode_span = self.rust_2024_migration.as_mut().and_then(|s| s.visit_explicit_deref()); @@ -342,7 +346,7 @@ impl<'tcx> PatCtxt<'tcx> { if let Some(s) = &mut self.rust_2024_migration { s.leave_ref(opt_old_mode_span); } - PatKind::Deref { subpattern } + PatKind::Deref { pin, subpattern } } hir::PatKind::Box(subpattern) => PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index a87257fa79bf5..db8b2518981d3 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -774,8 +774,9 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, "]", depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); } - PatKind::Deref { subpattern } => { + PatKind::Deref { pin, subpattern } => { print_indented!(self, "Deref { ", depth_lvl + 1); + print_indented!(self, format_args!("pin: {pin:?}"), depth_lvl + 2); print_indented!(self, "subpattern:", depth_lvl + 2); self.print_pat(subpattern, depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 364ffc7bace0d..2652840863776 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -4,6 +4,7 @@ // tidy-alphabetical-start #![allow(unused_crate_dependencies)] +#![cfg_attr(feature = "rustc", feature(if_let_guard))] // tidy-alphabetical-end pub(crate) mod checks; diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 5e75192ff3092..3e97842b7f395 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -4,8 +4,8 @@ use std::iter::once; use rustc_abi::{FIRST_VARIANT, FieldIdx, Integer, VariantIdx}; use rustc_arena::DroplessArena; -use rustc_hir::HirId; use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, HirId}; use rustc_index::{Idx, IndexVec}; use rustc_middle::middle::stability::EvalResult; use rustc_middle::thir::{self, Pat, PatKind, PatRange, PatRangeBoundary}; @@ -468,12 +468,12 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { fields = vec![]; arity = 0; } - PatKind::Deref { subpattern } => { + PatKind::Deref { pin, subpattern } => { fields = vec![self.lower_pat(subpattern).at_index(0)]; arity = 1; - ctor = match ty.pinned_ref() { - None if ty.is_ref() => Ref, - Some((inner_ty, _)) => { + ctor = match pin { + hir::Pinnedness::Not if ty.is_ref() => Ref, + hir::Pinnedness::Pinned if let Some((inner_ty, _)) = ty.pinned_ref() => { self.internal_state.has_lowered_deref_pat.set(true); DerefPattern(RevealedTy(inner_ty)) } diff --git a/tests/ui/pin-ergonomics/user-type-projection.rs b/tests/ui/pin-ergonomics/user-type-projection.rs new file mode 100644 index 0000000000000..f482586b6ebcc --- /dev/null +++ b/tests/ui/pin-ergonomics/user-type-projection.rs @@ -0,0 +1,19 @@ +#![crate_type = "rlib"] +#![feature(pin_ergonomics)] +#![expect(incomplete_features)] +//@ edition: 2024 +//@ check-pass + +// Test that we don't ICE when projecting user-type-annotations through a `&pin` pattern. +// +// Historically, this could occur when the code handling those projections did not know +// about `&pin` patterns, and incorrectly treated them as plain `&`/`&mut` patterns instead. + +struct Data { + x: u32 +} + +pub fn project_user_type_through_pin() -> u32 { + let &pin const Data { x }: &pin const Data = &pin const Data { x: 30 }; + x +} diff --git a/tests/ui/thir-print/str-patterns.stdout b/tests/ui/thir-print/str-patterns.stdout index 6941ab15130f8..09212e7d68aee 100644 --- a/tests/ui/thir-print/str-patterns.stdout +++ b/tests/ui/thir-print/str-patterns.stdout @@ -10,6 +10,7 @@ Thir { span: $DIR/str-patterns.rs:11:9: 11:16 (#0), extra: None, kind: Deref { + pin: Not, subpattern: Pat { ty: str, span: $DIR/str-patterns.rs:11:9: 11:16 (#0), @@ -50,6 +51,7 @@ Thir { }, ), kind: Deref { + pin: Not, subpattern: Pat { ty: str, span: $DIR/str-patterns.rs:12:9: 12:17 (#0),