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].

`pat_binding_modes` is populated in `librustc_typeck/check/_match.rs` and
used wherever the HIR would be scraped prior to this PR. Unfortunately, one
blemish, namely a two callers to `contains_explicit_ref_binding`, remains.
This will likely have to be removed when the second part of [1], the
`pat_adjustments` table, is tackled. Appropriate comments have been added.

See #42640.

[1]: https://github.com/rust-lang/rfcs/pull/2005
[2]: https://github.com/rust-lang/rust/issues/42640#issuecomment-313535089
This commit is contained in:
Tobias Schottdorf 2017-07-21 19:29:43 -04:00
parent 5c71e4ef90
commit 851c77088d
22 changed files with 268 additions and 106 deletions

View File

@ -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 = {
@ -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 = {
@ -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,
}
}
@ -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)
};
@ -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();

View File

@ -10,7 +10,6 @@
// The Rust HIR.
pub use self::BindingMode::*;
pub use self::BinOp_::*;
pub use self::BlockCheckMode::*;
pub use self::CaptureClause::*;
@ -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)]
@ -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 `..`.

View File

@ -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 {
@ -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,
}
}
@ -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
@ -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,

View File

@ -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")?;
}
}

View File

@ -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 {

View File

@ -617,6 +617,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,
@ -637,6 +638,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,

View File

@ -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),
}
}
}
_ => {}
}
}));
}
@ -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));
@ -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);

View File

@ -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
@ -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,
@ -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
}))
@ -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

View File

@ -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))

View File

@ -0,0 +1,35 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use hir::BindingAnnotation::*;
use hir::BindingAnnotation;
use hir::Mutability;
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
pub enum BindingMode {
BindByReference(Mutability),
BindByValue(Mutability),
}
impl BindingMode {
pub fn convert(ba: BindingAnnotation) -> BindingMode {
match ba {
Unannotated => BindingMode::BindByValue(Mutability::MutImmutable),
Mutable => BindingMode::BindByValue(Mutability::MutMutable),
Ref => BindingMode::BindByReference(Mutability::MutImmutable),
RefMut => BindingMode::BindByReference(Mutability::MutMutable),
}
}
}
impl_stable_hash_for!(enum self::BindingMode {
BindByReference(mutability),
BindByValue(mutability)
});

View File

@ -40,6 +40,7 @@ use ty::layout::{Layout, TargetDataLayout};
use ty::inhabitedness::DefIdForest;
use ty::maps;
use ty::steal::Steal;
use ty::BindingMode;
use util::nodemap::{NodeMap, NodeSet, DefIdSet};
use util::nodemap::{FxHashMap, FxHashSet};
use rustc_data_structures::accumulate_vec::AccumulateVec;
@ -223,6 +224,9 @@ pub struct TypeckTables<'tcx> {
pub adjustments: NodeMap<Vec<ty::adjustment::Adjustment<'tcx>>>,
// Stores the actual binding mode for all instances of hir::BindingAnnotation.
pub pat_binding_modes: NodeMap<BindingMode>,
/// Borrows
pub upvar_capture_map: ty::UpvarCaptureMap<'tcx>,
@ -274,6 +278,7 @@ impl<'tcx> TypeckTables<'tcx> {
node_types: FxHashMap(),
node_substs: NodeMap(),
adjustments: NodeMap(),
pat_binding_modes: NodeMap(),
upvar_capture_map: FxHashMap(),
closure_tys: NodeMap(),
closure_kinds: NodeMap(),

View File

@ -74,6 +74,9 @@ pub use self::sty::InferTy::*;
pub use self::sty::RegionKind::*;
pub use self::sty::TypeVariants::*;
pub use self::binding::BindingMode;
pub use self::binding::BindingMode::*;
pub use self::context::{TyCtxt, GlobalArenas, tls};
pub use self::context::{Lift, TypeckTables};
@ -84,6 +87,7 @@ pub use self::trait_def::TraitDef;
pub use self::maps::queries;
pub mod adjustment;
pub mod binding;
pub mod cast;
pub mod error;
pub mod fast_reject;

View File

@ -871,14 +871,15 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
}
}
fn local_binding_mode(&self, node_id: ast::NodeId) -> hir::BindingMode {
fn local_binding_mode(&self, node_id: ast::NodeId) -> ty::BindingMode {
let pat = match self.tcx.hir.get(node_id) {
hir_map::Node::NodeLocal(pat) => pat,
node => bug!("bad node for local: {:?}", node)
};
match pat.node {
hir::PatKind::Binding(mode, ..) => mode,
hir::PatKind::Binding(..) =>
*self.tables.pat_binding_modes.get(&pat.id).expect("missing binding mode"),
_ => bug!("local is not a binding: {:?}", pat)
}
}
@ -913,7 +914,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
Some(ImmutabilityBlame::ClosureEnv(_)) => {}
Some(ImmutabilityBlame::ImmLocal(node_id)) => {
let let_span = self.tcx.hir.span(node_id);
if let hir::BindingMode::BindByValue(..) = self.local_binding_mode(node_id) {
if let ty::BindByValue(..) = self.local_binding_mode(node_id) {
if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(let_span) {
let (_, is_implicit_self) = self.local_ty(node_id);
if is_implicit_self && snippet != "self" {
@ -930,7 +931,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
Some(ImmutabilityBlame::LocalDeref(node_id)) => {
let let_span = self.tcx.hir.span(node_id);
match self.local_binding_mode(node_id) {
hir::BindingMode::BindByRef(..) => {
ty::BindByReference(..) => {
let snippet = self.tcx.sess.codemap().span_to_snippet(let_span);
if let Ok(snippet) = snippet {
db.span_label(
@ -940,7 +941,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
);
}
}
hir::BindingMode::BindByValue(..) => {
ty::BindByValue(..) => {
if let (Some(local_ty), is_implicit_self) = self.local_ty(node_id) {
if let Some(msg) =
self.suggest_mut_for_immutable(local_ty, is_implicit_self) {

View File

@ -268,7 +268,12 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
fn check_for_bindings_named_the_same_as_variants(cx: &MatchVisitor, pat: &Pat) {
pat.walk(|p| {
if let PatKind::Binding(hir::BindByValue(hir::MutImmutable), _, name, None) = p.node {
if let PatKind::Binding(_, _, name, None) = p.node {
let bm = *cx.tables.pat_binding_modes.get(&p.id).expect("missing binding mode");
if bm != ty::BindByValue(hir::MutImmutable) {
// Nothing to check.
return true;
}
let pat_ty = cx.tables.pat_ty(p);
if let ty::TyAdt(edef, _) = pat_ty.sty {
if edef.is_enum() && edef.variants.iter().any(|variant| {
@ -452,8 +457,9 @@ fn check_legality_of_move_bindings(cx: &MatchVisitor,
pats: &[P<Pat>]) {
let mut by_ref_span = None;
for pat in pats {
pat.each_binding(|bm, _, span, _path| {
if let hir::BindByRef(..) = bm {
pat.each_binding(|_, id, span, _path| {
let bm = *cx.tables.pat_binding_modes.get(&id).expect("missing binding mode");
if let ty::BindByReference(..) = bm {
by_ref_span = Some(span);
}
})
@ -484,10 +490,16 @@ fn check_legality_of_move_bindings(cx: &MatchVisitor,
for pat in pats {
pat.walk(|p| {
if let PatKind::Binding(hir::BindByValue(..), _, _, ref sub) = p.node {
let pat_ty = cx.tables.node_id_to_type(p.id);
if pat_ty.moves_by_default(cx.tcx, cx.param_env, pat.span) {
check_move(p, sub.as_ref().map(|p| &**p));
if let PatKind::Binding(_, _, _, ref sub) = p.node {
let bm = *cx.tables.pat_binding_modes.get(&p.id).expect("missing binding mode");
match bm {
ty::BindByValue(..) => {
let pat_ty = cx.tables.node_id_to_type(p.id);
if pat_ty.moves_by_default(cx.tcx, cx.param_env, pat.span) {
check_move(p, sub.as_ref().map(|p| &**p));
}
}
_ => {}
}
}
true

View File

@ -374,27 +374,31 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
}
}
PatKind::Binding(bm, def_id, ref ident, ref sub) => {
PatKind::Binding(_, def_id, ref ident, ref sub) => {
let id = self.tcx.hir.as_local_node_id(def_id).unwrap();
let var_ty = self.tables.node_id_to_type(pat.id);
let region = match var_ty.sty {
ty::TyRef(r, _) => Some(r),
_ => None,
};
let bm = *self.tables.pat_binding_modes.get(&pat.id)
.expect("missing binding mode");
let (mutability, mode) = match bm {
hir::BindByValue(hir::MutMutable) =>
ty::BindByValue(hir::MutMutable) =>
(Mutability::Mut, BindingMode::ByValue),
hir::BindByValue(hir::MutImmutable) =>
ty::BindByValue(hir::MutImmutable) =>
(Mutability::Not, BindingMode::ByValue),
hir::BindByRef(hir::MutMutable) =>
(Mutability::Not, BindingMode::ByRef(region.unwrap(), BorrowKind::Mut)),
hir::BindByRef(hir::MutImmutable) =>
(Mutability::Not, BindingMode::ByRef(region.unwrap(), BorrowKind::Shared)),
ty::BindByReference(hir::MutMutable) =>
(Mutability::Not, BindingMode::ByRef(
region.unwrap(), BorrowKind::Mut)),
ty::BindByReference(hir::MutImmutable) =>
(Mutability::Not, BindingMode::ByRef(
region.unwrap(), BorrowKind::Shared)),
};
// A ref x pattern is the same node used for x, and as such it has
// x's type, which is &T, where we want T (the type being matched).
if let hir::BindByRef(_) = bm {
if let ty::BindByReference(_) = bm {
if let ty::TyRef(_, mt) = ty.sty {
ty = mt.ty;
} else {

View File

@ -44,9 +44,13 @@ impl UnusedMut {
let mut mutables = FxHashMap();
for p in pats {
p.each_binding(|mode, id, _, path1| {
p.each_binding(|_, id, span, path1| {
let bm = match cx.tables.pat_binding_modes.get(&id) {
Some(&bm) => bm,
None => span_bug!(span, "missing binding mode"),
};
let name = path1.node;
if let hir::BindByValue(hir::MutMutable) = mode {
if let ty::BindByValue(hir::MutMutable) = bm {
if !name.as_str().starts_with("_") {
match mutables.entry(name) {
Vacant(entry) => {

View File

@ -2277,8 +2277,9 @@ impl<'a> Resolver<'a> {
false, pat.span)
.and_then(LexicalScopeBinding::item);
let resolution = binding.map(NameBinding::def).and_then(|def| {
let ivmode = BindingMode::ByValue(Mutability::Immutable);
let always_binding = !pat_src.is_refutable() || opt_pat.is_some() ||
bmode != BindingMode::ByValue(Mutability::Immutable);
bmode != ivmode;
match def {
Def::StructCtor(_, CtorKind::Const) |
Def::VariantCtor(_, CtorKind::Const) |

View File

@ -113,10 +113,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
self.demand_eqtype(pat.span, expected, rhs_ty);
common_type
}
PatKind::Binding(bm, def_id, _, ref sub) => {
PatKind::Binding(ba, def_id, _, ref sub) => {
// Note the binding mode in the typeck tables. For now, what we store is always
// identical to what could be scraped from the HIR, but this will change with
// default binding modes (#42640).
let bm = ty::BindingMode::convert(ba);
self.inh.tables.borrow_mut().pat_binding_modes.insert(pat.id, bm);
let typ = self.local_ty(pat.span, pat.id);
match bm {
hir::BindByRef(mutbl) => {
ty::BindByReference(mutbl) => {
// if the binding is like
// ref x | ref const x | ref mut x
// then `x` is assigned a value of type `&M T` where M is the mutability
@ -131,7 +137,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
self.demand_eqtype(pat.span, region_ty, typ);
}
// otherwise the type of x is the expected type T
hir::BindByValue(_) => {
ty::BindByValue(_) => {
// As above, `T <: typeof(x)` is required but we
// use equality, see (*) below.
self.demand_eqtype(pat.span, expected, typ);
@ -396,11 +402,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
match_src: hir::MatchSource) -> Ty<'tcx> {
let tcx = self.tcx;
// Not entirely obvious: if matches may create ref bindings, we
// want to use the *precise* type of the discriminant, *not* some
// supertype, as the "discriminant type" (issue #23116).
// Not entirely obvious: if matches may create ref bindings, we want to
// use the *precise* type of the discriminant, *not* some supertype, as
// the "discriminant type" (issue #23116).
//
// FIXME(tschottdorf): don't call contains_explicit_ref_binding, which
// is problematic as the HIR is being scraped, but ref bindings may be
// implicit after #42640. We need to make sure that pat_adjustments
// (once introduced) is populated by the time we get here.
let contains_ref_bindings = arms.iter()
.filter_map(|a| a.contains_ref_binding())
.filter_map(|a| a.contains_explicit_ref_binding())
.max_by_key(|m| match *m {
hir::MutMutable => 1,
hir::MutImmutable => 0,

View File

@ -3999,7 +3999,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
local: &'gcx hir::Local,
init: &'gcx hir::Expr) -> Ty<'tcx>
{
let ref_bindings = local.pat.contains_ref_binding();
// FIXME(tschottdorf): contains_explicit_ref_binding() must be removed
// for #42640.
let ref_bindings = local.pat.contains_explicit_ref_binding();
let local_ty = self.local_ty(init.span, local.id);
if let Some(m) = ref_bindings {

View File

@ -1196,9 +1196,13 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
mc.cat_pattern(discr_cmt, root_pat, |sub_cmt, sub_pat| {
match sub_pat.node {
// `ref x` pattern
PatKind::Binding(hir::BindByRef(mutbl), ..) => {
self.link_region_from_node_type(sub_pat.span, sub_pat.id,
mutbl, sub_cmt);
PatKind::Binding(..) => {
let bm = *mc.tables.pat_binding_modes.get(&sub_pat.id)
.expect("missing binding mode");
if let ty::BindByReference(mutbl) = bm {
self.link_region_from_node_type(sub_pat.span, sub_pat.id,
mutbl, sub_cmt);
}
}
_ => {}
}

View File

@ -178,6 +178,15 @@ impl<'cx, 'gcx, 'tcx> Visitor<'gcx> for WritebackCx<'cx, 'gcx, 'tcx> {
}
fn visit_pat(&mut self, p: &'gcx hir::Pat) {
match p.node {
hir::PatKind::Binding(..) => {
let bm = *self.fcx.tables.borrow().pat_binding_modes.get(&p.id)
.expect("missing binding mode");
self.tables.pat_binding_modes.insert(p.id, bm);
}
_ => {}
};
self.visit_node_id(p.span, p.id);
intravisit::walk_pat(self, p);
}

View File

@ -867,13 +867,14 @@ mod tests {
pat: P(ast::Pat {
id: ast::DUMMY_NODE_ID,
node: PatKind::Ident(
ast::BindingMode::ByValue(ast::Mutability::Immutable),
Spanned{
span: sp(6,7),
node: Ident::from_str("b")},
None
),
span: sp(6,7)
ast::BindingMode::ByValue(
ast::Mutability::Immutable),
Spanned{
span: sp(6,7),
node: Ident::from_str("b")},
None
),
span: sp(6,7)
}),
id: ast::DUMMY_NODE_ID
}],