From 05b9a732024a41703be4757a04ec36d7a84869ca Mon Sep 17 00:00:00 2001 From: Frank King Date: Fri, 30 Jan 2026 17:41:54 +0800 Subject: [PATCH 1/8] Implement borrowck of pinned borrows --- compiler/rustc_borrowck/src/borrow_set.rs | 2 + .../rustc_borrowck/src/borrowck_errors.rs | 32 +++ compiler/rustc_borrowck/src/dataflow.rs | 194 ++++++++++++++- compiler/rustc_borrowck/src/def_use.rs | 4 +- .../src/diagnostics/conflict_errors.rs | 99 +++++++- .../rustc_borrowck/src/diagnostics/mod.rs | 16 +- compiler/rustc_borrowck/src/lib.rs | 132 ++++++++-- .../src/polonius/legacy/loan_invalidations.rs | 20 +- .../rustc_borrowck/src/session_diagnostics.rs | 55 +++++ compiler/rustc_codegen_ssa/src/mir/analyze.rs | 8 +- .../src/check_consts/check.rs | 8 +- .../src/check_consts/resolver.rs | 6 +- compiler/rustc_middle/src/mir/pretty.rs | 2 + compiler/rustc_middle/src/mir/statement.rs | 7 +- compiler/rustc_middle/src/mir/syntax.rs | 3 + compiler/rustc_middle/src/mir/visit.rs | 8 +- .../rustc_mir_build/src/check_unsafety.rs | 10 +- compiler/rustc_mir_build/src/thir/cx/expr.rs | 35 +-- .../src/impls/borrowed_locals.rs | 6 +- .../rustc_mir_dataflow/src/impls/liveness.rs | 6 +- .../src/deduce_param_attrs.rs | 8 +- .../src/known_panics_lint.rs | 5 +- compiler/rustc_mir_transform/src/liveness.rs | 6 +- .../rustc_mir_transform/src/promote_consts.rs | 5 +- .../src/unstable/convert/stable/mir.rs | 4 + .../pin-ergonomics/borrow-unpin.pinned.stderr | 231 +++++------------- tests/ui/pin-ergonomics/borrow-unpin.rs | 71 ++---- .../pin-ergonomics/borrow-unpin.unpin.stderr | 81 +++--- tests/ui/pin-ergonomics/borrow.rs | 28 ++- tests/ui/pin-ergonomics/borrow.stderr | 38 +++ 30 files changed, 768 insertions(+), 362 deletions(-) create mode 100644 tests/ui/pin-ergonomics/borrow.stderr diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 4644c210137fe..0452e47c68c0d 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -124,6 +124,8 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> { mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default | mir::MutBorrowKind::TwoPhaseBorrow, } => "mut ", + mir::BorrowKind::Pinned(mir::Mutability::Not) => "pin const ", + mir::BorrowKind::Pinned(mir::Mutability::Mut) => "pin mut ", }; write!(w, "&{:?} {}{:?}", self.region, kind, self.borrowed_place) } diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index 4fcb5f3b5a94d..a6a095e468478 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -484,6 +484,38 @@ impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, 'infcx, 'tcx> { pub(crate) fn temporary_value_borrowed_for_too_long(&self, span: Span) -> Diag<'infcx> { struct_span_code_err!(self.dcx(), span, E0716, "temporary value dropped while borrowed") } + + pub(crate) fn cannot_move_out_while_pinned( + &self, + span: Span, + pin_span: Span, + place: &str, + pin_place: &str, + value_place: &str, + ) -> Diag<'infcx> { + self.dcx().create_err(crate::session_diagnostics::MovePin { + place, + pin_place, + value_place, + span, + pin_span, + }) + } + + pub(crate) fn cannot_mutably_borrow_pinned( + &self, + span: Span, + pin_span: Span, + place: &str, + pin_place: &str, + ) -> Diag<'infcx> { + self.dcx().create_err(crate::session_diagnostics::MutablyBorrowPin { + place, + span, + pin_place, + pin_span, + }) + } } pub(crate) fn borrowed_data_escapes_closure<'tcx>( diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 813ceaeb8da9f..a4c97097ed690 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -21,6 +21,7 @@ use crate::{BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, plac // computed individually with `iterate_to_fixpoint`. pub(crate) struct Borrowck<'a, 'tcx> { pub(crate) borrows: Borrows<'a, 'tcx>, + pub(crate) pins: Pins<'a, 'tcx>, pub(crate) uninits: MaybeUninitializedPlaces<'a, 'tcx>, pub(crate) ever_inits: EverInitializedPlaces<'a, 'tcx>, } @@ -33,6 +34,7 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> { fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { BorrowckDomain { borrows: self.borrows.bottom_value(body), + pinned_borrows: self.pins.bottom_value(body), uninits: self.uninits.bottom_value(body), ever_inits: self.ever_inits.bottom_value(body), } @@ -50,6 +52,7 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> { loc: Location, ) { self.borrows.apply_early_statement_effect(&mut state.borrows, stmt, loc); + self.pins.apply_early_statement_effect(&mut state.pinned_borrows, stmt, loc); self.uninits.apply_early_statement_effect(&mut state.uninits, stmt, loc); self.ever_inits.apply_early_statement_effect(&mut state.ever_inits, stmt, loc); } @@ -61,6 +64,7 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> { loc: Location, ) { self.borrows.apply_primary_statement_effect(&mut state.borrows, stmt, loc); + self.pins.apply_primary_statement_effect(&mut state.pinned_borrows, stmt, loc); self.uninits.apply_primary_statement_effect(&mut state.uninits, stmt, loc); self.ever_inits.apply_primary_statement_effect(&mut state.ever_inits, stmt, loc); } @@ -72,6 +76,7 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> { loc: Location, ) { self.borrows.apply_early_terminator_effect(&mut state.borrows, term, loc); + self.pins.apply_early_terminator_effect(&mut state.pinned_borrows, term, loc); self.uninits.apply_early_terminator_effect(&mut state.uninits, term, loc); self.ever_inits.apply_early_terminator_effect(&mut state.ever_inits, term, loc); } @@ -83,6 +88,7 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> { loc: Location, ) -> TerminatorEdges<'mir, 'tcx> { self.borrows.apply_primary_terminator_effect(&mut state.borrows, term, loc); + self.pins.apply_primary_terminator_effect(&mut state.pinned_borrows, term, loc); self.uninits.apply_primary_terminator_effect(&mut state.uninits, term, loc); self.ever_inits.apply_primary_terminator_effect(&mut state.ever_inits, term, loc); @@ -116,6 +122,8 @@ where fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("borrows: ")?; self.borrows.fmt_with(ctxt, f)?; + f.write_str(" pinned_borrows: ")?; + self.pinned_borrows.fmt_with(ctxt, f)?; f.write_str(" uninits: ")?; self.uninits.fmt_with(ctxt, f)?; f.write_str(" ever_inits: ")?; @@ -134,6 +142,12 @@ where f.write_str("\n")?; } + if self.pinned_borrows != old.pinned_borrows { + f.write_str("pinned_borrows: ")?; + self.pinned_borrows.fmt_diff_with(&old.pinned_borrows, ctxt, f)?; + f.write_str("\n")?; + } + if self.uninits != old.uninits { f.write_str("uninits: ")?; self.uninits.fmt_diff_with(&old.uninits, ctxt, f)?; @@ -154,6 +168,7 @@ where #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) struct BorrowckDomain { pub(crate) borrows: BorrowsDomain, + pub(crate) pinned_borrows: BorrowsDomain, pub(crate) uninits: MaybeUninitializedPlacesDomain, pub(crate) ever_inits: EverInitializedPlacesDomain, } @@ -164,6 +179,8 @@ rustc_index::newtype_index! { pub struct BorrowIndex {} } +impl DebugWithContext for BorrowIndex {} + /// `Borrows` stores the data used in the analyses that track the flow /// of borrows. /// @@ -178,6 +195,19 @@ pub struct Borrows<'a, 'tcx> { borrows_out_of_scope_at_location: FxIndexMap>, } +/// `Pins` stores the data used in the analyses that track the flow +/// of pins. +/// +/// It uniquely identifies every pinned borrow by a +/// `PinIndex`, and maps each such index to a `PinData` +/// describing the pin. These indexes are used for representing the +/// pins in compact bitvectors. +pub(crate) struct Pins<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + body: &'a Body<'tcx>, + borrow_set: &'a BorrowSet<'tcx>, +} + struct OutOfScopePrecomputer<'a, 'tcx> { visited: DenseBitSet, visit_stack: Vec, @@ -617,4 +647,166 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> { } } -impl DebugWithContext for BorrowIndex {} +impl<'a, 'tcx> Pins<'a, 'tcx> { + pub(crate) fn new( + tcx: TyCtxt<'tcx>, + body: &'a Body<'tcx>, + borrow_set: &'a BorrowSet<'tcx>, + ) -> Self { + Pins { tcx, body, borrow_set } + } + + fn gen_pins_on_place(&self, state: &mut >::Domain, place: Place<'tcx>) { + self.borrow_set + .local_map + .get(&place.local) + .into_iter() + .flat_map(|bs| bs.iter()) + .copied() + .filter(|&index| self.borrow_set[index].borrowed_place == place) + .for_each(|index| { + state.gen_(index); + }); + } + + /// Kill any pins whose original pinned place conflicts with `place`. + fn kill_pins_on_place(&self, state: &mut >::Domain, place: Place<'tcx>) { + debug!("kill_pins_on_place: place={:?}", place); + + let other_pins_of_local = self + .borrow_set + .local_map + .get(&place.local) + .into_iter() + .flat_map(|bs| bs.iter()) + .copied(); + + // If the place is a local with no projections, all pins of this + // local must be killed. This is purely an optimization so we don't have to call + // `places_conflict` for every pin. + if place.projection.is_empty() { + state.kill_all(other_pins_of_local); + return; + } + + // By passing `PlaceConflictBias::NoOverlap`, we conservatively assume that any given + // pair of array indices are not equal, so that when `places_conflict` returns true, we + // will be assured that two places being compared definitely denotes the same sets of + // locations. + let definitely_conflicting_pins = other_pins_of_local.filter(|&i| { + places_conflict( + self.tcx, + self.body, + self.borrow_set[i].borrowed_place, + place, + PlaceConflictBias::NoOverlap, + ) + }); + + state.kill_all(definitely_conflicting_pins); + } +} + +/// Forward dataflow computation of the set of pins that are in scope at a particular location. +/// - we gen the introduced pins +/// - we kill pins on locals going out of scope +/// - we kill pins when the pinned place is moved or overwritten +impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Pins<'_, 'tcx> { + type Domain = BorrowsDomain; + + const NAME: &'static str = "pins"; + + fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { + // bottom = nothing is pinned yet + MixedBitSet::new_empty(self.borrow_set.len()) + } + + fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { + // no pins have been created prior to function execution + } + + fn apply_early_statement_effect( + &self, + state: &mut Self::Domain, + statement: &mir::Statement<'tcx>, + _location: Location, + ) { + // Kill pins early on reassignment/StorageDead so that the visitor + // (which runs after the early phase) sees the updated pin state. + // Note: we do NOT kill pins when the Pin result local goes out of scope + // (kill_pins_by_pin_local), because a pinned place stays pinned until + // the place itself is reassigned. + match &statement.kind { + mir::StatementKind::Assign(box (lhs, _)) => { + self.kill_pins_on_place(state, *lhs); + } + mir::StatementKind::StorageDead(local) => { + self.kill_pins_on_place(state, Place::from(*local)); + } + _ => {} + } + } + + fn apply_primary_statement_effect( + &self, + state: &mut Self::Domain, + stmt: &mir::Statement<'tcx>, + _location: Location, + ) { + match &stmt.kind { + mir::StatementKind::Assign(box (lhs, rhs)) => { + self.kill_pins_on_place(state, *lhs); + + // Check if this is a pinned borrow + if let mir::Rvalue::Ref(_, mir::BorrowKind::Pinned(_), place) = rhs { + // Generate the pin + self.gen_pins_on_place(state, *place); + } + } + + mir::StatementKind::StorageDead(local) => { + // Kill all pins on locals that are going out of scope + self.kill_pins_on_place(state, Place::from(*local)); + } + + mir::StatementKind::FakeRead(..) + | mir::StatementKind::SetDiscriminant { .. } + | mir::StatementKind::StorageLive(..) + | mir::StatementKind::Retag { .. } + | mir::StatementKind::PlaceMention(..) + | mir::StatementKind::AscribeUserType(..) + | mir::StatementKind::Coverage(..) + | mir::StatementKind::Intrinsic(..) + | mir::StatementKind::ConstEvalCounter + | mir::StatementKind::BackwardIncompatibleDropHint { .. } + | mir::StatementKind::Nop => {} + } + } + + fn apply_early_terminator_effect( + &self, + _state: &mut Self::Domain, + _terminator: &mir::Terminator<'tcx>, + _location: Location, + ) { + // No early terminator effects for pins + } + + fn apply_primary_terminator_effect<'mir>( + &self, + state: &mut Self::Domain, + terminator: &'mir mir::Terminator<'tcx>, + _location: Location, + ) -> TerminatorEdges<'mir, 'tcx> { + if let mir::TerminatorKind::InlineAsm { operands, .. } = &terminator.kind { + for op in operands { + if let mir::InlineAsmOperand::Out { place: Some(place), .. } + | mir::InlineAsmOperand::InOut { out_place: Some(place), .. } = *op + { + self.kill_pins_on_place(state, place); + } + } + } + terminator.edges() + } +} diff --git a/compiler/rustc_borrowck/src/def_use.rs b/compiler/rustc_borrowck/src/def_use.rs index 502265a83523e..255fa6140fe3a 100644 --- a/compiler/rustc_borrowck/src/def_use.rs +++ b/compiler/rustc_borrowck/src/def_use.rs @@ -56,11 +56,13 @@ pub(crate) fn categorize(context: PlaceContext) -> Option { PlaceContext::NonUse(NonUseContext::AscribeUserTy(_)) | PlaceContext::MutatingUse(MutatingUseContext::RawBorrow) | + PlaceContext::MutatingUse(MutatingUseContext::PinnedBorrow) | PlaceContext::NonMutatingUse(NonMutatingUseContext::RawBorrow) | PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) | PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) | PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) | - PlaceContext::MutatingUse(MutatingUseContext::Retag) => + PlaceContext::MutatingUse(MutatingUseContext::Retag) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::PinnedBorrow) => Some(DefUse::Use), /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index ffe5e15c48ec1..82500684d8420 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1771,8 +1771,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let first_borrow_desc; let mut err = match (gen_borrow_kind, issued_borrow.kind) { ( - BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep), - BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow }, + BorrowKind::Shared + | BorrowKind::Fake(FakeBorrowKind::Deep) + | BorrowKind::Pinned(mir::Mutability::Not), + BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow } + | BorrowKind::Pinned(mir::Mutability::Mut), ) => { first_borrow_desc = "mutable "; let mut err = self.cannot_reborrow_already_borrowed( @@ -1796,8 +1799,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { err } ( - BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow }, - BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep), + BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow } + | BorrowKind::Pinned(mir::Mutability::Mut), + BorrowKind::Shared + | BorrowKind::Fake(FakeBorrowKind::Deep) + | BorrowKind::Pinned(mir::Mutability::Not), ) => { first_borrow_desc = "immutable "; let mut err = self.cannot_reborrow_already_borrowed( @@ -1828,8 +1834,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } ( - BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow }, - BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow }, + BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow } + | BorrowKind::Pinned(mir::Mutability::Mut), + BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow } + | BorrowKind::Pinned(mir::Mutability::Mut), ) => { first_borrow_desc = "first "; let mut err = self.cannot_mutably_borrow_multiply( @@ -1868,7 +1876,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None) } - (BorrowKind::Mut { .. }, BorrowKind::Fake(FakeBorrowKind::Shallow)) => { + ( + BorrowKind::Mut { .. } | BorrowKind::Pinned(mir::Mutability::Mut), + BorrowKind::Fake(FakeBorrowKind::Shallow), + ) => { if let Some(immutable_section_description) = self.classify_immutable_section(issued_borrow.assigned_place) { @@ -1931,7 +1942,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } ( - BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep), + BorrowKind::Shared + | BorrowKind::Fake(FakeBorrowKind::Deep) + | BorrowKind::Pinned(mir::Mutability::Not), BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }, ) => { first_borrow_desc = "first "; @@ -1948,7 +1961,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ) } - (BorrowKind::Mut { .. }, BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }) => { + ( + BorrowKind::Mut { .. } | BorrowKind::Pinned(mir::Mutability::Mut), + BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }, + ) => { first_borrow_desc = "first "; self.cannot_reborrow_already_uniquely_borrowed( span, @@ -1964,12 +1980,17 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } ( - BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep), - BorrowKind::Shared | BorrowKind::Fake(_), + BorrowKind::Shared + | BorrowKind::Fake(FakeBorrowKind::Deep) + | BorrowKind::Pinned(mir::Mutability::Not), + BorrowKind::Shared | BorrowKind::Fake(_) | BorrowKind::Pinned(mir::Mutability::Not), ) | ( BorrowKind::Fake(FakeBorrowKind::Shallow), - BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake(_), + BorrowKind::Mut { .. } + | BorrowKind::Shared + | BorrowKind::Fake(_) + | BorrowKind::Pinned(_), ) => { unreachable!() } @@ -2038,6 +2059,60 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { err } + pub(crate) fn report_move_after_pinned( + &mut self, + location: Location, + (place, span): (Place<'tcx>, Span), + borrow: &BorrowData<'tcx>, + ) { + debug!( + "report_move_after_pinned: location={:?} place={:?} span={:?} borrow={:?}", + location, place, span, borrow + ); + + let value_msg = self.describe_any_place(place.as_ref()); + let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref()); + + let borrow_spans = self.retrieve_borrow_spans(borrow); + let borrow_span = borrow_spans.args_or_use(); + + let move_spans = self.move_spans(place.as_ref(), location); + let span = move_spans.args_or_use(); + + let err = self.cannot_move_out_while_pinned( + span, + borrow_span, + &self.describe_any_place(place.as_ref()), + &borrow_msg, + &value_msg, + ); + self.buffer_error(err); + } + + pub(crate) fn report_mutably_borrow_after_pinned( + &mut self, + location: Location, + (place, span): (Place<'tcx>, Span), + borrow: &BorrowData<'tcx>, + ) { + debug!( + "report_mutably_borrow_after_pinned: location={:?} place={:?} span={:?} borrow={:?}", + location, place, span, borrow + ); + + let borrow_spans = self.borrow_spans(span, location); + let span = borrow_spans.args_or_use(); + + let pin_spans = self.retrieve_borrow_spans(borrow); + let pin_span = pin_spans.args_or_use(); + + let value_msg = self.describe_any_place(place.as_ref()); + let pin_msg = self.describe_any_place(borrow.borrowed_place.as_ref()); + + let err = self.cannot_mutably_borrow_pinned(span, pin_span, &value_msg, &pin_msg); + self.buffer_error(err); + } + fn suggest_copy_for_type_in_cloned_ref(&self, err: &mut Diag<'infcx>, place: Place<'tcx>) { let tcx = self.infcx.tcx; let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return }; diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 86d7119639a3f..fc4afd0bb4b50 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -866,13 +866,15 @@ impl UseSpans<'_> { err.subdiagnostic(match kind { Some(kd) => match kd { rustc_middle::mir::BorrowKind::Shared - | rustc_middle::mir::BorrowKind::Fake(_) => { - CaptureVarKind::Immut { kind_span: capture_kind_span } - } - - rustc_middle::mir::BorrowKind::Mut { .. } => { - CaptureVarKind::Mut { kind_span: capture_kind_span } - } + | rustc_middle::mir::BorrowKind::Fake(_) + | rustc_middle::mir::BorrowKind::Pinned( + rustc_middle::mir::Mutability::Not, + ) => CaptureVarKind::Immut { kind_span: capture_kind_span }, + + rustc_middle::mir::BorrowKind::Mut { .. } + | rustc_middle::mir::BorrowKind::Pinned( + rustc_middle::mir::Mutability::Mut, + ) => CaptureVarKind::Mut { kind_span: capture_kind_span }, }, None => CaptureVarKind::Move { kind_span: capture_kind_span }, }); diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 822fe1e58ebba..9cbf047126793 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -53,7 +53,7 @@ use tracing::{debug, instrument}; use crate::borrow_set::{BorrowData, BorrowSet}; use crate::consumers::{BodyWithBorrowckFacts, RustcFacts}; -use crate::dataflow::{BorrowIndex, Borrowck, BorrowckDomain, Borrows}; +use crate::dataflow::{BorrowIndex, Borrowck, BorrowckDomain, Borrows, Pins}; use crate::diagnostics::{ AccessKind, BorrowckDiagnosticsBuffer, IllegalMoveOriginKind, MoveError, RegionName, }; @@ -602,13 +602,14 @@ fn get_flow_results<'a, 'tcx>( borrow_set: &'a BorrowSet<'tcx>, regioncx: &RegionInferenceContext<'tcx>, ) -> Results<'tcx, Borrowck<'a, 'tcx>> { - // We compute these three analyses individually, but them combine them into + // We compute these four analyses individually, but them combine them into // a single results so that `mbcx` can visit them all together. let borrows = Borrows::new(tcx, body, regioncx, borrow_set).iterate_to_fixpoint( tcx, body, Some("borrowck"), ); + let pins = Pins::new(tcx, body, borrow_set).iterate_to_fixpoint(tcx, body, Some("borrowck")); let uninits = MaybeUninitializedPlaces::new(tcx, body, move_data).iterate_to_fixpoint( tcx, body, @@ -622,16 +623,27 @@ fn get_flow_results<'a, 'tcx>( let analysis = Borrowck { borrows: borrows.analysis, + pins: pins.analysis, uninits: uninits.analysis, ever_inits: ever_inits.analysis, }; + assert_eq!(borrows.entry_states.len(), pins.entry_states.len()); assert_eq!(borrows.entry_states.len(), uninits.entry_states.len()); assert_eq!(borrows.entry_states.len(), ever_inits.entry_states.len()); - let entry_states: EntryStates<_> = - itertools::izip!(borrows.entry_states, uninits.entry_states, ever_inits.entry_states) - .map(|(borrows, uninits, ever_inits)| BorrowckDomain { borrows, uninits, ever_inits }) - .collect(); + let entry_states: EntryStates<_> = itertools::izip!( + borrows.entry_states, + pins.entry_states, + uninits.entry_states, + ever_inits.entry_states + ) + .map(|(borrows, pinned_borrows, uninits, ever_inits)| BorrowckDomain { + borrows, + pinned_borrows, + uninits, + ever_inits, + }) + .collect(); Results { analysis, entry_states } } @@ -1254,6 +1266,23 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { } } + fn pinned_borrows<'s>( + &self, + state: &'s BorrowckDomain, + ) -> Option<&'s MixedBitSet> { + // FIXME(pin_ergonomics): borrowck behaviors depend on a safe trait + // which should not contain any safety invariants. + // if place + // .ty(self.body, self.infcx.tcx) + // .ty + // .is_unpin(self.infcx.tcx, self.body.typing_env(self.infcx.tcx)) + // { + // None + // } else { + Some(&state.pinned_borrows) + // } + } + #[instrument(level = "debug", skip(self, state))] fn check_access_for_conflict( &mut self, @@ -1266,6 +1295,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { let mut error_reported = false; let borrows_in_scope = self.borrows_in_scope(location, state); + let pinned_borrows = self.pinned_borrows(state); each_borrow_involving_path( self, @@ -1273,7 +1303,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { self.body, (sd, place_span.0), self.borrow_set, - |borrow_index| borrows_in_scope.contains(borrow_index), + |borrow_index| { + borrows_in_scope.contains(borrow_index) + || pinned_borrows.is_some_and(|p| p.contains(borrow_index)) + }, |this, borrow_index, borrow| match (rw, borrow.kind) { // Obviously an activation is compatible with its own // reservation (or even prior activating uses of same @@ -1293,13 +1326,19 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { ControlFlow::Continue(()) } - (Read(_), BorrowKind::Shared | BorrowKind::Fake(_)) + ( + Read(_), + BorrowKind::Shared | BorrowKind::Fake(_) | BorrowKind::Pinned(Mutability::Not), + ) | ( Read(ReadKind::Borrow(BorrowKind::Fake(FakeBorrowKind::Shallow))), - BorrowKind::Mut { .. }, + BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut), ) => ControlFlow::Continue(()), - (Reservation(_), BorrowKind::Fake(_) | BorrowKind::Shared) => { + ( + Reservation(_), + BorrowKind::Fake(_) | BorrowKind::Shared | BorrowKind::Pinned(Mutability::Not), + ) => { // This used to be a future compatibility warning (to be // disallowed on NLL). See rust-lang/rust#56254 ControlFlow::Continue(()) @@ -1310,7 +1349,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { ControlFlow::Continue(()) } - (Read(kind), BorrowKind::Mut { .. }) => { + // Ignore the expired borrow (pinnedness never conflicts with a read) + (Read(_), BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut)) + if !borrows_in_scope.contains(borrow_index) => + { + ControlFlow::Continue(()) + } + + (Read(kind), BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut)) => { // Reading from mere reservations of mutable-borrows is OK. if !is_active(this.dominators(), borrow, location) { assert!(borrow.kind.is_two_phase_borrow()); @@ -1333,6 +1379,37 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { ControlFlow::Break(()) } + // Handle the expired borrows (only pinned borrows) + (Reservation(kind) | Activation(kind, _) | Write(kind), _) + if !borrows_in_scope.contains(borrow_index) => + { + debug_assert!( + pinned_borrows.is_none_or(|p| p.contains(borrow_index)), + "unexpected expired but non-pinned borrow {borrow_index:?}: {borrow:?}", + ); + match kind { + // Pinnedness doesn't conflict with a drop or write + WriteKind::StorageDeadOrDrop | WriteKind::Mutate | WriteKind::Replace => { + ControlFlow::Continue(()) + } + // Mutable (pinned) borrow doesn't conflict with an expired borrow + WriteKind::MutableBorrow(BorrowKind::Pinned(Mutability::Mut)) => { + ControlFlow::Continue(()) + } + // Mutable (but non-pinned) borrow conflicts with an earlier pinned borrow + WriteKind::MutableBorrow(_) => { + this.report_mutably_borrow_after_pinned(location, place_span, borrow); + error_reported = true; + ControlFlow::Break(()) + } + WriteKind::Move => { + this.report_move_after_pinned(location, place_span, borrow); + error_reported = true; + ControlFlow::Break(()) + } + } + } + (Reservation(kind) | Activation(kind, _) | Write(kind), _) => { match rw { Reservation(..) => { @@ -1473,6 +1550,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { BorrowKind::Fake(FakeBorrowKind::Shallow) => { (Shallow(Some(ArtificialField::FakeBorrow)), Read(ReadKind::Borrow(bk))) } + BorrowKind::Pinned(Mutability::Not) => { + (Shallow(None), Read(ReadKind::Borrow(BorrowKind::Pinned(Mutability::Not)))) + } + BorrowKind::Pinned(Mutability::Mut) => ( + Shallow(None), + Write(WriteKind::MutableBorrow(BorrowKind::Pinned(Mutability::Mut))), + ), BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep) => { (Deep, Read(ReadKind::Borrow(bk))) } @@ -1838,8 +1922,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { // only mutable borrows should be 2-phase assert!(match borrow.kind { - BorrowKind::Shared | BorrowKind::Fake(_) => false, - BorrowKind::Mut { .. } => true, + BorrowKind::Shared | BorrowKind::Fake(_) | BorrowKind::Pinned(Mutability::Not) => + false, + BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut) => true, }); self.access_place( @@ -2415,7 +2500,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | WriteKind::Replace | WriteKind::StorageDeadOrDrop | WriteKind::MutableBorrow(BorrowKind::Shared) - | WriteKind::MutableBorrow(BorrowKind::Fake(_)), + | WriteKind::MutableBorrow(BorrowKind::Fake(_)) + | WriteKind::MutableBorrow(BorrowKind::Pinned(Mutability::Mut)), ) => { if self.is_mutable(place.as_ref(), is_local_mutation_allowed).is_err() && !self.has_buffered_diags() @@ -2439,12 +2525,28 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { return false; } Read( - ReadKind::Borrow(BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake(_)) + ReadKind::Borrow( + BorrowKind::Mut { .. } + | BorrowKind::Shared + | BorrowKind::Fake(_) + | BorrowKind::Pinned(Mutability::Not), + ) | ReadKind::Copy, ) => { // Access authorized return false; } + Reservation(WriteKind::MutableBorrow(BorrowKind::Pinned(_))) => { + span_bug!(span, "invalid reservation with a pinned borrow kind: {:?}", kind); + } + Write(WriteKind::MutableBorrow(BorrowKind::Pinned(Mutability::Not))) + | Read(ReadKind::Borrow(BorrowKind::Pinned(Mutability::Mut))) => { + span_bug!( + span, + "invalid read with a pinned mutable borrow kind or write with an immutable pinned borrow kind: {:?}", + kind + ); + } } // rust-lang/rust#21232, #54986: during period where we reject diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index 439aa1a91e068..02940f00a1b2f 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -262,6 +262,12 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep) => { (Deep, Read(ReadKind::Borrow(bk))) } + BorrowKind::Pinned(Mutability::Not) => { + (Deep, Read(ReadKind::Borrow(BorrowKind::Pinned(Mutability::Not)))) + } + BorrowKind::Pinned(Mutability::Mut) => { + (Deep, Write(WriteKind::MutableBorrow(BorrowKind::Pinned(Mutability::Mut)))) + } BorrowKind::Mut { .. } => { let wk = WriteKind::MutableBorrow(bk); if bk.is_two_phase_borrow() { @@ -373,7 +379,12 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { // have already taken the reservation } - (Read(_), BorrowKind::Fake(_) | BorrowKind::Shared) + ( + Read(_), + BorrowKind::Fake(_) + | BorrowKind::Shared + | BorrowKind::Pinned(Mutability::Not), + ) | ( Read(ReadKind::Borrow(BorrowKind::Fake(FakeBorrowKind::Shallow))), BorrowKind::Mut { .. }, @@ -381,7 +392,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { // Reads don't invalidate shared or shallow borrows } - (Read(_), BorrowKind::Mut { .. }) => { + (Read(_), BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut)) => { // Reading from mere reservations of mutable-borrows is OK. if !is_active(this.dominators, borrow, location) { // If the borrow isn't active yet, reads don't invalidate it @@ -422,8 +433,9 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { // only mutable borrows should be 2-phase assert!(match borrow.kind { - BorrowKind::Shared | BorrowKind::Fake(_) => false, - BorrowKind::Mut { .. } => true, + BorrowKind::Shared | BorrowKind::Fake(_) | BorrowKind::Pinned(Mutability::Not) => + false, + BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut) => true, }); self.access_place( diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index cd4d1f16b21af..f46e764eb9e2b 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -340,6 +340,61 @@ pub(crate) struct MoveBorrow<'a> { pub borrow_span: Span, } +#[derive(Diagnostic)] +#[diag( + "cannot move out of {$place -> + [value] value + *[other] {$place} +} because it is pinned" +)] +pub(crate) struct MovePin<'a> { + pub place: &'a str, + pub pin_place: &'a str, + pub value_place: &'a str, + #[primary_span] + #[label( + "move out of {$value_place -> + [value] value + *[other] {$value_place} + } occurs here" + )] + pub span: Span, + #[label( + "pin of {$pin_place -> + [value] value + *[other] {$pin_place} + } occurs here" + )] + pub pin_span: Span, +} + +#[derive(Diagnostic)] +#[diag( + "cannot borrow {$place -> + [value] value + *[other] {$place} +} as mutable because it is pinned" +)] +pub(crate) struct MutablyBorrowPin<'a> { + pub place: &'a str, + #[primary_span] + #[label( + "borrow of {$place -> + [value] value + *[other] {$place} + } as mutable occurs here" + )] + pub span: Span, + pub pin_place: &'a str, + #[label( + "pin of {$pin_place -> + [value] value + *[other] {$pin_place} + } occurs here" + )] + pub pin_span: Span, +} + #[derive(Diagnostic)] #[diag("opaque type used twice with different lifetimes")] pub(crate) struct LifetimeMismatchOpaqueParam<'tcx> { diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index de755d5617801..8896264507861 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -204,7 +204,7 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer } PlaceContext::NonUse(_) - | PlaceContext::NonMutatingUse(NonMutatingUseContext::PlaceMention) + | PlaceContext::NonMutatingUse(NonMutatingUseContext::PlaceMention ) | PlaceContext::MutatingUse(MutatingUseContext::Retag) => {} PlaceContext::NonMutatingUse( @@ -233,13 +233,15 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer | MutatingUseContext::AsmOutput | MutatingUseContext::Borrow | MutatingUseContext::RawBorrow - | MutatingUseContext::Projection, + | MutatingUseContext::Projection + | MutatingUseContext::PinnedBorrow, ) | PlaceContext::NonMutatingUse( NonMutatingUseContext::SharedBorrow | NonMutatingUseContext::FakeBorrow | NonMutatingUseContext::RawBorrow - | NonMutatingUseContext::Projection, + | NonMutatingUseContext::Projection + | NonMutatingUseContext::PinnedBorrow, ) => { self.locals[local] = LocalKind::Memory; } diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index a45f1124f0af4..76a6e6ba0f0bd 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -582,7 +582,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } } - Rvalue::Ref(_, BorrowKind::Mut { .. }, place) + Rvalue::Ref(_, BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut), place) | Rvalue::RawPtr(RawPtrKind::Mut, place) => { // Inside mutable statics, we allow arbitrary mutable references. // We've allowed `static mut FOO = &mut [elements];` for a long time (the exact @@ -596,7 +596,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } } - Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Fake(_), place) + Rvalue::Ref( + _, + BorrowKind::Shared | BorrowKind::Fake(_) | BorrowKind::Pinned(Mutability::Not), + place, + ) | Rvalue::RawPtr(RawPtrKind::Const, place) => { let borrowed_place_has_mut_interior = qualifs::in_place::( self.ccx, diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index 044b8b091b8de..6d0938ed9efda 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -103,8 +103,10 @@ where fn ref_allows_mutation(&self, kind: mir::BorrowKind, place: mir::Place<'tcx>) -> bool { match kind { - mir::BorrowKind::Mut { .. } => true, - mir::BorrowKind::Shared | mir::BorrowKind::Fake(_) => { + mir::BorrowKind::Mut { .. } | mir::BorrowKind::Pinned(mir::Mutability::Mut) => true, + mir::BorrowKind::Shared + | mir::BorrowKind::Fake(_) + | mir::BorrowKind::Pinned(mir::Mutability::Not) => { self.shared_borrow_allows_mutation(place) } } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index bf3d595e99436..1f04cc73d1342 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1117,6 +1117,8 @@ impl<'tcx> Debug for Rvalue<'tcx> { BorrowKind::Fake(FakeBorrowKind::Deep) => "fake ", BorrowKind::Fake(FakeBorrowKind::Shallow) => "fake shallow ", BorrowKind::Mut { .. } => "mut ", + BorrowKind::Pinned(Mutability::Not) => "pin ", + BorrowKind::Pinned(Mutability::Mut) => "pin mut ", }; // When printing regions, add trailing space if necessary. diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index f66c4755de4dd..9b549995f44ac 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -838,6 +838,7 @@ impl BorrowKind { match *self { BorrowKind::Shared | BorrowKind::Fake(_) => Mutability::Not, BorrowKind::Mut { .. } => Mutability::Mut, + BorrowKind::Pinned(mutability) => mutability, } } @@ -847,9 +848,8 @@ impl BorrowKind { match *self { BorrowKind::Shared | BorrowKind::Fake(_) - | BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::ClosureCapture } => { - false - } + | BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::ClosureCapture } + | BorrowKind::Pinned(_) => false, BorrowKind::Mut { kind: MutBorrowKind::TwoPhaseBorrow } => true, } } @@ -862,6 +862,7 @@ impl BorrowKind { // We have no type corresponding to a shallow borrow, so use // `&` as an approximation. BorrowKind::Fake(_) => hir::Mutability::Not, + BorrowKind::Pinned(mutability) => mutability, } } } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 455089f285d17..87bd025cb4822 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -178,6 +178,9 @@ pub enum BorrowKind { /// Data is mutable and not aliasable. Mut { kind: MutBorrowKind }, + + /// Data is pinned and cannot be moved or mutably borrowed until dropped or reassigned. + Pinned(Mutability), } #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)] diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 16a8743a6d67b..1ff413123e1f0 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -726,6 +726,8 @@ macro_rules! make_mir_visitor { ), BorrowKind::Mut { .. } => PlaceContext::MutatingUse(MutatingUseContext::Borrow), + BorrowKind::Pinned(Mutability::Not) => PlaceContext::NonMutatingUse(NonMutatingUseContext::PinnedBorrow), + BorrowKind::Pinned(Mutability::Mut) => PlaceContext::MutatingUse(MutatingUseContext::PinnedBorrow), }; self.visit_place(path, ctx, location); } @@ -1334,6 +1336,8 @@ pub enum NonMutatingUseContext { FakeBorrow, /// `&raw const`. RawBorrow, + /// `&pin const`. + PinnedBorrow, /// PlaceMention statement. /// /// This statement is executed as a check that the `Place` is live without reading from it, @@ -1366,6 +1370,8 @@ pub enum MutatingUseContext { Borrow, /// `&raw mut`. RawBorrow, + /// `&pin mut`. + PinnedBorrow, /// Used as base for another place, e.g., `x` in `x.y`. Could potentially mutate the place. /// For example, the projection `x.y` is marked as a mutation in these cases: /// ```ignore (illustrative) @@ -1486,7 +1492,7 @@ impl PlaceContext { ) => ty::Invariant, PlaceContext::NonMutatingUse( Inspect | Copy | Move | PlaceMention | SharedBorrow | FakeBorrow | RawBorrow - | Projection, + | Projection | PinnedBorrow, ) => ty::Covariant, PlaceContext::NonUse(AscribeUserTy(variance)) => variance, } diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index aed0c6e6085d3..8ddef167b98e5 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -676,15 +676,19 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { visit::walk_expr(&mut visitor, expr); if visitor.found { match borrow_kind { - BorrowKind::Fake(_) | BorrowKind::Shared + BorrowKind::Fake(_) + | BorrowKind::Shared + | BorrowKind::Pinned(hir::Mutability::Not) if !self.thir[arg].ty.is_freeze(self.tcx, self.typing_env) => { self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField) } - BorrowKind::Mut { .. } => { + BorrowKind::Mut { .. } | BorrowKind::Pinned(hir::Mutability::Mut) => { self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField) } - BorrowKind::Fake(_) | BorrowKind::Shared => {} + BorrowKind::Fake(_) + | BorrowKind::Shared + | BorrowKind::Pinned(hir::Mutability::Not) => {} } } } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 69260792a95d2..22f06ad9e844b 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -192,10 +192,7 @@ impl<'tcx> ThirBuildCx<'tcx> { } Adjust::Borrow(AutoBorrow::Pin(mutbl)) => { // expr = &pin (mut|const|) arget - let borrow_kind = match mutbl { - hir::Mutability::Mut => BorrowKind::Mut { kind: mir::MutBorrowKind::Default }, - hir::Mutability::Not => BorrowKind::Shared, - }; + let borrow_kind = BorrowKind::Pinned(mutbl); let new_pin_target = Ty::new_ref(self.tcx, self.tcx.lifetimes.re_erased, expr.ty, mutbl); let arg = self.thir.exprs.push(expr); @@ -446,37 +443,17 @@ impl<'tcx> ThirBuildCx<'tcx> { // Make `&pin mut $expr` and `&pin const $expr` into // `Pin { __pointer: &mut { $expr } }` and `Pin { __pointer: &$expr }`. - hir::ExprKind::AddrOf(hir::BorrowKind::Pin, mutbl, arg_expr) => match expr_ty.kind() { + hir::ExprKind::AddrOf(hir::BorrowKind::Pin, mutability, arg_expr) => match expr_ty + .kind() + { &ty::Adt(adt_def, args) if tcx.is_lang_item(adt_def.did(), hir::LangItem::Pin) => { let ty = args.type_at(0); - let arg_ty = self.typeck_results.expr_ty(arg_expr); - let mut arg = self.mirror_expr(arg_expr); - // For `&pin mut $place` where `$place` is not `Unpin`, move the place - // `$place` to ensure it will not be used afterwards. - if mutbl.is_mut() && !arg_ty.is_unpin(self.tcx, self.typing_env) { - let block = self.thir.blocks.push(Block { - targeted_by_break: false, - region_scope: region::Scope { - local_id: arg_expr.hir_id.local_id, - data: region::ScopeData::Node, - }, - span: arg_expr.span, - stmts: Box::new([]), - expr: Some(arg), - safety_mode: BlockSafety::Safe, - }); - arg = self.thir.exprs.push(Expr { - temp_scope_id: arg_expr.hir_id.local_id, - ty: arg_ty, - span: arg_expr.span, - kind: ExprKind::Block { block }, - }); - } + let arg = self.mirror_expr(arg_expr); let expr = self.thir.exprs.push(Expr { temp_scope_id: expr.hir_id.local_id, ty, span: expr.span, - kind: ExprKind::Borrow { borrow_kind: mutbl.to_borrow_kind(), arg }, + kind: ExprKind::Borrow { borrow_kind: BorrowKind::Pinned(mutability), arg }, }); ExprKind::Adt(Box::new(AdtExpr { adt_def, diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index d5548266aa017..b319d8db34fbe 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -78,7 +78,11 @@ where // We ignore fake borrows as these get removed after analysis and shouldn't effect // the layout of generators. Rvalue::RawPtr(_, borrowed_place) - | Rvalue::Ref(_, BorrowKind::Mut { .. } | BorrowKind::Shared, borrowed_place) => { + | Rvalue::Ref( + _, + BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Pinned(_), + borrowed_place, + ) => { if !borrowed_place.is_indirect() { self.trans.gen_(borrowed_place.local); } diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index e439f2e052fe8..632e203de7f5a 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -182,7 +182,8 @@ impl DefUse { MutatingUseContext::RawBorrow | MutatingUseContext::Borrow | MutatingUseContext::Drop - | MutatingUseContext::Retag, + | MutatingUseContext::Retag + | MutatingUseContext::PinnedBorrow, ) | PlaceContext::NonMutatingUse( NonMutatingUseContext::RawBorrow @@ -191,7 +192,8 @@ impl DefUse { | NonMutatingUseContext::Move | NonMutatingUseContext::PlaceMention | NonMutatingUseContext::FakeBorrow - | NonMutatingUseContext::SharedBorrow, + | NonMutatingUseContext::SharedBorrow + | NonMutatingUseContext::PinnedBorrow, ) => DefUse::Use, PlaceContext::MutatingUse(MutatingUseContext::Projection) diff --git a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs index ad94c23f229c4..c756b9c866910 100644 --- a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs +++ b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs @@ -65,7 +65,8 @@ impl<'tcx> Visitor<'tcx> for DeduceParamAttrs { | MutatingUseContext::Yield | MutatingUseContext::Drop | MutatingUseContext::Borrow - | MutatingUseContext::RawBorrow) => { + | MutatingUseContext::RawBorrow + | MutatingUseContext::PinnedBorrow) => { self.usage[i] |= UsageSummary::MUTATE; self.usage[i] |= UsageSummary::CAPTURE; } @@ -83,7 +84,7 @@ impl<'tcx> Visitor<'tcx> for DeduceParamAttrs { self.usage[i] |= UsageSummary::MUTATE; self.usage[i] |= UsageSummary::CAPTURE; } - PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) => { + PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow | NonMutatingUseContext::PinnedBorrow) => { // Not mutating if the parameter is `Freeze`. self.usage[i] |= UsageSummary::SHARED_BORROW; self.usage[i] |= UsageSummary::CAPTURE; @@ -95,7 +96,8 @@ impl<'tcx> Visitor<'tcx> for DeduceParamAttrs { | NonMutatingUseContext::Move | NonMutatingUseContext::FakeBorrow | NonMutatingUseContext::PlaceMention - | NonMutatingUseContext::Projection) => {} + | NonMutatingUseContext::Projection + ) => {} } } diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 8d0922db8f40e..867349c2813e7 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -946,8 +946,11 @@ impl<'tcx> Visitor<'tcx> for CanConstProp { | NonMutatingUse(NonMutatingUseContext::SharedBorrow) | NonMutatingUse(NonMutatingUseContext::FakeBorrow) | NonMutatingUse(NonMutatingUseContext::RawBorrow) + | NonMutatingUse(NonMutatingUseContext::PinnedBorrow) | MutatingUse(MutatingUseContext::Borrow) - | MutatingUse(MutatingUseContext::RawBorrow) => { + | MutatingUse(MutatingUseContext::RawBorrow) + | MutatingUse(MutatingUseContext::PinnedBorrow) + => { trace!("local {:?} can't be propagated because it's used: {:?}", local, context); self.can_const_prop[local] = ConstPropMode::NoPropagation; } diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs index 0ddb9157c7db1..2fc2eac55d650 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -1442,7 +1442,8 @@ impl DefUse { MutatingUseContext::RawBorrow | MutatingUseContext::Borrow | MutatingUseContext::Drop - | MutatingUseContext::Retag, + | MutatingUseContext::Retag + | MutatingUseContext::PinnedBorrow, ) | PlaceContext::NonMutatingUse( NonMutatingUseContext::RawBorrow @@ -1451,7 +1452,8 @@ impl DefUse { | NonMutatingUseContext::Move | NonMutatingUseContext::FakeBorrow | NonMutatingUseContext::SharedBorrow - | NonMutatingUseContext::PlaceMention, + | NonMutatingUseContext::PlaceMention + | NonMutatingUseContext::PinnedBorrow, ) => Some(DefUse::Use), PlaceContext::NonUse( diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index a47b3ce64ed23..7eacea4224ba0 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -399,7 +399,7 @@ impl<'tcx> Validator<'_, 'tcx> { return Err(Unpromotable); } - BorrowKind::Shared => { + BorrowKind::Shared | BorrowKind::Pinned(Mutability::Not) => { let has_mut_interior = self.qualif_local::(place.local); if has_mut_interior { return Err(Unpromotable); @@ -408,7 +408,8 @@ impl<'tcx> Validator<'_, 'tcx> { // FIXME: consider changing this to only promote &mut [] for default borrows, // also forbidding two phase borrows - BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow } => { + BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow } + | BorrowKind::Pinned(Mutability::Mut) => { let ty = place.ty(self.body, self.tcx).ty; // In theory, any zero-sized value could be borrowed diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index 0d04053aab76b..3cb6a8dedaaed 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -281,6 +281,10 @@ impl<'tcx> Stable<'tcx> for mir::BorrowKind { Shared => crate::mir::BorrowKind::Shared, Fake(kind) => crate::mir::BorrowKind::Fake(kind.stable(tables, cx)), Mut { kind } => crate::mir::BorrowKind::Mut { kind: kind.stable(tables, cx) }, + Pinned(mir::Mutability::Not) => crate::mir::BorrowKind::Shared, + Pinned(mir::Mutability::Mut) => { + crate::mir::BorrowKind::Mut { kind: crate::mir::MutBorrowKind::Default } + } } } } diff --git a/tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr b/tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr index cc438461a5d15..b309dace105f4 100644 --- a/tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr +++ b/tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr @@ -1,34 +1,25 @@ -error[E0382]: use of moved value: `foo` - --> $DIR/borrow-unpin.rs:39:14 +error: cannot move out of `foo` because it is pinned + --> $DIR/borrow-unpin.rs:28:14 | -LL | let foo = Foo::default(); - | --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait -LL | foo_pin_mut(&pin mut foo); - | --- value moved here +LL | foo_pin_mut(&pin mut foo); // ok + | ------------ pin of `foo` occurs here LL | foo_move(foo); - | ^^^ value used here after move - | -note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:16:1 - | -LL | struct Foo(PhantomPinned); - | ^^^^^^^^^^ consider implementing `Clone` for this type -... -LL | foo_pin_mut(&pin mut foo); - | --- you could clone this value + | ^^^ move out of `foo` occurs here -error[E0382]: use of moved value: `foo` - --> $DIR/borrow-unpin.rs:43:14 +error[E0505]: cannot move out of `foo` because it is borrowed + --> $DIR/borrow-unpin.rs:32:14 | -LL | let foo = Foo::default(); - | --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait +LL | let mut foo = Foo::default(); + | ------- binding `foo` declared here LL | let x = &pin mut foo; - | --- value moved here + | ------------ borrow of `foo` occurs here LL | foo_move(foo); - | ^^^ value used here after move + | ^^^ move out of `foo` occurs here +LL | foo_pin_mut(x); // ok + | - borrow later used here | note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:16:1 + --> $DIR/borrow-unpin.rs:13:1 | LL | struct Foo(PhantomPinned); | ^^^^^^^^^^ consider implementing `Clone` for this type @@ -36,37 +27,28 @@ LL | struct Foo(PhantomPinned); LL | let x = &pin mut foo; | --- you could clone this value -error[E0382]: use of moved value: `foo` - --> $DIR/borrow-unpin.rs:52:14 +error: cannot move out of `foo` because it is pinned + --> $DIR/borrow-unpin.rs:39:14 | -LL | let mut foo = Foo::default(); - | ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait LL | foo_pin_mut(&pin mut foo); // ok - | --- value moved here + | ------------ pin of `foo` occurs here LL | foo_move(foo); - | ^^^ value used here after move - | -note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:16:1 - | -LL | struct Foo(PhantomPinned); - | ^^^^^^^^^^ consider implementing `Clone` for this type -... -LL | foo_pin_mut(&pin mut foo); // ok - | --- you could clone this value + | ^^^ move out of `foo` occurs here -error[E0382]: use of moved value: `foo` - --> $DIR/borrow-unpin.rs:56:14 +error[E0505]: cannot move out of `foo` because it is borrowed + --> $DIR/borrow-unpin.rs:43:14 | LL | let mut foo = Foo::default(); - | ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait + | ------- binding `foo` declared here LL | let x = &pin mut foo; // ok - | --- value moved here + | ------------ borrow of `foo` occurs here LL | foo_move(foo); - | ^^^ value used here after move + | ^^^ move out of `foo` occurs here +LL | foo_pin_mut(x); // ok + | - borrow later used here | note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:16:1 + --> $DIR/borrow-unpin.rs:13:1 | LL | struct Foo(PhantomPinned); | ^^^^^^^^^^ consider implementing `Clone` for this type @@ -74,8 +56,16 @@ LL | struct Foo(PhantomPinned); LL | let x = &pin mut foo; // ok | --- you could clone this value +error: cannot move out of `foo` because it is pinned + --> $DIR/borrow-unpin.rs:50:14 + | +LL | foo_pin_ref(&pin const foo); // ok + | -------------- pin of `foo` occurs here +LL | foo_move(foo); + | ^^^ move out of `foo` occurs here + error[E0505]: cannot move out of `foo` because it is borrowed - --> $DIR/borrow-unpin.rs:68:14 + --> $DIR/borrow-unpin.rs:54:14 | LL | let foo = Foo::default(); | --- binding `foo` declared here @@ -83,12 +73,11 @@ LL | let x = &pin const foo; // ok | -------------- borrow of `foo` occurs here LL | foo_move(foo); | ^^^ move out of `foo` occurs here -LL | LL | foo_pin_ref(x); | - borrow later used here | note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:16:1 + --> $DIR/borrow-unpin.rs:13:1 | LL | struct Foo(PhantomPinned); | ^^^^^^^^^^ consider implementing `Clone` for this type @@ -96,143 +85,47 @@ LL | struct Foo(PhantomPinned); LL | let x = &pin const foo; // ok | --- you could clone this value -error[E0382]: borrow of moved value: `foo` - --> $DIR/borrow-unpin.rs:76:13 - | -LL | let mut foo = Foo::default(); - | ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait -LL | foo_pin_mut(&pin mut foo); // ok - | --- value moved here -LL | foo_ref(&foo); - | ^^^^ value borrowed here after move - | -note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:16:1 - | -LL | struct Foo(PhantomPinned); - | ^^^^^^^^^^ consider implementing `Clone` for this type -... -LL | foo_pin_mut(&pin mut foo); // ok - | --- you could clone this value - -error[E0382]: borrow of moved value: `foo` - --> $DIR/borrow-unpin.rs:80:13 +error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable + --> $DIR/borrow-unpin.rs:65:13 | -LL | let mut foo = Foo::default(); - | ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait LL | let x = &pin mut foo; // ok - | --- value moved here + | ------------ mutable borrow occurs here LL | foo_ref(&foo); - | ^^^^ value borrowed here after move - | -note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:16:1 - | -LL | struct Foo(PhantomPinned); - | ^^^^^^^^^^ consider implementing `Clone` for this type -... -LL | let x = &pin mut foo; // ok - | --- you could clone this value + | ^^^^ immutable borrow occurs here +LL | foo_pin_mut(x); + | - mutable borrow later used here -error[E0382]: use of moved value: `foo` - --> $DIR/borrow-unpin.rs:99:26 - | -LL | let mut foo = Foo::default(); - | ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait -LL | foo_pin_mut(&pin mut foo); // ok - | --- value moved here -LL | foo_pin_mut(&pin mut foo); - | ^^^ value used here after move - | -note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:16:1 +error[E0499]: cannot borrow `foo` as mutable more than once at a time + --> $DIR/borrow-unpin.rs:87:17 | -LL | struct Foo(PhantomPinned); - | ^^^^^^^^^^ consider implementing `Clone` for this type -... -LL | foo_pin_mut(&pin mut foo); // ok - | --- you could clone this value - -error[E0382]: use of moved value: `foo` - --> $DIR/borrow-unpin.rs:103:26 - | -LL | let mut foo = Foo::default(); - | ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait LL | let x = &pin mut foo; // ok - | --- value moved here + | ------------ first mutable borrow occurs here LL | foo_pin_mut(&pin mut foo); - | ^^^ value used here after move - | -note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:16:1 - | -LL | struct Foo(PhantomPinned); - | ^^^^^^^^^^ consider implementing `Clone` for this type -... -LL | let x = &pin mut foo; // ok - | --- you could clone this value + | ^^^^^^^^^^^^ second mutable borrow occurs here +LL | foo_pin_mut(x); + | - first borrow later used here -error[E0505]: cannot move out of `foo` because it is borrowed - --> $DIR/borrow-unpin.rs:115:26 +error[E0502]: cannot borrow `foo` as mutable because it is also borrowed as immutable + --> $DIR/borrow-unpin.rs:98:17 | -LL | let mut foo = Foo::default(); - | ------- binding `foo` declared here LL | let x = &pin const foo; // ok - | -------------- borrow of `foo` occurs here + | -------------- immutable borrow occurs here LL | foo_pin_mut(&pin mut foo); - | ^^^ move out of `foo` occurs here -LL | + | ^^^^^^^^^^^^ mutable borrow occurs here LL | foo_pin_ref(x); - | - borrow later used here - | -note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:16:1 - | -LL | struct Foo(PhantomPinned); - | ^^^^^^^^^^ consider implementing `Clone` for this type -... -LL | let x = &pin const foo; // ok - | --- you could clone this value - -error[E0382]: borrow of moved value: `foo` - --> $DIR/borrow-unpin.rs:123:17 - | -LL | let mut foo = Foo::default(); - | ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait -LL | foo_pin_mut(&pin mut foo); // ok - | --- value moved here -LL | foo_pin_ref(&pin const foo); - | ^^^^^^^^^^^^^^ value borrowed here after move - | -note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:16:1 - | -LL | struct Foo(PhantomPinned); - | ^^^^^^^^^^ consider implementing `Clone` for this type -... -LL | foo_pin_mut(&pin mut foo); // ok - | --- you could clone this value + | - immutable borrow later used here -error[E0382]: borrow of moved value: `foo` - --> $DIR/borrow-unpin.rs:127:17 +error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable + --> $DIR/borrow-unpin.rs:109:17 | -LL | let mut foo = Foo::default(); - | ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait LL | let x = &pin mut foo; // ok - | --- value moved here + | ------------ mutable borrow occurs here LL | foo_pin_ref(&pin const foo); - | ^^^^^^^^^^^^^^ value borrowed here after move - | -note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:16:1 - | -LL | struct Foo(PhantomPinned); - | ^^^^^^^^^^ consider implementing `Clone` for this type -... -LL | let x = &pin mut foo; // ok - | --- you could clone this value + | ^^^^^^^^^^^^^^ immutable borrow occurs here +LL | foo_pin_mut(x); + | - mutable borrow later used here -error: aborting due to 12 previous errors +error: aborting due to 10 previous errors -Some errors have detailed explanations: E0382, E0505. -For more information about an error, try `rustc --explain E0382`. +Some errors have detailed explanations: E0499, E0502, E0505. +For more information about an error, try `rustc --explain E0499`. diff --git a/tests/ui/pin-ergonomics/borrow-unpin.rs b/tests/ui/pin-ergonomics/borrow-unpin.rs index 61e69bab12bc9..caab5c0f49369 100644 --- a/tests/ui/pin-ergonomics/borrow-unpin.rs +++ b/tests/ui/pin-ergonomics/borrow-unpin.rs @@ -2,14 +2,11 @@ #![feature(pin_ergonomics)] #![allow(dead_code, incomplete_features)] -// For now, in order to ensure soundness, we move the place in `&pin mut place` -// if `place` is not `Unpin`. -// In the next step, we borrow the place instead of moving it, after that we -// have to makes sure `&pin mut place` and `&pin const place` cannot violate -// the mut-xor-share rules. +// This test ensures that the place cannot be mutably borrowed or moved after pinned +// no matter if `place` is `Unpin` or not. -use std::pin::Pin; use std::marker::PhantomPinned; +use std::pin::Pin; #[cfg(pinned)] #[derive(Default)] @@ -19,66 +16,53 @@ struct Foo(PhantomPinned); #[derive(Default)] struct Foo; -fn foo_mut(_: &mut Foo) { -} - -fn foo_ref(_: &Foo) { -} - -fn foo_pin_mut(_: Pin<&mut Foo>) { -} - -fn foo_pin_ref(_: Pin<&Foo>) { -} - +fn foo_mut(_: &mut Foo) {} +fn foo_ref(_: &Foo) {} +fn foo_pin_mut(_: &pin mut Foo) {} +fn foo_pin_ref(_: &pin const Foo) {} fn foo_move(_: Foo) {} fn immutable_pin_mut_then_move() { - let foo = Foo::default(); - foo_pin_mut(&pin mut foo); //[unpin]~ ERROR cannot borrow `foo` as mutable, as it is not declared as mutable - foo_move(foo); //[pinned]~ ERROR use of moved value: `foo` + let mut foo = Foo::default(); + foo_pin_mut(&pin mut foo); // ok + foo_move(foo); //~ ERROR cannot move out of `foo` because it is pinned - let foo = Foo::default(); - let x = &pin mut foo; //[unpin]~ ERROR cannot borrow `foo` as mutable, as it is not declared as mutable - foo_move(foo); //[pinned]~ ERROR use of moved value: `foo` - //[unpin]~^ ERROR cannot move out of `foo` because it is borrowed - foo_pin_mut(x); // + let mut foo = Foo::default(); + let x = &pin mut foo; + foo_move(foo); //~ ERROR cannot move out of `foo` because it is borrowed + foo_pin_mut(x); // ok } - fn pin_mut_then_move() { let mut foo = Foo::default(); foo_pin_mut(&pin mut foo); // ok - foo_move(foo); //[pinned]~ ERROR use of moved value: `foo` + foo_move(foo); //~ ERROR cannot move out of `foo` because it is pinned let mut foo = Foo::default(); let x = &pin mut foo; // ok - foo_move(foo); //[pinned]~ ERROR use of moved value: `foo` - //[unpin]~^ ERROR cannot move out of `foo` because it is borrowed - foo_pin_mut(x); // + foo_move(foo); //~ ERROR cannot move out of `foo` because it is borrowed + foo_pin_mut(x); // ok } fn pin_ref_then_move() { let foo = Foo::default(); foo_pin_ref(&pin const foo); // ok - foo_move(foo); // ok + foo_move(foo); //~ ERROR cannot move out of `foo` because it is pinned let foo = Foo::default(); let x = &pin const foo; // ok - foo_move(foo); //[pinned]~ ERROR cannot move out of `foo` because it is borrowed - //[unpin]~^ ERROR cannot move out of `foo` because it is borrowed + foo_move(foo); //~ ERROR cannot move out of `foo` because it is borrowed foo_pin_ref(x); } fn pin_mut_then_ref() { let mut foo = Foo::default(); foo_pin_mut(&pin mut foo); // ok - foo_ref(&foo); //[pinned]~ ERROR borrow of moved value: `foo` + foo_ref(&foo); // ok let mut foo = Foo::default(); let x = &pin mut foo; // ok - foo_ref(&foo); //[pinned]~ ERROR borrow of moved value: `foo` - //[unpin]~^ ERROR cannot borrow `foo` as immutable because it is also borrowed as mutable + foo_ref(&foo); //~ ERROR cannot borrow `foo` as immutable because it is also borrowed as mutable foo_pin_mut(x); } @@ -96,12 +80,11 @@ fn pin_ref_then_ref() { fn pin_mut_then_pin_mut() { let mut foo = Foo::default(); foo_pin_mut(&pin mut foo); // ok - foo_pin_mut(&pin mut foo); //[pinned]~ ERROR use of moved value: `foo` + foo_pin_mut(&pin mut foo); // ok let mut foo = Foo::default(); let x = &pin mut foo; // ok - foo_pin_mut(&pin mut foo); //[pinned]~ ERROR use of moved value: `foo` - //[unpin]~^ ERROR cannot borrow `foo` as mutable more than once at a time + foo_pin_mut(&pin mut foo); //~ ERROR cannot borrow `foo` as mutable more than once at a time foo_pin_mut(x); } @@ -112,20 +95,18 @@ fn pin_ref_then_pin_mut() { let mut foo = Foo::default(); let x = &pin const foo; // ok - foo_pin_mut(&pin mut foo); //[pinned]~ ERROR cannot move out of `foo` because it is borrowed - //[unpin]~^ ERROR cannot borrow `foo` as mutable because it is also borrowed as immutable + foo_pin_mut(&pin mut foo); //~ ERROR cannot borrow `foo` as mutable because it is also borrowed as immutable foo_pin_ref(x); } fn pin_mut_then_pin_ref() { let mut foo = Foo::default(); foo_pin_mut(&pin mut foo); // ok - foo_pin_ref(&pin const foo); //[pinned]~ ERROR borrow of moved value: `foo` + foo_pin_ref(&pin const foo); // ok let mut foo = Foo::default(); let x = &pin mut foo; // ok - foo_pin_ref(&pin const foo); //[pinned]~ ERROR borrow of moved value: `foo` - //[unpin]~^ ERROR cannot borrow `foo` as immutable because it is also borrowed as mutable + foo_pin_ref(&pin const foo); //~ ERROR cannot borrow `foo` as immutable because it is also borrowed as mutable foo_pin_mut(x); } diff --git a/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr b/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr index bf9921343ee7d..b338426d7b0b5 100644 --- a/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr +++ b/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr @@ -1,40 +1,25 @@ -error[E0596]: cannot borrow `foo` as mutable, as it is not declared as mutable - --> $DIR/borrow-unpin.rs:38:17 +error: cannot move out of `foo` because it is pinned + --> $DIR/borrow-unpin.rs:28:14 | -LL | foo_pin_mut(&pin mut foo); - | ^^^^^^^^^^^^ cannot borrow as mutable - | -help: consider changing this to be mutable - | -LL | let mut foo = Foo::default(); - | +++ - -error[E0596]: cannot borrow `foo` as mutable, as it is not declared as mutable - --> $DIR/borrow-unpin.rs:42:13 - | -LL | let x = &pin mut foo; - | ^^^^^^^^^^^^ cannot borrow as mutable - | -help: consider changing this to be mutable - | -LL | let mut foo = Foo::default(); - | +++ +LL | foo_pin_mut(&pin mut foo); // ok + | ------------ pin of `foo` occurs here +LL | foo_move(foo); + | ^^^ move out of `foo` occurs here error[E0505]: cannot move out of `foo` because it is borrowed - --> $DIR/borrow-unpin.rs:43:14 + --> $DIR/borrow-unpin.rs:32:14 | -LL | let foo = Foo::default(); - | --- binding `foo` declared here +LL | let mut foo = Foo::default(); + | ------- binding `foo` declared here LL | let x = &pin mut foo; | ------------ borrow of `foo` occurs here LL | foo_move(foo); | ^^^ move out of `foo` occurs here -LL | -LL | foo_pin_mut(x); // +LL | foo_pin_mut(x); // ok | - borrow later used here | note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:20:1 + --> $DIR/borrow-unpin.rs:17:1 | LL | struct Foo; | ^^^^^^^^^^ consider implementing `Clone` for this type @@ -42,8 +27,16 @@ LL | struct Foo; LL | let x = &pin mut foo; | --- you could clone this value +error: cannot move out of `foo` because it is pinned + --> $DIR/borrow-unpin.rs:39:14 + | +LL | foo_pin_mut(&pin mut foo); // ok + | ------------ pin of `foo` occurs here +LL | foo_move(foo); + | ^^^ move out of `foo` occurs here + error[E0505]: cannot move out of `foo` because it is borrowed - --> $DIR/borrow-unpin.rs:56:14 + --> $DIR/borrow-unpin.rs:43:14 | LL | let mut foo = Foo::default(); | ------- binding `foo` declared here @@ -51,12 +44,11 @@ LL | let x = &pin mut foo; // ok | ------------ borrow of `foo` occurs here LL | foo_move(foo); | ^^^ move out of `foo` occurs here -LL | -LL | foo_pin_mut(x); // +LL | foo_pin_mut(x); // ok | - borrow later used here | note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:20:1 + --> $DIR/borrow-unpin.rs:17:1 | LL | struct Foo; | ^^^^^^^^^^ consider implementing `Clone` for this type @@ -64,8 +56,16 @@ LL | struct Foo; LL | let x = &pin mut foo; // ok | --- you could clone this value +error: cannot move out of `foo` because it is pinned + --> $DIR/borrow-unpin.rs:50:14 + | +LL | foo_pin_ref(&pin const foo); // ok + | -------------- pin of `foo` occurs here +LL | foo_move(foo); + | ^^^ move out of `foo` occurs here + error[E0505]: cannot move out of `foo` because it is borrowed - --> $DIR/borrow-unpin.rs:68:14 + --> $DIR/borrow-unpin.rs:54:14 | LL | let foo = Foo::default(); | --- binding `foo` declared here @@ -73,12 +73,11 @@ LL | let x = &pin const foo; // ok | -------------- borrow of `foo` occurs here LL | foo_move(foo); | ^^^ move out of `foo` occurs here -LL | LL | foo_pin_ref(x); | - borrow later used here | note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:20:1 + --> $DIR/borrow-unpin.rs:17:1 | LL | struct Foo; | ^^^^^^^^^^ consider implementing `Clone` for this type @@ -87,50 +86,46 @@ LL | let x = &pin const foo; // ok | --- you could clone this value error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable - --> $DIR/borrow-unpin.rs:80:13 + --> $DIR/borrow-unpin.rs:65:13 | LL | let x = &pin mut foo; // ok | ------------ mutable borrow occurs here LL | foo_ref(&foo); | ^^^^ immutable borrow occurs here -LL | LL | foo_pin_mut(x); | - mutable borrow later used here error[E0499]: cannot borrow `foo` as mutable more than once at a time - --> $DIR/borrow-unpin.rs:103:17 + --> $DIR/borrow-unpin.rs:87:17 | LL | let x = &pin mut foo; // ok | ------------ first mutable borrow occurs here LL | foo_pin_mut(&pin mut foo); | ^^^^^^^^^^^^ second mutable borrow occurs here -LL | LL | foo_pin_mut(x); | - first borrow later used here error[E0502]: cannot borrow `foo` as mutable because it is also borrowed as immutable - --> $DIR/borrow-unpin.rs:115:17 + --> $DIR/borrow-unpin.rs:98:17 | LL | let x = &pin const foo; // ok | -------------- immutable borrow occurs here LL | foo_pin_mut(&pin mut foo); | ^^^^^^^^^^^^ mutable borrow occurs here -LL | LL | foo_pin_ref(x); | - immutable borrow later used here error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable - --> $DIR/borrow-unpin.rs:127:17 + --> $DIR/borrow-unpin.rs:109:17 | LL | let x = &pin mut foo; // ok | ------------ mutable borrow occurs here LL | foo_pin_ref(&pin const foo); | ^^^^^^^^^^^^^^ immutable borrow occurs here -LL | LL | foo_pin_mut(x); | - mutable borrow later used here -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors -Some errors have detailed explanations: E0499, E0502, E0505, E0596. +Some errors have detailed explanations: E0499, E0502, E0505. For more information about an error, try `rustc --explain E0499`. diff --git a/tests/ui/pin-ergonomics/borrow.rs b/tests/ui/pin-ergonomics/borrow.rs index f221165848bac..214832340b2d3 100644 --- a/tests/ui/pin-ergonomics/borrow.rs +++ b/tests/ui/pin-ergonomics/borrow.rs @@ -1,4 +1,3 @@ -//@ check-pass #![feature(pin_ergonomics)] #![allow(dead_code, incomplete_features)] @@ -9,11 +8,9 @@ use std::pin::Pin; struct Foo; -fn foo_pin_mut(_: Pin<&mut Foo>) { -} +fn foo_pin_mut(_: Pin<&mut Foo>) {} -fn foo_pin_ref(_: Pin<&Foo>) { -} +fn foo_pin_ref(_: Pin<&Foo>) {} fn bar() { let mut x: Pin<&mut _> = &pin mut Foo; @@ -27,12 +24,21 @@ fn bar() { foo_pin_ref(x); } -fn baz(mut x: Foo, y: Foo) { - let _x = &pin mut x; - let _x = x; // ok because `Foo: Unpin` and thus `&pin mut x` doesn't move `x` - - let _y = &pin const y; - let _y = y; // ok because `&pin const y` dosn't move `y` +fn baz(mut x: Foo, mut y: Foo) { + { + let _x = &pin mut x; + } + let _x = &mut x; //~ ERROR cannot borrow `x` as mutable because it is pinned + let _x = x; //~ ERROR cannot move out of `x` because it is pinned + + x = Foo; + let _x = &mut x; // ok + + { + let _y = &pin const y; + } + let _y = &mut y; //~ ERROR cannot borrow `y` as mutable because it is pinned + let _y = y; //~ ERROR cannot move out of `y` because it is pinned } fn main() {} diff --git a/tests/ui/pin-ergonomics/borrow.stderr b/tests/ui/pin-ergonomics/borrow.stderr new file mode 100644 index 0000000000000..75376eff1d35a --- /dev/null +++ b/tests/ui/pin-ergonomics/borrow.stderr @@ -0,0 +1,38 @@ +error: cannot borrow `x` as mutable because it is pinned + --> $DIR/borrow.rs:31:14 + | +LL | let _x = &pin mut x; + | ---------- pin of `x` occurs here +LL | } +LL | let _x = &mut x; + | ^^^^^^ borrow of `x` as mutable occurs here + +error: cannot move out of `x` because it is pinned + --> $DIR/borrow.rs:32:14 + | +LL | let _x = &pin mut x; + | ---------- pin of `x` occurs here +... +LL | let _x = x; + | ^ move out of `x` occurs here + +error: cannot borrow `y` as mutable because it is pinned + --> $DIR/borrow.rs:40:14 + | +LL | let _y = &pin const y; + | ------------ pin of `y` occurs here +LL | } +LL | let _y = &mut y; + | ^^^^^^ borrow of `y` as mutable occurs here + +error: cannot move out of `y` because it is pinned + --> $DIR/borrow.rs:41:14 + | +LL | let _y = &pin const y; + | ------------ pin of `y` occurs here +... +LL | let _y = y; + | ^ move out of `y` occurs here + +error: aborting due to 4 previous errors + From 122d1becc779bf674d2031eef37477b107bf4a3e Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Mon, 27 Apr 2026 14:59:51 +0200 Subject: [PATCH 2/8] Fix pin ergonomics borrowck for transient pinned borrows --- compiler/rustc_borrowck/src/borrow_set.rs | 4 +- compiler/rustc_borrowck/src/dataflow.rs | 7 +-- .../src/diagnostics/conflict_errors.rs | 26 +++++----- .../rustc_borrowck/src/diagnostics/mod.rs | 2 + compiler/rustc_borrowck/src/lib.rs | 47 +++++++++++-------- .../src/polonius/legacy/loan_invalidations.rs | 28 ++++++----- .../src/check_consts/check.rs | 8 +++- .../src/check_consts/resolver.rs | 4 +- compiler/rustc_middle/src/mir/pretty.rs | 4 +- compiler/rustc_middle/src/mir/statement.rs | 6 +-- compiler/rustc_middle/src/mir/syntax.rs | 18 ++++++- compiler/rustc_middle/src/mir/visit.rs | 4 +- .../rustc_mir_build/src/check_unsafety.rs | 6 +-- compiler/rustc_mir_build/src/thir/cx/expr.rs | 11 +++-- .../src/impls/borrowed_locals.rs | 2 +- .../rustc_mir_transform/src/promote_consts.rs | 4 +- .../src/unstable/convert/stable/mir.rs | 4 +- .../borrow-pinned-projection.rs | 34 ++++++++++++++ .../borrow-pinned-projection.stderr | 20 ++++++++ .../pin-coercion-get-mut-then-as-mut.rs | 24 ++++++++++ .../pin-coercion-unpin-roundtrip.rs | 29 ++++++++++++ 21 files changed, 220 insertions(+), 72 deletions(-) create mode 100644 tests/ui/pin-ergonomics/borrow-pinned-projection.rs create mode 100644 tests/ui/pin-ergonomics/borrow-pinned-projection.stderr create mode 100644 tests/ui/pin-ergonomics/pin-coercion-get-mut-then-as-mut.rs create mode 100644 tests/ui/pin-ergonomics/pin-coercion-unpin-roundtrip.rs diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 0452e47c68c0d..fb7e54afcb19e 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -124,8 +124,8 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> { mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default | mir::MutBorrowKind::TwoPhaseBorrow, } => "mut ", - mir::BorrowKind::Pinned(mir::Mutability::Not) => "pin const ", - mir::BorrowKind::Pinned(mir::Mutability::Mut) => "pin mut ", + mir::BorrowKind::Pinned(mir::Mutability::Not, _) => "pin const ", + mir::BorrowKind::Pinned(mir::Mutability::Mut, _) => "pin mut ", }; write!(w, "&{:?} {}{:?}", self.region, kind, self.borrowed_place) } diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index a4c97097ed690..c1e402391c846 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -757,9 +757,10 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Pins<'_, 'tcx> { mir::StatementKind::Assign(box (lhs, rhs)) => { self.kill_pins_on_place(state, *lhs); - // Check if this is a pinned borrow - if let mir::Rvalue::Ref(_, mir::BorrowKind::Pinned(_), place) = rhs { - // Generate the pin + // Only user-written `&pin` borrows create long-lived pin facts. + if let mir::Rvalue::Ref(_, mir::BorrowKind::Pinned(_, kind), place) = rhs + && matches!(*kind, mir::PinBorrowKind::Persistent) + { self.gen_pins_on_place(state, *place); } } diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 82500684d8420..bab4ab158fb9d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1773,9 +1773,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ( BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep) - | BorrowKind::Pinned(mir::Mutability::Not), + | BorrowKind::Pinned(mir::Mutability::Not, _), BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow } - | BorrowKind::Pinned(mir::Mutability::Mut), + | BorrowKind::Pinned(mir::Mutability::Mut, _), ) => { first_borrow_desc = "mutable "; let mut err = self.cannot_reborrow_already_borrowed( @@ -1800,10 +1800,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } ( BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow } - | BorrowKind::Pinned(mir::Mutability::Mut), + | BorrowKind::Pinned(mir::Mutability::Mut, _), BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep) - | BorrowKind::Pinned(mir::Mutability::Not), + | BorrowKind::Pinned(mir::Mutability::Not, _), ) => { first_borrow_desc = "immutable "; let mut err = self.cannot_reborrow_already_borrowed( @@ -1835,9 +1835,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ( BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow } - | BorrowKind::Pinned(mir::Mutability::Mut), + | BorrowKind::Pinned(mir::Mutability::Mut, _), BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow } - | BorrowKind::Pinned(mir::Mutability::Mut), + | BorrowKind::Pinned(mir::Mutability::Mut, _), ) => { first_borrow_desc = "first "; let mut err = self.cannot_mutably_borrow_multiply( @@ -1877,7 +1877,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } ( - BorrowKind::Mut { .. } | BorrowKind::Pinned(mir::Mutability::Mut), + BorrowKind::Mut { .. } | BorrowKind::Pinned(mir::Mutability::Mut, _), BorrowKind::Fake(FakeBorrowKind::Shallow), ) => { if let Some(immutable_section_description) = @@ -1944,7 +1944,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ( BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep) - | BorrowKind::Pinned(mir::Mutability::Not), + | BorrowKind::Pinned(mir::Mutability::Not, _), BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }, ) => { first_borrow_desc = "first "; @@ -1962,7 +1962,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } ( - BorrowKind::Mut { .. } | BorrowKind::Pinned(mir::Mutability::Mut), + BorrowKind::Mut { .. } | BorrowKind::Pinned(mir::Mutability::Mut, _), BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }, ) => { first_borrow_desc = "first "; @@ -1982,15 +1982,17 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ( BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep) - | BorrowKind::Pinned(mir::Mutability::Not), - BorrowKind::Shared | BorrowKind::Fake(_) | BorrowKind::Pinned(mir::Mutability::Not), + | BorrowKind::Pinned(mir::Mutability::Not, _), + BorrowKind::Shared + | BorrowKind::Fake(_) + | BorrowKind::Pinned(mir::Mutability::Not, _), ) | ( BorrowKind::Fake(FakeBorrowKind::Shallow), BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake(_) - | BorrowKind::Pinned(_), + | BorrowKind::Pinned(..), ) => { unreachable!() } diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index fc4afd0bb4b50..bb1eb9ee4a5c5 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -869,11 +869,13 @@ impl UseSpans<'_> { | rustc_middle::mir::BorrowKind::Fake(_) | rustc_middle::mir::BorrowKind::Pinned( rustc_middle::mir::Mutability::Not, + _, ) => CaptureVarKind::Immut { kind_span: capture_kind_span }, rustc_middle::mir::BorrowKind::Mut { .. } | rustc_middle::mir::BorrowKind::Pinned( rustc_middle::mir::Mutability::Mut, + _, ) => CaptureVarKind::Mut { kind_span: capture_kind_span }, }, None => CaptureVarKind::Move { kind_span: capture_kind_span }, diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 9cbf047126793..efb24e31511fe 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1328,16 +1328,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { ( Read(_), - BorrowKind::Shared | BorrowKind::Fake(_) | BorrowKind::Pinned(Mutability::Not), + BorrowKind::Shared + | BorrowKind::Fake(_) + | BorrowKind::Pinned(Mutability::Not, _), ) | ( Read(ReadKind::Borrow(BorrowKind::Fake(FakeBorrowKind::Shallow))), - BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut), + BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut, _), ) => ControlFlow::Continue(()), ( Reservation(_), - BorrowKind::Fake(_) | BorrowKind::Shared | BorrowKind::Pinned(Mutability::Not), + BorrowKind::Fake(_) + | BorrowKind::Shared + | BorrowKind::Pinned(Mutability::Not, _), ) => { // This used to be a future compatibility warning (to be // disallowed on NLL). See rust-lang/rust#56254 @@ -1350,13 +1354,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { } // Ignore the expired borrow (pinnedness never conflicts with a read) - (Read(_), BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut)) + (Read(_), BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut, _)) if !borrows_in_scope.contains(borrow_index) => { ControlFlow::Continue(()) } - (Read(kind), BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut)) => { + (Read(kind), BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut, _)) => { // Reading from mere reservations of mutable-borrows is OK. if !is_active(this.dominators(), borrow, location) { assert!(borrow.kind.is_two_phase_borrow()); @@ -1393,7 +1397,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { ControlFlow::Continue(()) } // Mutable (pinned) borrow doesn't conflict with an expired borrow - WriteKind::MutableBorrow(BorrowKind::Pinned(Mutability::Mut)) => { + WriteKind::MutableBorrow(BorrowKind::Pinned(Mutability::Mut, _)) => { ControlFlow::Continue(()) } // Mutable (but non-pinned) borrow conflicts with an earlier pinned borrow @@ -1550,12 +1554,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { BorrowKind::Fake(FakeBorrowKind::Shallow) => { (Shallow(Some(ArtificialField::FakeBorrow)), Read(ReadKind::Borrow(bk))) } - BorrowKind::Pinned(Mutability::Not) => { - (Shallow(None), Read(ReadKind::Borrow(BorrowKind::Pinned(Mutability::Not)))) - } - BorrowKind::Pinned(Mutability::Mut) => ( + BorrowKind::Pinned(Mutability::Not, pin_kind) => ( + Shallow(None), + Read(ReadKind::Borrow(BorrowKind::Pinned(Mutability::Not, pin_kind))), + ), + BorrowKind::Pinned(Mutability::Mut, pin_kind) => ( Shallow(None), - Write(WriteKind::MutableBorrow(BorrowKind::Pinned(Mutability::Mut))), + Write(WriteKind::MutableBorrow(BorrowKind::Pinned( + Mutability::Mut, + pin_kind, + ))), ), BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep) => { (Deep, Read(ReadKind::Borrow(bk))) @@ -1922,9 +1930,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { // only mutable borrows should be 2-phase assert!(match borrow.kind { - BorrowKind::Shared | BorrowKind::Fake(_) | BorrowKind::Pinned(Mutability::Not) => - false, - BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut) => true, + BorrowKind::Shared + | BorrowKind::Fake(_) + | BorrowKind::Pinned(Mutability::Not, _) => false, + BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut, _) => true, }); self.access_place( @@ -2501,7 +2510,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | WriteKind::StorageDeadOrDrop | WriteKind::MutableBorrow(BorrowKind::Shared) | WriteKind::MutableBorrow(BorrowKind::Fake(_)) - | WriteKind::MutableBorrow(BorrowKind::Pinned(Mutability::Mut)), + | WriteKind::MutableBorrow(BorrowKind::Pinned(Mutability::Mut, _)), ) => { if self.is_mutable(place.as_ref(), is_local_mutation_allowed).is_err() && !self.has_buffered_diags() @@ -2529,18 +2538,18 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake(_) - | BorrowKind::Pinned(Mutability::Not), + | BorrowKind::Pinned(Mutability::Not, _), ) | ReadKind::Copy, ) => { // Access authorized return false; } - Reservation(WriteKind::MutableBorrow(BorrowKind::Pinned(_))) => { + Reservation(WriteKind::MutableBorrow(BorrowKind::Pinned(..))) => { span_bug!(span, "invalid reservation with a pinned borrow kind: {:?}", kind); } - Write(WriteKind::MutableBorrow(BorrowKind::Pinned(Mutability::Not))) - | Read(ReadKind::Borrow(BorrowKind::Pinned(Mutability::Mut))) => { + Write(WriteKind::MutableBorrow(BorrowKind::Pinned(Mutability::Not, _))) + | Read(ReadKind::Borrow(BorrowKind::Pinned(Mutability::Mut, _))) => { span_bug!( span, "invalid read with a pinned mutable borrow kind or write with an immutable pinned borrow kind: {:?}", diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index 02940f00a1b2f..ecf28552b444e 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -262,12 +262,17 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep) => { (Deep, Read(ReadKind::Borrow(bk))) } - BorrowKind::Pinned(Mutability::Not) => { - (Deep, Read(ReadKind::Borrow(BorrowKind::Pinned(Mutability::Not)))) - } - BorrowKind::Pinned(Mutability::Mut) => { - (Deep, Write(WriteKind::MutableBorrow(BorrowKind::Pinned(Mutability::Mut)))) - } + BorrowKind::Pinned(Mutability::Not, pin_kind) => ( + Deep, + Read(ReadKind::Borrow(BorrowKind::Pinned(Mutability::Not, pin_kind))), + ), + BorrowKind::Pinned(Mutability::Mut, pin_kind) => ( + Deep, + Write(WriteKind::MutableBorrow(BorrowKind::Pinned( + Mutability::Mut, + pin_kind, + ))), + ), BorrowKind::Mut { .. } => { let wk = WriteKind::MutableBorrow(bk); if bk.is_two_phase_borrow() { @@ -383,7 +388,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { Read(_), BorrowKind::Fake(_) | BorrowKind::Shared - | BorrowKind::Pinned(Mutability::Not), + | BorrowKind::Pinned(Mutability::Not, _), ) | ( Read(ReadKind::Borrow(BorrowKind::Fake(FakeBorrowKind::Shallow))), @@ -392,7 +397,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { // Reads don't invalidate shared or shallow borrows } - (Read(_), BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut)) => { + (Read(_), BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut, _)) => { // Reading from mere reservations of mutable-borrows is OK. if !is_active(this.dominators, borrow, location) { // If the borrow isn't active yet, reads don't invalidate it @@ -433,9 +438,10 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { // only mutable borrows should be 2-phase assert!(match borrow.kind { - BorrowKind::Shared | BorrowKind::Fake(_) | BorrowKind::Pinned(Mutability::Not) => - false, - BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut) => true, + BorrowKind::Shared + | BorrowKind::Fake(_) + | BorrowKind::Pinned(Mutability::Not, _) => false, + BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut, _) => true, }); self.access_place( diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 76a6e6ba0f0bd..10c07d2f1d5e6 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -582,7 +582,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } } - Rvalue::Ref(_, BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut), place) + Rvalue::Ref( + _, + BorrowKind::Mut { .. } | BorrowKind::Pinned(Mutability::Mut, _), + place, + ) | Rvalue::RawPtr(RawPtrKind::Mut, place) => { // Inside mutable statics, we allow arbitrary mutable references. // We've allowed `static mut FOO = &mut [elements];` for a long time (the exact @@ -598,7 +602,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Rvalue::Ref( _, - BorrowKind::Shared | BorrowKind::Fake(_) | BorrowKind::Pinned(Mutability::Not), + BorrowKind::Shared | BorrowKind::Fake(_) | BorrowKind::Pinned(Mutability::Not, _), place, ) | Rvalue::RawPtr(RawPtrKind::Const, place) => { diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index 6d0938ed9efda..63022a82eeadd 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -103,10 +103,10 @@ where fn ref_allows_mutation(&self, kind: mir::BorrowKind, place: mir::Place<'tcx>) -> bool { match kind { - mir::BorrowKind::Mut { .. } | mir::BorrowKind::Pinned(mir::Mutability::Mut) => true, + mir::BorrowKind::Mut { .. } | mir::BorrowKind::Pinned(mir::Mutability::Mut, _) => true, mir::BorrowKind::Shared | mir::BorrowKind::Fake(_) - | mir::BorrowKind::Pinned(mir::Mutability::Not) => { + | mir::BorrowKind::Pinned(mir::Mutability::Not, _) => { self.shared_borrow_allows_mutation(place) } } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 1f04cc73d1342..03b7e32103e4f 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1117,8 +1117,8 @@ impl<'tcx> Debug for Rvalue<'tcx> { BorrowKind::Fake(FakeBorrowKind::Deep) => "fake ", BorrowKind::Fake(FakeBorrowKind::Shallow) => "fake shallow ", BorrowKind::Mut { .. } => "mut ", - BorrowKind::Pinned(Mutability::Not) => "pin ", - BorrowKind::Pinned(Mutability::Mut) => "pin mut ", + BorrowKind::Pinned(Mutability::Not, _) => "pin ", + BorrowKind::Pinned(Mutability::Mut, _) => "pin mut ", }; // When printing regions, add trailing space if necessary. diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 9b549995f44ac..a4351d000bc58 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -838,7 +838,7 @@ impl BorrowKind { match *self { BorrowKind::Shared | BorrowKind::Fake(_) => Mutability::Not, BorrowKind::Mut { .. } => Mutability::Mut, - BorrowKind::Pinned(mutability) => mutability, + BorrowKind::Pinned(mutability, _) => mutability, } } @@ -849,7 +849,7 @@ impl BorrowKind { BorrowKind::Shared | BorrowKind::Fake(_) | BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::ClosureCapture } - | BorrowKind::Pinned(_) => false, + | BorrowKind::Pinned(..) => false, BorrowKind::Mut { kind: MutBorrowKind::TwoPhaseBorrow } => true, } } @@ -862,7 +862,7 @@ impl BorrowKind { // We have no type corresponding to a shallow borrow, so use // `&` as an approximation. BorrowKind::Fake(_) => hir::Mutability::Not, - BorrowKind::Pinned(mutability) => mutability, + BorrowKind::Pinned(mutability, _) => mutability, } } } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 87bd025cb4822..c30dc64e22fe5 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -179,8 +179,22 @@ pub enum BorrowKind { /// Data is mutable and not aliasable. Mut { kind: MutBorrowKind }, - /// Data is pinned and cannot be moved or mutably borrowed until dropped or reassigned. - Pinned(Mutability), + /// Data is pinned. + Pinned(Mutability, PinBorrowKind), +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)] +#[derive(Hash, HashStable)] +pub enum PinBorrowKind { + /// A user-written `&pin` borrow. + /// + /// The borrowed place stays pinned after this borrow ends, until reassignment. + Persistent, + /// A compiler-generated pinned borrow used for adjustments or coercions. + /// + /// This remains a normal borrow while live, but does not create a persistent + /// pin fact after it ends. + Transient, } #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)] diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 1ff413123e1f0..52288e4e13fcb 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -726,8 +726,8 @@ macro_rules! make_mir_visitor { ), BorrowKind::Mut { .. } => PlaceContext::MutatingUse(MutatingUseContext::Borrow), - BorrowKind::Pinned(Mutability::Not) => PlaceContext::NonMutatingUse(NonMutatingUseContext::PinnedBorrow), - BorrowKind::Pinned(Mutability::Mut) => PlaceContext::MutatingUse(MutatingUseContext::PinnedBorrow), + BorrowKind::Pinned(Mutability::Not, _) => PlaceContext::NonMutatingUse(NonMutatingUseContext::PinnedBorrow), + BorrowKind::Pinned(Mutability::Mut, _) => PlaceContext::MutatingUse(MutatingUseContext::PinnedBorrow), }; self.visit_place(path, ctx, location); } diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 8ddef167b98e5..a6541d7a89afb 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -678,17 +678,17 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { match borrow_kind { BorrowKind::Fake(_) | BorrowKind::Shared - | BorrowKind::Pinned(hir::Mutability::Not) + | BorrowKind::Pinned(hir::Mutability::Not, _) if !self.thir[arg].ty.is_freeze(self.tcx, self.typing_env) => { self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField) } - BorrowKind::Mut { .. } | BorrowKind::Pinned(hir::Mutability::Mut) => { + BorrowKind::Mut { .. } | BorrowKind::Pinned(hir::Mutability::Mut, _) => { self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField) } BorrowKind::Fake(_) | BorrowKind::Shared - | BorrowKind::Pinned(hir::Mutability::Not) => {} + | BorrowKind::Pinned(hir::Mutability::Not, _) => {} } } } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 22f06ad9e844b..0a419d8ab2ba2 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -10,7 +10,7 @@ use rustc_middle::hir::place::{ Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind, }; use rustc_middle::middle::region; -use rustc_middle::mir::{self, AssignOp, BinOp, BorrowKind, UnOp}; +use rustc_middle::mir::{self, AssignOp, BinOp, BorrowKind, PinBorrowKind, UnOp}; use rustc_middle::thir::*; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, PointerCoercion, @@ -191,8 +191,8 @@ impl<'tcx> ThirBuildCx<'tcx> { ExprKind::RawBorrow { mutability, arg: self.thir.exprs.push(expr) } } Adjust::Borrow(AutoBorrow::Pin(mutbl)) => { - // expr = &pin (mut|const|) arget - let borrow_kind = BorrowKind::Pinned(mutbl); + // Build the transient `&pin` borrow needed by this adjustment. + let borrow_kind = BorrowKind::Pinned(mutbl, PinBorrowKind::Transient); let new_pin_target = Ty::new_ref(self.tcx, self.tcx.lifetimes.re_erased, expr.ty, mutbl); let arg = self.thir.exprs.push(expr); @@ -453,7 +453,10 @@ impl<'tcx> ThirBuildCx<'tcx> { temp_scope_id: expr.hir_id.local_id, ty, span: expr.span, - kind: ExprKind::Borrow { borrow_kind: BorrowKind::Pinned(mutability), arg }, + kind: ExprKind::Borrow { + borrow_kind: BorrowKind::Pinned(mutability, PinBorrowKind::Persistent), + arg, + }, }); ExprKind::Adt(Box::new(AdtExpr { adt_def, diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index b319d8db34fbe..aca586eb72f78 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -80,7 +80,7 @@ where Rvalue::RawPtr(_, borrowed_place) | Rvalue::Ref( _, - BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Pinned(_), + BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Pinned(..), borrowed_place, ) => { if !borrowed_place.is_indirect() { diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 7eacea4224ba0..331870b387139 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -399,7 +399,7 @@ impl<'tcx> Validator<'_, 'tcx> { return Err(Unpromotable); } - BorrowKind::Shared | BorrowKind::Pinned(Mutability::Not) => { + BorrowKind::Shared | BorrowKind::Pinned(Mutability::Not, _) => { let has_mut_interior = self.qualif_local::(place.local); if has_mut_interior { return Err(Unpromotable); @@ -409,7 +409,7 @@ impl<'tcx> Validator<'_, 'tcx> { // FIXME: consider changing this to only promote &mut [] for default borrows, // also forbidding two phase borrows BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow } - | BorrowKind::Pinned(Mutability::Mut) => { + | BorrowKind::Pinned(Mutability::Mut, _) => { let ty = place.ty(self.body, self.tcx).ty; // In theory, any zero-sized value could be borrowed diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index 3cb6a8dedaaed..774728d3bb6da 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -281,8 +281,8 @@ impl<'tcx> Stable<'tcx> for mir::BorrowKind { Shared => crate::mir::BorrowKind::Shared, Fake(kind) => crate::mir::BorrowKind::Fake(kind.stable(tables, cx)), Mut { kind } => crate::mir::BorrowKind::Mut { kind: kind.stable(tables, cx) }, - Pinned(mir::Mutability::Not) => crate::mir::BorrowKind::Shared, - Pinned(mir::Mutability::Mut) => { + Pinned(mir::Mutability::Not, _) => crate::mir::BorrowKind::Shared, + Pinned(mir::Mutability::Mut, _) => { crate::mir::BorrowKind::Mut { kind: crate::mir::MutBorrowKind::Default } } } diff --git a/tests/ui/pin-ergonomics/borrow-pinned-projection.rs b/tests/ui/pin-ergonomics/borrow-pinned-projection.rs new file mode 100644 index 0000000000000..648d001830efa --- /dev/null +++ b/tests/ui/pin-ergonomics/borrow-pinned-projection.rs @@ -0,0 +1,34 @@ +#![feature(pin_ergonomics)] +#![allow(incomplete_features)] + +// This protects pinning projected places. Pinning `pair.0` must not pin +// unrelated disjoint fields, but it must reject later mutable borrows or moves +// of `pair.0` until reassignment. + +struct Foo; + +fn mutable_borrow_of_pinned_projection() { + let mut pair = (Foo, Foo); + + { + let _pin = &pin mut pair.0; + } + + let _other = &mut pair.1; + let _borrow = &mut pair.0; + //~^ ERROR cannot borrow `pair.0` as mutable because it is pinned +} + +fn move_of_pinned_projection() { + let mut pair = (Foo, Foo); + + { + let _pin = &pin mut pair.0; + } + + let _other = &mut pair.1; + let _moved = pair.0; + //~^ ERROR cannot move out of `pair.0` because it is pinned +} + +fn main() {} diff --git a/tests/ui/pin-ergonomics/borrow-pinned-projection.stderr b/tests/ui/pin-ergonomics/borrow-pinned-projection.stderr new file mode 100644 index 0000000000000..93028eb651dfd --- /dev/null +++ b/tests/ui/pin-ergonomics/borrow-pinned-projection.stderr @@ -0,0 +1,20 @@ +error: cannot borrow `pair.0` as mutable because it is pinned + --> $DIR/borrow-pinned-projection.rs:18:19 + | +LL | let _pin = &pin mut pair.0; + | --------------- pin of `pair.0` occurs here +... +LL | let _borrow = &mut pair.0; + | ^^^^^^^^^^^ borrow of `pair.0` as mutable occurs here + +error: cannot move out of `pair.0` because it is pinned + --> $DIR/borrow-pinned-projection.rs:30:18 + | +LL | let _pin = &pin mut pair.0; + | --------------- pin of `pair.0` occurs here +... +LL | let _moved = pair.0; + | ^^^^^^ move out of `pair.0` occurs here + +error: aborting due to 2 previous errors + diff --git a/tests/ui/pin-ergonomics/pin-coercion-get-mut-then-as-mut.rs b/tests/ui/pin-ergonomics/pin-coercion-get-mut-then-as-mut.rs new file mode 100644 index 0000000000000..2ea4cf871e8ae --- /dev/null +++ b/tests/ui/pin-ergonomics/pin-coercion-get-mut-then-as-mut.rs @@ -0,0 +1,24 @@ +//@ check-pass +//@ edition:2024 + +#![feature(pin_ergonomics)] +#![allow(incomplete_features)] + +// This protects calling `Pin<&mut T>::as_mut()` after using `get_mut()` for +// `T: Unpin`; the `get_mut()` path must not make the later `as_mut()` borrow +// fail as pinned. + +fn pin_mut_to_ref(value: &pin mut T) -> &T { + value +} + +fn regression(value: &mut T) { + let mut value: &pin mut T = value; + let _: &mut T = value.get_mut(); + let _: &T = pin_mut_to_ref(value.as_mut()); +} + +fn main() { + let mut value = (); + regression(&mut value); +} diff --git a/tests/ui/pin-ergonomics/pin-coercion-unpin-roundtrip.rs b/tests/ui/pin-ergonomics/pin-coercion-unpin-roundtrip.rs new file mode 100644 index 0000000000000..6038787d5ef2f --- /dev/null +++ b/tests/ui/pin-ergonomics/pin-coercion-unpin-roundtrip.rs @@ -0,0 +1,29 @@ +//@ check-pass +//@ edition:2024 + +#![feature(pin_ergonomics)] +#![allow(incomplete_features)] + +// This protects the `T: Unpin` round trip from `&mut T` to `&pin mut T` and +// back to `&mut T`, which must not leave the reference falsely treated as pinned. + +fn to_pin_mut(value: &mut T) -> &pin mut T { + value +} + +fn to_mut(value: &pin mut T) -> &mut T { + value +} + +fn touch_mut(_: &mut T) {} + +fn regression(value: &mut T) { + let value: &pin mut T = to_pin_mut(value); + let value: &mut T = to_mut(value); + touch_mut(value); +} + +fn main() { + let mut value = (); + regression(&mut value); +} From d7f4bbd9336e1ee5bae077a163ab0fe41ee9d974 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Tue, 28 Apr 2026 16:32:43 +0200 Subject: [PATCH 3/8] Require pin_v2 for direct pin borrows of ADTs --- compiler/rustc_hir_typeck/src/errors.rs | 15 ++++++++ compiler/rustc_hir_typeck/src/expr.rs | 19 ++++++++++ .../borrow-pinned-projection.rs | 1 + .../borrow-pinned-projection.stderr | 4 +-- .../pin-ergonomics/borrow-unpin.pinned.stderr | 26 +++++++------- tests/ui/pin-ergonomics/borrow-unpin.rs | 2 ++ .../pin-ergonomics/borrow-unpin.unpin.stderr | 26 +++++++------- tests/ui/pin-ergonomics/borrow.rs | 1 + tests/ui/pin-ergonomics/borrow.stderr | 8 ++--- .../direct-borrow-requires-pin-v2.rs | 17 +++++++++ .../direct-borrow-requires-pin-v2.stderr | 35 +++++++++++++++++++ .../ui/pin-ergonomics/user-type-projection.rs | 1 + 12 files changed, 123 insertions(+), 32 deletions(-) create mode 100644 tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.rs create mode 100644 tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.stderr diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index b55e7933cc1e9..5959f57128ae0 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -1385,6 +1385,21 @@ pub(crate) struct ProjectOnNonPinProjectType { pub sugg_span: Option, } +#[derive(Diagnostic)] +#[diag("cannot directly pin an ADT that is not `#[pin_v2]`")] +pub(crate) struct DirectPinBorrowOfNonPinProjectType { + #[primary_span] + pub span: Span, + #[note("type defined here")] + pub def_span: Option, + #[suggestion( + "add `#[pin_v2]` here", + code = "#[pin_v2]\n", + applicability = "machine-applicable" + )] + pub sugg_span: Option, +} + #[derive(Diagnostic)] #[diag("falling back to `f32` as the trait bound `f32: From` is not satisfied")] pub(crate) struct FloatLiteralF32Fallback { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 084079561a6d3..890c0fdde085e 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -499,6 +499,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ty::new_ptr(self.tcx, ty, mutbl) } hir::BorrowKind::Ref | hir::BorrowKind::Pin => { + if kind == hir::BorrowKind::Pin { + self.check_pin_borrow_of_adt_requires_pin_v2(ty, expr.span); + } + // Note: at this point, we cannot say what the best lifetime // is to use for resulting pointer. We want to use the // shortest lifetime possible so as to avoid spurious borrowck @@ -523,6 +527,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn check_pin_borrow_of_adt_requires_pin_v2(&self, ty: Ty<'tcx>, span: Span) { + let ty = self.structurally_resolve_type(span, ty); + if let Some(adt) = ty.ty_adt_def() + && !adt.is_pin_project() + { + let def_span = self.tcx.hir_span_if_local(adt.did()); + let sugg_span = def_span.map(|span| span.shrink_to_lo()); + self.dcx().emit_err(crate::errors::DirectPinBorrowOfNonPinProjectType { + span, + def_span, + sugg_span, + }); + } + } + /// Does this expression refer to a place that either: /// * Is based on a local or static. /// * Contains a dereference diff --git a/tests/ui/pin-ergonomics/borrow-pinned-projection.rs b/tests/ui/pin-ergonomics/borrow-pinned-projection.rs index 648d001830efa..228707234e189 100644 --- a/tests/ui/pin-ergonomics/borrow-pinned-projection.rs +++ b/tests/ui/pin-ergonomics/borrow-pinned-projection.rs @@ -5,6 +5,7 @@ // unrelated disjoint fields, but it must reject later mutable borrows or moves // of `pair.0` until reassignment. +#[pin_v2] struct Foo; fn mutable_borrow_of_pinned_projection() { diff --git a/tests/ui/pin-ergonomics/borrow-pinned-projection.stderr b/tests/ui/pin-ergonomics/borrow-pinned-projection.stderr index 93028eb651dfd..4b20519f84774 100644 --- a/tests/ui/pin-ergonomics/borrow-pinned-projection.stderr +++ b/tests/ui/pin-ergonomics/borrow-pinned-projection.stderr @@ -1,5 +1,5 @@ error: cannot borrow `pair.0` as mutable because it is pinned - --> $DIR/borrow-pinned-projection.rs:18:19 + --> $DIR/borrow-pinned-projection.rs:19:19 | LL | let _pin = &pin mut pair.0; | --------------- pin of `pair.0` occurs here @@ -8,7 +8,7 @@ LL | let _borrow = &mut pair.0; | ^^^^^^^^^^^ borrow of `pair.0` as mutable occurs here error: cannot move out of `pair.0` because it is pinned - --> $DIR/borrow-pinned-projection.rs:30:18 + --> $DIR/borrow-pinned-projection.rs:31:18 | LL | let _pin = &pin mut pair.0; | --------------- pin of `pair.0` occurs here diff --git a/tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr b/tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr index b309dace105f4..89bae9b4cf802 100644 --- a/tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr +++ b/tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr @@ -1,5 +1,5 @@ error: cannot move out of `foo` because it is pinned - --> $DIR/borrow-unpin.rs:28:14 + --> $DIR/borrow-unpin.rs:30:14 | LL | foo_pin_mut(&pin mut foo); // ok | ------------ pin of `foo` occurs here @@ -7,7 +7,7 @@ LL | foo_move(foo); | ^^^ move out of `foo` occurs here error[E0505]: cannot move out of `foo` because it is borrowed - --> $DIR/borrow-unpin.rs:32:14 + --> $DIR/borrow-unpin.rs:34:14 | LL | let mut foo = Foo::default(); | ------- binding `foo` declared here @@ -19,7 +19,7 @@ LL | foo_pin_mut(x); // ok | - borrow later used here | note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:13:1 + --> $DIR/borrow-unpin.rs:14:1 | LL | struct Foo(PhantomPinned); | ^^^^^^^^^^ consider implementing `Clone` for this type @@ -28,7 +28,7 @@ LL | let x = &pin mut foo; | --- you could clone this value error: cannot move out of `foo` because it is pinned - --> $DIR/borrow-unpin.rs:39:14 + --> $DIR/borrow-unpin.rs:41:14 | LL | foo_pin_mut(&pin mut foo); // ok | ------------ pin of `foo` occurs here @@ -36,7 +36,7 @@ LL | foo_move(foo); | ^^^ move out of `foo` occurs here error[E0505]: cannot move out of `foo` because it is borrowed - --> $DIR/borrow-unpin.rs:43:14 + --> $DIR/borrow-unpin.rs:45:14 | LL | let mut foo = Foo::default(); | ------- binding `foo` declared here @@ -48,7 +48,7 @@ LL | foo_pin_mut(x); // ok | - borrow later used here | note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:13:1 + --> $DIR/borrow-unpin.rs:14:1 | LL | struct Foo(PhantomPinned); | ^^^^^^^^^^ consider implementing `Clone` for this type @@ -57,7 +57,7 @@ LL | let x = &pin mut foo; // ok | --- you could clone this value error: cannot move out of `foo` because it is pinned - --> $DIR/borrow-unpin.rs:50:14 + --> $DIR/borrow-unpin.rs:52:14 | LL | foo_pin_ref(&pin const foo); // ok | -------------- pin of `foo` occurs here @@ -65,7 +65,7 @@ LL | foo_move(foo); | ^^^ move out of `foo` occurs here error[E0505]: cannot move out of `foo` because it is borrowed - --> $DIR/borrow-unpin.rs:54:14 + --> $DIR/borrow-unpin.rs:56:14 | LL | let foo = Foo::default(); | --- binding `foo` declared here @@ -77,7 +77,7 @@ LL | foo_pin_ref(x); | - borrow later used here | note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:13:1 + --> $DIR/borrow-unpin.rs:14:1 | LL | struct Foo(PhantomPinned); | ^^^^^^^^^^ consider implementing `Clone` for this type @@ -86,7 +86,7 @@ LL | let x = &pin const foo; // ok | --- you could clone this value error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable - --> $DIR/borrow-unpin.rs:65:13 + --> $DIR/borrow-unpin.rs:67:13 | LL | let x = &pin mut foo; // ok | ------------ mutable borrow occurs here @@ -96,7 +96,7 @@ LL | foo_pin_mut(x); | - mutable borrow later used here error[E0499]: cannot borrow `foo` as mutable more than once at a time - --> $DIR/borrow-unpin.rs:87:17 + --> $DIR/borrow-unpin.rs:89:17 | LL | let x = &pin mut foo; // ok | ------------ first mutable borrow occurs here @@ -106,7 +106,7 @@ LL | foo_pin_mut(x); | - first borrow later used here error[E0502]: cannot borrow `foo` as mutable because it is also borrowed as immutable - --> $DIR/borrow-unpin.rs:98:17 + --> $DIR/borrow-unpin.rs:100:17 | LL | let x = &pin const foo; // ok | -------------- immutable borrow occurs here @@ -116,7 +116,7 @@ LL | foo_pin_ref(x); | - immutable borrow later used here error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable - --> $DIR/borrow-unpin.rs:109:17 + --> $DIR/borrow-unpin.rs:111:17 | LL | let x = &pin mut foo; // ok | ------------ mutable borrow occurs here diff --git a/tests/ui/pin-ergonomics/borrow-unpin.rs b/tests/ui/pin-ergonomics/borrow-unpin.rs index caab5c0f49369..23c5b372c687d 100644 --- a/tests/ui/pin-ergonomics/borrow-unpin.rs +++ b/tests/ui/pin-ergonomics/borrow-unpin.rs @@ -9,10 +9,12 @@ use std::marker::PhantomPinned; use std::pin::Pin; #[cfg(pinned)] +#[pin_v2] #[derive(Default)] struct Foo(PhantomPinned); #[cfg(unpin)] +#[pin_v2] #[derive(Default)] struct Foo; diff --git a/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr b/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr index b338426d7b0b5..1ff363e004ffc 100644 --- a/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr +++ b/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr @@ -1,5 +1,5 @@ error: cannot move out of `foo` because it is pinned - --> $DIR/borrow-unpin.rs:28:14 + --> $DIR/borrow-unpin.rs:30:14 | LL | foo_pin_mut(&pin mut foo); // ok | ------------ pin of `foo` occurs here @@ -7,7 +7,7 @@ LL | foo_move(foo); | ^^^ move out of `foo` occurs here error[E0505]: cannot move out of `foo` because it is borrowed - --> $DIR/borrow-unpin.rs:32:14 + --> $DIR/borrow-unpin.rs:34:14 | LL | let mut foo = Foo::default(); | ------- binding `foo` declared here @@ -19,7 +19,7 @@ LL | foo_pin_mut(x); // ok | - borrow later used here | note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:17:1 + --> $DIR/borrow-unpin.rs:19:1 | LL | struct Foo; | ^^^^^^^^^^ consider implementing `Clone` for this type @@ -28,7 +28,7 @@ LL | let x = &pin mut foo; | --- you could clone this value error: cannot move out of `foo` because it is pinned - --> $DIR/borrow-unpin.rs:39:14 + --> $DIR/borrow-unpin.rs:41:14 | LL | foo_pin_mut(&pin mut foo); // ok | ------------ pin of `foo` occurs here @@ -36,7 +36,7 @@ LL | foo_move(foo); | ^^^ move out of `foo` occurs here error[E0505]: cannot move out of `foo` because it is borrowed - --> $DIR/borrow-unpin.rs:43:14 + --> $DIR/borrow-unpin.rs:45:14 | LL | let mut foo = Foo::default(); | ------- binding `foo` declared here @@ -48,7 +48,7 @@ LL | foo_pin_mut(x); // ok | - borrow later used here | note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:17:1 + --> $DIR/borrow-unpin.rs:19:1 | LL | struct Foo; | ^^^^^^^^^^ consider implementing `Clone` for this type @@ -57,7 +57,7 @@ LL | let x = &pin mut foo; // ok | --- you could clone this value error: cannot move out of `foo` because it is pinned - --> $DIR/borrow-unpin.rs:50:14 + --> $DIR/borrow-unpin.rs:52:14 | LL | foo_pin_ref(&pin const foo); // ok | -------------- pin of `foo` occurs here @@ -65,7 +65,7 @@ LL | foo_move(foo); | ^^^ move out of `foo` occurs here error[E0505]: cannot move out of `foo` because it is borrowed - --> $DIR/borrow-unpin.rs:54:14 + --> $DIR/borrow-unpin.rs:56:14 | LL | let foo = Foo::default(); | --- binding `foo` declared here @@ -77,7 +77,7 @@ LL | foo_pin_ref(x); | - borrow later used here | note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/borrow-unpin.rs:17:1 + --> $DIR/borrow-unpin.rs:19:1 | LL | struct Foo; | ^^^^^^^^^^ consider implementing `Clone` for this type @@ -86,7 +86,7 @@ LL | let x = &pin const foo; // ok | --- you could clone this value error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable - --> $DIR/borrow-unpin.rs:65:13 + --> $DIR/borrow-unpin.rs:67:13 | LL | let x = &pin mut foo; // ok | ------------ mutable borrow occurs here @@ -96,7 +96,7 @@ LL | foo_pin_mut(x); | - mutable borrow later used here error[E0499]: cannot borrow `foo` as mutable more than once at a time - --> $DIR/borrow-unpin.rs:87:17 + --> $DIR/borrow-unpin.rs:89:17 | LL | let x = &pin mut foo; // ok | ------------ first mutable borrow occurs here @@ -106,7 +106,7 @@ LL | foo_pin_mut(x); | - first borrow later used here error[E0502]: cannot borrow `foo` as mutable because it is also borrowed as immutable - --> $DIR/borrow-unpin.rs:98:17 + --> $DIR/borrow-unpin.rs:100:17 | LL | let x = &pin const foo; // ok | -------------- immutable borrow occurs here @@ -116,7 +116,7 @@ LL | foo_pin_ref(x); | - immutable borrow later used here error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable - --> $DIR/borrow-unpin.rs:109:17 + --> $DIR/borrow-unpin.rs:111:17 | LL | let x = &pin mut foo; // ok | ------------ mutable borrow occurs here diff --git a/tests/ui/pin-ergonomics/borrow.rs b/tests/ui/pin-ergonomics/borrow.rs index 214832340b2d3..be06d83c5270e 100644 --- a/tests/ui/pin-ergonomics/borrow.rs +++ b/tests/ui/pin-ergonomics/borrow.rs @@ -6,6 +6,7 @@ use std::pin::Pin; +#[pin_v2] struct Foo; fn foo_pin_mut(_: Pin<&mut Foo>) {} diff --git a/tests/ui/pin-ergonomics/borrow.stderr b/tests/ui/pin-ergonomics/borrow.stderr index 75376eff1d35a..9ff7e4e2a73d4 100644 --- a/tests/ui/pin-ergonomics/borrow.stderr +++ b/tests/ui/pin-ergonomics/borrow.stderr @@ -1,5 +1,5 @@ error: cannot borrow `x` as mutable because it is pinned - --> $DIR/borrow.rs:31:14 + --> $DIR/borrow.rs:32:14 | LL | let _x = &pin mut x; | ---------- pin of `x` occurs here @@ -8,7 +8,7 @@ LL | let _x = &mut x; | ^^^^^^ borrow of `x` as mutable occurs here error: cannot move out of `x` because it is pinned - --> $DIR/borrow.rs:32:14 + --> $DIR/borrow.rs:33:14 | LL | let _x = &pin mut x; | ---------- pin of `x` occurs here @@ -17,7 +17,7 @@ LL | let _x = x; | ^ move out of `x` occurs here error: cannot borrow `y` as mutable because it is pinned - --> $DIR/borrow.rs:40:14 + --> $DIR/borrow.rs:41:14 | LL | let _y = &pin const y; | ------------ pin of `y` occurs here @@ -26,7 +26,7 @@ LL | let _y = &mut y; | ^^^^^^ borrow of `y` as mutable occurs here error: cannot move out of `y` because it is pinned - --> $DIR/borrow.rs:41:14 + --> $DIR/borrow.rs:42:14 | LL | let _y = &pin const y; | ------------ pin of `y` occurs here diff --git a/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.rs b/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.rs new file mode 100644 index 0000000000000..4d5ab1630ea9a --- /dev/null +++ b/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.rs @@ -0,0 +1,17 @@ +#![feature(pin_ergonomics)] +#![allow(incomplete_features)] +//@ normalize-stderr: "\n\n\z" -> "\n" + +struct NotPinV2; + +fn direct_pin_mut(mut value: NotPinV2) { + let _ = &pin mut value; + //~^ ERROR cannot directly pin an ADT that is not `#[pin_v2]` +} + +fn direct_pin_const(value: NotPinV2) { + let _ = &pin const value; + //~^ ERROR cannot directly pin an ADT that is not `#[pin_v2]` +} + +fn main() {} diff --git a/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.stderr b/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.stderr new file mode 100644 index 0000000000000..1e82a18f4a12c --- /dev/null +++ b/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.stderr @@ -0,0 +1,35 @@ +error: cannot directly pin an ADT that is not `#[pin_v2]` + --> $DIR/direct-borrow-requires-pin-v2.rs:8:13 + | +LL | let _ = &pin mut value; + | ^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/direct-borrow-requires-pin-v2.rs:5:1 + | +LL | struct NotPinV2; + | ^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct NotPinV2; + | + +error: cannot directly pin an ADT that is not `#[pin_v2]` + --> $DIR/direct-borrow-requires-pin-v2.rs:13:13 + | +LL | let _ = &pin const value; + | ^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/direct-borrow-requires-pin-v2.rs:5:1 + | +LL | struct NotPinV2; + | ^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct NotPinV2; + | + +error: aborting due to 2 previous errors diff --git a/tests/ui/pin-ergonomics/user-type-projection.rs b/tests/ui/pin-ergonomics/user-type-projection.rs index f482586b6ebcc..4e8ef9887425e 100644 --- a/tests/ui/pin-ergonomics/user-type-projection.rs +++ b/tests/ui/pin-ergonomics/user-type-projection.rs @@ -9,6 +9,7 @@ // Historically, this could occur when the code handling those projections did not know // about `&pin` patterns, and incorrectly treated them as plain `&`/`&mut` patterns instead. +#[pin_v2] struct Data { x: u32 } From edeac5e00458704ac6b13d71a871066894030bbb Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Thu, 30 Apr 2026 18:29:49 +0200 Subject: [PATCH 4/8] Gate direct pin-borrow ADT diagnostic on !Unpin --- compiler/rustc_hir_typeck/src/errors.rs | 2 +- compiler/rustc_hir_typeck/src/expr.rs | 27 ++++- .../direct-borrow-requires-pin-v2.rs | 78 ++++++++++++-- .../direct-borrow-requires-pin-v2.stderr | 100 +++++++++++++++--- 4 files changed, 183 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 5959f57128ae0..369d963cfdece 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -1386,7 +1386,7 @@ pub(crate) struct ProjectOnNonPinProjectType { } #[derive(Diagnostic)] -#[diag("cannot directly pin an ADT that is not `#[pin_v2]`")] +#[diag("cannot directly pin a type that is not structurally pinnable")] pub(crate) struct DirectPinBorrowOfNonPinProjectType { #[primary_span] pub span: Span, diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 890c0fdde085e..5f370cf68d6b1 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -529,10 +529,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pin_borrow_of_adt_requires_pin_v2(&self, ty: Ty<'tcx>, span: Span) { let ty = self.structurally_resolve_type(span, ty); - if let Some(adt) = ty.ty_adt_def() - && !adt.is_pin_project() - { - let def_span = self.tcx.hir_span_if_local(adt.did()); + if self.direct_pin_borrow_requires_pin_v2(ty, span) { + let def_span = ty.ty_adt_def().and_then(|adt| self.tcx.hir_span_if_local(adt.did())); let sugg_span = def_span.map(|span| span.shrink_to_lo()); self.dcx().emit_err(crate::errors::DirectPinBorrowOfNonPinProjectType { span, @@ -542,6 +540,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn direct_pin_borrow_requires_pin_v2(&self, ty: Ty<'tcx>, span: Span) -> bool { + if ty.references_error() { + return false; + } + + // Manual `Unpin` impls do not prove that pinned projections through a generic ADT + // are safe, so keep those cases aligned with pinned-pattern projection checks. + match ty.kind() { + ty::Adt(adt, args) if adt.is_pin_project() || adt.is_pin() => false, + ty::Adt(_, args) if args.types().next().is_some() => true, + ty::Adt(..) => !self.type_known_to_be_unpin(ty, span), + ty::Param(..) => !self.type_known_to_be_unpin(ty, span), + _ => ty.has_param() || ty.has_infer() || ty.has_aliases() || ty.has_placeholders(), + } + } + + fn type_known_to_be_unpin(&self, ty: Ty<'tcx>, span: Span) -> bool { + let unpin_def_id = self.tcx.require_lang_item(LangItem::Unpin, span); + self.type_implements_trait(unpin_def_id, [ty], self.param_env).must_apply_modulo_regions() + } + /// Does this expression refer to a place that either: /// * Is based on a local or static. /// * Contains a dereference diff --git a/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.rs b/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.rs index 4d5ab1630ea9a..53ebd236c357c 100644 --- a/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.rs +++ b/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.rs @@ -1,17 +1,83 @@ #![feature(pin_ergonomics)] -#![allow(incomplete_features)] +#![allow(dead_code, incomplete_features)] //@ normalize-stderr: "\n\n\z" -> "\n" -struct NotPinV2; +use std::marker::PhantomPinned; +use std::pin::Pin; -fn direct_pin_mut(mut value: NotPinV2) { +struct UnpinAdt; + +struct NotUnpin { + _pin: PhantomPinned, +} + +#[pin_v2] +struct PinV2NotUnpin { + _pin: PhantomPinned, +} + +struct GenericAdt { + x: T, +} + +impl Unpin for GenericAdt {} + +struct DropGenericAdt(T); + +impl Drop for DropGenericAdt { + fn drop(&mut self) {} +} + +fn direct_pin_mut_unpin() { + let _: Pin<&mut _> = &pin mut UnpinAdt; +} + +fn direct_pin_const_unpin() { + let _: Pin<&_> = &pin const UnpinAdt; +} + +fn direct_pin_mut_not_unpin() { + let _ = &pin mut NotUnpin { _pin: PhantomPinned }; + //~^ ERROR cannot directly pin a type that is not structurally pinnable +} + +fn direct_pin_const_not_unpin() { + let _ = &pin const NotUnpin { _pin: PhantomPinned }; + //~^ ERROR cannot directly pin a type that is not structurally pinnable +} + +fn direct_pin_mut_pin_v2_not_unpin() { + let _: Pin<&mut _> = &pin mut PinV2NotUnpin { _pin: PhantomPinned }; +} + +fn direct_pin_const_pin_v2_not_unpin() { + let _: Pin<&_> = &pin const PinV2NotUnpin { _pin: PhantomPinned }; +} + +fn direct_pin_mut_generic(mut value: GenericAdt) { let _ = &pin mut value; - //~^ ERROR cannot directly pin an ADT that is not `#[pin_v2]` + //~^ ERROR cannot directly pin a type that is not structurally pinnable } -fn direct_pin_const(value: NotPinV2) { +fn direct_pin_const_generic(value: GenericAdt) { let _ = &pin const value; - //~^ ERROR cannot directly pin an ADT that is not `#[pin_v2]` + //~^ ERROR cannot directly pin a type that is not structurally pinnable +} + +pub fn unsound_pin_borrow(input: &pin mut GenericAdt) -> &pin mut T { + &pin mut input.x + //~^ ERROR cannot directly pin a type that is not structurally pinnable +} + +fn direct_pin_mut_manual_unpin_generic() { + let mut value = GenericAdt { x: NotUnpin { _pin: PhantomPinned } }; + let _ = &pin mut value; + //~^ ERROR cannot directly pin a type that is not structurally pinnable +} + +fn direct_pin_mut_drop_generic(mut input: DropGenericAdt) { + let _: &pin mut _ = &pin mut input; + //~^ ERROR cannot directly pin a type that is not structurally pinnable } fn main() {} diff --git a/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.stderr b/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.stderr index 1e82a18f4a12c..342e5be183b87 100644 --- a/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.stderr +++ b/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.stderr @@ -1,35 +1,109 @@ -error: cannot directly pin an ADT that is not `#[pin_v2]` - --> $DIR/direct-borrow-requires-pin-v2.rs:8:13 +error: cannot directly pin a type that is not structurally pinnable + --> $DIR/direct-borrow-requires-pin-v2.rs:40:13 + | +LL | let _ = &pin mut NotUnpin { _pin: PhantomPinned }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/direct-borrow-requires-pin-v2.rs:10:1 + | +LL | struct NotUnpin { + | ^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct NotUnpin { + | + +error: cannot directly pin a type that is not structurally pinnable + --> $DIR/direct-borrow-requires-pin-v2.rs:45:13 + | +LL | let _ = &pin const NotUnpin { _pin: PhantomPinned }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/direct-borrow-requires-pin-v2.rs:10:1 + | +LL | struct NotUnpin { + | ^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct NotUnpin { + | + +error: cannot directly pin a type that is not structurally pinnable + --> $DIR/direct-borrow-requires-pin-v2.rs:58:13 | LL | let _ = &pin mut value; | ^^^^^^^^^^^^^^ | note: type defined here - --> $DIR/direct-borrow-requires-pin-v2.rs:5:1 + --> $DIR/direct-borrow-requires-pin-v2.rs:19:1 | -LL | struct NotPinV2; - | ^^^^^^^^^^^^^^^ +LL | struct GenericAdt { + | ^^^^^^^^^^^^^^^^^^^^ help: add `#[pin_v2]` here | LL + #[pin_v2] -LL | struct NotPinV2; +LL | struct GenericAdt { | -error: cannot directly pin an ADT that is not `#[pin_v2]` - --> $DIR/direct-borrow-requires-pin-v2.rs:13:13 +error: cannot directly pin a type that is not structurally pinnable + --> $DIR/direct-borrow-requires-pin-v2.rs:63:13 | LL | let _ = &pin const value; | ^^^^^^^^^^^^^^^^ | note: type defined here - --> $DIR/direct-borrow-requires-pin-v2.rs:5:1 + --> $DIR/direct-borrow-requires-pin-v2.rs:19:1 | -LL | struct NotPinV2; - | ^^^^^^^^^^^^^^^ +LL | struct GenericAdt { + | ^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct GenericAdt { + | + +error: cannot directly pin a type that is not structurally pinnable + --> $DIR/direct-borrow-requires-pin-v2.rs:68:5 + | +LL | &pin mut input.x + | ^^^^^^^^^^^^^^^^ + +error: cannot directly pin a type that is not structurally pinnable + --> $DIR/direct-borrow-requires-pin-v2.rs:74:13 + | +LL | let _ = &pin mut value; + | ^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/direct-borrow-requires-pin-v2.rs:19:1 + | +LL | struct GenericAdt { + | ^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct GenericAdt { + | + +error: cannot directly pin a type that is not structurally pinnable + --> $DIR/direct-borrow-requires-pin-v2.rs:79:25 + | +LL | let _: &pin mut _ = &pin mut input; + | ^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/direct-borrow-requires-pin-v2.rs:25:1 + | +LL | struct DropGenericAdt(T); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add `#[pin_v2]` here | LL + #[pin_v2] -LL | struct NotPinV2; +LL | struct DropGenericAdt(T); | -error: aborting due to 2 previous errors +error: aborting due to 7 previous errors From 7843ffde0bfbd701a07a7451bf9ed4624a237539 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Sat, 2 May 2026 13:19:07 +0200 Subject: [PATCH 5/8] Skip long-lived pin facts for Unpin places --- compiler/rustc_borrowck/src/dataflow.rs | 5 ++- compiler/rustc_borrowck/src/lib.rs | 10 ------ .../borrow-pinned-projection.rs | 31 +++++++++++++++++-- .../borrow-pinned-projection.stderr | 24 ++++++++++++-- tests/ui/pin-ergonomics/borrow-unpin.rs | 10 +++--- .../pin-ergonomics/borrow-unpin.unpin.stderr | 26 +--------------- tests/ui/pin-ergonomics/borrow.rs | 10 +++--- tests/ui/pin-ergonomics/borrow.stderr | 8 ++--- .../direct-borrow-requires-pin-v2.rs | 24 ++++++++++++++ 9 files changed, 93 insertions(+), 55 deletions(-) diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index c1e402391c846..360410fe29e46 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -761,7 +761,10 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Pins<'_, 'tcx> { if let mir::Rvalue::Ref(_, mir::BorrowKind::Pinned(_, kind), place) = rhs && matches!(*kind, mir::PinBorrowKind::Persistent) { - self.gen_pins_on_place(state, *place); + let place_ty = place.ty(self.body, self.tcx).ty; + if !place_ty.is_unpin(self.tcx, self.body.typing_env(self.tcx)) { + self.gen_pins_on_place(state, *place); + } } } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index efb24e31511fe..ea121103c2b83 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1270,17 +1270,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { &self, state: &'s BorrowckDomain, ) -> Option<&'s MixedBitSet> { - // FIXME(pin_ergonomics): borrowck behaviors depend on a safe trait - // which should not contain any safety invariants. - // if place - // .ty(self.body, self.infcx.tcx) - // .ty - // .is_unpin(self.infcx.tcx, self.body.typing_env(self.infcx.tcx)) - // { - // None - // } else { Some(&state.pinned_borrows) - // } } #[instrument(level = "debug", skip(self, state))] diff --git a/tests/ui/pin-ergonomics/borrow-pinned-projection.rs b/tests/ui/pin-ergonomics/borrow-pinned-projection.rs index 228707234e189..d0c891f8a7772 100644 --- a/tests/ui/pin-ergonomics/borrow-pinned-projection.rs +++ b/tests/ui/pin-ergonomics/borrow-pinned-projection.rs @@ -6,10 +6,11 @@ // of `pair.0` until reassignment. #[pin_v2] -struct Foo; +#[derive(Default)] +struct Foo(std::marker::PhantomPinned); fn mutable_borrow_of_pinned_projection() { - let mut pair = (Foo, Foo); + let mut pair = (Foo::default(), Foo::default()); { let _pin = &pin mut pair.0; @@ -21,7 +22,7 @@ fn mutable_borrow_of_pinned_projection() { } fn move_of_pinned_projection() { - let mut pair = (Foo, Foo); + let mut pair = (Foo::default(), Foo::default()); { let _pin = &pin mut pair.0; @@ -32,4 +33,28 @@ fn move_of_pinned_projection() { //~^ ERROR cannot move out of `pair.0` because it is pinned } +#[pin_v2] +struct ContainsUnpinField { + field: String, + _pin: std::marker::PhantomPinned, +} + +fn pinned_parent_still_blocks_unpin_field_move(mut value: ContainsUnpinField) { + { + let _ = &pin mut value; + } + + let _moved = value.field; + //~^ ERROR cannot move out of `value.field` because it is pinned +} + +fn pinned_parent_still_blocks_unpin_field_mut_borrow(mut value: ContainsUnpinField) { + { + let _ = &pin mut value; + } + + let _ = &mut value.field; + //~^ ERROR cannot borrow `value.field` as mutable because it is pinned +} + fn main() {} diff --git a/tests/ui/pin-ergonomics/borrow-pinned-projection.stderr b/tests/ui/pin-ergonomics/borrow-pinned-projection.stderr index 4b20519f84774..416ad776c96d5 100644 --- a/tests/ui/pin-ergonomics/borrow-pinned-projection.stderr +++ b/tests/ui/pin-ergonomics/borrow-pinned-projection.stderr @@ -1,5 +1,5 @@ error: cannot borrow `pair.0` as mutable because it is pinned - --> $DIR/borrow-pinned-projection.rs:19:19 + --> $DIR/borrow-pinned-projection.rs:20:19 | LL | let _pin = &pin mut pair.0; | --------------- pin of `pair.0` occurs here @@ -8,7 +8,7 @@ LL | let _borrow = &mut pair.0; | ^^^^^^^^^^^ borrow of `pair.0` as mutable occurs here error: cannot move out of `pair.0` because it is pinned - --> $DIR/borrow-pinned-projection.rs:31:18 + --> $DIR/borrow-pinned-projection.rs:32:18 | LL | let _pin = &pin mut pair.0; | --------------- pin of `pair.0` occurs here @@ -16,5 +16,23 @@ LL | let _pin = &pin mut pair.0; LL | let _moved = pair.0; | ^^^^^^ move out of `pair.0` occurs here -error: aborting due to 2 previous errors +error: cannot move out of `value.field` because it is pinned + --> $DIR/borrow-pinned-projection.rs:47:18 + | +LL | let _ = &pin mut value; + | -------------- pin of `value` occurs here +... +LL | let _moved = value.field; + | ^^^^^^^^^^^ move out of `value.field` occurs here + +error: cannot borrow `value.field` as mutable because it is pinned + --> $DIR/borrow-pinned-projection.rs:56:13 + | +LL | let _ = &pin mut value; + | -------------- pin of `value` occurs here +... +LL | let _ = &mut value.field; + | ^^^^^^^^^^^^^^^^ borrow of `value.field` as mutable occurs here + +error: aborting due to 4 previous errors diff --git a/tests/ui/pin-ergonomics/borrow-unpin.rs b/tests/ui/pin-ergonomics/borrow-unpin.rs index 23c5b372c687d..ac66c4353e161 100644 --- a/tests/ui/pin-ergonomics/borrow-unpin.rs +++ b/tests/ui/pin-ergonomics/borrow-unpin.rs @@ -2,8 +2,8 @@ #![feature(pin_ergonomics)] #![allow(dead_code, incomplete_features)] -// This test ensures that the place cannot be mutably borrowed or moved after pinned -// no matter if `place` is `Unpin` or not. +// This test ensures `!Unpin` places cannot be mutably borrowed or moved after pinning. +// `Unpin` places still obey ordinary borrow rules, but expired `&pin` borrows do not pin. use std::marker::PhantomPinned; use std::pin::Pin; @@ -27,7 +27,7 @@ fn foo_move(_: Foo) {} fn immutable_pin_mut_then_move() { let mut foo = Foo::default(); foo_pin_mut(&pin mut foo); // ok - foo_move(foo); //~ ERROR cannot move out of `foo` because it is pinned + foo_move(foo); //[pinned]~ ERROR cannot move out of `foo` because it is pinned let mut foo = Foo::default(); let x = &pin mut foo; @@ -38,7 +38,7 @@ fn immutable_pin_mut_then_move() { fn pin_mut_then_move() { let mut foo = Foo::default(); foo_pin_mut(&pin mut foo); // ok - foo_move(foo); //~ ERROR cannot move out of `foo` because it is pinned + foo_move(foo); //[pinned]~ ERROR cannot move out of `foo` because it is pinned let mut foo = Foo::default(); let x = &pin mut foo; // ok @@ -49,7 +49,7 @@ fn pin_mut_then_move() { fn pin_ref_then_move() { let foo = Foo::default(); foo_pin_ref(&pin const foo); // ok - foo_move(foo); //~ ERROR cannot move out of `foo` because it is pinned + foo_move(foo); //[pinned]~ ERROR cannot move out of `foo` because it is pinned let foo = Foo::default(); let x = &pin const foo; // ok diff --git a/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr b/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr index 1ff363e004ffc..f8fb6dc75fc98 100644 --- a/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr +++ b/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr @@ -1,11 +1,3 @@ -error: cannot move out of `foo` because it is pinned - --> $DIR/borrow-unpin.rs:30:14 - | -LL | foo_pin_mut(&pin mut foo); // ok - | ------------ pin of `foo` occurs here -LL | foo_move(foo); - | ^^^ move out of `foo` occurs here - error[E0505]: cannot move out of `foo` because it is borrowed --> $DIR/borrow-unpin.rs:34:14 | @@ -27,14 +19,6 @@ LL | struct Foo; LL | let x = &pin mut foo; | --- you could clone this value -error: cannot move out of `foo` because it is pinned - --> $DIR/borrow-unpin.rs:41:14 - | -LL | foo_pin_mut(&pin mut foo); // ok - | ------------ pin of `foo` occurs here -LL | foo_move(foo); - | ^^^ move out of `foo` occurs here - error[E0505]: cannot move out of `foo` because it is borrowed --> $DIR/borrow-unpin.rs:45:14 | @@ -56,14 +40,6 @@ LL | struct Foo; LL | let x = &pin mut foo; // ok | --- you could clone this value -error: cannot move out of `foo` because it is pinned - --> $DIR/borrow-unpin.rs:52:14 - | -LL | foo_pin_ref(&pin const foo); // ok - | -------------- pin of `foo` occurs here -LL | foo_move(foo); - | ^^^ move out of `foo` occurs here - error[E0505]: cannot move out of `foo` because it is borrowed --> $DIR/borrow-unpin.rs:56:14 | @@ -125,7 +101,7 @@ LL | foo_pin_ref(&pin const foo); LL | foo_pin_mut(x); | - mutable borrow later used here -error: aborting due to 10 previous errors +error: aborting due to 7 previous errors Some errors have detailed explanations: E0499, E0502, E0505. For more information about an error, try `rustc --explain E0499`. diff --git a/tests/ui/pin-ergonomics/borrow.rs b/tests/ui/pin-ergonomics/borrow.rs index be06d83c5270e..ba4a912d8c5cf 100644 --- a/tests/ui/pin-ergonomics/borrow.rs +++ b/tests/ui/pin-ergonomics/borrow.rs @@ -4,22 +4,24 @@ // Makes sure we can handle `&pin mut place` and `&pin const place` as sugar for // `std::pin::pin!(place)` and `Pin::new(&place)`. +use std::marker::PhantomPinned; use std::pin::Pin; #[pin_v2] -struct Foo; +#[derive(Default)] +struct Foo(PhantomPinned); fn foo_pin_mut(_: Pin<&mut Foo>) {} fn foo_pin_ref(_: Pin<&Foo>) {} fn bar() { - let mut x: Pin<&mut _> = &pin mut Foo; + let mut x: Pin<&mut _> = &pin mut Foo::default(); foo_pin_mut(x.as_mut()); foo_pin_mut(x.as_mut()); foo_pin_ref(x); - let x: Pin<&_> = &pin const Foo; + let x: Pin<&_> = &pin const Foo::default(); foo_pin_ref(x); foo_pin_ref(x); @@ -32,7 +34,7 @@ fn baz(mut x: Foo, mut y: Foo) { let _x = &mut x; //~ ERROR cannot borrow `x` as mutable because it is pinned let _x = x; //~ ERROR cannot move out of `x` because it is pinned - x = Foo; + x = Foo::default(); let _x = &mut x; // ok { diff --git a/tests/ui/pin-ergonomics/borrow.stderr b/tests/ui/pin-ergonomics/borrow.stderr index 9ff7e4e2a73d4..3161df5fd6c60 100644 --- a/tests/ui/pin-ergonomics/borrow.stderr +++ b/tests/ui/pin-ergonomics/borrow.stderr @@ -1,5 +1,5 @@ error: cannot borrow `x` as mutable because it is pinned - --> $DIR/borrow.rs:32:14 + --> $DIR/borrow.rs:34:14 | LL | let _x = &pin mut x; | ---------- pin of `x` occurs here @@ -8,7 +8,7 @@ LL | let _x = &mut x; | ^^^^^^ borrow of `x` as mutable occurs here error: cannot move out of `x` because it is pinned - --> $DIR/borrow.rs:33:14 + --> $DIR/borrow.rs:35:14 | LL | let _x = &pin mut x; | ---------- pin of `x` occurs here @@ -17,7 +17,7 @@ LL | let _x = x; | ^ move out of `x` occurs here error: cannot borrow `y` as mutable because it is pinned - --> $DIR/borrow.rs:41:14 + --> $DIR/borrow.rs:43:14 | LL | let _y = &pin const y; | ------------ pin of `y` occurs here @@ -26,7 +26,7 @@ LL | let _y = &mut y; | ^^^^^^ borrow of `y` as mutable occurs here error: cannot move out of `y` because it is pinned - --> $DIR/borrow.rs:42:14 + --> $DIR/borrow.rs:44:14 | LL | let _y = &pin const y; | ------------ pin of `y` occurs here diff --git a/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.rs b/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.rs index 53ebd236c357c..cddaeca50387c 100644 --- a/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.rs +++ b/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.rs @@ -80,4 +80,28 @@ fn direct_pin_mut_drop_generic(mut input: DropGenericAdt) { //~^ ERROR cannot directly pin a type that is not structurally pinnable } +fn direct_pin_mut_unpin_then_mut_borrow_and_move() { + let mut value = UnpinAdt; + { + let _ = &pin mut value; + } + let _ = &mut value; + let _ = value; +} + +fn direct_pin_const_unpin_then_move() { + let value = UnpinAdt; + { + let _ = &pin const value; + } + let _ = value; +} + +fn direct_pin_mut_generic_unpin_bound_then_move(mut value: T) { + { + let _ = &pin mut value; + } + let _ = value; +} + fn main() {} From be61203a93191106a63529bee2b2d06c67d52191 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Sun, 3 May 2026 15:33:47 +0200 Subject: [PATCH 6/8] Inline pinned_borrows access Remove the now-unnecessary helper method and access state.pinned_borrows directly. --- compiler/rustc_borrowck/src/lib.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index ea121103c2b83..07a66c1ac04e6 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1266,13 +1266,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { } } - fn pinned_borrows<'s>( - &self, - state: &'s BorrowckDomain, - ) -> Option<&'s MixedBitSet> { - Some(&state.pinned_borrows) - } - #[instrument(level = "debug", skip(self, state))] fn check_access_for_conflict( &mut self, @@ -1285,7 +1278,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { let mut error_reported = false; let borrows_in_scope = self.borrows_in_scope(location, state); - let pinned_borrows = self.pinned_borrows(state); + let pinned_borrows = &state.pinned_borrows; each_borrow_involving_path( self, @@ -1294,8 +1287,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { (sd, place_span.0), self.borrow_set, |borrow_index| { - borrows_in_scope.contains(borrow_index) - || pinned_borrows.is_some_and(|p| p.contains(borrow_index)) + borrows_in_scope.contains(borrow_index) || pinned_borrows.contains(borrow_index) }, |this, borrow_index, borrow| match (rw, borrow.kind) { // Obviously an activation is compatible with its own @@ -1378,7 +1370,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { if !borrows_in_scope.contains(borrow_index) => { debug_assert!( - pinned_borrows.is_none_or(|p| p.contains(borrow_index)), + pinned_borrows.contains(borrow_index), "unexpected expired but non-pinned borrow {borrow_index:?}: {borrow:?}", ); match kind { From a72c9870203b848f30a9a97eae82ce4762814310 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Sun, 3 May 2026 17:21:21 +0200 Subject: [PATCH 7/8] Handle ref pin patterns in borrowck --- .../src/builder/matches/mod.rs | 14 ++++-- .../src/builder/matches/test.rs | 2 +- .../src/builder/matches/util.rs | 16 ++++--- .../borrow-pinned-projection.rs | 42 ++++++++++++++++++ .../borrow-pinned-projection.stderr | 44 +++++++++++++++++-- .../pin-ergonomics/borrow-unpin.pinned.stderr | 28 +++++++++--- tests/ui/pin-ergonomics/borrow-unpin.rs | 16 +++++++ .../pin-ergonomics/borrow-unpin.unpin.stderr | 8 ++-- tests/ui/pin-ergonomics/borrow.rs | 16 +++++++ tests/ui/pin-ergonomics/borrow.stderr | 38 +++++++++++++++- 10 files changed, 200 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 5604e86e06722..bc55837b27894 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -2694,8 +2694,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ScheduleDrops::Yes, ); - let rvalue = - Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source); + let rvalue = Rvalue::Ref( + re_erased, + util::ref_pat_borrow_kind(pinnedness, mutbl), + binding.source, + ); let rvalue = match pinnedness { ty::Pinnedness::Not => rvalue, ty::Pinnedness::Pinned => { @@ -2739,8 +2742,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let rvalue = match binding.binding_mode.0 { ByRef::No => Rvalue::Use(self.consume_by_copy_or_move(binding.source)), ByRef::Yes(pinnedness, mutbl) => { - let rvalue = - Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source); + let rvalue = Rvalue::Ref( + re_erased, + util::ref_pat_borrow_kind(pinnedness, mutbl), + binding.source, + ); match pinnedness { ty::Pinnedness::Not => rvalue, ty::Pinnedness::Pinned => { diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index 9b7b6f574fe3f..37d2505c2eab9 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -339,7 +339,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Mutability::Not => (LangItem::Deref, sym::deref), Mutability::Mut => (LangItem::DerefMut, sym::deref_mut), }; - let borrow_kind = super::util::ref_pat_borrow_kind(mutability); + let borrow_kind = super::util::ref_pat_borrow_kind(ty::Pinnedness::Not, mutability); let source_info = self.source_info(span); let re_erased = self.tcx.lifetimes.re_erased; let trait_item = self.tcx.require_lang_item(trait_item, span); diff --git a/compiler/rustc_mir_build/src/builder/matches/util.rs b/compiler/rustc_mir_build/src/builder/matches/util.rs index f1df90f93fd63..c91f8b4a6f524 100644 --- a/compiler/rustc_mir_build/src/builder/matches/util.rs +++ b/compiler/rustc_mir_build/src/builder/matches/util.rs @@ -1,6 +1,6 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_middle::mir::*; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, Ty}; use rustc_span::Span; use tracing::debug; @@ -228,9 +228,15 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { } #[must_use] -pub(crate) fn ref_pat_borrow_kind(ref_mutability: Mutability) -> BorrowKind { - match ref_mutability { - Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default }, - Mutability::Not => BorrowKind::Shared, +pub(crate) fn ref_pat_borrow_kind( + pinnedness: ty::Pinnedness, + ref_mutability: Mutability, +) -> BorrowKind { + match pinnedness { + ty::Pinnedness::Pinned => BorrowKind::Pinned(ref_mutability, PinBorrowKind::Persistent), + ty::Pinnedness::Not => match ref_mutability { + Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default }, + Mutability::Not => BorrowKind::Shared, + }, } } diff --git a/tests/ui/pin-ergonomics/borrow-pinned-projection.rs b/tests/ui/pin-ergonomics/borrow-pinned-projection.rs index d0c891f8a7772..2c71aaf23a6c5 100644 --- a/tests/ui/pin-ergonomics/borrow-pinned-projection.rs +++ b/tests/ui/pin-ergonomics/borrow-pinned-projection.rs @@ -21,6 +21,18 @@ fn mutable_borrow_of_pinned_projection() { //~^ ERROR cannot borrow `pair.0` as mutable because it is pinned } +fn ref_pin_pattern_mutable_borrow_of_pinned_projection() { + let mut pair = (Foo::default(), Foo::default()); + + { + let ref pin mut _pin = pair.0; + } + + let _other = &mut pair.1; + let _borrow = &mut pair.0; + //~^ ERROR cannot borrow `pair.0` as mutable because it is pinned +} + fn move_of_pinned_projection() { let mut pair = (Foo::default(), Foo::default()); @@ -33,6 +45,18 @@ fn move_of_pinned_projection() { //~^ ERROR cannot move out of `pair.0` because it is pinned } +fn ref_pin_pattern_move_of_pinned_projection() { + let mut pair = (Foo::default(), Foo::default()); + + { + let ref pin mut _pin = pair.0; + } + + let _other = &mut pair.1; + let _moved = pair.0; + //~^ ERROR cannot move out of `pair.0` because it is pinned +} + #[pin_v2] struct ContainsUnpinField { field: String, @@ -48,6 +72,15 @@ fn pinned_parent_still_blocks_unpin_field_move(mut value: ContainsUnpinField) { //~^ ERROR cannot move out of `value.field` because it is pinned } +fn ref_pin_pattern_parent_still_blocks_unpin_field_move(mut value: ContainsUnpinField) { + { + let ref pin mut _pin = value; + } + + let _moved = value.field; + //~^ ERROR cannot move out of `value.field` because it is pinned +} + fn pinned_parent_still_blocks_unpin_field_mut_borrow(mut value: ContainsUnpinField) { { let _ = &pin mut value; @@ -57,4 +90,13 @@ fn pinned_parent_still_blocks_unpin_field_mut_borrow(mut value: ContainsUnpinFie //~^ ERROR cannot borrow `value.field` as mutable because it is pinned } +fn ref_pin_pattern_parent_still_blocks_unpin_field_mut_borrow(mut value: ContainsUnpinField) { + { + let ref pin mut _pin = value; + } + + let _ = &mut value.field; + //~^ ERROR cannot borrow `value.field` as mutable because it is pinned +} + fn main() {} diff --git a/tests/ui/pin-ergonomics/borrow-pinned-projection.stderr b/tests/ui/pin-ergonomics/borrow-pinned-projection.stderr index 416ad776c96d5..6b18f5c42ec55 100644 --- a/tests/ui/pin-ergonomics/borrow-pinned-projection.stderr +++ b/tests/ui/pin-ergonomics/borrow-pinned-projection.stderr @@ -7,8 +7,17 @@ LL | let _pin = &pin mut pair.0; LL | let _borrow = &mut pair.0; | ^^^^^^^^^^^ borrow of `pair.0` as mutable occurs here +error: cannot borrow `pair.0` as mutable because it is pinned + --> $DIR/borrow-pinned-projection.rs:32:19 + | +LL | let ref pin mut _pin = pair.0; + | ---------------- pin of `pair.0` occurs here +... +LL | let _borrow = &mut pair.0; + | ^^^^^^^^^^^ borrow of `pair.0` as mutable occurs here + error: cannot move out of `pair.0` because it is pinned - --> $DIR/borrow-pinned-projection.rs:32:18 + --> $DIR/borrow-pinned-projection.rs:44:18 | LL | let _pin = &pin mut pair.0; | --------------- pin of `pair.0` occurs here @@ -16,8 +25,17 @@ LL | let _pin = &pin mut pair.0; LL | let _moved = pair.0; | ^^^^^^ move out of `pair.0` occurs here +error: cannot move out of `pair.0` because it is pinned + --> $DIR/borrow-pinned-projection.rs:56:18 + | +LL | let ref pin mut _pin = pair.0; + | ---------------- pin of `pair.0` occurs here +... +LL | let _moved = pair.0; + | ^^^^^^ move out of `pair.0` occurs here + error: cannot move out of `value.field` because it is pinned - --> $DIR/borrow-pinned-projection.rs:47:18 + --> $DIR/borrow-pinned-projection.rs:71:18 | LL | let _ = &pin mut value; | -------------- pin of `value` occurs here @@ -25,8 +43,17 @@ LL | let _ = &pin mut value; LL | let _moved = value.field; | ^^^^^^^^^^^ move out of `value.field` occurs here +error: cannot move out of `value.field` because it is pinned + --> $DIR/borrow-pinned-projection.rs:80:18 + | +LL | let ref pin mut _pin = value; + | ---------------- pin of `value` occurs here +... +LL | let _moved = value.field; + | ^^^^^^^^^^^ move out of `value.field` occurs here + error: cannot borrow `value.field` as mutable because it is pinned - --> $DIR/borrow-pinned-projection.rs:56:13 + --> $DIR/borrow-pinned-projection.rs:89:13 | LL | let _ = &pin mut value; | -------------- pin of `value` occurs here @@ -34,5 +61,14 @@ LL | let _ = &pin mut value; LL | let _ = &mut value.field; | ^^^^^^^^^^^^^^^^ borrow of `value.field` as mutable occurs here -error: aborting due to 4 previous errors +error: cannot borrow `value.field` as mutable because it is pinned + --> $DIR/borrow-pinned-projection.rs:98:13 + | +LL | let ref pin mut _pin = value; + | ---------------- pin of `value` occurs here +... +LL | let _ = &mut value.field; + | ^^^^^^^^^^^^^^^^ borrow of `value.field` as mutable occurs here + +error: aborting due to 8 previous errors diff --git a/tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr b/tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr index 89bae9b4cf802..be42c83507b7b 100644 --- a/tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr +++ b/tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr @@ -85,8 +85,26 @@ LL | struct Foo(PhantomPinned); LL | let x = &pin const foo; // ok | --- you could clone this value +error: cannot move out of `foo` because it is pinned + --> $DIR/borrow-unpin.rs:65:14 + | +LL | let ref pin mut _x = foo; + | -------------- pin of `foo` occurs here +LL | } +LL | foo_move(foo); + | ^^^ move out of `foo` occurs here + +error: cannot borrow `foo` as mutable because it is pinned + --> $DIR/borrow-unpin.rs:73:13 + | +LL | let ref pin mut _x = foo; + | -------------- pin of `foo` occurs here +LL | } +LL | foo_mut(&mut foo); + | ^^^^^^^^ borrow of `foo` as mutable occurs here + error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable - --> $DIR/borrow-unpin.rs:67:13 + --> $DIR/borrow-unpin.rs:83:13 | LL | let x = &pin mut foo; // ok | ------------ mutable borrow occurs here @@ -96,7 +114,7 @@ LL | foo_pin_mut(x); | - mutable borrow later used here error[E0499]: cannot borrow `foo` as mutable more than once at a time - --> $DIR/borrow-unpin.rs:89:17 + --> $DIR/borrow-unpin.rs:105:17 | LL | let x = &pin mut foo; // ok | ------------ first mutable borrow occurs here @@ -106,7 +124,7 @@ LL | foo_pin_mut(x); | - first borrow later used here error[E0502]: cannot borrow `foo` as mutable because it is also borrowed as immutable - --> $DIR/borrow-unpin.rs:100:17 + --> $DIR/borrow-unpin.rs:116:17 | LL | let x = &pin const foo; // ok | -------------- immutable borrow occurs here @@ -116,7 +134,7 @@ LL | foo_pin_ref(x); | - immutable borrow later used here error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable - --> $DIR/borrow-unpin.rs:111:17 + --> $DIR/borrow-unpin.rs:127:17 | LL | let x = &pin mut foo; // ok | ------------ mutable borrow occurs here @@ -125,7 +143,7 @@ LL | foo_pin_ref(&pin const foo); LL | foo_pin_mut(x); | - mutable borrow later used here -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors Some errors have detailed explanations: E0499, E0502, E0505. For more information about an error, try `rustc --explain E0499`. diff --git a/tests/ui/pin-ergonomics/borrow-unpin.rs b/tests/ui/pin-ergonomics/borrow-unpin.rs index ac66c4353e161..8e5f3b3bec328 100644 --- a/tests/ui/pin-ergonomics/borrow-unpin.rs +++ b/tests/ui/pin-ergonomics/borrow-unpin.rs @@ -57,6 +57,22 @@ fn pin_ref_then_move() { foo_pin_ref(x); } +fn ref_pin_mut_then_move() { + let mut foo = Foo::default(); + { + let ref pin mut _x = foo; + } + foo_move(foo); //[pinned]~ ERROR cannot move out of `foo` because it is pinned +} + +fn ref_pin_mut_then_mut_borrow() { + let mut foo = Foo::default(); + { + let ref pin mut _x = foo; + } + foo_mut(&mut foo); //[pinned]~ ERROR cannot borrow `foo` as mutable because it is pinned +} + fn pin_mut_then_ref() { let mut foo = Foo::default(); foo_pin_mut(&pin mut foo); // ok diff --git a/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr b/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr index f8fb6dc75fc98..963de703d3348 100644 --- a/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr +++ b/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr @@ -62,7 +62,7 @@ LL | let x = &pin const foo; // ok | --- you could clone this value error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable - --> $DIR/borrow-unpin.rs:67:13 + --> $DIR/borrow-unpin.rs:83:13 | LL | let x = &pin mut foo; // ok | ------------ mutable borrow occurs here @@ -72,7 +72,7 @@ LL | foo_pin_mut(x); | - mutable borrow later used here error[E0499]: cannot borrow `foo` as mutable more than once at a time - --> $DIR/borrow-unpin.rs:89:17 + --> $DIR/borrow-unpin.rs:105:17 | LL | let x = &pin mut foo; // ok | ------------ first mutable borrow occurs here @@ -82,7 +82,7 @@ LL | foo_pin_mut(x); | - first borrow later used here error[E0502]: cannot borrow `foo` as mutable because it is also borrowed as immutable - --> $DIR/borrow-unpin.rs:100:17 + --> $DIR/borrow-unpin.rs:116:17 | LL | let x = &pin const foo; // ok | -------------- immutable borrow occurs here @@ -92,7 +92,7 @@ LL | foo_pin_ref(x); | - immutable borrow later used here error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable - --> $DIR/borrow-unpin.rs:111:17 + --> $DIR/borrow-unpin.rs:127:17 | LL | let x = &pin mut foo; // ok | ------------ mutable borrow occurs here diff --git a/tests/ui/pin-ergonomics/borrow.rs b/tests/ui/pin-ergonomics/borrow.rs index ba4a912d8c5cf..2d41894bf53e0 100644 --- a/tests/ui/pin-ergonomics/borrow.rs +++ b/tests/ui/pin-ergonomics/borrow.rs @@ -44,4 +44,20 @@ fn baz(mut x: Foo, mut y: Foo) { let _y = y; //~ ERROR cannot move out of `y` because it is pinned } +fn ref_pin_mut_then_mut_borrow_and_move(mut x: Foo) { + { + let ref pin mut _x = x; + } + let _x = &mut x; //~ ERROR cannot borrow `x` as mutable because it is pinned + let _x = x; //~ ERROR cannot move out of `x` because it is pinned +} + +fn ref_pin_const_then_mut_borrow_and_move(mut y: Foo) { + { + let ref pin const _y = y; + } + let _y = &mut y; //~ ERROR cannot borrow `y` as mutable because it is pinned + let _y = y; //~ ERROR cannot move out of `y` because it is pinned +} + fn main() {} diff --git a/tests/ui/pin-ergonomics/borrow.stderr b/tests/ui/pin-ergonomics/borrow.stderr index 3161df5fd6c60..3cfd3d9caf7d0 100644 --- a/tests/ui/pin-ergonomics/borrow.stderr +++ b/tests/ui/pin-ergonomics/borrow.stderr @@ -34,5 +34,41 @@ LL | let _y = &pin const y; LL | let _y = y; | ^ move out of `y` occurs here -error: aborting due to 4 previous errors +error: cannot borrow `x` as mutable because it is pinned + --> $DIR/borrow.rs:51:14 + | +LL | let ref pin mut _x = x; + | -------------- pin of `x` occurs here +LL | } +LL | let _x = &mut x; + | ^^^^^^ borrow of `x` as mutable occurs here + +error: cannot move out of `x` because it is pinned + --> $DIR/borrow.rs:52:14 + | +LL | let ref pin mut _x = x; + | -------------- pin of `x` occurs here +... +LL | let _x = x; + | ^ move out of `x` occurs here + +error: cannot borrow `y` as mutable because it is pinned + --> $DIR/borrow.rs:59:14 + | +LL | let ref pin const _y = y; + | ---------------- pin of `y` occurs here +LL | } +LL | let _y = &mut y; + | ^^^^^^ borrow of `y` as mutable occurs here + +error: cannot move out of `y` because it is pinned + --> $DIR/borrow.rs:60:14 + | +LL | let ref pin const _y = y; + | ---------------- pin of `y` occurs here +... +LL | let _y = y; + | ^ move out of `y` occurs here + +error: aborting due to 8 previous errors From 2c431b385ec84ceba34581d3ebb9db2e6d06530e Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman <127837395+P8L1@users.noreply.github.com> Date: Sun, 3 May 2026 18:34:27 +0200 Subject: [PATCH 8/8] Update PinBorrowKind to use StableHash instead of old HashStable Refer to https://github.com/rust-lang/rust/commit/e7d28d3e9b6daaec005e0a56ca8998af10fe6fc3 --- compiler/rustc_middle/src/mir/syntax.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index c30dc64e22fe5..3ed79e8f7681b 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -184,7 +184,7 @@ pub enum BorrowKind { } #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)] -#[derive(Hash, HashStable)] +#[derive(Hash, StableHash)] pub enum PinBorrowKind { /// A user-written `&pin` borrow. ///