Merge remote-tracking branch 'origin/master' into 0.11.0-release

Conflicts:
	RELEASES.txt
This commit is contained in:
Alex Crichton 2014-07-02 18:48:23 -07:00
commit c4ac124edd
10 changed files with 605 additions and 237 deletions

View File

@ -41,7 +41,7 @@ script: |
if [[ $LLVM_VERSION != '3.4' ]]; then exit 0; fi
fi &&
make tidy &&
travis_wait make -j4 rustc-stage1 &&
make -j4 rustc-stage1 RUSTFLAGS='-Z time-passes' &&
make check-stage1-std check-stage1-rpass check-stage1-cfail check-stage1-rfail check-stage1-doc
env:

View File

@ -487,7 +487,6 @@ TyOverby <ty@pre-alpha.com>
Tycho Sci <tychosci@gmail.com>
Tyler Bindon <martica@martica.org>
U-NOV2010\eugals
User Jyyou <jyyou@plaslab.cs.nctu.edu.tw>
Utkarsh Kukreti <utkarshkukreti@gmail.com>
Uwe Dauernheim <uwe@dauernheim.net>
Vadim Chugunov <vadimcn@gmail.com>

View File

@ -1,7 +1,7 @@
Version 0.11 (July 2014)
Version 0.11.0 (July 2014)
-------------------------
* ~1700 cahnges, numerous bugfixes
* ~1700 changes, numerous bugfixes
* Language
* ~[T] has been removed from the language. This type is superseded by
@ -13,15 +13,15 @@ Version 0.11 (July 2014)
* @T has been removed from the language. This type is superseded by the
standard library's std::gc::Gc<T> type.
* Struct fields are now all private by default.
* Vector indices and shift amounts are both required to be a `uint` now
* Vector indices and shift amounts are both required to be a `uint`
instead of any integral type.
* Byte character, byte string, and raw byte string literals are now all
supported by prefixing the normal literal with a `b`.
* Multiple ABIs are no longer allowed in an ABI string
* The syntax for lifetimes on clousres/procedures has been tweaked
slightly.
* Floating point modulus has been removed from the language, it is still
provided by a library implementation, however.
* The syntax for lifetimes on closures/procedures has been tweaked
slightly: `<'a>|A, B|: 'b + K -> T`
* Floating point modulus has been removed from the language; however it
is still provided by a library implementation.
* Private enum variants are now disallowed.
* The `priv` keyword has been removed from the language.
* A closure can no longer be invoked through a &-pointer.
@ -29,10 +29,9 @@ Version 0.11 (July 2014)
* The transmute intrinsic no longer works on type parameters.
* Statics now allow blocks/items in their definition.
* Trait bounds are separated from objects with + instead of : now.
* Mutably borrowed objects can no longer be read while they are
borrowed.
* Objects can no longer be read while they are mutably borrowed.
* The address of a static is now marked as insignificant unless the
#[inline(never)] attribute is placed on a static.
#[inline(never)] attribute is placed it.
* The #[unsafe_destructor] attribute is now behind a feature gate.
* Struct literals are no longer allowed in ambiguous positions such as
if, while, match, and for..in.
@ -48,13 +47,13 @@ Version 0.11 (July 2014)
* Libraries
* The standard library is now a "facade" over a number of underlying
libraries. This means that development on the standard library should
be speeder due to less to compile, as well as a clearer line between
be speeder due to smaller crates, as well as a clearer line between
all dependencies.
* A new library, libcore, lives under the standard library's facade
which is Rust's "0-assumption" library, suitable for embedded and
kernel development for example.
* A regex crate has been added to the standard distribution. This crate
includes statically compiled regulard expressions.
includes statically compiled regular expressions.
* The unwrap/unwrap_err methods on Result require a Show bound for
better error messages.
* The return types of the std::comm primitives have been centralized
@ -72,11 +71,11 @@ Version 0.11 (July 2014)
* A graphviz crate has been added for creating .dot files.
* The std::cast module has been migrated into std::mem.
* The std::local_data api has been migrated from freestanding functions
to based on methods.
to being based on methods.
* The Pod trait has been renamed to Copy.
* jemalloc has been added as the default allocator for types.
* The api for allocating memory in rust has been modified for sized
deallocation as well as using proper alignment.
* The API for allocating memory has been changed to use proper alignment
and sized deallocation
* Connecting a TcpStream or binding a TcpListener is now based on a
string address and a u16 port. This allows connecting to a hostname as
opposed to an IP.
@ -100,11 +99,11 @@ Version 0.11 (July 2014)
discovery of breaking changes.
* The compiler will now try to suggest how to annotate lifetimes if a
lifetime-related error occurs.
* Debug info continues to be improved greatly with better support for
situations such as LTO and general bug fixes.
* Debug info continues to be improved greatly with general bug fixes and
better support for situations like link time optimization (LTO).
* Usage of syntax extensions when cross-compiling has been fixed.
* The equivalent of ffunction-sections and fdata-sections have been
enabled by default with the equivalent of --gc-sections.
* Functionality equivalent to GCC & Clang's -ffunction-sections,
-fdata-sections and --gc-sections has been enabled by default
* The compiler is now stricter about where it will load module files
from when a module is declared via `mod foo;`.
* The #[phase(syntax)] attribute has been renamed to #[phase(plugin)].
@ -128,7 +127,7 @@ Version 0.11 (July 2014)
* Cross-compiling to mipsel is now supported.
* Stability attributes are now inherited by default and no longer apply
to intra-crate usage, only inter-crate usage.
* Error message related to non-exhaustive match statements have been
* Error message related to non-exhaustive match expressions have been
greatly improved.
Version 0.10 (April 2014)

View File

@ -8,17 +8,16 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(non_camel_case_types)]
use middle::const_eval::{compare_const_vals, const_bool, const_float, const_nil, const_val};
use middle::const_eval::{eval_const_expr, lookup_const_by_id};
use middle::def::*;
use middle::pat_util::*;
use middle::ty::*;
use middle::ty;
use std::fmt;
use std::gc::{Gc, GC};
use std::iter;
use std::iter::AdditiveIterator;
use std::iter::range_inclusive;
use syntax::ast::*;
use syntax::ast_util::{is_unguarded, walk_pat};
use syntax::codemap::{Span, Spanned, DUMMY_SP};
@ -28,7 +27,71 @@ use syntax::visit;
use syntax::visit::{Visitor, FnKind};
use util::ppaux::ty_to_str;
type Matrix = Vec<Vec<Gc<Pat>>>;
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| {
row.iter().map(|&pat| pat_to_str(pat)).collect::<Vec<String>>()
}).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(())
}
}
struct MatchCheckCtxt<'a> {
tcx: &'a ty::ctxt
}
#[deriving(Clone, PartialEq)]
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(DefId),
/// Literal values.
ConstantValue(const_val),
/// Ranges of literal values (2..5).
ConstantRange(const_val, const_val),
/// Array patterns of length n.
Slice(uint)
}
#[deriving(Clone)]
enum Usefulness {
@ -50,22 +113,6 @@ impl Usefulness {
}
}
fn def_to_path(tcx: &ty::ctxt, id: DefId) -> Path {
ty::with_path(tcx, id, |mut path| Path {
global: false,
segments: path.last().map(|elem| PathSegment {
identifier: Ident::new(elem.name()),
lifetimes: vec!(),
types: OwnedSlice::empty()
}).move_iter().collect(),
span: DUMMY_SP,
})
}
struct MatchCheckCtxt<'a> {
tcx: &'a ty::ctxt,
}
impl<'a> Visitor<()> for MatchCheckCtxt<'a> {
fn visit_expr(&mut self, ex: &Expr, _: ()) {
check_expr(self, ex);
@ -78,11 +125,8 @@ impl<'a> Visitor<()> for MatchCheckCtxt<'a> {
}
}
pub fn check_crate(tcx: &ty::ctxt,
krate: &Crate) {
let mut cx = MatchCheckCtxt {
tcx: tcx,
};
pub fn check_crate(tcx: &ty::ctxt, krate: &Crate) {
let mut cx = MatchCheckCtxt { tcx: tcx, };
visit::walk_crate(&mut cx, krate, ());
@ -116,12 +160,12 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
// If the type *is* empty, it's vacuously exhaustive
return;
}
let m: Matrix = arms
let m: Matrix = Matrix(arms
.iter()
.filter(|&arm| is_unguarded(arm))
.flat_map(|arm| arm.pats.iter())
.map(|pat| vec!(pat.clone()))
.collect();
.collect());
check_exhaustive(cx, ex.span, &m);
},
_ => ()
@ -130,7 +174,7 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
// Check for unreachable patterns
fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) {
let mut seen = Vec::new();
let mut seen = Matrix(vec!());
for arm in arms.iter() {
for pat in arm.pats.iter() {
// Check that we do not match against a static NaN (#6804)
@ -161,7 +205,11 @@ fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) {
NotUseful => cx.tcx.sess.span_err(pat.span, "unreachable pattern"),
_ => ()
}
if arm.guard.is_none() { seen.push(v); }
if arm.guard.is_none() {
let Matrix(mut rows) = seen;
rows.push(v);
seen = Matrix(rows);
}
}
}
}
@ -175,10 +223,6 @@ fn raw_pat(p: Gc<Pat>) -> Gc<Pat> {
fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, m: &Matrix) {
match is_useful(cx, m, [wild()], ConstructWitness) {
NotUseful => {
// This is good, wildcard pattern isn't reachable
return;
}
Useful(pats) => {
let witness = match pats.as_slice() {
[witness] => witness,
@ -188,38 +232,58 @@ fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, m: &Matrix) {
let msg = format!("non-exhaustive patterns: `{0}` not covered", pat_to_str(&*witness));
cx.tcx.sess.span_err(sp, msg.as_slice());
}
NotUseful => {
// This is good, wildcard pattern isn't reachable
}
}
}
#[deriving(Clone, PartialEq)]
enum ctor {
single,
variant(DefId),
val(const_val),
range(const_val, const_val),
vec(uint)
}
fn const_val_to_expr(value: &const_val) -> Gc<Expr> {
let node = match value {
&const_bool(b) => LitBool(b),
&const_nil => LitNil,
_ => unreachable!()
};
box(GC) Expr {
box (GC) Expr {
id: 0,
node: ExprLit(box(GC) Spanned { node: node, span: DUMMY_SP }),
span: DUMMY_SP
}
}
fn construct_witness(cx: &MatchCheckCtxt, ctor: &ctor, pats: Vec<Gc<Pat>>, lty: ty::t) -> Gc<Pat> {
let pat = match ty::get(lty).sty {
fn def_to_path(tcx: &ty::ctxt, id: DefId) -> Path {
ty::with_path(tcx, id, |mut path| Path {
global: false,
segments: path.last().map(|elem| PathSegment {
identifier: Ident::new(elem.name()),
lifetimes: vec!(),
types: OwnedSlice::empty()
}).move_iter().collect(),
span: DUMMY_SP,
})
}
/// 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 {
ty::ty_tup(_) => PatTup(pats),
ty::ty_enum(cid, _) | ty::ty_struct(cid, _) => {
let (vid, is_structure) = match ctor {
&variant(vid) => (vid,
&Variant(vid) => (vid,
ty::enum_variant_with_id(cx.tcx, cid, vid).arg_names.is_some()),
_ => (cid, true)
};
@ -235,103 +299,95 @@ fn construct_witness(cx: &MatchCheckCtxt, ctor: &ctor, pats: Vec<Gc<Pat>>, lty:
} else {
PatEnum(def_to_path(cx.tcx, vid), Some(pats))
}
},
}
ty::ty_rptr(_, ty::mt { ty: ty, .. }) => {
match ty::get(ty).sty {
ty::ty_vec(_, Some(n)) => match ctor {
&Single => {
assert_eq!(pats.len(), n);
PatVec(pats, None, vec!())
},
_ => unreachable!()
},
ty::ty_vec(_, None) => match ctor {
&vec(_) => PatVec(pats, None, vec!()),
&Slice(n) => {
assert_eq!(pats.len(), n);
PatVec(pats, None, vec!())
},
_ => unreachable!()
},
ty::ty_str => PatWild,
_ => {
assert_eq!(pats.len(), 1);
PatRegion(pats.get(0).clone())
}
}
},
}
ty::ty_box(_) => {
assert_eq!(pats.len(), 1);
PatBox(pats.get(0).clone())
},
}
ty::ty_vec(_, Some(len)) => {
assert_eq!(pats.len(), len);
PatVec(pats, None, vec!())
}
_ => {
match ctor {
&vec(_) => PatVec(pats, None, vec!()),
&val(ref v) => PatLit(const_val_to_expr(v)),
match *ctor {
ConstantValue(ref v) => PatLit(const_val_to_expr(v)),
_ => PatWild
}
}
};
box(GC) Pat {
box (GC) Pat {
id: 0,
node: pat,
span: DUMMY_SP
}
}
fn missing_constructor(cx: &MatchCheckCtxt, m: &Matrix, left_ty: ty::t) -> Option<ctor> {
let used_constructors: Vec<ctor> = m.iter()
.filter_map(|r| pat_ctor_id(cx, left_ty, *r.get(0)))
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())
.collect();
all_constructors(cx, m, left_ty)
all_constructors(cx, left_ty, max_slice_length)
.move_iter()
.find(|c| !used_constructors.contains(c))
}
fn all_constructors(cx: &MatchCheckCtxt, m: &Matrix, left_ty: ty::t) -> Vec<ctor> {
// This produces a list of all vector constructors that we would expect to appear
// in an exhaustive set of patterns. Because such a list would normally be infinite,
// we narrow it down to only those constructors that actually appear in the inspected
// column, plus, any that are missing and not covered by a pattern with a destructured slice.
fn vec_constructors(m: &Matrix) -> Vec<ctor> {
let max_vec_len = m.iter().map(|r| match r.get(0).node {
PatVec(ref before, _, ref after) => before.len() + after.len(),
_ => 0u
}).max().unwrap_or(0u);
let min_vec_len_with_slice = m.iter().map(|r| match r.get(0).node {
PatVec(ref before, Some(_), ref after) => before.len() + after.len(),
_ => max_vec_len + 1
}).min().unwrap_or(max_vec_len + 1);
let other_lengths = m.iter().map(|r| match r.get(0).node {
PatVec(ref before, _, ref after) => before.len() + after.len(),
_ => 0u
}).filter(|&len| len > min_vec_len_with_slice);
iter::range_inclusive(0u, min_vec_len_with_slice)
.chain(other_lengths)
.map(|len| vec(len))
.collect()
}
/// 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> {
match ty::get(left_ty).sty {
ty::ty_bool =>
[true, false].iter().map(|b| val(const_bool(*b))).collect(),
[true, false].iter().map(|b| ConstantValue(const_bool(*b))).collect(),
ty::ty_nil =>
vec!(val(const_nil)),
vec!(ConstantValue(const_nil)),
ty::ty_rptr(_, ty::mt { ty: ty, .. }) => match ty::get(ty).sty {
ty::ty_vec(_, None) => vec_constructors(m),
_ => vec!(single)
ty::ty_vec(_, None) =>
range_inclusive(0, max_slice_length).map(|length| Slice(length)).collect(),
_ => vec!(Single)
},
ty::ty_enum(eid, _) =>
ty::enum_variants(cx.tcx, eid)
.iter()
.map(|va| variant(va.id))
.map(|va| Variant(va.id))
.collect(),
ty::ty_vec(_, None) =>
vec_constructors(m),
ty::ty_vec(_, Some(n)) =>
vec!(vec(n)),
_ =>
vec!(single)
vec!(Single)
}
}
@ -348,15 +404,16 @@ fn all_constructors(cx: &MatchCheckCtxt, m: &Matrix, left_ty: ty::t) -> Vec<ctor
// 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, m: &Matrix, v: &[Gc<Pat>],
witness: WitnessPreference) -> Usefulness {
if m.len() == 0u {
fn is_useful(cx: &MatchCheckCtxt, m @ &Matrix(ref rows): &Matrix,
v: &[Gc<Pat>], witness: WitnessPreference) -> Usefulness {
debug!("{:}", m);
if rows.len() == 0u {
return Useful(vec!());
}
if m.get(0).len() == 0u {
if rows.get(0).len() == 0u {
return NotUseful;
}
let real_pat = match m.iter().find(|r| r.get(0).id != 0) {
let real_pat = match rows.iter().find(|r| r.get(0).id != 0) {
Some(r) => {
match r.get(0).node {
// An arm of the form `ref x @ sub_pat` has type
@ -374,10 +431,16 @@ fn is_useful(cx: &MatchCheckCtxt, m: &Matrix, v: &[Gc<Pat>],
ty::pat_ty(cx.tcx, &*real_pat)
};
match pat_ctor_id(cx, left_ty, v[0]) {
None => match missing_constructor(cx, m, left_ty) {
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() {
match missing_constructor(cx, m, left_ty, max_slice_length) {
None => {
all_constructors(cx, m, left_ty).move_iter().filter_map(|c| {
all_constructors(cx, left_ty, max_slice_length).move_iter().filter_map(|c| {
is_useful_specialized(cx, m, v, c.clone(),
left_ty, witness).useful().map(|pats| {
Useful(match witness {
@ -400,14 +463,15 @@ fn is_useful(cx: &MatchCheckCtxt, m: &Matrix, v: &[Gc<Pat>],
}).nth(0).unwrap_or(NotUseful)
},
Some(ctor) => {
let matrix = m.iter().filter_map(|r| default(cx, r.as_slice())).collect();
Some(constructor) => {
let matrix = Matrix(rows.iter().filter_map(|r|
default(cx, r.as_slice())).collect());
match is_useful(cx, &matrix, v.tail(), witness) {
Useful(pats) => Useful(match witness {
ConstructWitness => {
let arity = constructor_arity(cx, &ctor, left_ty);
let arity = constructor_arity(cx, &constructor, left_ty);
let wild_pats = Vec::from_elem(arity, wild());
let enum_pat = construct_witness(cx, &ctor, wild_pats, left_ty);
let enum_pat = construct_witness(cx, &constructor, wild_pats, left_ty);
(vec!(enum_pat)).append(pats.as_slice())
}
LeaveOutWitness => vec!()
@ -415,64 +479,82 @@ fn is_useful(cx: &MatchCheckCtxt, m: &Matrix, v: &[Gc<Pat>],
result => result
}
}
},
Some(v0_ctor) => is_useful_specialized(cx, m, v, v0_ctor, left_ty, witness)
}
} else {
constructors.move_iter().filter_map(|c| {
is_useful_specialized(cx, m, v, c.clone(), left_ty, witness)
.useful().map(|pats| Useful(pats))
}).nth(0).unwrap_or(NotUseful)
}
}
fn is_useful_specialized(cx: &MatchCheckCtxt, m: &Matrix, v: &[Gc<Pat>],
ctor: ctor, lty: ty::t, witness: WitnessPreference) -> Usefulness {
fn is_useful_specialized(cx: &MatchCheckCtxt, &Matrix(ref m): &Matrix, v: &[Gc<Pat>],
ctor: Constructor, lty: ty::t, witness: WitnessPreference) -> Usefulness {
let arity = constructor_arity(cx, &ctor, lty);
let matrix = m.iter().filter_map(|r| {
let matrix = Matrix(m.iter().filter_map(|r| {
specialize(cx, r.as_slice(), &ctor, arity)
}).collect();
}).collect());
match specialize(cx, v, &ctor, arity) {
Some(v) => is_useful(cx, &matrix, v.as_slice(), witness),
None => NotUseful
}
}
fn pat_ctor_id(cx: &MatchCheckCtxt, left_ty: ty::t, p: Gc<Pat>) -> Option<ctor> {
/// 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> {
let pat = raw_pat(p);
match pat.node {
PatIdent(..) =>
match cx.tcx.def_map.borrow().find(&pat.id) {
Some(&DefStatic(did, false)) => {
let const_expr = lookup_const_by_id(cx.tcx, did).unwrap();
Some(val(eval_const_expr(cx.tcx, &*const_expr)))
vec!(ConstantValue(eval_const_expr(cx.tcx, &*const_expr)))
},
Some(&DefVariant(_, id, _)) => Some(variant(id)),
_ => None
Some(&DefVariant(_, id, _)) => vec!(Variant(id)),
_ => vec!()
},
PatEnum(..) =>
match cx.tcx.def_map.borrow().find(&pat.id) {
Some(&DefStatic(did, false)) => {
let const_expr = lookup_const_by_id(cx.tcx, did).unwrap();
Some(val(eval_const_expr(cx.tcx, &*const_expr)))
vec!(ConstantValue(eval_const_expr(cx.tcx, &*const_expr)))
},
Some(&DefVariant(_, id, _)) => Some(variant(id)),
_ => Some(single)
Some(&DefVariant(_, id, _)) => vec!(Variant(id)),
_ => vec!(Single)
},
PatStruct(..) =>
match cx.tcx.def_map.borrow().find(&pat.id) {
Some(&DefVariant(_, id, _)) => Some(variant(id)),
_ => Some(single)
Some(&DefVariant(_, id, _)) => vec!(Variant(id)),
_ => vec!(Single)
},
PatLit(expr) =>
Some(val(eval_const_expr(cx.tcx, &*expr))),
vec!(ConstantValue(eval_const_expr(cx.tcx, &*expr))),
PatRange(lo, hi) =>
Some(range(eval_const_expr(cx.tcx, &*lo), eval_const_expr(cx.tcx, &*hi))),
PatVec(ref before, _, ref after) => match ty::get(left_ty).sty {
ty::ty_vec(_, Some(n)) =>
Some(vec(n)),
_ =>
Some(vec(before.len() + after.len()))
},
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()))
}
},
PatBox(_) | PatTup(_) | PatRegion(..) =>
Some(single),
vec!(Single),
PatWild | PatWildMulti =>
None,
vec!(),
PatMac(_) =>
cx.tcx.sess.bug("unexpanded macro")
}
@ -482,53 +564,53 @@ fn is_wild(cx: &MatchCheckCtxt, p: Gc<Pat>) -> bool {
let pat = raw_pat(p);
match pat.node {
PatWild | PatWildMulti => true,
PatIdent(_, _, _) => {
PatIdent(_, _, _) =>
match cx.tcx.def_map.borrow().find(&pat.id) {
Some(&DefVariant(_, _, _)) | Some(&DefStatic(..)) => false,
_ => true
}
}
},
PatVec(ref before, Some(_), ref after) =>
before.is_empty() && after.is_empty(),
_ => false
}
}
fn constructor_arity(cx: &MatchCheckCtxt, ctor: &ctor, ty: ty::t) -> uint {
/// 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.
fn constructor_arity(cx: &MatchCheckCtxt, ctor: &Constructor, ty: ty::t) -> uint {
match ty::get(ty).sty {
ty::ty_tup(ref fs) => fs.len(),
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 {
vec(n) => n,
_ => 0u
Slice(length) => length,
_ => unreachable!()
},
ty::ty_str => 0u,
_ => 1u
},
ty::ty_enum(eid, _) => {
match *ctor {
variant(id) => enum_variant_with_id(cx.tcx, eid, id).args.len(),
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(_, _) => match *ctor {
vec(n) => n,
_ => 0u
},
ty::ty_vec(_, Some(n)) => n,
_ => 0u
}
}
fn wild() -> Gc<Pat> {
box(GC) Pat {id: 0, node: PatWild, span: DUMMY_SP}
}
fn range_covered_by_constructor(ctor_id: &ctor, from: &const_val, to: &const_val) -> Option<bool> {
let (c_from, c_to) = match *ctor_id {
val(ref value) => (value, value),
range(ref from, ref to) => (from, to),
single => return Some(true),
_ => unreachable!()
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!()
};
let cmp_from = compare_const_vals(c_from, from);
let cmp_to = compare_const_vals(c_to, to);
@ -538,22 +620,30 @@ fn range_covered_by_constructor(ctor_id: &ctor, from: &const_val, to: &const_val
}
}
/// 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.
fn specialize(cx: &MatchCheckCtxt, r: &[Gc<Pat>],
ctor_id: &ctor, arity: uint) -> Option<Vec<Gc<Pat>>> {
constructor: &Constructor, arity: uint) -> Option<Vec<Gc<Pat>>> {
let &Pat {
id: ref pat_id, node: ref n, span: ref pat_span
id: pat_id, node: ref node, span: pat_span
} = &(*raw_pat(r[0]));
let head: Option<Vec<Gc<Pat>>> = match n {
&PatWild => {
Some(Vec::from_elem(arity, wild()))
}
&PatWildMulti => {
Some(Vec::from_elem(arity, wild()))
}
let head: Option<Vec<Gc<Pat>>> = match node {
&PatWild =>
Some(Vec::from_elem(arity, wild())),
&PatWildMulti =>
Some(Vec::from_elem(arity, wild())),
&PatIdent(_, _, _) => {
let opt_def = cx.tcx.def_map.borrow().find_copy(pat_id);
let opt_def = cx.tcx.def_map.borrow().find_copy(&pat_id);
match opt_def {
Some(DefVariant(_, id, _)) => if *ctor_id == variant(id) {
Some(DefVariant(_, id, _)) => if *constructor == Variant(id) {
Some(vec!())
} else {
None
@ -561,11 +651,11 @@ fn specialize(cx: &MatchCheckCtxt, r: &[Gc<Pat>],
Some(DefStatic(did, _)) => {
let const_expr = lookup_const_by_id(cx.tcx, did).unwrap();
let e_v = eval_const_expr(cx.tcx, &*const_expr);
match range_covered_by_constructor(ctor_id, &e_v, &e_v) {
match range_covered_by_constructor(constructor, &e_v, &e_v) {
Some(true) => Some(vec!()),
Some(false) => None,
None => {
cx.tcx.sess.span_err(*pat_span, "mismatched types between arms");
cx.tcx.sess.span_err(pat_span, "mismatched types between arms");
None
}
}
@ -575,22 +665,23 @@ fn specialize(cx: &MatchCheckCtxt, r: &[Gc<Pat>],
}
}
}
&PatEnum(_, ref args) => {
let def = cx.tcx.def_map.borrow().get_copy(pat_id);
let def = cx.tcx.def_map.borrow().get_copy(&pat_id);
match def {
DefStatic(did, _) => {
let const_expr = lookup_const_by_id(cx.tcx, did).unwrap();
let e_v = eval_const_expr(cx.tcx, &*const_expr);
match range_covered_by_constructor(ctor_id, &e_v, &e_v) {
match range_covered_by_constructor(constructor, &e_v, &e_v) {
Some(true) => Some(vec!()),
Some(false) => None,
None => {
cx.tcx.sess.span_err(*pat_span, "mismatched types between arms");
cx.tcx.sess.span_err(pat_span, "mismatched types between arms");
None
}
}
}
DefVariant(_, id, _) if *ctor_id != variant(id) => None,
DefVariant(_, id, _) if *constructor != Variant(id) => None,
DefVariant(..) | DefFn(..) | DefStruct(..) => {
Some(match args {
&Some(ref args) => args.clone(),
@ -603,9 +694,9 @@ fn specialize(cx: &MatchCheckCtxt, r: &[Gc<Pat>],
&PatStruct(_, ref pattern_fields, _) => {
// Is this a struct or an enum variant?
let def = cx.tcx.def_map.borrow().get_copy(pat_id);
let def = cx.tcx.def_map.borrow().get_copy(&pat_id);
let class_id = match def {
DefVariant(_, variant_id, _) => if *ctor_id == variant(variant_id) {
DefVariant(_, variant_id, _) => if *constructor == Variant(variant_id) {
Some(variant_id)
} else {
None
@ -633,11 +724,11 @@ fn specialize(cx: &MatchCheckCtxt, r: &[Gc<Pat>],
&PatLit(ref expr) => {
let expr_value = eval_const_expr(cx.tcx, &**expr);
match range_covered_by_constructor(ctor_id, &expr_value, &expr_value) {
match range_covered_by_constructor(constructor, &expr_value, &expr_value) {
Some(true) => Some(vec!()),
Some(false) => None,
None => {
cx.tcx.sess.span_err(*pat_span, "mismatched types between arms");
cx.tcx.sess.span_err(pat_span, "mismatched types between arms");
None
}
}
@ -646,41 +737,42 @@ fn specialize(cx: &MatchCheckCtxt, r: &[Gc<Pat>],
&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(ctor_id, &from_value, &to_value) {
match range_covered_by_constructor(constructor, &from_value, &to_value) {
Some(true) => Some(vec!()),
Some(false) => None,
None => {
cx.tcx.sess.span_err(*pat_span, "mismatched types between arms");
cx.tcx.sess.span_err(pat_span, "mismatched types between arms");
None
}
}
}
&PatVec(ref before, ref slice, ref after) => {
match *ctor_id {
vec(_) => {
let num_elements = before.len() + after.len();
if num_elements < arity && slice.is_some() {
let mut result = Vec::new();
result.push_all(before.as_slice());
result.grow_fn(arity - num_elements, |_| wild());
result.push_all(after.as_slice());
Some(result)
} else if num_elements == arity {
let mut result = Vec::new();
result.push_all(before.as_slice());
result.push_all(after.as_slice());
Some(result)
} else {
None
}
}
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)
},
_ => None
}
}
&PatMac(_) => {
cx.tcx.sess.span_err(*pat_span, "unexpanded macro");
cx.tcx.sess.span_err(pat_span, "unexpanded macro");
None
}
};
@ -740,7 +832,7 @@ fn check_fn(cx: &mut MatchCheckCtxt,
}
fn is_refutable(cx: &MatchCheckCtxt, pat: Gc<Pat>) -> Option<Gc<Pat>> {
let pats = vec!(vec!(pat));
let pats = Matrix(vec!(vec!(pat)));
is_useful(cx, &pats, [wild()], ConstructWitness)
.useful()
.map(|pats| {

View File

@ -19,13 +19,14 @@ use middle::def;
use middle::freevars;
use middle::pat_util;
use middle::ty;
use middle::typeck::MethodCall;
use middle::typeck::{MethodCall, MethodObject, MethodOrigin, MethodParam};
use middle::typeck::{MethodStatic};
use middle::typeck;
use syntax::ast;
use syntax::codemap::{Span};
use util::ppaux::Repr;
use std::gc::Gc;
use syntax::ast;
use syntax::codemap::Span;
///////////////////////////////////////////////////////////////////////////
// The Delegate trait
@ -101,6 +102,74 @@ pub enum MutateMode {
WriteAndRead, // x += y
}
enum OverloadedCallType {
FnOverloadedCall,
FnMutOverloadedCall,
FnOnceOverloadedCall,
}
impl OverloadedCallType {
fn from_trait_id(tcx: &ty::ctxt, trait_id: ast::DefId)
-> OverloadedCallType {
for &(maybe_function_trait, overloaded_call_type) in [
(tcx.lang_items.fn_once_trait(), FnOnceOverloadedCall),
(tcx.lang_items.fn_mut_trait(), FnMutOverloadedCall),
(tcx.lang_items.fn_trait(), FnOverloadedCall)
].iter() {
match maybe_function_trait {
Some(function_trait) if function_trait == trait_id => {
return overloaded_call_type
}
_ => continue,
}
}
tcx.sess.bug("overloaded call didn't map to known function trait")
}
fn from_method_id(tcx: &ty::ctxt, method_id: ast::DefId)
-> OverloadedCallType {
let method_descriptor =
match tcx.methods.borrow_mut().find(&method_id) {
None => {
tcx.sess.bug("overloaded call method wasn't in method \
map")
}
Some(ref method_descriptor) => (*method_descriptor).clone(),
};
let impl_id = match method_descriptor.container {
ty::TraitContainer(_) => {
tcx.sess.bug("statically resolved overloaded call method \
belonged to a trait?!")
}
ty::ImplContainer(impl_id) => impl_id,
};
let trait_ref = match ty::impl_trait_ref(tcx, impl_id) {
None => {
tcx.sess.bug("statically resolved overloaded call impl \
didn't implement a trait?!")
}
Some(ref trait_ref) => (*trait_ref).clone(),
};
OverloadedCallType::from_trait_id(tcx, trait_ref.def_id)
}
fn from_method_origin(tcx: &ty::ctxt, origin: &MethodOrigin)
-> OverloadedCallType {
match *origin {
MethodStatic(def_id) => {
OverloadedCallType::from_method_id(tcx, def_id)
}
MethodParam(ref method_param) => {
OverloadedCallType::from_trait_id(tcx, method_param.trait_id)
}
MethodObject(ref method_object) => {
OverloadedCallType::from_trait_id(tcx, method_object.trait_id)
}
}
}
}
///////////////////////////////////////////////////////////////////////////
// The ExprUseVisitor type
//
@ -413,19 +482,37 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> {
}
}
_ => {
match self.tcx()
.method_map
.borrow()
.find(&MethodCall::expr(call.id)) {
Some(_) => {
// FIXME(#14774, pcwalton): Implement this.
let overloaded_call_type =
match self.tcx()
.method_map
.borrow()
.find(&MethodCall::expr(call.id)) {
Some(ref method_callee) => {
OverloadedCallType::from_method_origin(
self.tcx(),
&method_callee.origin)
}
None => {
self.tcx().sess.span_bug(
callee.span,
format!("unexpected callee type {}",
callee_ty.repr(self.tcx())).as_slice());
callee_ty.repr(self.tcx())).as_slice())
}
};
match overloaded_call_type {
FnMutOverloadedCall => {
self.borrow_expr(callee,
ty::ReScope(call.id),
ty::MutBorrow,
ClosureInvocation);
}
FnOverloadedCall => {
self.borrow_expr(callee,
ty::ReScope(call.id),
ty::ImmBorrow,
ClosureInvocation);
}
FnOnceOverloadedCall => self.consume_expr(callee),
}
}
}

View File

@ -12,9 +12,10 @@ use middle::def::*;
use middle::resolve;
use std::collections::HashMap;
use std::gc::{Gc, GC};
use syntax::ast::*;
use syntax::ast_util::{path_to_ident, walk_pat};
use syntax::codemap::Span;
use syntax::codemap::{Span, DUMMY_SP};
pub type PatIdMap = HashMap<Ident, NodeId>;
@ -111,3 +112,7 @@ pub fn simple_identifier<'a>(pat: &'a Pat) -> Option<&'a Path> {
}
}
}
pub fn wild() -> Gc<Pat> {
box (GC) Pat { id: 0, node: PatWild, span: DUMMY_SP }
}

View File

@ -0,0 +1,74 @@
// Copyright 2012 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.
#![feature(overloaded_calls)]
use std::ops::{Fn, FnMut, FnOnce};
struct SFn {
x: int,
y: int,
}
impl Fn<(int,),int> for SFn {
fn call(&self, (z,): (int,)) -> int {
self.x * self.y * z
}
}
struct SFnMut {
x: int,
y: int,
}
impl FnMut<(int,),int> for SFnMut {
fn call_mut(&mut self, (z,): (int,)) -> int {
self.x * self.y * z
}
}
struct SFnOnce {
x: String,
}
impl FnOnce<(String,),uint> for SFnOnce {
fn call_once(self, (z,): (String,)) -> uint {
self.x.len() + z.len()
}
}
fn f() {
let mut s = SFn {
x: 1,
y: 2,
};
let sp = &mut s;
s(3); //~ ERROR cannot borrow `s` as immutable because it is also borrowed as mutable
//~^ ERROR cannot borrow `s` as immutable because it is also borrowed as mutable
}
fn g() {
let s = SFnMut {
x: 1,
y: 2,
};
s(3); //~ ERROR cannot borrow immutable local variable `s` as mutable
}
fn h() {
let s = SFnOnce {
x: "hello".to_string(),
};
s(" world".to_string());
s(" world".to_string()); //~ ERROR use of moved value: `s`
}
fn main() {}

View File

@ -1,4 +1,4 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// 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.
//
@ -11,10 +11,19 @@
enum t { a(u), b }
enum u { c, d }
fn main() {
let x = a(c);
match x { //~ ERROR non-exhaustive patterns: `a(c)` not covered
a(d) => { fail!("hello"); }
b => { fail!("goodbye"); }
fn match_nested_vecs<'a, T>(l1: Option<&'a [T]>, l2: Result<&'a [T], ()>) -> &'static str {
match (l1, l2) { //~ ERROR non-exhaustive patterns: `(Some([]), Err(_))` not covered
(Some([]), Ok([])) => "Some(empty), Ok(empty)",
(Some([_, ..]), Ok(_)) | (Some([_, ..]), Err(())) => "Some(non-empty), any",
(None, Ok([])) | (None, Err(())) | (None, Ok([_])) => "None, Ok(less than one element)",
(None, Ok([_, _, ..])) => "None, Ok(at least two elements)"
}
}
fn main() {
let x = a(c);
match x { //~ ERROR non-exhaustive patterns: `a(c)` not covered
a(d) => { fail!("hello"); }
b => { fail!("goodbye"); }
}
}

View File

@ -0,0 +1,21 @@
// Copyright 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.
fn main() {
assert_eq!(count_members(&[1, 2, 3, 4]), 4);
}
fn count_members(v: &[uint]) -> uint {
match v {
[] => 0,
[_] => 1,
[_x, ..xs] => 1 + count_members(xs)
}
}

View File

@ -0,0 +1,82 @@
// Copyright 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.
fn match_vecs<'a, T>(l1: &'a [T], l2: &'a [T]) -> &'static str {
match (l1, l2) {
([], []) => "both empty",
([], [..]) | ([..], []) => "one empty",
([..], [..]) => "both non-empty"
}
}
fn match_vecs_cons<'a, T>(l1: &'a [T], l2: &'a [T]) -> &'static str {
match (l1, l2) {
([], []) => "both empty",
([], [_, ..]) | ([_, ..], []) => "one empty",
([_, ..], [_, ..]) => "both non-empty"
}
}
fn match_vecs_snoc<'a, T>(l1: &'a [T], l2: &'a [T]) -> &'static str {
match (l1, l2) {
([], []) => "both empty",
([], [.., _]) | ([.., _], []) => "one empty",
([.., _], [.., _]) => "both non-empty"
}
}
fn match_nested_vecs_cons<'a, T>(l1: Option<&'a [T]>, l2: Result<&'a [T], ()>) -> &'static str {
match (l1, l2) {
(Some([]), Ok([])) => "Some(empty), Ok(empty)",
(Some([_, ..]), Ok(_)) | (Some([_, ..]), Err(())) => "Some(non-empty), any",
(None, Ok([])) | (None, Err(())) | (None, Ok([_])) => "None, Ok(less than one element)",
(None, Ok([_, _, ..])) => "None, Ok(at least two elements)",
_ => "other"
}
}
fn match_nested_vecs_snoc<'a, T>(l1: Option<&'a [T]>, l2: Result<&'a [T], ()>) -> &'static str {
match (l1, l2) {
(Some([]), Ok([])) => "Some(empty), Ok(empty)",
(Some([.., _]), Ok(_)) | (Some([.., _]), Err(())) => "Some(non-empty), any",
(None, Ok([])) | (None, Err(())) | (None, Ok([_])) => "None, Ok(less than one element)",
(None, Ok([.., _, _])) => "None, Ok(at least two elements)",
_ => "other"
}
}
fn main() {
assert_eq!(match_vecs(&[1i, 2], &[2i, 3]), "both non-empty");
assert_eq!(match_vecs(&[], &[1i, 2, 3, 4]), "one empty");
assert_eq!(match_vecs::<uint>(&[], &[]), "both empty");
assert_eq!(match_vecs(&[1i, 2, 3], &[]), "one empty");
assert_eq!(match_vecs_cons(&[1i, 2], &[2i, 3]), "both non-empty");
assert_eq!(match_vecs_cons(&[], &[1i, 2, 3, 4]), "one empty");
assert_eq!(match_vecs_cons::<uint>(&[], &[]), "both empty");
assert_eq!(match_vecs_cons(&[1i, 2, 3], &[]), "one empty");
assert_eq!(match_vecs_snoc(&[1i, 2], &[2i, 3]), "both non-empty");
assert_eq!(match_vecs_snoc(&[], &[1i, 2, 3, 4]), "one empty");
assert_eq!(match_vecs_snoc::<uint>(&[], &[]), "both empty");
assert_eq!(match_vecs_snoc(&[1i, 2, 3], &[]), "one empty");
assert_eq!(match_nested_vecs_cons(None, Ok(&[4u, 2u])), "None, Ok(at least two elements)");
assert_eq!(match_nested_vecs_cons::<uint>(None, Err(())), "None, Ok(less than one element)");
assert_eq!(match_nested_vecs_cons::<bool>(Some(&[]), Ok(&[])), "Some(empty), Ok(empty)");
assert_eq!(match_nested_vecs_cons(Some(&[1i]), Err(())), "Some(non-empty), any");
assert_eq!(match_nested_vecs_cons(Some(&[(42i, ())]), Ok(&[(1i, ())])), "Some(non-empty), any");
assert_eq!(match_nested_vecs_snoc(None, Ok(&[4u, 2u])), "None, Ok(at least two elements)");
assert_eq!(match_nested_vecs_snoc::<uint>(None, Err(())), "None, Ok(less than one element)");
assert_eq!(match_nested_vecs_snoc::<bool>(Some(&[]), Ok(&[])), "Some(empty), Ok(empty)");
assert_eq!(match_nested_vecs_snoc(Some(&[1i]), Err(())), "Some(non-empty), any");
assert_eq!(match_nested_vecs_snoc(Some(&[(42i, ())]), Ok(&[(1i, ())])), "Some(non-empty), any");
}