diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 4644c210137fe..fb7e54afcb19e 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..c1e402391c846 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,167 @@ 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); + + // 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); + } + } + + 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..bab4ab158fb9d 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,19 @@ 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 +2061,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..bb1eb9ee4a5c5 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -866,13 +866,17 @@ 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..efb24e31511fe 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,23 @@ 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 +1353,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 +1383,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 +1554,17 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { BorrowKind::Fake(FakeBorrowKind::Shallow) => { (Shallow(Some(ArtificialField::FakeBorrow)), Read(ReadKind::Borrow(bk))) } + 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, + pin_kind, + ))), + ), BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep) => { (Deep, Read(ReadKind::Borrow(bk))) } @@ -1838,8 +1930,10 @@ 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 +2509,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 +2534,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..ecf28552b444e 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -262,6 +262,17 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep) => { (Deep, Read(ReadKind::Borrow(bk))) } + 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() { @@ -373,7 +384,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 +397,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 +438,10 @@ 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..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 { .. }, 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 +600,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..63022a82eeadd 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_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/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index bf3d595e99436..03b7e32103e4f 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..a4351d000bc58 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..c30dc64e22fe5 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -178,6 +178,23 @@ pub enum BorrowKind { /// Data is mutable and not aliasable. Mut { kind: MutBorrowKind }, + + /// 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 16a8743a6d67b..52288e4e13fcb 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..a6541d7a89afb 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..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,11 +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 = match mutbl { - hir::Mutability::Mut => BorrowKind::Mut { kind: mir::MutBorrowKind::Default }, - hir::Mutability::Not => BorrowKind::Shared, - }; + // 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); @@ -446,37 +443,20 @@ 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, 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 d5548266aa017..aca586eb72f78 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..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::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..774728d3bb6da 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-pinned-projection.rs b/tests/ui/pin-ergonomics/borrow-pinned-projection.rs new file mode 100644 index 0000000000000..228707234e189 --- /dev/null +++ b/tests/ui/pin-ergonomics/borrow-pinned-projection.rs @@ -0,0 +1,35 @@ +#![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. + +#[pin_v2] +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..4b20519f84774 --- /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:19: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:31: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/borrow-unpin.pinned.stderr b/tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr index cc438461a5d15..89bae9b4cf802 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:30: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:34: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:14: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:41: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:45: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:14: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: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:68:14 + --> $DIR/borrow-unpin.rs:56: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:14: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:67: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:89: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:100: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:111: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..23c5b372c687d 100644 --- a/tests/ui/pin-ergonomics/borrow-unpin.rs +++ b/tests/ui/pin-ergonomics/borrow-unpin.rs @@ -2,83 +2,69 @@ #![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)] +#[pin_v2] #[derive(Default)] struct Foo(PhantomPinned); #[cfg(unpin)] +#[pin_v2] #[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 +82,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 +97,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..1ff363e004ffc 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:30: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:34: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:19: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: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:56:14 + --> $DIR/borrow-unpin.rs:45: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:19: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: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:68:14 + --> $DIR/borrow-unpin.rs:56: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:19: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:67: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:89: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:100: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:111: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..be06d83c5270e 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)] @@ -7,13 +6,12 @@ use std::pin::Pin; +#[pin_v2] 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 +25,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..9ff7e4e2a73d4 --- /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:32: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:33: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:41: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:42: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 + 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/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); +} 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 }