rust/src/librustc/middle/check_match.rs

1190 lines
45 KiB
Rust
Raw Normal View History

// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub use self::Constructor::*;
use self::Usefulness::*;
use self::WitnessPreference::*;
use middle::const_eval::{compare_const_vals, const_bool, const_float, const_val};
use middle::const_eval::{const_expr_to_pat, eval_const_expr, lookup_const_by_id};
use middle::def::*;
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::expr_use_visitor as euv;
use middle::mem_categorization::cmt;
use middle::pat_util::*;
use middle::ty::*;
use middle::ty;
use std::cmp::Ordering;
use std::fmt;
use std::iter::{range_inclusive, AdditiveIterator, FromIterator, IntoIterator, repeat};
use std::num::Float;
2014-09-07 12:09:06 -05:00
use std::slice;
2015-01-03 21:42:21 -06:00
use syntax::ast::{self, DUMMY_NODE_ID, NodeId, Pat};
use syntax::ast_util;
use syntax::codemap::{Span, Spanned, DUMMY_SP};
use syntax::fold::{Folder, noop_fold_pat};
use syntax::print::pprust::pat_to_string;
use syntax::parse::token;
2014-09-07 12:09:06 -05:00
use syntax::ptr::P;
2015-01-03 21:42:21 -06:00
use syntax::visit::{self, Visitor, FnKind};
use util::ppaux::ty_to_string;
use util::nodemap::FnvHashMap;
pub const DUMMY_WILD_PAT: &'static Pat = &Pat {
2014-09-07 12:09:06 -05:00
id: DUMMY_NODE_ID,
node: ast::PatWild(ast::PatWildSingle),
2014-09-07 12:09:06 -05:00
span: DUMMY_SP
};
struct Matrix<'a>(Vec<Vec<&'a Pat>>);
/// Pretty-printer for matrices of patterns, example:
/// ++++++++++++++++++++++++++
2015-01-07 10:58:31 -06:00
/// + _ + [] +
/// ++++++++++++++++++++++++++
/// + true + [First] +
/// ++++++++++++++++++++++++++
/// + true + [Second(true)] +
/// ++++++++++++++++++++++++++
/// + false + [_] +
/// ++++++++++++++++++++++++++
/// + _ + [_, _, ..tail] +
/// ++++++++++++++++++++++++++
std: Rename Show/String to Debug/Display This commit is an implementation of [RFC 565][rfc] which is a stabilization of the `std::fmt` module and the implementations of various formatting traits. Specifically, the following changes were performed: [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0565-show-string-guidelines.md * The `Show` trait is now deprecated, it was renamed to `Debug` * The `String` trait is now deprecated, it was renamed to `Display` * Many `Debug` and `Display` implementations were audited in accordance with the RFC and audited implementations now have the `#[stable]` attribute * Integers and floats no longer print a suffix * Smart pointers no longer print details that they are a smart pointer * Paths with `Debug` are now quoted and escape characters * The `unwrap` methods on `Result` now require `Display` instead of `Debug` * The `Error` trait no longer has a `detail` method and now requires that `Display` must be implemented. With the loss of `String`, this has moved into libcore. * `impl<E: Error> FromError<E> for Box<Error>` now exists * `derive(Show)` has been renamed to `derive(Debug)`. This is not currently warned about due to warnings being emitted on stage1+ While backwards compatibility is attempted to be maintained with a blanket implementation of `Display` for the old `String` trait (and the same for `Show`/`Debug`) this is still a breaking change due to primitives no longer implementing `String` as well as modifications such as `unwrap` and the `Error` trait. Most code is fairly straightforward to update with a rename or tweaks of method calls. [breaking-change] Closes #21436
2015-01-20 17:45:07 -06:00
impl<'a> fmt::Debug for Matrix<'a> {
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| {
row.iter()
.map(|&pat| pat_to_string(&*pat))
.collect::<Vec<String>>()
}).collect();
let column_count = m.iter().map(|row| row.len()).max().unwrap_or(0);
assert!(m.iter().all(|row| row.len() == column_count));
let column_widths: Vec<uint> = (0..column_count).map(|col| {
pretty_printed_matrix.iter().map(|row| row[col].len()).max().unwrap_or(0)
}).collect();
let total_width = column_widths.iter().cloned().sum() + column_count * 3 + 1;
std: Second pass stabilization for `string` This commit performs a second pass over the `std::string` module, performing the following actions: * The name `std::string` is now stable. * The `String::from_utf8` function is now stable after having been altered to return a new `FromUtf8Error` structure. The `FromUtf8Error` structure is now stable as well as its `into_bytes` and `utf8_error` methods. * The `String::from_utf8_lossy` function is now stable. * The `String::from_chars` method is now deprecated in favor of `.collect()` * The `String::from_raw_parts` method is now stable * The `String::from_str` function remains experimental * The `String::from_raw_buf` function remains experimental * The `String::from_raw_buf_len` function remains experimental * The `String::from_utf8_unchecked` function is now stable * The `String::from_char` function is now deprecated in favor of `repeat(c).take(n).collect()` * The `String::grow` function is now deprecated in favor of `.extend(repeat(c).take(n)` * The `String::capacity` method is now stable * The `String::reserve` method is now stable * The `String::reserve_exact` method is now stable * The `String::shrink_to_fit` method is now stable * The `String::pop` method is now stable * The `String::as_mut_vec` method is now stable * The `String::is_empty` method is now stable * The `IntoString` trait is now deprecated (there are no implementors) * The `String::truncate` method is now stable * The `String::insert` method is now stable * The `String::remove` method is now stable * The `String::push` method is now stable * The `String::push_str` method is now stable * The `String::from_utf16` function is now stable after its error type has now become an opaque structure to carry more semantic information in the future. A number of these changes are breaking changes, but the migrations should be fairly straightforward on a case-by-case basis (outlined above where possible). [breaking-change]
2014-12-28 12:29:56 -06:00
let br = repeat('+').take(total_width).collect::<String>();
try!(write!(f, "{}\n", br));
for row in pretty_printed_matrix {
try!(write!(f, "+"));
2014-09-14 22:27:36 -05:00
for (column, pat_str) in row.into_iter().enumerate() {
try!(write!(f, " "));
try!(write!(f, "{:1$}", pat_str, column_widths[column]));
try!(write!(f, " +"));
}
try!(write!(f, "\n"));
try!(write!(f, "{}\n", br));
}
Ok(())
}
}
2014-09-07 12:09:06 -05:00
impl<'a> FromIterator<Vec<&'a Pat>> for Matrix<'a> {
fn from_iter<T: IntoIterator<Item=Vec<&'a Pat>>>(iter: T) -> Matrix<'a> {
Matrix(iter.into_iter().collect())
}
}
pub struct MatchCheckCtxt<'a, 'tcx: 'a> {
pub tcx: &'a ty::ctxt<'tcx>,
pub param_env: ParameterEnvironment<'a, 'tcx>,
}
#[derive(Clone, PartialEq)]
pub enum Constructor {
/// The constructor of all patterns that don't vary by constructor,
/// e.g. struct patterns and fixed-length arrays.
Single,
/// Enum variants.
Variant(ast::DefId),
/// Literal values.
ConstantValue(const_val),
/// Ranges of literal values (2..5).
ConstantRange(const_val, const_val),
/// Array patterns of length n.
Slice(uint),
/// Array patterns with a subslice.
SliceWithSubslice(uint, uint)
}
#[derive(Clone, PartialEq)]
enum Usefulness {
Useful,
2014-09-07 12:09:06 -05:00
UsefulWithWitness(Vec<P<Pat>>),
NotUseful
}
#[derive(Copy)]
enum WitnessPreference {
ConstructWitness,
LeaveOutWitness
}
impl<'a, 'tcx, 'v> Visitor<'v> for MatchCheckCtxt<'a, 'tcx> {
fn visit_expr(&mut self, ex: &ast::Expr) {
2014-03-05 21:07:47 -06:00
check_expr(self, ex);
}
fn visit_local(&mut self, l: &ast::Local) {
2014-03-05 21:07:47 -06:00
check_local(self, l);
}
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v ast::FnDecl,
b: &'v ast::Block, s: Span, n: NodeId) {
check_fn(self, fk, fd, b, s, n);
}
}
2014-09-07 12:09:06 -05:00
pub fn check_crate(tcx: &ty::ctxt) {
visit::walk_crate(&mut MatchCheckCtxt {
tcx: tcx,
param_env: ty::empty_parameter_environment(tcx),
}, tcx.map.krate());
tcx.sess.abort_if_errors();
}
fn check_expr(cx: &mut MatchCheckCtxt, ex: &ast::Expr) {
visit::walk_expr(cx, ex);
2012-08-06 14:34:08 -05:00
match ex.node {
ast::ExprMatch(ref scrut, ref arms, source) => {
2015-01-31 11:20:46 -06:00
for arm in arms {
// First, check legality of move bindings.
check_legality_of_move_bindings(cx,
arm.guard.is_some(),
&arm.pats);
// Second, if there is a guard on each arm, make sure it isn't
// assigning or borrowing anything mutably.
match arm.guard {
2014-09-07 12:09:06 -05:00
Some(ref guard) => check_for_mutation_in_guard(cx, &**guard),
None => {}
}
}
let mut static_inliner = StaticInliner::new(cx.tcx, None);
2014-09-07 12:09:06 -05:00
let inlined_arms = arms.iter().map(|arm| {
(arm.pats.iter().map(|pat| {
static_inliner.fold_pat((*pat).clone())
}).collect(), arm.guard.as_ref().map(|e| &**e))
}).collect::<Vec<(Vec<P<Pat>>, Option<&ast::Expr>)>>();
// Bail out early if inlining failed.
if static_inliner.failed {
return;
}
for pat in inlined_arms
.iter()
.flat_map(|&(ref pats, _)| pats.iter()) {
// Third, check legality of move bindings.
check_legality_of_bindings_in_at_patterns(cx, &**pat);
// Fourth, check if there are any references to NaN that we should warn about.
check_for_static_nan(cx, &**pat);
// Fifth, check if for any of the patterns that match an enumerated type
// are bindings with the same name as one of the variants of said type.
check_for_bindings_named_the_same_as_variants(cx, &**pat);
2014-09-07 12:09:06 -05:00
}
// Fourth, check for unreachable arms.
check_arms(cx, &inlined_arms[..], source);
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.
let pat_ty = node_id_to_type(cx.tcx, scrut.id);
if inlined_arms.is_empty() {
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;
}
let matrix: Matrix = inlined_arms
2014-09-07 12:09:06 -05:00
.iter()
.filter(|&&(_, guard)| guard.is_none())
2014-12-09 11:21:18 -06:00
.flat_map(|arm| arm.0.iter())
2014-09-07 12:09:06 -05:00
.map(|pat| vec![&**pat])
.collect();
check_exhaustive(cx, ex.span, &matrix, source);
},
_ => ()
}
}
fn is_expr_const_nan(tcx: &ty::ctxt, expr: &ast::Expr) -> bool {
match eval_const_expr(tcx, expr) {
const_float(f) => f.is_nan(),
_ => false
}
}
fn check_for_bindings_named_the_same_as_variants(cx: &MatchCheckCtxt, pat: &Pat) {
ast_util::walk_pat(pat, |p| {
match p.node {
ast::PatIdent(ast::BindByValue(ast::MutImmutable), ident, None) => {
let pat_ty = ty::pat_ty(cx.tcx, p);
if let ty::ty_enum(def_id, _) = pat_ty.sty {
let def = cx.tcx.def_map.borrow().get(&p.id).map(|d| d.full_def());
if let Some(DefLocal(_)) = def {
if ty::enum_variants(cx.tcx, def_id).iter().any(|variant|
token::get_name(variant.name) == token::get_name(ident.node.name)
&& variant.args.len() == 0
) {
span_warn!(cx.tcx.sess, p.span, E0170,
"pattern binding `{}` is named the same as one \
of the variants of the type `{}`",
&token::get_ident(ident.node), ty_to_string(cx.tcx, pat_ty));
span_help!(cx.tcx.sess, p.span,
"if you meant to match on a variant, \
consider making the path in the pattern qualified: `{}::{}`",
ty_to_string(cx.tcx, pat_ty), &token::get_ident(ident.node));
}
}
}
2014-09-07 12:09:06 -05:00
}
_ => ()
}
true
});
}
// Check that we do not match against a static NaN (#6804)
fn check_for_static_nan(cx: &MatchCheckCtxt, pat: &Pat) {
ast_util::walk_pat(pat, |p| {
match p.node {
ast::PatLit(ref expr) if is_expr_const_nan(cx.tcx, &**expr) => {
span_warn!(cx.tcx.sess, p.span, E0003,
"unmatchable NaN in pattern, \
use the is_nan method in a guard instead");
}
_ => ()
}
true
});
}
// Check for unreachable patterns
fn check_arms(cx: &MatchCheckCtxt,
arms: &[(Vec<P<Pat>>, Option<&ast::Expr>)],
source: ast::MatchSource) {
2014-09-07 12:09:06 -05:00
let mut seen = Matrix(vec![]);
let mut printed_if_let_err = false;
2015-01-31 11:20:46 -06:00
for &(ref pats, guard) in arms {
for pat in pats {
2014-09-07 12:09:06 -05:00
let v = vec![&**pat];
match is_useful(cx, &seen, &v[..], LeaveOutWitness) {
NotUseful => {
2014-10-02 23:41:24 -05:00
match source {
ast::MatchSource::IfLetDesugar { .. } => {
2014-10-02 23:41:24 -05:00
if printed_if_let_err {
// we already printed an irrefutable if-let pattern error.
// We don't want two, that's just confusing.
} else {
// find the first arm pattern so we can use its span
let &(ref first_arm_pats, _) = &arms[0];
let first_pat = &first_arm_pats[0];
2014-10-02 23:41:24 -05:00
let span = first_pat.span;
span_err!(cx.tcx.sess, span, E0162, "irrefutable if-let pattern");
printed_if_let_err = true;
}
},
ast::MatchSource::WhileLetDesugar => {
// find the first arm pattern so we can use its span
let &(ref first_arm_pats, _) = &arms[0];
let first_pat = &first_arm_pats[0];
let span = first_pat.span;
2014-10-02 23:41:24 -05:00
span_err!(cx.tcx.sess, span, E0165, "irrefutable while-let pattern");
},
ast::MatchSource::ForLoopDesugar => {
// this is a bug, because on `match iter.next()` we cover
// `Some(<head>)` and `None`. It's impossible to have an unreachable
// pattern
// (see libsyntax/ext/expand.rs for the full expansion of a for loop)
cx.tcx.sess.span_bug(pat.span, "unreachable for-loop pattern")
},
ast::MatchSource::Normal => {
span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern")
},
}
}
Useful => (),
UsefulWithWitness(_) => unreachable!()
}
2014-09-07 12:09:06 -05:00
if guard.is_none() {
let Matrix(mut rows) = seen;
rows.push(v);
seen = Matrix(rows);
}
}
}
}
2014-09-07 12:09:06 -05:00
fn raw_pat<'a>(p: &'a Pat) -> &'a Pat {
match p.node {
ast::PatIdent(_, _, Some(ref s)) => raw_pat(&**s),
2014-09-07 12:09:06 -05:00
_ => p
}
}
fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, matrix: &Matrix, source: ast::MatchSource) {
match is_useful(cx, matrix, &[DUMMY_WILD_PAT], ConstructWitness) {
UsefulWithWitness(pats) => {
let witness = match &pats[..] {
2014-09-07 12:09:06 -05:00
[ref witness] => &**witness,
[] => DUMMY_WILD_PAT,
_ => unreachable!()
};
match source {
ast::MatchSource::ForLoopDesugar => {
// `witness` has the form `Some(<head>)`, peel off the `Some`
let witness = match witness.node {
ast::PatEnum(_, Some(ref pats)) => match &pats[..] {
[ref pat] => &**pat,
_ => unreachable!(),
},
_ => unreachable!(),
};
span_err!(cx.tcx.sess, sp, E0297,
"refutable pattern in `for` loop binding: \
`{}` not covered",
pat_to_string(witness));
},
_ => {
span_err!(cx.tcx.sess, sp, E0004,
"non-exhaustive patterns: `{}` not covered",
pat_to_string(witness)
);
},
}
}
NotUseful => {
// This is good, wildcard pattern isn't reachable
},
_ => unreachable!()
}
}
fn const_val_to_expr(value: &const_val) -> P<ast::Expr> {
let node = match value {
&const_bool(b) => ast::LitBool(b),
_ => unreachable!()
};
P(ast::Expr {
id: 0,
node: ast::ExprLit(P(Spanned { node: node, span: DUMMY_SP })),
span: DUMMY_SP
2014-09-07 12:09:06 -05:00
})
}
pub struct StaticInliner<'a, 'tcx: 'a> {
pub tcx: &'a ty::ctxt<'tcx>,
pub failed: bool,
pub renaming_map: Option<&'a mut FnvHashMap<(NodeId, Span), NodeId>>,
}
impl<'a, 'tcx> StaticInliner<'a, 'tcx> {
pub fn new<'b>(tcx: &'b ty::ctxt<'tcx>,
renaming_map: Option<&'b mut FnvHashMap<(NodeId, Span), NodeId>>)
-> StaticInliner<'b, 'tcx> {
StaticInliner {
tcx: tcx,
failed: false,
renaming_map: renaming_map
}
}
}
struct RenamingRecorder<'map> {
substituted_node_id: NodeId,
origin_span: Span,
renaming_map: &'map mut FnvHashMap<(NodeId, Span), NodeId>
}
impl<'map> ast_util::IdVisitingOperation for RenamingRecorder<'map> {
fn visit_id(&mut self, node_id: NodeId) {
let key = (node_id, self.origin_span);
self.renaming_map.insert(key, self.substituted_node_id);
}
}
impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> {
2014-09-07 12:09:06 -05:00
fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> {
return match pat.node {
ast::PatIdent(..) | ast::PatEnum(..) => {
let def = self.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def());
match def {
rustc: Add `const` globals to the language This change is an implementation of [RFC 69][rfc] which adds a third kind of global to the language, `const`. This global is most similar to what the old `static` was, and if you're unsure about what to use then you should use a `const`. The semantics of these three kinds of globals are: * A `const` does not represent a memory location, but only a value. Constants are translated as rvalues, which means that their values are directly inlined at usage location (similar to a #define in C/C++). Constant values are, well, constant, and can not be modified. Any "modification" is actually a modification to a local value on the stack rather than the actual constant itself. Almost all values are allowed inside constants, whether they have interior mutability or not. There are a few minor restrictions listed in the RFC, but they should in general not come up too often. * A `static` now always represents a memory location (unconditionally). Any references to the same `static` are actually a reference to the same memory location. Only values whose types ascribe to `Sync` are allowed in a `static`. This restriction is in place because many threads may access a `static` concurrently. Lifting this restriction (and allowing unsafe access) is a future extension not implemented at this time. * A `static mut` continues to always represent a memory location. All references to a `static mut` continue to be `unsafe`. This is a large breaking change, and many programs will need to be updated accordingly. A summary of the breaking changes is: * Statics may no longer be used in patterns. Statics now always represent a memory location, which can sometimes be modified. To fix code, repurpose the matched-on-`static` to a `const`. static FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } change this code to: const FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } * Statics may no longer refer to other statics by value. Due to statics being able to change at runtime, allowing them to reference one another could possibly lead to confusing semantics. If you are in this situation, use a constant initializer instead. Note, however, that statics may reference other statics by address, however. * Statics may no longer be used in constant expressions, such as array lengths. This is due to the same restrictions as listed above. Use a `const` instead. [breaking-change] [rfc]: https://github.com/rust-lang/rfcs/pull/246
2014-10-06 10:17:01 -05:00
Some(DefConst(did)) => match lookup_const_by_id(self.tcx, did) {
2014-09-07 12:09:06 -05:00
Some(const_expr) => {
const_expr_to_pat(self.tcx, const_expr, pat.span).map(|new_pat| {
if let Some(ref mut renaming_map) = self.renaming_map {
// Record any renamings we do here
record_renamings(const_expr, &pat, renaming_map);
}
2014-09-07 12:09:06 -05:00
new_pat
})
}
None => {
self.failed = true;
span_err!(self.tcx.sess, pat.span, E0158,
rustc: Add `const` globals to the language This change is an implementation of [RFC 69][rfc] which adds a third kind of global to the language, `const`. This global is most similar to what the old `static` was, and if you're unsure about what to use then you should use a `const`. The semantics of these three kinds of globals are: * A `const` does not represent a memory location, but only a value. Constants are translated as rvalues, which means that their values are directly inlined at usage location (similar to a #define in C/C++). Constant values are, well, constant, and can not be modified. Any "modification" is actually a modification to a local value on the stack rather than the actual constant itself. Almost all values are allowed inside constants, whether they have interior mutability or not. There are a few minor restrictions listed in the RFC, but they should in general not come up too often. * A `static` now always represents a memory location (unconditionally). Any references to the same `static` are actually a reference to the same memory location. Only values whose types ascribe to `Sync` are allowed in a `static`. This restriction is in place because many threads may access a `static` concurrently. Lifting this restriction (and allowing unsafe access) is a future extension not implemented at this time. * A `static mut` continues to always represent a memory location. All references to a `static mut` continue to be `unsafe`. This is a large breaking change, and many programs will need to be updated accordingly. A summary of the breaking changes is: * Statics may no longer be used in patterns. Statics now always represent a memory location, which can sometimes be modified. To fix code, repurpose the matched-on-`static` to a `const`. static FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } change this code to: const FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } * Statics may no longer refer to other statics by value. Due to statics being able to change at runtime, allowing them to reference one another could possibly lead to confusing semantics. If you are in this situation, use a constant initializer instead. Note, however, that statics may reference other statics by address, however. * Statics may no longer be used in constant expressions, such as array lengths. This is due to the same restrictions as listed above. Use a `const` instead. [breaking-change] [rfc]: https://github.com/rust-lang/rfcs/pull/246
2014-10-06 10:17:01 -05:00
"statics cannot be referenced in patterns");
pat
}
},
_ => noop_fold_pat(pat, self)
}
}
_ => noop_fold_pat(pat, self)
};
fn record_renamings(const_expr: &ast::Expr,
substituted_pat: &ast::Pat,
renaming_map: &mut FnvHashMap<(NodeId, Span), NodeId>) {
let mut renaming_recorder = RenamingRecorder {
substituted_node_id: substituted_pat.id,
origin_span: substituted_pat.span,
renaming_map: renaming_map,
};
let mut id_visitor = ast_util::IdVisitor {
operation: &mut renaming_recorder,
pass_through_items: true,
visited_outermost: false,
};
id_visitor.visit_expr(const_expr);
}
}
}
/// 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<&Pat>, left_ty: Ty) -> P<Pat> {
2014-09-07 12:09:06 -05:00
let pats_len = pats.len();
2014-09-14 22:27:36 -05:00
let mut pats = pats.into_iter().map(|p| P((*p).clone()));
let pat = match left_ty.sty {
ty::ty_tup(_) => ast::PatTup(pats.collect()),
2014-06-19 13:55:12 -05:00
ty::ty_enum(cid, _) | ty::ty_struct(cid, _) => {
let (vid, is_structure) = match ctor {
&Variant(vid) =>
(vid, ty::enum_variant_with_id(cx.tcx, cid, vid).arg_names.is_some()),
_ =>
(cid, !ty::is_tuple_struct(cx.tcx, cid))
};
2014-06-19 13:55:12 -05:00
if is_structure {
let fields = ty::lookup_struct_fields(cx.tcx, vid);
let field_pats: Vec<_> = fields.into_iter()
2014-09-07 12:09:06 -05:00
.zip(pats)
.filter(|&(_, ref pat)| pat.node != ast::PatWild(ast::PatWildSingle))
.map(|(field, pat)| Spanned {
span: DUMMY_SP,
node: ast::FieldPat {
ident: ast::Ident::new(field.name),
pat: pat,
is_shorthand: false,
}
2014-06-19 13:55:12 -05:00
}).collect();
2014-09-07 12:09:06 -05:00
let has_more_fields = field_pats.len() < pats_len;
ast::PatStruct(def_to_path(cx.tcx, vid), field_pats, has_more_fields)
2014-06-19 13:55:12 -05:00
} else {
ast::PatEnum(def_to_path(cx.tcx, vid), Some(pats.collect()))
2014-06-19 13:55:12 -05:00
}
}
ty::ty_rptr(_, ty::mt { ty, mutbl }) => {
match ty.sty {
ty::ty_vec(_, Some(n)) => match ctor {
&Single => {
2014-09-07 12:09:06 -05:00
assert_eq!(pats_len, n);
ast::PatVec(pats.collect(), None, vec!())
},
_ => unreachable!()
},
ty::ty_vec(_, None) => match ctor {
&Slice(n) => {
2014-09-07 12:09:06 -05:00
assert_eq!(pats_len, n);
ast::PatVec(pats.collect(), None, vec!())
},
_ => unreachable!()
},
ty::ty_str => ast::PatWild(ast::PatWildSingle),
_ => {
2014-09-07 12:09:06 -05:00
assert_eq!(pats_len, 1);
ast::PatRegion(pats.nth(0).unwrap(), mutbl)
}
}
}
ty::ty_vec(_, Some(len)) => {
2014-09-07 12:09:06 -05:00
assert_eq!(pats_len, len);
ast::PatVec(pats.collect(), None, vec![])
}
_ => {
match *ctor {
ConstantValue(ref v) => ast::PatLit(const_val_to_expr(v)),
_ => ast::PatWild(ast::PatWildSingle),
}
}
};
P(ast::Pat {
id: 0,
node: pat,
span: DUMMY_SP
2014-09-07 12:09:06 -05:00
})
}
fn missing_constructor(cx: &MatchCheckCtxt, &Matrix(ref rows): &Matrix,
left_ty: Ty, max_slice_length: uint) -> Option<Constructor> {
let used_constructors: Vec<Constructor> = rows.iter()
.flat_map(|row| pat_constructors(cx, row[0], left_ty, max_slice_length).into_iter())
.collect();
all_constructors(cx, left_ty, max_slice_length)
2014-09-14 22:27:36 -05:00
.into_iter()
.find(|c| !used_constructors.contains(c))
}
/// 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,
max_slice_length: uint) -> Vec<Constructor> {
match left_ty.sty {
ty::ty_bool =>
[true, false].iter().map(|b| ConstantValue(const_bool(*b))).collect(),
ty::ty_rptr(_, ty::mt { ty, .. }) => match ty.sty {
ty::ty_vec(_, None) =>
range_inclusive(0, max_slice_length).map(|length| Slice(length)).collect(),
_ => vec!(Single)
},
ty::ty_enum(eid, _) =>
2014-06-19 13:55:12 -05:00
ty::enum_variants(cx.tcx, eid)
.iter()
.map(|va| Variant(va.id))
2014-06-19 13:55:12 -05:00
.collect(),
_ =>
vec!(Single)
}
}
// 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).
// Note: is_useful doesn't work on empty types, as the paper notes.
// So it assumes that v is non-empty.
fn is_useful(cx: &MatchCheckCtxt,
matrix: &Matrix,
2014-09-07 12:09:06 -05:00
v: &[&Pat],
witness: WitnessPreference)
-> Usefulness {
let &Matrix(ref rows) = matrix;
debug!("{:?}", matrix);
if rows.len() == 0 {
return match witness {
ConstructWitness => UsefulWithWitness(vec!()),
LeaveOutWitness => Useful
};
}
if rows[0].len() == 0 {
return NotUseful;
}
let real_pat = match rows.iter().find(|r| (*r)[0].id != DUMMY_NODE_ID) {
Some(r) => raw_pat(r[0]),
None if v.len() == 0 => return NotUseful,
2014-05-13 00:20:52 -05:00
None => v[0]
};
2014-09-07 12:09:06 -05:00
let left_ty = if real_pat.id == DUMMY_NODE_ID {
ty::mk_nil(cx.tcx)
} else {
ty::pat_ty(cx.tcx, &*real_pat)
};
let max_slice_length = rows.iter().filter_map(|row| match row[0].node {
ast::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() {
match missing_constructor(cx, matrix, left_ty, max_slice_length) {
None => {
2014-09-14 22:27:36 -05:00
all_constructors(cx, left_ty, max_slice_length).into_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);
2014-09-07 12:09:06 -05:00
let mut result = {
let pat_slice = &pats[..];
let subpats: Vec<_> = (0..arity).map(|i| {
pat_slice.get(i).map_or(DUMMY_WILD_PAT, |p| &**p)
2014-12-30 12:51:18 -06:00
}).collect();
2014-09-07 12:09:06 -05:00
vec![construct_witness(cx, &c, subpats, left_ty)]
};
2014-09-14 22:27:36 -05:00
result.extend(pats.into_iter().skip(arity));
result
}),
result => result
}
}).find(|result| result != &NotUseful).unwrap_or(NotUseful)
},
Some(constructor) => {
2014-09-07 12:09:06 -05:00
let matrix = rows.iter().filter_map(|r| {
if pat_is_binding_or_wild(&cx.tcx.def_map, raw_pat(r[0])) {
Some(r.tail().to_vec())
2014-09-07 12:09:06 -05:00
} else {
None
}
}).collect();
2014-06-19 13:55:12 -05:00
match is_useful(cx, &matrix, v.tail(), witness) {
UsefulWithWitness(pats) => {
let arity = constructor_arity(cx, &constructor, left_ty);
2014-12-30 12:51:18 -06:00
let wild_pats: Vec<_> = repeat(DUMMY_WILD_PAT).take(arity).collect();
let enum_pat = construct_witness(cx, &constructor, wild_pats, left_ty);
2014-09-07 12:09:06 -05:00
let mut new_pats = vec![enum_pat];
2014-09-14 22:27:36 -05:00
new_pats.extend(pats.into_iter());
2014-09-07 12:09:06 -05:00
UsefulWithWitness(new_pats)
},
result => result
}
}
}
} else {
2014-09-14 22:27:36 -05:00
constructors.into_iter().map(|c|
is_useful_specialized(cx, matrix, v, c.clone(), left_ty, witness)
).find(|result| result != &NotUseful).unwrap_or(NotUseful)
}
}
2014-09-07 12:09:06 -05:00
fn is_useful_specialized(cx: &MatchCheckCtxt, &Matrix(ref m): &Matrix,
v: &[&Pat], ctor: Constructor, lty: Ty,
2014-09-07 12:09:06 -05:00
witness: WitnessPreference) -> Usefulness {
let arity = constructor_arity(cx, &ctor, lty);
let matrix = Matrix(m.iter().filter_map(|r| {
specialize(cx, &r[..], &ctor, 0, arity)
}).collect());
match specialize(cx, v, &ctor, 0, arity) {
Some(v) => is_useful(cx, &matrix, &v[..], witness),
None => NotUseful
}
}
/// 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.
2014-09-07 12:09:06 -05:00
fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat,
left_ty: Ty, max_slice_length: uint) -> Vec<Constructor> {
let pat = raw_pat(p);
match pat.node {
ast::PatIdent(..) =>
match cx.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def()) {
Some(DefConst(..)) =>
rustc: Add `const` globals to the language This change is an implementation of [RFC 69][rfc] which adds a third kind of global to the language, `const`. This global is most similar to what the old `static` was, and if you're unsure about what to use then you should use a `const`. The semantics of these three kinds of globals are: * A `const` does not represent a memory location, but only a value. Constants are translated as rvalues, which means that their values are directly inlined at usage location (similar to a #define in C/C++). Constant values are, well, constant, and can not be modified. Any "modification" is actually a modification to a local value on the stack rather than the actual constant itself. Almost all values are allowed inside constants, whether they have interior mutability or not. There are a few minor restrictions listed in the RFC, but they should in general not come up too often. * A `static` now always represents a memory location (unconditionally). Any references to the same `static` are actually a reference to the same memory location. Only values whose types ascribe to `Sync` are allowed in a `static`. This restriction is in place because many threads may access a `static` concurrently. Lifting this restriction (and allowing unsafe access) is a future extension not implemented at this time. * A `static mut` continues to always represent a memory location. All references to a `static mut` continue to be `unsafe`. This is a large breaking change, and many programs will need to be updated accordingly. A summary of the breaking changes is: * Statics may no longer be used in patterns. Statics now always represent a memory location, which can sometimes be modified. To fix code, repurpose the matched-on-`static` to a `const`. static FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } change this code to: const FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } * Statics may no longer refer to other statics by value. Due to statics being able to change at runtime, allowing them to reference one another could possibly lead to confusing semantics. If you are in this situation, use a constant initializer instead. Note, however, that statics may reference other statics by address, however. * Statics may no longer be used in constant expressions, such as array lengths. This is due to the same restrictions as listed above. Use a `const` instead. [breaking-change] [rfc]: https://github.com/rust-lang/rfcs/pull/246
2014-10-06 10:17:01 -05:00
cx.tcx.sess.span_bug(pat.span, "const pattern should've \
been rewritten"),
Some(DefStruct(_)) => vec!(Single),
Some(DefVariant(_, id, _)) => vec!(Variant(id)),
_ => vec!()
2014-06-19 13:55:12 -05:00
},
ast::PatEnum(..) =>
match cx.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def()) {
Some(DefConst(..)) =>
cx.tcx.sess.span_bug(pat.span, "const pattern should've \
rustc: Add `const` globals to the language This change is an implementation of [RFC 69][rfc] which adds a third kind of global to the language, `const`. This global is most similar to what the old `static` was, and if you're unsure about what to use then you should use a `const`. The semantics of these three kinds of globals are: * A `const` does not represent a memory location, but only a value. Constants are translated as rvalues, which means that their values are directly inlined at usage location (similar to a #define in C/C++). Constant values are, well, constant, and can not be modified. Any "modification" is actually a modification to a local value on the stack rather than the actual constant itself. Almost all values are allowed inside constants, whether they have interior mutability or not. There are a few minor restrictions listed in the RFC, but they should in general not come up too often. * A `static` now always represents a memory location (unconditionally). Any references to the same `static` are actually a reference to the same memory location. Only values whose types ascribe to `Sync` are allowed in a `static`. This restriction is in place because many threads may access a `static` concurrently. Lifting this restriction (and allowing unsafe access) is a future extension not implemented at this time. * A `static mut` continues to always represent a memory location. All references to a `static mut` continue to be `unsafe`. This is a large breaking change, and many programs will need to be updated accordingly. A summary of the breaking changes is: * Statics may no longer be used in patterns. Statics now always represent a memory location, which can sometimes be modified. To fix code, repurpose the matched-on-`static` to a `const`. static FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } change this code to: const FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } * Statics may no longer refer to other statics by value. Due to statics being able to change at runtime, allowing them to reference one another could possibly lead to confusing semantics. If you are in this situation, use a constant initializer instead. Note, however, that statics may reference other statics by address, however. * Statics may no longer be used in constant expressions, such as array lengths. This is due to the same restrictions as listed above. Use a `const` instead. [breaking-change] [rfc]: https://github.com/rust-lang/rfcs/pull/246
2014-10-06 10:17:01 -05:00
been rewritten"),
Some(DefVariant(_, id, _)) => vec!(Variant(id)),
_ => vec!(Single)
2014-06-19 13:55:12 -05:00
},
ast::PatStruct(..) =>
match cx.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def()) {
Some(DefConst(..)) =>
cx.tcx.sess.span_bug(pat.span, "const pattern should've \
rustc: Add `const` globals to the language This change is an implementation of [RFC 69][rfc] which adds a third kind of global to the language, `const`. This global is most similar to what the old `static` was, and if you're unsure about what to use then you should use a `const`. The semantics of these three kinds of globals are: * A `const` does not represent a memory location, but only a value. Constants are translated as rvalues, which means that their values are directly inlined at usage location (similar to a #define in C/C++). Constant values are, well, constant, and can not be modified. Any "modification" is actually a modification to a local value on the stack rather than the actual constant itself. Almost all values are allowed inside constants, whether they have interior mutability or not. There are a few minor restrictions listed in the RFC, but they should in general not come up too often. * A `static` now always represents a memory location (unconditionally). Any references to the same `static` are actually a reference to the same memory location. Only values whose types ascribe to `Sync` are allowed in a `static`. This restriction is in place because many threads may access a `static` concurrently. Lifting this restriction (and allowing unsafe access) is a future extension not implemented at this time. * A `static mut` continues to always represent a memory location. All references to a `static mut` continue to be `unsafe`. This is a large breaking change, and many programs will need to be updated accordingly. A summary of the breaking changes is: * Statics may no longer be used in patterns. Statics now always represent a memory location, which can sometimes be modified. To fix code, repurpose the matched-on-`static` to a `const`. static FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } change this code to: const FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } * Statics may no longer refer to other statics by value. Due to statics being able to change at runtime, allowing them to reference one another could possibly lead to confusing semantics. If you are in this situation, use a constant initializer instead. Note, however, that statics may reference other statics by address, however. * Statics may no longer be used in constant expressions, such as array lengths. This is due to the same restrictions as listed above. Use a `const` instead. [breaking-change] [rfc]: https://github.com/rust-lang/rfcs/pull/246
2014-10-06 10:17:01 -05:00
been rewritten"),
Some(DefVariant(_, id, _)) => vec!(Variant(id)),
_ => vec!(Single)
},
ast::PatLit(ref expr) =>
2014-09-07 12:09:06 -05:00
vec!(ConstantValue(eval_const_expr(cx.tcx, &**expr))),
ast::PatRange(ref lo, ref hi) =>
2014-09-07 12:09:06 -05:00
vec!(ConstantRange(eval_const_expr(cx.tcx, &**lo), eval_const_expr(cx.tcx, &**hi))),
ast::PatVec(ref before, ref slice, ref after) =>
match 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()))
}
},
ast::PatBox(_) | ast::PatTup(_) | ast::PatRegion(..) =>
vec!(Single),
ast::PatWild(_) =>
vec!(),
ast::PatMac(_) =>
cx.tcx.sess.bug("unexpanded macro")
}
}
/// 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 (_, 42, Some([])) has the arity of 3.
/// A struct pattern's arity is the number of fields it contains, etc.
pub fn constructor_arity(cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> uint {
match ty.sty {
ty::ty_tup(ref fs) => fs.len(),
ty::ty_uniq(_) => 1,
ty::ty_rptr(_, ty::mt { ty, .. }) => match ty.sty {
ty::ty_vec(_, None) => match *ctor {
Slice(length) => length,
ConstantValue(_) => 0,
_ => unreachable!()
},
ty::ty_str => 0,
_ => 1
},
ty::ty_enum(eid, _) => {
match *ctor {
Variant(id) => enum_variant_with_id(cx.tcx, eid, id).args.len(),
_ => unreachable!()
}
}
ty::ty_struct(cid, _) => ty::lookup_struct_fields(cx.tcx, cid).len(),
ty::ty_vec(_, Some(n)) => n,
_ => 0
}
}
fn range_covered_by_constructor(ctor: &Constructor,
from: &const_val, to: &const_val) -> Option<bool> {
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(cmp_from), Some(cmp_to)) => {
Some(cmp_from != Ordering::Less && cmp_to != Ordering::Greater)
}
2014-06-02 13:49:44 -05:00
_ => None
}
}
/// 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.
pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
2014-09-07 12:09:06 -05:00
constructor: &Constructor, col: uint, arity: uint) -> Option<Vec<&'a Pat>> {
let &Pat {
id: pat_id, ref node, span: pat_span
2014-09-07 12:09:06 -05:00
} = raw_pat(r[col]);
let head: Option<Vec<&Pat>> = match *node {
ast::PatWild(_) =>
2014-12-30 12:51:18 -06:00
Some(repeat(DUMMY_WILD_PAT).take(arity).collect()),
ast::PatIdent(_, _, _) => {
let opt_def = cx.tcx.def_map.borrow().get(&pat_id).map(|d| d.full_def());
match opt_def {
rustc: Add `const` globals to the language This change is an implementation of [RFC 69][rfc] which adds a third kind of global to the language, `const`. This global is most similar to what the old `static` was, and if you're unsure about what to use then you should use a `const`. The semantics of these three kinds of globals are: * A `const` does not represent a memory location, but only a value. Constants are translated as rvalues, which means that their values are directly inlined at usage location (similar to a #define in C/C++). Constant values are, well, constant, and can not be modified. Any "modification" is actually a modification to a local value on the stack rather than the actual constant itself. Almost all values are allowed inside constants, whether they have interior mutability or not. There are a few minor restrictions listed in the RFC, but they should in general not come up too often. * A `static` now always represents a memory location (unconditionally). Any references to the same `static` are actually a reference to the same memory location. Only values whose types ascribe to `Sync` are allowed in a `static`. This restriction is in place because many threads may access a `static` concurrently. Lifting this restriction (and allowing unsafe access) is a future extension not implemented at this time. * A `static mut` continues to always represent a memory location. All references to a `static mut` continue to be `unsafe`. This is a large breaking change, and many programs will need to be updated accordingly. A summary of the breaking changes is: * Statics may no longer be used in patterns. Statics now always represent a memory location, which can sometimes be modified. To fix code, repurpose the matched-on-`static` to a `const`. static FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } change this code to: const FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } * Statics may no longer refer to other statics by value. Due to statics being able to change at runtime, allowing them to reference one another could possibly lead to confusing semantics. If you are in this situation, use a constant initializer instead. Note, however, that statics may reference other statics by address, however. * Statics may no longer be used in constant expressions, such as array lengths. This is due to the same restrictions as listed above. Use a `const` instead. [breaking-change] [rfc]: https://github.com/rust-lang/rfcs/pull/246
2014-10-06 10:17:01 -05:00
Some(DefConst(..)) =>
cx.tcx.sess.span_bug(pat_span, "const pattern should've \
been rewritten"),
Some(DefVariant(_, id, _)) => if *constructor == Variant(id) {
Some(vec!())
} else {
None
2014-06-19 13:55:12 -05:00
},
2014-12-30 12:51:18 -06:00
_ => Some(repeat(DUMMY_WILD_PAT).take(arity).collect())
}
}
ast::PatEnum(_, ref args) => {
let def = cx.tcx.def_map.borrow()[pat_id].full_def();
match def {
rustc: Add `const` globals to the language This change is an implementation of [RFC 69][rfc] which adds a third kind of global to the language, `const`. This global is most similar to what the old `static` was, and if you're unsure about what to use then you should use a `const`. The semantics of these three kinds of globals are: * A `const` does not represent a memory location, but only a value. Constants are translated as rvalues, which means that their values are directly inlined at usage location (similar to a #define in C/C++). Constant values are, well, constant, and can not be modified. Any "modification" is actually a modification to a local value on the stack rather than the actual constant itself. Almost all values are allowed inside constants, whether they have interior mutability or not. There are a few minor restrictions listed in the RFC, but they should in general not come up too often. * A `static` now always represents a memory location (unconditionally). Any references to the same `static` are actually a reference to the same memory location. Only values whose types ascribe to `Sync` are allowed in a `static`. This restriction is in place because many threads may access a `static` concurrently. Lifting this restriction (and allowing unsafe access) is a future extension not implemented at this time. * A `static mut` continues to always represent a memory location. All references to a `static mut` continue to be `unsafe`. This is a large breaking change, and many programs will need to be updated accordingly. A summary of the breaking changes is: * Statics may no longer be used in patterns. Statics now always represent a memory location, which can sometimes be modified. To fix code, repurpose the matched-on-`static` to a `const`. static FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } change this code to: const FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } * Statics may no longer refer to other statics by value. Due to statics being able to change at runtime, allowing them to reference one another could possibly lead to confusing semantics. If you are in this situation, use a constant initializer instead. Note, however, that statics may reference other statics by address, however. * Statics may no longer be used in constant expressions, such as array lengths. This is due to the same restrictions as listed above. Use a `const` instead. [breaking-change] [rfc]: https://github.com/rust-lang/rfcs/pull/246
2014-10-06 10:17:01 -05:00
DefConst(..) =>
cx.tcx.sess.span_bug(pat_span, "const pattern should've \
been rewritten"),
DefVariant(_, id, _) if *constructor != Variant(id) => None,
DefVariant(..) | DefStruct(..) => {
Some(match args {
2014-09-07 12:09:06 -05:00
&Some(ref args) => args.iter().map(|p| &**p).collect(),
2014-12-30 12:51:18 -06:00
&None => repeat(DUMMY_WILD_PAT).take(arity).collect(),
})
}
_ => None
}
}
ast::PatStruct(_, ref pattern_fields, _) => {
// Is this a struct or an enum variant?
let def = cx.tcx.def_map.borrow()[pat_id].full_def();
let class_id = match def {
rustc: Add `const` globals to the language This change is an implementation of [RFC 69][rfc] which adds a third kind of global to the language, `const`. This global is most similar to what the old `static` was, and if you're unsure about what to use then you should use a `const`. The semantics of these three kinds of globals are: * A `const` does not represent a memory location, but only a value. Constants are translated as rvalues, which means that their values are directly inlined at usage location (similar to a #define in C/C++). Constant values are, well, constant, and can not be modified. Any "modification" is actually a modification to a local value on the stack rather than the actual constant itself. Almost all values are allowed inside constants, whether they have interior mutability or not. There are a few minor restrictions listed in the RFC, but they should in general not come up too often. * A `static` now always represents a memory location (unconditionally). Any references to the same `static` are actually a reference to the same memory location. Only values whose types ascribe to `Sync` are allowed in a `static`. This restriction is in place because many threads may access a `static` concurrently. Lifting this restriction (and allowing unsafe access) is a future extension not implemented at this time. * A `static mut` continues to always represent a memory location. All references to a `static mut` continue to be `unsafe`. This is a large breaking change, and many programs will need to be updated accordingly. A summary of the breaking changes is: * Statics may no longer be used in patterns. Statics now always represent a memory location, which can sometimes be modified. To fix code, repurpose the matched-on-`static` to a `const`. static FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } change this code to: const FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } * Statics may no longer refer to other statics by value. Due to statics being able to change at runtime, allowing them to reference one another could possibly lead to confusing semantics. If you are in this situation, use a constant initializer instead. Note, however, that statics may reference other statics by address, however. * Statics may no longer be used in constant expressions, such as array lengths. This is due to the same restrictions as listed above. Use a `const` instead. [breaking-change] [rfc]: https://github.com/rust-lang/rfcs/pull/246
2014-10-06 10:17:01 -05:00
DefConst(..) =>
cx.tcx.sess.span_bug(pat_span, "const pattern should've \
been rewritten"),
DefVariant(_, variant_id, _) => if *constructor == Variant(variant_id) {
Some(variant_id)
} else {
None
},
_ => {
// 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),
}
}
};
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.node.ident.name == sf.name) {
Some(ref f) => &*f.node.pat,
_ => DUMMY_WILD_PAT
}
}).collect();
args
})
}
ast::PatTup(ref args) =>
2014-09-07 12:09:06 -05:00
Some(args.iter().map(|p| &**p).collect()),
ast::PatBox(ref inner) | ast::PatRegion(ref inner, _) =>
2014-09-07 12:09:06 -05:00
Some(vec![&**inner]),
ast::PatLit(ref expr) => {
let expr_value = eval_const_expr(cx.tcx, &**expr);
match range_covered_by_constructor(constructor, &expr_value, &expr_value) {
2014-09-07 12:09:06 -05:00
Some(true) => Some(vec![]),
Some(false) => None,
None => {
2015-01-18 18:58:25 -06:00
span_err!(cx.tcx.sess, pat_span, E0298, "mismatched types between arms");
None
}
}
}
ast::PatRange(ref from, ref to) => {
let from_value = eval_const_expr(cx.tcx, &**from);
let to_value = eval_const_expr(cx.tcx, &**to);
match range_covered_by_constructor(constructor, &from_value, &to_value) {
2014-09-07 12:09:06 -05:00
Some(true) => Some(vec![]),
Some(false) => None,
None => {
2015-01-18 18:58:25 -06:00
span_err!(cx.tcx.sess, pat_span, E0299, "mismatched types between arms");
2014-06-02 13:49:44 -05:00
None
}
2013-05-21 04:04:55 -05:00
}
}
ast::PatVec(ref before, ref slice, ref after) => {
match *constructor {
// Fixed-length vectors.
Single => {
2014-09-07 12:09:06 -05:00
let mut pats: Vec<&Pat> = before.iter().map(|p| &**p).collect();
2014-12-30 12:51:18 -06:00
pats.extend(repeat(DUMMY_WILD_PAT).take(arity - before.len() - after.len()));
2014-09-07 12:09:06 -05:00
pats.extend(after.iter().map(|p| &**p));
Some(pats)
},
Slice(length) if before.len() + after.len() <= length && slice.is_some() => {
2014-09-07 12:09:06 -05:00
let mut pats: Vec<&Pat> = before.iter().map(|p| &**p).collect();
2014-12-30 12:51:18 -06:00
pats.extend(repeat(DUMMY_WILD_PAT).take(arity - before.len() - after.len()));
2014-09-07 12:09:06 -05:00
pats.extend(after.iter().map(|p| &**p));
Some(pats)
},
Slice(length) if before.len() + after.len() == length => {
2014-09-07 12:09:06 -05:00
let mut pats: Vec<&Pat> = before.iter().map(|p| &**p).collect();
pats.extend(after.iter().map(|p| &**p));
Some(pats)
},
SliceWithSubslice(prefix, suffix)
if before.len() == prefix
&& after.len() == suffix
&& slice.is_some() => {
2014-09-07 12:09:06 -05:00
let mut pats: Vec<&Pat> = before.iter().map(|p| &**p).collect();
pats.extend(after.iter().map(|p| &**p));
Some(pats)
}
_ => None
}
}
ast::PatMac(_) => {
2015-01-18 18:58:25 -06:00
span_err!(cx.tcx.sess, pat_span, E0300, "unexpanded macro");
None
}
};
head.map(|mut head| {
2015-01-12 15:59:18 -06:00
head.push_all(&r[..col]);
head.push_all(&r[col + 1..]);
head
})
}
fn check_local(cx: &mut MatchCheckCtxt, loc: &ast::Local) {
visit::walk_local(cx, loc);
let name = match loc.source {
ast::LocalLet => "local",
ast::LocalFor => "`for` loop"
};
let mut static_inliner = StaticInliner::new(cx.tcx, None);
2014-09-07 12:09:06 -05:00
is_refutable(cx, &*static_inliner.fold_pat(loc.pat.clone()), |pat| {
span_err!(cx.tcx.sess, loc.pat.span, E0005,
"refutable pattern in {} binding: `{}` not covered",
name, pat_to_string(pat)
);
});
// Check legality of move bindings and `@` patterns.
2014-09-07 12:09:06 -05:00
check_legality_of_move_bindings(cx, false, slice::ref_slice(&loc.pat));
check_legality_of_bindings_in_at_patterns(cx, &*loc.pat);
}
2014-03-05 21:07:47 -06:00
fn check_fn(cx: &mut MatchCheckCtxt,
kind: FnKind,
decl: &ast::FnDecl,
body: &ast::Block,
sp: Span,
fn_id: NodeId) {
match kind {
visit::FkFnBlock => {}
_ => cx.param_env = ParameterEnvironment::for_item(cx.tcx, fn_id),
}
visit::walk_fn(cx, kind, decl, body, sp);
2015-01-31 11:20:46 -06:00
for input in &decl.inputs {
2014-09-07 12:09:06 -05:00
is_refutable(cx, &*input.pat, |pat| {
span_err!(cx.tcx.sess, input.pat.span, E0006,
"refutable pattern in function argument: `{}` not covered",
pat_to_string(pat)
);
});
check_legality_of_move_bindings(cx, false, slice::ref_slice(&input.pat));
check_legality_of_bindings_in_at_patterns(cx, &*input.pat);
}
}
2014-12-08 19:26:43 -06:00
fn is_refutable<A, F>(cx: &MatchCheckCtxt, pat: &Pat, refutable: F) -> Option<A> where
F: FnOnce(&Pat) -> A,
{
let pats = Matrix(vec!(vec!(pat)));
2014-11-17 02:39:01 -06:00
match is_useful(cx, &pats, &[DUMMY_WILD_PAT], ConstructWitness) {
UsefulWithWitness(pats) => {
assert_eq!(pats.len(), 1);
2014-09-07 12:09:06 -05:00
Some(refutable(&*pats[0]))
},
NotUseful => None,
Useful => unreachable!()
}
}
// Legality of move bindings checking
2013-10-06 14:27:36 -05:00
fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
has_guard: bool,
2014-09-07 12:09:06 -05:00
pats: &[P<Pat>]) {
let tcx = cx.tcx;
2014-04-22 11:06:43 -05:00
let def_map = &tcx.def_map;
let mut by_ref_span = None;
2015-01-31 11:20:46 -06:00
for pat in pats {
2014-05-16 12:15:33 -05:00
pat_bindings(def_map, &**pat, |bm, _, span, _path| {
match bm {
ast::BindByRef(_) => {
by_ref_span = Some(span);
}
ast::BindByValue(_) => {
}
}
})
}
let check_move = |p: &Pat, sub: Option<&Pat>| {
// check legality of moving out of the enum
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)) {
span_err!(cx.tcx.sess, p.span, E0007, "cannot bind by-move with sub-bindings");
} else if has_guard {
span_err!(cx.tcx.sess, p.span, E0008, "cannot bind by-move into a pattern guard");
} else if by_ref_span.is_some() {
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");
}
};
2015-01-31 11:20:46 -06:00
for pat in pats {
ast_util::walk_pat(&**pat, |p| {
2014-05-16 12:15:33 -05:00
if pat_is_binding(def_map, &*p) {
match p.node {
ast::PatIdent(ast::BindByValue(_), _, ref sub) => {
let pat_ty = ty::node_id_to_type(tcx, p.id);
if ty::type_moves_by_default(&cx.param_env, pat.span, pat_ty) {
2014-09-07 12:09:06 -05:00
check_move(p, sub.as_ref().map(|p| &**p));
}
}
ast::PatIdent(ast::BindByRef(_), _, _) => {
}
_ => {
cx.tcx.sess.span_bug(
p.span,
2015-01-07 10:58:31 -06:00
&format!("binding pattern {} is not an \
identifier: {:?}",
p.id,
p.node));
}
}
}
true
});
}
}
/// Ensures that a pattern guard doesn't borrow by mutable reference or
/// assign.
fn check_for_mutation_in_guard<'a, 'tcx>(cx: &'a MatchCheckCtxt<'a, 'tcx>,
guard: &ast::Expr) {
let mut checker = MutationChecker {
cx: cx,
};
let mut visitor = ExprUseVisitor::new(&mut checker,
&checker.cx.param_env);
visitor.walk_expr(guard);
}
struct MutationChecker<'a, 'tcx: 'a> {
cx: &'a MatchCheckCtxt<'a, 'tcx>,
}
impl<'a, 'tcx> Delegate<'tcx> for MutationChecker<'a, 'tcx> {
fn matched_pat(&mut self, _: &Pat, _: cmt, _: euv::MatchMode) {}
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 => {
2015-01-18 18:58:25 -06:00
span_err!(self.cx.tcx.sess, span, E0301,
"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 => {
2015-01-18 18:58:25 -06:00
span_err!(self.cx.tcx.sess, span, E0302, "cannot assign in a pattern guard")
}
Init => {}
}
}
}
/// 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) {
AtBindingPatternVisitor { cx: cx, bindings_allowed: true }.visit_pat(pat);
}
struct AtBindingPatternVisitor<'a, 'b:'a, 'tcx:'b> {
cx: &'a MatchCheckCtxt<'b, 'tcx>,
bindings_allowed: bool
}
impl<'a, 'b, 'tcx, 'v> Visitor<'v> for AtBindingPatternVisitor<'a, 'b, 'tcx> {
fn visit_pat(&mut self, pat: &Pat) {
if !self.bindings_allowed && pat_is_binding(&self.cx.tcx.def_map, pat) {
2015-01-18 18:58:25 -06:00
span_err!(self.cx.tcx.sess, pat.span, E0303,
"pattern bindings are not allowed \
after an `@`");
}
match pat.node {
ast::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),
}
}
}