Implement &pin patterns and ref pin bindings

This commit is contained in:
Frank King 2025-11-02 19:23:36 +08:00
parent 6647be9364
commit 5ef48ed448
62 changed files with 886 additions and 240 deletions

View file

@ -648,9 +648,10 @@ impl Pat {
PatKind::Path(qself, path) => TyKind::Path(qself.clone(), path.clone()),
PatKind::MacCall(mac) => TyKind::MacCall(mac.clone()),
// `&mut? P` can be reinterpreted as `&mut? T` where `T` is `P` reparsed as a type.
PatKind::Ref(pat, mutbl) => {
pat.to_ty().map(|ty| TyKind::Ref(None, MutTy { ty, mutbl: *mutbl }))?
}
PatKind::Ref(pat, pinned, mutbl) => pat.to_ty().map(|ty| match pinned {
Pinnedness::Not => TyKind::Ref(None, MutTy { ty, mutbl: *mutbl }),
Pinnedness::Pinned => TyKind::PinnedRef(None, MutTy { ty, mutbl: *mutbl }),
})?,
// A slice/array pattern `[P]` can be reparsed as `[T]`, an unsized array,
// when `P` can be reparsed as a type `T`.
PatKind::Slice(pats) if let [pat] = pats.as_slice() => {
@ -696,7 +697,7 @@ impl Pat {
// Trivial wrappers over inner patterns.
PatKind::Box(s)
| PatKind::Deref(s)
| PatKind::Ref(s, _)
| PatKind::Ref(s, _, _)
| PatKind::Paren(s)
| PatKind::Guard(s, _) => s.walk(it),
@ -717,7 +718,7 @@ impl Pat {
/// Strip off all reference patterns (`&`, `&mut`) and return the inner pattern.
pub fn peel_refs(&self) -> &Pat {
let mut current = self;
while let PatKind::Ref(inner, _) = &current.kind {
while let PatKind::Ref(inner, _, _) = &current.kind {
current = inner;
}
current
@ -765,7 +766,9 @@ impl Pat {
PatKind::Missing => unreachable!(),
PatKind::Wild => Some("_".to_string()),
PatKind::Ident(BindingMode::NONE, ident, None) => Some(format!("{ident}")),
PatKind::Ref(pat, mutbl) => pat.descr().map(|d| format!("&{}{d}", mutbl.prefix_str())),
PatKind::Ref(pat, pinned, mutbl) => {
pat.descr().map(|d| format!("&{}{d}", pinned.prefix_str(*mutbl)))
}
_ => None,
}
}
@ -913,7 +916,7 @@ pub enum PatKind {
Deref(Box<Pat>),
/// A reference pattern (e.g., `&mut (a, b)`).
Ref(Box<Pat>, Mutability),
Ref(Box<Pat>, Pinnedness, Mutability),
/// A literal, const block or path.
Expr(Box<Expr>),

View file

@ -317,4 +317,15 @@ impl Pinnedness {
pub fn is_pinned(self) -> bool {
matches!(self, Self::Pinned)
}
/// Returns `""` (empty string), "mut", `"pin mut "` or `"pin const "` depending
/// on the pinnedness and mutability.
pub fn prefix_str(self, mutbl: Mutability) -> &'static str {
match (self, mutbl) {
(Pinnedness::Pinned, Mutability::Mut) => "pin mut ",
(Pinnedness::Pinned, Mutability::Not) => "pin const ",
(Pinnedness::Not, Mutability::Mut) => "mut ",
(Pinnedness::Not, Mutability::Not) => "",
}
}
}

View file

@ -124,8 +124,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
PatKind::Deref(inner) => {
break hir::PatKind::Deref(self.lower_pat(inner));
}
PatKind::Ref(inner, mutbl) => {
break hir::PatKind::Ref(self.lower_pat(inner), *mutbl);
PatKind::Ref(inner, pinned, mutbl) => {
break hir::PatKind::Ref(self.lower_pat(inner), *pinned, *mutbl);
}
PatKind::Range(e1, e2, Spanned { node: end, .. }) => {
break hir::PatKind::Range(

View file

@ -1807,8 +1807,14 @@ impl<'a> State<'a> {
self.print_pat(inner);
self.pclose();
}
PatKind::Ref(inner, mutbl) => {
PatKind::Ref(inner, pinned, mutbl) => {
self.word("&");
if pinned.is_pinned() {
self.word("pin ");
if mutbl.is_not() {
self.word("const ");
}
}
if mutbl.is_mut() {
self.word("mut ");
}

View file

@ -867,11 +867,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
for (binding_span, opt_ref_pat) in finder.ref_pat_for_binding {
if let Some(ref_pat) = opt_ref_pat
&& !finder.cannot_remove.contains(&ref_pat.hir_id)
&& let hir::PatKind::Ref(subpat, mutbl) = ref_pat.kind
&& let hir::PatKind::Ref(subpat, pinned, mutbl) = ref_pat.kind
&& let Some(ref_span) = ref_pat.span.trim_end(subpat.span)
{
let pinned_str = if pinned.is_pinned() { "pinned " } else { "" };
let mutable_str = if mutbl.is_mut() { "mutable " } else { "" };
let msg = format!("consider removing the {mutable_str}borrow");
let msg = format!("consider removing the {pinned_str}{mutable_str}borrow");
suggestions.push((ref_span, msg, "".to_string()));
} else {
let msg = "consider borrowing the pattern binding".to_string();

View file

@ -772,11 +772,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
&& let Some(hir_id) = (BindingFinder { span: pat_span }).visit_body(&body).break_value()
&& let node = self.infcx.tcx.hir_node(hir_id)
&& let hir::Node::LetStmt(hir::LetStmt {
pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. },
pat: hir::Pat { kind: hir::PatKind::Ref(_, _, _), .. },
..
})
| hir::Node::Param(Param {
pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. },
pat: hir::Pat { kind: hir::PatKind::Ref(_, _, _), .. },
..
}) = node
{
@ -1494,7 +1494,7 @@ impl<'tcx> Visitor<'tcx> for BindingFinder {
}
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) -> Self::Result {
if let hir::Pat { kind: hir::PatKind::Ref(_, _), span, .. } = param.pat
if let hir::Pat { kind: hir::PatKind::Ref(_, _, _), span, .. } = param.pat
&& *span == self.span
{
ControlFlow::Break(param.hir_id)

View file

@ -1722,7 +1722,9 @@ impl<'hir> Pat<'hir> {
match self.kind {
Missing => unreachable!(),
Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Err(_) => true,
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_short_(it),
Box(s) | Deref(s) | Ref(s, _, _) | Binding(.., Some(s)) | Guard(s, _) => {
s.walk_short_(it)
}
Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)),
TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)),
Slice(before, slice, after) => {
@ -1749,7 +1751,7 @@ impl<'hir> Pat<'hir> {
use PatKind::*;
match self.kind {
Missing | Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Err(_) => {}
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_(it),
Box(s) | Deref(s) | Ref(s, _, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_(it),
Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)),
TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)),
Slice(before, slice, after) => {
@ -1938,7 +1940,7 @@ pub enum PatKind<'hir> {
Deref(&'hir Pat<'hir>),
/// A reference pattern (e.g., `&mut (a, b)`).
Ref(&'hir Pat<'hir>, Mutability),
Ref(&'hir Pat<'hir>, Pinnedness, Mutability),
/// A literal, const block or path.
Expr(&'hir PatExpr<'hir>),

View file

@ -752,7 +752,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V:
}
PatKind::Box(ref subpattern)
| PatKind::Deref(ref subpattern)
| PatKind::Ref(ref subpattern, _) => {
| PatKind::Ref(ref subpattern, _, _) => {
try_visit!(visitor.visit_pat(subpattern));
}
PatKind::Binding(_, _hir_id, ident, ref optional_subpattern) => {

View file

@ -593,7 +593,7 @@ fn resolve_local<'tcx>(
is_binding_pat(subpat)
}
PatKind::Ref(_, _)
PatKind::Ref(_, _, _)
| PatKind::Binding(hir::BindingMode(hir::ByRef::No, _), ..)
| PatKind::Missing
| PatKind::Wild

View file

@ -2028,9 +2028,15 @@ impl<'a> State<'a> {
self.print_pat(inner);
self.pclose();
}
PatKind::Ref(inner, mutbl) => {
PatKind::Ref(inner, pinned, mutbl) => {
let is_range_inner = matches!(inner.kind, PatKind::Range(..));
self.word("&");
if pinned.is_pinned() {
self.word("pin ");
if mutbl.is_not() {
self.word("const ");
}
}
self.word(mutbl.prefix_str());
if is_range_inner {
self.popen();

View file

@ -523,7 +523,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| hir::PatKind::TupleStruct(_, _, _)
| hir::PatKind::Tuple(_, _)
| hir::PatKind::Box(_)
| hir::PatKind::Ref(_, _)
| hir::PatKind::Ref(_, _, _)
| hir::PatKind::Deref(_)
| hir::PatKind::Expr(_)
| hir::PatKind::Range(_, _, _)

View file

@ -1231,7 +1231,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
debug!("pat_ty(pat={:?}) found adjustment `{:?}`", pat, first_adjust);
return Ok(first_adjust.source);
}
} else if let PatKind::Ref(subpat, _) = pat.kind
} else if let PatKind::Ref(subpat, _, _) = pat.kind
&& self.cx.typeck_results().skipped_ref_pats().contains(pat.hir_id)
{
return self.pat_ty_adjusted(subpat);
@ -1817,13 +1817,13 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
self.cat_pattern(place_with_id, subpat, op)?;
}
PatKind::Ref(subpat, _)
PatKind::Ref(subpat, _, _)
if self.cx.typeck_results().skipped_ref_pats().contains(pat.hir_id) =>
{
self.cat_pattern(place_with_id, subpat, op)?;
}
PatKind::Box(subpat) | PatKind::Ref(subpat, _) => {
PatKind::Box(subpat) | PatKind::Ref(subpat, _, _) => {
// box p1, &p1, &mut p1. we can ignore the mutability of
// PatKind::Ref since that information is already contained
// in the type.

View file

@ -2610,7 +2610,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut current_node = parent_node;
while let Node::Pat(parent_pat) = current_node {
if let hir::PatKind::Ref(_, mutability) = parent_pat.kind {
if let hir::PatKind::Ref(_, _, mutability) = parent_pat.kind {
ref_muts.push(mutability);
current_node = self.tcx.parent_hir_node(parent_pat.hir_id);
} else {

View file

@ -91,6 +91,7 @@ struct TopInfo<'tcx> {
#[derive(Copy, Clone)]
struct PatInfo<'tcx> {
binding_mode: ByRef,
max_pinnedness: PinnednessCap,
max_ref_mutbl: MutblCap,
top_info: TopInfo<'tcx>,
decl_origin: Option<DeclOrigin<'tcx>>,
@ -241,6 +242,19 @@ impl MutblCap {
}
}
/// `ref` or `ref mut` bindings (not pinned, explicitly or match-ergonomics) are only allowed behind
/// an `&pin` reference if the binding's type is `Unpin`.
///
/// Normally, the borrow checker enforces this (not implemented yet), but we track it here for better
/// diagnostics.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum PinnednessCap {
/// No restriction on pinnedness.
Not,
/// Pinnedness restricted to pinned.
Pinned,
}
/// Variations on RFC 3627's Rule 4: when do reference patterns match against inherited references?
///
/// "Inherited reference" designates the `&`/`&mut` types that arise from using match ergonomics, i.e.
@ -374,6 +388,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let top_info = TopInfo { expected, origin_expr, span, hir_id: pat.hir_id };
let pat_info = PatInfo {
binding_mode: ByRef::No,
max_pinnedness: PinnednessCap::Not,
max_ref_mutbl: MutblCap::Mut,
top_info,
decl_origin,
@ -489,22 +504,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let old_pat_info = pat_info;
let pat_info = PatInfo { current_depth: old_pat_info.current_depth + 1, ..old_pat_info };
let adjust_binding_mode = |inner_pinnedness, inner_mutability| {
match pat_info.binding_mode {
// If default binding mode is by value, make it `ref`, `ref mut`, `ref pin const`
// or `ref pin mut` (depending on whether we observe `&`, `&mut`, `&pin const` or
// `&pin mut`).
ByRef::No => ByRef::Yes(inner_pinnedness, inner_mutability),
// When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`).
// Pinnedness is preserved.
ByRef::Yes(pinnedness, Mutability::Mut) => ByRef::Yes(pinnedness, inner_mutability),
// Once a `ref`, always a `ref`.
// This is because a `& &mut` cannot mutate the underlying value.
// Pinnedness is preserved.
ByRef::Yes(pinnedness, Mutability::Not) => ByRef::Yes(pinnedness, Mutability::Not),
}
};
match pat.kind {
// Peel off a `&` or `&mut`from the scrutinee type. See the examples in
// `tests/ui/rfcs/rfc-2005-default-binding-mode`.
@ -524,19 +523,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.or_default()
.push(PatAdjustment { kind: PatAdjust::BuiltinDeref, source: expected });
let mut binding_mode = adjust_binding_mode(Pinnedness::Not, inner_mutability);
let mut max_ref_mutbl = pat_info.max_ref_mutbl;
if self.downgrade_mut_inside_shared() {
binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl());
}
if matches!(binding_mode, ByRef::Yes(_, Mutability::Not)) {
max_ref_mutbl = MutblCap::Not;
}
debug!("default binding mode is now {:?}", binding_mode);
// Use the old pat info to keep `current_depth` to its old value.
let new_pat_info = PatInfo { binding_mode, max_ref_mutbl, ..old_pat_info };
let new_pat_info =
self.adjust_pat_info(Pinnedness::Not, inner_mutability, old_pat_info);
// Recurse with the new expected type.
self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, new_pat_info)
}
@ -569,20 +559,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
});
}
let binding_mode = adjust_binding_mode(Pinnedness::Pinned, inner_mutability);
// If the pinnedness is `Not`, it means the pattern is unpinned
// and thus requires an `Unpin` bound.
if matches!(binding_mode, ByRef::Yes(Pinnedness::Not, _)) {
self.register_bound(
inner_ty,
self.tcx.require_lang_item(hir::LangItem::Unpin, pat.span),
self.misc(pat.span),
)
}
debug!("default binding mode is now {:?}", binding_mode);
// Use the old pat info to keep `current_depth` to its old value.
let new_pat_info = PatInfo { binding_mode, ..old_pat_info };
let new_pat_info =
self.adjust_pat_info(Pinnedness::Pinned, inner_mutability, old_pat_info);
self.check_deref_pattern(
pat,
@ -689,13 +668,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info),
PatKind::Deref(inner) => self.check_pat_deref(pat.span, inner, expected, pat_info),
PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info),
PatKind::Ref(inner, pinned, mutbl) => {
self.check_pat_ref(pat, inner, pinned, mutbl, expected, pat_info)
}
PatKind::Slice(before, slice, after) => {
self.check_pat_slice(pat.span, before, slice, after, expected, pat_info)
}
}
}
fn adjust_pat_info(
&self,
inner_pinnedness: Pinnedness,
inner_mutability: Mutability,
pat_info: PatInfo<'tcx>,
) -> PatInfo<'tcx> {
let mut binding_mode = match pat_info.binding_mode {
// If default binding mode is by value, make it `ref`, `ref mut`, `ref pin const`
// or `ref pin mut` (depending on whether we observe `&`, `&mut`, `&pin const` or
// `&pin mut`).
ByRef::No => ByRef::Yes(inner_pinnedness, inner_mutability),
ByRef::Yes(pinnedness, mutability) => {
let pinnedness = match pinnedness {
// When `ref`, stay a `ref` (on `&`) or downgrade to `ref pin` (on `&pin`).
Pinnedness::Not => inner_pinnedness,
// When `ref pin`, stay a `ref pin`.
// This is because we cannot get an `&mut T` from `&mut &pin mut T` unless `T: Unpin`.
// Note that `&T` and `&mut T` are `Unpin`, which implies
// `& &pin const T` <-> `&pin const &T` and `&mut &pin mut T` <-> `&pin mut &mut T`
// (i.e. mutually coercible).
Pinnedness::Pinned => Pinnedness::Pinned,
};
let mutability = match mutability {
// When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`).
Mutability::Mut => inner_mutability,
// Once a `ref`, always a `ref`.
// This is because a `& &mut` cannot mutate the underlying value.
Mutability::Not => Mutability::Not,
};
ByRef::Yes(pinnedness, mutability)
}
};
let PatInfo { mut max_ref_mutbl, mut max_pinnedness, .. } = pat_info;
if self.downgrade_mut_inside_shared() {
binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl());
}
match binding_mode {
ByRef::Yes(_, Mutability::Not) => max_ref_mutbl = MutblCap::Not,
ByRef::Yes(Pinnedness::Pinned, _) => max_pinnedness = PinnednessCap::Pinned,
_ => {}
}
debug!("default binding mode is now {:?}", binding_mode);
PatInfo { binding_mode, max_pinnedness, max_ref_mutbl, ..pat_info }
}
fn check_deref_pattern(
&self,
pat: &'tcx Pat<'tcx>,
@ -1195,6 +1223,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
};
// If there exists a pinned reference in the pattern but the binding is not pinned,
// it means the binding is unpinned and thus requires an `Unpin` bound.
if pat_info.max_pinnedness == PinnednessCap::Pinned
&& matches!(bm.0, ByRef::Yes(Pinnedness::Not, _))
{
self.register_bound(
expected,
self.tcx.require_lang_item(hir::LangItem::Unpin, pat.span),
self.misc(pat.span),
)
}
if matches!(bm.0, ByRef::Yes(_, Mutability::Mut))
&& let MutblCap::WeaklyNot(and_pat_span) = pat_info.max_ref_mutbl
{
@ -1223,22 +1263,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let local_ty = self.local_ty(pat.span, pat.hir_id);
let eq_ty = match bm.0 {
ByRef::Yes(Pinnedness::Not, mutbl) => {
ByRef::Yes(pinnedness, mutbl) => {
// If the binding is like `ref x | ref mut x`,
// then `x` is assigned a value of type `&M T` where M is the
// mutability and T is the expected type.
//
// Under pin ergonomics, if the binding is like `ref pin const|mut x`,
// then `x` is assigned a value of type `&pin M T` where M is the
// mutability and T is the expected type.
//
// `x` is assigned a value of type `&M T`, hence `&M T <: typeof(x)`
// is required. However, we use equality, which is stronger.
// See (note_1) for an explanation.
self.new_ref_ty(pat.span, mutbl, expected)
self.new_ref_ty(pat.span, pinnedness, mutbl, expected)
}
// Wrapping the type into `Pin` if the binding is like `ref pin const|mut x`
ByRef::Yes(Pinnedness::Pinned, mutbl) => Ty::new_adt(
self.tcx,
self.tcx.adt_def(self.tcx.require_lang_item(hir::LangItem::Pin, pat.span)),
self.tcx.mk_args(&[self.new_ref_ty(pat.span, mutbl, expected).into()]),
),
// Otherwise, the type of x is the expected type `T`.
ByRef::No => expected, // As above, `T <: typeof(x)` is required, but we use equality, see (note_1).
};
@ -1331,18 +1369,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
/// Precondition: pat is a `Ref(_)` pattern
// FIXME(pin_ergonomics): add suggestions for `&pin mut` or `&pin const` patterns
fn borrow_pat_suggestion(&self, err: &mut Diag<'_>, pat: &Pat<'_>) {
let tcx = self.tcx;
if let PatKind::Ref(inner, mutbl) = pat.kind
if let PatKind::Ref(inner, pinned, mutbl) = pat.kind
&& let PatKind::Binding(_, _, binding, ..) = inner.kind
{
let binding_parent = tcx.parent_hir_node(pat.hir_id);
debug!(?inner, ?pat, ?binding_parent);
let mutability = match mutbl {
ast::Mutability::Mut => "mut",
ast::Mutability::Not => "",
};
let pin_and_mut = pinned.prefix_str(mutbl).trim_end();
let mut_var_suggestion = 'block: {
if mutbl.is_not() {
@ -1392,7 +1428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// so we don't suggest moving something to the type that does not exist
hir::Node::Param(hir::Param { ty_span, pat, .. }) if pat.span != *ty_span => {
err.multipart_suggestion_verbose(
format!("to take parameter `{binding}` by reference, move `&{mutability}` to the type"),
format!("to take parameter `{binding}` by reference, move `&{pin_and_mut}` to the type"),
vec![
(pat.span.until(inner.span), "".to_owned()),
(ty_span.shrink_to_lo(), mutbl.ref_prefix_str().to_owned()),
@ -1406,13 +1442,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
hir::Node::Pat(pt) if let PatKind::TupleStruct(_, pat_arr, _) = pt.kind => {
for i in pat_arr.iter() {
if let PatKind::Ref(the_ref, _) = i.kind
if let PatKind::Ref(the_ref, _, _) = i.kind
&& let PatKind::Binding(mt, _, ident, _) = the_ref.kind
{
let BindingMode(_, mtblty) = mt;
err.span_suggestion_verbose(
i.span,
format!("consider removing `&{mutability}` from the pattern"),
format!("consider removing `&{pin_and_mut}` from the pattern"),
mtblty.prefix_str().to_string() + &ident.name.to_string(),
Applicability::MaybeIncorrect,
);
@ -1426,7 +1462,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// rely on match ergonomics or it might be nested `&&pat`
err.span_suggestion_verbose(
pat.span.until(inner.span),
format!("consider removing `&{mutability}` from the pattern"),
format!("consider removing `&{pin_and_mut}` from the pattern"),
"",
Applicability::MaybeIncorrect,
);
@ -2677,6 +2713,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
pat: &'tcx Pat<'tcx>,
inner: &'tcx Pat<'tcx>,
pat_pinned: Pinnedness,
pat_mutbl: Mutability,
mut expected: Ty<'tcx>,
mut pat_info: PatInfo<'tcx>,
@ -2699,9 +2736,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Determine whether we're consuming an inherited reference and resetting the default
// binding mode, based on edition and enabled experimental features.
if let ByRef::Yes(inh_pin, inh_mut) = pat_info.binding_mode
// FIXME(pin_ergonomics): since `&pin` pattern is supported, the condition here
// should be adjusted to `pat_pin == inh_pin`
&& (!self.tcx.features().pin_ergonomics() || inh_pin == Pinnedness::Not)
&& pat_pinned == inh_pin
{
match self.ref_pat_matches_inherited_ref(pat.span.edition()) {
InheritedRefMatchRule::EatOuter => {
@ -2821,21 +2856,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// to avoid creating needless variables. This also helps with
// the bad interactions of the given hack detailed in (note_1).
debug!("check_pat_ref: expected={:?}", expected);
match *expected.kind() {
ty::Ref(_, r_ty, r_mutbl)
if (ref_pat_matches_mut_ref && r_mutbl >= pat_mutbl)
|| r_mutbl == pat_mutbl =>
match expected.maybe_pinned_ref() {
Some((r_ty, r_pinned, r_mutbl))
if ((ref_pat_matches_mut_ref && r_mutbl >= pat_mutbl)
|| r_mutbl == pat_mutbl)
&& pat_pinned == r_pinned =>
{
if r_mutbl == Mutability::Not {
pat_info.max_ref_mutbl = MutblCap::Not;
}
if r_pinned == Pinnedness::Pinned {
pat_info.max_pinnedness = PinnednessCap::Pinned;
}
(expected, r_ty)
}
_ => {
let inner_ty = self.next_ty_var(inner.span);
let ref_ty = self.new_ref_ty(pat.span, pat_mutbl, inner_ty);
let ref_ty = self.new_ref_ty(pat.span, pat_pinned, pat_mutbl, inner_ty);
debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty);
let err = self.demand_eqtype_pat_diag(
pat.span,
@ -2864,10 +2902,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ref_ty
}
/// Create a reference type with a fresh region variable.
fn new_ref_ty(&self, span: Span, mutbl: Mutability, ty: Ty<'tcx>) -> Ty<'tcx> {
/// Create a reference or pinned reference type with a fresh region variable.
fn new_ref_ty(
&self,
span: Span,
pinnedness: Pinnedness,
mutbl: Mutability,
ty: Ty<'tcx>,
) -> Ty<'tcx> {
let region = self.next_region_var(RegionVariableOrigin::PatternRegion(span));
Ty::new_ref(self.tcx, region, ty, mutbl)
let ref_ty = Ty::new_ref(self.tcx, region, ty, mutbl);
if pinnedness.is_pinned() {
return self.new_pinned_ty(span, ref_ty);
}
ref_ty
}
/// Create a pinned type.
fn new_pinned_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
Ty::new_adt(
self.tcx,
self.tcx.adt_def(self.tcx.require_lang_item(LangItem::Pin, span)),
self.tcx.mk_args(&[ty.into()]),
)
}
fn error_inherited_ref_mutability_mismatch(

View file

@ -1705,7 +1705,7 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
}
let (parentheses, endpoints) = match &pat.kind {
PatKind::Ref(subpat, _) => (true, matches_ellipsis_pat(subpat)),
PatKind::Ref(subpat, _, _) => (true, matches_ellipsis_pat(subpat)),
_ => (false, matches_ellipsis_pat(pat)),
};

View file

@ -1307,7 +1307,8 @@ impl EarlyLintPass for UnusedParens {
Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
// Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342.
// Also avoid linting on `& mut? (p0 | .. | pn)`, #64106.
Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space),
// FIXME(pin_ergonomics): check pinned patterns
Ref(p, _, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space),
}
}

View file

@ -11,7 +11,7 @@ pub use basic_blocks::{BasicBlocks, SwitchTargetValue};
use either::Either;
use polonius_engine::Atom;
use rustc_abi::{FieldIdx, VariantIdx};
pub use rustc_ast::Mutability;
pub use rustc_ast::{Mutability, Pinnedness};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::graph::dominators::Dominators;
use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, ErrorGuaranteed, IntoDiagArg};

View file

@ -1366,6 +1366,19 @@ impl<'tcx> Ty<'tcx> {
None
}
pub fn maybe_pinned_ref(self) -> Option<(Ty<'tcx>, ty::Pinnedness, ty::Mutability)> {
match *self.kind() {
Adt(def, args)
if def.is_pin()
&& let ty::Ref(_, ty, mutbl) = *args.type_at(0).kind() =>
{
Some((ty, ty::Pinnedness::Pinned, mutbl))
}
ty::Ref(_, ty, mutbl) => Some((ty, ty::Pinnedness::Not, mutbl)),
_ => None,
}
}
/// Panics if called on any type other than `Box<T>`.
pub fn expect_boxed_ty(self) -> Ty<'tcx> {
self.boxed_ty()

View file

@ -284,14 +284,12 @@ impl<'tcx> MatchPairTree<'tcx> {
}
PatKind::Deref { ref subpattern }
| PatKind::DerefPattern { ref subpattern, borrow: ByRef::No } => {
if cfg!(debug_assertions) && matches!(pattern.kind, PatKind::DerefPattern { .. }) {
// Only deref patterns on boxes can be lowered using a built-in deref.
debug_assert!(pattern.ty.is_box());
}
| PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(Pinnedness::Pinned, _) }
if let Some(ref_ty) = pattern.ty.pinned_ty()
&& ref_ty.is_ref() =>
{
MatchPairTree::for_pattern(
place_builder.deref(),
place_builder.field(FieldIdx::ZERO, ref_ty).deref(),
subpattern,
cx,
&mut subpairs,
@ -300,12 +298,14 @@ impl<'tcx> MatchPairTree<'tcx> {
None
}
PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(Pinnedness::Pinned, _) } => {
let Some(ref_ty) = pattern.ty.pinned_ty() else {
rustc_middle::bug!("RefPin pattern on non-`Pin` type {:?}", pattern.ty);
};
PatKind::DerefPattern { borrow: ByRef::Yes(Pinnedness::Pinned, _), .. } => {
rustc_middle::bug!("RefPin pattern on non-`Pin` type {:?}", pattern.ty)
}
PatKind::Deref { ref subpattern }
| PatKind::DerefPattern { ref subpattern, borrow: ByRef::No } => {
MatchPairTree::for_pattern(
place_builder.field(FieldIdx::ZERO, ref_ty).deref(),
place_builder.deref(),
subpattern,
cx,
&mut subpairs,

View file

@ -97,7 +97,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
// adjustments in *reverse order* (last-in-first-out, so that the last `Deref` inserted
// gets the least-dereferenced type).
let unadjusted_pat = match pat.kind {
hir::PatKind::Ref(inner, _)
hir::PatKind::Ref(inner, _, _)
if self.typeck_results.skipped_ref_pats().contains(pat.hir_id) =>
{
self.lower_pattern(inner)
@ -319,7 +319,7 @@ impl<'a, 'tcx> PatCtxt<'a, '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, _, _) => {
// 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());
@ -370,10 +370,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
if let Some(pty) = ty.pinned_ty()
&& let &ty::Ref(_, rty, _) = pty.kind() =>
{
debug_assert!(
self.tcx.features().pin_ergonomics(),
"`pin_ergonomics` must be enabled to have a by-pin-ref binding"
);
ty = rty;
}
hir::Pinnedness::Not if let &ty::Ref(_, rty, _) = ty.kind() => {

View file

@ -2301,10 +2301,10 @@ impl<'a> Parser<'a> {
pat.span.shrink_to_hi(),
pat.span.shrink_to_lo(),
),
PatKind::Ref(ref inner_pat, _)
PatKind::Ref(ref inner_pat, _, _)
// Fix suggestions for multi-reference `self` parameters (e.g. `&&&self`)
// cc: https://github.com/rust-lang/rust/pull/146305
if let PatKind::Ref(_, _) = &inner_pat.kind
if let PatKind::Ref(_, _, _) = &inner_pat.kind
&& let PatKind::Path(_, path) = &pat.peel_refs().kind
&& let [a, ..] = path.segments.as_slice()
&& a.ident.name == kw::SelfLower =>
@ -2312,7 +2312,7 @@ impl<'a> Parser<'a> {
let mut inner = inner_pat;
let mut span_vec = vec![pat.span];
while let PatKind::Ref(ref inner_type, _) = inner.kind {
while let PatKind::Ref(ref inner_type, _, _) = inner.kind {
inner = inner_type;
span_vec.push(inner.span.shrink_to_lo());
}
@ -2334,10 +2334,10 @@ impl<'a> Parser<'a> {
return None;
}
// Also catches `fn foo(&a)`.
PatKind::Ref(ref inner_pat, mutab)
PatKind::Ref(ref inner_pat, pinned, mutab)
if let PatKind::Ident(_, ident, _) = inner_pat.clone().kind =>
{
let mutab = mutab.prefix_str();
let mutab = pinned.prefix_str(mutab);
(
ident,
"self: ",

View file

@ -863,14 +863,15 @@ impl<'a> Parser<'a> {
assert!(found_raw);
let mutability = self.parse_const_or_mut().unwrap();
(ast::BorrowKind::Raw, mutability)
} else if let Some((ast::Pinnedness::Pinned, mutbl)) = self.parse_pin_and_mut() {
} else {
match self.parse_pin_and_mut() {
// `mut?`
(ast::Pinnedness::Not, mutbl) => (ast::BorrowKind::Ref, mutbl),
// `pin [ const | mut ]`.
// `pin` has been gated in `self.parse_pin_and_mut()` so we don't
// need to gate it here.
(ast::BorrowKind::Pin, mutbl)
} else {
// `mut?`
(ast::BorrowKind::Ref, self.parse_mutability())
(ast::Pinnedness::Pinned, mutbl) => (ast::BorrowKind::Pin, mutbl),
}
}
}

View file

@ -36,8 +36,8 @@ use rustc_ast::tokenstream::{
use rustc_ast::util::case::Case;
use rustc_ast::{
self as ast, AnonConst, AttrArgs, AttrId, ByRef, Const, CoroutineKind, DUMMY_NODE_ID,
DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Pinnedness, Recovered,
Safety, StrLit, Visibility, VisibilityKind,
DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered, Safety, StrLit,
Visibility, VisibilityKind,
};
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap;
@ -1315,11 +1315,11 @@ impl<'a> Parser<'a> {
if self.eat_keyword(exp!(Mut)) { Mutability::Mut } else { Mutability::Not }
}
/// Parses reference binding mode (`ref`, `ref mut`, or nothing).
/// Parses reference binding mode (`ref`, `ref mut`, `ref pin const`, `ref pin mut`, or nothing).
fn parse_byref(&mut self) -> ByRef {
if self.eat_keyword(exp!(Ref)) {
// FIXME(pin_ergonomics): support `ref pin const|mut` bindings
ByRef::Yes(Pinnedness::Not, self.parse_mutability())
let (pinnedness, mutability) = self.parse_pin_and_mut();
ByRef::Yes(pinnedness, mutability)
} else {
ByRef::No
}

View file

@ -7,8 +7,7 @@ use rustc_ast::util::parser::ExprPrecedence;
use rustc_ast::visit::{self, Visitor};
use rustc_ast::{
self as ast, Arm, AttrVec, BindingMode, ByRef, Expr, ExprKind, LocalKind, MacCall, Mutability,
Pat, PatField, PatFieldsRest, PatKind, Path, Pinnedness, QSelf, RangeEnd, RangeSyntax, Stmt,
StmtKind,
Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt, StmtKind,
};
use rustc_ast_pretty::pprust;
use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey};
@ -661,7 +660,7 @@ impl<'a> Parser<'a> {
// Sub-patterns
// FIXME: this doesn't work with recursive subpats (`&mut &mut <err>`)
PatKind::Box(subpat) | PatKind::Ref(subpat, _)
PatKind::Box(subpat) | PatKind::Ref(subpat, _, _)
if matches!(subpat.kind, PatKind::Err(_) | PatKind::Expr(_)) =>
{
self.maybe_add_suggestions_then_emit(subpat.span, p.span, false)
@ -777,11 +776,10 @@ impl<'a> Parser<'a> {
self.bump();
self.dcx().emit_err(SwitchRefBoxOrder { span });
}
// Parse ref ident @ pat / ref mut ident @ pat
let mutbl = self.parse_mutability();
// Parse ref ident @ pat / ref mut ident @ pat / ref pin const|mut ident @ pat
let (pinned, mutbl) = self.parse_pin_and_mut();
self.parse_pat_ident(
// FIXME(pin_ergonomics): support `ref pin const|mut` bindings
BindingMode(ByRef::Yes(Pinnedness::Not, mutbl), Mutability::Not),
BindingMode(ByRef::Yes(pinned, mutbl), Mutability::Not),
syntax_loc,
)?
} else if self.eat_keyword(exp!(Box)) {
@ -982,7 +980,7 @@ impl<'a> Parser<'a> {
});
}
/// Parse `&pat` / `&mut pat`.
/// Parse `&pat` / `&mut pat` / `&pin const pat` / `&pin mut pat`.
fn parse_pat_deref(&mut self, expected: Option<Expected>) -> PResult<'a, PatKind> {
self.expect_and()?;
if let Some((lifetime, _)) = self.token.lifetime() {
@ -995,9 +993,9 @@ impl<'a> Parser<'a> {
});
}
let mutbl = self.parse_mutability();
let (pinned, mutbl) = self.parse_pin_and_mut();
let subpat = self.parse_pat_with_range_pat(false, expected, None)?;
Ok(PatKind::Ref(Box::new(subpat), mutbl))
Ok(PatKind::Ref(Box::new(subpat), pinned, mutbl))
}
/// Parse a tuple or parenthesis pattern.

View file

@ -142,6 +142,7 @@ pub enum TokenType {
SymNull,
SymOptions,
SymOut,
SymPin,
SymPreservesFlags,
SymPure,
SymReadonly,
@ -568,6 +569,7 @@ macro_rules! exp {
(Null) => { exp!(@sym, null, SymNull) };
(Options) => { exp!(@sym, options, SymOptions) };
(Out) => { exp!(@sym, out, SymOut) };
(Pin) => { exp!(@sym, pin, SymPin) };
(PreservesFlags) => { exp!(@sym, preserves_flags, SymPreservesFlags) };
(Pure) => { exp!(@sym, pure, SymPure) };
(Readonly) => { exp!(@sym, readonly, SymReadonly) };

View file

@ -728,10 +728,7 @@ impl<'a> Parser<'a> {
fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> {
let and_span = self.prev_token.span;
let mut opt_lifetime = self.check_lifetime().then(|| self.expect_lifetime());
let (pinned, mut mutbl) = match self.parse_pin_and_mut() {
Some(pin_mut) => pin_mut,
None => (Pinnedness::Not, self.parse_mutability()),
};
let (pinned, mut mutbl) = self.parse_pin_and_mut();
if self.token.is_lifetime() && mutbl == Mutability::Mut && opt_lifetime.is_none() {
// A lifetime is invalid here: it would be part of a bare trait bound, which requires
// it to be followed by a plus, but we disallow plus in the pointee type.
@ -773,28 +770,17 @@ impl<'a> Parser<'a> {
})
}
/// Parses `pin` and `mut` annotations on references.
/// Parses `pin` and `mut` annotations on references, patterns, or borrow modifiers.
///
/// It must be either `pin const` or `pin mut`.
pub(crate) fn parse_pin_and_mut(&mut self) -> Option<(Pinnedness, Mutability)> {
if self.token.is_ident_named(sym::pin) {
let result = self.look_ahead(1, |token| {
if token.is_keyword(kw::Const) {
Some((Pinnedness::Pinned, Mutability::Not))
} else if token.is_keyword(kw::Mut) {
Some((Pinnedness::Pinned, Mutability::Mut))
} else {
None
}
});
if result.is_some() {
/// It must be either `pin const`, `pin mut`, `mut`, or nothing (immutable).
pub(crate) fn parse_pin_and_mut(&mut self) -> (Pinnedness, Mutability) {
if self.token.is_ident_named(sym::pin) && self.look_ahead(1, Token::is_mutability) {
self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span);
self.bump();
self.bump();
}
result
assert!(self.eat_keyword(exp!(Pin)));
let mutbl = self.parse_const_or_mut().unwrap();
(Pinnedness::Pinned, mutbl)
} else {
None
(Pinnedness::Not, self.parse_mutability())
}
}

View file

@ -462,8 +462,12 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
PatKind::Deref { subpattern } => {
fields = vec![self.lower_pat(subpattern).at_index(0)];
arity = 1;
ctor = match ty.kind() {
ty::Ref(..) => Ref,
ctor = match ty.pinned_ref() {
None if ty.is_ref() => Ref,
Some((inner_ty, _)) => {
self.internal_state.has_lowered_deref_pat.set(true);
DerefPattern(RevealedTy(inner_ty))
}
_ => span_bug!(
pat.span,
"pattern has unexpected type: pat: {:?}, ty: {:?}",

View file

@ -303,7 +303,7 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol {
return kw::Underscore;
}
PatKind::Binding(_, _, ident, _) => return ident.name,
PatKind::Box(p) | PatKind::Ref(p, _) | PatKind::Guard(p, _) => return name_from_pat(p),
PatKind::Box(p) | PatKind::Ref(p, _, _) | PatKind::Guard(p, _) => return name_from_pat(p),
PatKind::TupleStruct(p, ..) | PatKind::Expr(PatExpr { kind: PatExprKind::Path(p), .. }) => {
qpath_to_string(p)
}

View file

@ -55,7 +55,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool {
| PatKind::Err(_) => false,
PatKind::Struct(_, a, etc) => etc.is_none() && a.iter().all(|x| unary_pattern(x.pat)),
PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a),
PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x),
PatKind::Ref(x, _, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x),
PatKind::Expr(_) => true,
}
}

View file

@ -43,7 +43,7 @@ pub(super) fn check<'tcx>(
let mut snippet = make_iterator_snippet(cx, arg, &mut applicability);
// Checks if `pat` is a single reference to a binding (`&x`)
let is_ref_to_binding =
matches!(pat.kind, PatKind::Ref(inner, _) if matches!(inner.kind, PatKind::Binding(..)));
matches!(pat.kind, PatKind::Ref(inner, _, _) if matches!(inner.kind, PatKind::Binding(..)));
// If `pat` is not a binding or a reference to a binding (`x` or `&x`)
// we need to map it to the binding returned by the function (i.e. `.map(|(x, _)| x)`)
if !(matches!(pat.kind, PatKind::Binding(..)) || is_ref_to_binding) {

View file

@ -157,7 +157,7 @@ fn check_iter(
),
);
},
hir::PatKind::Ref(pat, _) => make_span_lint_and_sugg(
hir::PatKind::Ref(pat, _, _) => make_span_lint_and_sugg(
cx,
parent_expr_span,
format!(
@ -196,7 +196,7 @@ fn check_to_owned(
&& let filter_body = cx.tcx.hir_body(closure.body)
&& let [filter_params] = filter_body.params
&& msrv.meets(cx, msrvs::STRING_RETAIN)
&& let hir::PatKind::Ref(pat, _) = filter_params.pat.kind
&& let hir::PatKind::Ref(pat, _, _) = filter_params.pat.kind
{
make_span_lint_and_sugg(
cx,

View file

@ -78,7 +78,7 @@ fn is_variant_or_wildcard(cx: &LateContext<'_>, pat: &Pat<'_>, can_be_wild: bool
.is_lang_item(cx, ResultErr)
== must_match_err
},
PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) => {
PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _, _) => {
is_variant_or_wildcard(cx, pat, can_be_wild, must_match_err)
},
_ => false,

View file

@ -254,7 +254,7 @@ pub(super) fn try_parse_pattern<'tcx>(
) -> Option<OptionPat<'tcx>> {
match pat.kind {
PatKind::Wild => Some(OptionPat::Wild),
PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
PatKind::Ref(pat, _, _) => f(cx, pat, ref_count + 1, ctxt),
PatKind::Expr(PatExpr {
kind: PatExprKind::Path(qpath),
hir_id,

View file

@ -50,7 +50,7 @@ where
}
let remaining_suggs = pats.filter_map(|pat| {
if let PatKind::Ref(refp, _) = pat.kind {
if let PatKind::Ref(refp, _, _) = pat.kind {
Some((pat.span, snippet(cx, refp.span, "..").to_string()))
} else {
None

View file

@ -264,7 +264,7 @@ impl<'a> NormalizedPat<'a> {
PatKind::Binding(.., Some(pat))
| PatKind::Box(pat)
| PatKind::Deref(pat)
| PatKind::Ref(pat, _)
| PatKind::Ref(pat, _, _)
| PatKind::Guard(pat, _) => Self::from_pat(cx, arena, pat),
PatKind::Never => Self::Never,
PatKind::Struct(ref path, fields, _) => {

View file

@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv:
&& !pat_contains_disallowed_or(cx, arm.pat, msrv)
{
let pat_span = match (arm.pat.kind, binding.byref_ident) {
(PatKind::Ref(pat, _), Some(_)) => pat.span,
(PatKind::Ref(pat, _, _), Some(_)) => pat.span,
(PatKind::Ref(..), None) | (_, Some(_)) => continue,
_ => arm.pat.span,
};
@ -49,7 +49,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv:
&& !pat_contains_disallowed_or(cx, let_expr.pat, msrv)
{
let pat_span = match (let_expr.pat.kind, binding.byref_ident) {
(PatKind::Ref(pat, _), Some(_)) => pat.span,
(PatKind::Ref(pat, _, _), Some(_)) => pat.span,
(PatKind::Ref(..), None) | (_, Some(_)) => continue,
_ => let_expr.pat.span,
};

View file

@ -186,7 +186,7 @@ fn find_method_sugg_for_if_let<'tcx>(
// also look inside refs
// if we have &None for example, peel it so we can detect "if let None = x"
let check_pat = match let_pat.kind {
PatKind::Ref(inner, _mutability) => inner,
PatKind::Ref(inner, _pinnedness, _mutability) => inner,
_ => let_pat,
};
let op_ty = cx.typeck_results().expr_ty(let_expr);

View file

@ -373,7 +373,7 @@ impl<'a> PatState<'a> {
},
// Patterns for things which can only contain a single sub-pattern.
PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) | PatKind::Box(pat) | PatKind::Deref(pat) => {
PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _, _) | PatKind::Box(pat) | PatKind::Deref(pat) => {
self.add_pat(cx, pat)
},
PatKind::Tuple([sub_pat], pos)

View file

@ -404,7 +404,7 @@ fn is_find_or_filter<'a>(
&& let filter_body = cx.tcx.hir_body(filter_body_id)
&& let [filter_param] = filter_body.params
// optional ref pattern: `filter(|&x| ..)`
&& let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind {
&& let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _, _) = filter_param.pat.kind {
(ref_pat, true)
} else {
(filter_param.pat, false)

View file

@ -50,7 +50,7 @@ fn is_method(
fn pat_is_recv(ident: Ident, param: &hir::Pat<'_>) -> bool {
match param.kind {
hir::PatKind::Binding(_, _, other, _) => ident == other,
hir::PatKind::Ref(pat, _) => pat_is_recv(ident, pat),
hir::PatKind::Ref(pat, _, _) => pat_is_recv(ident, pat),
_ => false,
}
}

View file

@ -81,7 +81,7 @@ pub(super) fn check<'tcx>(
}
match it.kind {
PatKind::Binding(BindingMode(_, Mutability::Mut), _, _, _) | PatKind::Ref(_, Mutability::Mut) => {
PatKind::Binding(BindingMode(_, Mutability::Mut), _, _, _) | PatKind::Ref(_, _, Mutability::Mut) => {
to_be_discarded = true;
false
},

View file

@ -8,7 +8,7 @@ use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
use rustc_hir::{self as hir, LangItem};
use rustc_lint::LateContext;
use rustc_middle::mir::Mutability;
use rustc_middle::mir::{Mutability, Pinnedness};
use rustc_middle::ty;
use rustc_middle::ty::adjustment::Adjust;
use rustc_span::symbol::Ident;
@ -50,7 +50,7 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_
let closure_body = cx.tcx.hir_body(body);
let closure_expr = peel_blocks(closure_body.value);
match closure_body.params[0].pat.kind {
hir::PatKind::Ref(inner, Mutability::Not) => {
hir::PatKind::Ref(inner, Pinnedness::Not, Mutability::Not) => {
if let hir::PatKind::Binding(hir::BindingMode::NONE, .., name, None) = inner.kind
&& ident_eq(name, closure_expr)
{

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_errors::Applicability;
use rustc_hir::{BindingMode, Mutability, Node, Pat, PatKind};
use rustc_hir::{BindingMode, Mutability, Node, Pat, PatKind, Pinnedness};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
@ -37,7 +37,7 @@ declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]);
impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef {
fn check_pat(&mut self, cx: &LateContext<'tcx>, ref_pat: &'tcx Pat<'_>) {
if let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind
if let PatKind::Ref(pat, Pinnedness::Not, Mutability::Not) = ref_pat.kind
&& !ref_pat.span.from_expansion()
&& cx
.tcx

View file

@ -7,7 +7,7 @@ use clippy_utils::msrvs::{self, MsrvStack};
use clippy_utils::over;
use rustc_ast::PatKind::*;
use rustc_ast::mut_visit::*;
use rustc_ast::{self as ast, DUMMY_NODE_ID, Mutability, Pat, PatKind};
use rustc_ast::{self as ast, DUMMY_NODE_ID, Mutability, Pat, PatKind, Pinnedness};
use rustc_ast_pretty::pprust;
use rustc_data_structures::thin_vec::{ThinVec, thin_vec};
use rustc_errors::Applicability;
@ -151,8 +151,8 @@ fn insert_necessary_parens(pat: &mut Box<Pat>) {
walk_pat(self, pat);
let target = match &mut pat.kind {
// `i @ a | b`, `box a | b`, and `& mut? a | b`.
Ident(.., Some(p)) | Box(p) | Ref(p, _) if matches!(&p.kind, Or(ps) if ps.len() > 1) => p,
Ref(p, Mutability::Not) if matches!(p.kind, Ident(BindingMode::MUT, ..)) => p, // `&(mut x)`
Ident(.., Some(p)) | Box(p) | Ref(p, _, _) if matches!(&p.kind, Or(ps) if ps.len() > 1) => p,
Ref(p, Pinnedness::Not, Mutability::Not) if matches!(p.kind, Ident(BindingMode::MUT, ..)) => p, // `&(mut x)`
_ => return,
};
target.kind = Paren(Box::new(take_pat(target)));
@ -241,7 +241,8 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<Pat>, focus_idx: usize
// Skip immutable refs, as grouping them saves few characters,
// and almost always requires adding parens (increasing noisiness).
// In the case of only two patterns, replacement adds net characters.
| Ref(_, Mutability::Not)
// FIXME(pin_ergonomics): handle pinned patterns
| Ref(_, _, Mutability::Not)
// Dealt with elsewhere.
| Or(_) | Paren(_) | Deref(_) | Guard(..) => false,
// Transform `box x | ... | box y` into `box (x | y)`.
@ -254,10 +255,10 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<Pat>, focus_idx: usize
|k| always_pat!(k, Box(p) => *p),
),
// Transform `&mut x | ... | &mut y` into `&mut (x | y)`.
Ref(target, Mutability::Mut) => extend_with_matching(
Ref(target, _, Mutability::Mut) => extend_with_matching(
target, start, alternatives,
|k| matches!(k, Ref(_, Mutability::Mut)),
|k| always_pat!(k, Ref(p, _) => *p),
|k| matches!(k, Ref(_, _, Mutability::Mut)),
|k| always_pat!(k, Ref(p, _, _) => *p),
),
// Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`.
Ident(b1, i1, Some(target)) => extend_with_matching(

View file

@ -793,9 +793,9 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
kind!("Deref({pat})");
self.pat(pat);
},
PatKind::Ref(pat, muta) => {
PatKind::Ref(pat, pinn, muta) => {
bind!(self, pat);
kind!("Ref({pat}, Mutability::{muta:?})");
kind!("Ref({pat}, Pinning::{pinn:?}, Mutability::{muta:?})");
self.pat(pat);
},
PatKind::Guard(pat, cond) => {

View file

@ -45,9 +45,8 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool {
&& eq_expr_opt(lt.as_deref(), rt.as_deref())
&& eq_range_end(&le.node, &re.node)
},
(Box(l), Box(r))
| (Ref(l, Mutability::Not), Ref(r, Mutability::Not))
| (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r),
(Box(l), Box(r)) => eq_pat(l, r),
(Ref(l, l_pin, l_mut), Ref(r, r_pin, r_mut)) => l_pin == r_pin && l_mut == r_mut && eq_pat(l, r),
(Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, eq_pat),
(Path(lq, lp), Path(rq, rp)) => both(lq.as_deref(), rq.as_deref(), eq_qself) && eq_path(lp, rp),
(TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => {

View file

@ -8,9 +8,9 @@ use rustc_data_structures::fx::FxHasher;
use rustc_hir::MatchSource::TryDesugar;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{
AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField,
ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeKind,
Node, Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind,
AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, ByRef, Closure, ConstArg, ConstArgKind, Expr,
ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime,
LifetimeKind, Node, Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind,
StructTailExpr, TraitBoundModifiers, Ty, TyKind, TyPat, TyPatKind,
};
use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
@ -538,7 +538,7 @@ impl HirEqInterExpr<'_, '_, '_> {
&& both(le.as_ref(), re.as_ref(), |a, b| self.eq_pat_expr(a, b))
&& (li == ri)
},
(PatKind::Ref(le, lm), PatKind::Ref(re, rm)) => lm == rm && self.eq_pat(le, re),
(PatKind::Ref(le, lp, lm), PatKind::Ref(re, rp, rm)) => lp == rp && lm == rm && self.eq_pat(le, re),
(PatKind::Slice(ls, li, le), PatKind::Slice(rs, ri, re)) => {
over(ls, rs, |l, r| self.eq_pat(l, r))
&& over(le, re, |l, r| self.eq_pat(l, r))
@ -1131,6 +1131,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
PatKind::Missing => unreachable!(),
PatKind::Binding(BindingMode(by_ref, mutability), _, _, pat) => {
std::mem::discriminant(by_ref).hash(&mut self.s);
if let ByRef::Yes(pi, mu) = by_ref {
std::mem::discriminant(pi).hash(&mut self.s);
std::mem::discriminant(mu).hash(&mut self.s);
}
std::mem::discriminant(mutability).hash(&mut self.s);
if let Some(pat) = pat {
self.hash_pat(pat);
@ -1152,8 +1156,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
}
std::mem::discriminant(i).hash(&mut self.s);
},
PatKind::Ref(pat, mu) => {
PatKind::Ref(pat, pi, mu) => {
self.hash_pat(pat);
std::mem::discriminant(pi).hash(&mut self.s);
std::mem::discriminant(mu).hash(&mut self.s);
},
PatKind::Guard(pat, guard) => {

View file

@ -1462,7 +1462,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
PatKind::Missing => unreachable!(),
PatKind::Wild | PatKind::Never => false, // If `!` typechecked then the type is empty, so not refutable.
PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
PatKind::Box(pat) | PatKind::Ref(pat, _, _) => is_refutable(cx, pat),
PatKind::Expr(PatExpr {
kind: PatExprKind::Path(qpath),
hir_id,
@ -1612,7 +1612,7 @@ pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) ->
}
pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
while let PatKind::Ref(subpat, _) = pat.kind {
while let PatKind::Ref(subpat, _, _) = pat.kind {
pat = subpat;
}
pat
@ -2157,7 +2157,7 @@ where
/// references removed.
pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
if let PatKind::Ref(pat, _) = pat.kind {
if let PatKind::Ref(pat, _, _) = pat.kind {
peel(pat, count + 1)
} else {
(pat, count)

View file

@ -17,7 +17,9 @@ use crate::shape::Shape;
use crate::source_map::SpanUtils;
use crate::spanned::Spanned;
use crate::types::{PathContext, rewrite_path};
use crate::utils::{format_mutability, mk_sp, mk_sp_lo_plus_one, rewrite_ident};
use crate::utils::{
format_mutability, format_pinnedness_and_mutability, mk_sp, mk_sp_lo_plus_one, rewrite_ident,
};
/// Returns `true` if the given pattern is "short".
/// A short pattern is defined by the following grammar:
@ -69,7 +71,7 @@ fn is_short_pattern_inner(context: &RewriteContext<'_>, pat: &ast::Pat) -> bool
}
ast::PatKind::Box(ref p)
| PatKind::Deref(ref p)
| ast::PatKind::Ref(ref p, _)
| ast::PatKind::Ref(ref p, _, _)
| ast::PatKind::Paren(ref p) => is_short_pattern_inner(context, &*p),
PatKind::Or(ref pats) => pats.iter().all(|p| is_short_pattern_inner(context, p)),
}
@ -133,10 +135,13 @@ impl Rewrite for Pat {
PatKind::Ident(BindingMode(by_ref, mutability), ident, ref sub_pat) => {
let mut_prefix = format_mutability(mutability).trim();
let (ref_kw, mut_infix) = match by_ref {
// FIXME(pin_ergonomics): format the pinnedness
ByRef::Yes(_, rmutbl) => ("ref", format_mutability(rmutbl).trim()),
ByRef::No => ("", ""),
let (ref_kw, pin_infix, mut_infix) = match by_ref {
ByRef::Yes(pinnedness, rmutbl) => {
let (pin_infix, mut_infix) =
format_pinnedness_and_mutability(pinnedness, rmutbl);
("ref", pin_infix.trim(), mut_infix.trim())
}
ByRef::No => ("", "", ""),
};
let id_str = rewrite_ident(context, ident);
let sub_pat = match *sub_pat {
@ -147,6 +152,7 @@ impl Rewrite for Pat {
.checked_sub(
mut_prefix.len()
+ ref_kw.len()
+ pin_infix.len()
+ mut_infix.len()
+ id_str.len()
+ 2,
@ -193,18 +199,17 @@ impl Rewrite for Pat {
(true, true) => (self.span.lo(), "".to_owned()),
};
// combine result of above and mut
let (second_lo, second) = match (first.is_empty(), mut_infix.is_empty()) {
// combine result of above and pin
let (second_lo, second) = match (first.is_empty(), pin_infix.is_empty()) {
(false, false) => {
let lo = context.snippet_provider.span_after(self.span, "ref");
let end_span = mk_sp(first_lo, self.span.hi());
let hi = context.snippet_provider.span_before(end_span, "mut");
let hi = context.snippet_provider.span_before(self.span, "pin");
(
context.snippet_provider.span_after(end_span, "mut"),
context.snippet_provider.span_after(self.span, "pin"),
combine_strs_with_missing_comments(
context,
&first,
mut_infix,
pin_infix,
mk_sp(lo, hi),
shape,
true,
@ -212,7 +217,33 @@ impl Rewrite for Pat {
)
}
(false, true) => (first_lo, first),
(true, false) => unreachable!("mut_infix necessarily follows a ref"),
(true, false) => unreachable!("pin_infix necessarily follows a ref"),
(true, true) => (self.span.lo(), "".to_owned()),
};
// combine result of above and const|mut
let (third_lo, third) = match (second.is_empty(), mut_infix.is_empty()) {
(false, false) => {
let lo = context.snippet_provider.span_after(
self.span,
if pin_infix.is_empty() { "ref" } else { "pin" },
);
let end_span = mk_sp(second_lo, self.span.hi());
let hi = context.snippet_provider.span_before(end_span, mut_infix);
(
context.snippet_provider.span_after(end_span, mut_infix),
combine_strs_with_missing_comments(
context,
&second,
mut_infix,
mk_sp(lo, hi),
shape,
true,
)?,
)
}
(false, true) => (second_lo, second),
(true, false) => unreachable!("mut_infix necessarily follows a pin or ref"),
(true, true) => (self.span.lo(), "".to_owned()),
};
@ -232,9 +263,9 @@ impl Rewrite for Pat {
combine_strs_with_missing_comments(
context,
&second,
&third,
&next,
mk_sp(second_lo, ident.span.lo()),
mk_sp(third_lo, ident.span.lo()),
shape,
true,
)
@ -263,8 +294,10 @@ impl Rewrite for Pat {
PatKind::Range(ref lhs, ref rhs, ref end_kind) => {
rewrite_range_pat(context, shape, lhs, rhs, end_kind, self.span)
}
PatKind::Ref(ref pat, mutability) => {
let prefix = format!("&{}", format_mutability(mutability));
PatKind::Ref(ref pat, pinnedness, mutability) => {
let (pin_prefix, mut_prefix) =
format_pinnedness_and_mutability(pinnedness, mutability);
let prefix = format!("&{}{}", pin_prefix, mut_prefix);
rewrite_unary_prefix(context, &prefix, &**pat, shape)
}
PatKind::Tuple(ref items) => rewrite_tuple_pat(items, None, self.span, context, shape),
@ -551,7 +584,7 @@ pub(crate) fn can_be_overflowed_pat(
| ast::PatKind::Tuple(..)
| ast::PatKind::Struct(..)
| ast::PatKind::TupleStruct(..) => context.use_block_indent() && len == 1,
ast::PatKind::Ref(ref p, _) | ast::PatKind::Box(ref p) => {
ast::PatKind::Ref(ref p, _, _) | ast::PatKind::Box(ref p) => {
can_be_overflowed_pat(context, &TuplePatField::Pat(p), len)
}
ast::PatKind::Expr(ref expr) => can_be_overflowed_expr(context, expr, len),

View file

@ -132,6 +132,19 @@ pub(crate) fn format_mutability(mutability: ast::Mutability) -> &'static str {
}
}
#[inline]
pub(crate) fn format_pinnedness_and_mutability(
pinnedness: ast::Pinnedness,
mutability: ast::Mutability,
) -> (&'static str, &'static str) {
match (pinnedness, mutability) {
(ast::Pinnedness::Pinned, ast::Mutability::Mut) => ("pin ", "mut "),
(ast::Pinnedness::Pinned, ast::Mutability::Not) => ("pin ", "const "),
(ast::Pinnedness::Not, ast::Mutability::Mut) => ("", "mut "),
(ast::Pinnedness::Not, ast::Mutability::Not) => ("", ""),
}
}
#[inline]
pub(crate) fn format_extern(ext: ast::Extern, explicit_abi: bool) -> Cow<'static, str> {
match ext {

View file

@ -28,3 +28,22 @@ fn borrows() {
pin const
foo;
}
fn patterns<'a>(
&pin mut x: &pin
mut
i32,
&
pin
const
y: &
'a pin
const
i32,
ref pin mut z: i32,
mut
ref
pin
const
w: i32,
) {}

View file

@ -23,3 +23,11 @@ fn borrows() {
let x: Pin<&_> = &pin const foo;
}
fn patterns<'a>(
&pin mut x: &pin mut i32,
&pin const y: &'a pin const i32,
ref pin mut z: i32,
mut ref pin const w: i32,
) {
}

View file

@ -41,4 +41,7 @@ fn bar() {
foo_const(x);
}
fn patterns<'a>(&pin mut x: Pin<&'_ mut i32>, &pin const y: Pin<&'a i32>,
ref pin mut z: i32, ref pin const w: i32) { }
fn main() { }

View file

@ -37,4 +37,11 @@ fn bar() {
foo_const(x);
}
fn patterns<'a>(
&pin mut x: Pin<&mut i32>,
&pin const y: Pin<&'a i32>,
ref pin mut z: i32,
ref pin const w: i32,
) {}
fn main() { }

View file

@ -34,4 +34,7 @@ fn bar() {
foo_const(x);
}
fn patterns<'a>(&pin mut x: &pin mut i32, &pin const y: &'a pin const i32,
ref pin mut z: i32, ref pin const w: i32) {}
fn main() {}

View file

@ -47,6 +47,17 @@ fn borrows() {
foo_const(x);
}
fn patterns<'a>(
&pin mut x: &pin mut i32,
//~^ ERROR pinned reference syntax is experimental
//~| ERROR pinned reference syntax is experimental
&pin const y: &'a pin const i32,
//~^ ERROR pinned reference syntax is experimental
//~| ERROR pinned reference syntax is experimental
ref pin mut z: i32, //~ ERROR pinned reference syntax is experimental
ref pin const w: i32, //~ ERROR pinned reference syntax is experimental
) {}
#[cfg(any())]
mod not_compiled {
use std::pin::Pin;
@ -91,6 +102,17 @@ mod not_compiled {
foo_const(x);
foo_const(x);
}
fn patterns<'a>(
&pin mut x: &pin mut i32,
//~^ ERROR pinned reference syntax is experimental
//~| ERROR pinned reference syntax is experimental
&pin const y: &'a pin const i32,
//~^ ERROR pinned reference syntax is experimental
//~| ERROR pinned reference syntax is experimental
ref pin mut z: i32, //~ ERROR pinned reference syntax is experimental
ref pin const w: i32, //~ ERROR pinned reference syntax is experimental
) {}
}
fn main() {}

View file

@ -79,7 +79,67 @@ LL | let x: Pin<&_> = &pin const Foo;
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: pinned reference syntax is experimental
--> $DIR/feature-gate-pin_ergonomics.rs:59:23
--> $DIR/feature-gate-pin_ergonomics.rs:51:6
|
LL | &pin mut x: &pin mut i32,
| ^^^
|
= note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
= help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: pinned reference syntax is experimental
--> $DIR/feature-gate-pin_ergonomics.rs:51:18
|
LL | &pin mut x: &pin mut i32,
| ^^^
|
= note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
= help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: pinned reference syntax is experimental
--> $DIR/feature-gate-pin_ergonomics.rs:54:6
|
LL | &pin const y: &'a pin const i32,
| ^^^
|
= note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
= help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: pinned reference syntax is experimental
--> $DIR/feature-gate-pin_ergonomics.rs:54:23
|
LL | &pin const y: &'a pin const i32,
| ^^^
|
= note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
= help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: pinned reference syntax is experimental
--> $DIR/feature-gate-pin_ergonomics.rs:57:9
|
LL | ref pin mut z: i32,
| ^^^
|
= note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
= help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: pinned reference syntax is experimental
--> $DIR/feature-gate-pin_ergonomics.rs:58:9
|
LL | ref pin const w: i32,
| ^^^
|
= note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
= help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: pinned reference syntax is experimental
--> $DIR/feature-gate-pin_ergonomics.rs:70:23
|
LL | fn foo_sugar(&pin mut self) {}
| ^^^
@ -89,7 +149,7 @@ LL | fn foo_sugar(&pin mut self) {}
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: pinned reference syntax is experimental
--> $DIR/feature-gate-pin_ergonomics.rs:60:29
--> $DIR/feature-gate-pin_ergonomics.rs:71:29
|
LL | fn foo_sugar_const(&pin const self) {}
| ^^^
@ -99,7 +159,7 @@ LL | fn foo_sugar_const(&pin const self) {}
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: pinned reference syntax is experimental
--> $DIR/feature-gate-pin_ergonomics.rs:66:18
--> $DIR/feature-gate-pin_ergonomics.rs:77:18
|
LL | let _y: &pin mut Foo = x;
| ^^^
@ -109,7 +169,7 @@ LL | let _y: &pin mut Foo = x;
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: pinned reference syntax is experimental
--> $DIR/feature-gate-pin_ergonomics.rs:69:22
--> $DIR/feature-gate-pin_ergonomics.rs:80:22
|
LL | fn foo_sugar(_: &pin mut Foo) {}
| ^^^
@ -119,7 +179,7 @@ LL | fn foo_sugar(_: &pin mut Foo) {}
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: pinned reference syntax is experimental
--> $DIR/feature-gate-pin_ergonomics.rs:81:22
--> $DIR/feature-gate-pin_ergonomics.rs:92:22
|
LL | fn baz_sugar(_: &pin const Foo) {}
| ^^^
@ -129,7 +189,7 @@ LL | fn baz_sugar(_: &pin const Foo) {}
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: pinned reference syntax is experimental
--> $DIR/feature-gate-pin_ergonomics.rs:84:35
--> $DIR/feature-gate-pin_ergonomics.rs:95:35
|
LL | let mut x: Pin<&mut _> = &pin mut Foo;
| ^^^
@ -139,7 +199,7 @@ LL | let mut x: Pin<&mut _> = &pin mut Foo;
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: pinned reference syntax is experimental
--> $DIR/feature-gate-pin_ergonomics.rs:89:27
--> $DIR/feature-gate-pin_ergonomics.rs:100:27
|
LL | let x: Pin<&_> = &pin const Foo;
| ^^^
@ -148,6 +208,66 @@ LL | let x: Pin<&_> = &pin const Foo;
= help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: pinned reference syntax is experimental
--> $DIR/feature-gate-pin_ergonomics.rs:107:10
|
LL | &pin mut x: &pin mut i32,
| ^^^
|
= note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
= help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: pinned reference syntax is experimental
--> $DIR/feature-gate-pin_ergonomics.rs:107:22
|
LL | &pin mut x: &pin mut i32,
| ^^^
|
= note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
= help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: pinned reference syntax is experimental
--> $DIR/feature-gate-pin_ergonomics.rs:110:10
|
LL | &pin const y: &'a pin const i32,
| ^^^
|
= note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
= help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: pinned reference syntax is experimental
--> $DIR/feature-gate-pin_ergonomics.rs:110:27
|
LL | &pin const y: &'a pin const i32,
| ^^^
|
= note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
= help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: pinned reference syntax is experimental
--> $DIR/feature-gate-pin_ergonomics.rs:113:13
|
LL | ref pin mut z: i32,
| ^^^
|
= note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
= help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: pinned reference syntax is experimental
--> $DIR/feature-gate-pin_ergonomics.rs:114:13
|
LL | ref pin const w: i32,
| ^^^
|
= note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
= help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: the `#[pin_v2]` attribute is an experimental feature
--> $DIR/feature-gate-pin_ergonomics.rs:5:1
|
@ -196,7 +316,7 @@ help: consider reborrowing the `Pin` instead of moving it
LL | x.as_mut().foo();
| +++++++++
error: aborting due to 18 previous errors
error: aborting due to 30 previous errors
Some errors have detailed explanations: E0382, E0658.
For more information about an error, try `rustc --explain E0382`.

View file

@ -0,0 +1,114 @@
//@ edition:2024
#![feature(pin_ergonomics)]
#![allow(incomplete_features)]
// This test verifies:
// - a `&pin mut $pat` can be used to match on a pinned reference type `&pin mut T`;
// - the subpattern can only convert the binding mode `&pin mut` to `&mut` when `T: Unpin`;
// - the subpattern can only remove the binding mode `&pin mut` when `T: Copy`;
#[pin_v2]
struct Foo<T>(T);
trait IsPinMut {}
trait IsPinConst {}
trait IsMut {}
trait IsRef {}
impl<T: ?Sized> IsPinMut for &pin mut T {}
impl<T: ?Sized> IsPinConst for &pin const T {}
impl<T: ?Sized> IsMut for &mut T {}
impl<T: ?Sized> IsRef for &T {}
fn assert_pin_mut<T: IsPinMut>(_: T) {}
fn assert_pin_const<T: IsPinConst>(_: T) {}
fn assert_mut<T: IsMut>(_: T) {}
fn assert_ref<T: IsRef>(_: T) {}
fn assert_ty<T>(_: T) {}
fn normal<T>(foo_mut: &pin mut Foo<T>, foo_const: &pin const Foo<T>) {
let &pin mut Foo(ref pin mut x) = foo_mut; // ok
assert_pin_mut(x);
let &pin const Foo(ref pin const x) = foo_const; // ok
assert_pin_const(x);
}
fn by_value_copy<T: Copy>(foo_mut: &pin mut Foo<T>, foo_const: &pin const Foo<T>) {
let &pin mut Foo(x) = foo_mut;
assert_ty::<T>(x);
let &pin const Foo(x) = foo_const;
assert_ty::<T>(x);
}
fn by_value_non_copy<T>(foo_mut: &pin mut Foo<T>, foo_const: &pin const Foo<T>) {
let &pin mut Foo(x) = foo_mut;
//~^ ERROR cannot move out of `foo_mut.pointer` which is behind a mutable reference
assert_ty::<T>(x);
let &pin const Foo(x) = foo_const;
//~^ ERROR cannot move out of `foo_const.pointer` which is behind a shared reference
assert_ty::<T>(x);
}
fn by_ref_non_pinned_unpin<T: Unpin>(foo_mut: &pin mut Foo<T>, foo_const: &pin const Foo<T>) {
let &pin mut Foo(ref mut x) = foo_mut;
assert_mut(x);
let &pin const Foo(ref x) = foo_const;
assert_ref(x);
}
fn by_ref_non_pinned_non_unpin<T>(foo_mut: &pin mut Foo<T>, foo_const: &pin const Foo<T>) {
let &pin mut Foo(ref mut x) = foo_mut;
//~^ ERROR `T` cannot be unpinned
assert_mut(x);
let &pin const Foo(ref x) = foo_const;
//~^ ERROR `T` cannot be unpinned
assert_ref(x);
}
// Makes sure that `ref pin` binding mode cannot be changed to a `ref` binding mode.
//
// This mimics the following code:
// ```
// fn f<'a, T>(
// ((&x,),): &'a (&'a mut (&'a T,),),
// ) -> T {
// x
// }
// ```
fn tuple_tuple_ref_pin_mut_pat_and_pin_mut_of_tuple_mut_of_tuple_pin_mut_ty<'a, T>(
((&pin mut x,),): &'a pin mut (&'a mut (&'a pin mut Foo<T>,),),
//~^ ERROR cannot explicitly dereference within an implicitly-borrowing pattern
//~| ERROR cannot move out of a mutable reference
) -> Foo<T> {
x
}
fn tuple_ref_pin_mut_pat_and_mut_of_tuple_pin_mut_ty<'a, T>(
(&pin mut x,): &'a mut (&'a pin mut Foo<T>,), //~ ERROR `T` cannot be unpinned
) -> &'a mut Foo<T> {
x
}
fn tuple_ref_pin_mut_pat_and_mut_of_mut_tuple_pin_mut_ty<'a, T>(
(&pin mut x,): &'a mut &'a pin mut (Foo<T>,), //~ ERROR mismatched type
) -> &'a mut Foo<T> {
x
}
fn ref_pin_mut_tuple_pat_and_mut_of_tuple_pin_mut_ty<'a, T>(
&pin mut (x,): &'a mut (&'a pin mut Foo<T>,), //~ ERROR mismatched type
) -> &'a mut Foo<T> {
x
}
fn ref_pin_mut_tuple_pat_and_mut_of_mut_tuple_pin_mut_ty<'a, T>(
&pin mut (x,): &'a mut &'a pin mut (Foo<T>,), //~ ERROR mismatched type
) -> &'a mut Foo<T> {
x
}
fn main() {}

View file

@ -0,0 +1,152 @@
error[E0277]: `T` cannot be unpinned
--> $DIR/pin-pattern.rs:63:22
|
LL | let &pin mut Foo(ref mut x) = foo_mut;
| ^^^^^^^^^ the trait `Unpin` is not implemented for `T`
|
= note: consider using the `pin!` macro
consider using `Box::pin` if you need to access the pinned value outside of the current scope
help: consider restricting type parameter `T` with trait `Unpin`
|
LL | fn by_ref_non_pinned_non_unpin<T: std::marker::Unpin>(foo_mut: &pin mut Foo<T>, foo_const: &pin const Foo<T>) {
| ++++++++++++++++++++
error[E0277]: `T` cannot be unpinned
--> $DIR/pin-pattern.rs:67:24
|
LL | let &pin const Foo(ref x) = foo_const;
| ^^^^^ the trait `Unpin` is not implemented for `T`
|
= note: consider using the `pin!` macro
consider using `Box::pin` if you need to access the pinned value outside of the current scope
help: consider restricting type parameter `T` with trait `Unpin`
|
LL | fn by_ref_non_pinned_non_unpin<T: std::marker::Unpin>(foo_mut: &pin mut Foo<T>, foo_const: &pin const Foo<T>) {
| ++++++++++++++++++++
error[E0277]: `T` cannot be unpinned
--> $DIR/pin-pattern.rs:91:15
|
LL | (&pin mut x,): &'a mut (&'a pin mut Foo<T>,),
| ^ within `Foo<T>`, the trait `Unpin` is not implemented for `T`
|
= note: consider using the `pin!` macro
consider using `Box::pin` if you need to access the pinned value outside of the current scope
note: required because it appears within the type `Foo<T>`
--> $DIR/pin-pattern.rs:11:8
|
LL | struct Foo<T>(T);
| ^^^
help: consider restricting type parameter `T` with trait `Unpin`
|
LL | fn tuple_ref_pin_mut_pat_and_mut_of_tuple_pin_mut_ty<'a, T: std::marker::Unpin>(
| ++++++++++++++++++++
error[E0308]: mismatched types
--> $DIR/pin-pattern.rs:97:6
|
LL | (&pin mut x,): &'a mut &'a pin mut (Foo<T>,),
| ^^^^^^^^^^ ----------------------------- expected due to this
| |
| expected `Foo<T>`, found `Pin<&mut _>`
|
= note: expected struct `Foo<T>`
found struct `Pin<&mut _>`
note: to declare a mutable binding use: `mut x`
--> $DIR/pin-pattern.rs:97:6
|
LL | (&pin mut x,): &'a mut &'a pin mut (Foo<T>,),
| ^^^^^^^^^^
help: consider removing `&pin mut` from the pattern
|
LL - (&pin mut x,): &'a mut &'a pin mut (Foo<T>,),
LL + (x,): &'a mut &'a pin mut (Foo<T>,),
|
error[E0308]: mismatched types
--> $DIR/pin-pattern.rs:103:5
|
LL | &pin mut (x,): &'a mut (&'a pin mut Foo<T>,),
| ^^^^^^^^^^^^^ ----------------------------- expected due to this
| |
| expected `&mut (Pin<&mut Foo<T>>,)`, found `Pin<&mut _>`
|
= note: expected mutable reference `&'a mut (Pin<&'a mut Foo<T>>,)`
found struct `Pin<&mut _>`
error[E0308]: mismatched types
--> $DIR/pin-pattern.rs:109:5
|
LL | &pin mut (x,): &'a mut &'a pin mut (Foo<T>,),
| ^^^^^^^^^^^^^ ----------------------------- expected due to this
| |
| expected `&mut Pin<&mut (Foo<T>,)>`, found `Pin<&mut _>`
|
= note: expected mutable reference `&'a mut Pin<&'a mut (Foo<T>,)>`
found struct `Pin<&mut _>`
error[E0507]: cannot move out of `foo_mut.pointer` which is behind a mutable reference
--> $DIR/pin-pattern.rs:45:27
|
LL | let &pin mut Foo(x) = foo_mut;
| - ^^^^^^^
| |
| data moved here
| move occurs because `x` has type `T`, which does not implement the `Copy` trait
|
help: consider removing the pinned mutable borrow
|
LL - let &pin mut Foo(x) = foo_mut;
LL + let Foo(x) = foo_mut;
|
error[E0507]: cannot move out of `foo_const.pointer` which is behind a shared reference
--> $DIR/pin-pattern.rs:49:29
|
LL | let &pin const Foo(x) = foo_const;
| - ^^^^^^^^^
| |
| data moved here
| move occurs because `x` has type `T`, which does not implement the `Copy` trait
|
help: consider removing the pinned borrow
|
LL - let &pin const Foo(x) = foo_const;
LL + let Foo(x) = foo_const;
|
error: cannot explicitly dereference within an implicitly-borrowing pattern
--> $DIR/pin-pattern.rs:83:7
|
LL | ((&pin mut x,),): &'a pin mut (&'a mut (&'a pin mut Foo<T>,),),
| ^^^^^^^^ reference pattern not allowed when implicitly borrowing
|
= note: for more information, see <https://doc.rust-lang.org/reference/patterns.html#binding-modes>
note: matching on a reference type with a non-reference pattern implicitly borrows the contents
--> $DIR/pin-pattern.rs:83:6
|
LL | ((&pin mut x,),): &'a pin mut (&'a mut (&'a pin mut Foo<T>,),),
| ^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&mut _`
help: match on the reference with a reference pattern to avoid implicitly borrowing
|
LL | (&mut (&pin mut x,),): &'a pin mut (&'a mut (&'a pin mut Foo<T>,),),
| ++++
error[E0507]: cannot move out of a mutable reference
--> $DIR/pin-pattern.rs:83:5
|
LL | ((&pin mut x,),): &'a pin mut (&'a mut (&'a pin mut Foo<T>,),),
| ^^^^^^^^^^^-^^^^
| |
| data moved here
| move occurs because `x` has type `Foo<T>`, which does not implement the `Copy` trait
|
help: consider borrowing the pattern binding
|
LL | ((&pin mut ref x,),): &'a pin mut (&'a mut (&'a pin mut Foo<T>,),),
| +++
error: aborting due to 10 previous errors
Some errors have detailed explanations: E0277, E0308, E0507.
For more information about an error, try `rustc --explain E0277`.

View file

@ -8,8 +8,16 @@ struct Foo;
mod pin {
pub struct Foo;
#[expect(non_camel_case_types)]
pub struct pin;
fn foo() {
let _x: &pin = &pin;
}
}
fn main() {
let _x: &pin ::Foo = &pin::Foo;
let _x: &pin::Foo = &pin::Foo;
let &pin: &i32 = &0;
let ref pin: i32 = 0;
}

View file

@ -3,6 +3,22 @@
// Makes sure we don't accidentally accept `&pin Foo` without the `const` keyword.
fn main() {
fn ty() {
let _x: &pin i32 = todo!(); //~ ERROR found `i32`
}
fn expr() {
let x = 0_i32;
let _x = &pin x; //~ ERROR found `x`
}
fn pat() {
let &pin _x: &pin i32 = todo!(); //~ ERROR found `_x`
}
fn binding() {
let ref pin _x: i32 = todo!(); //~ ERROR found `_x`
}
fn main() {
}

View file

@ -12,5 +12,41 @@ LL - let _x: &pin i32 = todo!();
LL + let _x: &in i32 = todo!();
|
error: aborting due to 1 previous error
error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `x`
--> $DIR/sugar-no-const.rs:12:19
|
LL | let _x = &pin x;
| ^ expected one of 8 possible tokens
|
help: there is a keyword `in` with a similar name
|
LL - let _x = &pin x;
LL + let _x = &in x;
|
error: expected one of `:`, `;`, `=`, `@`, or `|`, found `_x`
--> $DIR/sugar-no-const.rs:16:14
|
LL | let &pin _x: &pin i32 = todo!();
| ^^ expected one of `:`, `;`, `=`, `@`, or `|`
|
help: there is a keyword `in` with a similar name
|
LL - let &pin _x: &pin i32 = todo!();
LL + let &in _x: &pin i32 = todo!();
|
error: expected one of `:`, `;`, `=`, `@`, or `|`, found `_x`
--> $DIR/sugar-no-const.rs:20:17
|
LL | let ref pin _x: i32 = todo!();
| ^^ expected one of `:`, `;`, `=`, `@`, or `|`
|
help: there is a keyword `in` with a similar name
|
LL - let ref pin _x: i32 = todo!();
LL + let ref in _x: i32 = todo!();
|
error: aborting due to 4 previous errors