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 4fe77278706ec..8ae4918edbfdd 100644 --- a/compiler/rustc_hir_typeck/src/autoderef.rs +++ b/compiler/rustc_hir_typeck/src/autoderef.rs @@ -6,7 +6,8 @@ use itertools::Itertools; use rustc_hir_analysis::autoderef::{Autoderef, AutoderefKind}; use rustc_infer::infer::InferOk; use rustc_infer::traits::PredicateObligations; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; +use rustc_middle::bug; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind, OverloadedDeref}; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; @@ -45,22 +46,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(autoderef.final_ty())); let steps: Vec<_> = steps .iter() - .map(|&(source, kind)| { - if let AutoderefKind::Overloaded = kind { - self.try_overloaded_deref(autoderef.span(), source).and_then( - |InferOk { value: method, obligations: o }| { + .map(|&(source, kind)| match kind { + AutoderefKind::Overloaded => { + self.try_overloaded_deref(autoderef.span(), source) + .and_then(|InferOk { value: method, obligations: o }| { obligations.extend(o); // FIXME: we should assert the sig is &T here... there's no reason for this to be fallible. if let ty::Ref(_, _, mutbl) = *method.sig.output().kind() { - Some(OverloadedDeref { mutbl, span: autoderef.span() }) + Some(DerefAdjustKind::Overloaded(OverloadedDeref { + mutbl, + span: autoderef.span(), + })) } else { None } - }, - ) - } else { - None + }) + .unwrap_or(DerefAdjustKind::Builtin) } + AutoderefKind::Pin => { + bug!("Pin autoderef kind should not be present in the steps") + } + AutoderefKind::Builtin => DerefAdjustKind::Builtin, }) .zip_eq(targets) .map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target }) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 127965cb4b301..856752177e479 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -50,7 +50,8 @@ use rustc_infer::traits::{ }; use rustc_middle::span_bug; use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion, + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, + PointerCoercion, }; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; @@ -268,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; @@ -595,7 +605,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No); Some(( - Adjustment { kind: Adjust::Deref(None), target: ty_a }, + Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: ty_a }, Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), target: Ty::new_ref(self.tcx, r_borrow, ty_a, mutbl_b), @@ -606,7 +616,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { coerce_mutbls(mt_a, mt_b)?; Some(( - Adjustment { kind: Adjust::Deref(None), target: ty_a }, + Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: ty_a }, Adjustment { kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b)), target: Ty::new_ptr(self.tcx, ty_a, mt_b), @@ -846,6 +856,62 @@ impl<'f, 'tcx> Coerce<'f, '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>, @@ -936,7 +1002,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { self.unify_and( a_raw, b, - [Adjustment { kind: Adjust::Deref(None), target: mt_a.ty }], + [Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: mt_a.ty }], Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b)), ForceLeakCheck::No, ) diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 27b66d07f98e4..1de4f0259a32b 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -23,6 +23,7 @@ use rustc_middle::hir::place::ProjectionKind; // Export these here so that Clippy can use them. pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection}; use rustc_middle::mir::FakeReadCause; +use rustc_middle::ty::adjustment::DerefAdjustKind; use rustc_middle::ty::{ self, BorrowKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt as _, adjustment, }; @@ -829,14 +830,14 @@ 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(None) => {} + 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` // is of type `Rc`, then this in fact is equivalent to // `x.deref()`. Since `deref()` is declared with `&self`, // this is an autoref of `x`. - adjustment::Adjust::Deref(Some(ref deref)) => { + adjustment::Adjust::Deref(DerefAdjustKind::Overloaded(deref)) => { let bk = ty::BorrowKind::from_mutbl(deref.mutbl); self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk); } @@ -893,6 +894,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), + ); + } } } @@ -1325,9 +1336,9 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx { let target = self.cx.resolve_vars_if_possible(adjustment.target); match adjustment.kind { - adjustment::Adjust::Deref(overloaded) => { + adjustment::Adjust::Deref(deref_kind) => { // Equivalent to *expr or something similar. - let base = if let Some(deref) = overloaded { + let base = if let DerefAdjustKind::Overloaded(deref) = deref_kind { let ref_ty = Ty::new_ref( self.cx.tcx(), self.cx.tcx().lifetimes.re_erased, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index da719e615fd70..f14fe9c0b7a65 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -20,7 +20,9 @@ use rustc_hir_analysis::hir_ty_lowering::{ use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::{DefineOpaqueTypes, InferResult}; use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, +}; use rustc_middle::ty::{ self, AdtKind, CanonicalUserType, GenericArgsRef, GenericParamDefKind, IsIdentity, SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, UserArgs, @@ -266,7 +268,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("apply_adjustments: adding `{:?}` as diverging type var", a.target); } } - Adjust::Deref(Some(overloaded_deref)) => { + Adjust::Deref(DerefAdjustKind::Overloaded(overloaded_deref)) => { self.enforce_context_effects( None, expr.span, @@ -274,9 +276,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.mk_args(&[expr_ty.into()]), ); } - Adjust::Deref(None) => { + 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. } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 06fd89837d516..cb00685ce38a3 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -2857,7 +2857,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // the bad interactions of the given hack detailed in (note_1). debug!("check_pat_ref: expected={:?}", expected); match expected.maybe_pinned_ref() { - Some((r_ty, r_pinned, r_mutbl)) + Some((r_ty, r_pinned, r_mutbl, _)) if ((ref_pat_matches_mut_ref && r_mutbl >= pat_mutbl) || r_mutbl == pat_mutbl) && pat_pinned == r_pinned => diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index a48db2cc855c0..c33f8bdaffe27 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -4,8 +4,8 @@ use rustc_infer::infer::InferOk; use rustc_infer::traits::{Obligation, ObligationCauseCode}; use rustc_middle::span_bug; use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, OverloadedDeref, - PointerCoercion, + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, + OverloadedDeref, PointerCoercion, }; use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, sym}; @@ -298,7 +298,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.typeck_results.borrow_mut().adjustments_mut().remove(expr.hir_id); if let Some(mut adjustments) = previous_adjustments { for adjustment in &mut adjustments { - if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind + if let Adjust::Deref(DerefAdjustKind::Overloaded(ref mut deref)) = + adjustment.kind && let Some(ok) = self.try_mutable_overloaded_place_op( expr.span, source, diff --git a/compiler/rustc_lint/src/autorefs.rs b/compiler/rustc_lint/src/autorefs.rs index 5490a3aac9b7a..ccac382e079e4 100644 --- a/compiler/rustc_lint/src/autorefs.rs +++ b/compiler/rustc_lint/src/autorefs.rs @@ -1,6 +1,8 @@ use rustc_ast::{BorrowKind, UnOp}; use rustc_hir::{Expr, ExprKind, Mutability}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, OverloadedDeref}; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AutoBorrow, DerefAdjustKind, OverloadedDeref, +}; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::sym; @@ -165,12 +167,14 @@ fn peel_derefs_adjustments<'a>(mut adjs: &'a [Adjustment<'a>]) -> &'a [Adjustmen /// an implicit borrow (or has an implicit borrow via an overloaded deref). fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<(Mutability, bool)> { match kind { - &Adjust::Deref(Some(OverloadedDeref { mutbl, .. })) => Some((mutbl, true)), + &Adjust::Deref(DerefAdjustKind::Overloaded(OverloadedDeref { mutbl, .. })) => { + Some((mutbl, true)) + } &Adjust::Borrow(AutoBorrow::Ref(mutbl)) => Some((mutbl.into(), false)), Adjust::NeverToAny | Adjust::Pointer(..) | Adjust::ReborrowPin(..) - | Adjust::Deref(None) - | Adjust::Borrow(AutoBorrow::RawPtr(..)) => None, + | Adjust::Deref(DerefAdjustKind::Builtin | DerefAdjustKind::Pin) + | Adjust::Borrow(AutoBorrow::RawPtr(..) | AutoBorrow::Pin(..)) => None, } } diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index 24682c4562a03..66b4e3c16e936 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -1,7 +1,7 @@ use rustc_hir::def::DefKind; use rustc_hir::{Expr, ExprKind}; use rustc_middle::ty; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::sym; @@ -114,7 +114,10 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { // If there is any user defined auto-deref step, then we don't want to warn. // https://github.com/rust-lang/rust-clippy/issues/9272 - if arg_adjustments.iter().any(|adj| matches!(adj.kind, Adjust::Deref(Some(_)))) { + if arg_adjustments + .iter() + .any(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_)))) + { return; } diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index c806366b518ae..e2bfb147c12dc 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -97,7 +97,7 @@ pub enum Adjust { NeverToAny, /// Dereference once, producing a place. - Deref(Option), + Deref(DerefAdjustKind), /// Take the address and produce either a `&` or `*` pointer. Borrow(AutoBorrow), @@ -105,9 +105,29 @@ 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), } +#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] +pub enum DerefAdjustKind { + Builtin, + Overloaded(OverloadedDeref), + Pin, +} + +impl DerefAdjustKind { + pub fn is_builtin(&self) -> bool { + matches!(self, DerefAdjustKind::Builtin) + } + pub fn is_overloaded(&self) -> bool { + matches!(self, DerefAdjustKind::Overloaded(_)) + } + pub fn is_pin(&self) -> bool { + matches!(self, DerefAdjustKind::Pin) + } +} + /// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)` /// call, with the signature `&'a T -> &'a U` or `&'a mut T -> &'a mut U`. /// The target type is `U` in both cases, with the region and mutability @@ -190,6 +210,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_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 852a0017687f2..27fd271449873 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1352,25 +1352,17 @@ impl<'tcx> Ty<'tcx> { } } - pub fn pinned_ref(self) -> Option<(Ty<'tcx>, ty::Mutability)> { - if let Adt(def, args) = self.kind() - && def.is_pin() - && let &ty::Ref(_, ty, mutbl) = args.type_at(0).kind() - { - return Some((ty, mutbl)); - } - None - } - - pub fn maybe_pinned_ref(self) -> Option<(Ty<'tcx>, ty::Pinnedness, ty::Mutability)> { - match *self.kind() { + pub fn maybe_pinned_ref( + self, + ) -> Option<(Ty<'tcx>, ty::Pinnedness, ty::Mutability, Region<'tcx>)> { + match self.kind() { Adt(def, args) if def.is_pin() - && let ty::Ref(_, ty, mutbl) = *args.type_at(0).kind() => + && let &ty::Ref(region, ty, mutbl) = args.type_at(0).kind() => { - Some((ty, ty::Pinnedness::Pinned, mutbl)) + Some((ty, ty::Pinnedness::Pinned, mutbl, region)) } - ty::Ref(_, ty, mutbl) => Some((ty, ty::Pinnedness::Not, mutbl)), + &Ref(region, ty, mutbl) => Some((ty, ty::Pinnedness::Not, mutbl, region)), _ => None, } } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 50ccbd50d9711..541a803d8dbdd 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -14,7 +14,7 @@ use rustc_middle::middle::region; use rustc_middle::mir::{self, AssignOp, BinOp, BorrowKind, UnOp}; use rustc_middle::thir::*; use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCoercion, + Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, PointerCoercion, }; use rustc_middle::ty::{ self, AdtKind, GenericArgs, InlineConstArgs, InlineConstArgsParts, ScalarInt, Ty, UpvarArgs, @@ -140,11 +140,24 @@ impl<'tcx> ThirBuildCx<'tcx> { } Adjust::NeverToAny if adjustment.target.is_never() => return expr, Adjust::NeverToAny => ExprKind::NeverToAny { source: self.thir.exprs.push(expr) }, - Adjust::Deref(None) => { + Adjust::Deref(DerefAdjustKind::Builtin) => { adjust_span(&mut expr); ExprKind::Deref { arg: self.thir.exprs.push(expr) } } - Adjust::Deref(Some(deref)) => { + 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. let call_def_id = deref.method_call(self.tcx); @@ -178,6 +191,37 @@ impl<'tcx> ThirBuildCx<'tcx> { Adjust::Borrow(AutoBorrow::RawPtr(mutability)) => { ExprKind::RawBorrow { mutability, arg: self.thir.exprs.push(expr) } } + Adjust::Borrow(AutoBorrow::Pin(mutbl)) => { + // expr = &mut target + 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/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index df86233c2b055..601deee0582fc 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -473,9 +473,9 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { PatKind::Deref { subpattern } => { fields = vec![self.lower_pat(subpattern).at_index(0)]; arity = 1; - ctor = match ty.pinned_ref() { - None if ty.is_ref() => Ref, - Some((inner_ty, _)) => { + ctor = match ty.maybe_pinned_ref() { + Some((_, ty::Pinnedness::Not, _, _)) => Ref, + Some((inner_ty, ty::Pinnedness::Pinned, _, _)) => { self.internal_state.has_lowered_deref_pat.set(true); DerefPattern(RevealedTy(inner_ty)) } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index 0a2442b71e78f..5d665d709f26a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -12,7 +12,7 @@ use rustc_hir::{ }; use rustc_middle::bug; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, DerefAdjustKind}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer}; use rustc_middle::ty::{ self, GenericArg, GenericArgKind, GenericArgsRef, InferConst, IsSuggestable, Term, TermKind, @@ -592,7 +592,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // first adjustment was not a builtin deref. let adjustment = match typeck_results.expr_adjustments(receiver) { [ - Adjustment { kind: Adjust::Deref(None), target: _ }, + Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: _ }, .., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), target: _ }, ] => "", diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 21385ee4fdc7f..3e46c370fb704 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -10,7 +10,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind, find_attr}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_middle::ty::{ self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt, TypeckResults, }; @@ -236,7 +236,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx .iter() .rev() .fold(0, |acc, adjustment| match adjustment.kind { - Adjust::Deref(Some(_)) => acc + 1, + Adjust::Deref(DerefAdjustKind::Overloaded(_)) => acc + 1, Adjust::Deref(_) if acc > 0 => acc + 1, _ => acc, }) diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 011cbf8c5d41c..cf1626507c63a 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -704,12 +704,12 @@ where let mut n_needed = 0; loop { if let Some(Adjustment { - kind: Adjust::Deref(overloaded_deref), + kind: Adjust::Deref(deref), target, }) = iter.next() { n_total += 1; - if overloaded_deref.is_some() { + if deref.is_overloaded() { n_needed = n_total; } ty = *target; diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs index 6c95c7b54c500..fc0789894cc2c 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs @@ -12,7 +12,7 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Closure, Expr, ExprKind, HirId, LetStmt, Mutability, UnOp}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter::OnlyBodies; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_span::Symbol; use rustc_span::symbol::sym; @@ -88,7 +88,7 @@ fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option, e: &hir::Expr<'_>, recv: &hir::Expr<'_ && cx.tcx.lang_items().clone_trait() == Some(trait_id) // no autoderefs && !cx.typeck_results().expr_adjustments(obj).iter() - .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) + .any(|a| matches!(a.kind, Adjust::Deref(DerefAdjustKind::Overloaded(..)))) { let obj_ty = cx.typeck_results().expr_ty(obj); if let ty::Ref(_, ty, mutability) = obj_ty.kind() { diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs index 3d489075ce8ac..1239d8927acfe 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs @@ -58,7 +58,7 @@ pub(super) fn check( .iter() .map(|x| &x.kind) .collect::>() - && let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj + && let [ty::adjustment::Adjust::Deref(ty::adjustment::DerefAdjustKind::Builtin), ty::adjustment::Adjust::Borrow(_)] = *adj && let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap() && let Some(method_name) = cx.tcx.get_diagnostic_name(method_did) { diff --git a/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs b/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs index e378cbd6ae0ad..0b8f6ae9f2779 100644 --- a/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs +++ b/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs @@ -4,7 +4,7 @@ use rustc_ast::BorrowKind; use rustc_errors::{Applicability, Diag}; use rustc_hir::{Expr, ExprKind, Node, QPath}; use rustc_lint::LateContext; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_span::sym; use super::SWAP_WITH_TEMPORARY; @@ -47,7 +47,7 @@ impl<'tcx> ArgKind<'tcx> { && let adjustments = cx.typeck_results().expr_adjustments(arg) && adjustments .first() - .is_some_and(|adj| matches!(adj.kind, Adjust::Deref(None))) + .is_some_and(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Builtin))) && adjustments .last() .is_some_and(|adj| matches!(adj.kind, Adjust::Borrow(_))) diff --git a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs index e67ba5c4d3148..98f319be7a457 100644 --- a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs +++ b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs @@ -4,7 +4,7 @@ use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::{self, ExistentialPredicate, Ty}; use rustc_span::{Span, sym}; @@ -58,7 +58,7 @@ pub(super) fn check(cx: &LateContext<'_>, receiver: &Expr<'_>, call_span: Span) |diag| { let derefs = recv_adjusts .iter() - .filter(|adj| matches!(adj.kind, Adjust::Deref(None))) + .filter(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Builtin))) .count(); diag.note( diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index a6a39cb6ab30e..04c1e12c5cb87 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -14,7 +14,7 @@ use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind, OverloadedDeref}; use rustc_middle::ty::{ self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty, }; @@ -78,7 +78,7 @@ fn check_addr_of_expr( // For matching uses of `Cow::from` [ Adjustment { - kind: Adjust::Deref(None), + kind: Adjust::Deref(DerefAdjustKind::Builtin), target: referent_ty, }, Adjustment { @@ -89,7 +89,7 @@ fn check_addr_of_expr( // For matching uses of arrays | [ Adjustment { - kind: Adjust::Deref(None), + kind: Adjust::Deref(DerefAdjustKind::Builtin), target: referent_ty, }, Adjustment { @@ -104,11 +104,11 @@ fn check_addr_of_expr( // For matching everything else | [ Adjustment { - kind: Adjust::Deref(None), + kind: Adjust::Deref(DerefAdjustKind::Builtin), target: referent_ty, }, Adjustment { - kind: Adjust::Deref(Some(OverloadedDeref { .. })), + kind: Adjust::Deref(DerefAdjustKind::Overloaded(OverloadedDeref { .. })), .. }, Adjustment { diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs index f852080d0f2a0..3737249a40cb0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs @@ -7,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{self as hir, LangItem, Node}; use rustc_lint::LateContext; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_span::{Span, Symbol, sym}; @@ -161,7 +161,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { && cx.tcx.lang_items().clone_trait().is_some_and(|id| id == trait_id) // no autoderefs && !cx.typeck_results().expr_adjustments(obj).iter() - .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) + .any(|a| matches!(a.kind, Adjust::Deref(DerefAdjustKind::Overloaded(..)))) && obj.res_local_id() == Some(local_id) { true diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 9b0008a29c6b4..f99748127a8f6 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -33,7 +33,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::mir::{ConstValue, UnevaluatedConst}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; use rustc_middle::ty::{ self, AliasTyKind, EarlyBinder, GenericArgs, GenericArgsRef, Instance, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeckResults, TypingEnv, @@ -907,7 +907,7 @@ fn does_adjust_borrow(adjust: &Adjustment<'_>) -> Option { match adjust.kind { Adjust::Borrow(_) => Some(BorrowCause::AutoBorrow), // Custom deref calls `::deref(&x)` resulting in a borrow. - Adjust::Deref(Some(_)) => Some(BorrowCause::AutoDeref), + Adjust::Deref(DerefAdjustKind::Overloaded(_)) => Some(BorrowCause::AutoDeref), // All other adjustments read the value. _ => None, } diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index 2bdd5739a5579..d184744162e4e 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -19,7 +19,7 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_span::Symbol; use std::{cmp, ops}; @@ -132,7 +132,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS .typeck_results() .expr_adjustments(e) .iter() - .any(|adj| matches!(adj.kind, Adjust::Deref(Some(_)))) + .any(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_)))) { self.eagerness |= NoChange; return; @@ -211,12 +211,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // Custom `Deref` impl might have side effects ExprKind::Unary(UnOp::Deref, e) - if self - .cx - .typeck_results() - .expr_ty(e) - .builtin_deref(true) - .is_none() => + if self.cx.typeck_results().expr_ty(e).builtin_deref(true).is_none() => { self.eagerness |= NoChange; }, diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 409f130134891..1e893b9b3d622 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -115,7 +115,7 @@ use rustc_middle::hir::nested_filter; use rustc_middle::hir::place::PlaceBase; use rustc_middle::lint::LevelAndSource; use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, PointerCoercion}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, DerefAdjustKind, PointerCoercion}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt, @@ -484,8 +484,8 @@ pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Optio .expr_adjustments(e) .iter() .find_map(|a| match a.kind { - Adjust::Deref(Some(d)) => Some(Some(d.mutbl)), - Adjust::Deref(None) => None, + Adjust::Deref(DerefAdjustKind::Overloaded(d)) => Some(Some(d.mutbl)), + Adjust::Deref(DerefAdjustKind::Builtin | DerefAdjustKind::Pin) => None, _ => Some(None), }) .and_then(|x| x) @@ -3535,7 +3535,9 @@ pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) cx.typeck_results().expr_adjustments(expr).iter().any(|adj| { matches!( adj.kind, - Adjust::Deref(Some(_)) | Adjust::Pointer(PointerCoercion::Unsize) | Adjust::NeverToAny + Adjust::Deref(DerefAdjustKind::Overloaded(_)) + | Adjust::Pointer(PointerCoercion::Unsize) + | Adjust::NeverToAny ) }) } diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index a90d64e972c1f..9fcf972ec6cdc 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -17,7 +17,7 @@ use rustc_lint::LateContext; use rustc_middle::mir::ConstValue; use rustc_middle::mir::interpret::Scalar; use rustc_middle::traits::EvaluationResult; -use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg, @@ -1327,6 +1327,7 @@ pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option { pub fn adjust_derefs_manually_drop<'tcx>(adjustments: &'tcx [Adjustment<'tcx>], mut ty: Ty<'tcx>) -> bool { adjustments.iter().any(|a| { let ty = mem::replace(&mut ty, a.target); - matches!(a.kind, Adjust::Deref(Some(op)) if op.mutbl == Mutability::Mut) && is_manually_drop(ty) + matches!(a.kind, Adjust::Deref(DerefAdjustKind::Overloaded(op)) if op.mutbl == Mutability::Mut) + && is_manually_drop(ty) }) } 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" + ); +}