Rollup merge of #151282 - Zalathar:pin-pat, r=Nadrieril

THIR patterns: Explicitly distinguish `&pin` from plain `&`/`&mut`

Currently, `thir::PatKind::Deref` is used for ordinary `&`/`&mut` patterns, and also for `&pin const` and `&pin mut` patterns under `feature(pin_ergonomics)`. The only way to distinguish between them is by inspecting the `Ty` attached to the pattern node.

That's non-obvious, making it easy to miss, and is also a bit confusing to read when it does occur.

This PR therefore adds an explicit `pin: hir::Pinnedness` field to `thir::PatKind::Deref`, to explicitly distinguish pin-deref nodes from ordinary builtin-deref nodes.

(I'm not deeply familiar with the future of pin-patterns, so I'm not sure whether that information is best carried as a field or as a separate `PatKind`, but I think this approach is at least an improvement over the status quo.)

r? Nadrieril (or compiler)
This commit is contained in:
Matthias Krüger
2026-01-24 15:35:09 +01:00
committed by GitHub
11 changed files with 71 additions and 24 deletions
+12 -2
View File
@@ -807,12 +807,22 @@ pub enum PatKind<'tcx> {
subpatterns: Vec<FieldPat<'tcx>>,
},
/// `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<Pat<'tcx>>,
},
/// 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<Pat<'tcx>>,
/// Whether the pattern scrutinee needs to be borrowed in order to call `Deref::deref` or
+1 -1
View File
@@ -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 } => {
@@ -2,6 +2,7 @@
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 @@ pub(super) fn for_pattern(
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(),
@@ -10,7 +10,7 @@
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 @@ fn visit_primary_bindings_special(
| 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);
}
@@ -294,8 +294,12 @@ fn valtree_to_pat(&self, value: ty::Value<'tcx>) -> Box<Pat<'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 {
@@ -132,12 +132,16 @@ fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'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 @@ fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tc
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 @@ fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tc
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),
+2 -1
View File
@@ -774,8 +774,9 @@ fn print_pat_kind(&mut self, pat_kind: &PatKind<'tcx>, depth_lvl: usize) {
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);
@@ -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;
+5 -5
View File
@@ -4,8 +4,8 @@
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 @@ pub fn lower_pat(&self, pat: &'p Pat<'tcx>) -> DeconstructedPat<'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))
}
@@ -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
}
+2
View File
@@ -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),