Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions compiler/rustc_hir_analysis/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
26 changes: 16 additions & 10 deletions compiler/rustc_hir_typeck/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 })
Expand Down
74 changes: 70 additions & 4 deletions compiler/rustc_hir_typeck/src/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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),
Expand All @@ -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),
Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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,
)
Expand Down
19 changes: 15 additions & 4 deletions compiler/rustc_hir_typeck/src/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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<T>`, 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);
}
Expand Down Expand Up @@ -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),
);
}
}
}

Expand Down Expand Up @@ -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,
Expand Down
11 changes: 8 additions & 3 deletions compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -266,17 +268,20 @@ 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,
overloaded_deref.method_call(self.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.
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_hir_typeck/src/place_op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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,
Expand Down
12 changes: 8 additions & 4 deletions compiler/rustc_lint/src/autorefs.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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,
}
}
7 changes: 5 additions & 2 deletions compiler/rustc_lint/src/noop_method_call.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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;
}

Expand Down
25 changes: 24 additions & 1 deletion compiler/rustc_middle/src/ty/adjustment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,37 @@ pub enum Adjust {
NeverToAny,

/// Dereference once, producing a place.
Deref(Option<OverloadedDeref>),
Deref(DerefAdjustKind),

/// Take the address and produce either a `&` or `*` pointer.
Borrow(AutoBorrow),

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
Expand Down Expand Up @@ -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
Expand Down
Loading
Loading