diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index 1f06b1c94237d..aae20f369b916 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -17,6 +17,8 @@ pub enum AutoderefKind { Builtin, /// A type which must dispatch to a `Deref` implementation. Overloaded, + /// A pinned reference type, such as `Pin<&T>` and `Pin<&mut T>`. + Pin, } struct AutoderefSnapshot<'tcx> { at_start: bool, diff --git a/compiler/rustc_hir_typeck/src/autoderef.rs b/compiler/rustc_hir_typeck/src/autoderef.rs index f7700179d2a3a..8ae4918edbfdd 100644 --- a/compiler/rustc_hir_typeck/src/autoderef.rs +++ b/compiler/rustc_hir_typeck/src/autoderef.rs @@ -6,6 +6,7 @@ use itertools::Itertools; use rustc_hir_analysis::autoderef::{Autoderef, AutoderefKind}; use rustc_infer::infer::InferOk; use rustc_infer::traits::PredicateObligations; +use rustc_middle::bug; use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind, OverloadedDeref}; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; @@ -62,6 +63,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) .unwrap_or(DerefAdjustKind::Builtin) } + AutoderefKind::Pin => { + bug!("Pin autoderef kind should not be present in the steps") + } AutoderefKind::Builtin => DerefAdjustKind::Builtin, }) .zip_eq(targets) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 36a07b361d9de..b6dcc3148a090 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -269,12 +269,21 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { return self.coerce_to_raw_ptr(a, b, b_mutbl); } ty::Ref(r_b, _, mutbl_b) => { + if self.tcx.features().pin_ergonomics() + && a.pinned_ty().is_some_and(|ty| ty.is_ref()) + && let Ok(coerce) = self.commit_if_ok(|_| self.coerce_maybe_pinned_ref(a, b)) + { + return Ok(coerce); + } return self.coerce_to_ref(a, b, r_b, mutbl_b); } ty::Adt(pin, _) if self.tcx.features().pin_ergonomics() && self.tcx.is_lang_item(pin.did(), hir::LangItem::Pin) => { + if a.is_ref() && b.pinned_ty().is_some_and(|ty| ty.is_ref()) { + return self.coerce_maybe_pinned_ref(a, b); + } let pin_coerce = self.commit_if_ok(|_| self.coerce_to_pin_ref(a, b)); if pin_coerce.is_ok() { return pin_coerce; @@ -844,7 +853,69 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // To complete the reborrow, we need to make sure we can unify the inner types, and if so we // add the adjustments. - self.unify_and(a, b, [], Adjust::ReborrowPin(mut_b), ForceLeakCheck::No) + self.unify_and( + a, + b, + [Adjustment { kind: Adjust::Deref(DerefAdjustKind::Pin), target: a_ty }], + Adjust::Borrow(AutoBorrow::Pin(mut_b)), + ForceLeakCheck::No, + ) + } + + /// Coerce pinned reference to regular reference or vice versa + /// + /// - `Pin<&mut T>` <-> `&mut T` when `T: Unpin` + /// - `Pin<&T>` <-> `&T` when `T: Unpin` + /// - `Pin<&mut T>` <-> `Pin<&T>` when `T: Unpin` + #[instrument(skip(self), level = "trace")] + fn coerce_maybe_pinned_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { + let span = self.cause.span; + let Some((a_ty, a_pinnedness, a_mutbl, a_region)) = a.maybe_pinned_ref() else { + span_bug!(span, "expect pinned reference or reference, found {:?}", a); + }; + let Some((_b_ty, b_pinnedness, b_mutbl, _b_region)) = b.maybe_pinned_ref() else { + span_bug!(span, "expect pinned reference or reference, found {:?}", b); + }; + use ty::Pinnedness::*; + if a_pinnedness == b_pinnedness { + span_bug!(span, "expect different pinnedness, found {:?} and {:?}", a, b); + } + + coerce_mutbls(a_mutbl, b_mutbl)?; + + let (deref, borrow) = match (a_pinnedness, b_pinnedness) { + (Not, Not) | (Pinned, Pinned) => { + span_bug!(span, "expect different pinnedness, found {:?} and {:?}", a, b) + } + (Pinned, Not) => { + let mutbl = AutoBorrowMutability::new(b_mutbl, AllowTwoPhase::Yes); + (DerefAdjustKind::Pin, AutoBorrow::Ref(mutbl)) + } + (Not, Pinned) => (DerefAdjustKind::Builtin, AutoBorrow::Pin(b_mutbl)), + }; + let mut coerce = self.unify_and( + // update a with b's pinnedness and mutability since we'll be coercing pinnedness and mutability + match b_pinnedness { + Pinned => Ty::new_pinned_ref(self.tcx, a_region, a_ty, b_mutbl), + Not => Ty::new_ref(self.tcx, a_region, a_ty, b_mutbl), + }, + b, + [Adjustment { kind: Adjust::Deref(deref), target: a_ty }], + Adjust::Borrow(borrow), + ForceLeakCheck::No, + )?; + + // Create an obligation for `a_ty: Unpin`. + let cause = + self.cause(self.cause.span, ObligationCauseCode::Coercion { source: a, target: b }); + let pred = ty::TraitRef::new( + self.tcx, + self.tcx.require_lang_item(hir::LangItem::Unpin, self.cause.span), + [a_ty], + ); + let obligation = Obligation::new(self.tcx, cause, self.fcx.param_env, pred); + coerce.obligations.push(obligation); + Ok(coerce) } fn coerce_from_fn_pointer( diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 171184d3a562b..d8022eb22fde2 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -734,7 +734,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx self.consume_or_copy(&place_with_id, place_with_id.hir_id); } - adjustment::Adjust::Deref(DerefAdjustKind::Builtin) => {} + adjustment::Adjust::Deref(DerefAdjustKind::Builtin | DerefAdjustKind::Pin) => {} // Autoderefs for overloaded Deref calls in fact reference // their receiver. That is, if we have `(*x)` where `x` @@ -749,16 +749,6 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx adjustment::Adjust::Borrow(ref autoref) => { self.walk_autoref(expr, &place_with_id, autoref); } - - adjustment::Adjust::ReborrowPin(mutbl) => { - // Reborrowing a Pin is like a combinations of a deref and a borrow, so we do - // both. - let bk = match mutbl { - ty::Mutability::Not => ty::BorrowKind::Immutable, - ty::Mutability::Mut => ty::BorrowKind::Mutable, - }; - self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk); - } } place_with_id = self.cat_expr_adjusted(expr, place_with_id, adjustment)?; } @@ -798,6 +788,16 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx ty::BorrowKind::from_mutbl(m), ); } + + adjustment::AutoBorrow::Pin(m) => { + debug!("walk_autoref: expr.hir_id={} base_place={:?}", expr.hir_id, base_place); + + self.delegate.borrow_mut().borrow( + base_place, + base_place.hir_id, + ty::BorrowKind::from_mutbl(m), + ); + } } } @@ -1291,8 +1291,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) - | adjustment::Adjust::Borrow(_) - | adjustment::Adjust::ReborrowPin(..) => { + | adjustment::Adjust::Borrow(_) => { // Result is an rvalue. Ok(self.cat_rvalue(expr.hir_id, target)) } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 67007523a0671..30900439a90a2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -279,13 +279,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Adjust::Deref(DerefAdjustKind::Builtin) => { // FIXME(const_trait_impl): We *could* enforce `&T: [const] Deref` here. } + Adjust::Deref(DerefAdjustKind::Pin) => { + // FIXME(const_trait_impl): We *could* enforce `Pin<&T>: [const] Deref` here. + } Adjust::Pointer(_pointer_coercion) => { // FIXME(const_trait_impl): We should probably enforce these. } - Adjust::ReborrowPin(_mutability) => { - // FIXME(const_trait_impl): We could enforce these; they correspond to - // `&mut T: DerefMut` tho, so it's kinda moot. - } Adjust::Borrow(_) => { // No effects to enforce here. } diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 608bc7dffd9c0..add67bd826b1c 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -18,7 +18,8 @@ use rustc_lint::builtin::{ }; use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion, + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, + PointerCoercion, }; use rustc_middle::ty::{ self, AssocContainer, GenericArgs, GenericArgsRef, GenericParamDefKind, Ty, TyCtxt, @@ -243,12 +244,16 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { ty::Ref(_, ty, _) => *ty, _ => bug!("Expected a reference type for argument to Pin"), }; + adjustments.push(Adjustment { + kind: Adjust::Deref(DerefAdjustKind::Pin), + target: inner_ty, + }); Ty::new_pinned_ref(self.tcx, region, inner_ty, mutbl) } _ => bug!("Cannot adjust receiver type for reborrowing pin of {target:?}"), }; - - adjustments.push(Adjustment { kind: Adjust::ReborrowPin(mutbl), target }); + adjustments + .push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Pin(mutbl)), target }); } None => {} } diff --git a/compiler/rustc_lint/src/autorefs.rs b/compiler/rustc_lint/src/autorefs.rs index 5bb7df80ffb36..d6e5db05a0c32 100644 --- a/compiler/rustc_lint/src/autorefs.rs +++ b/compiler/rustc_lint/src/autorefs.rs @@ -173,8 +173,7 @@ fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<(Muta &Adjust::Borrow(AutoBorrow::Ref(mutbl)) => Some((mutbl.into(), false)), Adjust::NeverToAny | Adjust::Pointer(..) - | Adjust::ReborrowPin(..) - | Adjust::Deref(DerefAdjustKind::Builtin) - | Adjust::Borrow(AutoBorrow::RawPtr(..)) => None, + | Adjust::Deref(DerefAdjustKind::Builtin | DerefAdjustKind::Pin) + | Adjust::Borrow(AutoBorrow::RawPtr(..) | AutoBorrow::Pin(..)) => None, } } diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 6d546aede4cf4..3802bde088a54 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -103,15 +103,13 @@ pub enum Adjust { Borrow(AutoBorrow), Pointer(PointerCoercion), - - /// Take a pinned reference and reborrow as a `Pin<&mut T>` or `Pin<&T>`. - ReborrowPin(hir::Mutability), } #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub enum DerefAdjustKind { Builtin, Overloaded(OverloadedDeref), + Pin, } /// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)` @@ -196,6 +194,9 @@ pub enum AutoBorrow { /// Converts from T to *T. RawPtr(hir::Mutability), + + /// Converts from T to Pin<&T>. + Pin(hir::Mutability), } /// Information for `CoerceUnsized` impls, storing information we diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 0117a10e3a8c6..04c77b05242e8 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -144,6 +144,19 @@ impl<'tcx> ThirBuildCx<'tcx> { adjust_span(&mut expr); ExprKind::Deref { arg: self.thir.exprs.push(expr) } } + Adjust::Deref(DerefAdjustKind::Pin) => { + adjust_span(&mut expr); + // pointer = ($expr).pointer + let pin_ty = expr.ty.pinned_ty().expect("Deref(Pin) with non-Pin type"); + let pointer_target = ExprKind::Field { + lhs: self.thir.exprs.push(expr), + variant_index: FIRST_VARIANT, + name: FieldIdx::ZERO, + }; + let expr = Expr { temp_scope_id, ty: pin_ty, span, kind: pointer_target }; + // expr = *pointer + ExprKind::Deref { arg: self.thir.exprs.push(expr) } + } Adjust::Deref(DerefAdjustKind::Overloaded(deref)) => { // We don't need to do call adjust_span here since // deref coercions always start with a built-in deref. @@ -178,46 +191,15 @@ impl<'tcx> ThirBuildCx<'tcx> { Adjust::Borrow(AutoBorrow::RawPtr(mutability)) => { ExprKind::RawBorrow { mutability, arg: self.thir.exprs.push(expr) } } - Adjust::ReborrowPin(mutbl) => { - debug!("apply ReborrowPin adjustment"); - // Rewrite `$expr` as `Pin { __pointer: &(mut)? *($expr).__pointer }` - - // We'll need these types later on - let pin_ty_args = match expr.ty.kind() { - ty::Adt(_, args) => args, - _ => bug!("ReborrowPin with non-Pin type"), - }; - let pin_ty = pin_ty_args.iter().next().unwrap().expect_ty(); - let ptr_target_ty = match pin_ty.kind() { - ty::Ref(_, ty, _) => *ty, - _ => bug!("ReborrowPin with non-Ref type"), - }; - - // pointer = ($expr).__pointer - let pointer_target = ExprKind::Field { - lhs: self.thir.exprs.push(expr), - variant_index: FIRST_VARIANT, - name: FieldIdx::ZERO, - }; - let arg = Expr { temp_scope_id, ty: pin_ty, span, kind: pointer_target }; - let arg = self.thir.exprs.push(arg); - - // arg = *pointer - let expr = ExprKind::Deref { arg }; - let arg = self.thir.exprs.push(Expr { - temp_scope_id, - ty: ptr_target_ty, - span, - kind: expr, - }); - - // expr = &mut target + 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 new_pin_target = - Ty::new_ref(self.tcx, self.tcx.lifetimes.re_erased, ptr_target_ty, mutbl); + Ty::new_ref(self.tcx, self.tcx.lifetimes.re_erased, expr.ty, mutbl); + let arg = self.thir.exprs.push(expr); let expr = self.thir.exprs.push(Expr { temp_scope_id, ty: new_pin_target, @@ -225,7 +207,7 @@ impl<'tcx> ThirBuildCx<'tcx> { kind: ExprKind::Borrow { borrow_kind, arg }, }); - // kind = Pin { __pointer: pointer } + // kind = Pin { pointer } let pin_did = self.tcx.require_lang_item(rustc_hir::LangItem::Pin, span); let args = self.tcx.mk_args(&[new_pin_target.into()]); let kind = ExprKind::Adt(Box::new(AdtExpr { diff --git a/tests/ui/pin-ergonomics/pin-coercion-check.normal.stderr b/tests/ui/pin-ergonomics/pin-coercion-check.normal.stderr new file mode 100644 index 0000000000000..0806ad64448c4 --- /dev/null +++ b/tests/ui/pin-ergonomics/pin-coercion-check.normal.stderr @@ -0,0 +1,227 @@ +error[E0277]: `T` cannot be unpinned + --> $DIR/pin-coercion-check.rs:9:36 + | +LL | |x: Pin<&mut T>| -> &mut T { x.get_mut() }; + | ^^^^^^^ the trait `Unpin` is not implemented for `T` + | + = note: consider using the `pin!` macro + consider using `Box::pin` if you need to access the pinned value outside of the current scope +note: required by a bound in `Pin::<&'a mut T>::get_mut` + --> $SRC_DIR/core/src/pin.rs:LL:COL +help: consider restricting type parameter `T` with trait `Unpin` + | +LL | fn get() { + | ++++++++++++++++++++ + +error[E0308]: mismatched types + --> $DIR/pin-coercion-check.rs:18:34 + | +LL | |x: Pin<&mut T>| -> &mut T { x }; + | ------ ^ expected `&mut T`, found `Pin<&mut T>` + | | + | expected `&mut T` because of return type + | + = note: expected mutable reference `&mut T` + found struct `Pin<&mut T>` +help: consider mutably borrowing here + | +LL | |x: Pin<&mut T>| -> &mut T { &mut x }; + | ++++ + +error[E0308]: mismatched types + --> $DIR/pin-coercion-check.rs:21:30 + | +LL | |x: Pin<&mut T>| -> &T { x }; + | -- ^ expected `&T`, found `Pin<&mut T>` + | | + | expected `&T` because of return type + | + = note: expected reference `&T` + found struct `Pin<&mut T>` +help: consider borrowing here + | +LL | |x: Pin<&mut T>| -> &T { &x }; + | + + +error[E0308]: mismatched types + --> $DIR/pin-coercion-check.rs:24:26 + | +LL | |x: Pin<&T>| -> &T { x }; + | -- ^ expected `&T`, found `Pin<&T>` + | | + | expected `&T` because of return type + | + = note: expected reference `&T` + found struct `Pin<&T>` +help: consider borrowing here + | +LL | |x: Pin<&T>| -> &T { &x }; + | + + +error[E0308]: mismatched types + --> $DIR/pin-coercion-check.rs:27:30 + | +LL | |x: Pin<&T>| -> &mut T { x }; + | ------ ^ expected `&mut T`, found `Pin<&T>` + | | + | expected `&mut T` because of return type + | + = note: expected mutable reference `&mut T` + found struct `Pin<&T>` +help: consider mutably borrowing here + | +LL | |x: Pin<&T>| -> &mut T { &mut x }; + | ++++ + +error[E0308]: mismatched types + --> $DIR/pin-coercion-check.rs:31:34 + | +LL | |x: Pin<&mut U>| -> &mut U { x }; + | ------ ^ expected `&mut U`, found `Pin<&mut U>` + | | + | expected `&mut U` because of return type + | + = note: expected mutable reference `&mut U` + found struct `Pin<&mut U>` +help: consider mutably borrowing here + | +LL | |x: Pin<&mut U>| -> &mut U { &mut x }; + | ++++ + +error[E0308]: mismatched types + --> $DIR/pin-coercion-check.rs:33:30 + | +LL | |x: Pin<&mut U>| -> &U { x }; + | -- ^ expected `&U`, found `Pin<&mut U>` + | | + | expected `&U` because of return type + | + = note: expected reference `&U` + found struct `Pin<&mut U>` +help: consider borrowing here + | +LL | |x: Pin<&mut U>| -> &U { &x }; + | + + +error[E0308]: mismatched types + --> $DIR/pin-coercion-check.rs:35:26 + | +LL | |x: Pin<&U>| -> &U { x }; + | -- ^ expected `&U`, found `Pin<&U>` + | | + | expected `&U` because of return type + | + = note: expected reference `&U` + found struct `Pin<&U>` +help: consider borrowing here + | +LL | |x: Pin<&U>| -> &U { &x }; + | + + +error[E0308]: mismatched types + --> $DIR/pin-coercion-check.rs:37:30 + | +LL | |x: Pin<&U>| -> &mut U { x }; + | ------ ^ expected `&mut U`, found `Pin<&U>` + | | + | expected `&mut U` because of return type + | + = note: expected mutable reference `&mut U` + found struct `Pin<&U>` +help: consider mutably borrowing here + | +LL | |x: Pin<&U>| -> &mut U { &mut x }; + | ++++ + +error[E0308]: mismatched types + --> $DIR/pin-coercion-check.rs:43:34 + | +LL | |x: &mut T| -> Pin<&mut T> { x }; + | ----------- ^ expected `Pin<&mut T>`, found `&mut T` + | | + | expected `Pin<&mut T>` because of return type + | + = note: expected struct `Pin<&mut T>` + found mutable reference `&mut T` + +error[E0308]: mismatched types + --> $DIR/pin-coercion-check.rs:46:30 + | +LL | |x: &mut T| -> Pin<&T> { x }; + | ------- ^ expected `Pin<&T>`, found `&mut T` + | | + | expected `Pin<&T>` because of return type + | + = note: expected struct `Pin<&T>` + found mutable reference `&mut T` + +error[E0308]: mismatched types + --> $DIR/pin-coercion-check.rs:49:26 + | +LL | |x: &T| -> Pin<&T> { x }; + | ------- ^ expected `Pin<&T>`, found `&T` + | | + | expected `Pin<&T>` because of return type + | + = note: expected struct `Pin<&T>` + found reference `&T` + +error[E0308]: mismatched types + --> $DIR/pin-coercion-check.rs:52:30 + | +LL | |x: &T| -> Pin<&mut T> { x }; + | ----------- ^ expected `Pin<&mut T>`, found `&T` + | | + | expected `Pin<&mut T>` because of return type + | + = note: expected struct `Pin<&mut T>` + found reference `&T` + +error[E0308]: mismatched types + --> $DIR/pin-coercion-check.rs:56:34 + | +LL | |x: &mut U| -> Pin<&mut U> { x }; + | ----------- ^ expected `Pin<&mut U>`, found `&mut U` + | | + | expected `Pin<&mut U>` because of return type + | + = note: expected struct `Pin<&mut U>` + found mutable reference `&mut U` + +error[E0308]: mismatched types + --> $DIR/pin-coercion-check.rs:58:30 + | +LL | |x: &mut U| -> Pin<&U> { x }; + | ------- ^ expected `Pin<&U>`, found `&mut U` + | | + | expected `Pin<&U>` because of return type + | + = note: expected struct `Pin<&U>` + found mutable reference `&mut U` + +error[E0308]: mismatched types + --> $DIR/pin-coercion-check.rs:60:26 + | +LL | |x: &U| -> Pin<&U> { x }; + | ------- ^ expected `Pin<&U>`, found `&U` + | | + | expected `Pin<&U>` because of return type + | + = note: expected struct `Pin<&U>` + found reference `&U` + +error[E0308]: mismatched types + --> $DIR/pin-coercion-check.rs:62:30 + | +LL | |x: &U| -> Pin<&mut U> { x }; + | ----------- ^ expected `Pin<&mut U>`, found `&U` + | | + | expected `Pin<&mut U>` because of return type + | + = note: expected struct `Pin<&mut U>` + found reference `&U` + +error: aborting due to 17 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/pin-ergonomics/pin-coercion-check.pin_ergonomics.stderr b/tests/ui/pin-ergonomics/pin-coercion-check.pin_ergonomics.stderr new file mode 100644 index 0000000000000..ce343fd24a5b8 --- /dev/null +++ b/tests/ui/pin-ergonomics/pin-coercion-check.pin_ergonomics.stderr @@ -0,0 +1,155 @@ +error[E0277]: `T` cannot be unpinned + --> $DIR/pin-coercion-check.rs:9:36 + | +LL | |x: Pin<&mut T>| -> &mut T { x.get_mut() }; + | ^^^^^^^ the trait `Unpin` is not implemented for `T` + | + = note: consider using the `pin!` macro + consider using `Box::pin` if you need to access the pinned value outside of the current scope +note: required by a bound in `Pin::<&'a mut T>::get_mut` + --> $SRC_DIR/core/src/pin.rs:LL:COL +help: consider restricting type parameter `T` with trait `Unpin` + | +LL | fn get() { + | ++++++++++++++++++++ + +error[E0277]: `T` cannot be unpinned + --> $DIR/pin-coercion-check.rs:18:34 + | +LL | |x: Pin<&mut T>| -> &mut T { x }; + | ^ the trait `Unpin` is not implemented for `T` + | + = note: consider using the `pin!` macro + consider using `Box::pin` if you need to access the pinned value outside of the current scope + = note: required for the cast from `Pin<&mut T>` to `&mut T` +help: consider restricting type parameter `T` with trait `Unpin` + | +LL | fn pin_to_ref() { + | ++++++++++++++++++++ + +error[E0277]: `T` cannot be unpinned + --> $DIR/pin-coercion-check.rs:21:30 + | +LL | |x: Pin<&mut T>| -> &T { x }; + | ^ the trait `Unpin` is not implemented for `T` + | + = note: consider using the `pin!` macro + consider using `Box::pin` if you need to access the pinned value outside of the current scope + = note: required for the cast from `Pin<&mut T>` to `&T` +help: consider restricting type parameter `T` with trait `Unpin` + | +LL | fn pin_to_ref() { + | ++++++++++++++++++++ + +error[E0277]: `T` cannot be unpinned + --> $DIR/pin-coercion-check.rs:24:26 + | +LL | |x: Pin<&T>| -> &T { x }; + | ^ the trait `Unpin` is not implemented for `T` + | + = note: consider using the `pin!` macro + consider using `Box::pin` if you need to access the pinned value outside of the current scope + = note: required for the cast from `Pin<&T>` to `&T` +help: consider restricting type parameter `T` with trait `Unpin` + | +LL | fn pin_to_ref() { + | ++++++++++++++++++++ + +error[E0308]: mismatched types + --> $DIR/pin-coercion-check.rs:27:30 + | +LL | |x: Pin<&T>| -> &mut T { x }; + | ------ ^ expected `&mut T`, found `Pin<&T>` + | | + | expected `&mut T` because of return type + | + = note: expected mutable reference `&mut T` + found struct `Pin<&T>` +help: consider mutably borrowing here + | +LL | |x: Pin<&T>| -> &mut T { &mut x }; + | ++++ + +error[E0308]: mismatched types + --> $DIR/pin-coercion-check.rs:37:30 + | +LL | |x: Pin<&U>| -> &mut U { x }; + | ------ ^ expected `&mut U`, found `Pin<&U>` + | | + | expected `&mut U` because of return type + | + = note: expected mutable reference `&mut U` + found struct `Pin<&U>` +help: consider mutably borrowing here + | +LL | |x: Pin<&U>| -> &mut U { &mut x }; + | ++++ + +error[E0277]: `T` cannot be unpinned + --> $DIR/pin-coercion-check.rs:43:34 + | +LL | |x: &mut T| -> Pin<&mut T> { x }; + | ^ the trait `Unpin` is not implemented for `T` + | + = note: consider using the `pin!` macro + consider using `Box::pin` if you need to access the pinned value outside of the current scope + = note: required for the cast from `&mut T` to `Pin<&mut T>` +help: consider restricting type parameter `T` with trait `Unpin` + | +LL | fn ref_to_pin() { + | ++++++++++++++++++++ + +error[E0277]: `T` cannot be unpinned + --> $DIR/pin-coercion-check.rs:46:30 + | +LL | |x: &mut T| -> Pin<&T> { x }; + | ^ the trait `Unpin` is not implemented for `T` + | + = note: consider using the `pin!` macro + consider using `Box::pin` if you need to access the pinned value outside of the current scope + = note: required for the cast from `&mut T` to `Pin<&T>` +help: consider restricting type parameter `T` with trait `Unpin` + | +LL | fn ref_to_pin() { + | ++++++++++++++++++++ + +error[E0277]: `T` cannot be unpinned + --> $DIR/pin-coercion-check.rs:49:26 + | +LL | |x: &T| -> Pin<&T> { x }; + | ^ the trait `Unpin` is not implemented for `T` + | + = note: consider using the `pin!` macro + consider using `Box::pin` if you need to access the pinned value outside of the current scope + = note: required for the cast from `&T` to `Pin<&T>` +help: consider restricting type parameter `T` with trait `Unpin` + | +LL | fn ref_to_pin() { + | ++++++++++++++++++++ + +error[E0308]: mismatched types + --> $DIR/pin-coercion-check.rs:52:30 + | +LL | |x: &T| -> Pin<&mut T> { x }; + | ----------- ^ types differ in mutability + | | + | expected `Pin<&mut T>` because of return type + | + = note: expected struct `Pin<&mut T>` + found reference `&T` + +error[E0308]: mismatched types + --> $DIR/pin-coercion-check.rs:62:30 + | +LL | |x: &U| -> Pin<&mut U> { x }; + | ----------- ^ types differ in mutability + | | + | expected `Pin<&mut U>` because of return type + | + = note: expected struct `Pin<&mut U>` + found reference `&U` + +error: aborting due to 11 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/pin-ergonomics/pin-coercion-check.rs b/tests/ui/pin-ergonomics/pin-coercion-check.rs new file mode 100644 index 0000000000000..9b51851219ccd --- /dev/null +++ b/tests/ui/pin-ergonomics/pin-coercion-check.rs @@ -0,0 +1,66 @@ +//@ revisions: pin_ergonomics normal +//@ edition:2024 +#![cfg_attr(pin_ergonomics, feature(pin_ergonomics))] +#![allow(incomplete_features)] + +use std::pin::Pin; + +fn get() { + |x: Pin<&mut T>| -> &mut T { x.get_mut() }; //~ ERROR `T` cannot be unpinned + |x: Pin<&T>| -> &T { x.get_ref() }; + + |x: Pin<&mut U>| -> &mut U { x.get_mut() }; + |x: Pin<&U>| -> &U { x.get_ref() }; +} + +fn pin_to_ref() { + // T: !Unpin + |x: Pin<&mut T>| -> &mut T { x }; + //[normal]~^ ERROR mismatched types + //[pin_ergonomics]~^^ ERROR `T` cannot be unpinned + |x: Pin<&mut T>| -> &T { x }; + //[normal]~^ ERROR mismatched types + //[pin_ergonomics]~^^ ERROR `T` cannot be unpinned + |x: Pin<&T>| -> &T { x }; + //[normal]~^ ERROR mismatched types + //[pin_ergonomics]~^^ ERROR `T` cannot be unpinned + |x: Pin<&T>| -> &mut T { x }; + //~^ ERROR mismatched types + + // U: Unpin + |x: Pin<&mut U>| -> &mut U { x }; + //[normal]~^ ERROR mismatched types + |x: Pin<&mut U>| -> &U { x }; + //[normal]~^ ERROR mismatched types + |x: Pin<&U>| -> &U { x }; + //[normal]~^ ERROR mismatched types + |x: Pin<&U>| -> &mut U { x }; + //~^ ERROR mismatched types +} + +fn ref_to_pin() { + // T: !Unpin + |x: &mut T| -> Pin<&mut T> { x }; + //[normal]~^ ERROR mismatched types + //[pin_ergonomics]~^^ ERROR `T` cannot be unpinned + |x: &mut T| -> Pin<&T> { x }; + //[normal]~^ ERROR mismatched types + //[pin_ergonomics]~^^ ERROR `T` cannot be unpinned + |x: &T| -> Pin<&T> { x }; + //[normal]~^ ERROR mismatched types + //[pin_ergonomics]~^^ ERROR `T` cannot be unpinned + |x: &T| -> Pin<&mut T> { x }; + //~^ ERROR mismatched types + + // U: Unpin + |x: &mut U| -> Pin<&mut U> { x }; + //[normal]~^ ERROR mismatched types + |x: &mut U| -> Pin<&U> { x }; + //[normal]~^ ERROR mismatched types + |x: &U| -> Pin<&U> { x }; + //[normal]~^ ERROR mismatched types + |x: &U| -> Pin<&mut U> { x }; + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/pin-ergonomics/pin-coercion.rs b/tests/ui/pin-ergonomics/pin-coercion.rs new file mode 100644 index 0000000000000..6ace4b97edb34 --- /dev/null +++ b/tests/ui/pin-ergonomics/pin-coercion.rs @@ -0,0 +1,56 @@ +//@ run-pass +//@ edition:2024 +#![feature(pin_ergonomics)] +#![allow(incomplete_features)] +#![deny(dead_code)] + +use std::cell::RefCell; + +fn coerce_mut_to_pin_mut(x: &mut T) -> &pin mut T { + x +} +fn coerce_ref_to_pin_ref(x: &T) -> &pin const T { + x +} +fn coerce_pin_mut_to_mut(x: &pin mut T) -> &mut T { + x +} +fn coerce_pin_ref_to_ref(x: &pin const T) -> &T { + x +} + +fn coerce_pin_mut_to_ref(x: &pin mut T) -> &T { + x +} +fn coerce_mut_to_pin_ref(x: &mut T) -> &pin const T { + x +} + +fn test(x: &mut RefCell) { + let mut x: &pin mut _ = coerce_mut_to_pin_mut(x); + x.get_mut().get_mut().push_str("&mut T -> &pin mut T\n"); + let x_ref: &_ = coerce_pin_mut_to_ref(x.as_mut()); + x_ref.borrow_mut().push_str("&pin mut T -> &T\n"); + let x: &mut _ = coerce_pin_mut_to_mut(x); + x.get_mut().push_str("&pin mut T -> &mut T\n"); + let x: &pin const _ = coerce_mut_to_pin_ref(x); + x.borrow_mut().push_str("&mut T -> &pin const T\n"); + let x: &_ = coerce_pin_ref_to_ref(x); + x.borrow_mut().push_str("&pin const T -> &T\n"); + let x: &pin const _ = coerce_ref_to_pin_ref(x); + x.borrow_mut().push_str("&T -> &pin const T\n"); +} + +fn main() { + let mut x = RefCell::new(String::new()); + test(&mut x); + assert_eq!( + x.borrow().as_str(), + "&mut T -> &pin mut T\n\ + &pin mut T -> &T\n\ + &pin mut T -> &mut T\n\ + &mut T -> &pin const T\n\ + &pin const T -> &T\n\ + &T -> &pin const T\n" + ); +}