diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 8f44931826398..607c43522ecb9 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -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), @@ -756,7 +757,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, } } @@ -904,7 +907,7 @@ pub enum PatKind { Deref(Box), /// A reference pattern (e.g., `&mut (a, b)`). - Ref(Box, Mutability), + Ref(Box, Pinnedness, Mutability), /// A literal, const block or path. Expr(Box), diff --git a/compiler/rustc_ast_ir/src/lib.rs b/compiler/rustc_ast_ir/src/lib.rs index 0f63fad157431..fb00f5d9b6236 100644 --- a/compiler/rustc_ast_ir/src/lib.rs +++ b/compiler/rustc_ast_ir/src/lib.rs @@ -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) => "", + } + } } diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 815338c84fa6c..3571fd6523974 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -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( diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 93f4e47342f14..21b14f1610779 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -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 "); } diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index be2a5b1ebe22d..3322c590a6cee 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -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(); diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index e23c7eebeca1e..291feea144091 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -765,11 +765,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 { @@ -1444,7 +1444,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) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 3272f34a54526..88392b91d9f22 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1697,7 +1697,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) => { @@ -1724,7 +1726,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) => { @@ -1913,7 +1915,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>), diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 232178ee403a6..7427921619a6c 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -748,7 +748,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) => { diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index bac34454b3b7a..1a28321e1d5a8 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -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 diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 5bf449a97dcd2..34db5029a0e2d 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -2020,9 +2020,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(); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index e720958746f7a..3631006ad9574 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -514,7 +514,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(_, _, _) diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index b9f1463d8007d..561230c193ce2 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -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. diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 8e9c6eb046aed..010f7b19d7412 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -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 { diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index f27085d59c0ed..f11f9c8e2c47f 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -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>, @@ -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( diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index d3468499b4b3a..684a8c72b3a58 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -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)), }; diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 533ab67053095..17effe6890541 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1304,7 +1304,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), } } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index fab04df500a5c..3012f7f7aba8a 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -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}; diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 953c806658aef..d8f16ef5561db 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -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`. pub fn expect_boxed_ty(self) -> Ty<'tcx> { self.boxed_ty() 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 af565ef636993..a0d54354a9c63 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -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, diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 404f410e22191..8531c5f39f84f 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -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() => { diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index c36cd610c6a59..44cb938576bf1 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2303,10 +2303,10 @@ impl<'a> Parser<'a> { pat.span.shrink_to_lo(), ), // 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: ", diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e7c89f0796224..9e7d4bca37d05 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -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() { - // `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()) + 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::Pinnedness::Pinned, mutbl) => (ast::BorrowKind::Pin, mutbl), + } } } diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 30870c810942f..203a93b52012b 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -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 } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index f964ecb90326f..0e9796c04c8ab 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -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 `) - 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) -> 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. diff --git a/compiler/rustc_parse/src/parser/token_type.rs b/compiler/rustc_parse/src/parser/token_type.rs index 4c4f09ab3ea8a..08c5b06575cd4 100644 --- a/compiler/rustc_parse/src/parser/token_type.rs +++ b/compiler/rustc_parse/src/parser/token_type.rs @@ -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) }; diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 65347496599d7..6b69bc096bfd4 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -693,10 +693,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. @@ -738,28 +735,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() { - self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span); - self.bump(); - self.bump(); - } - result + /// 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); + assert!(self.eat_keyword(exp!(Pin))); + let mutbl = self.parse_const_or_mut().unwrap(); + (Pinnedness::Pinned, mutbl) } else { - None + (Pinnedness::Not, self.parse_mutability()) } } diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 8df34ee941b21..c3f6eaccfabca 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -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: {:?}", diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 23aaa6cf98604..060abfd05e042 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -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) } diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs index c3fc09343dbfe..9b5cd7e1731f3 100644 --- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs +++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs @@ -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, } } diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs index c38cf83f44100..3455a47ba078d 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs @@ -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) { diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs index 674f0da818f5f..6870c9819fc06 100644 --- a/src/tools/clippy/clippy_lints/src/manual_retain.rs +++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs @@ -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, diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs index c9293412fba82..9ced6c9d452ba 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs @@ -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, diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs index 235cb9e4ecce8..19b3572bd3ae7 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs @@ -254,7 +254,7 @@ pub(super) fn try_parse_pattern<'tcx>( ) -> Option> { 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, diff --git a/src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs b/src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs index 5934ec409935a..042817f5cdb82 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs @@ -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 diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index be914429edb44..c20217563d62b 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -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, _) => { diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs index 7a1dd94567b14..757ecf75ed45e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs @@ -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, }; diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index a0f88cf911d84..bc3783750e5cc 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -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); diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index 44c4d7a31ff31..57a91cf846b83 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -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) diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs index 26b19848fe1b6..7b10c37de42df 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs @@ -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) diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs index 8d95b70c6a4bc..aaface3aa971d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs @@ -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, } } diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs index d43dc23a86b23..1c26648e26eb1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -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 }, diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs index 1bc29c9c1dd1c..a1aac96ccf86a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs @@ -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) { diff --git a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs index 1b6896827fed8..29b4da93b7fb2 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs @@ -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 diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index 4621c22d6b532..bbb1b831888fd 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -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) { 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, 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, 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( diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 129e8a45aa892..63fdfb31a1aaa 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -792,9 +792,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) => { diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index e797c96156045..567e54d11a040 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -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)) => { diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 710b88e92154e..c1a28edbf09bc 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -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}; @@ -536,7 +536,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)) @@ -1129,6 +1129,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); @@ -1150,8 +1154,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) => { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 4c0d70d320b9f..d98e3073b41d1 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -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) diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs index 03752c371ae11..285bc9a9e4213 100644 --- a/src/tools/rustfmt/src/patterns.rs +++ b/src/tools/rustfmt/src/patterns.rs @@ -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), diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index b9950e94d0c31..3a2975024a33b 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -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 { diff --git a/src/tools/rustfmt/tests/source/pin_sugar.rs b/src/tools/rustfmt/tests/source/pin_sugar.rs index e5b47339b928d..5f52e2449260f 100644 --- a/src/tools/rustfmt/tests/source/pin_sugar.rs +++ b/src/tools/rustfmt/tests/source/pin_sugar.rs @@ -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, +) {} diff --git a/src/tools/rustfmt/tests/target/pin_sugar.rs b/src/tools/rustfmt/tests/target/pin_sugar.rs index 09ad23a5807fc..f366ab4da9537 100644 --- a/src/tools/rustfmt/tests/target/pin_sugar.rs +++ b/src/tools/rustfmt/tests/target/pin_sugar.rs @@ -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, +) { +} diff --git a/tests/pretty/pin-ergonomics-hir.pp b/tests/pretty/pin-ergonomics-hir.pp index beca5988017dc..127cb531cecec 100644 --- a/tests/pretty/pin-ergonomics-hir.pp +++ b/tests/pretty/pin-ergonomics-hir.pp @@ -41,4 +41,7 @@ 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() { } diff --git a/tests/pretty/pin-ergonomics-hir.rs b/tests/pretty/pin-ergonomics-hir.rs index 5f2158258f077..a5ccf7253a8ed 100644 --- a/tests/pretty/pin-ergonomics-hir.rs +++ b/tests/pretty/pin-ergonomics-hir.rs @@ -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() { } diff --git a/tests/pretty/pin-ergonomics.rs b/tests/pretty/pin-ergonomics.rs index 8e8ced791b134..87f102d7c8908 100644 --- a/tests/pretty/pin-ergonomics.rs +++ b/tests/pretty/pin-ergonomics.rs @@ -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() {} diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs index 07aba0d1d2815..7f669d0b93e5d 100644 --- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs +++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs @@ -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() {} diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr index cff564c01fc6b..b721654a506f0 100644 --- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr +++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr @@ -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 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 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 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 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 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 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 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 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 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 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 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 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`. diff --git a/tests/ui/pin-ergonomics/pin-pattern.rs b/tests/ui/pin-ergonomics/pin-pattern.rs new file mode 100644 index 0000000000000..a61cc7abbf215 --- /dev/null +++ b/tests/ui/pin-ergonomics/pin-pattern.rs @@ -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); + +trait IsPinMut {} +trait IsPinConst {} +trait IsMut {} +trait IsRef {} +impl IsPinMut for &pin mut T {} +impl IsPinConst for &pin const T {} +impl IsMut for &mut T {} +impl IsRef for &T {} + +fn assert_pin_mut(_: T) {} +fn assert_pin_const(_: T) {} +fn assert_mut(_: T) {} +fn assert_ref(_: T) {} +fn assert_ty(_: T) {} + +fn normal(foo_mut: &pin mut Foo, foo_const: &pin const Foo) { + 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(foo_mut: &pin mut Foo, foo_const: &pin const Foo) { + let &pin mut Foo(x) = foo_mut; + assert_ty::(x); + + let &pin const Foo(x) = foo_const; + assert_ty::(x); +} + +fn by_value_non_copy(foo_mut: &pin mut Foo, foo_const: &pin const Foo) { + let &pin mut Foo(x) = foo_mut; + //~^ ERROR cannot move out of `foo_mut.pointer` which is behind a mutable reference + assert_ty::(x); + + let &pin const Foo(x) = foo_const; + //~^ ERROR cannot move out of `foo_const.pointer` which is behind a shared reference + assert_ty::(x); +} + +fn by_ref_non_pinned_unpin(foo_mut: &pin mut Foo, foo_const: &pin const Foo) { + 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(foo_mut: &pin mut Foo, foo_const: &pin const Foo) { + 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,),), + //~^ ERROR cannot explicitly dereference within an implicitly-borrowing pattern + //~| ERROR cannot move out of a mutable reference +) -> Foo { + 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,), //~ ERROR `T` cannot be unpinned +) -> &'a mut Foo { + 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,), //~ ERROR mismatched type +) -> &'a mut Foo { + 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,), //~ ERROR mismatched type +) -> &'a mut Foo { + 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,), //~ ERROR mismatched type +) -> &'a mut Foo { + x +} + +fn main() {} diff --git a/tests/ui/pin-ergonomics/pin-pattern.stderr b/tests/ui/pin-ergonomics/pin-pattern.stderr new file mode 100644 index 0000000000000..49f7a396f3ce2 --- /dev/null +++ b/tests/ui/pin-ergonomics/pin-pattern.stderr @@ -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(foo_mut: &pin mut Foo, foo_const: &pin const Foo) { + | ++++++++++++++++++++ + +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(foo_mut: &pin mut Foo, foo_const: &pin const Foo) { + | ++++++++++++++++++++ + +error[E0277]: `T` cannot be unpinned + --> $DIR/pin-pattern.rs:91:15 + | +LL | (&pin mut x,): &'a mut (&'a pin mut Foo,), + | ^ within `Foo`, 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` + --> $DIR/pin-pattern.rs:11:8 + | +LL | struct Foo(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,), + | ^^^^^^^^^^ ----------------------------- expected due to this + | | + | expected `Foo`, found `Pin<&mut _>` + | + = note: expected struct `Foo` + 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,), + | ^^^^^^^^^^ +help: consider removing `&pin mut` from the pattern + | +LL - (&pin mut x,): &'a mut &'a pin mut (Foo,), +LL + (x,): &'a mut &'a pin mut (Foo,), + | + +error[E0308]: mismatched types + --> $DIR/pin-pattern.rs:103:5 + | +LL | &pin mut (x,): &'a mut (&'a pin mut Foo,), + | ^^^^^^^^^^^^^ ----------------------------- expected due to this + | | + | expected `&mut (Pin<&mut Foo>,)`, found `Pin<&mut _>` + | + = note: expected mutable reference `&'a mut (Pin<&'a mut Foo>,)` + found struct `Pin<&mut _>` + +error[E0308]: mismatched types + --> $DIR/pin-pattern.rs:109:5 + | +LL | &pin mut (x,): &'a mut &'a pin mut (Foo,), + | ^^^^^^^^^^^^^ ----------------------------- expected due to this + | | + | expected `&mut Pin<&mut (Foo,)>`, found `Pin<&mut _>` + | + = note: expected mutable reference `&'a mut Pin<&'a mut (Foo,)>` + 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,),), + | ^^^^^^^^ reference pattern not allowed when implicitly borrowing + | + = note: for more information, see +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,),), + | ^^^^^^^^^^^^^ 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,),), + | ++++ + +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,),), + | ^^^^^^^^^^^-^^^^ + | | + | data moved here + | move occurs because `x` has type `Foo`, 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,),), + | +++ + +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`. diff --git a/tests/ui/pin-ergonomics/sugar-ambiguity.rs b/tests/ui/pin-ergonomics/sugar-ambiguity.rs index d183000931ec1..fbe12e6f77905 100644 --- a/tests/ui/pin-ergonomics/sugar-ambiguity.rs +++ b/tests/ui/pin-ergonomics/sugar-ambiguity.rs @@ -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; } diff --git a/tests/ui/pin-ergonomics/sugar-no-const.rs b/tests/ui/pin-ergonomics/sugar-no-const.rs index dd6456b603481..7270366808497 100644 --- a/tests/ui/pin-ergonomics/sugar-no-const.rs +++ b/tests/ui/pin-ergonomics/sugar-no-const.rs @@ -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() { +} diff --git a/tests/ui/pin-ergonomics/sugar-no-const.stderr b/tests/ui/pin-ergonomics/sugar-no-const.stderr index 062b6d3f4871a..151bb92ed4cd6 100644 --- a/tests/ui/pin-ergonomics/sugar-no-const.stderr +++ b/tests/ui/pin-ergonomics/sugar-no-const.stderr @@ -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