Skip to content

Implement pin-project in pattern matching for &pin mut|const T #139751

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
25 changes: 19 additions & 6 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -719,14 +719,14 @@ pub struct PatField {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[derive(Encodable, Decodable, HashStable_Generic)]
pub enum ByRef {
Yes(Mutability),
Yes(Pinnedness, Mutability),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@traviscross Does it make sense to add Pinnedness here? It enables us to write ref pin mut|const binding modes, I think.

No,
}

impl ByRef {
#[must_use]
pub fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self {
if let ByRef::Yes(old_mutbl) = &mut self {
if let ByRef::Yes(_, old_mutbl) = &mut self {
*old_mutbl = cmp::min(*old_mutbl, mutbl);
}
self
Expand All @@ -744,20 +744,33 @@ pub struct BindingMode(pub ByRef, pub Mutability);

impl BindingMode {
pub const NONE: Self = Self(ByRef::No, Mutability::Not);
pub const REF: Self = Self(ByRef::Yes(Mutability::Not), Mutability::Not);
pub const REF: Self = Self(ByRef::Yes(Pinnedness::Not, Mutability::Not), Mutability::Not);
pub const REF_PIN: Self =
Self(ByRef::Yes(Pinnedness::Pinned, Mutability::Not), Mutability::Not);
pub const MUT: Self = Self(ByRef::No, Mutability::Mut);
pub const REF_MUT: Self = Self(ByRef::Yes(Mutability::Mut), Mutability::Not);
pub const MUT_REF: Self = Self(ByRef::Yes(Mutability::Not), Mutability::Mut);
pub const MUT_REF_MUT: Self = Self(ByRef::Yes(Mutability::Mut), Mutability::Mut);
pub const REF_MUT: Self = Self(ByRef::Yes(Pinnedness::Not, Mutability::Mut), Mutability::Not);
pub const REF_PIN_MUT: Self =
Self(ByRef::Yes(Pinnedness::Pinned, Mutability::Mut), Mutability::Not);
pub const MUT_REF: Self = Self(ByRef::Yes(Pinnedness::Not, Mutability::Not), Mutability::Mut);
pub const MUT_REF_PIN: Self =
Self(ByRef::Yes(Pinnedness::Pinned, Mutability::Not), Mutability::Mut);
pub const MUT_REF_MUT: Self =
Self(ByRef::Yes(Pinnedness::Not, Mutability::Mut), Mutability::Mut);
pub const MUT_REF_PIN_MUT: Self =
Self(ByRef::Yes(Pinnedness::Pinned, Mutability::Mut), Mutability::Mut);

pub fn prefix_str(self) -> &'static str {
match self {
Self::NONE => "",
Self::REF => "ref ",
Self::REF_PIN => "ref pin const ",
Self::MUT => "mut ",
Self::REF_MUT => "ref mut ",
Self::REF_PIN_MUT => "ref pin mut ",
Self::MUT_REF => "mut ref ",
Self::MUT_REF_PIN => "mut ref pin ",
Self::MUT_REF_MUT => "mut ref mut ",
Self::MUT_REF_PIN_MUT => "mut ref pin mut ",
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_ast_ir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,10 @@ pub enum Pinnedness {
Not,
Pinned,
}

impl Pinnedness {
/// Return `true` if self is pinned
pub fn is_pinned(self) -> bool {
matches!(self, Self::Pinned)
}
}
7 changes: 6 additions & 1 deletion compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1698,10 +1698,15 @@ impl<'a> State<'a> {
if mutbl.is_mut() {
self.word_nbsp("mut");
}
if let ByRef::Yes(rmutbl) = by_ref {
if let ByRef::Yes(pinnedness, rmutbl) = by_ref {
self.word_nbsp("ref");
if pinnedness.is_pinned() {
self.word_nbsp("pin");
}
if rmutbl.is_mut() {
self.word_nbsp("mut");
} else if pinnedness.is_pinned() {
self.word_nbsp("const");
}
}
self.print_ident(*ident);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1225,7 +1225,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}

LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
binding_mode: BindingMode(ByRef::Yes(_), _),
binding_mode: BindingMode(ByRef::Yes(..), _),
..
})) => {
let pattern_span: Span = local_decl.source_info.span;
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2530,6 +2530,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
_ => bug!("Deref of unexpected type: {:?}", base_ty),
}
}
// Check as the inner reference type if it is a field projection
// from the `&pin` pattern
ProjectionElem::Field(FieldIdx::ZERO, _)
if let Some(adt) =
place_base.ty(self.body(), self.infcx.tcx).ty.ty_adt_def()
&& adt.is_pin()
&& self.infcx.tcx.features().pin_ergonomics() =>
{
self.is_mutable(place_base, is_local_mutation_allowed)
}
// All other projections are owned by their base path, so mutable if
// base path is mutable
ProjectionElem::Field(..)
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rustc_ast::{
pub use rustc_ast::{
AssignOp, AssignOpKind, AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind,
BoundConstness, BoundPolarity, ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto,
MetaItemInner, MetaItemLit, Movability, Mutability, UnOp,
MetaItemInner, MetaItemLit, Movability, Mutability, Pinnedness, UnOp,
};
use rustc_attr_data_structures::AttributeKind;
use rustc_data_structures::fingerprint::Fingerprint;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/check/region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,7 @@ fn resolve_local<'tcx>(
// & expression, and its lifetime would be extended to the end of the block (due
// to a different rule, not the below code).
match pat.kind {
PatKind::Binding(hir::BindingMode(hir::ByRef::Yes(_), _), ..) => true,
PatKind::Binding(hir::BindingMode(hir::ByRef::Yes(..), _), ..) => true,

PatKind::Struct(_, field_pats, _) => field_pats.iter().any(|fp| is_binding_pat(fp.pat)),

Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_hir_pretty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1906,10 +1906,15 @@ impl<'a> State<'a> {
if mutbl.is_mut() {
self.word_nbsp("mut");
}
if let ByRef::Yes(rmutbl) = by_ref {
if let ByRef::Yes(pinnedness, rmutbl) = by_ref {
self.word_nbsp("ref");
if pinnedness.is_pinned() {
self.word_nbsp("pin");
}
if rmutbl.is_mut() {
self.word_nbsp("mut");
} else if pinnedness.is_pinned() {
self.word_nbsp("const");
}
}
self.print_ident(ident);
Expand Down
56 changes: 39 additions & 17 deletions compiler/rustc_hir_typeck/src/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -985,7 +985,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
// of the pattern, as this just looks confusing, instead use the span
// of the discriminant.
match bm.0 {
hir::ByRef::Yes(m) => {
hir::ByRef::Yes(_, m) => {
let bk = ty::BorrowKind::from_mutbl(m);
self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk);
}
Expand All @@ -1003,7 +1003,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
// Deref patterns on boxes don't borrow, so we ignore them here.
// HACK: this could be a fake pattern corresponding to a deref inserted by match
// ergonomics, in which case `pat.hir_id` will be the id of the subpattern.
if let hir::ByRef::Yes(mutability) =
if let hir::ByRef::Yes(_, mutability) =
self.cx.typeck_results().deref_pat_borrow_mode(place.place.ty(), subpattern)
{
let bk = ty::BorrowKind::from_mutbl(mutability);
Expand Down Expand Up @@ -1255,22 +1255,34 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
.get(pat.hir_id)
.expect("missing binding mode");

if matches!(bm.0, hir::ByRef::Yes(_)) {
// a bind-by-ref means that the base_ty will be the type of the ident itself,
// but what we want here is the type of the underlying value being borrowed.
// So peel off one-level, turning the &T into T.
match self.cx.structurally_resolve_type(pat.span, base_ty).builtin_deref(false)
{
Some(ty) => Ok(ty),
None => {
debug!("By-ref binding of non-derefable type");
Err(self
.cx
.report_bug(pat.span, "by-ref binding of non-derefable type"))
match bm.0 {
hir::ByRef::Yes(pinnedness, _) => {
let base_ty = if pinnedness.is_pinned() {
base_ty.pinned_ty().ok_or_else(|| {
debug!("By-pin-ref binding of non-`Pin` type");
self.cx.report_bug(pat.span, "by-pin-ref binding of non-`Pin` type")
})?
} else {
base_ty
};
// a bind-by-ref means that the base_ty will be the type of the ident itself,
// but what we want here is the type of the underlying value being borrowed.
// So peel off one-level, turning the &T into T.
match self
.cx
.structurally_resolve_type(pat.span, base_ty)
.builtin_deref(false)
{
Some(ty) => Ok(ty),
None => {
debug!("By-ref binding of non-derefable type");
Err(self
.cx
.report_bug(pat.span, "by-ref binding of non-derefable type"))
}
}
}
} else {
Ok(base_ty)
_ => Ok(base_ty),
}
}
_ => Ok(base_ty),
Expand Down Expand Up @@ -1696,6 +1708,16 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
};
self.pat_deref_place(pat.hir_id, place_with_id, pat, target_ty)?
}
adjustment::PatAdjust::PinDeref => {
let target_ty = match adjusts.peek() {
Some(&&next_adjust) => next_adjust.source,
// At the end of the deref chain, we get `pat`'s scrutinee.
None => self.pat_ty_unadjusted(pat)?,
};
let kind = ProjectionKind::Field(FieldIdx::ZERO, FIRST_VARIANT);
place_with_id = self.cat_projection(pat.hir_id, place_with_id, target_ty, kind);
self.cat_deref(pat.hir_id, place_with_id)?
}
};
}
drop(typeck_results); // explicitly release borrow of typeck results, just in case.
Expand Down Expand Up @@ -1867,7 +1889,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
// Deref patterns on boxes are lowered using a built-in deref.
hir::ByRef::No => self.cat_deref(hir_id, base_place),
// For other types, we create a temporary to match on.
hir::ByRef::Yes(mutability) => {
hir::ByRef::Yes(_, mutability) => {
let re_erased = self.cx.tcx().lifetimes.re_erased;
let ty = Ty::new_ref(self.cx.tcx(), re_erased, target_ty, mutability);
// A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ...
Expand Down
Loading
Loading