2014-02-10 08:36:31 -06:00
|
|
|
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
2012-12-03 18:48:01 -06:00
|
|
|
// 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.
|
|
|
|
|
2014-06-23 17:12:17 -05:00
|
|
|
use middle::const_eval::{compare_const_vals, const_bool, const_float, const_nil, const_val};
|
2014-07-13 08:12:47 -05:00
|
|
|
use middle::const_eval::{const_expr_to_pat, eval_const_expr, lookup_const_by_id};
|
2014-05-14 14:31:30 -05:00
|
|
|
use middle::def::*;
|
librustc: Disallow mutation and assignment in pattern guards, and modify
the CFG for match statements.
There were two bugs in issue #14684. One was simply that the borrow
check didn't know about the correct CFG for match statements: the
pattern must be a predecessor of the guard. This disallows the bad
behavior if there are bindings in the pattern. But it isn't enough to
prevent the memory safety problem, because of wildcards; thus, this
patch introduces a more restrictive rule, which disallows assignments
and mutable borrows inside guards outright.
I discussed this with Niko and we decided this was the best plan of
action.
This breaks code that performs mutable borrows in pattern guards. Most
commonly, the code looks like this:
impl Foo {
fn f(&mut self, ...) {}
fn g(&mut self, ...) {
match bar {
Baz if self.f(...) => { ... }
_ => { ... }
}
}
}
Change this code to not use a guard. For example:
impl Foo {
fn f(&mut self, ...) {}
fn g(&mut self, ...) {
match bar {
Baz => {
if self.f(...) {
...
} else {
...
}
}
_ => { ... }
}
}
}
Sometimes this can result in code duplication, but often it illustrates
a hidden memory safety problem.
Closes #14684.
[breaking-change]
2014-07-25 17:18:19 -05:00
|
|
|
use middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Init};
|
|
|
|
use middle::expr_use_visitor::{JustWrite, LoanCause, MutateMode};
|
|
|
|
use middle::expr_use_visitor::{WriteAndRead};
|
|
|
|
use middle::mem_categorization::cmt;
|
2012-12-13 15:05:22 -06:00
|
|
|
use middle::pat_util::*;
|
|
|
|
use middle::ty::*;
|
|
|
|
use middle::ty;
|
2014-06-25 12:07:37 -05:00
|
|
|
use std::fmt;
|
2014-06-11 21:33:52 -05:00
|
|
|
use std::gc::{Gc, GC};
|
2014-06-25 12:07:37 -05:00
|
|
|
use std::iter::AdditiveIterator;
|
|
|
|
use std::iter::range_inclusive;
|
2012-09-04 13:54:36 -05:00
|
|
|
use syntax::ast::*;
|
2014-08-17 15:10:25 -05:00
|
|
|
use syntax::ast_util::walk_pat;
|
2014-06-07 07:17:01 -05:00
|
|
|
use syntax::codemap::{Span, Spanned, DUMMY_SP};
|
2014-07-13 08:12:47 -05:00
|
|
|
use syntax::fold::{Folder, noop_fold_pat};
|
2014-06-21 05:39:03 -05:00
|
|
|
use syntax::print::pprust::pat_to_string;
|
2014-07-13 08:12:47 -05:00
|
|
|
use syntax::parse::token;
|
2014-08-30 09:22:19 -05:00
|
|
|
use syntax::visit::{mod, Visitor, FnKind};
|
2014-06-21 05:39:03 -05:00
|
|
|
use util::ppaux::ty_to_string;
|
2014-06-07 07:17:01 -05:00
|
|
|
|
2014-06-25 12:07:37 -05:00
|
|
|
struct Matrix(Vec<Vec<Gc<Pat>>>);
|
|
|
|
|
|
|
|
/// Pretty-printer for matrices of patterns, example:
|
|
|
|
/// ++++++++++++++++++++++++++
|
|
|
|
/// + _ + [] +
|
|
|
|
/// ++++++++++++++++++++++++++
|
|
|
|
/// + true + [First] +
|
|
|
|
/// ++++++++++++++++++++++++++
|
|
|
|
/// + true + [Second(true)] +
|
|
|
|
/// ++++++++++++++++++++++++++
|
|
|
|
/// + false + [_] +
|
|
|
|
/// ++++++++++++++++++++++++++
|
|
|
|
/// + _ + [_, _, ..tail] +
|
|
|
|
/// ++++++++++++++++++++++++++
|
|
|
|
impl fmt::Show for Matrix {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
try!(write!(f, "\n"));
|
|
|
|
|
|
|
|
let &Matrix(ref m) = self;
|
|
|
|
let pretty_printed_matrix: Vec<Vec<String>> = m.iter().map(|row| {
|
2014-07-07 18:35:15 -05:00
|
|
|
row.iter()
|
|
|
|
.map(|&pat| pat_to_string(&*pat))
|
|
|
|
.collect::<Vec<String>>()
|
2014-06-25 12:07:37 -05:00
|
|
|
}).collect();
|
|
|
|
|
|
|
|
let column_count = m.iter().map(|row| row.len()).max().unwrap_or(0u);
|
|
|
|
assert!(m.iter().all(|row| row.len() == column_count));
|
|
|
|
let column_widths: Vec<uint> = range(0, column_count).map(|col| {
|
|
|
|
pretty_printed_matrix.iter().map(|row| row.get(col).len()).max().unwrap_or(0u)
|
|
|
|
}).collect();
|
|
|
|
|
|
|
|
let total_width = column_widths.iter().map(|n| *n).sum() + column_count * 3 + 1;
|
|
|
|
let br = String::from_char(total_width, '+');
|
|
|
|
try!(write!(f, "{}\n", br));
|
|
|
|
for row in pretty_printed_matrix.move_iter() {
|
|
|
|
try!(write!(f, "+"));
|
|
|
|
for (column, pat_str) in row.move_iter().enumerate() {
|
|
|
|
try!(write!(f, " "));
|
|
|
|
f.width = Some(*column_widths.get(column));
|
|
|
|
try!(f.pad(pat_str.as_slice()));
|
|
|
|
try!(write!(f, " +"));
|
|
|
|
}
|
|
|
|
try!(write!(f, "\n"));
|
|
|
|
try!(write!(f, "{}\n", br));
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-13 08:12:47 -05:00
|
|
|
impl FromIterator<Vec<Gc<Pat>>> for Matrix {
|
|
|
|
fn from_iter<T: Iterator<Vec<Gc<Pat>>>>(mut iterator: T) -> Matrix {
|
|
|
|
Matrix(iterator.collect())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
pub struct MatchCheckCtxt<'a, 'tcx: 'a> {
|
|
|
|
pub tcx: &'a ty::ctxt<'tcx>
|
2014-06-25 12:07:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[deriving(Clone, PartialEq)]
|
2014-06-21 07:56:23 -05:00
|
|
|
pub enum Constructor {
|
2014-06-25 12:07:37 -05:00
|
|
|
/// The constructor of all patterns that don't vary by constructor,
|
|
|
|
/// e.g. struct patterns and fixed-length arrays.
|
|
|
|
Single,
|
|
|
|
/// Enum variants.
|
|
|
|
Variant(DefId),
|
|
|
|
/// Literal values.
|
|
|
|
ConstantValue(const_val),
|
|
|
|
/// Ranges of literal values (2..5).
|
|
|
|
ConstantRange(const_val, const_val),
|
|
|
|
/// Array patterns of length n.
|
2014-08-30 09:22:19 -05:00
|
|
|
Slice(uint),
|
|
|
|
/// Array patterns with a subslice.
|
|
|
|
SliceWithSubslice(uint, uint)
|
2014-06-25 12:07:37 -05:00
|
|
|
}
|
2014-06-07 07:17:01 -05:00
|
|
|
|
2014-07-03 15:53:20 -05:00
|
|
|
#[deriving(Clone, PartialEq)]
|
2014-06-07 07:17:01 -05:00
|
|
|
enum Usefulness {
|
2014-07-03 15:53:20 -05:00
|
|
|
Useful,
|
|
|
|
UsefulWithWitness(Vec<Gc<Pat>>),
|
2014-06-07 07:17:01 -05:00
|
|
|
NotUseful
|
|
|
|
}
|
|
|
|
|
|
|
|
enum WitnessPreference {
|
|
|
|
ConstructWitness,
|
|
|
|
LeaveOutWitness
|
|
|
|
}
|
|
|
|
|
2014-09-09 17:54:36 -05:00
|
|
|
impl<'a, 'tcx, 'v> Visitor<'v> for MatchCheckCtxt<'a, 'tcx> {
|
2014-09-12 05:10:30 -05:00
|
|
|
fn visit_expr(&mut self, ex: &Expr) {
|
2014-03-05 21:07:47 -06:00
|
|
|
check_expr(self, ex);
|
2013-08-12 20:29:15 -05:00
|
|
|
}
|
2014-09-12 05:10:30 -05:00
|
|
|
fn visit_local(&mut self, l: &Local) {
|
2014-03-05 21:07:47 -06:00
|
|
|
check_local(self, l);
|
2013-08-12 20:29:15 -05:00
|
|
|
}
|
2014-09-09 17:54:36 -05:00
|
|
|
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl,
|
|
|
|
b: &'v Block, s: Span, _: NodeId) {
|
2014-05-14 01:20:25 -05:00
|
|
|
check_fn(self, fk, fd, b, s);
|
2013-08-12 20:29:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-25 12:07:37 -05:00
|
|
|
pub fn check_crate(tcx: &ty::ctxt, krate: &Crate) {
|
2014-07-13 08:12:47 -05:00
|
|
|
let mut cx = MatchCheckCtxt { tcx: tcx };
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_crate(&mut cx, krate);
|
2011-07-25 06:45:09 -05:00
|
|
|
tcx.sess.abort_if_errors();
|
|
|
|
}
|
|
|
|
|
2014-03-05 21:07:47 -06:00
|
|
|
fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_expr(cx, ex);
|
2012-08-06 14:34:08 -05:00
|
|
|
match ex.node {
|
2014-06-07 07:17:01 -05:00
|
|
|
ExprMatch(scrut, ref arms) => {
|
|
|
|
// First, check legality of move bindings.
|
|
|
|
for arm in arms.iter() {
|
|
|
|
check_legality_of_move_bindings(cx,
|
|
|
|
arm.guard.is_some(),
|
|
|
|
arm.pats.as_slice());
|
2014-07-28 13:33:06 -05:00
|
|
|
for pat in arm.pats.iter() {
|
|
|
|
check_legality_of_bindings_in_at_patterns(cx, &**pat);
|
|
|
|
}
|
2014-06-07 07:17:01 -05:00
|
|
|
}
|
2012-12-05 21:01:14 -06:00
|
|
|
|
librustc: Disallow mutation and assignment in pattern guards, and modify
the CFG for match statements.
There were two bugs in issue #14684. One was simply that the borrow
check didn't know about the correct CFG for match statements: the
pattern must be a predecessor of the guard. This disallows the bad
behavior if there are bindings in the pattern. But it isn't enough to
prevent the memory safety problem, because of wildcards; thus, this
patch introduces a more restrictive rule, which disallows assignments
and mutable borrows inside guards outright.
I discussed this with Niko and we decided this was the best plan of
action.
This breaks code that performs mutable borrows in pattern guards. Most
commonly, the code looks like this:
impl Foo {
fn f(&mut self, ...) {}
fn g(&mut self, ...) {
match bar {
Baz if self.f(...) => { ... }
_ => { ... }
}
}
}
Change this code to not use a guard. For example:
impl Foo {
fn f(&mut self, ...) {}
fn g(&mut self, ...) {
match bar {
Baz => {
if self.f(...) {
...
} else {
...
}
}
_ => { ... }
}
}
}
Sometimes this can result in code duplication, but often it illustrates
a hidden memory safety problem.
Closes #14684.
[breaking-change]
2014-07-25 17:18:19 -05:00
|
|
|
// Second, if there is a guard on each arm, make sure it isn't
|
|
|
|
// assigning or borrowing anything mutably.
|
|
|
|
for arm in arms.iter() {
|
|
|
|
match arm.guard {
|
|
|
|
Some(guard) => check_for_mutation_in_guard(cx, &*guard),
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-17 15:10:25 -05:00
|
|
|
let mut static_inliner = StaticInliner::new(cx.tcx);
|
|
|
|
let inlined_arms = arms
|
|
|
|
.iter()
|
|
|
|
.map(|arm| Arm {
|
|
|
|
pats: arm.pats.iter().map(|pat| {
|
|
|
|
static_inliner.fold_pat(*pat)
|
|
|
|
}).collect(),
|
|
|
|
..arm.clone()
|
|
|
|
})
|
|
|
|
.collect::<Vec<Arm>>();
|
|
|
|
|
|
|
|
if static_inliner.failed {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Third, check if there are any references to NaN that we should warn about.
|
|
|
|
check_for_static_nan(cx, inlined_arms.as_slice());
|
|
|
|
|
|
|
|
// Fourth, check for unreachable arms.
|
|
|
|
check_arms(cx, inlined_arms.as_slice());
|
2014-06-19 13:55:12 -05:00
|
|
|
|
|
|
|
// Finally, check if the whole match expression is exhaustive.
|
|
|
|
// Check for empty enum, because is_useful only works on inhabited types.
|
2014-06-07 07:17:01 -05:00
|
|
|
let pat_ty = node_id_to_type(cx.tcx, scrut.id);
|
2014-08-17 15:10:25 -05:00
|
|
|
if inlined_arms.is_empty() {
|
2014-07-11 11:54:01 -05:00
|
|
|
if !type_is_empty(cx.tcx, pat_ty) {
|
|
|
|
// We know the type is inhabited, so this must be wrong
|
|
|
|
span_err!(cx.tcx.sess, ex.span, E0002,
|
|
|
|
"non-exhaustive patterns: type {} is non-empty",
|
|
|
|
ty_to_string(cx.tcx, pat_ty)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
// If the type *is* empty, it's vacuously exhaustive
|
|
|
|
return;
|
2014-06-07 07:17:01 -05:00
|
|
|
}
|
2014-07-13 08:12:47 -05:00
|
|
|
|
2014-08-17 15:10:25 -05:00
|
|
|
let matrix: Matrix = inlined_arms
|
|
|
|
.move_iter()
|
|
|
|
.filter(|arm| arm.guard.is_none())
|
|
|
|
.flat_map(|arm| arm.pats.move_iter())
|
|
|
|
.map(|pat| vec![pat])
|
2014-07-13 08:12:47 -05:00
|
|
|
.collect();
|
|
|
|
check_exhaustive(cx, ex.span, &matrix);
|
2014-06-07 07:17:01 -05:00
|
|
|
},
|
2014-07-21 22:54:28 -05:00
|
|
|
ExprForLoop(ref pat, _, _, _) => {
|
2014-08-17 15:10:25 -05:00
|
|
|
let mut static_inliner = StaticInliner::new(cx.tcx);
|
2014-07-21 22:54:28 -05:00
|
|
|
match is_refutable(cx, static_inliner.fold_pat(*pat)) {
|
|
|
|
Some(uncovered_pat) => {
|
|
|
|
cx.tcx.sess.span_err(
|
|
|
|
pat.span,
|
|
|
|
format!("refutable pattern in `for` loop binding: \
|
|
|
|
`{}` not covered",
|
|
|
|
pat_to_string(&*uncovered_pat)).as_slice());
|
|
|
|
},
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check legality of move bindings.
|
|
|
|
check_legality_of_move_bindings(cx, false, [ *pat ]);
|
2014-07-28 13:33:06 -05:00
|
|
|
check_legality_of_bindings_in_at_patterns(cx, &**pat);
|
2014-07-21 22:54:28 -05:00
|
|
|
}
|
2014-06-07 07:17:01 -05:00
|
|
|
_ => ()
|
2012-01-14 18:05:07 -06:00
|
|
|
}
|
2011-07-25 06:45:09 -05:00
|
|
|
}
|
|
|
|
|
2014-07-13 08:12:47 -05:00
|
|
|
fn is_expr_const_nan(tcx: &ty::ctxt, expr: &Expr) -> bool {
|
|
|
|
match eval_const_expr(tcx, expr) {
|
|
|
|
const_float(f) => f.is_nan(),
|
|
|
|
_ => false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-17 15:10:25 -05:00
|
|
|
// Check that we do not match against a static NaN (#6804)
|
|
|
|
fn check_for_static_nan(cx: &MatchCheckCtxt, arms: &[Arm]) {
|
2013-08-03 11:45:23 -05:00
|
|
|
for arm in arms.iter() {
|
2014-08-17 15:10:25 -05:00
|
|
|
for &pat in arm.pats.iter() {
|
|
|
|
walk_pat(&*pat, |p| {
|
2014-07-13 08:12:47 -05:00
|
|
|
match p.node {
|
|
|
|
PatLit(expr) if is_expr_const_nan(cx.tcx, &*expr) => {
|
2014-08-17 15:10:25 -05:00
|
|
|
span_warn!(cx.tcx.sess, p.span, E0003,
|
2014-07-13 08:12:47 -05:00
|
|
|
"unmatchable NaN in pattern, \
|
|
|
|
use the is_nan method in a guard instead");
|
2013-07-24 11:00:33 -05:00
|
|
|
}
|
2014-07-13 08:12:47 -05:00
|
|
|
_ => ()
|
2013-07-24 11:00:33 -05:00
|
|
|
}
|
2013-08-02 01:17:20 -05:00
|
|
|
true
|
2013-11-21 17:42:55 -06:00
|
|
|
});
|
2014-08-17 15:10:25 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-07-24 11:00:33 -05:00
|
|
|
|
2014-08-17 15:10:25 -05:00
|
|
|
// Check for unreachable patterns
|
|
|
|
fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) {
|
|
|
|
let mut seen = Matrix(vec!());
|
|
|
|
for arm in arms.iter() {
|
|
|
|
for &pat in arm.pats.iter() {
|
|
|
|
let v = vec![pat];
|
2014-06-07 07:17:01 -05:00
|
|
|
match is_useful(cx, &seen, v.as_slice(), LeaveOutWitness) {
|
2014-07-01 11:39:41 -05:00
|
|
|
NotUseful => span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern"),
|
2014-07-03 15:53:20 -05:00
|
|
|
Useful => (),
|
|
|
|
UsefulWithWitness(_) => unreachable!()
|
2011-07-25 06:45:09 -05:00
|
|
|
}
|
2014-06-25 12:07:37 -05:00
|
|
|
if arm.guard.is_none() {
|
|
|
|
let Matrix(mut rows) = seen;
|
|
|
|
rows.push(v);
|
|
|
|
seen = Matrix(rows);
|
|
|
|
}
|
2011-07-25 06:45:09 -05:00
|
|
|
}
|
|
|
|
}
|
2012-01-30 23:00:57 -06:00
|
|
|
}
|
|
|
|
|
2014-07-13 08:12:47 -05:00
|
|
|
fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, matrix: &Matrix) {
|
|
|
|
match is_useful(cx, matrix, [wild()], ConstructWitness) {
|
2014-07-03 15:53:20 -05:00
|
|
|
UsefulWithWitness(pats) => {
|
2014-06-07 07:17:01 -05:00
|
|
|
let witness = match pats.as_slice() {
|
2014-06-19 13:55:12 -05:00
|
|
|
[witness] => witness,
|
2014-06-07 07:17:01 -05:00
|
|
|
[] => wild(),
|
|
|
|
_ => unreachable!()
|
|
|
|
};
|
2014-07-11 11:54:01 -05:00
|
|
|
span_err!(cx.tcx.sess, sp, E0004,
|
|
|
|
"non-exhaustive patterns: `{}` not covered",
|
|
|
|
pat_to_string(&*witness)
|
|
|
|
);
|
2012-02-15 02:40:42 -06:00
|
|
|
}
|
2014-06-25 12:07:37 -05:00
|
|
|
NotUseful => {
|
|
|
|
// This is good, wildcard pattern isn't reachable
|
2014-07-03 15:53:20 -05:00
|
|
|
},
|
|
|
|
_ => unreachable!()
|
2014-06-07 07:17:01 -05:00
|
|
|
}
|
2014-01-10 16:02:36 -06:00
|
|
|
}
|
2012-04-24 04:13:25 -05:00
|
|
|
|
2014-06-07 07:17:01 -05:00
|
|
|
fn const_val_to_expr(value: &const_val) -> Gc<Expr> {
|
|
|
|
let node = match value {
|
|
|
|
&const_bool(b) => LitBool(b),
|
2014-06-23 17:12:17 -05:00
|
|
|
&const_nil => LitNil,
|
2014-06-07 07:17:01 -05:00
|
|
|
_ => unreachable!()
|
|
|
|
};
|
2014-06-25 12:07:37 -05:00
|
|
|
box (GC) Expr {
|
2014-06-07 07:17:01 -05:00
|
|
|
id: 0,
|
|
|
|
node: ExprLit(box(GC) Spanned { node: node, span: DUMMY_SP }),
|
|
|
|
span: DUMMY_SP
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
pub struct StaticInliner<'a, 'tcx: 'a> {
|
|
|
|
pub tcx: &'a ty::ctxt<'tcx>,
|
2014-08-17 15:10:25 -05:00
|
|
|
pub failed: bool
|
|
|
|
}
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
impl<'a, 'tcx> StaticInliner<'a, 'tcx> {
|
|
|
|
pub fn new<'a>(tcx: &'a ty::ctxt<'tcx>) -> StaticInliner<'a, 'tcx> {
|
2014-08-17 15:10:25 -05:00
|
|
|
StaticInliner {
|
|
|
|
tcx: tcx,
|
|
|
|
failed: false
|
|
|
|
}
|
|
|
|
}
|
2014-07-13 08:12:47 -05:00
|
|
|
}
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> {
|
2014-07-13 08:12:47 -05:00
|
|
|
fn fold_pat(&mut self, pat: Gc<Pat>) -> Gc<Pat> {
|
|
|
|
match pat.node {
|
|
|
|
PatIdent(..) | PatEnum(..) => {
|
|
|
|
let def = self.tcx.def_map.borrow().find_copy(&pat.id);
|
|
|
|
match def {
|
2014-08-17 15:10:25 -05:00
|
|
|
Some(DefStatic(did, _)) => match lookup_const_by_id(self.tcx, did) {
|
|
|
|
Some(const_expr) => box (GC) Pat {
|
|
|
|
span: pat.span,
|
|
|
|
..(*const_expr_to_pat(self.tcx, const_expr)).clone()
|
|
|
|
},
|
|
|
|
None => {
|
|
|
|
self.failed = true;
|
|
|
|
span_err!(self.tcx.sess, pat.span, E0158,
|
|
|
|
"extern statics cannot be referenced in patterns");
|
|
|
|
pat
|
|
|
|
}
|
2014-07-13 08:12:47 -05:00
|
|
|
},
|
|
|
|
_ => noop_fold_pat(pat, self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => noop_fold_pat(pat, self)
|
|
|
|
}
|
|
|
|
}
|
2014-06-25 12:07:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Constructs a partial witness for a pattern given a list of
|
|
|
|
/// patterns expanded by the specialization step.
|
|
|
|
///
|
|
|
|
/// When a pattern P is discovered to be useful, this function is used bottom-up
|
|
|
|
/// to reconstruct a complete witness, e.g. a pattern P' that covers a subset
|
|
|
|
/// of values, V, where each value in that set is not covered by any previously
|
|
|
|
/// used patterns and is covered by the pattern P'. Examples:
|
|
|
|
///
|
|
|
|
/// left_ty: tuple of 3 elements
|
|
|
|
/// pats: [10, 20, _] => (10, 20, _)
|
|
|
|
///
|
|
|
|
/// left_ty: struct X { a: (bool, &'static str), b: uint}
|
|
|
|
/// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 }
|
|
|
|
fn construct_witness(cx: &MatchCheckCtxt, ctor: &Constructor,
|
|
|
|
pats: Vec<Gc<Pat>>, left_ty: ty::t) -> Gc<Pat> {
|
|
|
|
let pat = match ty::get(left_ty).sty {
|
2014-06-07 07:17:01 -05:00
|
|
|
ty::ty_tup(_) => PatTup(pats),
|
|
|
|
|
2014-06-19 13:55:12 -05:00
|
|
|
ty::ty_enum(cid, _) | ty::ty_struct(cid, _) => {
|
|
|
|
let (vid, is_structure) = match ctor {
|
2014-07-13 08:12:47 -05:00
|
|
|
&Variant(vid) =>
|
|
|
|
(vid, ty::enum_variant_with_id(cx.tcx, cid, vid).arg_names.is_some()),
|
|
|
|
_ =>
|
|
|
|
(cid, ty::lookup_struct_fields(cx.tcx, cid).iter()
|
|
|
|
.any(|field| field.name != token::special_idents::unnamed_field.name))
|
2014-06-07 07:17:01 -05:00
|
|
|
};
|
2014-06-19 13:55:12 -05:00
|
|
|
if is_structure {
|
|
|
|
let fields = ty::lookup_struct_fields(cx.tcx, vid);
|
2014-07-07 13:54:50 -05:00
|
|
|
let field_pats: Vec<FieldPat> = fields.move_iter()
|
2014-06-19 13:55:12 -05:00
|
|
|
.zip(pats.iter())
|
2014-08-06 10:04:44 -05:00
|
|
|
.filter(|&(_, pat)| pat.node != PatWild(PatWildSingle))
|
2014-06-19 13:55:12 -05:00
|
|
|
.map(|(field, pat)| FieldPat {
|
|
|
|
ident: Ident::new(field.name),
|
|
|
|
pat: pat.clone()
|
|
|
|
}).collect();
|
2014-07-07 13:54:50 -05:00
|
|
|
let has_more_fields = field_pats.len() < pats.len();
|
|
|
|
PatStruct(def_to_path(cx.tcx, vid), field_pats, has_more_fields)
|
2014-06-19 13:55:12 -05:00
|
|
|
} else {
|
|
|
|
PatEnum(def_to_path(cx.tcx, vid), Some(pats))
|
|
|
|
}
|
2014-06-25 12:07:37 -05:00
|
|
|
}
|
2014-06-07 07:17:01 -05:00
|
|
|
|
|
|
|
ty::ty_rptr(_, ty::mt { ty: ty, .. }) => {
|
|
|
|
match ty::get(ty).sty {
|
2014-06-25 12:07:37 -05:00
|
|
|
ty::ty_vec(_, Some(n)) => match ctor {
|
|
|
|
&Single => {
|
|
|
|
assert_eq!(pats.len(), n);
|
|
|
|
PatVec(pats, None, vec!())
|
|
|
|
},
|
|
|
|
_ => unreachable!()
|
|
|
|
},
|
2014-06-07 07:17:01 -05:00
|
|
|
ty::ty_vec(_, None) => match ctor {
|
2014-06-25 12:07:37 -05:00
|
|
|
&Slice(n) => {
|
|
|
|
assert_eq!(pats.len(), n);
|
|
|
|
PatVec(pats, None, vec!())
|
|
|
|
},
|
2014-06-07 07:17:01 -05:00
|
|
|
_ => unreachable!()
|
|
|
|
},
|
2014-08-06 10:04:44 -05:00
|
|
|
ty::ty_str => PatWild(PatWildSingle),
|
2014-06-25 12:07:37 -05:00
|
|
|
|
2014-06-07 07:17:01 -05:00
|
|
|
_ => {
|
|
|
|
assert_eq!(pats.len(), 1);
|
|
|
|
PatRegion(pats.get(0).clone())
|
|
|
|
}
|
|
|
|
}
|
2014-06-25 12:07:37 -05:00
|
|
|
}
|
2014-06-07 07:17:01 -05:00
|
|
|
|
|
|
|
ty::ty_box(_) => {
|
|
|
|
assert_eq!(pats.len(), 1);
|
|
|
|
PatBox(pats.get(0).clone())
|
2014-06-25 12:07:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
ty::ty_vec(_, Some(len)) => {
|
|
|
|
assert_eq!(pats.len(), len);
|
|
|
|
PatVec(pats, None, vec!())
|
|
|
|
}
|
2014-06-07 07:17:01 -05:00
|
|
|
|
|
|
|
_ => {
|
2014-06-25 12:07:37 -05:00
|
|
|
match *ctor {
|
|
|
|
ConstantValue(ref v) => PatLit(const_val_to_expr(v)),
|
2014-08-06 10:04:44 -05:00
|
|
|
_ => PatWild(PatWildSingle),
|
2014-06-07 07:17:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-06-25 12:07:37 -05:00
|
|
|
box (GC) Pat {
|
2014-06-07 07:17:01 -05:00
|
|
|
id: 0,
|
|
|
|
node: pat,
|
|
|
|
span: DUMMY_SP
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-25 12:07:37 -05:00
|
|
|
fn missing_constructor(cx: &MatchCheckCtxt, &Matrix(ref rows): &Matrix,
|
|
|
|
left_ty: ty::t, max_slice_length: uint) -> Option<Constructor> {
|
|
|
|
let used_constructors: Vec<Constructor> = rows.iter()
|
|
|
|
.flat_map(|row| pat_constructors(cx, *row.get(0), left_ty, max_slice_length).move_iter())
|
2014-06-07 07:17:01 -05:00
|
|
|
.collect();
|
2014-06-25 12:07:37 -05:00
|
|
|
all_constructors(cx, left_ty, max_slice_length)
|
2014-06-07 07:17:01 -05:00
|
|
|
.move_iter()
|
|
|
|
.find(|c| !used_constructors.contains(c))
|
|
|
|
}
|
|
|
|
|
2014-06-25 12:07:37 -05:00
|
|
|
/// This determines the set of all possible constructors of a pattern matching
|
|
|
|
/// values of type `left_ty`. For vectors, this would normally be an infinite set
|
|
|
|
/// but is instead bounded by the maximum fixed length of slice patterns in
|
|
|
|
/// the column of patterns being analyzed.
|
|
|
|
fn all_constructors(cx: &MatchCheckCtxt, left_ty: ty::t,
|
|
|
|
max_slice_length: uint) -> Vec<Constructor> {
|
2014-06-07 07:17:01 -05:00
|
|
|
match ty::get(left_ty).sty {
|
|
|
|
ty::ty_bool =>
|
2014-06-25 12:07:37 -05:00
|
|
|
[true, false].iter().map(|b| ConstantValue(const_bool(*b))).collect(),
|
2014-06-07 07:17:01 -05:00
|
|
|
|
2014-06-23 17:12:17 -05:00
|
|
|
ty::ty_nil =>
|
2014-06-25 12:07:37 -05:00
|
|
|
vec!(ConstantValue(const_nil)),
|
2014-06-23 17:12:17 -05:00
|
|
|
|
2014-06-07 07:17:01 -05:00
|
|
|
ty::ty_rptr(_, ty::mt { ty: ty, .. }) => match ty::get(ty).sty {
|
2014-06-25 12:07:37 -05:00
|
|
|
ty::ty_vec(_, None) =>
|
|
|
|
range_inclusive(0, max_slice_length).map(|length| Slice(length)).collect(),
|
|
|
|
_ => vec!(Single)
|
2014-06-07 07:17:01 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
ty::ty_enum(eid, _) =>
|
2014-06-19 13:55:12 -05:00
|
|
|
ty::enum_variants(cx.tcx, eid)
|
|
|
|
.iter()
|
2014-06-25 12:07:37 -05:00
|
|
|
.map(|va| Variant(va.id))
|
2014-06-19 13:55:12 -05:00
|
|
|
.collect(),
|
2014-06-07 07:17:01 -05:00
|
|
|
|
|
|
|
_ =>
|
2014-06-25 12:07:37 -05:00
|
|
|
vec!(Single)
|
2014-06-07 07:17:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-24 04:13:25 -05:00
|
|
|
// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html
|
|
|
|
//
|
|
|
|
// Whether a vector `v` of patterns is 'useful' in relation to a set of such
|
|
|
|
// vectors `m` is defined as there being a set of inputs that will match `v`
|
|
|
|
// but not any of the sets in `m`.
|
|
|
|
//
|
|
|
|
// This is used both for reachability checking (if a pattern isn't useful in
|
|
|
|
// relation to preceding patterns, it is not reachable) and exhaustiveness
|
|
|
|
// checking (if a wildcard pattern is useful in relation to a matrix, the
|
|
|
|
// matrix isn't exhaustive).
|
|
|
|
|
2012-07-27 15:13:03 -05:00
|
|
|
// Note: is_useful doesn't work on empty types, as the paper notes.
|
|
|
|
// So it assumes that v is non-empty.
|
2014-07-28 13:33:06 -05:00
|
|
|
fn is_useful(cx: &MatchCheckCtxt,
|
|
|
|
matrix: &Matrix,
|
|
|
|
v: &[Gc<Pat>],
|
|
|
|
witness: WitnessPreference)
|
|
|
|
-> Usefulness {
|
|
|
|
let &Matrix(ref rows) = matrix;
|
2014-07-03 15:53:20 -05:00
|
|
|
debug!("{:}", matrix);
|
2014-06-25 12:07:37 -05:00
|
|
|
if rows.len() == 0u {
|
2014-07-03 15:53:20 -05:00
|
|
|
return match witness {
|
|
|
|
ConstructWitness => UsefulWithWitness(vec!()),
|
|
|
|
LeaveOutWitness => Useful
|
|
|
|
};
|
2014-03-08 14:36:22 -06:00
|
|
|
}
|
2014-06-25 12:07:37 -05:00
|
|
|
if rows.get(0).len() == 0u {
|
2014-06-07 07:17:01 -05:00
|
|
|
return NotUseful;
|
2014-03-08 14:36:22 -06:00
|
|
|
}
|
2014-06-25 12:07:37 -05:00
|
|
|
let real_pat = match rows.iter().find(|r| r.get(0).id != 0) {
|
2014-07-06 16:54:40 -05:00
|
|
|
Some(r) => raw_pat(*r.get(0)),
|
2014-06-07 07:17:01 -05:00
|
|
|
None if v.len() == 0 => return NotUseful,
|
2014-05-13 00:20:52 -05:00
|
|
|
None => v[0]
|
2012-04-24 04:13:25 -05:00
|
|
|
};
|
2014-06-07 07:17:01 -05:00
|
|
|
let left_ty = if real_pat.id == 0 {
|
|
|
|
ty::mk_nil()
|
|
|
|
} else {
|
|
|
|
ty::pat_ty(cx.tcx, &*real_pat)
|
|
|
|
};
|
|
|
|
|
2014-06-25 12:07:37 -05:00
|
|
|
let max_slice_length = rows.iter().filter_map(|row| match row.get(0).node {
|
|
|
|
PatVec(ref before, _, ref after) => Some(before.len() + after.len()),
|
|
|
|
_ => None
|
|
|
|
}).max().map_or(0, |v| v + 1);
|
|
|
|
|
|
|
|
let constructors = pat_constructors(cx, v[0], left_ty, max_slice_length);
|
|
|
|
if constructors.is_empty() {
|
2014-07-03 15:53:20 -05:00
|
|
|
match missing_constructor(cx, matrix, left_ty, max_slice_length) {
|
2014-06-07 07:17:01 -05:00
|
|
|
None => {
|
2014-07-03 15:53:20 -05:00
|
|
|
all_constructors(cx, left_ty, max_slice_length).move_iter().map(|c| {
|
|
|
|
match is_useful_specialized(cx, matrix, v, c.clone(), left_ty, witness) {
|
|
|
|
UsefulWithWitness(pats) => UsefulWithWitness({
|
|
|
|
let arity = constructor_arity(cx, &c, left_ty);
|
|
|
|
let subpats = {
|
|
|
|
let pat_slice = pats.as_slice();
|
|
|
|
Vec::from_fn(arity, |i| {
|
|
|
|
pat_slice.get(i).map(|p| p.clone())
|
|
|
|
.unwrap_or_else(|| wild())
|
|
|
|
})
|
|
|
|
};
|
|
|
|
let mut result = vec!(construct_witness(cx, &c, subpats, left_ty));
|
|
|
|
result.extend(pats.move_iter().skip(arity));
|
|
|
|
result
|
|
|
|
}),
|
|
|
|
result => result
|
|
|
|
}
|
|
|
|
}).find(|result| result != &NotUseful).unwrap_or(NotUseful)
|
2014-06-07 07:17:01 -05:00
|
|
|
},
|
|
|
|
|
2014-06-25 12:07:37 -05:00
|
|
|
Some(constructor) => {
|
2014-07-13 08:12:47 -05:00
|
|
|
let matrix = rows.iter().filter_map(|r| default(cx, r.as_slice())).collect();
|
2014-06-19 13:55:12 -05:00
|
|
|
match is_useful(cx, &matrix, v.tail(), witness) {
|
2014-07-03 15:53:20 -05:00
|
|
|
UsefulWithWitness(pats) => {
|
|
|
|
let arity = constructor_arity(cx, &constructor, left_ty);
|
|
|
|
let wild_pats = Vec::from_elem(arity, wild());
|
|
|
|
let enum_pat = construct_witness(cx, &constructor, wild_pats, left_ty);
|
|
|
|
UsefulWithWitness(vec!(enum_pat).append(pats.as_slice()))
|
|
|
|
},
|
2014-06-07 07:17:01 -05:00
|
|
|
result => result
|
|
|
|
}
|
2012-05-03 10:35:12 -05:00
|
|
|
}
|
2014-06-25 12:07:37 -05:00
|
|
|
}
|
|
|
|
} else {
|
2014-07-03 15:53:20 -05:00
|
|
|
constructors.move_iter().map(|c|
|
|
|
|
is_useful_specialized(cx, matrix, v, c.clone(), left_ty, witness)
|
|
|
|
).find(|result| result != &NotUseful).unwrap_or(NotUseful)
|
2012-01-30 23:00:57 -06:00
|
|
|
}
|
2011-07-25 06:45:09 -05:00
|
|
|
}
|
|
|
|
|
2014-06-25 12:07:37 -05:00
|
|
|
fn is_useful_specialized(cx: &MatchCheckCtxt, &Matrix(ref m): &Matrix, v: &[Gc<Pat>],
|
|
|
|
ctor: Constructor, lty: ty::t, witness: WitnessPreference) -> Usefulness {
|
2014-06-07 07:17:01 -05:00
|
|
|
let arity = constructor_arity(cx, &ctor, lty);
|
2014-06-25 12:07:37 -05:00
|
|
|
let matrix = Matrix(m.iter().filter_map(|r| {
|
2014-06-21 07:56:23 -05:00
|
|
|
specialize(cx, r.as_slice(), &ctor, 0u, arity)
|
2014-06-25 12:07:37 -05:00
|
|
|
}).collect());
|
2014-06-21 07:56:23 -05:00
|
|
|
match specialize(cx, v, &ctor, 0u, arity) {
|
2014-06-07 07:17:01 -05:00
|
|
|
Some(v) => is_useful(cx, &matrix, v.as_slice(), witness),
|
|
|
|
None => NotUseful
|
2011-07-25 06:45:09 -05:00
|
|
|
}
|
2012-04-24 04:13:25 -05:00
|
|
|
}
|
|
|
|
|
2014-06-25 12:07:37 -05:00
|
|
|
/// Determines the constructors that the given pattern can be specialized to.
|
|
|
|
///
|
|
|
|
/// In most cases, there's only one constructor that a specific pattern
|
|
|
|
/// represents, such as a specific enum variant or a specific literal value.
|
|
|
|
/// Slice patterns, however, can match slices of different lengths. For instance,
|
|
|
|
/// `[a, b, ..tail]` can match a slice of length 2, 3, 4 and so on.
|
|
|
|
///
|
|
|
|
/// On the other hand, a wild pattern and an identifier pattern cannot be
|
|
|
|
/// specialized in any way.
|
|
|
|
fn pat_constructors(cx: &MatchCheckCtxt, p: Gc<Pat>,
|
|
|
|
left_ty: ty::t, max_slice_length: uint) -> Vec<Constructor> {
|
2012-04-24 04:13:25 -05:00
|
|
|
let pat = raw_pat(p);
|
2013-03-20 00:17:42 -05:00
|
|
|
match pat.node {
|
2014-06-19 13:55:12 -05:00
|
|
|
PatIdent(..) =>
|
2014-06-07 07:17:01 -05:00
|
|
|
match cx.tcx.def_map.borrow().find(&pat.id) {
|
2014-07-13 08:12:47 -05:00
|
|
|
Some(&DefStatic(..)) =>
|
|
|
|
cx.tcx.sess.span_bug(pat.span, "static pattern should've been rewritten"),
|
2014-07-03 15:53:20 -05:00
|
|
|
Some(&DefStruct(_)) => vec!(Single),
|
2014-06-25 12:07:37 -05:00
|
|
|
Some(&DefVariant(_, id, _)) => vec!(Variant(id)),
|
|
|
|
_ => vec!()
|
2014-06-19 13:55:12 -05:00
|
|
|
},
|
|
|
|
PatEnum(..) =>
|
|
|
|
match cx.tcx.def_map.borrow().find(&pat.id) {
|
2014-07-13 08:12:47 -05:00
|
|
|
Some(&DefStatic(..)) =>
|
|
|
|
cx.tcx.sess.span_bug(pat.span, "static pattern should've been rewritten"),
|
2014-06-25 12:07:37 -05:00
|
|
|
Some(&DefVariant(_, id, _)) => vec!(Variant(id)),
|
|
|
|
_ => vec!(Single)
|
2014-06-19 13:55:12 -05:00
|
|
|
},
|
|
|
|
PatStruct(..) =>
|
|
|
|
match cx.tcx.def_map.borrow().find(&pat.id) {
|
2014-07-13 08:12:47 -05:00
|
|
|
Some(&DefStatic(..)) =>
|
|
|
|
cx.tcx.sess.span_bug(pat.span, "static pattern should've been rewritten"),
|
2014-06-25 12:07:37 -05:00
|
|
|
Some(&DefVariant(_, id, _)) => vec!(Variant(id)),
|
|
|
|
_ => vec!(Single)
|
2014-06-07 07:17:01 -05:00
|
|
|
},
|
|
|
|
PatLit(expr) =>
|
2014-06-25 12:07:37 -05:00
|
|
|
vec!(ConstantValue(eval_const_expr(cx.tcx, &*expr))),
|
2014-06-07 07:17:01 -05:00
|
|
|
PatRange(lo, hi) =>
|
2014-06-25 12:07:37 -05:00
|
|
|
vec!(ConstantRange(eval_const_expr(cx.tcx, &*lo), eval_const_expr(cx.tcx, &*hi))),
|
|
|
|
PatVec(ref before, ref slice, ref after) =>
|
|
|
|
match ty::get(left_ty).sty {
|
|
|
|
ty::ty_vec(_, Some(_)) => vec!(Single),
|
|
|
|
_ => if slice.is_some() {
|
|
|
|
range_inclusive(before.len() + after.len(), max_slice_length)
|
|
|
|
.map(|length| Slice(length))
|
|
|
|
.collect()
|
|
|
|
} else {
|
|
|
|
vec!(Slice(before.len() + after.len()))
|
|
|
|
}
|
|
|
|
},
|
2014-06-07 07:17:01 -05:00
|
|
|
PatBox(_) | PatTup(_) | PatRegion(..) =>
|
2014-06-25 12:07:37 -05:00
|
|
|
vec!(Single),
|
2014-08-06 10:04:44 -05:00
|
|
|
PatWild(_) =>
|
2014-06-25 12:07:37 -05:00
|
|
|
vec!(),
|
2014-06-07 07:17:01 -05:00
|
|
|
PatMac(_) =>
|
|
|
|
cx.tcx.sess.bug("unexpanded macro")
|
2011-07-25 06:45:09 -05:00
|
|
|
}
|
2012-04-24 04:13:25 -05:00
|
|
|
}
|
2011-07-25 06:45:09 -05:00
|
|
|
|
2014-06-25 12:07:37 -05:00
|
|
|
/// This computes the arity of a constructor. The arity of a constructor
|
|
|
|
/// is how many subpattern patterns of that constructor should be expanded to.
|
|
|
|
///
|
|
|
|
/// For instance, a tuple pattern (_, 42u, Some([])) has the arity of 3.
|
|
|
|
/// A struct pattern's arity is the number of fields it contains, etc.
|
2014-06-21 07:56:23 -05:00
|
|
|
pub fn constructor_arity(cx: &MatchCheckCtxt, ctor: &Constructor, ty: ty::t) -> uint {
|
2014-04-09 02:15:31 -05:00
|
|
|
match ty::get(ty).sty {
|
|
|
|
ty::ty_tup(ref fs) => fs.len(),
|
2014-06-07 07:17:01 -05:00
|
|
|
ty::ty_box(_) | ty::ty_uniq(_) => 1u,
|
|
|
|
ty::ty_rptr(_, ty::mt { ty: ty, .. }) => match ty::get(ty).sty {
|
|
|
|
ty::ty_vec(_, None) => match *ctor {
|
2014-06-25 12:07:37 -05:00
|
|
|
Slice(length) => length,
|
2014-07-05 09:22:21 -05:00
|
|
|
ConstantValue(_) => 0u,
|
2014-06-25 12:07:37 -05:00
|
|
|
_ => unreachable!()
|
2014-06-07 07:17:01 -05:00
|
|
|
},
|
2014-06-08 18:28:26 -05:00
|
|
|
ty::ty_str => 0u,
|
2014-06-07 07:17:01 -05:00
|
|
|
_ => 1u
|
2014-04-09 02:15:31 -05:00
|
|
|
},
|
|
|
|
ty::ty_enum(eid, _) => {
|
2014-06-07 07:17:01 -05:00
|
|
|
match *ctor {
|
2014-06-25 12:07:37 -05:00
|
|
|
Variant(id) => enum_variant_with_id(cx.tcx, eid, id).args.len(),
|
2014-06-07 07:17:01 -05:00
|
|
|
_ => unreachable!()
|
2014-04-09 02:15:31 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ty::ty_struct(cid, _) => ty::lookup_struct_fields(cx.tcx, cid).len(),
|
2014-06-25 12:07:37 -05:00
|
|
|
ty::ty_vec(_, Some(n)) => n,
|
2014-04-09 02:15:31 -05:00
|
|
|
_ => 0u
|
2012-04-24 04:13:25 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-25 12:07:37 -05:00
|
|
|
fn range_covered_by_constructor(ctor: &Constructor,
|
2014-07-13 08:12:47 -05:00
|
|
|
from: &const_val, to: &const_val) -> Option<bool> {
|
2014-06-25 12:07:37 -05:00
|
|
|
let (c_from, c_to) = match *ctor {
|
|
|
|
ConstantValue(ref value) => (value, value),
|
|
|
|
ConstantRange(ref from, ref to) => (from, to),
|
|
|
|
Single => return Some(true),
|
|
|
|
_ => unreachable!()
|
2014-06-02 13:49:44 -05:00
|
|
|
};
|
|
|
|
let cmp_from = compare_const_vals(c_from, from);
|
|
|
|
let cmp_to = compare_const_vals(c_to, to);
|
|
|
|
match (cmp_from, cmp_to) {
|
|
|
|
(Some(val1), Some(val2)) => Some(val1 >= 0 && val2 <= 0),
|
|
|
|
_ => None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-25 12:07:37 -05:00
|
|
|
/// This is the main specialization step. It expands the first pattern in the given row
|
|
|
|
/// into `arity` patterns based on the constructor. For most patterns, the step is trivial,
|
|
|
|
/// for instance tuple patterns are flattened and box patterns expand into their inner pattern.
|
|
|
|
///
|
|
|
|
/// OTOH, slice patterns with a subslice pattern (..tail) can be expanded into multiple
|
|
|
|
/// different patterns.
|
|
|
|
/// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing
|
|
|
|
/// fields filled with wild patterns.
|
2014-06-21 07:56:23 -05:00
|
|
|
pub fn specialize(cx: &MatchCheckCtxt, r: &[Gc<Pat>],
|
|
|
|
constructor: &Constructor, col: uint, arity: uint) -> Option<Vec<Gc<Pat>>> {
|
2014-06-07 07:17:01 -05:00
|
|
|
let &Pat {
|
2014-06-25 12:07:37 -05:00
|
|
|
id: pat_id, node: ref node, span: pat_span
|
2014-06-21 07:56:23 -05:00
|
|
|
} = &(*raw_pat(r[col]));
|
2014-06-25 12:07:37 -05:00
|
|
|
let head: Option<Vec<Gc<Pat>>> = match node {
|
2014-08-06 10:04:44 -05:00
|
|
|
&PatWild(_) =>
|
2014-06-25 12:07:37 -05:00
|
|
|
Some(Vec::from_elem(arity, wild())),
|
|
|
|
|
2014-06-07 07:17:01 -05:00
|
|
|
&PatIdent(_, _, _) => {
|
2014-06-25 12:07:37 -05:00
|
|
|
let opt_def = cx.tcx.def_map.borrow().find_copy(&pat_id);
|
2014-06-07 07:17:01 -05:00
|
|
|
match opt_def {
|
2014-07-13 08:12:47 -05:00
|
|
|
Some(DefStatic(..)) =>
|
|
|
|
cx.tcx.sess.span_bug(pat_span, "static pattern should've been rewritten"),
|
2014-06-25 12:07:37 -05:00
|
|
|
Some(DefVariant(_, id, _)) => if *constructor == Variant(id) {
|
2014-06-21 16:16:31 -05:00
|
|
|
Some(vec!())
|
|
|
|
} else {
|
|
|
|
None
|
2014-06-19 13:55:12 -05:00
|
|
|
},
|
2014-07-13 08:12:47 -05:00
|
|
|
_ => Some(Vec::from_elem(arity, wild()))
|
2012-11-02 11:56:09 -05:00
|
|
|
}
|
2014-06-07 07:17:01 -05:00
|
|
|
}
|
2014-06-25 12:07:37 -05:00
|
|
|
|
2014-06-07 07:17:01 -05:00
|
|
|
&PatEnum(_, ref args) => {
|
2014-06-25 12:07:37 -05:00
|
|
|
let def = cx.tcx.def_map.borrow().get_copy(&pat_id);
|
2014-06-07 07:17:01 -05:00
|
|
|
match def {
|
2014-07-13 08:12:47 -05:00
|
|
|
DefStatic(..) =>
|
|
|
|
cx.tcx.sess.span_bug(pat_span, "static pattern should've been rewritten"),
|
2014-06-25 12:07:37 -05:00
|
|
|
DefVariant(_, id, _) if *constructor != Variant(id) => None,
|
2014-06-07 07:17:01 -05:00
|
|
|
DefVariant(..) | DefFn(..) | DefStruct(..) => {
|
|
|
|
Some(match args {
|
|
|
|
&Some(ref args) => args.clone(),
|
|
|
|
&None => Vec::from_elem(arity, wild())
|
|
|
|
})
|
|
|
|
}
|
|
|
|
_ => None
|
2012-08-06 19:01:14 -05:00
|
|
|
}
|
2014-06-07 07:17:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
&PatStruct(_, ref pattern_fields, _) => {
|
|
|
|
// Is this a struct or an enum variant?
|
2014-06-25 12:07:37 -05:00
|
|
|
let def = cx.tcx.def_map.borrow().get_copy(&pat_id);
|
2014-06-07 07:17:01 -05:00
|
|
|
let class_id = match def {
|
2014-07-13 08:12:47 -05:00
|
|
|
DefStatic(..) =>
|
|
|
|
cx.tcx.sess.span_bug(pat_span, "static pattern should've been rewritten"),
|
2014-06-25 12:07:37 -05:00
|
|
|
DefVariant(_, variant_id, _) => if *constructor == Variant(variant_id) {
|
2014-06-07 07:17:01 -05:00
|
|
|
Some(variant_id)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
},
|
2014-07-04 18:45:47 -05:00
|
|
|
_ => {
|
|
|
|
// Assume this is a struct.
|
|
|
|
match ty::ty_to_def_id(node_id_to_type(cx.tcx, pat_id)) {
|
|
|
|
None => {
|
|
|
|
cx.tcx.sess.span_bug(pat_span,
|
|
|
|
"struct pattern wasn't of a \
|
|
|
|
type with a def ID?!")
|
|
|
|
}
|
|
|
|
Some(def_id) => Some(def_id),
|
|
|
|
}
|
|
|
|
}
|
2014-06-07 07:17:01 -05:00
|
|
|
};
|
|
|
|
class_id.map(|variant_id| {
|
|
|
|
let struct_fields = ty::lookup_struct_fields(cx.tcx, variant_id);
|
|
|
|
let args = struct_fields.iter().map(|sf| {
|
|
|
|
match pattern_fields.iter().find(|f| f.ident.name == sf.name) {
|
|
|
|
Some(f) => f.pat,
|
|
|
|
_ => wild()
|
2012-10-24 20:47:59 -05:00
|
|
|
}
|
2014-06-07 07:17:01 -05:00
|
|
|
}).collect();
|
|
|
|
args
|
|
|
|
})
|
|
|
|
}
|
2014-06-02 10:18:23 -05:00
|
|
|
|
2014-06-07 07:17:01 -05:00
|
|
|
&PatTup(ref args) =>
|
|
|
|
Some(args.clone()),
|
|
|
|
|
|
|
|
&PatBox(ref inner) | &PatRegion(ref inner) =>
|
|
|
|
Some(vec!(inner.clone())),
|
|
|
|
|
|
|
|
&PatLit(ref expr) => {
|
|
|
|
let expr_value = eval_const_expr(cx.tcx, &**expr);
|
2014-06-25 12:07:37 -05:00
|
|
|
match range_covered_by_constructor(constructor, &expr_value, &expr_value) {
|
2014-06-07 07:17:01 -05:00
|
|
|
Some(true) => Some(vec!()),
|
|
|
|
Some(false) => None,
|
|
|
|
None => {
|
2014-06-25 12:07:37 -05:00
|
|
|
cx.tcx.sess.span_err(pat_span, "mismatched types between arms");
|
2014-03-08 14:36:22 -06:00
|
|
|
None
|
2014-06-07 07:17:01 -05:00
|
|
|
}
|
2012-08-06 19:01:14 -05:00
|
|
|
}
|
2014-06-07 07:17:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
&PatRange(ref from, ref to) => {
|
|
|
|
let from_value = eval_const_expr(cx.tcx, &**from);
|
|
|
|
let to_value = eval_const_expr(cx.tcx, &**to);
|
2014-06-25 12:07:37 -05:00
|
|
|
match range_covered_by_constructor(constructor, &from_value, &to_value) {
|
2014-06-07 07:17:01 -05:00
|
|
|
Some(true) => Some(vec!()),
|
|
|
|
Some(false) => None,
|
|
|
|
None => {
|
2014-06-25 12:07:37 -05:00
|
|
|
cx.tcx.sess.span_err(pat_span, "mismatched types between arms");
|
2014-06-02 13:49:44 -05:00
|
|
|
None
|
2014-06-07 07:17:01 -05:00
|
|
|
}
|
2013-05-21 04:04:55 -05:00
|
|
|
}
|
2014-06-07 07:17:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
&PatVec(ref before, ref slice, ref after) => {
|
2014-06-25 12:07:37 -05:00
|
|
|
match *constructor {
|
|
|
|
// Fixed-length vectors.
|
|
|
|
Single => {
|
|
|
|
let mut pats = before.clone();
|
|
|
|
pats.grow_fn(arity - before.len() - after.len(), |_| wild());
|
|
|
|
pats.push_all(after.as_slice());
|
|
|
|
Some(pats)
|
|
|
|
},
|
|
|
|
Slice(length) if before.len() + after.len() <= length && slice.is_some() => {
|
|
|
|
let mut pats = before.clone();
|
|
|
|
pats.grow_fn(arity - before.len() - after.len(), |_| wild());
|
|
|
|
pats.push_all(after.as_slice());
|
|
|
|
Some(pats)
|
|
|
|
},
|
|
|
|
Slice(length) if before.len() + after.len() == length => {
|
|
|
|
let mut pats = before.clone();
|
|
|
|
pats.push_all(after.as_slice());
|
|
|
|
Some(pats)
|
|
|
|
},
|
2014-08-30 09:22:19 -05:00
|
|
|
SliceWithSubslice(prefix, suffix)
|
|
|
|
if before.len() == prefix
|
|
|
|
&& after.len() == suffix
|
|
|
|
&& slice.is_some() => {
|
|
|
|
let mut pats = before.clone();
|
|
|
|
pats.push_all(after.as_slice());
|
|
|
|
Some(pats)
|
|
|
|
}
|
2014-06-07 07:17:01 -05:00
|
|
|
_ => None
|
2012-12-08 14:22:43 -06:00
|
|
|
}
|
2014-06-07 07:17:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
&PatMac(_) => {
|
2014-06-25 12:07:37 -05:00
|
|
|
cx.tcx.sess.span_err(pat_span, "unexpanded macro");
|
2014-06-07 07:17:01 -05:00
|
|
|
None
|
|
|
|
}
|
2014-06-02 10:18:23 -05:00
|
|
|
};
|
2014-06-21 07:56:23 -05:00
|
|
|
head.map(|head| head.append(r.slice_to(col)).append(r.slice_from(col + 1)))
|
2011-07-25 06:45:09 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
fn default(cx: &MatchCheckCtxt, r: &[Gc<Pat>]) -> Option<Vec<Gc<Pat>>> {
|
2014-07-03 15:53:20 -05:00
|
|
|
if pat_is_binding_or_wild(&cx.tcx.def_map, &*raw_pat(r[0])) {
|
2014-03-08 14:36:22 -06:00
|
|
|
Some(Vec::from_slice(r.tail()))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2012-04-24 04:13:25 -05:00
|
|
|
}
|
|
|
|
|
2014-03-05 21:07:47 -06:00
|
|
|
fn check_local(cx: &mut MatchCheckCtxt, loc: &Local) {
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_local(cx, loc);
|
2014-05-26 07:01:09 -05:00
|
|
|
|
2014-05-26 07:42:48 -05:00
|
|
|
let name = match loc.source {
|
|
|
|
LocalLet => "local",
|
|
|
|
LocalFor => "`for` loop"
|
|
|
|
};
|
|
|
|
|
2014-08-17 15:10:25 -05:00
|
|
|
let mut static_inliner = StaticInliner::new(cx.tcx);
|
2014-07-13 08:12:47 -05:00
|
|
|
match is_refutable(cx, static_inliner.fold_pat(loc.pat)) {
|
2014-05-31 08:13:46 -05:00
|
|
|
Some(pat) => {
|
2014-07-11 11:54:01 -05:00
|
|
|
span_err!(cx.tcx.sess, loc.pat.span, E0005,
|
2014-06-19 13:55:12 -05:00
|
|
|
"refutable pattern in {} binding: `{}` not covered",
|
2014-06-21 05:39:03 -05:00
|
|
|
name, pat_to_string(&*pat)
|
2014-05-31 08:13:46 -05:00
|
|
|
);
|
|
|
|
},
|
|
|
|
None => ()
|
2011-08-01 08:26:48 -05:00
|
|
|
}
|
2012-12-05 21:01:14 -06:00
|
|
|
|
2014-07-28 13:33:06 -05:00
|
|
|
// Check legality of move bindings and `@` patterns.
|
2013-07-19 00:38:55 -05:00
|
|
|
check_legality_of_move_bindings(cx, false, [ loc.pat ]);
|
2014-07-28 13:33:06 -05:00
|
|
|
check_legality_of_bindings_in_at_patterns(cx, &*loc.pat);
|
2011-08-01 08:26:48 -05:00
|
|
|
}
|
|
|
|
|
2014-03-05 21:07:47 -06:00
|
|
|
fn check_fn(cx: &mut MatchCheckCtxt,
|
2014-09-09 17:54:36 -05:00
|
|
|
kind: FnKind,
|
2014-03-05 21:07:47 -06:00
|
|
|
decl: &FnDecl,
|
|
|
|
body: &Block,
|
2014-05-14 01:20:25 -05:00
|
|
|
sp: Span) {
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_fn(cx, kind, decl, body, sp);
|
2013-08-03 11:45:23 -05:00
|
|
|
for input in decl.inputs.iter() {
|
2014-05-31 08:13:46 -05:00
|
|
|
match is_refutable(cx, input.pat) {
|
|
|
|
Some(pat) => {
|
2014-07-11 11:54:01 -05:00
|
|
|
span_err!(cx.tcx.sess, input.pat.span, E0006,
|
2014-06-19 13:55:12 -05:00
|
|
|
"refutable pattern in function argument: `{}` not covered",
|
2014-06-21 05:39:03 -05:00
|
|
|
pat_to_string(&*pat)
|
2014-05-31 08:13:46 -05:00
|
|
|
);
|
|
|
|
},
|
|
|
|
None => ()
|
2012-11-06 20:41:06 -06:00
|
|
|
}
|
2014-06-24 15:41:42 -05:00
|
|
|
check_legality_of_move_bindings(cx, false, [input.pat]);
|
2014-07-28 13:33:06 -05:00
|
|
|
check_legality_of_bindings_in_at_patterns(cx, &*input.pat);
|
2012-11-06 20:41:06 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-31 08:13:46 -05:00
|
|
|
fn is_refutable(cx: &MatchCheckCtxt, pat: Gc<Pat>) -> Option<Gc<Pat>> {
|
2014-06-25 12:07:37 -05:00
|
|
|
let pats = Matrix(vec!(vec!(pat)));
|
2014-07-03 15:53:20 -05:00
|
|
|
match is_useful(cx, &pats, [wild()], ConstructWitness) {
|
|
|
|
UsefulWithWitness(pats) => {
|
2014-05-31 08:13:46 -05:00
|
|
|
assert_eq!(pats.len(), 1);
|
2014-07-03 15:53:20 -05:00
|
|
|
Some(pats.get(0).clone())
|
|
|
|
},
|
|
|
|
NotUseful => None,
|
|
|
|
Useful => unreachable!()
|
|
|
|
}
|
2011-08-01 08:26:48 -05:00
|
|
|
}
|
|
|
|
|
2012-12-05 21:01:14 -06:00
|
|
|
// Legality of move bindings checking
|
2013-10-06 14:27:36 -05:00
|
|
|
fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
|
2014-04-21 18:21:53 -05:00
|
|
|
has_guard: bool,
|
2014-05-16 12:15:33 -05:00
|
|
|
pats: &[Gc<Pat>]) {
|
2012-12-05 21:01:14 -06:00
|
|
|
let tcx = cx.tcx;
|
2014-04-22 11:06:43 -05:00
|
|
|
let def_map = &tcx.def_map;
|
2012-12-05 21:01:14 -06:00
|
|
|
let mut by_ref_span = None;
|
2013-08-03 11:45:23 -05:00
|
|
|
for pat in pats.iter() {
|
2014-05-16 12:15:33 -05:00
|
|
|
pat_bindings(def_map, &**pat, |bm, _, span, _path| {
|
2012-12-05 21:01:14 -06:00
|
|
|
match bm {
|
2013-09-01 20:45:37 -05:00
|
|
|
BindByRef(_) => {
|
2012-12-05 21:01:14 -06:00
|
|
|
by_ref_span = Some(span);
|
|
|
|
}
|
2013-10-20 07:31:23 -05:00
|
|
|
BindByValue(_) => {
|
2012-12-07 21:34:57 -06:00
|
|
|
}
|
2012-12-05 21:01:14 -06:00
|
|
|
}
|
2013-11-21 17:42:55 -06:00
|
|
|
})
|
2012-12-05 21:01:14 -06:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
let check_move: |&Pat, Option<Gc<Pat>>| = |p, sub| {
|
2012-12-14 19:50:48 -06:00
|
|
|
// check legality of moving out of the enum
|
2013-08-14 04:04:41 -05:00
|
|
|
|
2013-11-28 14:22:53 -06:00
|
|
|
// x @ Foo(..) is legal, but x @ Foo(y) isn't.
|
2014-05-16 12:15:33 -05:00
|
|
|
if sub.map_or(false, |p| pat_contains_bindings(def_map, &*p)) {
|
2014-07-11 11:54:01 -05:00
|
|
|
span_err!(cx.tcx.sess, p.span, E0007, "cannot bind by-move with sub-bindings");
|
2012-12-14 19:50:48 -06:00
|
|
|
} else if has_guard {
|
2014-07-11 11:54:01 -05:00
|
|
|
span_err!(cx.tcx.sess, p.span, E0008, "cannot bind by-move into a pattern guard");
|
2012-12-14 19:50:48 -06:00
|
|
|
} else if by_ref_span.is_some() {
|
2014-07-11 11:54:01 -05:00
|
|
|
span_err!(cx.tcx.sess, p.span, E0009,
|
|
|
|
"cannot bind by-move and by-ref in the same pattern");
|
|
|
|
span_note!(cx.tcx.sess, by_ref_span.unwrap(), "by-ref binding occurs here");
|
2012-12-14 19:50:48 -06:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-08-03 11:45:23 -05:00
|
|
|
for pat in pats.iter() {
|
2014-05-16 12:15:33 -05:00
|
|
|
walk_pat(&**pat, |p| {
|
|
|
|
if pat_is_binding(def_map, &*p) {
|
2012-12-05 21:01:14 -06:00
|
|
|
match p.node {
|
2014-04-21 18:21:53 -05:00
|
|
|
PatIdent(BindByValue(_), _, sub) => {
|
|
|
|
let pat_ty = ty::node_id_to_type(tcx, p.id);
|
|
|
|
if ty::type_moves_by_default(tcx, pat_ty) {
|
2013-01-10 12:59:58 -06:00
|
|
|
check_move(p, sub);
|
2012-12-05 21:01:14 -06:00
|
|
|
}
|
|
|
|
}
|
2014-04-21 18:21:53 -05:00
|
|
|
PatIdent(BindByRef(_), _, _) => {
|
|
|
|
}
|
2013-01-10 12:59:58 -06:00
|
|
|
_ => {
|
|
|
|
cx.tcx.sess.span_bug(
|
|
|
|
p.span,
|
2014-05-16 12:45:16 -05:00
|
|
|
format!("binding pattern {} is not an \
|
|
|
|
identifier: {:?}",
|
|
|
|
p.id,
|
|
|
|
p.node).as_slice());
|
2013-01-10 12:59:58 -06:00
|
|
|
}
|
2012-12-05 21:01:14 -06:00
|
|
|
}
|
|
|
|
}
|
2013-08-02 01:17:20 -05:00
|
|
|
true
|
2013-11-21 17:42:55 -06:00
|
|
|
});
|
2012-12-05 21:01:14 -06:00
|
|
|
}
|
|
|
|
}
|
librustc: Disallow mutation and assignment in pattern guards, and modify
the CFG for match statements.
There were two bugs in issue #14684. One was simply that the borrow
check didn't know about the correct CFG for match statements: the
pattern must be a predecessor of the guard. This disallows the bad
behavior if there are bindings in the pattern. But it isn't enough to
prevent the memory safety problem, because of wildcards; thus, this
patch introduces a more restrictive rule, which disallows assignments
and mutable borrows inside guards outright.
I discussed this with Niko and we decided this was the best plan of
action.
This breaks code that performs mutable borrows in pattern guards. Most
commonly, the code looks like this:
impl Foo {
fn f(&mut self, ...) {}
fn g(&mut self, ...) {
match bar {
Baz if self.f(...) => { ... }
_ => { ... }
}
}
}
Change this code to not use a guard. For example:
impl Foo {
fn f(&mut self, ...) {}
fn g(&mut self, ...) {
match bar {
Baz => {
if self.f(...) {
...
} else {
...
}
}
_ => { ... }
}
}
}
Sometimes this can result in code duplication, but often it illustrates
a hidden memory safety problem.
Closes #14684.
[breaking-change]
2014-07-25 17:18:19 -05:00
|
|
|
|
|
|
|
/// Ensures that a pattern guard doesn't borrow by mutable reference or
|
|
|
|
/// assign.
|
2014-04-22 07:56:37 -05:00
|
|
|
fn check_for_mutation_in_guard<'a, 'tcx>(cx: &'a MatchCheckCtxt<'a, 'tcx>, guard: &Expr) {
|
librustc: Disallow mutation and assignment in pattern guards, and modify
the CFG for match statements.
There were two bugs in issue #14684. One was simply that the borrow
check didn't know about the correct CFG for match statements: the
pattern must be a predecessor of the guard. This disallows the bad
behavior if there are bindings in the pattern. But it isn't enough to
prevent the memory safety problem, because of wildcards; thus, this
patch introduces a more restrictive rule, which disallows assignments
and mutable borrows inside guards outright.
I discussed this with Niko and we decided this was the best plan of
action.
This breaks code that performs mutable borrows in pattern guards. Most
commonly, the code looks like this:
impl Foo {
fn f(&mut self, ...) {}
fn g(&mut self, ...) {
match bar {
Baz if self.f(...) => { ... }
_ => { ... }
}
}
}
Change this code to not use a guard. For example:
impl Foo {
fn f(&mut self, ...) {}
fn g(&mut self, ...) {
match bar {
Baz => {
if self.f(...) {
...
} else {
...
}
}
_ => { ... }
}
}
}
Sometimes this can result in code duplication, but often it illustrates
a hidden memory safety problem.
Closes #14684.
[breaking-change]
2014-07-25 17:18:19 -05:00
|
|
|
let mut checker = MutationChecker {
|
|
|
|
cx: cx,
|
|
|
|
};
|
|
|
|
let mut visitor = ExprUseVisitor::new(&mut checker, checker.cx.tcx);
|
|
|
|
visitor.walk_expr(guard);
|
|
|
|
}
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
struct MutationChecker<'a, 'tcx: 'a> {
|
|
|
|
cx: &'a MatchCheckCtxt<'a, 'tcx>,
|
librustc: Disallow mutation and assignment in pattern guards, and modify
the CFG for match statements.
There were two bugs in issue #14684. One was simply that the borrow
check didn't know about the correct CFG for match statements: the
pattern must be a predecessor of the guard. This disallows the bad
behavior if there are bindings in the pattern. But it isn't enough to
prevent the memory safety problem, because of wildcards; thus, this
patch introduces a more restrictive rule, which disallows assignments
and mutable borrows inside guards outright.
I discussed this with Niko and we decided this was the best plan of
action.
This breaks code that performs mutable borrows in pattern guards. Most
commonly, the code looks like this:
impl Foo {
fn f(&mut self, ...) {}
fn g(&mut self, ...) {
match bar {
Baz if self.f(...) => { ... }
_ => { ... }
}
}
}
Change this code to not use a guard. For example:
impl Foo {
fn f(&mut self, ...) {}
fn g(&mut self, ...) {
match bar {
Baz => {
if self.f(...) {
...
} else {
...
}
}
_ => { ... }
}
}
}
Sometimes this can result in code duplication, but often it illustrates
a hidden memory safety problem.
Closes #14684.
[breaking-change]
2014-07-25 17:18:19 -05:00
|
|
|
}
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
impl<'a, 'tcx> Delegate for MutationChecker<'a, 'tcx> {
|
librustc: Disallow mutation and assignment in pattern guards, and modify
the CFG for match statements.
There were two bugs in issue #14684. One was simply that the borrow
check didn't know about the correct CFG for match statements: the
pattern must be a predecessor of the guard. This disallows the bad
behavior if there are bindings in the pattern. But it isn't enough to
prevent the memory safety problem, because of wildcards; thus, this
patch introduces a more restrictive rule, which disallows assignments
and mutable borrows inside guards outright.
I discussed this with Niko and we decided this was the best plan of
action.
This breaks code that performs mutable borrows in pattern guards. Most
commonly, the code looks like this:
impl Foo {
fn f(&mut self, ...) {}
fn g(&mut self, ...) {
match bar {
Baz if self.f(...) => { ... }
_ => { ... }
}
}
}
Change this code to not use a guard. For example:
impl Foo {
fn f(&mut self, ...) {}
fn g(&mut self, ...) {
match bar {
Baz => {
if self.f(...) {
...
} else {
...
}
}
_ => { ... }
}
}
}
Sometimes this can result in code duplication, but often it illustrates
a hidden memory safety problem.
Closes #14684.
[breaking-change]
2014-07-25 17:18:19 -05:00
|
|
|
fn consume(&mut self, _: NodeId, _: Span, _: cmt, _: ConsumeMode) {}
|
|
|
|
fn consume_pat(&mut self, _: &Pat, _: cmt, _: ConsumeMode) {}
|
|
|
|
fn borrow(&mut self,
|
|
|
|
_: NodeId,
|
|
|
|
span: Span,
|
|
|
|
_: cmt,
|
|
|
|
_: Region,
|
|
|
|
kind: BorrowKind,
|
|
|
|
_: LoanCause) {
|
|
|
|
match kind {
|
|
|
|
MutBorrow => {
|
|
|
|
self.cx
|
|
|
|
.tcx
|
|
|
|
.sess
|
|
|
|
.span_err(span,
|
|
|
|
"cannot mutably borrow in a pattern guard")
|
|
|
|
}
|
|
|
|
ImmBorrow | UniqueImmBorrow => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn decl_without_init(&mut self, _: NodeId, _: Span) {}
|
|
|
|
fn mutate(&mut self, _: NodeId, span: Span, _: cmt, mode: MutateMode) {
|
|
|
|
match mode {
|
|
|
|
JustWrite | WriteAndRead => {
|
|
|
|
self.cx
|
|
|
|
.tcx
|
|
|
|
.sess
|
|
|
|
.span_err(span, "cannot assign in a pattern guard")
|
|
|
|
}
|
|
|
|
Init => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-28 13:33:06 -05:00
|
|
|
/// Forbids bindings in `@` patterns. This is necessary for memory safety,
|
|
|
|
/// because of the way rvalues are handled in the borrow check. (See issue
|
|
|
|
/// #14587.)
|
|
|
|
fn check_legality_of_bindings_in_at_patterns(cx: &MatchCheckCtxt, pat: &Pat) {
|
2014-09-12 05:10:30 -05:00
|
|
|
AtBindingPatternVisitor { cx: cx, bindings_allowed: true }.visit_pat(pat);
|
2014-07-28 13:33:06 -05:00
|
|
|
}
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
struct AtBindingPatternVisitor<'a, 'b:'a, 'tcx:'b> {
|
|
|
|
cx: &'a MatchCheckCtxt<'b, 'tcx>,
|
2014-09-12 05:10:30 -05:00
|
|
|
bindings_allowed: bool
|
2014-07-28 13:33:06 -05:00
|
|
|
}
|
|
|
|
|
2014-09-09 17:54:36 -05:00
|
|
|
impl<'a, 'b, 'tcx, 'v> Visitor<'v> for AtBindingPatternVisitor<'a, 'b, 'tcx> {
|
2014-09-12 05:10:30 -05:00
|
|
|
fn visit_pat(&mut self, pat: &Pat) {
|
|
|
|
if !self.bindings_allowed && pat_is_binding(&self.cx.tcx.def_map, pat) {
|
2014-07-28 13:33:06 -05:00
|
|
|
self.cx.tcx.sess.span_err(pat.span,
|
|
|
|
"pattern bindings are not allowed \
|
|
|
|
after an `@`");
|
|
|
|
}
|
|
|
|
|
|
|
|
match pat.node {
|
2014-09-12 05:10:30 -05:00
|
|
|
PatIdent(_, _, Some(_)) => {
|
|
|
|
let bindings_were_allowed = self.bindings_allowed;
|
|
|
|
self.bindings_allowed = false;
|
|
|
|
visit::walk_pat(self, pat);
|
|
|
|
self.bindings_allowed = bindings_were_allowed;
|
|
|
|
}
|
|
|
|
_ => visit::walk_pat(self, pat),
|
2014-07-28 13:33:06 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|