diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 3ae3671b59347..421a81c0d2342 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -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::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 { - 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 { let id = self.next_id(); let parent_def = self.parent_def.unwrap(); diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index a3a133daa09c4..7f1d1480d46a7 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -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, Option>), + Binding(BindingAnnotation, DefId, Spanned, Option>), /// A struct or struct variant pattern, e.g. `Variant {x, y, ..}`. /// The `bool` is `true` in the presence of a `..`. diff --git a/src/librustc/hir/pat_util.rs b/src/librustc/hir/pat_util.rs index 0190e74df6953..144cb34ee356e 100644 --- a/src/librustc/hir/pat_util.rs +++ b/src/librustc/hir/pat_util.rs @@ -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(&self, mut f: F) - where F: FnMut(hir::BindingMode, ast::NodeId, Span, &Spanned), + where F: FnMut(hir::BindingAnnotation, ast::NodeId, Span, &Spanned), { 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 { 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 { + /// 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 { 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 { + pub fn contains_explicit_ref_binding(&self) -> Option { + // 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, diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index beaf65b77d81f..abfb00a24a115 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -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")?; } } diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs index 7805029a67ff7..b344084f580bf 100644 --- a/src/librustc/ich/impls_hir.rs +++ b/src/librustc/ich/impls_hir.rs @@ -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 { diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 3e227872848ef..e03cbb45414d1 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -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, diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 259bd4f099915..87e933e85e228 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -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); diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 557d4b24f3032..b4993aafc4c9e 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -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 diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 39cb5d1b8c8e8..9133a5e777db2 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -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)) diff --git a/src/librustc/ty/binding.rs b/src/librustc/ty/binding.rs new file mode 100644 index 0000000000000..3db61b76cc55c --- /dev/null +++ b/src/librustc/ty/binding.rs @@ -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 or the MIT license +// , 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) +}); diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 45ddd4c0ff179..be3cd99426d4d 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -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>>, + // Stores the actual binding mode for all instances of hir::BindingAnnotation. + pub pat_binding_modes: NodeMap, + /// 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(), diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 804f47b5283f2..914419ede361b 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -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; diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index 1bfc5805bc8fd..0124a77349ca5 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -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) { diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 95c8613232ec2..060ff503d4e58 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -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]) { 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 diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs index ab919da815206..f37a112a596ac 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_const_eval/pattern.rs @@ -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 { diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index 473c0f3ffda86..d7d0dc7cb352b 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -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) => { diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 88013b45a05a4..349a21af89586 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -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) | diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 68726a7b1c4eb..01d2986a53ca7 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -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, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 7f69885047b93..37fd0dd15861f 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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 { diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 82207428efc4f..9b7ecc194ca88 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -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); + } } _ => {} } diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 81e5dae5477eb..0a323efabec1d 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -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); } diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 7b105a8fa14c2..893bada2670df 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -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 }],