diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index 1f06b1c94237..aae20f369b91 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 f7700179d2a3..8ae4918edbfd 100644 --- a/compiler/rustc_hir_typeck/src/autoderef.rs +++ b/compiler/rustc_hir_typeck/src/autoderef.rs @@ -6,6 +6,7 @@ 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 @@ pub(crate) fn adjust_steps_as_infer_ok( }) .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 8a73125d6476..95eb1cfbea96 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -269,12 +269,21 @@ fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'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; @@ -847,6 +856,62 @@ fn coerce_to_pin_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { self.unify_and(a, b, [], Adjust::ReborrowPin(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( &self, a: Ty<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 0a492c795b29..5905e5610f72 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -736,7 +736,7 @@ fn walk_adjustment(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> { 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` @@ -800,6 +800,16 @@ fn walk_autoref( 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), + ); + } } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index c24d1127d5a6..f817ca842147 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -331,6 +331,9 @@ pub(crate) fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec { // 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. } diff --git a/compiler/rustc_lint/src/autorefs.rs b/compiler/rustc_lint/src/autorefs.rs index 34049288ea80..910a2918f9f7 100644 --- a/compiler/rustc_lint/src/autorefs.rs +++ b/compiler/rustc_lint/src/autorefs.rs @@ -173,7 +173,7 @@ fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<(Muta 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 6d546aede4cf..58687be7440b 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -105,6 +105,7 @@ pub enum Adjust { Pointer(PointerCoercion), /// Take a pinned reference and reborrow as a `Pin<&mut T>` or `Pin<&T>`. + // FIXME(pin_ergonomics): This can be replaced with a `Deref(Pin)` followed by a `Borrow(Pin)` ReborrowPin(hir::Mutability), } @@ -112,6 +113,7 @@ pub enum Adjust { pub enum DerefAdjustKind { Builtin, Overloaded(OverloadedDeref), + Pin, } /// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)` @@ -196,6 +198,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 c5eeb8b1aa85..c646b0fc45ea 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -143,6 +143,19 @@ fn apply_adjustment( 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. @@ -177,6 +190,37 @@ fn apply_adjustment( Adjust::Borrow(AutoBorrow::RawPtr(mutability)) => { 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, + }; + let new_pin_target = + 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, + span, + kind: ExprKind::Borrow { borrow_kind, arg }, + }); + + // 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 { + adt_def: self.tcx.adt_def(pin_did), + variant_index: FIRST_VARIANT, + args, + fields: Box::new([FieldExpr { name: FieldIdx::ZERO, expr }]), + user_ty: None, + base: AdtExprBase::None, + })); + + debug!(?kind); + kind + } Adjust::ReborrowPin(mutbl) => { debug!("apply ReborrowPin adjustment"); // Rewrite `$expr` as `Pin { __pointer: &(mut)? *($expr).__pointer }` 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 000000000000..0806ad64448c --- /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 000000000000..ce343fd24a5b --- /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 000000000000..9b51851219cc --- /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 000000000000..6ace4b97edb3 --- /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" + ); +}