From abae5e7e258f256cbdb4fa2a58f58ecb095ba626 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sat, 24 Sep 2016 18:24:34 +0300 Subject: [PATCH] split the exhaustiveness-checking logic to its own module `check_match` is now left with its grab bag of random checks. --- src/librustc_const_eval/_match.rs | 696 ++++++++++++++++++++++++ src/librustc_const_eval/check_match.rs | 707 +------------------------ src/librustc_const_eval/diagnostics.rs | 4 +- src/librustc_const_eval/lib.rs | 1 + 4 files changed, 726 insertions(+), 682 deletions(-) create mode 100644 src/librustc_const_eval/_match.rs diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs new file mode 100644 index 00000000000..dd894de96ee --- /dev/null +++ b/src/librustc_const_eval/_match.rs @@ -0,0 +1,696 @@ +// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use self::Constructor::*; +use self::Usefulness::*; +use self::WitnessPreference::*; + +use rustc::middle::const_val::ConstVal; +use eval::{eval_const_expr, compare_const_vals}; + +use rustc::hir::def::*; +use rustc::hir::def_id::{DefId}; +use rustc::hir::pat_util::def_to_path; +use rustc::ty::{self, Ty, TyCtxt}; + +use std::cmp::Ordering; +use std::fmt; +use std::iter::{FromIterator, IntoIterator, repeat}; + +use rustc::hir; +use rustc::hir::{Pat, PatKind}; +use rustc::hir::print::pat_to_string; +use rustc::util::common::ErrorReported; + +use syntax::ast::{self, DUMMY_NODE_ID}; +use syntax::codemap::Spanned; +use syntax::ptr::P; +use syntax_pos::{Span, DUMMY_SP}; + +pub const DUMMY_WILD_PAT: &'static Pat = &Pat { + id: DUMMY_NODE_ID, + node: PatKind::Wild, + span: DUMMY_SP +}; + +pub const DUMMY_WILD_PATTERN : Pattern<'static, 'static> = Pattern { + pat: DUMMY_WILD_PAT, + pattern_ty: None +}; + +#[derive(Copy, Clone)] +pub struct Pattern<'a, 'tcx> { + pat: &'a Pat, + pattern_ty: Option> +} + +impl<'a, 'tcx> Pattern<'a, 'tcx> { + fn as_raw(self) -> &'a Pat { + let mut pat = self.pat; + + while let PatKind::Binding(.., Some(ref s)) = pat.node { + pat = s; + } + + return pat; + } + + pub fn span(self) -> Span { + self.pat.span + } +} + +impl<'a, 'tcx> fmt::Debug for Pattern<'a, 'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}: {:?}", pat_to_string(self.pat), self.pattern_ty) + } +} + +pub struct Matrix<'a, 'tcx>(Vec>>); + +impl<'a, 'tcx> Matrix<'a, 'tcx> { + pub fn empty() -> Self { + Matrix(vec![]) + } + + pub fn push(&mut self, row: Vec>) { + self.0.push(row) + } +} + +/// Pretty-printer for matrices of patterns, example: +/// ++++++++++++++++++++++++++ +/// + _ + [] + +/// ++++++++++++++++++++++++++ +/// + true + [First] + +/// ++++++++++++++++++++++++++ +/// + true + [Second(true)] + +/// ++++++++++++++++++++++++++ +/// + false + [_] + +/// ++++++++++++++++++++++++++ +/// + _ + [_, _, ..tail] + +/// ++++++++++++++++++++++++++ +impl<'a, 'tcx> fmt::Debug for Matrix<'a, 'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "\n")?; + + let &Matrix(ref m) = self; + let pretty_printed_matrix: Vec> = m.iter().map(|row| { + row.iter().map(|pat| format!("{:?}", pat)).collect() + }).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 = (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; + let br = repeat('+').take(total_width).collect::(); + write!(f, "{}\n", br)?; + for row in pretty_printed_matrix { + write!(f, "+")?; + for (column, pat_str) in row.into_iter().enumerate() { + write!(f, " ")?; + write!(f, "{:1$}", pat_str, column_widths[column])?; + write!(f, " +")?; + } + write!(f, "\n")?; + write!(f, "{}\n", br)?; + } + Ok(()) + } +} + +impl<'a, 'tcx> FromIterator>> for Matrix<'a, 'tcx> { + fn from_iter>>>(iter: T) -> Self + { + Matrix(iter.into_iter().collect()) + } +} + +//NOTE: appears to be the only place other then InferCtxt to contain a ParamEnv +pub struct MatchCheckCtxt<'a, 'tcx: 'a> { + pub tcx: TyCtxt<'a, 'tcx, 'tcx>, + pub param_env: ty::ParameterEnvironment<'tcx>, +} + +#[derive(Clone, Debug, 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(DefId), + /// Literal values. + ConstantValue(ConstVal), + /// Ranges of literal values (2..5). + ConstantRange(ConstVal, ConstVal), + /// Array patterns of length n. + Slice(usize), +} + +impl Constructor { + fn variant_for_adt<'tcx, 'container, 'a>(&self, + adt: &'a ty::AdtDefData<'tcx, 'container>) + -> &'a ty::VariantDefData<'tcx, 'container> { + match self { + &Variant(vid) => adt.variant_with_id(vid), + _ => adt.struct_variant() + } + } +} + +#[derive(Clone, PartialEq)] +pub enum Usefulness { + Useful, + UsefulWithWitness(Vec>), + NotUseful +} + +#[derive(Copy, Clone)] +pub enum WitnessPreference { + ConstructWitness, + LeaveOutWitness +} + +fn const_val_to_expr(value: &ConstVal) -> P { + let node = match value { + &ConstVal::Bool(b) => ast::LitKind::Bool(b), + _ => bug!() + }; + P(hir::Expr { + id: DUMMY_NODE_ID, + node: hir::ExprLit(P(Spanned { node: node, span: DUMMY_SP })), + span: DUMMY_SP, + attrs: ast::ThinVec::new(), + }) +} + +/// 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: usize} +/// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 } +fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor, + pats: Vec<&Pat>, left_ty: Ty<'tcx>) -> P { + let pats_len = pats.len(); + let mut pats = pats.into_iter().map(|p| P((*p).clone())); + let pat = match left_ty.sty { + ty::TyTuple(..) => PatKind::Tuple(pats.collect(), None), + + ty::TyAdt(adt, _) => { + let v = ctor.variant_for_adt(adt); + match v.ctor_kind { + CtorKind::Fictive => { + let field_pats: hir::HirVec<_> = v.fields.iter() + .zip(pats) + .filter(|&(_, ref pat)| pat.node != PatKind::Wild) + .map(|(field, pat)| Spanned { + span: DUMMY_SP, + node: hir::FieldPat { + name: field.name, + pat: pat, + is_shorthand: false, + } + }).collect(); + let has_more_fields = field_pats.len() < pats_len; + PatKind::Struct(def_to_path(cx.tcx, v.did), field_pats, has_more_fields) + } + CtorKind::Fn => { + PatKind::TupleStruct(def_to_path(cx.tcx, v.did), pats.collect(), None) + } + CtorKind::Const => { + PatKind::Path(None, def_to_path(cx.tcx, v.did)) + } + } + } + + ty::TyRef(_, ty::TypeAndMut { mutbl, .. }) => { + assert_eq!(pats_len, 1); + PatKind::Ref(pats.nth(0).unwrap(), mutbl) + } + + ty::TySlice(_) => match ctor { + &Slice(n) => { + assert_eq!(pats_len, n); + PatKind::Slice(pats.collect(), None, hir::HirVec::new()) + }, + _ => unreachable!() + }, + + ty::TyArray(_, len) => { + assert_eq!(pats_len, len); + PatKind::Slice(pats.collect(), None, hir::HirVec::new()) + } + + _ => { + match *ctor { + ConstantValue(ref v) => PatKind::Lit(const_val_to_expr(v)), + _ => PatKind::Wild, + } + } + }; + + P(hir::Pat { + id: DUMMY_NODE_ID, + node: pat, + span: DUMMY_SP + }) +} + +fn missing_constructors(cx: &MatchCheckCtxt, &Matrix(ref rows): &Matrix, + left_ty: Ty, max_slice_length: usize) -> Vec { + let used_constructors: Vec = rows.iter() + .flat_map(|row| pat_constructors(cx, row[0], left_ty, max_slice_length)) + .collect(); + all_constructors(cx, left_ty, max_slice_length) + .into_iter() + .filter(|c| !used_constructors.contains(c)) + .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, + max_slice_length: usize) -> Vec { + match left_ty.sty { + ty::TyBool => + [true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(), + ty::TySlice(_) => + (0..max_slice_length+1).map(|length| Slice(length)).collect(), + ty::TyAdt(def, _) if def.is_enum() => + def.variants.iter().map(|v| Variant(v.did)).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. +pub fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, + matrix: &Matrix<'a, 'tcx>, + v: &[Pattern<'a, 'tcx>], + witness: WitnessPreference) + -> Usefulness { + let &Matrix(ref rows) = matrix; + debug!("is_useful({:?}, {:?})", matrix, v); + if rows.is_empty() { + return match witness { + ConstructWitness => UsefulWithWitness(vec!()), + LeaveOutWitness => Useful + }; + } + if rows[0].is_empty() { + return NotUseful; + } + assert!(rows.iter().all(|r| r.len() == v.len())); + let left_ty = match rows.iter().filter_map(|r| r[0].pattern_ty).next() + .or_else(|| v[0].pattern_ty) + { + Some(ty) => ty, + None => { + // all patterns are wildcards - we can pick any type we want + cx.tcx.types.bool + } + }; + + let max_slice_length = rows.iter().filter_map(|row| match row[0].pat.node { + PatKind::Slice(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); + debug!("is_useful - pat_constructors = {:?} left_ty = {:?}", constructors, + left_ty); + if constructors.is_empty() { + let constructors = missing_constructors(cx, matrix, left_ty, max_slice_length); + debug!("is_useful - missing_constructors = {:?}", constructors); + if constructors.is_empty() { + 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); + 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) + }).collect(); + vec![construct_witness(cx, &c, subpats, left_ty)] + }; + result.extend(pats.into_iter().skip(arity)); + result + }), + result => result + } + }).find(|result| result != &NotUseful).unwrap_or(NotUseful) + } else { + let matrix = rows.iter().filter_map(|r| { + match r[0].as_raw().node { + PatKind::Binding(..) | PatKind::Wild => Some(r[1..].to_vec()), + _ => None, + } + }).collect(); + match is_useful(cx, &matrix, &v[1..], witness) { + UsefulWithWitness(pats) => { + let mut new_pats: Vec<_> = constructors.into_iter().map(|constructor| { + let arity = constructor_arity(cx, &constructor, left_ty); + let wild_pats = vec![DUMMY_WILD_PAT; arity]; + construct_witness(cx, &constructor, wild_pats, left_ty) + }).collect(); + new_pats.extend(pats); + UsefulWithWitness(new_pats) + }, + result => result + } + } + } else { + constructors.into_iter().map(|c| + is_useful_specialized(cx, matrix, v, c.clone(), left_ty, witness) + ).find(|result| result != &NotUseful).unwrap_or(NotUseful) + } +} + +fn is_useful_specialized<'a, 'tcx>( + cx: &MatchCheckCtxt<'a, 'tcx>, + &Matrix(ref m): &Matrix<'a, 'tcx>, + v: &[Pattern<'a, 'tcx>], + ctor: Constructor, + lty: Ty<'tcx>, + 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. +fn pat_constructors(cx: &MatchCheckCtxt, p: Pattern, + left_ty: Ty, max_slice_length: usize) -> Vec { + let pat = p.as_raw(); + match pat.node { + PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..) => + match cx.tcx.expect_def(pat.id) { + Def::Variant(id) | Def::VariantCtor(id, _) => vec![Variant(id)], + Def::Struct(..) | Def::StructCtor(..) | Def::Union(..) | + Def::TyAlias(..) | Def::AssociatedTy(..) => vec![Single], + Def::Const(..) | Def::AssociatedConst(..) => + span_bug!(p.span(), "const pattern should've been rewritten"), + def => span_bug!(p.span(), "pat_constructors: unexpected definition {:?}", def), + }, + PatKind::Lit(ref expr) => + vec![ConstantValue(eval_const_expr(cx.tcx, &expr))], + PatKind::Range(ref lo, ref hi) => + vec![ConstantRange(eval_const_expr(cx.tcx, &lo), eval_const_expr(cx.tcx, &hi))], + PatKind::Slice(ref before, ref slice, ref after) => + match left_ty.sty { + ty::TyArray(..) => vec![Single], + ty::TySlice(_) if slice.is_some() => { + (before.len() + after.len()..max_slice_length+1) + .map(|length| Slice(length)) + .collect() + } + ty::TySlice(_) => vec!(Slice(before.len() + after.len())), + _ => span_bug!(pat.span, "pat_constructors: unexpected \ + slice pattern type {:?}", left_ty) + }, + PatKind::Box(..) | PatKind::Tuple(..) | PatKind::Ref(..) => + vec![Single], + PatKind::Binding(..) | PatKind::Wild => + vec![], + } +} + +/// 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) -> usize { + debug!("constructor_arity({:?}, {:?})", ctor, ty); + match ty.sty { + ty::TyTuple(ref fs) => fs.len(), + ty::TyBox(_) => 1, + ty::TySlice(_) => match *ctor { + Slice(length) => length, + ConstantValue(_) => { + // TODO: this is utterly wrong, but required for byte arrays + 0 + } + _ => bug!("bad slice pattern {:?} {:?}", ctor, ty) + }, + ty::TyRef(..) => 1, + ty::TyAdt(adt, _) => { + ctor.variant_for_adt(adt).fields.len() + } + ty::TyArray(_, n) => n, + _ => 0 + } +} + +fn range_covered_by_constructor(tcx: TyCtxt, span: Span, + ctor: &Constructor, + from: &ConstVal, to: &ConstVal) + -> Result { + let (c_from, c_to) = match *ctor { + ConstantValue(ref value) => (value, value), + ConstantRange(ref from, ref to) => (from, to), + Single => return Ok(true), + _ => bug!() + }; + let cmp_from = compare_const_vals(tcx, span, c_from, from)?; + let cmp_to = compare_const_vals(tcx, span, c_to, to)?; + Ok(cmp_from != Ordering::Less && cmp_to != Ordering::Greater) +} + +pub fn wrap_pat<'a, 'b, 'tcx>(cx: &MatchCheckCtxt<'b, 'tcx>, + pat: &'a Pat) + -> Pattern<'a, 'tcx> +{ + let pat_ty = cx.tcx.pat_ty(pat); + Pattern { + pat: pat, + pattern_ty: Some(match pat.node { + PatKind::Binding(hir::BindByRef(..), ..) => { + pat_ty.builtin_deref(false, ty::NoPreference).unwrap().ty + } + _ => pat_ty + }) + } +} + +/// 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<'a, 'b, 'tcx>( + cx: &MatchCheckCtxt<'b, 'tcx>, + r: &[Pattern<'a, 'tcx>], + constructor: &Constructor, col: usize, arity: usize) + -> Option>> +{ + let pat = r[col].as_raw(); + let &Pat { + id: pat_id, ref node, span: pat_span + } = pat; + let wpat = |pat: &'a Pat| wrap_pat(cx, pat); + + let head: Option> = match *node { + PatKind::Binding(..) | PatKind::Wild => + Some(vec![DUMMY_WILD_PATTERN; arity]), + + PatKind::Path(..) => { + match cx.tcx.expect_def(pat_id) { + Def::Const(..) | Def::AssociatedConst(..) => + span_bug!(pat_span, "const pattern should've \ + been rewritten"), + Def::VariantCtor(id, CtorKind::Const) if *constructor != Variant(id) => None, + Def::VariantCtor(_, CtorKind::Const) | + Def::StructCtor(_, CtorKind::Const) => Some(Vec::new()), + def => span_bug!(pat_span, "specialize: unexpected \ + definition {:?}", def), + } + } + + PatKind::TupleStruct(_, ref args, ddpos) => { + match cx.tcx.expect_def(pat_id) { + Def::Const(..) | Def::AssociatedConst(..) => + span_bug!(pat_span, "const pattern should've \ + been rewritten"), + Def::VariantCtor(id, CtorKind::Fn) if *constructor != Variant(id) => None, + Def::VariantCtor(_, CtorKind::Fn) | + Def::StructCtor(_, CtorKind::Fn) => { + match ddpos { + Some(ddpos) => { + let mut pats: Vec<_> = args[..ddpos].iter().map(|p| { + wpat(p) + }).collect(); + pats.extend(repeat(DUMMY_WILD_PATTERN).take(arity - args.len())); + pats.extend(args[ddpos..].iter().map(|p| wpat(p))); + Some(pats) + } + None => Some(args.iter().map(|p| wpat(p)).collect()) + } + } + def => span_bug!(pat_span, "specialize: unexpected definition: {:?}", def) + } + } + + PatKind::Struct(_, ref pattern_fields, _) => { + let adt = cx.tcx.node_id_to_type(pat_id).ty_adt_def().unwrap(); + let variant = constructor.variant_for_adt(adt); + let def_variant = adt.variant_of_def(cx.tcx.expect_def(pat_id)); + if variant.did == def_variant.did { + Some(variant.fields.iter().map(|sf| { + match pattern_fields.iter().find(|f| f.node.name == sf.name) { + Some(ref f) => wpat(&f.node.pat), + _ => DUMMY_WILD_PATTERN + } + }).collect()) + } else { + None + } + } + + PatKind::Tuple(ref args, Some(ddpos)) => { + let mut pats: Vec<_> = args[..ddpos].iter().map(|p| wpat(p)).collect(); + pats.extend(repeat(DUMMY_WILD_PATTERN).take(arity - args.len())); + pats.extend(args[ddpos..].iter().map(|p| wpat(p))); + Some(pats) + } + PatKind::Tuple(ref args, None) => + Some(args.iter().map(|p| wpat(&**p)).collect()), + + PatKind::Box(ref inner) | PatKind::Ref(ref inner, _) => + Some(vec![wpat(&**inner)]), + + PatKind::Lit(ref expr) => { + match r[col].pattern_ty { + Some(&ty::TyS { sty: ty::TyRef(_, mt), .. }) => { + // HACK: handle string literals. A string literal pattern + // serves both as an unary reference pattern and as a + // nullary value pattern, depending on the type. + Some(vec![Pattern { + pat: pat, + pattern_ty: Some(mt.ty) + }]) + } + Some(ty) => { + assert_eq!(constructor_arity(cx, constructor, ty), 0); + let expr_value = eval_const_expr(cx.tcx, &expr); + match range_covered_by_constructor( + cx.tcx, expr.span, constructor, &expr_value, &expr_value + ) { + Ok(true) => Some(vec![]), + Ok(false) => None, + Err(ErrorReported) => None, + } + } + None => span_bug!(pat.span, "literal pattern {:?} has no type", pat) + } + } + + PatKind::Range(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( + cx.tcx, pat_span, constructor, &from_value, &to_value + ) { + Ok(true) => Some(vec![]), + Ok(false) => None, + Err(ErrorReported) => None, + } + } + + PatKind::Slice(ref before, ref slice, ref after) => { + let pat_len = before.len() + after.len(); + match *constructor { + Single => { + // Fixed-length vectors. + Some( + before.iter().map(|p| wpat(p)).chain( + repeat(DUMMY_WILD_PATTERN).take(arity - pat_len).chain( + after.iter().map(|p| wpat(p)) + )).collect()) + }, + Slice(length) if pat_len <= length && slice.is_some() => { + Some( + before.iter().map(|p| wpat(p)).chain( + repeat(DUMMY_WILD_PATTERN).take(arity - pat_len).chain( + after.iter().map(|p| wpat(p)) + )).collect()) + } + Slice(length) if pat_len == length => { + Some( + before.iter().map(|p| wpat(p)).chain( + after.iter().map(|p| wpat(p)) + ).collect()) + } + _ => None + } + } + }; + debug!("specialize({:?}, {:?}) = {:?}", r[col], arity, head); + + head.map(|mut head| { + head.extend_from_slice(&r[..col]); + head.extend_from_slice(&r[col + 1..]); + head + }) +} + +pub fn is_refutable(cx: &MatchCheckCtxt, pat: &Pat, refutable: F) + -> Option where + F: FnOnce(&Pat) -> A, +{ + let pats = Matrix(vec!(vec!(wrap_pat(cx, pat)))); + match is_useful(cx, &pats, &[DUMMY_WILD_PATTERN], ConstructWitness) { + UsefulWithWitness(pats) => Some(refutable(&pats[0])), + NotUseful => None, + Useful => bug!() + } +} diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 7878ffff48d..e87f616bef2 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -8,169 +8,41 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use self::Constructor::*; -use self::Usefulness::*; -use self::WitnessPreference::*; +use _match::{MatchCheckCtxt, Matrix, wrap_pat, is_refutable, is_useful}; +use _match::{DUMMY_WILD_PATTERN, DUMMY_WILD_PAT}; +use _match::Usefulness::*; +use _match::WitnessPreference::*; + +use eval::report_const_eval_err; +use eval::{eval_const_expr_partial, const_expr_to_pat, lookup_const_by_id}; +use eval::EvalHint::ExprTypeChecked; use rustc::dep_graph::DepNode; + +use rustc::hir::pat_util::{pat_bindings, pat_contains_bindings}; + use rustc::middle::const_val::ConstVal; -use ::{eval_const_expr, eval_const_expr_partial, compare_const_vals}; -use ::{const_expr_to_pat, lookup_const_by_id}; -use ::EvalHint::ExprTypeChecked; -use eval::report_const_eval_err; -use rustc::hir::def::*; -use rustc::hir::def_id::{DefId}; use rustc::middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor}; use rustc::middle::expr_use_visitor::{LoanCause, MutateMode}; use rustc::middle::expr_use_visitor as euv; use rustc::middle::mem_categorization::{cmt}; -use rustc::hir::pat_util::*; use rustc::session::Session; use rustc::traits::Reveal; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::{self, TyCtxt}; use rustc_errors::DiagnosticBuilder; -use std::cmp::Ordering; -use std::fmt; -use std::iter::{FromIterator, IntoIterator, repeat}; -use rustc::hir; -use rustc::hir::{Pat, PatKind}; +use rustc::hir::def::*; use rustc::hir::intravisit::{self, Visitor, FnKind}; +use rustc::hir::print::pat_to_string; +use rustc::hir::{self, Pat, PatKind}; + use rustc_back::slice; -use syntax::ast::{self, DUMMY_NODE_ID, NodeId}; +use syntax::ast; use syntax::codemap::Spanned; -use syntax_pos::{Span, DUMMY_SP}; -use rustc::hir::print::pat_to_string; use syntax::ptr::P; use syntax::util::move_map::MoveMap; -use rustc::util::common::ErrorReported; - -pub const DUMMY_WILD_PAT: &'static Pat = &Pat { - id: DUMMY_NODE_ID, - node: PatKind::Wild, - span: DUMMY_SP -}; - -pub const DUMMY_WILD_PATTERN : Pattern<'static, 'static> = Pattern { - pat: DUMMY_WILD_PAT, - pattern_ty: None -}; - -#[derive(Copy, Clone)] -pub struct Pattern<'a, 'tcx> { - pat: &'a Pat, - pattern_ty: Option> -} - -impl<'a, 'tcx> Pattern<'a, 'tcx> { - fn as_raw(self) -> &'a Pat { - let mut pat = self.pat; - - while let PatKind::Binding(.., Some(ref s)) = pat.node { - pat = s; - } - - return pat; - } - - fn span(self) -> Span { - self.pat.span - } -} - -struct Matrix<'a, 'tcx>(Vec>>); - -impl<'a, 'tcx> fmt::Debug for Pattern<'a, 'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}: {:?}", pat_to_string(self.pat), self.pattern_ty) - } -} - -/// Pretty-printer for matrices of patterns, example: -/// ++++++++++++++++++++++++++ -/// + _ + [] + -/// ++++++++++++++++++++++++++ -/// + true + [First] + -/// ++++++++++++++++++++++++++ -/// + true + [Second(true)] + -/// ++++++++++++++++++++++++++ -/// + false + [_] + -/// ++++++++++++++++++++++++++ -/// + _ + [_, _, ..tail] + -/// ++++++++++++++++++++++++++ -impl<'a, 'tcx> fmt::Debug for Matrix<'a, 'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "\n")?; - - let &Matrix(ref m) = self; - let pretty_printed_matrix: Vec> = m.iter().map(|row| { - row.iter().map(|pat| format!("{:?}", pat)).collect() - }).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 = (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; - let br = repeat('+').take(total_width).collect::(); - write!(f, "{}\n", br)?; - for row in pretty_printed_matrix { - write!(f, "+")?; - for (column, pat_str) in row.into_iter().enumerate() { - write!(f, " ")?; - write!(f, "{:1$}", pat_str, column_widths[column])?; - write!(f, " +")?; - } - write!(f, "\n")?; - write!(f, "{}\n", br)?; - } - Ok(()) - } -} - -impl<'a, 'tcx> FromIterator>> for Matrix<'a, 'tcx> { - fn from_iter>>>(iter: T) -> Self - { - Matrix(iter.into_iter().collect()) - } -} - -//NOTE: appears to be the only place other then InferCtxt to contain a ParamEnv -pub struct MatchCheckCtxt<'a, 'tcx: 'a> { - pub tcx: TyCtxt<'a, 'tcx, 'tcx>, - pub param_env: ty::ParameterEnvironment<'tcx>, -} - -#[derive(Clone, Debug, 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(DefId), - /// Literal values. - ConstantValue(ConstVal), - /// Ranges of literal values (2..5). - ConstantRange(ConstVal, ConstVal), - /// Array patterns of length n. - Slice(usize), -} - -#[derive(Clone, PartialEq)] -enum Usefulness { - Useful, - UsefulWithWitness(Vec>), - NotUseful -} - -#[derive(Copy, Clone)] -enum WitnessPreference { - ConstructWitness, - LeaveOutWitness -} +use syntax_pos::Span; impl<'a, 'tcx, 'v> Visitor<'v> for MatchCheckCtxt<'a, 'tcx> { fn visit_expr(&mut self, ex: &hir::Expr) { @@ -180,7 +52,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MatchCheckCtxt<'a, 'tcx> { check_local(self, l); } fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl, - b: &'v hir::Block, s: Span, n: NodeId) { + b: &'v hir::Block, s: Span, n: ast::NodeId) { check_fn(self, fk, fd, b, s, n); } } @@ -341,7 +213,7 @@ fn pat_is_catchall(dm: &DefMap, pat: &Pat) -> bool { fn check_arms(cx: &MatchCheckCtxt, arms: &[(Vec>, Option<&hir::Expr>)], source: hir::MatchSource) { - let mut seen = Matrix(vec![]); + let mut seen = Matrix::empty(); let mut catchall = None; let mut printed_if_let_err = false; for &(ref pats, guard) in arms { @@ -390,7 +262,7 @@ fn check_arms(cx: &MatchCheckCtxt, hir::MatchSource::Normal => { let mut err = struct_span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern"); - err.span_label(pat.span, &format!("this is an unreachable pattern")); + err.span_label(pat.span, &"this is an unreachable pattern"); // if we had a catchall pattern, hint at that if let Some(catchall) = catchall { err.span_note(catchall, "this pattern matches any value"); @@ -407,13 +279,10 @@ fn check_arms(cx: &MatchCheckCtxt, UsefulWithWitness(_) => bug!() } if guard.is_none() { - let Matrix(mut rows) = seen; - rows.push(v); - seen = Matrix(rows); + seen.push(v); if catchall.is_none() && pat_is_catchall(&cx.tcx.def_map.borrow(), pat) { catchall = Some(pat.span); } - } } } @@ -485,18 +354,6 @@ fn check_exhaustive<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, } } -fn const_val_to_expr(value: &ConstVal) -> P { - let node = match value { - &ConstVal::Bool(b) => ast::LitKind::Bool(b), - _ => bug!() - }; - P(hir::Expr { - id: DUMMY_NODE_ID, - node: hir::ExprLit(P(Spanned { node: node, span: DUMMY_SP })), - span: DUMMY_SP, - attrs: ast::ThinVec::new(), - }) -} struct StaticInliner<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -588,505 +445,6 @@ impl<'a, 'tcx> StaticInliner<'a, 'tcx> { } } -/// 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: usize} -/// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 } -fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor, - pats: Vec<&Pat>, left_ty: Ty<'tcx>) -> P { - let pats_len = pats.len(); - let mut pats = pats.into_iter().map(|p| P((*p).clone())); - let pat = match left_ty.sty { - ty::TyTuple(..) => PatKind::Tuple(pats.collect(), None), - - ty::TyAdt(adt, _) => { - let v = ctor.variant_for_adt(adt); - match v.ctor_kind { - CtorKind::Fictive => { - let field_pats: hir::HirVec<_> = v.fields.iter() - .zip(pats) - .filter(|&(_, ref pat)| pat.node != PatKind::Wild) - .map(|(field, pat)| Spanned { - span: DUMMY_SP, - node: hir::FieldPat { - name: field.name, - pat: pat, - is_shorthand: false, - } - }).collect(); - let has_more_fields = field_pats.len() < pats_len; - PatKind::Struct(def_to_path(cx.tcx, v.did), field_pats, has_more_fields) - } - CtorKind::Fn => { - PatKind::TupleStruct(def_to_path(cx.tcx, v.did), pats.collect(), None) - } - CtorKind::Const => { - PatKind::Path(None, def_to_path(cx.tcx, v.did)) - } - } - } - - ty::TyRef(_, ty::TypeAndMut { mutbl, .. }) => { - assert_eq!(pats_len, 1); - PatKind::Ref(pats.nth(0).unwrap(), mutbl) - } - - ty::TySlice(_) => match ctor { - &Slice(n) => { - assert_eq!(pats_len, n); - PatKind::Slice(pats.collect(), None, hir::HirVec::new()) - }, - _ => unreachable!() - }, - - ty::TyArray(_, len) => { - assert_eq!(pats_len, len); - PatKind::Slice(pats.collect(), None, hir::HirVec::new()) - } - - _ => { - match *ctor { - ConstantValue(ref v) => PatKind::Lit(const_val_to_expr(v)), - _ => PatKind::Wild, - } - } - }; - - P(hir::Pat { - id: DUMMY_NODE_ID, - node: pat, - span: DUMMY_SP - }) -} - -impl Constructor { - fn variant_for_adt<'tcx, 'container, 'a>(&self, - adt: &'a ty::AdtDefData<'tcx, 'container>) - -> &'a ty::VariantDefData<'tcx, 'container> { - match self { - &Variant(vid) => adt.variant_with_id(vid), - _ => adt.struct_variant() - } - } -} - -fn missing_constructors(cx: &MatchCheckCtxt, &Matrix(ref rows): &Matrix, - left_ty: Ty, max_slice_length: usize) -> Vec { - let used_constructors: Vec = rows.iter() - .flat_map(|row| pat_constructors(cx, row[0], left_ty, max_slice_length)) - .collect(); - all_constructors(cx, left_ty, max_slice_length) - .into_iter() - .filter(|c| !used_constructors.contains(c)) - .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, - max_slice_length: usize) -> Vec { - match left_ty.sty { - ty::TyBool => - [true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(), - ty::TySlice(_) => - (0..max_slice_length+1).map(|length| Slice(length)).collect(), - ty::TyAdt(def, _) if def.is_enum() => - def.variants.iter().map(|v| Variant(v.did)).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<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, - matrix: &Matrix<'a, 'tcx>, - v: &[Pattern<'a, 'tcx>], - witness: WitnessPreference) - -> Usefulness { - let &Matrix(ref rows) = matrix; - debug!("is_useful({:?}, {:?})", matrix, v); - if rows.is_empty() { - return match witness { - ConstructWitness => UsefulWithWitness(vec!()), - LeaveOutWitness => Useful - }; - } - if rows[0].is_empty() { - return NotUseful; - } - assert!(rows.iter().all(|r| r.len() == v.len())); - let left_ty = match rows.iter().filter_map(|r| r[0].pattern_ty).next() - .or_else(|| v[0].pattern_ty) - { - Some(ty) => ty, - None => { - // all patterns are wildcards - we can pick any type we want - cx.tcx.types.bool - } - }; - - let max_slice_length = rows.iter().filter_map(|row| match row[0].pat.node { - PatKind::Slice(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); - debug!("is_useful - pat_constructors = {:?} left_ty = {:?}", constructors, - left_ty); - if constructors.is_empty() { - let constructors = missing_constructors(cx, matrix, left_ty, max_slice_length); - debug!("is_useful - missing_constructors = {:?}", constructors); - if constructors.is_empty() { - 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); - 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) - }).collect(); - vec![construct_witness(cx, &c, subpats, left_ty)] - }; - result.extend(pats.into_iter().skip(arity)); - result - }), - result => result - } - }).find(|result| result != &NotUseful).unwrap_or(NotUseful) - } else { - let matrix = rows.iter().filter_map(|r| { - match r[0].as_raw().node { - PatKind::Binding(..) | PatKind::Wild => Some(r[1..].to_vec()), - _ => None, - } - }).collect(); - match is_useful(cx, &matrix, &v[1..], witness) { - UsefulWithWitness(pats) => { - let mut new_pats: Vec<_> = constructors.into_iter().map(|constructor| { - let arity = constructor_arity(cx, &constructor, left_ty); - let wild_pats = vec![DUMMY_WILD_PAT; arity]; - construct_witness(cx, &constructor, wild_pats, left_ty) - }).collect(); - new_pats.extend(pats); - UsefulWithWitness(new_pats) - }, - result => result - } - } - } else { - constructors.into_iter().map(|c| - is_useful_specialized(cx, matrix, v, c.clone(), left_ty, witness) - ).find(|result| result != &NotUseful).unwrap_or(NotUseful) - } -} - -fn is_useful_specialized<'a, 'tcx>( - cx: &MatchCheckCtxt<'a, 'tcx>, - &Matrix(ref m): &Matrix<'a, 'tcx>, - v: &[Pattern<'a, 'tcx>], - ctor: Constructor, - lty: Ty<'tcx>, - 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. -fn pat_constructors(cx: &MatchCheckCtxt, p: Pattern, - left_ty: Ty, max_slice_length: usize) -> Vec { - let pat = p.as_raw(); - match pat.node { - PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..) => - match cx.tcx.expect_def(pat.id) { - Def::Variant(id) | Def::VariantCtor(id, ..) => vec![Variant(id)], - Def::Struct(..) | Def::StructCtor(..) | Def::Union(..) | - Def::TyAlias(..) | Def::AssociatedTy(..) => vec![Single], - Def::Const(..) | Def::AssociatedConst(..) => - span_bug!(p.span(), "const pattern should've been rewritten"), - def => span_bug!(p.span(), "pat_constructors: unexpected definition {:?}", def), - }, - PatKind::Lit(ref expr) => - vec![ConstantValue(eval_const_expr(cx.tcx, &expr))], - PatKind::Range(ref lo, ref hi) => - vec![ConstantRange(eval_const_expr(cx.tcx, &lo), eval_const_expr(cx.tcx, &hi))], - PatKind::Slice(ref before, ref slice, ref after) => - match left_ty.sty { - ty::TyArray(..) => vec![Single], - ty::TySlice(_) if slice.is_some() => { - (before.len() + after.len()..max_slice_length+1) - .map(|length| Slice(length)) - .collect() - } - ty::TySlice(_) => vec!(Slice(before.len() + after.len())), - _ => span_bug!(pat.span, "pat_constructors: unexpected \ - slice pattern type {:?}", left_ty) - }, - PatKind::Box(..) | PatKind::Tuple(..) | PatKind::Ref(..) => - vec![Single], - PatKind::Binding(..) | PatKind::Wild => - vec![], - } -} - -/// 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) -> usize { - debug!("constructor_arity({:?}, {:?})", ctor, ty); - match ty.sty { - ty::TyTuple(ref fs) => fs.len(), - ty::TyBox(_) => 1, - ty::TySlice(_) => match *ctor { - Slice(length) => length, - ConstantValue(_) => { - // TODO: this is utterly wrong, but required for byte arrays - 0 - } - _ => bug!("bad slice pattern {:?} {:?}", ctor, ty) - }, - ty::TyRef(..) => 1, - ty::TyAdt(adt, _) => { - ctor.variant_for_adt(adt).fields.len() - } - ty::TyArray(_, n) => n, - _ => 0 - } -} - -fn range_covered_by_constructor(tcx: TyCtxt, span: Span, - ctor: &Constructor, - from: &ConstVal, to: &ConstVal) - -> Result { - let (c_from, c_to) = match *ctor { - ConstantValue(ref value) => (value, value), - ConstantRange(ref from, ref to) => (from, to), - Single => return Ok(true), - _ => bug!() - }; - let cmp_from = compare_const_vals(tcx, span, c_from, from)?; - let cmp_to = compare_const_vals(tcx, span, c_to, to)?; - Ok(cmp_from != Ordering::Less && cmp_to != Ordering::Greater) -} - -fn wrap_pat<'a, 'b, 'tcx>(cx: &MatchCheckCtxt<'b, 'tcx>, - pat: &'a Pat) - -> Pattern<'a, 'tcx> -{ - let pat_ty = cx.tcx.pat_ty(pat); - Pattern { - pat: pat, - pattern_ty: Some(match pat.node { - PatKind::Binding(hir::BindByRef(..), ..) => { - pat_ty.builtin_deref(false, ty::NoPreference).unwrap().ty - } - _ => pat_ty - }) - } -} - -/// 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, 'b, 'tcx>( - cx: &MatchCheckCtxt<'b, 'tcx>, - r: &[Pattern<'a, 'tcx>], - constructor: &Constructor, col: usize, arity: usize) - -> Option>> -{ - let pat = r[col].as_raw(); - let &Pat { - id: pat_id, ref node, span: pat_span - } = pat; - let wpat = |pat: &'a Pat| wrap_pat(cx, pat); - - let head: Option> = match *node { - PatKind::Binding(..) | PatKind::Wild => - Some(vec![DUMMY_WILD_PATTERN; arity]), - - PatKind::Path(..) => { - match cx.tcx.expect_def(pat_id) { - Def::Const(..) | Def::AssociatedConst(..) => - span_bug!(pat_span, "const pattern should've \ - been rewritten"), - Def::VariantCtor(id, CtorKind::Const) if *constructor != Variant(id) => None, - Def::VariantCtor(_, CtorKind::Const) | - Def::StructCtor(_, CtorKind::Const) => Some(Vec::new()), - def => span_bug!(pat_span, "specialize: unexpected definition: {:?}", def), - } - } - - PatKind::TupleStruct(_, ref args, ddpos) => { - match cx.tcx.expect_def(pat_id) { - Def::Const(..) | Def::AssociatedConst(..) => - span_bug!(pat_span, "const pattern should've \ - been rewritten"), - Def::VariantCtor(id, CtorKind::Fn) if *constructor != Variant(id) => None, - Def::VariantCtor(_, CtorKind::Fn) | - Def::StructCtor(_, CtorKind::Fn) => { - match ddpos { - Some(ddpos) => { - let mut pats: Vec<_> = args[..ddpos].iter().map(|p| { - wpat(p) - }).collect(); - pats.extend(repeat(DUMMY_WILD_PATTERN).take(arity - args.len())); - pats.extend(args[ddpos..].iter().map(|p| wpat(p))); - Some(pats) - } - None => Some(args.iter().map(|p| wpat(p)).collect()) - } - } - def => span_bug!(pat_span, "specialize: unexpected definition: {:?}", def), - } - } - - PatKind::Struct(_, ref pattern_fields, _) => { - let adt = cx.tcx.node_id_to_type(pat_id).ty_adt_def().unwrap(); - let variant = constructor.variant_for_adt(adt); - let def_variant = adt.variant_of_def(cx.tcx.expect_def(pat_id)); - if variant.did == def_variant.did { - Some(variant.fields.iter().map(|sf| { - match pattern_fields.iter().find(|f| f.node.name == sf.name) { - Some(ref f) => wpat(&f.node.pat), - _ => DUMMY_WILD_PATTERN - } - }).collect()) - } else { - None - } - } - - PatKind::Tuple(ref args, Some(ddpos)) => { - let mut pats: Vec<_> = args[..ddpos].iter().map(|p| wpat(p)).collect(); - pats.extend(repeat(DUMMY_WILD_PATTERN).take(arity - args.len())); - pats.extend(args[ddpos..].iter().map(|p| wpat(p))); - Some(pats) - } - PatKind::Tuple(ref args, None) => - Some(args.iter().map(|p| wpat(&**p)).collect()), - - PatKind::Box(ref inner) | PatKind::Ref(ref inner, _) => - Some(vec![wpat(&**inner)]), - - PatKind::Lit(ref expr) => { - match r[col].pattern_ty { - Some(&ty::TyS { sty: ty::TyRef(_, mt), .. }) => { - // HACK: handle string literals. A string literal pattern - // serves both as an unary reference pattern and as a - // nullary value pattern, depending on the type. - Some(vec![Pattern { - pat: pat, - pattern_ty: Some(mt.ty) - }]) - } - Some(ty) => { - assert_eq!(constructor_arity(cx, constructor, ty), 0); - let expr_value = eval_const_expr(cx.tcx, &expr); - match range_covered_by_constructor( - cx.tcx, expr.span, constructor, &expr_value, &expr_value - ) { - Ok(true) => Some(vec![]), - Ok(false) => None, - Err(ErrorReported) => None, - } - } - None => span_bug!(pat.span, "literal pattern {:?} has no type", pat) - } - } - - PatKind::Range(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( - cx.tcx, pat_span, constructor, &from_value, &to_value - ) { - Ok(true) => Some(vec![]), - Ok(false) => None, - Err(ErrorReported) => None, - } - } - - PatKind::Slice(ref before, ref slice, ref after) => { - let pat_len = before.len() + after.len(); - match *constructor { - Single => { - // Fixed-length vectors. - Some( - before.iter().map(|p| wpat(p)).chain( - repeat(DUMMY_WILD_PATTERN).take(arity - pat_len).chain( - after.iter().map(|p| wpat(p)) - )).collect()) - }, - Slice(length) if pat_len <= length && slice.is_some() => { - Some( - before.iter().map(|p| wpat(p)).chain( - repeat(DUMMY_WILD_PATTERN).take(arity - pat_len).chain( - after.iter().map(|p| wpat(p)) - )).collect()) - } - Slice(length) if pat_len == length => { - Some( - before.iter().map(|p| wpat(p)).chain( - after.iter().map(|p| wpat(p)) - ).collect()) - } - _ => None - } - } - }; - debug!("specialize({:?}, {:?}) = {:?}", r[col], arity, head); - - head.map(|mut head| { - head.extend_from_slice(&r[..col]); - head.extend_from_slice(&r[col + 1..]); - head - }) -} - fn check_local(cx: &mut MatchCheckCtxt, loc: &hir::Local) { intravisit::walk_local(cx, loc); @@ -1103,7 +461,7 @@ fn check_fn(cx: &mut MatchCheckCtxt, decl: &hir::FnDecl, body: &hir::Block, sp: Span, - fn_id: NodeId) { + fn_id: ast::NodeId) { match kind { FnKind::Closure(_) => {} _ => cx.param_env = ty::ParameterEnvironment::for_item(cx.tcx, fn_id), @@ -1135,17 +493,6 @@ fn check_irrefutable(cx: &MatchCheckCtxt, pat: &Pat, is_fn_arg: bool) { }); } -fn is_refutable(cx: &MatchCheckCtxt, pat: &Pat, refutable: F) -> Option where - F: FnOnce(&Pat) -> A, -{ - let pats = Matrix(vec!(vec!(wrap_pat(cx, pat)))); - match is_useful(cx, &pats, &[DUMMY_WILD_PATTERN], ConstructWitness) { - UsefulWithWitness(pats) => Some(refutable(&pats[0])), - NotUseful => None, - Useful => bug!() - } -} - // Legality of move bindings checking fn check_legality_of_move_bindings(cx: &MatchCheckCtxt, has_guard: bool, @@ -1219,10 +566,10 @@ struct MutationChecker<'a, 'gcx: 'a> { impl<'a, 'gcx, 'tcx> Delegate<'tcx> for MutationChecker<'a, 'gcx> { fn matched_pat(&mut self, _: &Pat, _: cmt, _: euv::MatchMode) {} - fn consume(&mut self, _: NodeId, _: Span, _: cmt, _: ConsumeMode) {} + fn consume(&mut self, _: ast::NodeId, _: Span, _: cmt, _: ConsumeMode) {} fn consume_pat(&mut self, _: &Pat, _: cmt, _: ConsumeMode) {} fn borrow(&mut self, - _: NodeId, + _: ast::NodeId, span: Span, _: cmt, _: &'tcx ty::Region, @@ -1238,8 +585,8 @@ impl<'a, 'gcx, 'tcx> Delegate<'tcx> for MutationChecker<'a, 'gcx> { ty::ImmBorrow | ty::UniqueImmBorrow => {} } } - fn decl_without_init(&mut self, _: NodeId, _: Span) {} - fn mutate(&mut self, _: NodeId, span: Span, _: cmt, mode: MutateMode) { + fn decl_without_init(&mut self, _: ast::NodeId, _: Span) {} + fn mutate(&mut self, _: ast::NodeId, span: Span, _: cmt, mode: MutateMode) { match mode { MutateMode::JustWrite | MutateMode::WriteAndRead => { struct_span_err!(self.cx.tcx.sess, span, E0302, "cannot assign in a pattern guard") diff --git a/src/librustc_const_eval/diagnostics.rs b/src/librustc_const_eval/diagnostics.rs index 9cdc76f25a6..c2b39625e2e 100644 --- a/src/librustc_const_eval/diagnostics.rs +++ b/src/librustc_const_eval/diagnostics.rs @@ -40,7 +40,7 @@ Ensure the ordering of the match arm is correct and remove any superfluous arms. "##, -E0002: r##" +/*E0002: r##" This error indicates that an empty match expression is invalid because the type it is matching on is non-empty (there exist values of this type). In safe code it is impossible to create an instance of an empty type, so empty match @@ -68,7 +68,7 @@ fn foo(x: Option) { } } ``` -"##, +"##,*/ E0003: r##" diff --git a/src/librustc_const_eval/lib.rs b/src/librustc_const_eval/lib.rs index 9a97df96696..cbdb808a127 100644 --- a/src/librustc_const_eval/lib.rs +++ b/src/librustc_const_eval/lib.rs @@ -47,6 +47,7 @@ extern crate serialize as rustc_serialize; // used by deriving pub mod diagnostics; mod eval; +mod _match; pub mod check_match; pub mod pattern;