Rollup merge of #64508 - Centril:or-pat-hir, r=matthewjasper

or-patterns: Push `PatKind/PatternKind::Or` at top level to HIR & HAIR

Following up on work in https://github.com/rust-lang/rust/pull/64111, https://github.com/rust-lang/rust/pull/63693, and https://github.com/rust-lang/rust/pull/61708, in this PR:

- We change `hair::Arm.patterns: Vec<Pattern<'_>>` into `hir::Arm.pattern: Pattern<'_>`.

   - `fn hair::Arm::top_pats_hack` is introduced as a temporary crutch in MIR building to avoid more changes.

- We change `hir::Arm.pats: HirVec<P<Pat>>` into `hir::Arm.pat: P<Pat>`.

   - The hacks in `rustc::hir::lowering` are removed since the representation hack is no longer necessary.

   - In some places, `fn hir::Arm::top_pats_hack` is introduced to leave some things as future work.

   - Misc changes: HIR pretty printing is adjusted to behave uniformly wrt. top/inner levels, rvalue promotion is adjusted, regionck, and dead_code is also.

   - Type checking is adjusted to uniformly handle or-patterns at top/inner levels.
      To make things compile, `p_0 | ... | p_n` is redefined as a "reference pattern" in [`fn is_non_ref_pat`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_typeck/check/struct.FnCtxt.html#method.is_non_ref_pat) for now. This is done so that reference types are not eagerly stripped from the `expected: Ty<'tcx>`.

    - Liveness is adjusted wrt. the `unused_variables` and `unused_assignments` lints to handle top/inner levels uniformly and the handling of `fn` parameters, `let` locals, and `match` arms are unified in this respect. This is not tested for now as exhaustiveness checks are reachable and will ICE.

    - In `check_match`, checking `@` and by-move bindings is adjusted. However, exhaustiveness checking is not adjusted the moment and is handled by @dlrobertson in https://github.com/rust-lang/rust/pull/63688.

    - AST borrowck (`construct.rs`) is not adjusted as AST borrowck will be removed soon.

r? @matthewjasper
cc @dlrobertson @varkor @oli-obk
This commit is contained in:
Mazdak Farrokhzad 2019-09-25 16:26:18 +02:00 committed by GitHub
commit da7812785a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 379 additions and 491 deletions

View File

@ -1103,7 +1103,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm) {
visitor.visit_id(arm.hir_id);
walk_list!(visitor, visit_pat, &arm.pats);
visitor.visit_pat(&arm.pat);
if let Some(ref g) = arm.guard {
match g {
Guard::If(ref e) => visitor.visit_expr(e),

View File

@ -434,35 +434,6 @@ impl<'a> LoweringContext<'a> {
visit::walk_pat(self, p)
}
// HACK(or_patterns; Centril | dlrobertson): Avoid creating
// HIR nodes for `PatKind::Or` for the top level of a `ast::Arm`.
// This is a temporary hack that should go away once we push down
// `arm.pats: HirVec<P<Pat>>` -> `arm.pat: P<Pat>` to HIR. // Centril
fn visit_arm(&mut self, arm: &'tcx Arm) {
match &arm.pat.node {
PatKind::Or(pats) => pats.iter().for_each(|p| self.visit_pat(p)),
_ => self.visit_pat(&arm.pat),
}
walk_list!(self, visit_expr, &arm.guard);
self.visit_expr(&arm.body);
walk_list!(self, visit_attribute, &arm.attrs);
}
// HACK(or_patterns; Centril | dlrobertson): Same as above. // Centril
fn visit_expr(&mut self, e: &'tcx Expr) {
if let ExprKind::Let(pat, scrutinee) = &e.node {
walk_list!(self, visit_attribute, e.attrs.iter());
match &pat.node {
PatKind::Or(pats) => pats.iter().for_each(|p| self.visit_pat(p)),
_ => self.visit_pat(&pat),
}
self.visit_expr(scrutinee);
self.visit_expr_post(e);
return;
}
visit::walk_expr(self, e)
}
fn visit_item(&mut self, item: &'tcx Item) {
let hir_id = self.lctx.allocate_hir_id_counter(item.id);

View File

@ -247,14 +247,14 @@ impl LoweringContext<'_> {
// 4. The return type of the block is `bool` which seems like what the user wanted.
let scrutinee = self.lower_expr(scrutinee);
let then_arm = {
let pat = self.lower_pat_top_hack(pat);
let pat = self.lower_pat(pat);
let expr = self.expr_bool(span, true);
self.arm(pat, P(expr))
};
let else_arm = {
let pat = self.pat_wild(span);
let expr = self.expr_bool(span, false);
self.arm(hir_vec![pat], P(expr))
self.arm(pat, P(expr))
};
hir::ExprKind::Match(
P(scrutinee),
@ -278,7 +278,7 @@ impl LoweringContext<'_> {
None => (self.expr_block_empty(span), false),
Some(els) => (self.lower_expr(els), true),
};
let else_arm = self.arm(hir_vec![else_pat], P(else_expr));
let else_arm = self.arm(else_pat, P(else_expr));
// Handle then + scrutinee:
let then_expr = self.lower_block_expr(then);
@ -286,7 +286,7 @@ impl LoweringContext<'_> {
// `<pat> => <then>`:
ExprKind::Let(ref pat, ref scrutinee) => {
let scrutinee = self.lower_expr(scrutinee);
let pat = self.lower_pat_top_hack(pat);
let pat = self.lower_pat(pat);
(pat, scrutinee, hir::MatchSource::IfLetDesugar { contains_else_clause })
}
// `true => <then>`:
@ -303,7 +303,7 @@ impl LoweringContext<'_> {
// let temporaries live outside of `cond`.
let cond = self.expr_drop_temps(span_block, P(cond), ThinVec::new());
let pat = self.pat_bool(span, true);
(hir_vec![pat], cond, hir::MatchSource::IfDesugar { contains_else_clause })
(pat, cond, hir::MatchSource::IfDesugar { contains_else_clause })
}
};
let then_arm = self.arm(then_pat, P(then_expr));
@ -327,7 +327,7 @@ impl LoweringContext<'_> {
let else_arm = {
let else_pat = self.pat_wild(span);
let else_expr = self.expr_break(span, ThinVec::new());
self.arm(hir_vec![else_pat], else_expr)
self.arm(else_pat, else_expr)
};
// Handle then + scrutinee:
@ -343,7 +343,7 @@ impl LoweringContext<'_> {
// }
// }
let scrutinee = self.with_loop_condition_scope(|t| t.lower_expr(scrutinee));
let pat = self.lower_pat_top_hack(pat);
let pat = self.lower_pat(pat);
(pat, scrutinee, hir::MatchSource::WhileLetDesugar, hir::LoopSource::WhileLet)
}
_ => {
@ -371,7 +371,7 @@ impl LoweringContext<'_> {
let cond = self.expr_drop_temps(span_block, P(cond), ThinVec::new());
// `true => <then>`:
let pat = self.pat_bool(span, true);
(hir_vec![pat], cond, hir::MatchSource::WhileDesugar, hir::LoopSource::While)
(pat, cond, hir::MatchSource::WhileDesugar, hir::LoopSource::While)
}
};
let then_arm = self.arm(then_pat, P(then_expr));
@ -424,7 +424,7 @@ impl LoweringContext<'_> {
hir::Arm {
hir_id: self.next_id(),
attrs: self.lower_attrs(&arm.attrs),
pats: self.lower_pat_top_hack(&arm.pat),
pat: self.lower_pat(&arm.pat),
guard: match arm.guard {
Some(ref x) => Some(hir::Guard::If(P(self.lower_expr(x)))),
_ => None,
@ -434,16 +434,6 @@ impl LoweringContext<'_> {
}
}
/// HACK(or_patterns; Centril | dlrobertson): For now we don't push down top level or-patterns
/// `p | q` into `hir::PatKind::Or(...)` as post-lowering bits of the compiler are not ready
/// to deal with it. This should by fixed by pushing it down to HIR and then HAIR.
fn lower_pat_top_hack(&mut self, pat: &Pat) -> HirVec<P<hir::Pat>> {
match pat.node {
PatKind::Or(ref ps) => ps.iter().map(|x| self.lower_pat(x)).collect(),
_ => hir_vec![self.lower_pat(pat)],
}
}
pub(super) fn make_async_expr(
&mut self,
capture_clause: CaptureBy,
@ -592,7 +582,7 @@ impl LoweringContext<'_> {
);
P(this.expr(await_span, expr_break, ThinVec::new()))
});
self.arm(hir_vec![ready_pat], break_x)
self.arm(ready_pat, break_x)
};
// `::std::task::Poll::Pending => {}`
@ -603,7 +593,7 @@ impl LoweringContext<'_> {
hir_vec![],
);
let empty_block = P(self.expr_block_empty(span));
self.arm(hir_vec![pending_pat], empty_block)
self.arm(pending_pat, empty_block)
};
let inner_match_stmt = {
@ -645,7 +635,7 @@ impl LoweringContext<'_> {
});
// mut pinned => loop { ... }
let pinned_arm = self.arm(hir_vec![pinned_pat], loop_expr);
let pinned_arm = self.arm(pinned_pat, loop_expr);
// match <expr> {
// mut pinned => loop { .. }
@ -1079,7 +1069,7 @@ impl LoweringContext<'_> {
ThinVec::new(),
));
let some_pat = self.pat_some(pat.span, val_pat);
self.arm(hir_vec![some_pat], assign)
self.arm(some_pat, assign)
};
// `::std::option::Option::None => break`
@ -1087,7 +1077,7 @@ impl LoweringContext<'_> {
let break_expr =
self.with_loop_scope(e.id, |this| this.expr_break(e.span, ThinVec::new()));
let pat = self.pat_none(e.span);
self.arm(hir_vec![pat], break_expr)
self.arm(pat, break_expr)
};
// `mut iter`
@ -1158,7 +1148,7 @@ impl LoweringContext<'_> {
});
// `mut iter => { ... }`
let iter_arm = self.arm(hir_vec![iter_pat], loop_expr);
let iter_arm = self.arm(iter_pat, loop_expr);
// `match ::std::iter::IntoIterator::into_iter(<head>) { ... }`
let into_iter_expr = {
@ -1244,7 +1234,7 @@ impl LoweringContext<'_> {
ThinVec::from(attrs.clone()),
));
let ok_pat = self.pat_ok(span, val_pat);
self.arm(hir_vec![ok_pat], val_expr)
self.arm(ok_pat, val_expr)
};
// `Err(err) => #[allow(unreachable_code)]
@ -1279,7 +1269,7 @@ impl LoweringContext<'_> {
};
let err_pat = self.pat_err(try_span, err_local);
self.arm(hir_vec![err_pat], ret_expr)
self.arm(err_pat, ret_expr)
};
hir::ExprKind::Match(
@ -1474,14 +1464,11 @@ impl LoweringContext<'_> {
}
}
/// HACK(or_patterns; Centril | dlrobertson): For now we don't push down top level or-patterns
/// `p | q` into `hir::PatKind::Or(...)` as post-lowering bits of the compiler are not ready
/// to deal with it. This should by fixed by pushing it down to HIR and then HAIR.
fn arm(&mut self, pats: HirVec<P<hir::Pat>>, expr: P<hir::Expr>) -> hir::Arm {
fn arm(&mut self, pat: P<hir::Pat>, expr: P<hir::Expr>) -> hir::Arm {
hir::Arm {
hir_id: self.next_id(),
attrs: hir_vec![],
pats,
pat,
guard: None,
span: expr.span,
body: expr,

View File

@ -882,44 +882,61 @@ impl fmt::Debug for Pat {
impl Pat {
// FIXME(#19596) this is a workaround, but there should be a better way
fn walk_<G>(&self, it: &mut G) -> bool
where G: FnMut(&Pat) -> bool
{
fn walk_short_(&self, it: &mut impl FnMut(&Pat) -> bool) -> bool {
if !it(self) {
return false;
}
match self.node {
PatKind::Binding(.., Some(ref p)) => p.walk_(it),
PatKind::Struct(_, ref fields, _) => {
fields.iter().all(|field| field.pat.walk_(it))
}
PatKind::TupleStruct(_, ref s, _) | PatKind::Tuple(ref s, _) => {
s.iter().all(|p| p.walk_(it))
}
PatKind::Or(ref pats) => pats.iter().all(|p| p.walk_(it)),
PatKind::Box(ref s) | PatKind::Ref(ref s, _) => {
s.walk_(it)
}
PatKind::Slice(ref before, ref slice, ref after) => {
use PatKind::*;
match &self.node {
Wild | Lit(_) | Range(..) | Binding(.., None) | Path(_) => true,
Box(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_short_(it),
Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)),
TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)),
Slice(before, slice, after) => {
before.iter()
.chain(slice.iter())
.chain(after.iter())
.all(|p| p.walk_(it))
}
PatKind::Wild |
PatKind::Lit(_) |
PatKind::Range(..) |
PatKind::Binding(..) |
PatKind::Path(_) => {
true
.all(|p| p.walk_short_(it))
}
}
}
pub fn walk<F>(&self, mut it: F) -> bool
where F: FnMut(&Pat) -> bool
{
/// Walk the pattern in left-to-right order,
/// short circuiting (with `.all(..)`) if `false` is returned.
///
/// Note that when visiting e.g. `Tuple(ps)`,
/// if visiting `ps[0]` returns `false`,
/// then `ps[1]` will not be visited.
pub fn walk_short(&self, mut it: impl FnMut(&Pat) -> bool) -> bool {
self.walk_short_(&mut it)
}
// FIXME(#19596) this is a workaround, but there should be a better way
fn walk_(&self, it: &mut impl FnMut(&Pat) -> bool) {
if !it(self) {
return;
}
use PatKind::*;
match &self.node {
Wild | Lit(_) | Range(..) | Binding(.., None) | Path(_) => {},
Box(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_(it),
Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)),
TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)),
Slice(before, slice, after) => {
before.iter()
.chain(slice.iter())
.chain(after.iter())
.for_each(|p| p.walk_(it))
}
}
}
/// Walk the pattern in left-to-right order.
///
/// If `it(pat)` returns `false`, the children are not visited.
pub fn walk(&self, mut it: impl FnMut(&Pat) -> bool) {
self.walk_(&mut it)
}
}
@ -1259,21 +1276,32 @@ pub struct Local {
}
/// Represents a single arm of a `match` expression, e.g.
/// `<pats> (if <guard>) => <body>`.
/// `<pat> (if <guard>) => <body>`.
#[derive(RustcEncodable, RustcDecodable, Debug, HashStable)]
pub struct Arm {
#[stable_hasher(ignore)]
pub hir_id: HirId,
pub span: Span,
pub attrs: HirVec<Attribute>,
/// Multiple patterns can be combined with `|`
pub pats: HirVec<P<Pat>>,
/// If this pattern and the optional guard matches, then `body` is evaluated.
pub pat: P<Pat>,
/// Optional guard clause.
pub guard: Option<Guard>,
/// The expression the arm evaluates to if this arm matches.
pub body: P<Expr>,
}
impl Arm {
// HACK(or_patterns; Centril | dlrobertson): Remove this and
// correctly handle each case in which this method is used.
pub fn top_pats_hack(&self) -> &[P<Pat>] {
match &self.pat.node {
PatKind::Or(pats) => pats,
_ => std::slice::from_ref(&self.pat),
}
}
}
#[derive(RustcEncodable, RustcDecodable, Debug, HashStable)]
pub enum Guard {
If(P<Expr>),

View File

@ -66,9 +66,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::BindingAnnotation, HirId, Span, ast::Ident),
{
pub fn each_binding(&self, mut f: impl FnMut(hir::BindingAnnotation, HirId, Span, ast::Ident)) {
self.walk(|p| {
if let PatKind::Binding(binding_mode, _, ident, _) = p.node {
f(binding_mode, p.hir_id, p.span, ident);
@ -77,35 +75,57 @@ impl hir::Pat {
});
}
/// Call `f` on every "binding" in a pattern, e.g., on `a` in
/// `match foo() { Some(a) => (), None => () }`.
///
/// When encountering an or-pattern `p_0 | ... | p_n` only `p_0` will be visited.
pub fn each_binding_or_first(
&self,
f: &mut impl FnMut(hir::BindingAnnotation, HirId, Span, ast::Ident),
) {
self.walk(|p| match &p.node {
PatKind::Or(ps) => {
ps[0].each_binding_or_first(f);
false
},
PatKind::Binding(bm, _, ident, _) => {
f(*bm, p.hir_id, p.span, *ident);
true
}
_ => true,
})
}
/// Checks if the pattern contains any patterns that bind something to
/// an ident, e.g., `foo`, or `Foo(foo)` or `foo @ Bar(..)`.
pub fn contains_bindings(&self) -> bool {
let mut contains_bindings = false;
self.walk(|p| {
if let PatKind::Binding(..) = p.node {
contains_bindings = true;
false // there's at least one binding, can short circuit now.
} else {
true
}
});
contains_bindings
self.satisfies(|p| match p.node {
PatKind::Binding(..) => true,
_ => false,
})
}
/// Checks if the pattern contains any patterns that bind something to
/// an ident or wildcard, e.g., `foo`, or `Foo(_)`, `foo @ Bar(..)`,
pub fn contains_bindings_or_wild(&self) -> bool {
let mut contains_bindings = false;
self.walk(|p| {
match p.node {
PatKind::Binding(..) | PatKind::Wild => {
contains_bindings = true;
false // there's at least one binding/wildcard, can short circuit now.
}
_ => true
self.satisfies(|p| match p.node {
PatKind::Binding(..) | PatKind::Wild => true,
_ => false,
})
}
/// Checks if the pattern satisfies the given predicate on some sub-pattern.
fn satisfies(&self, pred: impl Fn(&Self) -> bool) -> bool {
let mut satisfies = false;
self.walk_short(|p| {
if pred(p) {
satisfies = true;
false // Found one, can short circuit now.
} else {
true
}
});
contains_bindings
satisfies
}
pub fn simple_ident(&self) -> Option<ast::Ident> {
@ -119,20 +139,20 @@ impl hir::Pat {
/// Returns variants that are necessary to exist for the pattern to match.
pub fn necessary_variants(&self) -> Vec<DefId> {
let mut variants = vec![];
self.walk(|p| {
match p.node {
PatKind::Path(hir::QPath::Resolved(_, ref path)) |
PatKind::TupleStruct(hir::QPath::Resolved(_, ref path), ..) |
PatKind::Struct(hir::QPath::Resolved(_, ref path), ..) => {
match path.res {
Res::Def(DefKind::Variant, id) => variants.push(id),
Res::Def(DefKind::Ctor(CtorOf::Variant, ..), id) => variants.push(id),
_ => ()
}
self.walk(|p| match &p.node {
PatKind::Or(_) => false,
PatKind::Path(hir::QPath::Resolved(_, path)) |
PatKind::TupleStruct(hir::QPath::Resolved(_, path), ..) |
PatKind::Struct(hir::QPath::Resolved(_, path), ..) => {
if let Res::Def(DefKind::Variant, id)
| Res::Def(DefKind::Ctor(CtorOf::Variant, ..), id)
= path.res
{
variants.push(id);
}
_ => ()
true
}
true
_ => true,
});
variants.sort();
variants.dedup();
@ -148,33 +168,14 @@ impl hir::Pat {
let mut result = None;
self.each_binding(|annotation, _, _, _| {
match annotation {
hir::BindingAnnotation::Ref => {
match result {
None | Some(hir::MutImmutable) => result = Some(hir::MutImmutable),
_ => (),
}
hir::BindingAnnotation::Ref => match result {
None | Some(hir::MutImmutable) => result = Some(hir::MutImmutable),
_ => {}
}
hir::BindingAnnotation::RefMut => result = Some(hir::MutMutable),
_ => (),
_ => {}
}
});
result
}
}
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_explicit_ref_binding(&self) -> Option<hir::Mutability> {
// FIXME(tschottdorf): contains_explicit_ref_binding() must be removed
// for #42640 (default match binding modes).
//
// See #44848.
self.pats.iter()
.filter_map(|pat| pat.contains_explicit_ref_binding())
.max_by_key(|m| match *m {
hir::MutMutable => 1,
hir::MutImmutable => 0,
})
}
}

View File

@ -1790,16 +1790,7 @@ impl<'a> State<'a> {
self.ann.pre(self, AnnNode::Arm(arm));
self.ibox(0);
self.print_outer_attributes(&arm.attrs);
let mut first = true;
for p in &arm.pats {
if first {
first = false;
} else {
self.s.space();
self.word_space("|");
}
self.print_pat(&p);
}
self.print_pat(&arm.pat);
self.s.space();
if let Some(ref g) = arm.guard {
match g {

View File

@ -259,19 +259,13 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> {
}
fn visit_arm(&mut self, arm: &'tcx hir::Arm) {
if arm.pats.len() == 1 {
let variants = arm.pats[0].necessary_variants();
// Inside the body, ignore constructions of variants
// necessary for the pattern to match. Those construction sites
// can't be reached unless the variant is constructed elsewhere.
let len = self.ignore_variant_stack.len();
self.ignore_variant_stack.extend_from_slice(&variants);
intravisit::walk_arm(self, arm);
self.ignore_variant_stack.truncate(len);
} else {
intravisit::walk_arm(self, arm);
}
// Inside the body, ignore constructions of variants
// necessary for the pattern to match. Those construction sites
// can't be reached unless the variant is constructed elsewhere.
let len = self.ignore_variant_stack.len();
self.ignore_variant_stack.extend(arm.pat.necessary_variants());
intravisit::walk_arm(self, arm);
self.ignore_variant_stack.truncate(len);
}
fn visit_pat(&mut self, pat: &'tcx hir::Pat) {

View File

@ -779,16 +779,12 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
fn arm_move_mode(&mut self, discr_cmt: mc::cmt<'tcx>, arm: &hir::Arm) -> TrackMatchMode {
let mut mode = Unknown;
for pat in &arm.pats {
self.determine_pat_move_mode(discr_cmt.clone(), &pat, &mut mode);
}
self.determine_pat_move_mode(discr_cmt.clone(), &arm.pat, &mut mode);
mode
}
fn walk_arm(&mut self, discr_cmt: mc::cmt<'tcx>, arm: &hir::Arm, mode: MatchMode) {
for pat in &arm.pats {
self.walk_pat(discr_cmt.clone(), &pat, mode);
}
self.walk_pat(discr_cmt.clone(), &arm.pat, mode);
if let Some(hir::Guard::If(ref e)) = arm.guard {
self.consume_expr(e)

View File

@ -96,7 +96,11 @@
use self::LiveNodeKind::*;
use self::VarKind::*;
use crate::hir;
use crate::hir::{Expr, HirId};
use crate::hir::def::*;
use crate::hir::def_id::DefId;
use crate::hir::intravisit::{self, Visitor, FnKind, NestedVisitorMap};
use crate::hir::Node;
use crate::hir::ptr::P;
use crate::ty::{self, TyCtxt};
@ -105,20 +109,16 @@ use crate::lint;
use crate::util::nodemap::{HirIdMap, HirIdSet};
use errors::Applicability;
use std::collections::{BTreeMap, VecDeque};
use rustc_data_structures::fx::FxIndexMap;
use std::collections::VecDeque;
use std::{fmt, u32};
use std::io::prelude::*;
use std::io;
use std::rc::Rc;
use syntax::ast;
use syntax::symbol::{kw, sym};
use syntax::symbol::sym;
use syntax_pos::Span;
use crate::hir;
use crate::hir::{Expr, HirId};
use crate::hir::def_id::DefId;
use crate::hir::intravisit::{self, Visitor, FnKind, NestedVisitorMap};
#[derive(Copy, Clone, PartialEq)]
struct Variable(u32);
@ -404,7 +404,7 @@ fn visit_fn<'tcx>(
lsets.warn_about_unused_args(body, entry_ln);
}
fn add_from_pat<'tcx>(ir: &mut IrMaps<'tcx>, pat: &P<hir::Pat>) {
fn add_from_pat(ir: &mut IrMaps<'_>, pat: &P<hir::Pat>) {
// For struct patterns, take note of which fields used shorthand
// (`x` rather than `x: x`).
let mut shorthand_field_ids = HirIdSet::default();
@ -412,26 +412,21 @@ fn add_from_pat<'tcx>(ir: &mut IrMaps<'tcx>, pat: &P<hir::Pat>) {
pats.push_back(pat);
while let Some(pat) = pats.pop_front() {
use crate::hir::PatKind::*;
match pat.node {
Binding(_, _, _, ref inner_pat) => {
match &pat.node {
Binding(.., inner_pat) => {
pats.extend(inner_pat.iter());
}
Struct(_, ref fields, _) => {
for field in fields {
if field.is_shorthand {
shorthand_field_ids.insert(field.pat.hir_id);
}
}
Struct(_, fields, _) => {
let ids = fields.iter().filter(|f| f.is_shorthand).map(|f| f.pat.hir_id);
shorthand_field_ids.extend(ids);
}
Ref(ref inner_pat, _) |
Box(ref inner_pat) => {
Ref(inner_pat, _) | Box(inner_pat) => {
pats.push_back(inner_pat);
}
TupleStruct(_, ref inner_pats, _) |
Tuple(ref inner_pats, _) => {
TupleStruct(_, inner_pats, _) | Tuple(inner_pats, _) | Or(inner_pats) => {
pats.extend(inner_pats.iter());
}
Slice(ref pre_pats, ref inner_pat, ref post_pats) => {
Slice(pre_pats, inner_pat, post_pats) => {
pats.extend(pre_pats.iter());
pats.extend(inner_pat.iter());
pats.extend(post_pats.iter());
@ -440,7 +435,7 @@ fn add_from_pat<'tcx>(ir: &mut IrMaps<'tcx>, pat: &P<hir::Pat>) {
}
}
pat.each_binding(|_bm, hir_id, _sp, ident| {
pat.each_binding(|_, hir_id, _, ident| {
ir.add_live_node_for_node(hir_id, VarDefNode(ident.span));
ir.add_variable(Local(LocalInfo {
id: hir_id,
@ -456,9 +451,7 @@ fn visit_local<'tcx>(ir: &mut IrMaps<'tcx>, local: &'tcx hir::Local) {
}
fn visit_arm<'tcx>(ir: &mut IrMaps<'tcx>, arm: &'tcx hir::Arm) {
for pat in &arm.pats {
add_from_pat(ir, pat);
}
add_from_pat(ir, &arm.pat);
intravisit::walk_arm(ir, arm);
}
@ -734,35 +727,15 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
self.ir.variable(hir_id, span)
}
fn pat_bindings<F>(&mut self, pat: &hir::Pat, mut f: F) where
F: FnMut(&mut Liveness<'a, 'tcx>, LiveNode, Variable, Span, HirId),
{
pat.each_binding(|_bm, hir_id, sp, n| {
let ln = self.live_node(hir_id, sp);
let var = self.variable(hir_id, n.span);
f(self, ln, var, n.span, hir_id);
})
}
fn arm_pats_bindings<F>(&mut self, pat: Option<&hir::Pat>, f: F) where
F: FnMut(&mut Liveness<'a, 'tcx>, LiveNode, Variable, Span, HirId),
{
if let Some(pat) = pat {
self.pat_bindings(pat, f);
}
}
fn define_bindings_in_pat(&mut self, pat: &hir::Pat, succ: LiveNode)
-> LiveNode {
self.define_bindings_in_arm_pats(Some(pat), succ)
}
fn define_bindings_in_arm_pats(&mut self, pat: Option<&hir::Pat>, succ: LiveNode)
-> LiveNode {
let mut succ = succ;
self.arm_pats_bindings(pat, |this, ln, var, _sp, _id| {
this.init_from_succ(ln, succ);
this.define(ln, var);
fn define_bindings_in_pat(&mut self, pat: &hir::Pat, mut succ: LiveNode) -> LiveNode {
// In an or-pattern, only consider the first pattern; any later patterns
// must have the same bindings, and we also consider the first pattern
// to be the "authoritative" set of ids.
pat.each_binding_or_first(&mut |_, hir_id, pat_sp, ident| {
let ln = self.live_node(hir_id, pat_sp);
let var = self.variable(hir_id, ident.span);
self.init_from_succ(ln, succ);
self.define(ln, var);
succ = ln;
});
succ
@ -1076,12 +1049,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
arm.guard.as_ref().map(|hir::Guard::If(e)| &**e),
body_succ
);
// only consider the first pattern; any later patterns must have
// the same bindings, and we also consider the first pattern to be
// the "authoritative" set of ids
let arm_succ =
self.define_bindings_in_arm_pats(arm.pats.first().map(|p| &**p),
guard_succ);
let arm_succ = self.define_bindings_in_pat(&arm.pat, guard_succ);
self.merge_from_succ(ln, arm_succ, first_merge);
first_merge = false;
};
@ -1388,74 +1356,36 @@ impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {
NestedVisitorMap::None
}
fn visit_local(&mut self, l: &'tcx hir::Local) {
check_local(self, l);
fn visit_local(&mut self, local: &'tcx hir::Local) {
self.check_unused_vars_in_pat(&local.pat, None, |spans, hir_id, ln, var| {
if local.init.is_some() {
self.warn_about_dead_assign(spans, hir_id, ln, var);
}
});
intravisit::walk_local(self, local);
}
fn visit_expr(&mut self, ex: &'tcx Expr) {
check_expr(self, ex);
}
fn visit_arm(&mut self, a: &'tcx hir::Arm) {
check_arm(self, a);
fn visit_arm(&mut self, arm: &'tcx hir::Arm) {
self.check_unused_vars_in_pat(&arm.pat, None, |_, _, _, _| {});
intravisit::walk_arm(self, arm);
}
}
fn check_local<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, local: &'tcx hir::Local) {
match local.init {
Some(_) => {
this.warn_about_unused_or_dead_vars_in_pat(&local.pat);
},
None => {
this.pat_bindings(&local.pat, |this, ln, var, sp, id| {
let span = local.pat.simple_ident().map_or(sp, |ident| ident.span);
this.warn_about_unused(vec![span], id, ln, var);
})
}
}
intravisit::walk_local(this, local);
}
fn check_arm<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, arm: &'tcx hir::Arm) {
// Only consider the variable from the first pattern; any later patterns must have
// the same bindings, and we also consider the first pattern to be the "authoritative" set of
// ids. However, we should take the spans of variables with the same name from the later
// patterns so the suggestions to prefix with underscores will apply to those too.
let mut vars: BTreeMap<String, (LiveNode, Variable, HirId, Vec<Span>)> = Default::default();
for pat in &arm.pats {
this.arm_pats_bindings(Some(&*pat), |this, ln, var, sp, id| {
let name = this.ir.variable_name(var);
vars.entry(name)
.and_modify(|(.., spans)| {
spans.push(sp);
})
.or_insert_with(|| {
(ln, var, id, vec![sp])
});
});
}
for (_, (ln, var, id, spans)) in vars {
this.warn_about_unused(spans, id, ln, var);
}
intravisit::walk_arm(this, arm);
}
fn check_expr<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, expr: &'tcx Expr) {
fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr) {
match expr.node {
hir::ExprKind::Assign(ref l, _) => {
this.check_place(&l);
intravisit::walk_expr(this, expr);
}
hir::ExprKind::AssignOp(_, ref l, _) => {
if !this.tables.is_method_call(expr) {
this.check_place(&l);
}
intravisit::walk_expr(this, expr);
}
hir::ExprKind::InlineAsm(ref ia, ref outputs, ref inputs) => {
@ -1470,8 +1400,6 @@ fn check_expr<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, expr: &'tcx Expr) {
}
this.visit_expr(output);
}
intravisit::walk_expr(this, expr);
}
// no correctness conditions related to liveness
@ -1484,13 +1412,13 @@ fn check_expr<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, expr: &'tcx Expr) {
hir::ExprKind::Lit(_) | hir::ExprKind::Block(..) | hir::ExprKind::AddrOf(..) |
hir::ExprKind::Struct(..) | hir::ExprKind::Repeat(..) |
hir::ExprKind::Closure(..) | hir::ExprKind::Path(_) | hir::ExprKind::Yield(..) |
hir::ExprKind::Box(..) | hir::ExprKind::Type(..) | hir::ExprKind::Err => {
intravisit::walk_expr(this, expr);
}
hir::ExprKind::Box(..) | hir::ExprKind::Type(..) | hir::ExprKind::Err => {}
}
intravisit::walk_expr(this, expr);
}
impl<'a, 'tcx> Liveness<'a, 'tcx> {
impl<'tcx> Liveness<'_, 'tcx> {
fn check_place(&mut self, expr: &'tcx Expr) {
match expr.node {
hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
@ -1503,7 +1431,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
// as being used.
let ln = self.live_node(expr.hir_id, expr.span);
let var = self.variable(var_hid, expr.span);
self.warn_about_dead_assign(expr.span, expr.hir_id, ln, var);
self.warn_about_dead_assign(vec![expr.span], expr.hir_id, ln, var);
}
}
}
@ -1525,109 +1453,112 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
}
fn warn_about_unused_args(&self, body: &hir::Body, entry_ln: LiveNode) {
for param in &body.params {
param.pat.each_binding(|_bm, hir_id, _, ident| {
let sp = ident.span;
let var = self.variable(hir_id, sp);
// Ignore unused self.
if ident.name != kw::SelfLower {
if !self.warn_about_unused(vec![sp], hir_id, entry_ln, var) {
if self.live_on_entry(entry_ln, var).is_none() {
self.report_dead_assign(hir_id, sp, var, true);
}
}
for p in &body.params {
self.check_unused_vars_in_pat(&p.pat, Some(entry_ln), |spans, hir_id, ln, var| {
if self.live_on_entry(ln, var).is_none() {
self.report_dead_assign(hir_id, spans, var, true);
}
})
});
}
}
fn warn_about_unused_or_dead_vars_in_pat(&mut self, pat: &hir::Pat) {
self.pat_bindings(pat, |this, ln, var, sp, id| {
if !this.warn_about_unused(vec![sp], id, ln, var) {
this.warn_about_dead_assign(sp, id, ln, var);
fn check_unused_vars_in_pat(
&self,
pat: &hir::Pat,
entry_ln: Option<LiveNode>,
on_used_on_entry: impl Fn(Vec<Span>, HirId, LiveNode, Variable),
) {
// In an or-pattern, only consider the variable; any later patterns must have the same
// bindings, and we also consider the first pattern to be the "authoritative" set of ids.
// However, we should take the spans of variables with the same name from the later
// patterns so the suggestions to prefix with underscores will apply to those too.
let mut vars: FxIndexMap<String, (LiveNode, Variable, HirId, Vec<Span>)> = <_>::default();
pat.each_binding(|_, hir_id, pat_sp, ident| {
let ln = entry_ln.unwrap_or_else(|| self.live_node(hir_id, pat_sp));
let var = self.variable(hir_id, ident.span);
vars.entry(self.ir.variable_name(var))
.and_modify(|(.., spans)| spans.push(ident.span))
.or_insert_with(|| (ln, var, hir_id, vec![ident.span]));
});
for (_, (ln, var, id, spans)) in vars {
if self.used_on_entry(ln, var) {
on_used_on_entry(spans, id, ln, var);
} else {
self.report_unused(spans, id, ln, var);
}
})
}
}
fn warn_about_unused(&self,
spans: Vec<Span>,
hir_id: HirId,
ln: LiveNode,
var: Variable)
-> bool {
if !self.used_on_entry(ln, var) {
let r = self.should_warn(var);
if let Some(name) = r {
// annoying: for parameters in funcs like `fn(x: i32)
// {ret}`, there is only one node, so asking about
// assigned_on_exit() is not meaningful.
let is_assigned = if ln == self.s.exit_ln {
false
} else {
self.assigned_on_exit(ln, var).is_some()
};
fn report_unused(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) {
if let Some(name) = self.should_warn(var).filter(|name| name != "self") {
// annoying: for parameters in funcs like `fn(x: i32)
// {ret}`, there is only one node, so asking about
// assigned_on_exit() is not meaningful.
let is_assigned = if ln == self.s.exit_ln {
false
} else {
self.assigned_on_exit(ln, var).is_some()
};
if is_assigned {
self.ir.tcx.lint_hir_note(
lint::builtin::UNUSED_VARIABLES,
hir_id,
spans,
&format!("variable `{}` is assigned to, but never used", name),
&format!("consider using `_{}` instead", name),
);
} else if name != "self" {
let mut err = self.ir.tcx.struct_span_lint_hir(
lint::builtin::UNUSED_VARIABLES,
hir_id,
spans.clone(),
&format!("unused variable: `{}`", name),
);
if is_assigned {
self.ir.tcx.lint_hir_note(
lint::builtin::UNUSED_VARIABLES,
hir_id,
spans,
&format!("variable `{}` is assigned to, but never used", name),
&format!("consider using `_{}` instead", name),
);
} else {
let mut err = self.ir.tcx.struct_span_lint_hir(
lint::builtin::UNUSED_VARIABLES,
hir_id,
spans.clone(),
&format!("unused variable: `{}`", name),
);
if self.ir.variable_is_shorthand(var) {
if let Node::Binding(pat) = self.ir.tcx.hir().get(hir_id) {
// Handle `ref` and `ref mut`.
let spans = spans.iter()
.map(|_span| (pat.span, format!("{}: _", name)))
.collect();
if self.ir.variable_is_shorthand(var) {
if let Node::Binding(pat) = self.ir.tcx.hir().get(hir_id) {
// Handle `ref` and `ref mut`.
let spans = spans.iter()
.map(|_span| (pat.span, format!("{}: _", name)))
.collect();
err.multipart_suggestion(
"try ignoring the field",
spans,
Applicability::MachineApplicable,
);
}
} else {
err.multipart_suggestion(
"consider prefixing with an underscore",
spans.iter().map(|span| (*span, format!("_{}", name))).collect(),
"try ignoring the field",
spans,
Applicability::MachineApplicable,
);
}
err.emit()
} else {
err.multipart_suggestion(
"consider prefixing with an underscore",
spans.iter().map(|span| (*span, format!("_{}", name))).collect(),
Applicability::MachineApplicable,
);
}
err.emit()
}
true
} else {
false
}
}
fn warn_about_dead_assign(&self, sp: Span, hir_id: HirId, ln: LiveNode, var: Variable) {
fn warn_about_dead_assign(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) {
if self.live_on_exit(ln, var).is_none() {
self.report_dead_assign(hir_id, sp, var, false);
self.report_dead_assign(hir_id, spans, var, false);
}
}
fn report_dead_assign(&self, hir_id: HirId, sp: Span, var: Variable, is_argument: bool) {
fn report_dead_assign(&self, hir_id: HirId, spans: Vec<Span>, var: Variable, is_param: bool) {
if let Some(name) = self.should_warn(var) {
if is_argument {
self.ir.tcx.struct_span_lint_hir(lint::builtin::UNUSED_ASSIGNMENTS, hir_id, sp,
if is_param {
self.ir.tcx.struct_span_lint_hir(lint::builtin::UNUSED_ASSIGNMENTS, hir_id, spans,
&format!("value passed to `{}` is never read", name))
.help("maybe it is overwritten before being read?")
.emit();
} else {
self.ir.tcx.struct_span_lint_hir(lint::builtin::UNUSED_ASSIGNMENTS, hir_id, sp,
self.ir.tcx.struct_span_lint_hir(lint::builtin::UNUSED_ASSIGNMENTS, hir_id, spans,
&format!("value assigned to `{}` is never read", name))
.help("maybe it is overwritten before being read?")
.emit();

View File

@ -390,7 +390,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
// patterns and the guard (if there is one) in the arm.
let bindings_exit = self.add_dummy_node(&[]);
for pat in &arm.pats {
for pat in arm.top_pats_hack() {
// Visit the pattern, coming from the discriminant exit
let mut pat_exit = self.pat(&pat, discr_exit);

View File

@ -142,7 +142,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Step 2. Create the otherwise and prebinding blocks.
// create binding start block for link them by false edges
let candidate_count = arms.iter().map(|c| c.patterns.len()).sum::<usize>();
let candidate_count = arms.iter().map(|c| c.top_pats_hack().len()).sum::<usize>();
let pre_binding_blocks: Vec<_> = (0..candidate_count)
.map(|_| self.cfg.start_new_block())
.collect();
@ -159,7 +159,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
.map(|arm| {
let arm_has_guard = arm.guard.is_some();
match_has_guard |= arm_has_guard;
let arm_candidates: Vec<_> = arm.patterns
let arm_candidates: Vec<_> = arm.top_pats_hack()
.iter()
.zip(candidate_pre_binding_blocks.by_ref())
.map(
@ -238,7 +238,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let scope = this.declare_bindings(
None,
arm.span,
&arm.patterns[0],
&arm.top_pats_hack()[0],
ArmHasGuard(arm.guard.is_some()),
Some((Some(&scrutinee_place), scrutinee_span)),
);

View File

@ -860,9 +860,9 @@ impl ToBorrowKind for hir::Mutability {
}
}
fn convert_arm<'a, 'tcx>(cx: &mut Cx<'a, 'tcx>, arm: &'tcx hir::Arm) -> Arm<'tcx> {
fn convert_arm<'tcx>(cx: &mut Cx<'_, 'tcx>, arm: &'tcx hir::Arm) -> Arm<'tcx> {
Arm {
patterns: arm.pats.iter().map(|p| cx.pattern_from_hir(p)).collect(),
pattern: cx.pattern_from_hir(&arm.pat),
guard: match arm.guard {
Some(hir::Guard::If(ref e)) => Some(Guard::If(e.to_ref())),
_ => None,

View File

@ -293,7 +293,7 @@ pub struct FruInfo<'tcx> {
#[derive(Clone, Debug)]
pub struct Arm<'tcx> {
pub patterns: Vec<Pattern<'tcx>>,
pub pattern: Pattern<'tcx>,
pub guard: Option<Guard<'tcx>>,
pub body: ExprRef<'tcx>,
pub lint_level: LintLevel,
@ -301,6 +301,17 @@ pub struct Arm<'tcx> {
pub span: Span,
}
impl Arm<'tcx> {
// HACK(or_patterns; Centril | dlrobertson): Remove this and
// correctly handle each case in which this method is used.
pub fn top_pats_hack(&self) -> &[Pattern<'tcx>] {
match &*self.pattern.kind {
PatternKind::Or { pats } => pats,
_ => std::slice::from_ref(&self.pattern),
}
}
}
#[derive(Clone, Debug)]
pub enum Guard<'tcx> {
If(ExprRef<'tcx>),

View File

@ -14,7 +14,6 @@ use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc::hir::def::*;
use rustc::hir::def_id::DefId;
use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
use rustc::hir::ptr::P;
use rustc::hir::{self, Pat, PatKind};
use smallvec::smallvec;
@ -76,7 +75,7 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
});
// Check legality of move bindings and `@` patterns.
self.check_patterns(false, slice::from_ref(&loc.pat));
self.check_patterns(false, &loc.pat);
}
fn visit_body(&mut self, body: &'tcx hir::Body) {
@ -84,7 +83,7 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
for param in &body.params {
self.check_irrefutable(&param.pat, "function argument");
self.check_patterns(false, slice::from_ref(&param.pat));
self.check_patterns(false, &param.pat);
}
}
}
@ -122,11 +121,9 @@ impl PatternContext<'_, '_> {
}
impl<'tcx> MatchVisitor<'_, 'tcx> {
fn check_patterns(&mut self, has_guard: bool, pats: &[P<Pat>]) {
check_legality_of_move_bindings(self, has_guard, pats);
for pat in pats {
check_legality_of_bindings_in_at_patterns(self, pat);
}
fn check_patterns(&mut self, has_guard: bool, pat: &Pat) {
check_legality_of_move_bindings(self, has_guard, pat);
check_legality_of_bindings_in_at_patterns(self, pat);
}
fn check_match(
@ -137,7 +134,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
) {
for arm in arms {
// First, check legality of move bindings.
self.check_patterns(arm.guard.is_some(), &arm.pats);
self.check_patterns(arm.guard.is_some(), &arm.pat);
// Second, if there is a guard on each arm, make sure it isn't
// assigning or borrowing anything mutably.
@ -146,9 +143,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
}
// Third, perform some lints.
for pat in &arm.pats {
check_for_bindings_named_same_as_variants(self, pat);
}
check_for_bindings_named_same_as_variants(self, &arm.pat);
}
let module = self.tcx.hir().get_module_parent(scrut.hir_id);
@ -156,7 +151,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
let mut have_errors = false;
let inlined_arms : Vec<(Vec<_>, _)> = arms.iter().map(|arm| (
arm.pats.iter().map(|pat| {
arm.top_pats_hack().iter().map(|pat| {
let mut patcx = PatternContext::new(self.tcx,
self.param_env.and(self.identity_substs),
self.tables);
@ -545,78 +540,60 @@ fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[Pattern<'_>]) -> Vec<Span> {
covered
}
// Legality of move bindings checking
fn check_legality_of_move_bindings(
cx: &mut MatchVisitor<'_, '_>,
has_guard: bool,
pats: &[P<Pat>],
) {
// Check the legality of legality of by-move bindings.
fn check_legality_of_move_bindings(cx: &mut MatchVisitor<'_, '_>, has_guard: bool, pat: &Pat) {
let mut by_ref_span = None;
for pat in pats {
pat.each_binding(|_, hir_id, span, _path| {
if let Some(&bm) = cx.tables.pat_binding_modes().get(hir_id) {
if let ty::BindByReference(..) = bm {
by_ref_span = Some(span);
pat.each_binding(|_, hir_id, span, _| {
if let Some(&bm) = cx.tables.pat_binding_modes().get(hir_id) {
if let ty::BindByReference(..) = bm {
by_ref_span = Some(span);
}
} else {
cx.tcx.sess.delay_span_bug(pat.span, "missing binding mode");
}
});
let span_vec = &mut Vec::new();
let mut check_move = |p: &Pat, sub: Option<&Pat>| {
// Check legality of moving out of the enum.
//
// `x @ Foo(..)` is legal, but `x @ Foo(y)` isn't.
if sub.map_or(false, |p| p.contains_bindings()) {
struct_span_err!(cx.tcx.sess, p.span, E0007, "cannot bind by-move with sub-bindings")
.span_label(p.span, "binds an already bound by-move value by moving it")
.emit();
} else if !has_guard && by_ref_span.is_some() {
span_vec.push(p.span);
}
};
pat.walk(|p| {
if let PatKind::Binding(.., sub) = &p.node {
if let Some(&bm) = cx.tables.pat_binding_modes().get(p.hir_id) {
if let ty::BindByValue(..) = bm {
let pat_ty = cx.tables.node_type(p.hir_id);
if !pat_ty.is_copy_modulo_regions(cx.tcx, cx.param_env, pat.span) {
check_move(p, sub.as_deref());
}
}
} else {
cx.tcx.sess.delay_span_bug(pat.span, "missing binding mode");
}
})
}
let span_vec = &mut Vec::new();
let check_move = |
cx: &mut MatchVisitor<'_, '_>,
p: &Pat,
sub: Option<&Pat>,
span_vec: &mut Vec<Span>,
| {
// check legality of moving out of the enum
// x @ Foo(..) is legal, but x @ Foo(y) isn't.
if sub.map_or(false, |p| p.contains_bindings()) {
struct_span_err!(cx.tcx.sess, p.span, E0007,
"cannot bind by-move with sub-bindings")
.span_label(p.span, "binds an already bound by-move value by moving it")
.emit();
} else if !has_guard {
if let Some(_by_ref_span) = by_ref_span {
span_vec.push(p.span);
}
}
};
true
});
for pat in pats {
pat.walk(|p| {
if let PatKind::Binding(_, _, _, ref sub) = p.node {
if let Some(&bm) = cx.tables.pat_binding_modes().get(p.hir_id) {
match bm {
ty::BindByValue(..) => {
let pat_ty = cx.tables.node_type(p.hir_id);
if !pat_ty.is_copy_modulo_regions(cx.tcx, cx.param_env, pat.span) {
check_move(cx, p, sub.as_ref().map(|p| &**p), span_vec);
}
}
_ => {}
}
} else {
cx.tcx.sess.delay_span_bug(pat.span, "missing binding mode");
}
}
true
});
}
if !span_vec.is_empty(){
let span = MultiSpan::from_spans(span_vec.clone());
if !span_vec.is_empty() {
let mut err = struct_span_err!(
cx.tcx.sess,
span,
MultiSpan::from_spans(span_vec.clone()),
E0009,
"cannot bind by-move and by-ref in the same pattern",
);
if let Some(by_ref_span) = by_ref_span {
err.span_label(by_ref_span, "both by-ref and by-move used");
}
for span in span_vec.iter(){
for span in span_vec.iter() {
err.span_label(*span, "by-move pattern here");
}
err.emit();
@ -627,7 +604,7 @@ fn check_legality_of_move_bindings(
/// because of the way rvalues are handled in the borrow check. (See issue
/// #14587.)
fn check_legality_of_bindings_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pat) {
AtBindingPatternVisitor { cx: cx, bindings_allowed: true }.visit_pat(pat);
AtBindingPatternVisitor { cx, bindings_allowed: true }.visit_pat(pat);
}
struct AtBindingPatternVisitor<'a, 'b, 'tcx> {

View File

@ -6,6 +6,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
#![feature(nll)]
#![feature(in_band_lifetimes)]
#![feature(inner_deref)]
#![feature(slice_patterns)]
#![feature(box_patterns)]
#![feature(box_syntax)]

View File

@ -499,19 +499,15 @@ fn check_expr_kind<'a, 'tcx>(
}
// Conditional control flow (possible to implement).
hir::ExprKind::Match(ref expr, ref hirvec_arm, ref _match_source) => {
hir::ExprKind::Match(ref expr, ref arms, ref _match_source) => {
// Compute the most demanding borrow from all the arms'
// patterns and set that on the discriminator.
let mut mut_borrow = false;
for pat in hirvec_arm.iter().flat_map(|arm| &arm.pats) {
mut_borrow = v.remove_mut_rvalue_borrow(pat);
}
if mut_borrow {
if arms.iter().fold(false, |_, arm| v.remove_mut_rvalue_borrow(&arm.pat)) {
v.mut_rvalue_borrows.insert(expr.hir_id);
}
let _ = v.check_expr(expr);
for index in hirvec_arm.iter() {
for index in arms.iter() {
let _ = v.check_expr(&*index.body);
if let Some(hir::Guard::If(ref expr)) = index.guard {
let _ = v.check_expr(&expr);

View File

@ -58,11 +58,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// collection into `Vec`), so we get types for all bindings.
let all_arm_pats_diverge: Vec<_> = arms.iter().map(|arm| {
let mut all_pats_diverge = Diverges::WarnedAlways;
for p in &arm.pats {
self.diverges.set(Diverges::Maybe);
self.check_pat_top(&p, discrim_ty, Some(discrim.span));
all_pats_diverge &= self.diverges.get();
}
self.diverges.set(Diverges::Maybe);
self.check_pat_top(&arm.pat, discrim_ty, Some(discrim.span));
all_pats_diverge &= self.diverges.get();
// As discussed with @eddyb, this is for disabling unreachable_code
// warnings on patterns (they're now subsumed by unreachable_patterns
@ -428,11 +426,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
//
// See #44848.
let contains_ref_bindings = arms.iter()
.filter_map(|a| a.contains_explicit_ref_binding())
.max_by_key(|m| match *m {
hir::MutMutable => 1,
hir::MutImmutable => 0,
});
.filter_map(|a| a.pat.contains_explicit_ref_binding())
.max_by_key(|m| match *m {
hir::MutMutable => 1,
hir::MutImmutable => 0,
});
if let Some(m) = contains_ref_bindings {
self.check_expr_with_needs(discrim, Needs::maybe_mut_place(m))

View File

@ -97,11 +97,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.check_pat_struct(pat, qpath, fields, *etc, expected, def_bm, discrim_span)
}
PatKind::Or(pats) => {
let expected_ty = self.structurally_resolved_type(pat.span, expected);
for pat in pats {
self.check_pat(pat, expected, def_bm, discrim_span);
}
expected_ty
expected
}
PatKind::Tuple(elements, ddpos) => {
self.check_pat_tuple(pat.span, elements, *ddpos, expected, def_bm, discrim_span)
@ -208,7 +207,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match pat.node {
PatKind::Struct(..) |
PatKind::TupleStruct(..) |
PatKind::Or(_) |
PatKind::Tuple(..) |
PatKind::Box(_) |
PatKind::Range(..) |
@ -226,6 +224,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => true,
}
}
// FIXME(or_patterns; Centril | dlrobertson): To keep things compiling
// for or-patterns at the top level, we need to make `p_0 | ... | p_n`
// a "non reference pattern". For example the following currently compiles:
// ```
// match &1 {
// e @ &(1...2) | e @ &(3...4) => {}
// _ => {}
// }
// ```
//
// We should consider whether we should do something special in nested or-patterns.
PatKind::Or(_) |
PatKind::Wild |
PatKind::Binding(..) |
PatKind::Ref(..) => false,
@ -426,12 +436,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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 and T is the expected type.
let region_ty = self.new_ref_ty(pat.span, mutbl, expected);
//
// `x` is assigned a value of type `&M T`, hence `&M T <: typeof(x)`
// is required. However, we use equality, which is stronger.
// See (note_1) for an explanation.
region_ty
self.new_ref_ty(pat.span, mutbl, expected)
}
// Otherwise, the type of x is the expected type `T`.
ty::BindByValue(_) => {

View File

@ -488,9 +488,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionCtxt<'a, 'tcx> {
fn visit_arm(&mut self, arm: &'tcx hir::Arm) {
// see above
for p in &arm.pats {
self.constrain_bindings_in_pat(p);
}
self.constrain_bindings_in_pat(&arm.pat);
intravisit::walk_arm(self, arm);
}
@ -1069,9 +1067,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
let discr_cmt = Rc::new(ignore_err!(self.with_mc(|mc| mc.cat_expr(discr))));
debug!("discr_cmt={:?}", discr_cmt);
for arm in arms {
for root_pat in &arm.pats {
self.link_pattern(discr_cmt.clone(), &root_pat);
}
self.link_pattern(discr_cmt.clone(), &arm.pat);
}
}

View File

@ -834,8 +834,6 @@ pub fn walk_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a Param) {
pub fn walk_arm<'a, V: Visitor<'a>>(visitor: &mut V, arm: &'a Arm) {
visitor.visit_pat(&arm.pat);
// NOTE(or_patterns; Centril | dlrobertson):
// If you change this, also change the hack in `lowering.rs`.
walk_list!(visitor, visit_expr, &arm.guard);
visitor.visit_expr(&arm.body);
walk_list!(visitor, visit_attribute, &arm.attrs);

View File

@ -33,6 +33,9 @@ fn main() {
let mut mut_unused_var = 1;
let (mut var, unused_var) = (1, 2);
// NOTE: `var` comes after `unused_var` lexicographically yet the warning
// for `var` will be emitted before the one for `unused_var`. We use an
// `IndexMap` to ensure this is the case instead of a `BTreeMap`.
if let SoulHistory { corridors_of_light,
mut hours_are_suns,

View File

@ -30,13 +30,13 @@ LL | let (mut var, unused_var) = (1, 2);
| ^^^^^^^^^^ help: consider prefixing with an underscore: `_unused_var`
warning: unused variable: `corridors_of_light`
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:37:26
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:40:26
|
LL | if let SoulHistory { corridors_of_light,
| ^^^^^^^^^^^^^^^^^^ help: try ignoring the field: `corridors_of_light: _`
warning: variable `hours_are_suns` is assigned to, but never used
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:38:30
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:41:30
|
LL | mut hours_are_suns,
| ^^^^^^^^^^^^^^
@ -44,7 +44,7 @@ LL | mut hours_are_suns,
= note: consider using `_hours_are_suns` instead
warning: value assigned to `hours_are_suns` is never read
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:40:9
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:43:9
|
LL | hours_are_suns = false;
| ^^^^^^^^^^^^^^
@ -58,43 +58,43 @@ LL | #![warn(unused)] // UI tests pass `-A unused` (#43896)
= help: maybe it is overwritten before being read?
warning: unused variable: `fire`
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:44:32
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:47:32
|
LL | let LovelyAmbition { lips, fire } = the_spirit;
| ^^^^ help: try ignoring the field: `fire: _`
warning: unused variable: `case`
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:53:23
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:56:23
|
LL | Large::Suit { case } => {}
| ^^^^ help: try ignoring the field: `case: _`
warning: unused variable: `case`
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:58:24
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:61:24
|
LL | &Large::Suit { case } => {}
| ^^^^ help: try ignoring the field: `case: _`
warning: unused variable: `case`
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:63:27
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:66:27
|
LL | box Large::Suit { case } => {}
| ^^^^ help: try ignoring the field: `case: _`
warning: unused variable: `case`
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:68:24
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:71:24
|
LL | (Large::Suit { case },) => {}
| ^^^^ help: try ignoring the field: `case: _`
warning: unused variable: `case`
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:73:24
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:76:24
|
LL | [Large::Suit { case }] => {}
| ^^^^ help: try ignoring the field: `case: _`
warning: unused variable: `case`
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:78:29
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:81:29
|
LL | Tuple(Large::Suit { case }, ()) => {}
| ^^^^ help: try ignoring the field: `case: _`