Skip to content

Commit

Permalink
Auto merge of #43399 - tschottdorf:bndmode-pat-adjustments, r=nikomat…
Browse files Browse the repository at this point in the history
…sakis

default binding modes: add pat_binding_modes

This PR kicks off the implementation of the [default binding modes RFC][1] by
introducing the `pat_binding_modes` typeck table mentioned in the [mentoring
instructions][2].

It is a WIP because I wasn't able to avoid all uses of the binding modes as
not all call sites are close enough to the typeck tables. I added marker
comments to any line matching `BindByRef|BindByValue` so that reviewers
are aware of all of them.

I will look into changing the HIR (as suggested in [2]) to not carry a
`BindingMode` unless one was explicitly specified, but this PR is good for
a first round of comments.

The actual changes are quite small and CI will fail due to overlong lines
caused by the marker comments.

See #42640.

cc @nikomatsakis

[1]: rust-lang/rfcs#2005
[2]: #42640 (comment)
  • Loading branch information
bors committed Jul 31, 2017
2 parents 2a6828e + 8f67f1e commit 37c7d0e
Show file tree
Hide file tree
Showing 22 changed files with 311 additions and 106 deletions.
22 changes: 13 additions & 9 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2191,7 +2191,7 @@ impl<'a> LoweringContext<'a> {
let next_ident = self.str_to_ident("__next");
let next_pat = self.pat_ident_binding_mode(e.span,
next_ident,
hir::BindByValue(hir::MutMutable));
hir::BindingAnnotation::Mutable);

// `::std::option::Option::Some(val) => next = val`
let pat_arm = {
Expand All @@ -2215,8 +2215,9 @@ impl<'a> LoweringContext<'a> {
};

// `mut iter`
let iter_pat = self.pat_ident_binding_mode(e.span, iter,
hir::BindByValue(hir::MutMutable));
let iter_pat = self.pat_ident_binding_mode(e.span,
iter,
hir::BindingAnnotation::Mutable);

// `match ::std::iter::Iterator::next(&mut iter) { ... }`
let match_expr = {
Expand Down Expand Up @@ -2503,10 +2504,13 @@ impl<'a> LoweringContext<'a> {
}
}

fn lower_binding_mode(&mut self, b: &BindingMode) -> hir::BindingMode {
fn lower_binding_mode(&mut self, b: &BindingMode) -> hir::BindingAnnotation {
match *b {
BindingMode::ByRef(m) => hir::BindByRef(self.lower_mutability(m)),
BindingMode::ByValue(m) => hir::BindByValue(self.lower_mutability(m)),
BindingMode::ByValue(Mutability::Immutable) =>
hir::BindingAnnotation::Unannotated,
BindingMode::ByRef(Mutability::Immutable) => hir::BindingAnnotation::Ref,
BindingMode::ByValue(Mutability::Mutable) => hir::BindingAnnotation::Mutable,
BindingMode::ByRef(Mutability::Mutable) => hir::BindingAnnotation::RefMut,
}
}

Expand Down Expand Up @@ -2647,7 +2651,7 @@ impl<'a> LoweringContext<'a> {
fn stmt_let(&mut self, sp: Span, mutbl: bool, ident: Name, ex: P<hir::Expr>)
-> (hir::Stmt, NodeId) {
let pat = if mutbl {
self.pat_ident_binding_mode(sp, ident, hir::BindByValue(hir::MutMutable))
self.pat_ident_binding_mode(sp, ident, hir::BindingAnnotation::Mutable)
} else {
self.pat_ident(sp, ident)
};
Expand Down Expand Up @@ -2703,10 +2707,10 @@ impl<'a> LoweringContext<'a> {
}

fn pat_ident(&mut self, span: Span, name: Name) -> P<hir::Pat> {
self.pat_ident_binding_mode(span, name, hir::BindByValue(hir::MutImmutable))
self.pat_ident_binding_mode(span, name, hir::BindingAnnotation::Unannotated)
}

fn pat_ident_binding_mode(&mut self, span: Span, name: Name, bm: hir::BindingMode)
fn pat_ident_binding_mode(&mut self, span: Span, name: Name, bm: hir::BindingAnnotation)
-> P<hir::Pat> {
let id = self.next_id();
let parent_def = self.parent_def.unwrap();
Expand Down
27 changes: 22 additions & 5 deletions src/librustc/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

// The Rust HIR.

pub use self::BindingMode::*;
pub use self::BinOp_::*;
pub use self::BlockCheckMode::*;
pub use self::CaptureClause::*;
Expand Down Expand Up @@ -628,10 +627,28 @@ pub struct FieldPat {
pub is_shorthand: bool,
}

/// Explicit binding annotations given in the HIR for a binding. Note
/// that this is not the final binding *mode* that we infer after type
/// inference.
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
pub enum BindingMode {
BindByRef(Mutability),
BindByValue(Mutability),
pub enum BindingAnnotation {
/// No binding annotation given: this means that the final binding mode
/// will depend on whether we have skipped through a `&` reference
/// when matching. For example, the `x` in `Some(x)` will have binding
/// mode `None`; if you do `let Some(x) = &Some(22)`, it will
/// ultimately be inferred to be by-reference.
///
/// Note that implicit reference skipping is not implemented yet (#42640).
Unannotated,

/// Annotated with `mut x` -- could be either ref or not, similar to `None`.
Mutable,

/// Annotated as `ref`, like `ref x`
Ref,

/// Annotated as `ref mut x`.
RefMut,
}

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
Expand All @@ -647,7 +664,7 @@ pub enum PatKind {

/// A fresh binding `ref mut binding @ OPT_SUBPATTERN`.
/// The `DefId` is for the definition of the variable being bound.
Binding(BindingMode, DefId, Spanned<Name>, Option<P<Pat>>),
Binding(BindingAnnotation, DefId, Spanned<Name>, Option<P<Pat>>),

/// A struct or struct variant pattern, e.g. `Variant {x, y, ..}`.
/// The `bool` is `true` in the presence of a `..`.
Expand Down
40 changes: 23 additions & 17 deletions src/librustc/hir/pat_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl hir::Pat {
/// Call `f` on every "binding" in a pattern, e.g., on `a` in
/// `match foo() { Some(a) => (), None => () }`
pub fn each_binding<F>(&self, mut f: F)
where F: FnMut(hir::BindingMode, ast::NodeId, Span, &Spanned<ast::Name>),
where F: FnMut(hir::BindingAnnotation, ast::NodeId, Span, &Spanned<ast::Name>),
{
self.walk(|p| {
if let PatKind::Binding(binding_mode, _, ref pth, _) = p.node {
Expand Down Expand Up @@ -130,12 +130,10 @@ impl hir::Pat {

pub fn simple_name(&self) -> Option<ast::Name> {
match self.node {
PatKind::Binding(hir::BindByValue(..), _, ref path1, None) => {
Some(path1.node)
}
_ => {
None
}
PatKind::Binding(hir::BindingAnnotation::Unannotated, _, ref path1, None) |
PatKind::Binding(hir::BindingAnnotation::Mutable, _, ref path1, None) =>
Some(path1.node),
_ => None,
}
}

Expand Down Expand Up @@ -163,16 +161,22 @@ impl hir::Pat {
}

/// Checks if the pattern contains any `ref` or `ref mut` bindings,
/// and if yes whether its containing mutable ones or just immutables ones.
pub fn contains_ref_binding(&self) -> Option<hir::Mutability> {
/// and if yes whether it contains mutable or just immutables ones.
///
/// FIXME(tschottdorf): this is problematic as the HIR is being scraped,
/// but ref bindings may be implicit after #42640.
pub fn contains_explicit_ref_binding(&self) -> Option<hir::Mutability> {
let mut result = None;
self.each_binding(|mode, _, _, _| {
if let hir::BindingMode::BindByRef(m) = mode {
// Pick Mutable as maximum
match result {
None | Some(hir::MutImmutable) => result = Some(m),
_ => (),
self.each_binding(|annotation, _, _, _| {
match annotation {
hir::BindingAnnotation::Ref => {
match result {
None | Some(hir::MutImmutable) => result = Some(hir::MutImmutable),
_ => (),
}
}
hir::BindingAnnotation::RefMut => result = Some(hir::MutMutable),
_ => (),
}
});
result
Expand All @@ -182,9 +186,11 @@ impl hir::Pat {
impl hir::Arm {
/// Checks if the patterns for this arm contain any `ref` or `ref mut`
/// bindings, and if yes whether its containing mutable ones or just immutables ones.
pub fn contains_ref_binding(&self) -> Option<hir::Mutability> {
pub fn contains_explicit_ref_binding(&self) -> Option<hir::Mutability> {
// FIXME(tschottdorf): contains_explicit_ref_binding() must be removed
// for #42640.
self.pats.iter()
.filter_map(|pat| pat.contains_ref_binding())
.filter_map(|pat| pat.contains_explicit_ref_binding())
.max_by_key(|m| match *m {
hir::MutMutable => 1,
hir::MutImmutable => 0,
Expand Down
12 changes: 8 additions & 4 deletions src/librustc/hir/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1651,12 +1651,16 @@ impl<'a> State<'a> {
PatKind::Wild => self.s.word("_")?,
PatKind::Binding(binding_mode, _, ref path1, ref sub) => {
match binding_mode {
hir::BindByRef(mutbl) => {
hir::BindingAnnotation::Ref => {
self.word_nbsp("ref")?;
self.print_mutability(mutbl)?;
self.print_mutability(hir::MutImmutable)?;
}
hir::BindByValue(hir::MutImmutable) => {}
hir::BindByValue(hir::MutMutable) => {
hir::BindingAnnotation::RefMut => {
self.word_nbsp("ref")?;
self.print_mutability(hir::MutMutable)?;
}
hir::BindingAnnotation::Unannotated => {}
hir::BindingAnnotation::Mutable => {
self.word_nbsp("mut")?;
}
}
Expand Down
8 changes: 5 additions & 3 deletions src/librustc/ich/impls_hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,9 +442,11 @@ impl_stable_hash_for!(struct hir::FieldPat {
is_shorthand
});

impl_stable_hash_for!(enum hir::BindingMode {
BindByRef(mutability),
BindByValue(mutability)
impl_stable_hash_for!(enum hir::BindingAnnotation {
Unannotated,
Mutable,
Ref,
RefMut
});

impl_stable_hash_for!(enum hir::RangeEnd {
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/ich/impls_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ for ty::TypeckTables<'tcx> {
ref node_types,
ref node_substs,
ref adjustments,
ref pat_binding_modes,
ref upvar_capture_map,
ref closure_tys,
ref closure_kinds,
Expand All @@ -636,6 +637,7 @@ for ty::TypeckTables<'tcx> {
ich::hash_stable_nodemap(hcx, hasher, node_types);
ich::hash_stable_nodemap(hcx, hasher, node_substs);
ich::hash_stable_nodemap(hcx, hasher, adjustments);
ich::hash_stable_nodemap(hcx, hasher, pat_binding_modes);
ich::hash_stable_hashmap(hcx, hasher, upvar_capture_map, |hcx, up_var_id| {
let ty::UpvarId {
var_id,
Expand Down
28 changes: 16 additions & 12 deletions src/librustc/middle/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -796,16 +796,19 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
debug!("determine_pat_move_mode cmt_discr={:?} pat={:?}", cmt_discr,
pat);
return_if_err!(self.mc.cat_pattern(cmt_discr, pat, |cmt_pat, pat| {
match pat.node {
PatKind::Binding(hir::BindByRef(..), ..) =>
mode.lub(BorrowingMatch),
PatKind::Binding(hir::BindByValue(..), ..) => {
match copy_or_move(&self.mc, self.param_env, &cmt_pat, PatBindingMove) {
Copy => mode.lub(CopyingMatch),
Move(..) => mode.lub(MovingMatch),
if let PatKind::Binding(..) = pat.node {
let bm = *self.mc.tables.pat_binding_modes.get(&pat.id)
.expect("missing binding mode");
match bm {
ty::BindByReference(..) =>
mode.lub(BorrowingMatch),
ty::BindByValue(..) => {
match copy_or_move(&self.mc, self.param_env, &cmt_pat, PatBindingMove) {
Copy => mode.lub(CopyingMatch),
Move(..) => mode.lub(MovingMatch),
}
}
}
_ => {}
}
}));
}
Expand All @@ -818,8 +821,9 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {

let ExprUseVisitor { ref mc, ref mut delegate, param_env } = *self;
return_if_err!(mc.cat_pattern(cmt_discr.clone(), pat, |cmt_pat, pat| {
if let PatKind::Binding(bmode, def_id, ..) = pat.node {
if let PatKind::Binding(_, def_id, ..) = pat.node {
debug!("binding cmt_pat={:?} pat={:?} match_mode={:?}", cmt_pat, pat, match_mode);
let bm = *mc.tables.pat_binding_modes.get(&pat.id).expect("missing binding mode");

// pat_ty: the type of the binding being produced.
let pat_ty = return_if_err!(mc.node_ty(pat.id));
Expand All @@ -832,14 +836,14 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
}

// It is also a borrow or copy/move of the value being matched.
match bmode {
hir::BindByRef(m) => {
match bm {
ty::BindByReference(m) => {
if let ty::TyRef(r, _) = pat_ty.sty {
let bk = ty::BorrowKind::from_mutbl(m);
delegate.borrow(pat.id, pat.span, cmt_pat, r, bk, RefBinding);
}
}
hir::BindByValue(..) => {
ty::BindByValue(..) => {
let mode = copy_or_move(mc, param_env, &cmt_pat, PatBindingMove);
debug!("walk_pat binding consuming pat");
delegate.consume_pat(pat, cmt_pat, mode);
Expand Down
34 changes: 20 additions & 14 deletions src/librustc/middle/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,11 +330,12 @@ impl MutabilityCategory {
ret
}

fn from_local(tcx: TyCtxt, id: ast::NodeId) -> MutabilityCategory {
fn from_local(tcx: TyCtxt, tables: &ty::TypeckTables, id: ast::NodeId) -> MutabilityCategory {
let ret = match tcx.hir.get(id) {
hir_map::NodeLocal(p) => match p.node {
PatKind::Binding(bind_mode, ..) => {
if bind_mode == hir::BindByValue(hir::MutMutable) {
PatKind::Binding(..) => {
let bm = *tables.pat_binding_modes.get(&p.id).expect("missing binding mode");
if bm == ty::BindByValue(hir::MutMutable) {
McDeclared
} else {
McImmutable
Expand Down Expand Up @@ -475,16 +476,21 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
// *being borrowed* is. But ideally we would put in a more
// fundamental fix to this conflated use of the node id.
let ret_ty = match pat.node {
PatKind::Binding(hir::BindByRef(_), ..) => {
// 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 base_ty.builtin_deref(false, ty::NoPreference) {
Some(t) => t.ty,
None => {
debug!("By-ref binding of non-derefable type {:?}", base_ty);
return Err(());
PatKind::Binding(..) => {
let bm = *self.tables.pat_binding_modes.get(&pat.id).expect("missing binding mode");
if let ty::BindByReference(_) = bm {
// 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 base_ty.builtin_deref(false, ty::NoPreference) {
Some(t) => t.ty,
None => {
debug!("By-ref binding of non-derefable type {:?}", base_ty);
return Err(());
}
}
} else {
base_ty
}
}
_ => base_ty,
Expand Down Expand Up @@ -659,7 +665,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
id,
span,
cat: Categorization::Local(vid),
mutbl: MutabilityCategory::from_local(self.tcx, vid),
mutbl: MutabilityCategory::from_local(self.tcx, self.tables, vid),
ty: expr_ty,
note: NoteNone
}))
Expand Down Expand Up @@ -711,7 +717,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
let var_ty = self.node_ty(var_id)?;

// Mutability of original variable itself
let var_mutbl = MutabilityCategory::from_local(self.tcx, var_id);
let var_mutbl = MutabilityCategory::from_local(self.tcx, self.tables, var_id);

// Construct the upvar. This represents access to the field
// from the environment (perhaps we should eventually desugar
Expand Down
26 changes: 25 additions & 1 deletion src/librustc/middle/region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -889,8 +889,32 @@ fn resolve_local<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>,
/// | ( ..., P&, ... )
/// | box P&
fn is_binding_pat(pat: &hir::Pat) -> bool {
// Note that the code below looks for *explicit* refs only, that is, it won't
// know about *implicit* refs as introduced in #42640.
//
// This is not a problem. For example, consider
//
// let (ref x, ref y) = (Foo { .. }, Bar { .. });
//
// Due to the explicit refs on the left hand side, the below code would signal
// that the temporary value on the right hand side should live until the end of
// the enclosing block (as opposed to being dropped after the let is complete).
//
// To create an implicit ref, however, you must have a borrowed value on the RHS
// already, as in this example (which won't compile before #42640):
//
// let Foo { x, .. } = &Foo { x: ..., ... };
//
// in place of
//
// let Foo { ref x, .. } = Foo { ... };
//
// In the former case (the implicit ref version), the temporary is created by the
// & expression, and its lifetime would be extended to the end of the block (due
// to a different rule, not the below code).
match pat.node {
PatKind::Binding(hir::BindByRef(_), ..) => true,
PatKind::Binding(hir::BindingAnnotation::Ref, ..) |
PatKind::Binding(hir::BindingAnnotation::RefMut, ..) => true,

PatKind::Struct(_, ref field_pats, _) => {
field_pats.iter().any(|fp| is_binding_pat(&fp.node.pat))
Expand Down
Loading

0 comments on commit 37c7d0e

Please sign in to comment.