From e5c01f4633f3109914390d67856b9910042a627e Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sat, 24 Sep 2016 16:12:37 +0300 Subject: [PATCH 01/13] comment some ugly points in check_match --- src/librustc_const_eval/check_match.rs | 38 ++++++++++++++++---------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 9281d8aa44a56..709eb49dfccf0 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -851,8 +851,11 @@ pub fn constructor_arity(_cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> us ty::TyBox(_) => 1, ty::TySlice(_) => match *ctor { Slice(length) => length, - ConstantValue(_) => 0, - _ => bug!() + ConstantValue(_) => { + // TODO: this is utterly wrong, but required for byte arrays + 0 + } + _ => bug!("bad slice pattern {:?} {:?}", ctor, ty) }, ty::TyRef(..) => 1, ty::TyAdt(adt, _) => { @@ -981,20 +984,25 @@ pub fn specialize<'a, 'b, 'tcx>( Some(vec![wpat(&**inner)]), PatKind::Lit(ref expr) => { - if let Some(&ty::TyS { sty: ty::TyRef(_, mt), .. }) = r[col].1 { - // 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![(pat, Some(mt.ty))]) - } else { - 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, + match r[col].1 { + 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![(pat, 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) } } From b69cca6da49b2cbb1d6346b5280c464847e2fe20 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sat, 24 Sep 2016 16:42:10 +0300 Subject: [PATCH 02/13] remove SliceWithSubslice, only used from old trans --- src/librustc_const_eval/check_match.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 709eb49dfccf0..135e14aeaaf4f 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -127,8 +127,6 @@ pub enum Constructor { ConstantRange(ConstVal, ConstVal), /// Array patterns of length n. Slice(usize), - /// Array patterns with a subslice. - SliceWithSubslice(usize, usize) } #[derive(Clone, PartialEq)] @@ -1042,16 +1040,6 @@ pub fn specialize<'a, 'b, 'tcx>( after.iter().map(|p| wpat(p)) ).collect()) } - SliceWithSubslice(prefix, suffix) - if before.len() == prefix - && after.len() == suffix - && slice.is_some() => { - // this is used by trans::_match only - let mut pats: Vec<_> = before.iter() - .map(|p| (&**p, None)).collect(); - pats.extend(after.iter().map(|p| (&**p, None))); - Some(pats) - } _ => None } } From bb5afb412198d9c91abe85b1227d005dfa58e196 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sat, 24 Sep 2016 16:49:51 +0300 Subject: [PATCH 03/13] use a struct abstraction in check_match --- src/librustc_const_eval/check_match.rs | 154 +++++++++++++++---------- 1 file changed, 94 insertions(+), 60 deletions(-) diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 135e14aeaaf4f..f8ac841a0a265 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -52,7 +52,55 @@ pub const DUMMY_WILD_PAT: &'static Pat = &Pat { span: DUMMY_SP }; -struct Matrix<'a, 'tcx>(Vec>)>>); +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; + } + + + /// Checks for common cases of "catchall" patterns that may not be intended as such. + fn is_catchall(self, dm: &DefMap) -> bool { + fn is_catchall(dm: &DefMap, pat: &Pat) -> bool { + match pat.node { + PatKind::Binding(.., None) => true, + PatKind::Binding(.., Some(ref s)) => is_catchall(dm, s), + PatKind::Ref(ref s, _) => is_catchall(dm, s), + PatKind::Tuple(ref v, _) => v.iter().all(|p|is_catchall(dm, &p)), + _ => false + } + } + is_catchall(dm, self.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: /// ++++++++++++++++++++++++++ @@ -72,9 +120,7 @@ impl<'a, 'tcx> fmt::Debug for Matrix<'a, 'tcx> { let &Matrix(ref m) = self; let pretty_printed_matrix: Vec> = m.iter().map(|row| { - row.iter() - .map(|&(pat,ty)| format!("{}: {:?}", pat_to_string(&pat), ty)) - .collect::>() + row.iter().map(|pat| format!("{:?}", pat)).collect() }).collect(); let column_count = m.iter().map(|row| row.len()).max().unwrap_or(0); @@ -100,9 +146,8 @@ impl<'a, 'tcx> fmt::Debug for Matrix<'a, 'tcx> { } } -impl<'a, 'tcx> FromIterator>)>> for Matrix<'a, 'tcx> { - fn from_iter>)>>>(iter: T) - -> Self +impl<'a, 'tcx> FromIterator>> for Matrix<'a, 'tcx> { + fn from_iter>>>(iter: T) -> Self { Matrix(iter.into_iter().collect()) } @@ -349,8 +394,8 @@ fn check_arms(cx: &MatchCheckCtxt, err.span_label(pat.span, &format!("this is an unreachable pattern")); // if we had a catchall pattern, hint at that for row in &seen.0 { - if pat_is_catchall(&cx.tcx.def_map.borrow(), row[0].0) { - span_note!(err, row[0].0.span, + if row[0].is_catchall(&cx.tcx.def_map.borrow()) { + span_note!(err, row[0].span(), "this pattern matches any value"); } } @@ -374,29 +419,11 @@ fn check_arms(cx: &MatchCheckCtxt, } } -/// Checks for common cases of "catchall" patterns that may not be intended as such. -fn pat_is_catchall(dm: &DefMap, p: &Pat) -> bool { - match p.node { - PatKind::Binding(.., None) => true, - PatKind::Binding(.., Some(ref s)) => pat_is_catchall(dm, &s), - PatKind::Ref(ref s, _) => pat_is_catchall(dm, &s), - PatKind::Tuple(ref v, _) => v.iter().all(|p| pat_is_catchall(dm, &p)), - _ => false - } -} - -fn raw_pat(p: &Pat) -> &Pat { - match p.node { - PatKind::Binding(.., Some(ref s)) => raw_pat(&s), - _ => p - } -} - fn check_exhaustive<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, sp: Span, matrix: &Matrix<'a, 'tcx>, source: hir::MatchSource) { - match is_useful(cx, matrix, &[(DUMMY_WILD_PAT, None)], ConstructWitness) { + match is_useful(cx, matrix, &[DUMMY_WILD_PATTERN], ConstructWitness) { UsefulWithWitness(pats) => { let witnesses = if pats.is_empty() { vec![DUMMY_WILD_PAT] @@ -655,7 +682,7 @@ impl Constructor { 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].0, left_ty, max_slice_length)) + .flat_map(|row| pat_constructors(cx, row[0], left_ty, max_slice_length)) .collect(); all_constructors(cx, left_ty, max_slice_length) .into_iter() @@ -695,7 +722,7 @@ fn all_constructors(_cx: &MatchCheckCtxt, left_ty: Ty, // So it assumes that v is non-empty. fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, matrix: &Matrix<'a, 'tcx>, - v: &[(&Pat, Option>)], + v: &[Pattern<'a, 'tcx>], witness: WitnessPreference) -> Usefulness { let &Matrix(ref rows) = matrix; @@ -710,7 +737,9 @@ fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, return NotUseful; } assert!(rows.iter().all(|r| r.len() == v.len())); - let left_ty = match rows.iter().filter_map(|r| r[0].1).next().or_else(|| v[0].1) { + 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 @@ -718,12 +747,12 @@ fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, } }; - let max_slice_length = rows.iter().filter_map(|row| match row[0].0.node { + 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].0, left_ty, max_slice_length); + 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() { @@ -749,7 +778,7 @@ fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, }).find(|result| result != &NotUseful).unwrap_or(NotUseful) } else { let matrix = rows.iter().filter_map(|r| { - match raw_pat(r[0].0).node { + match r[0].as_raw().node { PatKind::Binding(..) | PatKind::Wild => Some(r[1..].to_vec()), _ => None, } @@ -777,7 +806,7 @@ fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, fn is_useful_specialized<'a, 'tcx>( cx: &MatchCheckCtxt<'a, 'tcx>, &Matrix(ref m): &Matrix<'a, 'tcx>, - v: &[(&Pat, Option>)], + v: &[Pattern<'a, 'tcx>], ctor: Constructor, lty: Ty<'tcx>, witness: WitnessPreference) -> Usefulness @@ -801,9 +830,9 @@ fn is_useful_specialized<'a, 'tcx>( /// /// On the other hand, a wild pattern and an identifier pattern cannot be /// specialized in any way. -fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat, +fn pat_constructors(cx: &MatchCheckCtxt, p: Pattern, left_ty: Ty, max_slice_length: usize) -> Vec { - let pat = raw_pat(p); + let pat = p.as_raw(); match pat.node { PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..) => match cx.tcx.expect_def(pat.id) { @@ -811,8 +840,8 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat, Def::Struct(..) | Def::StructCtor(..) | Def::Union(..) | Def::TyAlias(..) | Def::AssociatedTy(..) => vec![Single], Def::Const(..) | Def::AssociatedConst(..) => - span_bug!(pat.span, "const pattern should've been rewritten"), - def => span_bug!(pat.span, "pat_constructors: unexpected definition {:?}", def), + 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))], @@ -881,15 +910,18 @@ fn range_covered_by_constructor(tcx: TyCtxt, span: Span, fn wrap_pat<'a, 'b, 'tcx>(cx: &MatchCheckCtxt<'b, 'tcx>, pat: &'a Pat) - -> (&'a Pat, Option>) + -> Pattern<'a, 'tcx> { let pat_ty = cx.tcx.pat_ty(pat); - (pat, Some(match pat.node { - PatKind::Binding(hir::BindByRef(..), ..) => { - pat_ty.builtin_deref(false, ty::NoPreference).unwrap().ty - } - _ => pat_ty - })) + 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 @@ -902,20 +934,19 @@ fn wrap_pat<'a, 'b, 'tcx>(cx: &MatchCheckCtxt<'b, 'tcx>, /// fields filled with wild patterns. pub fn specialize<'a, 'b, 'tcx>( cx: &MatchCheckCtxt<'b, 'tcx>, - r: &[(&'a Pat, Option>)], + r: &[Pattern<'a, 'tcx>], constructor: &Constructor, col: usize, arity: usize) - -> Option>)>> + -> Option>> { - let pat = raw_pat(r[col].0); + 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 dummy_pat = (DUMMY_WILD_PAT, None); - let head: Option)>> = match *node { + let head: Option> = match *node { PatKind::Binding(..) | PatKind::Wild => - Some(vec![dummy_pat; arity]), + Some(vec![DUMMY_WILD_PATTERN; arity]), PatKind::Path(..) => { match cx.tcx.expect_def(pat_id) { @@ -942,7 +973,7 @@ pub fn specialize<'a, 'b, 'tcx>( let mut pats: Vec<_> = args[..ddpos].iter().map(|p| { wpat(p) }).collect(); - pats.extend(repeat((DUMMY_WILD_PAT, None)).take(arity - args.len())); + pats.extend(repeat(DUMMY_WILD_PATTERN).take(arity - args.len())); pats.extend(args[ddpos..].iter().map(|p| wpat(p))); Some(pats) } @@ -961,7 +992,7 @@ pub fn specialize<'a, 'b, 'tcx>( 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_pat + _ => DUMMY_WILD_PATTERN } }).collect()) } else { @@ -971,7 +1002,7 @@ pub fn specialize<'a, 'b, 'tcx>( PatKind::Tuple(ref args, Some(ddpos)) => { let mut pats: Vec<_> = args[..ddpos].iter().map(|p| wpat(p)).collect(); - pats.extend(repeat(dummy_pat).take(arity - args.len())); + pats.extend(repeat(DUMMY_WILD_PATTERN).take(arity - args.len())); pats.extend(args[ddpos..].iter().map(|p| wpat(p))); Some(pats) } @@ -982,12 +1013,15 @@ pub fn specialize<'a, 'b, 'tcx>( Some(vec![wpat(&**inner)]), PatKind::Lit(ref expr) => { - match r[col].1 { + 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![(pat, Some(mt.ty))]) + Some(vec![Pattern { + pat: pat, + pattern_ty: Some(mt.ty) + }]) } Some(ty) => { assert_eq!(constructor_arity(cx, constructor, ty), 0); @@ -1023,14 +1057,14 @@ pub fn specialize<'a, 'b, 'tcx>( // Fixed-length vectors. Some( before.iter().map(|p| wpat(p)).chain( - repeat(dummy_pat).take(arity - pat_len).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_pat).take(arity - pat_len).chain( + repeat(DUMMY_WILD_PATTERN).take(arity - pat_len).chain( after.iter().map(|p| wpat(p)) )).collect()) } @@ -1105,7 +1139,7 @@ fn is_refutable(cx: &MatchCheckCtxt, pat: &Pat, refutable: F) -> Option F: FnOnce(&Pat) -> A, { let pats = Matrix(vec!(vec!(wrap_pat(cx, pat)))); - match is_useful(cx, &pats, &[(DUMMY_WILD_PAT, None)], ConstructWitness) { + match is_useful(cx, &pats, &[DUMMY_WILD_PATTERN], ConstructWitness) { UsefulWithWitness(pats) => Some(refutable(&pats[0])), NotUseful => None, Useful => bug!() From 732f22745dbac22fad9e8f9c0f17d37a23db52f8 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sat, 24 Sep 2016 17:45:24 +0300 Subject: [PATCH 04/13] move hair::cx::pattern to const_eval --- src/Cargo.lock | 1 + src/librustc_const_eval/Cargo.toml | 1 + src/librustc_const_eval/lib.rs | 2 + .../cx => librustc_const_eval}/pattern.rs | 243 +++++++++++------- src/librustc_mir/build/mod.rs | 4 +- src/librustc_mir/hair/cx/block.rs | 2 +- src/librustc_mir/hair/cx/expr.rs | 2 +- src/librustc_mir/hair/cx/mod.rs | 1 - src/librustc_mir/hair/mod.rs | 80 +----- 9 files changed, 158 insertions(+), 178 deletions(-) rename src/{librustc_mir/hair/cx => librustc_const_eval}/pattern.rs (59%) diff --git a/src/Cargo.lock b/src/Cargo.lock index a32ba02457edf..d12b95d8b22e3 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -335,6 +335,7 @@ dependencies = [ "rustc 0.0.0", "rustc_back 0.0.0", "rustc_const_math 0.0.0", + "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", "serialize 0.0.0", "syntax 0.0.0", diff --git a/src/librustc_const_eval/Cargo.toml b/src/librustc_const_eval/Cargo.toml index 8967672548b10..ec4d2fdf967d6 100644 --- a/src/librustc_const_eval/Cargo.toml +++ b/src/librustc_const_eval/Cargo.toml @@ -14,6 +14,7 @@ serialize = { path = "../libserialize" } rustc = { path = "../librustc" } rustc_back = { path = "../librustc_back" } rustc_const_math = { path = "../librustc_const_math" } +rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } syntax = { path = "../libsyntax" } graphviz = { path = "../libgraphviz" } diff --git a/src/librustc_const_eval/lib.rs b/src/librustc_const_eval/lib.rs index 7b40269ba56ad..9a97df9669600 100644 --- a/src/librustc_const_eval/lib.rs +++ b/src/librustc_const_eval/lib.rs @@ -36,6 +36,7 @@ #[macro_use] extern crate rustc; extern crate rustc_back; extern crate rustc_const_math; +extern crate rustc_data_structures; extern crate rustc_errors; extern crate graphviz; extern crate syntax_pos; @@ -47,6 +48,7 @@ pub mod diagnostics; mod eval; pub mod check_match; +pub mod pattern; pub use eval::*; diff --git a/src/librustc_mir/hair/cx/pattern.rs b/src/librustc_const_eval/pattern.rs similarity index 59% rename from src/librustc_mir/hair/cx/pattern.rs rename to src/librustc_const_eval/pattern.rs index 84f415463be05..b32c78b31bb36 100644 --- a/src/librustc_mir/hair/cx/pattern.rs +++ b/src/librustc_const_eval/pattern.rs @@ -1,4 +1,4 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,87 +8,128 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use hair::*; -use hair::cx::Cx; -use rustc_data_structures::indexed_vec::Idx; -use rustc_const_eval as const_eval; +use eval; + +use rustc::middle::const_val::ConstVal; +use rustc::mir::repr::{Field, Literal, BorrowKind, Mutability}; +use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region}; +use rustc::hir::{self, PatKind}; use rustc::hir::def::Def; use rustc::hir::pat_util::EnumerateAndAdjustIterator; -use rustc::ty::{self, Ty}; -use rustc::mir::repr::*; -use rustc::hir::{self, PatKind}; + +use rustc_data_structures::indexed_vec::Idx; + +use syntax::ast; use syntax::ptr::P; use syntax_pos::Span; -/// When there are multiple patterns in a single arm, each one has its -/// own node-ids for the bindings. References to the variables always -/// use the node-ids from the first pattern in the arm, so we just -/// remap the ids for all subsequent bindings to the first one. -/// -/// Example: -/// ``` -/// match foo { -/// Test1(flavor /* def 1 */) | -/// Test2(flavor /* def 2 */) if flavor /* ref 1 */.is_tasty() => { ... } -/// _ => { ... } -/// } -/// ``` -struct PatCx<'patcx, 'cx: 'patcx, 'gcx: 'cx+'tcx, 'tcx: 'cx> { - cx: &'patcx mut Cx<'cx, 'gcx, 'tcx>, +#[derive(Copy, Clone, Debug)] +pub enum BindingMode<'tcx> { + ByValue, + ByRef(&'tcx Region, BorrowKind), } -impl<'cx, 'gcx, 'tcx> Cx<'cx, 'gcx, 'tcx> { - pub fn irrefutable_pat(&mut self, pat: &hir::Pat) -> Pattern<'tcx> { - PatCx::new(self).to_pattern(pat) - } +#[derive(Clone, Debug)] +pub struct FieldPattern<'tcx> { + pub field: Field, + pub pattern: Pattern<'tcx>, +} - pub fn refutable_pat(&mut self, - pat: &hir::Pat) - -> Pattern<'tcx> { - PatCx::new(self).to_pattern(pat) - } +#[derive(Clone, Debug)] +pub struct Pattern<'tcx> { + pub ty: Ty<'tcx>, + pub span: Span, + pub kind: Box>, } -impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> { - fn new(cx: &'patcx mut Cx<'cx, 'gcx, 'tcx>) - -> PatCx<'patcx, 'cx, 'gcx, 'tcx> { - PatCx { - cx: cx, - } - } +#[derive(Clone, Debug)] +pub enum PatternKind<'tcx> { + Wild, + + /// x, ref x, x @ P, etc + Binding { + mutability: Mutability, + name: ast::Name, + mode: BindingMode<'tcx>, + var: ast::NodeId, + ty: Ty<'tcx>, + subpattern: Option>, + }, + + /// Foo(...) or Foo{...} or Foo, where `Foo` is a variant name from an adt with >1 variants + Variant { + adt_def: AdtDef<'tcx>, + variant_index: usize, + subpatterns: Vec>, + }, + + /// (...), Foo(...), Foo{...}, or Foo, where `Foo` is a variant name from an adt with 1 variant + Leaf { + subpatterns: Vec>, + }, + + /// box P, &P, &mut P, etc + Deref { + subpattern: Pattern<'tcx>, + }, + + Constant { + value: ConstVal, + }, + + Range { + lo: Literal<'tcx>, + hi: Literal<'tcx>, + }, + + /// matches against a slice, checking the length and extracting elements + Slice { + prefix: Vec>, + slice: Option>, + suffix: Vec>, + }, + + /// fixed match against an array, irrefutable + Array { + prefix: Vec>, + slice: Option>, + suffix: Vec>, + }, +} - fn to_pattern(&mut self, pat: &hir::Pat) -> Pattern<'tcx> { - let mut ty = self.cx.tcx.node_id_to_type(pat.id); +impl<'a, 'gcx, 'tcx> Pattern<'tcx> { + pub fn from_hir(tcx: TyCtxt<'a, 'gcx, 'tcx>, pat: &hir::Pat) -> Self { + let mut ty = tcx.node_id_to_type(pat.id); let kind = match pat.node { PatKind::Wild => PatternKind::Wild, PatKind::Lit(ref value) => { - let value = const_eval::eval_const_expr(self.cx.tcx.global_tcx(), value); + let value = eval::eval_const_expr(tcx.global_tcx(), value); PatternKind::Constant { value: value } } PatKind::Range(ref lo, ref hi) => { - let lo = const_eval::eval_const_expr(self.cx.tcx.global_tcx(), lo); + let lo = eval::eval_const_expr(tcx.global_tcx(), lo); let lo = Literal::Value { value: lo }; - let hi = const_eval::eval_const_expr(self.cx.tcx.global_tcx(), hi); + let hi = eval::eval_const_expr(tcx.global_tcx(), hi); let hi = Literal::Value { value: hi }; PatternKind::Range { lo: lo, hi: hi } }, PatKind::Path(..) => { - match self.cx.tcx.expect_def(pat.id) { + match tcx.expect_def(pat.id) { Def::Const(def_id) | Def::AssociatedConst(def_id) => { - let tcx = self.cx.tcx.global_tcx(); - let substs = Some(self.cx.tcx.node_id_item_substs(pat.id).substs); - match const_eval::lookup_const_by_id(tcx, def_id, substs) { + let tcx = tcx.global_tcx(); + let substs = Some(tcx.node_id_item_substs(pat.id).substs); + match eval::lookup_const_by_id(tcx, def_id, substs) { Some((const_expr, _const_ty)) => { - match const_eval::const_expr_to_pat(tcx, - const_expr, - pat.id, - pat.span) { + match eval::const_expr_to_pat(tcx, + const_expr, + pat.id, + pat.span) { Ok(pat) => - return self.to_pattern(&pat), + return Pattern::from_hir(tcx, &pat), Err(_) => span_bug!( pat.span, "illegal constant"), @@ -103,32 +144,33 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> { } } _ => { - self.variant_or_leaf(pat, vec![]) + PatternKind::from_variant_or_leaf(tcx, pat, vec![]) } } } PatKind::Ref(ref subpattern, _) | PatKind::Box(ref subpattern) => { - PatternKind::Deref { subpattern: self.to_pattern(subpattern) } + PatternKind::Deref { subpattern: Self::from_hir(tcx, subpattern) } } PatKind::Slice(ref prefix, ref slice, ref suffix) => { - let ty = self.cx.tcx.node_id_to_type(pat.id); + let ty = tcx.node_id_to_type(pat.id); match ty.sty { ty::TyRef(_, mt) => PatternKind::Deref { subpattern: Pattern { ty: mt.ty, span: pat.span, - kind: Box::new(self.slice_or_array_pattern(pat.span, mt.ty, prefix, - slice, suffix)), + kind: Box::new(PatternKind::from_slice_or_array( + tcx, pat.span, mt.ty, prefix, slice, suffix)) }, }, ty::TySlice(..) | ty::TyArray(..) => - self.slice_or_array_pattern(pat.span, ty, prefix, slice, suffix), + PatternKind::from_slice_or_array( + tcx, pat.span, ty, prefix, slice, suffix), ref sty => span_bug!( @@ -139,14 +181,14 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> { } PatKind::Tuple(ref subpatterns, ddpos) => { - match self.cx.tcx.node_id_to_type(pat.id).sty { + match tcx.node_id_to_type(pat.id).sty { ty::TyTuple(ref tys) => { let subpatterns = subpatterns.iter() .enumerate_and_adjust(tys.len(), ddpos) .map(|(i, subpattern)| FieldPattern { field: Field::new(i), - pattern: self.to_pattern(subpattern), + pattern: Self::from_hir(tcx, subpattern), }) .collect(); @@ -158,9 +200,9 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> { } PatKind::Binding(bm, ref ident, ref sub) => { - let def_id = self.cx.tcx.expect_def(pat.id).def_id(); - let id = self.cx.tcx.map.as_local_node_id(def_id).unwrap(); - let var_ty = self.cx.tcx.node_id_to_type(pat.id); + let def_id = tcx.expect_def(pat.id).def_id(); + let id = tcx.map.as_local_node_id(def_id).unwrap(); + let var_ty = tcx.node_id_to_type(pat.id); let region = match var_ty.sty { ty::TyRef(r, _) => Some(r), _ => None, @@ -192,31 +234,31 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> { name: ident.node, var: id, ty: var_ty, - subpattern: self.to_opt_pattern(sub), + subpattern: Self::from_opt_pattern(tcx, sub), } } PatKind::TupleStruct(_, ref subpatterns, ddpos) => { - let pat_ty = self.cx.tcx.node_id_to_type(pat.id); + let pat_ty = tcx.node_id_to_type(pat.id); let adt_def = match pat_ty.sty { ty::TyAdt(adt_def, _) => adt_def, _ => span_bug!(pat.span, "tuple struct pattern not applied to an ADT"), }; - let variant_def = adt_def.variant_of_def(self.cx.tcx.expect_def(pat.id)); + let variant_def = adt_def.variant_of_def(tcx.expect_def(pat.id)); let subpatterns = subpatterns.iter() .enumerate_and_adjust(variant_def.fields.len(), ddpos) .map(|(i, field)| FieldPattern { field: Field::new(i), - pattern: self.to_pattern(field), + pattern: Self::from_hir(tcx, field), }) .collect(); - self.variant_or_leaf(pat, subpatterns) + PatternKind::from_variant_or_leaf(tcx, pat, subpatterns) } PatKind::Struct(_, ref fields, _) => { - let pat_ty = self.cx.tcx.node_id_to_type(pat.id); + let pat_ty = tcx.node_id_to_type(pat.id); let adt_def = match pat_ty.sty { ty::TyAdt(adt_def, _) => adt_def, _ => { @@ -225,7 +267,7 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> { "struct pattern not applied to an ADT"); } }; - let variant_def = adt_def.variant_of_def(self.cx.tcx.expect_def(pat.id)); + let variant_def = adt_def.variant_of_def(tcx.expect_def(pat.id)); let subpatterns = fields.iter() @@ -239,12 +281,12 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> { }); FieldPattern { field: Field::new(index), - pattern: self.to_pattern(&field.node.pat), + pattern: Self::from_hir(tcx, &field.node.pat), } }) .collect(); - self.variant_or_leaf(pat, subpatterns) + PatternKind::from_variant_or_leaf(tcx, pat, subpatterns) } }; @@ -255,28 +297,33 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> { } } - fn to_patterns(&mut self, pats: &[P]) -> Vec> { - pats.iter().map(|p| self.to_pattern(p)).collect() + fn from_patterns(tcx: TyCtxt<'a, 'gcx, 'tcx>, pats: &[P]) -> Vec { + pats.iter().map(|p| Self::from_hir(tcx, p)).collect() } - fn to_opt_pattern(&mut self, pat: &Option>) -> Option> { - pat.as_ref().map(|p| self.to_pattern(p)) + fn from_opt_pattern(tcx: TyCtxt<'a, 'gcx, 'tcx>, pat: &Option>) -> Option + { + pat.as_ref().map(|p| Self::from_hir(tcx, p)) } +} - fn slice_or_array_pattern(&mut self, - span: Span, - ty: Ty<'tcx>, - prefix: &[P], - slice: &Option>, - suffix: &[P]) - -> PatternKind<'tcx> { +impl<'a, 'gcx, 'tcx> PatternKind<'tcx> { + fn from_slice_or_array( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + span: Span, + ty: Ty<'tcx>, + prefix: &[P], + slice: &Option>, + suffix: &[P]) + -> Self + { match ty.sty { ty::TySlice(..) => { // matching a slice or fixed-length array PatternKind::Slice { - prefix: self.to_patterns(prefix), - slice: self.to_opt_pattern(slice), - suffix: self.to_patterns(suffix), + prefix: Pattern::from_patterns(tcx, prefix), + slice: Pattern::from_opt_pattern(tcx, slice), + suffix: Pattern::from_patterns(tcx, suffix), } } @@ -284,9 +331,9 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> { // fixed-length array assert!(len >= prefix.len() + suffix.len()); PatternKind::Array { - prefix: self.to_patterns(prefix), - slice: self.to_opt_pattern(slice), - suffix: self.to_patterns(suffix), + prefix: Pattern::from_patterns(tcx, prefix), + slice: Pattern::from_opt_pattern(tcx, slice), + suffix: Pattern::from_patterns(tcx, suffix), } } @@ -296,14 +343,16 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> { } } - fn variant_or_leaf(&mut self, - pat: &hir::Pat, - subpatterns: Vec>) - -> PatternKind<'tcx> { - match self.cx.tcx.expect_def(pat.id) { + fn from_variant_or_leaf( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + pat: &hir::Pat, + subpatterns: Vec>) + -> Self + { + match tcx.expect_def(pat.id) { Def::Variant(variant_id) | Def::VariantCtor(variant_id, ..) => { - let enum_id = self.cx.tcx.parent_def_id(variant_id).unwrap(); - let adt_def = self.cx.tcx.lookup_adt_def(enum_id); + let enum_id = tcx.parent_def_id(variant_id).unwrap(); + let adt_def = tcx.lookup_adt_def(enum_id); if adt_def.variants.len() > 1 { PatternKind::Variant { adt_def: adt_def, diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 7b3306de7538b..a7249677e0103 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -9,6 +9,8 @@ // except according to those terms. use hair::cx::Cx; +use hair::Pattern; + use rustc::middle::region::{CodeExtent, CodeExtentData, ROOT_CODE_EXTENT}; use rustc::ty::{self, Ty}; use rustc::mir::repr::*; @@ -339,7 +341,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let lvalue = Lvalue::Local(Local::new(index + 1)); if let Some(pattern) = pattern { - let pattern = self.hir.irrefutable_pat(pattern); + let pattern = Pattern::from_hir(self.hir.tcx(), pattern); scope = self.declare_bindings(scope, ast_block.span, &pattern); unpack!(block = self.lvalue_into_pattern(block, pattern, &lvalue)); } diff --git a/src/librustc_mir/hair/cx/block.rs b/src/librustc_mir/hair/cx/block.rs index 52d54f2cc8572..ec1136368c135 100644 --- a/src/librustc_mir/hair/cx/block.rs +++ b/src/librustc_mir/hair/cx/block.rs @@ -57,7 +57,7 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, let remainder_extent = cx.tcx.region_maps.lookup_code_extent(remainder_extent); - let pattern = cx.irrefutable_pat(&local.pat); + let pattern = Pattern::from_hir(cx.tcx, &local.pat); result.push(StmtRef::Mirror(Box::new(Stmt { span: stmt.span, kind: StmtKind::Let { diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index f23c9d3caf096..45c49a4627ff6 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -657,7 +657,7 @@ fn to_borrow_kind(m: hir::Mutability) -> BorrowKind { fn convert_arm<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, arm: &'tcx hir::Arm) -> Arm<'tcx> { Arm { - patterns: arm.pats.iter().map(|p| cx.refutable_pat(p)).collect(), + patterns: arm.pats.iter().map(|p| Pattern::from_hir(cx.tcx, p)).collect(), guard: arm.guard.to_ref(), body: arm.body.to_ref(), } diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 9fe3addea4127..f87e0acaa4ca1 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -196,5 +196,4 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { mod block; mod expr; -mod pattern; mod to_ref; diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index 59137e2bcd78f..49a592b07fb4f 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -14,9 +14,7 @@ //! unit-tested and separated from the Rust source and compiler data //! structures. -use rustc::mir::repr::{BinOp, BorrowKind, Field, Literal, Mutability, UnOp, - TypedConstVal}; -use rustc::middle::const_val::ConstVal; +use rustc::mir::repr::{BinOp, BorrowKind, Field, Literal, UnOp, TypedConstVal}; use rustc::hir::def_id::DefId; use rustc::middle::region::CodeExtent; use rustc::ty::subst::Substs; @@ -28,6 +26,8 @@ use self::cx::Cx; pub mod cx; +pub use rustc_const_eval::pattern::{BindingMode, Pattern, PatternKind, FieldPattern}; + #[derive(Clone, Debug)] pub struct Block<'tcx> { pub extent: CodeExtent, @@ -266,86 +266,12 @@ pub struct Arm<'tcx> { pub body: ExprRef<'tcx>, } -#[derive(Clone, Debug)] -pub struct Pattern<'tcx> { - pub ty: Ty<'tcx>, - pub span: Span, - pub kind: Box>, -} - #[derive(Copy, Clone, Debug)] pub enum LogicalOp { And, Or, } -#[derive(Clone, Debug)] -pub enum PatternKind<'tcx> { - Wild, - - /// x, ref x, x @ P, etc - Binding { - mutability: Mutability, - name: ast::Name, - mode: BindingMode<'tcx>, - var: ast::NodeId, - ty: Ty<'tcx>, - subpattern: Option>, - }, - - /// Foo(...) or Foo{...} or Foo, where `Foo` is a variant name from an adt with >1 variants - Variant { - adt_def: AdtDef<'tcx>, - variant_index: usize, - subpatterns: Vec>, - }, - - /// (...), Foo(...), Foo{...}, or Foo, where `Foo` is a variant name from an adt with 1 variant - Leaf { - subpatterns: Vec>, - }, - - /// box P, &P, &mut P, etc - Deref { - subpattern: Pattern<'tcx>, - }, - - Constant { - value: ConstVal, - }, - - Range { - lo: Literal<'tcx>, - hi: Literal<'tcx>, - }, - - /// matches against a slice, checking the length and extracting elements - Slice { - prefix: Vec>, - slice: Option>, - suffix: Vec>, - }, - - /// fixed match against an array, irrefutable - Array { - prefix: Vec>, - slice: Option>, - suffix: Vec>, - }, -} - -#[derive(Copy, Clone, Debug)] -pub enum BindingMode<'tcx> { - ByValue, - ByRef(&'tcx Region, BorrowKind), -} - -#[derive(Clone, Debug)] -pub struct FieldPattern<'tcx> { - pub field: Field, - pub pattern: Pattern<'tcx>, -} - /////////////////////////////////////////////////////////////////////////// // The Mirror trait From 48387c8bd9b672c3640299d191969f7dc1e5a490 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sat, 24 Sep 2016 18:23:46 +0300 Subject: [PATCH 05/13] refactor the `pat_is_catchall` logic --- src/librustc_const_eval/check_match.rs | 40 +++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index f8ac841a0a265..7878ffff48d3b 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -74,21 +74,6 @@ impl<'a, 'tcx> Pattern<'a, 'tcx> { return pat; } - - /// Checks for common cases of "catchall" patterns that may not be intended as such. - fn is_catchall(self, dm: &DefMap) -> bool { - fn is_catchall(dm: &DefMap, pat: &Pat) -> bool { - match pat.node { - PatKind::Binding(.., None) => true, - PatKind::Binding(.., Some(ref s)) => is_catchall(dm, s), - PatKind::Ref(ref s, _) => is_catchall(dm, s), - PatKind::Tuple(ref v, _) => v.iter().all(|p|is_catchall(dm, &p)), - _ => false - } - } - is_catchall(dm, self.pat) - } - fn span(self) -> Span { self.pat.span } @@ -339,11 +324,25 @@ fn check_for_static_nan(cx: &MatchCheckCtxt, pat: &Pat) { }); } +/// Checks for common cases of "catchall" patterns that may not be intended as such. +fn pat_is_catchall(dm: &DefMap, pat: &Pat) -> bool { + match pat.node { + PatKind::Binding(.., None) => true, + PatKind::Binding(.., Some(ref s)) => pat_is_catchall(dm, s), + PatKind::Ref(ref s, _) => pat_is_catchall(dm, s), + PatKind::Tuple(ref v, _) => v.iter().all(|p| { + pat_is_catchall(dm, &p) + }), + _ => false + } +} + // Check for unreachable patterns fn check_arms(cx: &MatchCheckCtxt, arms: &[(Vec>, Option<&hir::Expr>)], source: hir::MatchSource) { let mut seen = Matrix(vec![]); + let mut catchall = None; let mut printed_if_let_err = false; for &(ref pats, guard) in arms { for pat in pats { @@ -393,11 +392,8 @@ fn check_arms(cx: &MatchCheckCtxt, "unreachable pattern"); err.span_label(pat.span, &format!("this is an unreachable pattern")); // if we had a catchall pattern, hint at that - for row in &seen.0 { - if row[0].is_catchall(&cx.tcx.def_map.borrow()) { - span_note!(err, row[0].span(), - "this pattern matches any value"); - } + if let Some(catchall) = catchall { + err.span_note(catchall, "this pattern matches any value"); } err.emit(); }, @@ -414,6 +410,10 @@ fn check_arms(cx: &MatchCheckCtxt, let Matrix(mut rows) = seen; rows.push(v); seen = Matrix(rows); + if catchall.is_none() && pat_is_catchall(&cx.tcx.def_map.borrow(), pat) { + catchall = Some(pat.span); + } + } } } From abae5e7e258f256cbdb4fa2a58f58ecb095ba626 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sat, 24 Sep 2016 18:24:34 +0300 Subject: [PATCH 06/13] 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 0000000000000..dd894de96eefb --- /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 7878ffff48d3b..e87f616bef28b 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 9cdc76f25a63f..c2b39625e2eee 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 9a97df9669600..cbdb808a12747 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; From 04a92a1f567ec39558235d0c8ecf12de5d297139 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sat, 24 Sep 2016 20:45:59 +0300 Subject: [PATCH 07/13] un-break the `construct_witness` logic Fixes #35609. --- src/librustc_const_eval/_match.rs | 333 ++++++++++++--------- src/librustc_const_eval/check_match.rs | 4 +- src/test/ui/check_match/issue-35609.rs | 53 ++++ src/test/ui/check_match/issue-35609.stderr | 50 ++++ 4 files changed, 290 insertions(+), 150 deletions(-) create mode 100644 src/test/ui/check_match/issue-35609.rs create mode 100644 src/test/ui/check_match/issue-35609.stderr diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index dd894de96eefb..2a11f22ca59c6 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -171,7 +171,7 @@ impl Constructor { #[derive(Clone, PartialEq)] pub enum Usefulness { Useful, - UsefulWithWitness(Vec>), + UsefulWithWitness(Vec), NotUseful } @@ -181,6 +181,13 @@ pub enum WitnessPreference { LeaveOutWitness } +#[derive(Copy, Clone, Debug)] +struct PatternContext<'tcx> { + ty: Ty<'tcx>, + max_slice_length: usize, +} + + fn const_val_to_expr(value: &ConstVal) -> P { let node = match value { &ConstVal::Bool(b) => ast::LitKind::Bool(b), @@ -194,93 +201,126 @@ fn const_val_to_expr(value: &ConstVal) -> P { }) } -/// 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), +/// A stack of patterns in reverse order of construction +#[derive(Clone, PartialEq, Eq)] +pub struct Witness(Vec>); - 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)) - } - } - } +impl Witness { + pub fn single_pattern(&self) -> &Pat { + assert_eq!(self.0.len(), 1); + &self.0[0] + } - ty::TyRef(_, ty::TypeAndMut { mutbl, .. }) => { - assert_eq!(pats_len, 1); - PatKind::Ref(pats.nth(0).unwrap(), mutbl) - } + fn push_wild_constructor<'a, 'tcx>( + mut self, + cx: &MatchCheckCtxt<'a, 'tcx>, + ctor: &Constructor, + ty: Ty<'tcx>) + -> Self + { + let arity = constructor_arity(cx, ctor, ty); + self.0.extend(repeat(DUMMY_WILD_PAT).take(arity).map(|p| P(p.clone()))); + self.apply_constructor(cx, ctor, ty) + } - 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()) - } + /// 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 apply_constructor<'a, 'tcx>( + mut self, + cx: &MatchCheckCtxt<'a,'tcx>, + ctor: &Constructor, + ty: Ty<'tcx>) + -> Self + { + let arity = constructor_arity(cx, ctor, ty); + let pat = { + let len = self.0.len(); + let mut pats = self.0.drain(len-arity..).rev(); + + match 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() < arity; + 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, .. }) => { + PatKind::Ref(pats.nth(0).unwrap(), mutbl) + } + + ty::TySlice(_) | ty::TyArray(..) => { + PatKind::Slice(pats.collect(), None, hir::HirVec::new()) + } - _ => { - match *ctor { - ConstantValue(ref v) => PatKind::Lit(const_val_to_expr(v)), - _ => PatKind::Wild, + _ => { + 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 - }) + self.0.push(P(hir::Pat { + id: DUMMY_NODE_ID, + node: pat, + span: DUMMY_SP + })); + + self + } } -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)) +/// Return the set of constructors from the same type as the first column of `matrix`, +/// that are matched only by wildcard patterns from that first column. +/// +/// Therefore, if there is some pattern that is unmatched by `matrix`, it will +/// still be unmatched if the first constructor is replaced by any of the constructors +/// in the return value. +fn missing_constructors(cx: &MatchCheckCtxt, matrix: &Matrix, + pcx: PatternContext) -> Vec { + let used_constructors: Vec = + matrix.0.iter() + .flat_map(|row| pat_constructors(cx, row[0], pcx).unwrap_or(vec![])) .collect(); - all_constructors(cx, left_ty, max_slice_length) - .into_iter() + all_constructors(cx, pcx).into_iter() .filter(|c| !used_constructors.contains(c)) .collect() } @@ -289,13 +329,12 @@ fn missing_constructors(cx: &MatchCheckCtxt, &Matrix(ref rows): &Matrix, /// 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 { +fn all_constructors(_cx: &MatchCheckCtxt, pcx: PatternContext) -> Vec { + match pcx.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(), + (0..pcx.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] @@ -324,7 +363,9 @@ pub fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, debug!("is_useful({:?}, {:?})", matrix, v); if rows.is_empty() { return match witness { - ConstructWitness => UsefulWithWitness(vec!()), + ConstructWitness => UsefulWithWitness(vec![Witness( + repeat(DUMMY_WILD_PAT).take(v.len()).map(|p| P(p.clone())).collect() + )]), LeaveOutWitness => Useful }; } @@ -332,44 +373,37 @@ pub fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, 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 pcx = PatternContext { + 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 + } + }, + 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 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); + debug!("is_useful: pcx={:?}, expanding {:?}", pcx, v[0]); - 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); + if let Some(constructors) = pat_constructors(cx, v[0], pcx) { + debug!("is_useful - expanding constructors: {:?}", constructors); + constructors.into_iter().map(|c| + is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness) + ).find(|result| result != &NotUseful).unwrap_or(NotUseful) + } else { + debug!("is_useful - expanding wildcard"); + let constructors = missing_constructors(cx, matrix, pcx); 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 - } + all_constructors(cx, pcx).into_iter().map(|c| { + is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness) }).find(|result| result != &NotUseful).unwrap_or(NotUseful) } else { let matrix = rows.iter().filter_map(|r| { @@ -380,21 +414,15 @@ pub fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, }).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) - }, + UsefulWithWitness(pats.into_iter().flat_map(|witness| { + constructors.iter().map(move |ctor| { + witness.clone().push_wild_constructor(cx, ctor, pcx.ty) + }) + }).collect()) + } 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) } } @@ -411,7 +439,14 @@ fn is_useful_specialized<'a, 'tcx>( specialize(cx, &r[..], &ctor, 0, arity) }).collect()); match specialize(cx, v, &ctor, 0, arity) { - Some(v) => is_useful(cx, &matrix, &v[..], witness), + Some(v) => match is_useful(cx, &matrix, &v[..], witness) { + UsefulWithWitness(witnesses) => UsefulWithWitness( + witnesses.into_iter() + .map(|witness| witness.apply_constructor(cx, &ctor, lty)) + .collect() + ), + result => result + }, None => NotUseful } } @@ -423,41 +458,43 @@ fn is_useful_specialized<'a, 'tcx>( /// 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 { +/// Returns None in case of a catch-all, which can't be specialized. +fn pat_constructors(cx: &MatchCheckCtxt, + p: Pattern, + pcx: PatternContext) + -> Option> +{ 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::Variant(id) | Def::VariantCtor(id, _) => Some(vec![Variant(id)]), Def::Struct(..) | Def::StructCtor(..) | Def::Union(..) | - Def::TyAlias(..) | Def::AssociatedTy(..) => vec![Single], + Def::TyAlias(..) | Def::AssociatedTy(..) => Some(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))], + Some(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))], + Some(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], + match pcx.ty.sty { + ty::TyArray(..) => Some(vec![Single]), ty::TySlice(_) if slice.is_some() => { - (before.len() + after.len()..max_slice_length+1) - .map(|length| Slice(length)) - .collect() + Some((before.len() + after.len()..pcx.max_slice_length+1) + .map(|length| Slice(length)) + .collect()) } - ty::TySlice(_) => vec!(Slice(before.len() + after.len())), + ty::TySlice(_) => Some(vec!(Slice(before.len() + after.len()))), _ => span_bug!(pat.span, "pat_constructors: unexpected \ - slice pattern type {:?}", left_ty) + slice pattern type {:?}", pcx.ty) }, PatKind::Box(..) | PatKind::Tuple(..) | PatKind::Ref(..) => - vec![Single], + Some(vec![Single]), PatKind::Binding(..) | PatKind::Wild => - vec![], + None, } } @@ -466,7 +503,7 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: Pattern, /// /// 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 { +fn constructor_arity(_cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> usize { debug!("constructor_arity({:?}, {:?})", ctor, ty); match ty.sty { ty::TyTuple(ref fs) => fs.len(), @@ -685,7 +722,7 @@ fn specialize<'a, 'b, 'tcx>( pub fn is_refutable(cx: &MatchCheckCtxt, pat: &Pat, refutable: F) -> Option where - F: FnOnce(&Pat) -> A, + F: FnOnce(&Witness) -> A, { let pats = Matrix(vec!(vec!(wrap_pat(cx, pat)))); match is_useful(cx, &pats, &[DUMMY_WILD_PATTERN], ConstructWitness) { diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index e87f616bef28b..d7ce3253af546 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -297,7 +297,7 @@ fn check_exhaustive<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, let witnesses = if pats.is_empty() { vec![DUMMY_WILD_PAT] } else { - pats.iter().map(|w| &**w).collect() + pats.iter().map(|w| w.single_pattern()).collect() }; match source { hir::MatchSource::ForLoopDesugar => { @@ -484,7 +484,7 @@ fn check_irrefutable(cx: &MatchCheckCtxt, pat: &Pat, is_fn_arg: bool) { }; is_refutable(cx, pat, |uncovered_pat| { - let pattern_string = pat_to_string(uncovered_pat); + let pattern_string = pat_to_string(uncovered_pat.single_pattern()); struct_span_err!(cx.tcx.sess, pat.span, E0005, "refutable pattern in {}: `{}` not covered", origin, diff --git a/src/test/ui/check_match/issue-35609.rs b/src/test/ui/check_match/issue-35609.rs new file mode 100644 index 0000000000000..6497f69035dec --- /dev/null +++ b/src/test/ui/check_match/issue-35609.rs @@ -0,0 +1,53 @@ +// Copyright 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. + +enum Enum { + A, B, C, D, E, F +} +use Enum::*; + +struct S(Enum, ()); +struct Sd { x: Enum, y: () } + +fn main() { + match (A, ()) { + (A, _) => {} + } + + match (A, A) { + (_, A) => {} + } + + match ((A, ()), ()) { + ((A, ()), _) => {} + } + + match ((A, ()), A) { + ((A, ()), _) => {} + } + + match ((A, ()), ()) { + ((A, _), _) => {} + } + + + match S(A, ()) { + S(A, _) => {} + } + + match (Sd { x: A, y: () }) { + Sd { x: A, y: _ } => {} + } + + match Some(A) { + Some(A) => (), + None => () + } +} diff --git a/src/test/ui/check_match/issue-35609.stderr b/src/test/ui/check_match/issue-35609.stderr new file mode 100644 index 0000000000000..66069c7a86a34 --- /dev/null +++ b/src/test/ui/check_match/issue-35609.stderr @@ -0,0 +1,50 @@ +error[E0004]: non-exhaustive patterns: `(B, _)`, `(C, _)`, `(D, _)` and 2 more not covered + --> $DIR/issue-35609.rs:20:11 + | +20 | match (A, ()) { + | ^^^^^^^ patterns `(B, _)`, `(C, _)`, `(D, _)` and 2 more not covered + +error[E0004]: non-exhaustive patterns: `(A, B)`, `(B, B)`, `(C, B)` and 27 more not covered + --> $DIR/issue-35609.rs:24:11 + | +24 | match (A, A) { + | ^^^^^^ patterns `(A, B)`, `(B, B)`, `(C, B)` and 27 more not covered + +error[E0004]: non-exhaustive patterns: `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered + --> $DIR/issue-35609.rs:28:11 + | +28 | match ((A, ()), ()) { + | ^^^^^^^^^^^^^ patterns `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered + +error[E0004]: non-exhaustive patterns: `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered + --> $DIR/issue-35609.rs:32:11 + | +32 | match ((A, ()), A) { + | ^^^^^^^^^^^^ patterns `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered + +error[E0004]: non-exhaustive patterns: `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered + --> $DIR/issue-35609.rs:36:11 + | +36 | match ((A, ()), ()) { + | ^^^^^^^^^^^^^ patterns `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered + +error[E0004]: non-exhaustive patterns: `S(B, _)`, `S(C, _)`, `S(D, _)` and 2 more not covered + --> $DIR/issue-35609.rs:41:11 + | +41 | match S(A, ()) { + | ^^^^^^^^ patterns `S(B, _)`, `S(C, _)`, `S(D, _)` and 2 more not covered + +error[E0004]: non-exhaustive patterns: `Sd { x: B, .. }`, `Sd { x: C, .. }`, `Sd { x: D, .. }` and 2 more not covered + --> $DIR/issue-35609.rs:45:11 + | +45 | match (Sd { x: A, y: () }) { + | ^^^^^^^^^^^^^^^^^^^^ patterns `Sd { x: B, .. }`, `Sd { x: C, .. }`, `Sd { x: D, .. }` and 2 more not covered + +error[E0004]: non-exhaustive patterns: `Some(B)`, `Some(C)`, `Some(D)` and 2 more not covered + --> $DIR/issue-35609.rs:49:11 + | +49 | match Some(A) { + | ^^^^^^^ patterns `Some(B)`, `Some(C)`, `Some(D)` and 2 more not covered + +error: aborting due to 8 previous errors + From e313d8b290ddd129f35ce0a6ae319bbd2c19a73a Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Mon, 26 Sep 2016 02:53:26 +0300 Subject: [PATCH 08/13] change match checking to use HAIR no intended functional changes --- src/Cargo.lock | 1 + src/librustc_const_eval/Cargo.toml | 1 + src/librustc_const_eval/_match.rs | 439 ++++++++---------- src/librustc_const_eval/check_match.rs | 17 +- src/librustc_const_eval/lib.rs | 1 + src/librustc_const_eval/pattern.rs | 172 ++++++- src/librustc_mir/build/matches/test.rs | 4 +- .../const-pattern-not-const-evaluable.rs | 8 +- 8 files changed, 383 insertions(+), 260 deletions(-) diff --git a/src/Cargo.lock b/src/Cargo.lock index d12b95d8b22e3..683ba90adf94a 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -330,6 +330,7 @@ dependencies = [ name = "rustc_const_eval" version = "0.0.0" dependencies = [ + "arena 0.0.0", "graphviz 0.0.0", "log 0.0.0", "rustc 0.0.0", diff --git a/src/librustc_const_eval/Cargo.toml b/src/librustc_const_eval/Cargo.toml index ec4d2fdf967d6..0e5cbce8639be 100644 --- a/src/librustc_const_eval/Cargo.toml +++ b/src/librustc_const_eval/Cargo.toml @@ -9,6 +9,7 @@ path = "lib.rs" crate-type = ["dylib"] [dependencies] +arena = { path = "../libarena" } log = { path = "../liblog" } serialize = { path = "../libserialize" } rustc = { path = "../librustc" } diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index 2a11f22ca59c6..ab2a7b01bb240 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -13,20 +13,20 @@ use self::Usefulness::*; use self::WitnessPreference::*; use rustc::middle::const_val::ConstVal; -use eval::{eval_const_expr, compare_const_vals}; +use eval::{compare_const_vals}; + +use rustc_data_structures::indexed_vec::Idx; + +use pattern::{FieldPattern, Pattern, PatternKind}; +use pattern::{PatternFoldable, PatternFolder}; -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::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::hir; +use rustc::hir::def::CtorKind; use rustc::hir::{Pat, PatKind}; -use rustc::hir::print::pat_to_string; use rustc::util::common::ErrorReported; use syntax::ast::{self, DUMMY_NODE_ID}; @@ -34,53 +34,69 @@ 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 -}; +use arena::TypedArena; -pub const DUMMY_WILD_PATTERN : Pattern<'static, 'static> = Pattern { - pat: DUMMY_WILD_PAT, - pattern_ty: None -}; +use std::cmp::Ordering; +use std::fmt; +use std::iter::{FromIterator, IntoIterator, repeat}; -#[derive(Copy, Clone)] -pub struct Pattern<'a, 'tcx> { - pat: &'a Pat, - pattern_ty: Option> +pub fn lower_pat<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: &Pat) + -> &'a Pattern<'tcx> +{ + cx.pattern_arena.alloc( + LiteralExpander.fold_pattern(&Pattern::from_hir(cx.tcx, pat)) + ) } -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; +struct LiteralExpander; +impl<'tcx> PatternFolder<'tcx> for LiteralExpander { + fn fold_pattern(&mut self, pat: &Pattern<'tcx>) -> Pattern<'tcx> { + match (&pat.ty.sty, &*pat.kind) { + (&ty::TyRef(_, mt), &PatternKind::Constant { ref value }) => { + Pattern { + ty: pat.ty, + span: pat.span, + kind: box PatternKind::Deref { + subpattern: Pattern { + ty: mt.ty, + span: pat.span, + kind: box PatternKind::Constant { value: value.clone() }, + } + } + } + } + (_, &PatternKind::Binding { subpattern: Some(ref s), .. }) => { + s.fold_with(self) + } + _ => pat.super_fold_with(self) } - - 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 const DUMMY_WILD_PAT: &'static Pat = &Pat { + id: DUMMY_NODE_ID, + node: PatKind::Wild, + span: DUMMY_SP +}; + +impl<'tcx> Pattern<'tcx> { + fn is_wildcard(&self) -> bool { + match *self.kind { + PatternKind::Binding { subpattern: None, .. } | PatternKind::Wild => + true, + _ => false + } } } -pub struct Matrix<'a, 'tcx>(Vec>>); +pub struct Matrix<'a, 'tcx: 'a>(Vec>>); impl<'a, 'tcx> Matrix<'a, 'tcx> { pub fn empty() -> Self { Matrix(vec![]) } - pub fn push(&mut self, row: Vec>) { + pub fn push(&mut self, row: Vec<&'a Pattern<'tcx>>) { self.0.push(row) } } @@ -129,8 +145,8 @@ impl<'a, 'tcx> fmt::Debug for Matrix<'a, 'tcx> { } } -impl<'a, 'tcx> FromIterator>> for Matrix<'a, 'tcx> { - fn from_iter>>>(iter: T) -> Self +impl<'a, 'tcx> FromIterator>> for Matrix<'a, 'tcx> { + fn from_iter>>>(iter: T) -> Self { Matrix(iter.into_iter().collect()) } @@ -140,6 +156,34 @@ impl<'a, 'tcx> FromIterator>> for Matrix<'a, 'tcx> { pub struct MatchCheckCtxt<'a, 'tcx: 'a> { pub tcx: TyCtxt<'a, 'tcx, 'tcx>, pub param_env: ty::ParameterEnvironment<'tcx>, + /// A wild pattern with an error type - it exists to avoid having to normalize + /// associated types to get field types. + pub wild_pattern: &'a Pattern<'tcx>, + pub pattern_arena: &'a TypedArena>, +} + +impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { + pub fn create_and_enter( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: ty::ParameterEnvironment<'tcx>, + f: F) -> R + where F: for<'b> FnOnce(MatchCheckCtxt<'b, 'tcx>) -> R + { + let wild_pattern = Pattern { + ty: tcx.types.err, + span: DUMMY_SP, + kind: box PatternKind::Wild + }; + + let pattern_arena = TypedArena::new(); + + f(MatchCheckCtxt { + tcx: tcx, + param_env: param_env, + wild_pattern: &wild_pattern, + pattern_arena: &pattern_arena, + }) + } } #[derive(Clone, Debug, PartialEq)] @@ -163,7 +207,11 @@ impl Constructor { -> &'a ty::VariantDefData<'tcx, 'container> { match self { &Variant(vid) => adt.variant_with_id(vid), - _ => adt.struct_variant() + &Single => { + assert_eq!(adt.variants.len(), 1); + &adt.variants[0] + } + _ => bug!("bad constructor {:?} for adt {:?}", self, adt) } } } @@ -320,6 +368,7 @@ fn missing_constructors(cx: &MatchCheckCtxt, matrix: &Matrix, matrix.0.iter() .flat_map(|row| pat_constructors(cx, row[0], pcx).unwrap_or(vec![])) .collect(); + debug!("used_constructors = {:?}", used_constructors); all_constructors(cx, pcx).into_iter() .filter(|c| !used_constructors.contains(c)) .collect() @@ -335,28 +384,28 @@ fn all_constructors(_cx: &MatchCheckCtxt, pcx: PatternContext) -> Vec (0..pcx.max_slice_length+1).map(|length| Slice(length)).collect(), - ty::TyAdt(def, _) if def.is_enum() => + ty::TyAdt(def, _) if def.is_enum() && def.variants.len() > 1 => 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. +/// 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>], + v: &[&'a Pattern<'tcx>], witness: WitnessPreference) -> Usefulness { let &Matrix(ref rows) = matrix; @@ -375,17 +424,11 @@ pub fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, assert!(rows.iter().all(|r| r.len() == v.len())); let pcx = PatternContext { - 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 - } - }, - max_slice_length: rows.iter().filter_map(|row| match row[0].pat.node { - PatKind::Slice(ref before, _, ref after) => Some(before.len() + after.len()), + ty: rows.iter().map(|r| r[0].ty).find(|ty| !ty.references_error()) + .unwrap_or(v[0].ty), + max_slice_length: rows.iter().filter_map(|row| match *row[0].kind { + PatternKind::Slice { ref prefix, slice: _, ref suffix } => + Some(prefix.len() + suffix.len()), _ => None }).max().map_or(0, |v| v + 1) }; @@ -407,9 +450,10 @@ pub fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, }).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, + if r[0].is_wildcard() { + Some(r[1..].to_vec()) + } else { + None } }).collect(); match is_useful(cx, &matrix, &v[1..], witness) { @@ -429,7 +473,7 @@ pub fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, fn is_useful_specialized<'a, 'tcx>( cx: &MatchCheckCtxt<'a, 'tcx>, &Matrix(ref m): &Matrix<'a, 'tcx>, - v: &[Pattern<'a, 'tcx>], + v: &[&'a Pattern<'tcx>], ctor: Constructor, lty: Ty<'tcx>, witness: WitnessPreference) -> Usefulness @@ -459,42 +503,30 @@ fn is_useful_specialized<'a, 'tcx>( /// `[a, b, ..tail]` can match a slice of length 2, 3, 4 and so on. /// /// Returns None in case of a catch-all, which can't be specialized. -fn pat_constructors(cx: &MatchCheckCtxt, - p: Pattern, +fn pat_constructors(_cx: &MatchCheckCtxt, + pat: &Pattern, pcx: PatternContext) -> Option> { - 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, _) => Some(vec![Variant(id)]), - Def::Struct(..) | Def::StructCtor(..) | Def::Union(..) | - Def::TyAlias(..) | Def::AssociatedTy(..) => Some(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) => - Some(vec![ConstantValue(eval_const_expr(cx.tcx, &expr))]), - PatKind::Range(ref lo, ref hi) => - Some(vec![ConstantRange(eval_const_expr(cx.tcx, &lo), eval_const_expr(cx.tcx, &hi))]), - PatKind::Slice(ref before, ref slice, ref after) => - match pcx.ty.sty { - ty::TyArray(..) => Some(vec![Single]), - ty::TySlice(_) if slice.is_some() => { - Some((before.len() + after.len()..pcx.max_slice_length+1) - .map(|length| Slice(length)) - .collect()) - } - ty::TySlice(_) => Some(vec!(Slice(before.len() + after.len()))), - _ => span_bug!(pat.span, "pat_constructors: unexpected \ - slice pattern type {:?}", pcx.ty) - }, - PatKind::Box(..) | PatKind::Tuple(..) | PatKind::Ref(..) => - Some(vec![Single]), - PatKind::Binding(..) | PatKind::Wild => + match *pat.kind { + PatternKind::Binding { .. } | PatternKind::Wild => None, + PatternKind::Leaf { .. } | PatternKind::Deref { .. } | PatternKind::Array { .. } => + Some(vec![Single]), + PatternKind::Variant { adt_def, variant_index, .. } => + Some(vec![Variant(adt_def.variants[variant_index].did)]), + PatternKind::Constant { ref value } => + Some(vec![ConstantValue(value.clone())]), + PatternKind::Range { ref lo, ref hi } => + Some(vec![ConstantRange(lo.clone(), hi.clone())]), + PatternKind::Slice { ref prefix, ref slice, ref suffix } => { + let pat_len = prefix.len() + suffix.len(); + if slice.is_some() { + Some((pat_len..pcx.max_slice_length+1).map(Slice).collect()) + } else { + Some(vec![Slice(pat_len)]) + } + } } } @@ -540,20 +572,20 @@ fn range_covered_by_constructor(tcx: TyCtxt, span: Span, 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> +fn patterns_for_variant<'a, 'tcx>( + cx: &MatchCheckCtxt<'a, 'tcx>, + subpatterns: &'a [FieldPattern<'tcx>], + arity: usize) + -> Vec<&'a Pattern<'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 - }) + let mut result = vec![cx.wild_pattern; arity]; + + for subpat in subpatterns { + result[subpat.field.index()] = &subpat.pattern; } + + debug!("patterns_for_variant({:?}, {:?}) = {:?}", subpatterns, arity, result); + result } /// This is the main specialization step. It expands the first pattern in the given row @@ -564,118 +596,44 @@ pub fn wrap_pat<'a, 'b, 'tcx>(cx: &MatchCheckCtxt<'b, 'tcx>, /// 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>], +fn specialize<'a, 'tcx>( + cx: &MatchCheckCtxt<'a, 'tcx>, + r: &[&'a Pattern<'tcx>], constructor: &Constructor, col: usize, arity: usize) - -> Option>> + -> 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), - } - } + let pat = &r[col]; - 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) - } - } + let head: Option> = match *pat.kind { + PatternKind::Binding { .. } | PatternKind::Wild => + Some(vec![cx.wild_pattern; arity]), - 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()) + PatternKind::Variant { adt_def, variant_index, ref subpatterns } => { + let ref variant = adt_def.variants[variant_index]; + if *constructor == Variant(variant.did) { + Some(patterns_for_variant(cx, subpatterns, arity)) } 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) + PatternKind::Leaf { ref subpatterns } => Some(patterns_for_variant(cx, subpatterns, arity)), + PatternKind::Deref { ref subpattern } => Some(vec![subpattern]), + + PatternKind::Constant { ref value } => { + assert_eq!(constructor_arity(cx, constructor, pat.ty), 0); + match range_covered_by_constructor( + cx.tcx, pat.span, constructor, value, value + ) { + Ok(true) => Some(vec![]), + Ok(false) => None, + Err(ErrorReported) => None, } } - PatKind::Range(ref from, ref to) => { - let from_value = eval_const_expr(cx.tcx, &from); - let to_value = eval_const_expr(cx.tcx, &to); + PatternKind::Range { ref lo, ref hi } => { match range_covered_by_constructor( - cx.tcx, pat_span, constructor, &from_value, &to_value + cx.tcx, pat.span, constructor, lo, hi ) { Ok(true) => Some(vec![]), Ok(false) => None, @@ -683,31 +641,29 @@ fn specialize<'a, 'b, 'tcx>( } } - 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() => { + PatternKind::Array { ref prefix, slice: _, ref suffix } => { + let pat_len = prefix.len() + suffix.len(); + Some( + prefix.iter().chain( + repeat(cx.wild_pattern).take(arity - pat_len).chain( + suffix.iter() + )).collect()) + } + + PatternKind::Slice { ref prefix, ref slice, ref suffix } => { + let pat_len = prefix.len() + suffix.len(); + if let Some(slice_count) = arity.checked_sub(pat_len) { + if slice_count == 0 || 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)) + prefix.iter().chain( + repeat(cx.wild_pattern).take(slice_count).chain( + suffix.iter() )).collect()) + } else { + None } - Slice(length) if pat_len == length => { - Some( - before.iter().map(|p| wpat(p)).chain( - after.iter().map(|p| wpat(p)) - ).collect()) - } - _ => None + } else { + None } } }; @@ -720,12 +676,15 @@ fn specialize<'a, 'b, 'tcx>( }) } -pub fn is_refutable(cx: &MatchCheckCtxt, pat: &Pat, refutable: F) - -> Option where +pub fn is_refutable<'a, 'tcx, A, F>( + cx: &MatchCheckCtxt<'a, 'tcx>, + pat: &'a Pattern<'tcx>, + refutable: F) + -> Option where F: FnOnce(&Witness) -> A, { - let pats = Matrix(vec!(vec!(wrap_pat(cx, pat)))); - match is_useful(cx, &pats, &[DUMMY_WILD_PATTERN], ConstructWitness) { + let pats = Matrix(vec![vec![pat]]); + match is_useful(cx, &pats, &[cx.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 d7ce3253af546..0ae13d31a569d 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use _match::{MatchCheckCtxt, Matrix, wrap_pat, is_refutable, is_useful}; -use _match::{DUMMY_WILD_PATTERN, DUMMY_WILD_PAT}; +use _match::{MatchCheckCtxt, Matrix, lower_pat, is_refutable, is_useful}; +use _match::{DUMMY_WILD_PAT}; use _match::Usefulness::*; use _match::WitnessPreference::*; @@ -58,9 +58,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MatchCheckCtxt<'a, 'tcx> { } pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { - tcx.visit_all_items_in_krate(DepNode::MatchCheck, &mut MatchCheckCtxt { - tcx: tcx, - param_env: tcx.empty_parameter_environment(), + MatchCheckCtxt::create_and_enter(tcx, tcx.empty_parameter_environment(), |mut cx| { + tcx.visit_all_items_in_krate(DepNode::MatchCheck, &mut cx); }); tcx.sess.abort_if_errors(); } @@ -138,7 +137,7 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &hir::Expr) { .iter() .filter(|&&(_, guard)| guard.is_none()) .flat_map(|arm| &arm.0) - .map(|pat| vec![wrap_pat(cx, &pat)]) + .map(|pat| vec![lower_pat(cx, &pat)]) .collect(); check_exhaustive(cx, scrut.span, &matrix, source); }, @@ -218,7 +217,7 @@ fn check_arms(cx: &MatchCheckCtxt, let mut printed_if_let_err = false; for &(ref pats, guard) in arms { for pat in pats { - let v = vec![wrap_pat(cx, &pat)]; + let v = vec![lower_pat(cx, &pat)]; match is_useful(cx, &seen, &v[..], LeaveOutWitness) { NotUseful => { @@ -292,7 +291,7 @@ fn check_exhaustive<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, sp: Span, matrix: &Matrix<'a, 'tcx>, source: hir::MatchSource) { - match is_useful(cx, matrix, &[DUMMY_WILD_PATTERN], ConstructWitness) { + match is_useful(cx, matrix, &[cx.wild_pattern], ConstructWitness) { UsefulWithWitness(pats) => { let witnesses = if pats.is_empty() { vec![DUMMY_WILD_PAT] @@ -483,7 +482,7 @@ fn check_irrefutable(cx: &MatchCheckCtxt, pat: &Pat, is_fn_arg: bool) { "local binding" }; - is_refutable(cx, pat, |uncovered_pat| { + is_refutable(cx, &lower_pat(cx, pat), |uncovered_pat| { let pattern_string = pat_to_string(uncovered_pat.single_pattern()); struct_span_err!(cx.tcx.sess, pat.span, E0005, "refutable pattern in {}: `{}` not covered", diff --git a/src/librustc_const_eval/lib.rs b/src/librustc_const_eval/lib.rs index cbdb808a12747..1a07ece044ff2 100644 --- a/src/librustc_const_eval/lib.rs +++ b/src/librustc_const_eval/lib.rs @@ -31,6 +31,7 @@ #![feature(box_patterns)] #![feature(box_syntax)] +extern crate arena; #[macro_use] extern crate syntax; #[macro_use] extern crate log; #[macro_use] extern crate rustc; diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs index b32c78b31bb36..715659f5885b5 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_const_eval/pattern.rs @@ -11,7 +11,7 @@ use eval; use rustc::middle::const_val::ConstVal; -use rustc::mir::repr::{Field, Literal, BorrowKind, Mutability}; +use rustc::mir::repr::{Field, BorrowKind, Mutability}; use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region}; use rustc::hir::{self, PatKind}; use rustc::hir::def::Def; @@ -78,8 +78,8 @@ pub enum PatternKind<'tcx> { }, Range { - lo: Literal<'tcx>, - hi: Literal<'tcx>, + lo: ConstVal, + hi: ConstVal, }, /// matches against a slice, checking the length and extracting elements @@ -111,9 +111,7 @@ impl<'a, 'gcx, 'tcx> Pattern<'tcx> { PatKind::Range(ref lo, ref hi) => { let lo = eval::eval_const_expr(tcx.global_tcx(), lo); - let lo = Literal::Value { value: lo }; let hi = eval::eval_const_expr(tcx.global_tcx(), hi); - let hi = Literal::Value { value: hi }; PatternKind::Range { lo: lo, hi: hi } }, @@ -375,3 +373,167 @@ impl<'a, 'gcx, 'tcx> PatternKind<'tcx> { } } } + +pub trait PatternFoldable<'tcx> : Sized { + fn fold_with>(&self, folder: &mut F) -> Self { + self.super_fold_with(folder) + } + + fn super_fold_with>(&self, folder: &mut F) -> Self; +} + +pub trait PatternFolder<'tcx> : Sized { + fn fold_pattern(&mut self, pattern: &Pattern<'tcx>) -> Pattern<'tcx> { + pattern.super_fold_with(self) + } + + fn fold_pattern_kind(&mut self, kind: &PatternKind<'tcx>) -> PatternKind<'tcx> { + kind.super_fold_with(self) + } +} + + +impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Box { + fn super_fold_with>(&self, folder: &mut F) -> Self { + let content: T = (**self).fold_with(folder); + box content + } +} + +impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Vec { + fn super_fold_with>(&self, folder: &mut F) -> Self { + self.iter().map(|t| t.fold_with(folder)).collect() + } +} + +impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Option { + fn super_fold_with>(&self, folder: &mut F) -> Self{ + self.as_ref().map(|t| t.fold_with(folder)) + } +} + +macro_rules! CopyImpls { + ($($ty:ty),+) => { + $( + impl<'tcx> PatternFoldable<'tcx> for $ty { + fn super_fold_with>(&self, _: &mut F) -> Self { + self.clone() + } + } + )+ + } +} + +macro_rules! TcxCopyImpls { + ($($ty:ident),+) => { + $( + impl<'tcx> PatternFoldable<'tcx> for $ty<'tcx> { + fn super_fold_with>(&self, _: &mut F) -> Self { + *self + } + } + )+ + } +} + +CopyImpls!{ Span, Field, Mutability, ast::Name, ast::NodeId, usize, ConstVal } +TcxCopyImpls!{ Ty, BindingMode, AdtDef } + +impl<'tcx> PatternFoldable<'tcx> for FieldPattern<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + FieldPattern { + field: self.field.fold_with(folder), + pattern: self.pattern.fold_with(folder) + } + } +} + +impl<'tcx> PatternFoldable<'tcx> for Pattern<'tcx> { + fn fold_with>(&self, folder: &mut F) -> Self { + folder.fold_pattern(self) + } + + fn super_fold_with>(&self, folder: &mut F) -> Self { + Pattern { + ty: self.ty.fold_with(folder), + span: self.span.fold_with(folder), + kind: self.kind.fold_with(folder) + } + } +} + +impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> { + fn fold_with>(&self, folder: &mut F) -> Self { + folder.fold_pattern_kind(self) + } + + fn super_fold_with>(&self, folder: &mut F) -> Self { + match *self { + PatternKind::Wild => PatternKind::Wild, + PatternKind::Binding { + mutability, + name, + mode, + var, + ty, + ref subpattern, + } => PatternKind::Binding { + mutability: mutability.fold_with(folder), + name: name.fold_with(folder), + mode: mode.fold_with(folder), + var: var.fold_with(folder), + ty: ty.fold_with(folder), + subpattern: subpattern.fold_with(folder), + }, + PatternKind::Variant { + adt_def, + variant_index, + ref subpatterns, + } => PatternKind::Variant { + adt_def: adt_def.fold_with(folder), + variant_index: variant_index.fold_with(folder), + subpatterns: subpatterns.fold_with(folder) + }, + PatternKind::Leaf { + ref subpatterns, + } => PatternKind::Leaf { + subpatterns: subpatterns.fold_with(folder), + }, + PatternKind::Deref { + ref subpattern, + } => PatternKind::Deref { + subpattern: subpattern.fold_with(folder), + }, + PatternKind::Constant { + ref value + } => PatternKind::Constant { + value: value.fold_with(folder) + }, + PatternKind::Range { + ref lo, + ref hi + } => PatternKind::Range { + lo: lo.fold_with(folder), + hi: hi.fold_with(folder) + }, + PatternKind::Slice { + ref prefix, + ref slice, + ref suffix, + } => PatternKind::Slice { + prefix: prefix.fold_with(folder), + slice: slice.fold_with(folder), + suffix: suffix.fold_with(folder) + }, + PatternKind::Array { + ref prefix, + ref slice, + ref suffix + } => PatternKind::Array { + prefix: prefix.fold_with(folder), + slice: slice.fold_with(folder), + suffix: suffix.fold_with(folder) + }, + } + } +} diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index bf43bfb326a58..78a1604a5cbba 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -73,8 +73,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { Test { span: match_pair.pattern.span, kind: TestKind::Range { - lo: lo.clone(), - hi: hi.clone(), + lo: Literal::Value { value: lo.clone() }, + hi: Literal::Value { value: hi.clone() }, ty: match_pair.pattern.ty.clone(), }, } diff --git a/src/test/compile-fail/const-pattern-not-const-evaluable.rs b/src/test/compile-fail/const-pattern-not-const-evaluable.rs index d68d63683a79c..3f6f38f9a18e1 100644 --- a/src/test/compile-fail/const-pattern-not-const-evaluable.rs +++ b/src/test/compile-fail/const-pattern-not-const-evaluable.rs @@ -19,19 +19,19 @@ use Cake::*; const BOO: (Cake, Cake) = (Marmor, BlackForest); //~^ ERROR: constant evaluation error [E0080] //~| unimplemented constant expression: enum variants -const FOO: Cake = BOO.1; +//~^^^ ERROR: constant evaluation error [E0080] +//~| unimplemented constant expression: enum variants +const FOO: Cake = BOO.1; //~ NOTE for expression here const fn foo() -> Cake { Marmor //~^ ERROR: constant evaluation error [E0080] //~| unimplemented constant expression: enum variants - //~^^^ ERROR: constant evaluation error [E0080] - //~| unimplemented constant expression: enum variants } const WORKS: Cake = Marmor; -const GOO: Cake = foo(); //~ NOTE for expression here +const GOO: Cake = foo(); fn main() { match BlackForest { From 37418b850f43fd95596c688c230f5e8d94e1962f Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Fri, 30 Sep 2016 00:37:19 +0300 Subject: [PATCH 09/13] stop using MatchCheckCtxt to hold the param-env for check_match --- src/librustc_const_eval/_match.rs | 18 -- src/librustc_const_eval/check_match.rs | 278 ++++++++++++++----------- 2 files changed, 161 insertions(+), 135 deletions(-) diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index ab2a7b01bb240..cac486b743c7e 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -155,7 +155,6 @@ impl<'a, 'tcx> FromIterator>> for Matrix<'a, 'tcx> { //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>, /// A wild pattern with an error type - it exists to avoid having to normalize /// associated types to get field types. pub wild_pattern: &'a Pattern<'tcx>, @@ -165,7 +164,6 @@ pub struct MatchCheckCtxt<'a, 'tcx: 'a> { impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { pub fn create_and_enter( tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParameterEnvironment<'tcx>, f: F) -> R where F: for<'b> FnOnce(MatchCheckCtxt<'b, 'tcx>) -> R { @@ -179,7 +177,6 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { f(MatchCheckCtxt { tcx: tcx, - param_env: param_env, wild_pattern: &wild_pattern, pattern_arena: &pattern_arena, }) @@ -675,18 +672,3 @@ fn specialize<'a, 'tcx>( head }) } - -pub fn is_refutable<'a, 'tcx, A, F>( - cx: &MatchCheckCtxt<'a, 'tcx>, - pat: &'a Pattern<'tcx>, - refutable: F) - -> Option where - F: FnOnce(&Witness) -> A, -{ - let pats = Matrix(vec![vec![pat]]); - match is_useful(cx, &pats, &[cx.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 0ae13d31a569d..a402db705b2e4 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use _match::{MatchCheckCtxt, Matrix, lower_pat, is_refutable, is_useful}; +use _match::{MatchCheckCtxt, Matrix, lower_pat, is_useful}; use _match::{DUMMY_WILD_PAT}; use _match::Usefulness::*; use _match::WitnessPreference::*; @@ -44,23 +44,47 @@ use syntax::ptr::P; use syntax::util::move_map::MoveMap; use syntax_pos::Span; -impl<'a, 'tcx, 'v> Visitor<'v> for MatchCheckCtxt<'a, 'tcx> { - fn visit_expr(&mut self, ex: &hir::Expr) { - check_expr(self, ex); +struct OuterVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx> } + +impl<'a, 'v, 'tcx> Visitor<'v> for OuterVisitor<'a, 'tcx> { + fn visit_expr(&mut self, _expr: &hir::Expr) { + return // const, static and N in [T; N] - shouldn't contain anything } - fn visit_local(&mut self, l: &hir::Local) { - check_local(self, l); + + fn visit_trait_item(&mut self, item: &hir::TraitItem) { + if let hir::ConstTraitItem(..) = item.node { + return // nothing worth match checking in a constant + } else { + intravisit::walk_trait_item(self, item); + } } + + fn visit_impl_item(&mut self, item: &hir::ImplItem) { + if let hir::ImplItemKind::Const(..) = item.node { + return // nothing worth match checking in a constant + } else { + intravisit::walk_impl_item(self, item); + } + } + fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl, - b: &'v hir::Block, s: Span, n: ast::NodeId) { - check_fn(self, fk, fd, b, s, n); + b: &'v hir::Block, s: Span, id: ast::NodeId) { + if let FnKind::Closure(..) = fk { + span_bug!(s, "check_match: closure outside of function") + } + + MatchVisitor { + tcx: self.tcx, + param_env: &ty::ParameterEnvironment::for_item(self.tcx, id) + }.visit_fn(fk, fd, b, s, id); } } +impl<'a, 'tcx> OuterVisitor<'a, 'tcx> { +} + pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { - MatchCheckCtxt::create_and_enter(tcx, tcx.empty_parameter_environment(), |mut cx| { - tcx.visit_all_items_in_krate(DepNode::MatchCheck, &mut cx); - }); + tcx.visit_all_items_in_krate(DepNode::MatchCheck, &mut OuterVisitor { tcx: tcx }); tcx.sess.abort_if_errors(); } @@ -68,65 +92,108 @@ fn create_e0004<'a>(sess: &'a Session, sp: Span, error_message: String) -> Diagn struct_span_err!(sess, sp, E0004, "{}", &error_message) } -fn check_expr(cx: &mut MatchCheckCtxt, ex: &hir::Expr) { - intravisit::walk_expr(cx, ex); - match ex.node { - hir::ExprMatch(ref scrut, ref arms, source) => { - 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. - if let Some(ref guard) = arm.guard { - check_for_mutation_in_guard(cx, &guard); - } - } +struct MatchVisitor<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: &'a ty::ParameterEnvironment<'tcx> +} - let mut static_inliner = StaticInliner::new(cx.tcx); - 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::>, Option<&hir::Expr>)>>(); +impl<'a, 'tcx, 'v> Visitor<'v> for MatchVisitor<'a, 'tcx> { + fn visit_expr(&mut self, ex: &hir::Expr) { + intravisit::walk_expr(self, ex); - // Bail out early if inlining failed. - if static_inliner.failed { - return; + match ex.node { + hir::ExprMatch(ref scrut, ref arms, source) => { + self.check_match(scrut, arms, source, ex.span); } + _ => {} + } + } - for pat in inlined_arms - .iter() - .flat_map(|&(ref pats, _)| pats) { - // Third, check legality of move bindings. - check_legality_of_bindings_in_at_patterns(cx, &pat); + fn visit_local(&mut self, loc: &hir::Local) { + intravisit::walk_local(self, loc); + + let pat = StaticInliner::new(self.tcx).fold_pat(loc.pat.clone()); + self.check_irrefutable(&pat, false); + + // Check legality of move bindings and `@` patterns. + self.check_patterns(false, slice::ref_slice(&loc.pat)); + } - // Fourth, check if there are any references to NaN that we should warn about. - check_for_static_nan(cx, &pat); + fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl, + b: &'v hir::Block, s: Span, n: ast::NodeId) { + intravisit::walk_fn(self, fk, fd, b, s, n); - // 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); + for input in &fd.inputs { + self.check_irrefutable(&input.pat, true); + self.check_patterns(false, slice::ref_slice(&input.pat)); + } + } +} + +impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { + fn check_patterns(&self, has_guard: bool, pats: &[P]) { + check_legality_of_move_bindings(self, has_guard, pats); + for pat in pats { + check_legality_of_bindings_in_at_patterns(self, pat); + } + } + + fn check_match( + &self, + scrut: &hir::Expr, + arms: &[hir::Arm], + source: hir::MatchSource, + span: Span) + { + for arm in arms { + // First, check legality of move bindings. + self.check_patterns(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. + if let Some(ref guard) = arm.guard { + check_for_mutation_in_guard(self, &guard); } + } + + let mut static_inliner = StaticInliner::new(self.tcx); + 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::>, Option<&hir::Expr>)>>(); + + // Bail out early if inlining failed. + if static_inliner.failed { + return; + } + for pat in inlined_arms.iter().flat_map(|&(ref pats, _)| pats) { + // Fourth, check if there are any references to NaN that we should warn about. + check_for_static_nan(self, &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(self, &pat); + } + + MatchCheckCtxt::create_and_enter(self.tcx, |ref cx| { // Fourth, check for unreachable arms. check_arms(cx, &inlined_arms[..], source); // Finally, check if the whole match expression is exhaustive. // Check for empty enum, because is_useful only works on inhabited types. - let pat_ty = cx.tcx.node_id_to_type(scrut.id); + let pat_ty = self.tcx.node_id_to_type(scrut.id); if inlined_arms.is_empty() { - if !pat_ty.is_uninhabited(cx.tcx) { + if !pat_ty.is_uninhabited(self.tcx) { // We know the type is inhabited, so this must be wrong - let mut err = create_e0004(cx.tcx.sess, ex.span, + let mut err = create_e0004(self.tcx.sess, span, format!("non-exhaustive patterns: type {} \ is non-empty", pat_ty)); - span_help!(&mut err, ex.span, - "Please ensure that all possible cases are being handled; \ - possibly adding wildcards or more match arms."); + span_help!(&mut err, span, + "Please ensure that all possible cases are being handled; \ + possibly adding wildcards or more match arms."); err.emit(); } // If the type *is* uninhabited, it's vacuously exhaustive @@ -140,12 +207,40 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &hir::Expr) { .map(|pat| vec![lower_pat(cx, &pat)]) .collect(); check_exhaustive(cx, scrut.span, &matrix, source); - }, - _ => () + }) + } + + fn check_irrefutable(&self, pat: &Pat, is_fn_arg: bool) { + let origin = if is_fn_arg { + "function argument" + } else { + "local binding" + }; + + MatchCheckCtxt::create_and_enter(self.tcx, |ref cx| { + let pats : Matrix = vec![vec![ + lower_pat(cx, pat) + ]].into_iter().collect(); + + let witness = match is_useful(cx, &pats, &[cx.wild_pattern], ConstructWitness) { + UsefulWithWitness(witness) => witness, + NotUseful => return, + Useful => bug!() + }; + + let pattern_string = pat_to_string(witness[0].single_pattern()); + let mut diag = struct_span_err!( + self.tcx.sess, pat.span, E0005, + "refutable pattern in {}: `{}` not covered", + origin, pattern_string + ); + diag.span_label(pat.span, &format!("pattern `{}` not covered", pattern_string)); + diag.emit(); + }); } } -fn check_for_bindings_named_the_same_as_variants(cx: &MatchCheckCtxt, pat: &Pat) { +fn check_for_bindings_named_the_same_as_variants(cx: &MatchVisitor, pat: &Pat) { pat.walk(|p| { if let PatKind::Binding(hir::BindByValue(hir::MutImmutable), name, None) = p.node { let pat_ty = cx.tcx.pat_ty(p); @@ -175,7 +270,7 @@ fn check_for_bindings_named_the_same_as_variants(cx: &MatchCheckCtxt, pat: &Pat) } // Check that we do not match against a static NaN (#6804) -fn check_for_static_nan(cx: &MatchCheckCtxt, pat: &Pat) { +fn check_for_static_nan(cx: &MatchVisitor, pat: &Pat) { pat.walk(|p| { if let PatKind::Lit(ref expr) = p.node { match eval_const_expr_partial(cx.tcx, &expr, ExprTypeChecked, None) { @@ -444,56 +539,8 @@ impl<'a, 'tcx> StaticInliner<'a, 'tcx> { } } -fn check_local(cx: &mut MatchCheckCtxt, loc: &hir::Local) { - intravisit::walk_local(cx, loc); - - let pat = StaticInliner::new(cx.tcx).fold_pat(loc.pat.clone()); - check_irrefutable(cx, &pat, false); - - // Check legality of move bindings and `@` patterns. - check_legality_of_move_bindings(cx, false, slice::ref_slice(&loc.pat)); - check_legality_of_bindings_in_at_patterns(cx, &loc.pat); -} - -fn check_fn(cx: &mut MatchCheckCtxt, - kind: FnKind, - decl: &hir::FnDecl, - body: &hir::Block, - sp: Span, - fn_id: ast::NodeId) { - match kind { - FnKind::Closure(_) => {} - _ => cx.param_env = ty::ParameterEnvironment::for_item(cx.tcx, fn_id), - } - - intravisit::walk_fn(cx, kind, decl, body, sp, fn_id); - - for input in &decl.inputs { - check_irrefutable(cx, &input.pat, true); - check_legality_of_move_bindings(cx, false, slice::ref_slice(&input.pat)); - check_legality_of_bindings_in_at_patterns(cx, &input.pat); - } -} - -fn check_irrefutable(cx: &MatchCheckCtxt, pat: &Pat, is_fn_arg: bool) { - let origin = if is_fn_arg { - "function argument" - } else { - "local binding" - }; - - is_refutable(cx, &lower_pat(cx, pat), |uncovered_pat| { - let pattern_string = pat_to_string(uncovered_pat.single_pattern()); - struct_span_err!(cx.tcx.sess, pat.span, E0005, - "refutable pattern in {}: `{}` not covered", - origin, - pattern_string, - ).span_label(pat.span, &format!("pattern `{}` not covered", pattern_string)).emit(); - }); -} - // Legality of move bindings checking -fn check_legality_of_move_bindings(cx: &MatchCheckCtxt, +fn check_legality_of_move_bindings(cx: &MatchVisitor, has_guard: bool, pats: &[P]) { let mut by_ref_span = None; @@ -532,13 +579,9 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt, pat.walk(|p| { if let PatKind::Binding(hir::BindByValue(..), _, ref sub) = p.node { let pat_ty = cx.tcx.node_id_to_type(p.id); - //FIXME: (@jroesch) this code should be floated up as well - cx.tcx.infer_ctxt(None, Some(cx.param_env.clone()), - Reveal::NotSpecializable).enter(|infcx| { - if infcx.type_moves_by_default(pat_ty, pat.span) { - check_move(p, sub.as_ref().map(|p| &**p)); - } - }); + if pat_ty.moves_by_default(cx.tcx, cx.param_env, pat.span) { + check_move(p, sub.as_ref().map(|p| &**p)); + } } true }); @@ -547,8 +590,9 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt, /// 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: &hir::Expr) { +/// +/// FIXME: this should be done by borrowck. +fn check_for_mutation_in_guard(cx: &MatchVisitor, guard: &hir::Expr) { cx.tcx.infer_ctxt(None, Some(cx.param_env.clone()), Reveal::NotSpecializable).enter(|infcx| { let mut checker = MutationChecker { @@ -560,7 +604,7 @@ fn check_for_mutation_in_guard<'a, 'tcx>(cx: &'a MatchCheckCtxt<'a, 'tcx>, } struct MutationChecker<'a, 'gcx: 'a> { - cx: &'a MatchCheckCtxt<'a, 'gcx>, + cx: &'a MatchVisitor<'a, 'gcx>, } impl<'a, 'gcx, 'tcx> Delegate<'tcx> for MutationChecker<'a, 'gcx> { @@ -600,12 +644,12 @@ impl<'a, 'gcx, 'tcx> Delegate<'tcx> for MutationChecker<'a, 'gcx> { /// 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) { +fn check_legality_of_bindings_in_at_patterns(cx: &MatchVisitor, pat: &Pat) { AtBindingPatternVisitor { cx: cx, bindings_allowed: true }.visit_pat(pat); } struct AtBindingPatternVisitor<'a, 'b:'a, 'tcx:'b> { - cx: &'a MatchCheckCtxt<'b, 'tcx>, + cx: &'a MatchVisitor<'b, 'tcx>, bindings_allowed: bool } From 76fb7d90ecde3659021341779fea598a6daab013 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Mon, 3 Oct 2016 21:39:21 +0300 Subject: [PATCH 10/13] remove StaticInliner and NaN checking NaN checking was a lint for a deprecated feature. It can go away. --- src/librustc_const_eval/_match.rs | 8 +- src/librustc_const_eval/check_match.rs | 214 +++++------------- src/librustc_const_eval/diagnostics.rs | 4 +- src/librustc_const_eval/eval.rs | 12 +- src/librustc_const_eval/pattern.rs | 176 ++++++++------ .../compile-fail/const-eval-overflow-2.rs | 4 - .../const-pattern-not-const-evaluable.rs | 4 +- src/test/compile-fail/issue-6804.rs | 36 --- 8 files changed, 184 insertions(+), 274 deletions(-) delete mode 100644 src/test/compile-fail/issue-6804.rs diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index cac486b743c7e..d70deb2f862d9 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -40,12 +40,10 @@ use std::cmp::Ordering; use std::fmt; use std::iter::{FromIterator, IntoIterator, repeat}; -pub fn lower_pat<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: &Pat) - -> &'a Pattern<'tcx> +pub fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pattern<'tcx>) + -> &'a Pattern<'tcx> { - cx.pattern_arena.alloc( - LiteralExpander.fold_pattern(&Pattern::from_hir(cx.tcx, pat)) - ) + cx.pattern_arena.alloc(LiteralExpander.fold_pattern(&pat)) } struct LiteralExpander; diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index a402db705b2e4..8021b36975a42 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -8,20 +8,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use _match::{MatchCheckCtxt, Matrix, lower_pat, is_useful}; +use _match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful}; use _match::{DUMMY_WILD_PAT}; use _match::Usefulness::*; use _match::WitnessPreference::*; +use pattern::{Pattern, PatternContext, PatternError}; + 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 rustc::middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor}; use rustc::middle::expr_use_visitor::{LoanCause, MutateMode}; use rustc::middle::expr_use_visitor as euv; @@ -39,9 +38,7 @@ use rustc::hir::{self, Pat, PatKind}; use rustc_back::slice; use syntax::ast; -use syntax::codemap::Spanned; use syntax::ptr::P; -use syntax::util::move_map::MoveMap; use syntax_pos::Span; struct OuterVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx> } @@ -80,9 +77,6 @@ impl<'a, 'v, 'tcx> Visitor<'v> for OuterVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> OuterVisitor<'a, 'tcx> { -} - pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { tcx.visit_all_items_in_krate(DepNode::MatchCheck, &mut OuterVisitor { tcx: tcx }); tcx.sess.abort_if_errors(); @@ -112,8 +106,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MatchVisitor<'a, 'tcx> { fn visit_local(&mut self, loc: &hir::Local) { intravisit::walk_local(self, loc); - let pat = StaticInliner::new(self.tcx).fold_pat(loc.pat.clone()); - self.check_irrefutable(&pat, false); + self.check_irrefutable(&loc.pat, false); // Check legality of move bindings and `@` patterns. self.check_patterns(false, slice::ref_slice(&loc.pat)); @@ -138,6 +131,27 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { } } + fn report_inlining_errors(&self, patcx: PatternContext, pat_span: Span) { + for error in patcx.errors { + match error { + PatternError::BadConstInPattern(span, def_id) => { + self.tcx.sess.span_err( + span, + &format!("constants of the type `{}` \ + cannot be used in patterns", + self.tcx.item_path_str(def_id))); + } + PatternError::StaticInPattern(span) => { + span_err!(self.tcx.sess, span, E0158, + "statics cannot be referenced in patterns"); + } + PatternError::ConstEval(err) => { + report_const_eval_err(self.tcx, &err, pat_span, "pattern").emit(); + } + } + } + } + fn check_match( &self, scrut: &hir::Expr, @@ -154,32 +168,36 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { if let Some(ref guard) = arm.guard { check_for_mutation_in_guard(self, &guard); } - } - - let mut static_inliner = StaticInliner::new(self.tcx); - 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::>, Option<&hir::Expr>)>>(); - // Bail out early if inlining failed. - if static_inliner.failed { - return; + // Third, perform some lints. + for pat in &arm.pats { + check_for_bindings_named_the_same_as_variants(self, pat); + } } - for pat in inlined_arms.iter().flat_map(|&(ref pats, _)| pats) { - // Fourth, check if there are any references to NaN that we should warn about. - check_for_static_nan(self, &pat); + MatchCheckCtxt::create_and_enter(self.tcx, |ref cx| { + let mut have_errors = false; + + let inlined_arms : Vec<(Vec<_>, _)> = arms.iter().map(|arm| ( + arm.pats.iter().map(|pat| { + let mut patcx = PatternContext::new(self.tcx); + let pattern = expand_pattern(cx, patcx.lower_pattern(&pat)); + if !patcx.errors.is_empty() { + self.report_inlining_errors(patcx, pat.span); + have_errors = true; + } + (pattern, &**pat) + }).collect(), + arm.guard.as_ref().map(|e| &**e) + )).collect(); - // 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(self, &pat); - } + // Bail out early if inlining failed. + if have_errors { + return; + } - MatchCheckCtxt::create_and_enter(self.tcx, |ref cx| { // Fourth, check for unreachable arms. - check_arms(cx, &inlined_arms[..], source); + check_arms(cx, &inlined_arms, source); // Finally, check if the whole match expression is exhaustive. // Check for empty enum, because is_useful only works on inhabited types. @@ -204,7 +222,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { .iter() .filter(|&&(_, guard)| guard.is_none()) .flat_map(|arm| &arm.0) - .map(|pat| vec![lower_pat(cx, &pat)]) + .map(|pat| vec![pat.0]) .collect(); check_exhaustive(cx, scrut.span, &matrix, source); }) @@ -218,8 +236,9 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { }; MatchCheckCtxt::create_and_enter(self.tcx, |ref cx| { + let mut patcx = PatternContext::new(self.tcx); let pats : Matrix = vec![vec![ - lower_pat(cx, pat) + expand_pattern(cx, patcx.lower_pattern(pat)) ]].into_iter().collect(); let witness = match is_useful(cx, &pats, &[cx.wild_pattern], ConstructWitness) { @@ -269,27 +288,6 @@ fn check_for_bindings_named_the_same_as_variants(cx: &MatchVisitor, pat: &Pat) { }); } -// Check that we do not match against a static NaN (#6804) -fn check_for_static_nan(cx: &MatchVisitor, pat: &Pat) { - pat.walk(|p| { - if let PatKind::Lit(ref expr) = p.node { - match eval_const_expr_partial(cx.tcx, &expr, ExprTypeChecked, None) { - Ok(ConstVal::Float(f)) if f.is_nan() => { - span_warn!(cx.tcx.sess, p.span, E0003, - "unmatchable NaN in pattern, \ - use the is_nan method in a guard instead"); - } - Ok(_) => {} - - Err(err) => { - report_const_eval_err(cx.tcx, &err, p.span, "pattern").emit(); - } - } - } - true - }); -} - /// Checks for common cases of "catchall" patterns that may not be intended as such. fn pat_is_catchall(dm: &DefMap, pat: &Pat) -> bool { match pat.node { @@ -304,15 +302,16 @@ fn pat_is_catchall(dm: &DefMap, pat: &Pat) -> bool { } // Check for unreachable patterns -fn check_arms(cx: &MatchCheckCtxt, - arms: &[(Vec>, Option<&hir::Expr>)], - source: hir::MatchSource) { +fn check_arms<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, + arms: &[(Vec<(&Pattern<'tcx>, &'a hir::Pat)>, Option<&hir::Expr>)], + source: hir::MatchSource) +{ let mut seen = Matrix::empty(); let mut catchall = None; let mut printed_if_let_err = false; for &(ref pats, guard) in arms { - for pat in pats { - let v = vec![lower_pat(cx, &pat)]; + for &(pat, hir_pat) in pats { + let v = vec![pat]; match is_useful(cx, &seen, &v[..], LeaveOutWitness) { NotUseful => { @@ -325,7 +324,7 @@ fn check_arms(cx: &MatchCheckCtxt, // 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; + let span = first_pat.0.span; struct_span_err!(cx.tcx.sess, span, E0162, "irrefutable if-let pattern") .span_label(span, &format!("irrefutable pattern")) @@ -338,7 +337,7 @@ fn check_arms(cx: &MatchCheckCtxt, // 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; + let span = first_pat.0.span; struct_span_err!(cx.tcx.sess, span, E0165, "irrefutable while-let pattern") .span_label(span, &format!("irrefutable pattern")) @@ -374,7 +373,7 @@ fn check_arms(cx: &MatchCheckCtxt, } if guard.is_none() { seen.push(v); - if catchall.is_none() && pat_is_catchall(&cx.tcx.def_map.borrow(), pat) { + if catchall.is_none() && pat_is_catchall(&cx.tcx.def_map.borrow(), hir_pat) { catchall = Some(pat.span); } } @@ -448,97 +447,6 @@ fn check_exhaustive<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, } } - -struct StaticInliner<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, - failed: bool -} - -impl<'a, 'tcx> StaticInliner<'a, 'tcx> { - pub fn new<'b>(tcx: TyCtxt<'b, 'tcx, 'tcx>) -> StaticInliner<'b, 'tcx> { - StaticInliner { - tcx: tcx, - failed: false - } - } -} - -impl<'a, 'tcx> StaticInliner<'a, 'tcx> { - fn fold_pat(&mut self, pat: P) -> P { - match pat.node { - PatKind::Path(..) => { - match self.tcx.expect_def(pat.id) { - Def::AssociatedConst(did) | Def::Const(did) => { - let substs = Some(self.tcx.node_id_item_substs(pat.id).substs); - if let Some((const_expr, _)) = lookup_const_by_id(self.tcx, did, substs) { - match const_expr_to_pat(self.tcx, const_expr, pat.id, pat.span) { - Ok(new_pat) => return new_pat, - Err(def_id) => { - self.failed = true; - self.tcx.sess.span_err( - pat.span, - &format!("constants of the type `{}` \ - cannot be used in patterns", - self.tcx.item_path_str(def_id))); - } - } - } else { - self.failed = true; - span_err!(self.tcx.sess, pat.span, E0158, - "statics cannot be referenced in patterns"); - } - } - _ => {} - } - } - _ => {} - } - - pat.map(|Pat { id, node, span }| { - let node = match node { - PatKind::Binding(binding_mode, pth1, sub) => { - PatKind::Binding(binding_mode, pth1, sub.map(|x| self.fold_pat(x))) - } - PatKind::TupleStruct(pth, pats, ddpos) => { - PatKind::TupleStruct(pth, pats.move_map(|x| self.fold_pat(x)), ddpos) - } - PatKind::Struct(pth, fields, etc) => { - let fs = fields.move_map(|f| { - Spanned { - span: f.span, - node: hir::FieldPat { - name: f.node.name, - pat: self.fold_pat(f.node.pat), - is_shorthand: f.node.is_shorthand, - }, - } - }); - PatKind::Struct(pth, fs, etc) - } - PatKind::Tuple(elts, ddpos) => { - PatKind::Tuple(elts.move_map(|x| self.fold_pat(x)), ddpos) - } - PatKind::Box(inner) => PatKind::Box(self.fold_pat(inner)), - PatKind::Ref(inner, mutbl) => PatKind::Ref(self.fold_pat(inner), mutbl), - PatKind::Slice(before, slice, after) => { - PatKind::Slice(before.move_map(|x| self.fold_pat(x)), - slice.map(|x| self.fold_pat(x)), - after.move_map(|x| self.fold_pat(x))) - } - PatKind::Wild | - PatKind::Lit(_) | - PatKind::Range(..) | - PatKind::Path(..) => node - }; - Pat { - id: id, - node: node, - span: span - } - }) - } -} - // Legality of move bindings checking fn check_legality_of_move_bindings(cx: &MatchVisitor, has_guard: bool, diff --git a/src/librustc_const_eval/diagnostics.rs b/src/librustc_const_eval/diagnostics.rs index c2b39625e2eee..f70583db70a60 100644 --- a/src/librustc_const_eval/diagnostics.rs +++ b/src/librustc_const_eval/diagnostics.rs @@ -71,7 +71,7 @@ fn foo(x: Option) { "##,*/ -E0003: r##" +/*E0003: r##" Not-a-Number (NaN) values cannot be compared for equality and hence can never match the input to a match expression. So, the following will not compile: @@ -100,7 +100,7 @@ match number { } ``` "##, - +*/ E0004: r##" This error indicates that the compiler cannot guarantee a matching pattern for diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 81dd642de5d27..c02cca0da7225 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -392,7 +392,7 @@ pub fn note_const_eval_err<'a, 'tcx>( pub fn eval_const_expr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, e: &Expr) -> ConstVal { - match eval_const_expr_partial(tcx, e, ExprTypeChecked, None) { + match eval_const_expr_checked(tcx, e) { Ok(r) => r, // non-const path still needs to be a fatal error, because enums are funky Err(s) => { @@ -407,15 +407,21 @@ pub fn eval_const_expr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } +pub fn eval_const_expr_checked<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + e: &Expr) -> EvalResult +{ + eval_const_expr_partial(tcx, e, ExprTypeChecked, None) +} + pub type FnArgMap<'a> = Option<&'a NodeMap>; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ConstEvalErr { pub span: Span, pub kind: ErrKind, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum ErrKind { CannotCast, CannotCastTo(&'static str), diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs index 715659f5885b5..4bd797afd0682 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_const_eval/pattern.rs @@ -15,6 +15,7 @@ use rustc::mir::repr::{Field, BorrowKind, Mutability}; use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region}; use rustc::hir::{self, PatKind}; use rustc::hir::def::Def; +use rustc::hir::def_id::DefId; use rustc::hir::pat_util::EnumerateAndAdjustIterator; use rustc_data_structures::indexed_vec::Idx; @@ -23,6 +24,13 @@ use syntax::ast; use syntax::ptr::P; use syntax_pos::Span; +#[derive(Clone, Debug)] +pub enum PatternError { + StaticInPattern(Span), + BadConstInPattern(Span, DefId), + ConstEval(eval::ConstEvalErr), +} + #[derive(Copy, Clone, Debug)] pub enum BindingMode<'tcx> { ByValue, @@ -97,78 +105,112 @@ pub enum PatternKind<'tcx> { }, } +pub struct PatternContext<'a, 'gcx: 'tcx, 'tcx: 'a> { + pub tcx: TyCtxt<'a, 'gcx, 'tcx>, + pub errors: Vec, +} + impl<'a, 'gcx, 'tcx> Pattern<'tcx> { pub fn from_hir(tcx: TyCtxt<'a, 'gcx, 'tcx>, pat: &hir::Pat) -> Self { - let mut ty = tcx.node_id_to_type(pat.id); + let mut pcx = PatternContext::new(tcx); + let result = pcx.lower_pattern(pat); + if !pcx.errors.is_empty() { + span_bug!(pat.span, "encountered errors lowering pattern: {:?}", pcx.errors) + } + result + } +} + +impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Self { + PatternContext { tcx: tcx, errors: vec![] } + } + + pub fn lower_pattern(&mut self, pat: &hir::Pat) -> Pattern<'tcx> { + let mut ty = self.tcx.node_id_to_type(pat.id); let kind = match pat.node { PatKind::Wild => PatternKind::Wild, PatKind::Lit(ref value) => { - let value = eval::eval_const_expr(tcx.global_tcx(), value); - PatternKind::Constant { value: value } + match eval::eval_const_expr_checked(self.tcx.global_tcx(), value) { + Ok(value) => { + PatternKind::Constant { value: value } + } + Err(e) => { + self.errors.push(PatternError::ConstEval(e)); + PatternKind::Wild + } + } } PatKind::Range(ref lo, ref hi) => { - let lo = eval::eval_const_expr(tcx.global_tcx(), lo); - let hi = eval::eval_const_expr(tcx.global_tcx(), hi); - PatternKind::Range { lo: lo, hi: hi } - }, + let r_lo = eval::eval_const_expr_checked(self.tcx.global_tcx(), lo); + if let Err(ref e_lo) = r_lo { + self.errors.push(PatternError::ConstEval(e_lo.clone())); + } + + let r_hi = eval::eval_const_expr_checked(self.tcx.global_tcx(), hi); + if let Err(ref e_hi) = r_hi { + self.errors.push(PatternError::ConstEval(e_hi.clone())); + } + + if let (Ok(lo), Ok(hi)) = (r_lo, r_hi) { + PatternKind::Range { lo: lo, hi: hi } + } else { + PatternKind::Wild + } + } PatKind::Path(..) => { - match tcx.expect_def(pat.id) { + match self.tcx.expect_def(pat.id) { Def::Const(def_id) | Def::AssociatedConst(def_id) => { - let tcx = tcx.global_tcx(); - let substs = Some(tcx.node_id_item_substs(pat.id).substs); + let tcx = self.tcx.global_tcx(); + let substs = Some(self.tcx.node_id_item_substs(pat.id).substs); match eval::lookup_const_by_id(tcx, def_id, substs) { Some((const_expr, _const_ty)) => { - match eval::const_expr_to_pat(tcx, - const_expr, - pat.id, - pat.span) { - Ok(pat) => - return Pattern::from_hir(tcx, &pat), - Err(_) => - span_bug!( - pat.span, "illegal constant"), + match eval::const_expr_to_pat( + tcx, const_expr, pat.id, pat.span) + { + Ok(pat) => return self.lower_pattern(&pat), + Err(_) => { + self.errors.push(PatternError::BadConstInPattern( + pat.span, def_id)); + PatternKind::Wild + } } } None => { - span_bug!( - pat.span, - "cannot eval constant: {:?}", - def_id) + self.errors.push(PatternError::StaticInPattern(pat.span)); + PatternKind::Wild } } } - _ => { - PatternKind::from_variant_or_leaf(tcx, pat, vec![]) - } + _ => self.lower_variant_or_leaf(pat, vec![]) } } PatKind::Ref(ref subpattern, _) | PatKind::Box(ref subpattern) => { - PatternKind::Deref { subpattern: Self::from_hir(tcx, subpattern) } + PatternKind::Deref { subpattern: self.lower_pattern(subpattern) } } PatKind::Slice(ref prefix, ref slice, ref suffix) => { - let ty = tcx.node_id_to_type(pat.id); + let ty = self.tcx.node_id_to_type(pat.id); match ty.sty { ty::TyRef(_, mt) => PatternKind::Deref { subpattern: Pattern { ty: mt.ty, span: pat.span, - kind: Box::new(PatternKind::from_slice_or_array( - tcx, pat.span, mt.ty, prefix, slice, suffix)) + kind: Box::new(self.slice_or_array_pattern( + pat.span, mt.ty, prefix, slice, suffix)) }, }, ty::TySlice(..) | ty::TyArray(..) => - PatternKind::from_slice_or_array( - tcx, pat.span, ty, prefix, slice, suffix), + self.slice_or_array_pattern(pat.span, ty, prefix, slice, suffix), ref sty => span_bug!( @@ -179,14 +221,14 @@ impl<'a, 'gcx, 'tcx> Pattern<'tcx> { } PatKind::Tuple(ref subpatterns, ddpos) => { - match tcx.node_id_to_type(pat.id).sty { + match self.tcx.node_id_to_type(pat.id).sty { ty::TyTuple(ref tys) => { let subpatterns = subpatterns.iter() .enumerate_and_adjust(tys.len(), ddpos) .map(|(i, subpattern)| FieldPattern { field: Field::new(i), - pattern: Self::from_hir(tcx, subpattern), + pattern: self.lower_pattern(subpattern) }) .collect(); @@ -198,9 +240,9 @@ impl<'a, 'gcx, 'tcx> Pattern<'tcx> { } PatKind::Binding(bm, ref ident, ref sub) => { - let def_id = tcx.expect_def(pat.id).def_id(); - let id = tcx.map.as_local_node_id(def_id).unwrap(); - let var_ty = tcx.node_id_to_type(pat.id); + let def_id = self.tcx.expect_def(pat.id).def_id(); + let id = self.tcx.map.as_local_node_id(def_id).unwrap(); + let var_ty = self.tcx.node_id_to_type(pat.id); let region = match var_ty.sty { ty::TyRef(r, _) => Some(r), _ => None, @@ -232,31 +274,31 @@ impl<'a, 'gcx, 'tcx> Pattern<'tcx> { name: ident.node, var: id, ty: var_ty, - subpattern: Self::from_opt_pattern(tcx, sub), + subpattern: self.lower_opt_pattern(sub), } } PatKind::TupleStruct(_, ref subpatterns, ddpos) => { - let pat_ty = tcx.node_id_to_type(pat.id); + let pat_ty = self.tcx.node_id_to_type(pat.id); let adt_def = match pat_ty.sty { ty::TyAdt(adt_def, _) => adt_def, _ => span_bug!(pat.span, "tuple struct pattern not applied to an ADT"), }; - let variant_def = adt_def.variant_of_def(tcx.expect_def(pat.id)); + let variant_def = adt_def.variant_of_def(self.tcx.expect_def(pat.id)); let subpatterns = subpatterns.iter() .enumerate_and_adjust(variant_def.fields.len(), ddpos) .map(|(i, field)| FieldPattern { field: Field::new(i), - pattern: Self::from_hir(tcx, field), + pattern: self.lower_pattern(field), }) .collect(); - PatternKind::from_variant_or_leaf(tcx, pat, subpatterns) + self.lower_variant_or_leaf(pat, subpatterns) } PatKind::Struct(_, ref fields, _) => { - let pat_ty = tcx.node_id_to_type(pat.id); + let pat_ty = self.tcx.node_id_to_type(pat.id); let adt_def = match pat_ty.sty { ty::TyAdt(adt_def, _) => adt_def, _ => { @@ -265,7 +307,7 @@ impl<'a, 'gcx, 'tcx> Pattern<'tcx> { "struct pattern not applied to an ADT"); } }; - let variant_def = adt_def.variant_of_def(tcx.expect_def(pat.id)); + let variant_def = adt_def.variant_of_def(self.tcx.expect_def(pat.id)); let subpatterns = fields.iter() @@ -279,12 +321,12 @@ impl<'a, 'gcx, 'tcx> Pattern<'tcx> { }); FieldPattern { field: Field::new(index), - pattern: Self::from_hir(tcx, &field.node.pat), + pattern: self.lower_pattern(&field.node.pat), } }) .collect(); - PatternKind::from_variant_or_leaf(tcx, pat, subpatterns) + self.lower_variant_or_leaf(pat, subpatterns) } }; @@ -295,33 +337,31 @@ impl<'a, 'gcx, 'tcx> Pattern<'tcx> { } } - fn from_patterns(tcx: TyCtxt<'a, 'gcx, 'tcx>, pats: &[P]) -> Vec { - pats.iter().map(|p| Self::from_hir(tcx, p)).collect() + fn lower_patterns(&mut self, pats: &[P]) -> Vec> { + pats.iter().map(|p| self.lower_pattern(p)).collect() } - fn from_opt_pattern(tcx: TyCtxt<'a, 'gcx, 'tcx>, pat: &Option>) -> Option + fn lower_opt_pattern(&mut self, pat: &Option>) -> Option> { - pat.as_ref().map(|p| Self::from_hir(tcx, p)) + pat.as_ref().map(|p| self.lower_pattern(p)) } -} -impl<'a, 'gcx, 'tcx> PatternKind<'tcx> { - fn from_slice_or_array( - tcx: TyCtxt<'a, 'gcx, 'tcx>, + fn slice_or_array_pattern( + &mut self, span: Span, ty: Ty<'tcx>, prefix: &[P], slice: &Option>, suffix: &[P]) - -> Self + -> PatternKind<'tcx> { match ty.sty { ty::TySlice(..) => { // matching a slice or fixed-length array PatternKind::Slice { - prefix: Pattern::from_patterns(tcx, prefix), - slice: Pattern::from_opt_pattern(tcx, slice), - suffix: Pattern::from_patterns(tcx, suffix), + prefix: self.lower_patterns(prefix), + slice: self.lower_opt_pattern(slice), + suffix: self.lower_patterns(suffix), } } @@ -329,28 +369,28 @@ impl<'a, 'gcx, 'tcx> PatternKind<'tcx> { // fixed-length array assert!(len >= prefix.len() + suffix.len()); PatternKind::Array { - prefix: Pattern::from_patterns(tcx, prefix), - slice: Pattern::from_opt_pattern(tcx, slice), - suffix: Pattern::from_patterns(tcx, suffix), + prefix: self.lower_patterns(prefix), + slice: self.lower_opt_pattern(slice), + suffix: self.lower_patterns(suffix), } } _ => { - span_bug!(span, "unexpanded macro or bad constant etc"); + span_bug!(span, "bad slice pattern type {:?}", ty); } } } - fn from_variant_or_leaf( - tcx: TyCtxt<'a, 'gcx, 'tcx>, + fn lower_variant_or_leaf( + &mut self, pat: &hir::Pat, subpatterns: Vec>) - -> Self + -> PatternKind<'tcx> { - match tcx.expect_def(pat.id) { + match self.tcx.expect_def(pat.id) { Def::Variant(variant_id) | Def::VariantCtor(variant_id, ..) => { - let enum_id = tcx.parent_def_id(variant_id).unwrap(); - let adt_def = tcx.lookup_adt_def(enum_id); + let enum_id = self.tcx.parent_def_id(variant_id).unwrap(); + let adt_def = self.tcx.lookup_adt_def(enum_id); if adt_def.variants.len() > 1 { PatternKind::Variant { adt_def: adt_def, diff --git a/src/test/compile-fail/const-eval-overflow-2.rs b/src/test/compile-fail/const-eval-overflow-2.rs index 264f02588ae5d..9b045ed1d02cc 100644 --- a/src/test/compile-fail/const-eval-overflow-2.rs +++ b/src/test/compile-fail/const-eval-overflow-2.rs @@ -21,10 +21,6 @@ const NEG_128: i8 = -128; const NEG_NEG_128: i8 = -NEG_128; //~^ ERROR constant evaluation error //~| attempt to negate with overflow -//~| ERROR constant evaluation error -//~| attempt to negate with overflow -//~| ERROR constant evaluation error -//~| attempt to negate with overflow fn main() { match -128i8 { diff --git a/src/test/compile-fail/const-pattern-not-const-evaluable.rs b/src/test/compile-fail/const-pattern-not-const-evaluable.rs index 3f6f38f9a18e1..b40aa2a8e27dc 100644 --- a/src/test/compile-fail/const-pattern-not-const-evaluable.rs +++ b/src/test/compile-fail/const-pattern-not-const-evaluable.rs @@ -19,9 +19,7 @@ use Cake::*; const BOO: (Cake, Cake) = (Marmor, BlackForest); //~^ ERROR: constant evaluation error [E0080] //~| unimplemented constant expression: enum variants -//~^^^ ERROR: constant evaluation error [E0080] -//~| unimplemented constant expression: enum variants -const FOO: Cake = BOO.1; //~ NOTE for expression here +const FOO: Cake = BOO.1; const fn foo() -> Cake { Marmor diff --git a/src/test/compile-fail/issue-6804.rs b/src/test/compile-fail/issue-6804.rs deleted file mode 100644 index f6b7e13c4f5e9..0000000000000 --- a/src/test/compile-fail/issue-6804.rs +++ /dev/null @@ -1,36 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(rustc_attrs)] -#![feature(slice_patterns)] -#![allow(dead_code)] - -// Matching against NaN should result in a warning - -use std::f64::NAN; - -#[rustc_error] -fn main() { //~ ERROR compilation successful - let x = NAN; - match x { - NAN => {}, - _ => {}, - }; - //~^^^ WARNING unmatchable NaN in pattern, use the is_nan method in a guard instead - //~| WARNING floating point constants cannot be used - //~| WARNING this was previously accepted - match [x, 1.0] { - [NAN, _] => {}, - _ => {}, - }; - //~^^^ WARNING unmatchable NaN in pattern, use the is_nan method in a guard instead - //~| WARNING floating point constants cannot be used - //~| WARNING this was previously accepted -} From 8d3e89b484bbcd1c2fec60794c196d7148dd9f5b Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Wed, 26 Oct 2016 22:38:22 +0300 Subject: [PATCH 11/13] handle mixed byte literal and byte array patterns Convert byte literal pattern to byte array patterns when they are both used together. so matching them is properly handled. I could've done the conversion eagerly, but that could have caused a bad worst-case for massive byte-array matches. Fixes #18027. Fixes #25051. Fixes #26510. --- src/librustc_const_eval/_match.rs | 185 +++++++++++++----- src/librustc_const_eval/check_match.rs | 10 +- .../compile-fail/match-byte-array-patterns.rs | 73 +++++++ .../run-pass/match-byte-array-patterns.rs | 54 +++++ 4 files changed, 272 insertions(+), 50 deletions(-) create mode 100644 src/test/compile-fail/match-byte-array-patterns.rs create mode 100644 src/test/run-pass/match-byte-array-patterns.rs diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index d70deb2f862d9..7f5eb31612cb3 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -15,6 +15,9 @@ use self::WitnessPreference::*; use rustc::middle::const_val::ConstVal; use eval::{compare_const_vals}; +use rustc_const_math::ConstInt; + +use rustc_data_structures::fnv::FnvHashMap; use rustc_data_structures::indexed_vec::Idx; use pattern::{FieldPattern, Pattern, PatternKind}; @@ -157,6 +160,7 @@ pub struct MatchCheckCtxt<'a, 'tcx: 'a> { /// associated types to get field types. pub wild_pattern: &'a Pattern<'tcx>, pub pattern_arena: &'a TypedArena>, + pub byte_array_map: FnvHashMap<*const Pattern<'tcx>, Vec<&'a Pattern<'tcx>>>, } impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { @@ -177,8 +181,31 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { tcx: tcx, wild_pattern: &wild_pattern, pattern_arena: &pattern_arena, + byte_array_map: FnvHashMap(), }) } + + // convert a byte-string pattern to a list of u8 patterns. + fn lower_byte_str_pattern(&mut self, pat: &'a Pattern<'tcx>) -> Vec<&'a Pattern<'tcx>> { + let pattern_arena = &*self.pattern_arena; + let tcx = self.tcx; + self.byte_array_map.entry(pat).or_insert_with(|| { + match pat.kind { + box PatternKind::Constant { + value: ConstVal::ByteStr(ref data) + } => { + data.iter().map(|c| &*pattern_arena.alloc(Pattern { + ty: tcx.types.u8, + span: pat.span, + kind: box PatternKind::Constant { + value: ConstVal::Integral(ConstInt::U8(*c)) + } + })).collect() + } + _ => span_bug!(pat.span, "unexpected byte array pattern {:?}", pat) + } + }).clone() + } } #[derive(Clone, Debug, PartialEq)] @@ -357,7 +384,8 @@ impl Witness { /// Therefore, if there is some pattern that is unmatched by `matrix`, it will /// still be unmatched if the first constructor is replaced by any of the constructors /// in the return value. -fn missing_constructors(cx: &MatchCheckCtxt, matrix: &Matrix, +fn missing_constructors(cx: &mut MatchCheckCtxt, + matrix: &Matrix, pcx: PatternContext) -> Vec { let used_constructors: Vec = matrix.0.iter() @@ -371,14 +399,20 @@ fn missing_constructors(cx: &MatchCheckCtxt, matrix: &Matrix, /// 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 +/// +/// This intentionally does not list ConstantValue specializations for +/// non-booleans, because we currently assume that there is always a +/// "non-standard constant" that matches. See issue #12483. +/// /// but is instead bounded by the maximum fixed length of slice patterns in /// the column of patterns being analyzed. -fn all_constructors(_cx: &MatchCheckCtxt, pcx: PatternContext) -> Vec { +fn all_constructors(_cx: &mut MatchCheckCtxt, pcx: PatternContext) -> Vec { match pcx.ty.sty { ty::TyBool => [true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(), ty::TySlice(_) => (0..pcx.max_slice_length+1).map(|length| Slice(length)).collect(), + ty::TyArray(_, length) => vec![Slice(length)], ty::TyAdt(def, _) if def.is_enum() && def.variants.len() > 1 => def.variants.iter().map(|v| Variant(v.did)).collect(), _ => vec![Single] @@ -398,7 +432,7 @@ fn all_constructors(_cx: &MatchCheckCtxt, pcx: PatternContext) -> Vec(cx: &MatchCheckCtxt<'a, 'tcx>, +pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>, matrix: &Matrix<'a, 'tcx>, v: &[&'a Pattern<'tcx>], witness: WitnessPreference) @@ -416,19 +450,22 @@ pub fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, if rows[0].is_empty() { return NotUseful; } - assert!(rows.iter().all(|r| r.len() == v.len())); + let &Matrix(ref rows) = matrix; + assert!(rows.iter().all(|r| r.len() == v.len())); let pcx = PatternContext { ty: rows.iter().map(|r| r[0].ty).find(|ty| !ty.references_error()) .unwrap_or(v[0].ty), max_slice_length: rows.iter().filter_map(|row| match *row[0].kind { PatternKind::Slice { ref prefix, slice: _, ref suffix } => Some(prefix.len() + suffix.len()), + PatternKind::Constant { value: ConstVal::ByteStr(ref data) } => + Some(data.len()), _ => None }).max().map_or(0, |v| v + 1) }; - debug!("is_useful: pcx={:?}, expanding {:?}", pcx, v[0]); + debug!("is_useful_expand_first_col: pcx={:?}, expanding {:?}", pcx, v[0]); if let Some(constructors) = pat_constructors(cx, v[0], pcx) { debug!("is_useful - expanding constructors: {:?}", constructors); @@ -453,6 +490,7 @@ pub fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, }).collect(); match is_useful(cx, &matrix, &v[1..], witness) { UsefulWithWitness(pats) => { + let cx = &*cx; UsefulWithWitness(pats.into_iter().flat_map(|witness| { constructors.iter().map(move |ctor| { witness.clone().push_wild_constructor(cx, ctor, pcx.ty) @@ -466,7 +504,7 @@ pub fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, } fn is_useful_specialized<'a, 'tcx>( - cx: &MatchCheckCtxt<'a, 'tcx>, + cx: &mut MatchCheckCtxt<'a, 'tcx>, &Matrix(ref m): &Matrix<'a, 'tcx>, v: &[&'a Pattern<'tcx>], ctor: Constructor, @@ -474,7 +512,7 @@ fn is_useful_specialized<'a, 'tcx>( witness: WitnessPreference) -> Usefulness { let arity = constructor_arity(cx, &ctor, lty); - let matrix = Matrix(m.iter().filter_map(|r| { + let matrix = Matrix(m.iter().flat_map(|r| { specialize(cx, &r[..], &ctor, 0, arity) }).collect()); match specialize(cx, v, &ctor, 0, arity) { @@ -498,7 +536,7 @@ fn is_useful_specialized<'a, 'tcx>( /// `[a, b, ..tail]` can match a slice of length 2, 3, 4 and so on. /// /// Returns None in case of a catch-all, which can't be specialized. -fn pat_constructors(_cx: &MatchCheckCtxt, +fn pat_constructors(_cx: &mut MatchCheckCtxt, pat: &Pattern, pcx: PatternContext) -> Option> @@ -506,7 +544,7 @@ fn pat_constructors(_cx: &MatchCheckCtxt, match *pat.kind { PatternKind::Binding { .. } | PatternKind::Wild => None, - PatternKind::Leaf { .. } | PatternKind::Deref { .. } | PatternKind::Array { .. } => + PatternKind::Leaf { .. } | PatternKind::Deref { .. } => Some(vec![Single]), PatternKind::Variant { adt_def, variant_index, .. } => Some(vec![Variant(adt_def.variants[variant_index].did)]), @@ -514,6 +552,10 @@ fn pat_constructors(_cx: &MatchCheckCtxt, Some(vec![ConstantValue(value.clone())]), PatternKind::Range { ref lo, ref hi } => Some(vec![ConstantRange(lo.clone(), hi.clone())]), + PatternKind::Array { .. } => match pcx.ty.sty { + ty::TyArray(_, length) => Some(vec![Slice(length)]), + _ => span_bug!(pat.span, "bad ty {:?} for array pattern", pcx.ty) + }, PatternKind::Slice { ref prefix, ref slice, ref suffix } => { let pat_len = prefix.len() + suffix.len(); if slice.is_some() { @@ -535,23 +577,55 @@ fn constructor_arity(_cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> usize match ty.sty { ty::TyTuple(ref fs) => fs.len(), ty::TyBox(_) => 1, - ty::TySlice(_) => match *ctor { + ty::TySlice(..) | ty::TyArray(..) => match *ctor { Slice(length) => length, - ConstantValue(_) => { - // TODO: this is utterly wrong, but required for byte arrays - 0 - } + ConstantValue(_) => 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 slice_pat_covered_by_constructor(_tcx: TyCtxt, _span: Span, + ctor: &Constructor, + prefix: &[Pattern], + slice: &Option, + suffix: &[Pattern]) + -> Result { + let data = match *ctor { + ConstantValue(ConstVal::ByteStr(ref data)) => data, + _ => bug!() + }; + + let pat_len = prefix.len() + suffix.len(); + if data.len() < pat_len || (slice.is_none() && data.len() > pat_len) { + return Ok(false); + } + + for (ch, pat) in + data[..prefix.len()].iter().zip(prefix).chain( + data[data.len()-suffix.len()..].iter().zip(suffix)) + { + match pat.kind { + box PatternKind::Constant { ref value } => match *value { + ConstVal::Integral(ConstInt::U8(u)) => { + if u != *ch { + return Ok(false); + } + }, + _ => span_bug!(pat.span, "bad const u8 {:?}", value) + }, + _ => {} + } + } + + Ok(true) +} + fn range_covered_by_constructor(tcx: TyCtxt, span: Span, ctor: &Constructor, from: &ConstVal, to: &ConstVal) @@ -568,7 +642,7 @@ fn range_covered_by_constructor(tcx: TyCtxt, span: Span, } fn patterns_for_variant<'a, 'tcx>( - cx: &MatchCheckCtxt<'a, 'tcx>, + cx: &mut MatchCheckCtxt<'a, 'tcx>, subpatterns: &'a [FieldPattern<'tcx>], arity: usize) -> Vec<&'a Pattern<'tcx>> @@ -592,7 +666,7 @@ fn patterns_for_variant<'a, 'tcx>( /// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing /// fields filled with wild patterns. fn specialize<'a, 'tcx>( - cx: &MatchCheckCtxt<'a, 'tcx>, + cx: &mut MatchCheckCtxt<'a, 'tcx>, r: &[&'a Pattern<'tcx>], constructor: &Constructor, col: usize, arity: usize) -> Option>> @@ -616,13 +690,27 @@ fn specialize<'a, 'tcx>( PatternKind::Deref { ref subpattern } => Some(vec![subpattern]), PatternKind::Constant { ref value } => { - assert_eq!(constructor_arity(cx, constructor, pat.ty), 0); - match range_covered_by_constructor( - cx.tcx, pat.span, constructor, value, value - ) { - Ok(true) => Some(vec![]), - Ok(false) => None, - Err(ErrorReported) => None, + match *constructor { + Slice(..) => match *value { + ConstVal::ByteStr(ref data) => { + if arity == data.len() { + Some(cx.lower_byte_str_pattern(pat)) + } else { + None + } + } + _ => span_bug!(pat.span, + "unexpected const-val {:?} with ctor {:?}", value, constructor) + }, + _ => { + match range_covered_by_constructor( + cx.tcx, pat.span, constructor, value, value + ) { + Ok(true) => Some(vec![]), + Ok(false) => None, + Err(ErrorReported) => None, + } + } } } @@ -636,29 +724,36 @@ fn specialize<'a, 'tcx>( } } - PatternKind::Array { ref prefix, slice: _, ref suffix } => { - let pat_len = prefix.len() + suffix.len(); - Some( - prefix.iter().chain( - repeat(cx.wild_pattern).take(arity - pat_len).chain( - suffix.iter() - )).collect()) - } - + PatternKind::Array { ref prefix, ref slice, ref suffix } | PatternKind::Slice { ref prefix, ref slice, ref suffix } => { - let pat_len = prefix.len() + suffix.len(); - if let Some(slice_count) = arity.checked_sub(pat_len) { - if slice_count == 0 || slice.is_some() { - Some( - prefix.iter().chain( - repeat(cx.wild_pattern).take(slice_count).chain( - suffix.iter() - )).collect()) - } else { - None + match *constructor { + Slice(..) => { + let pat_len = prefix.len() + suffix.len(); + if let Some(slice_count) = arity.checked_sub(pat_len) { + if slice_count == 0 || slice.is_some() { + Some( + prefix.iter().chain( + repeat(cx.wild_pattern).take(slice_count).chain( + suffix.iter() + )).collect()) + } else { + None + } + } else { + None + } } - } else { - None + ConstantValue(..) => { + match slice_pat_covered_by_constructor( + cx.tcx, pat.span, constructor, prefix, slice, suffix + ) { + Ok(true) => Some(vec![]), + Ok(false) => None, + Err(ErrorReported) => None + } + } + _ => span_bug!(pat.span, + "unexpected ctor {:?} for slice pat", constructor) } } }; diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 8021b36975a42..9aa1ac62f5523 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -175,7 +175,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { } } - MatchCheckCtxt::create_and_enter(self.tcx, |ref cx| { + MatchCheckCtxt::create_and_enter(self.tcx, |ref mut cx| { let mut have_errors = false; let inlined_arms : Vec<(Vec<_>, _)> = arms.iter().map(|arm| ( @@ -235,7 +235,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { "local binding" }; - MatchCheckCtxt::create_and_enter(self.tcx, |ref cx| { + MatchCheckCtxt::create_and_enter(self.tcx, |ref mut cx| { let mut patcx = PatternContext::new(self.tcx); let pats : Matrix = vec![vec![ expand_pattern(cx, patcx.lower_pattern(pat)) @@ -302,8 +302,8 @@ fn pat_is_catchall(dm: &DefMap, pat: &Pat) -> bool { } // Check for unreachable patterns -fn check_arms<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, - arms: &[(Vec<(&Pattern<'tcx>, &'a hir::Pat)>, Option<&hir::Expr>)], +fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>, + arms: &[(Vec<(&'a Pattern<'tcx>, &hir::Pat)>, Option<&hir::Expr>)], source: hir::MatchSource) { let mut seen = Matrix::empty(); @@ -381,7 +381,7 @@ fn check_arms<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, } } -fn check_exhaustive<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, +fn check_exhaustive<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>, sp: Span, matrix: &Matrix<'a, 'tcx>, source: hir::MatchSource) { diff --git a/src/test/compile-fail/match-byte-array-patterns.rs b/src/test/compile-fail/match-byte-array-patterns.rs new file mode 100644 index 0000000000000..86323656b873e --- /dev/null +++ b/src/test/compile-fail/match-byte-array-patterns.rs @@ -0,0 +1,73 @@ +// Copyright 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. + +#![feature(advanced_slice_patterns, slice_patterns)] + +fn main() { + let buf = &[0, 1, 2, 3]; + + match buf { + b"AAAA" => {}, + &[0x41, 0x41, 0x41, 0x41] => {} //~ ERROR unreachable pattern + _ => {} + } + + match buf { + &[0x41, 0x41, 0x41, 0x41] => {} + b"AAAA" => {}, //~ ERROR unreachable pattern + _ => {} + } + + match buf { + &[_, 0x41, 0x41, 0x41] => {}, + b"AAAA" => {}, //~ ERROR unreachable pattern + _ => {} + } + + match buf { + &[0x41, .., 0x41] => {} + b"AAAA" => {}, //~ ERROR unreachable pattern + _ => {} + } + + match buf { //~ ERROR non-exhaustive + b"AAAA" => {} + } + + let buf: &[u8] = buf; + + match buf { + b"AAAA" => {}, + &[0x41, 0x41, 0x41, 0x41] => {} //~ ERROR unreachable pattern + _ => {} + } + + match buf { + &[0x41, 0x41, 0x41, 0x41] => {} + b"AAAA" => {}, //~ ERROR unreachable pattern + _ => {} + } + + match buf { + &[_, 0x41, 0x41, 0x41] => {}, + b"AAAA" => {}, //~ ERROR unreachable pattern + _ => {} + } + + match buf { + &[0x41, .., 0x41] => {} + b"AAAA" => {}, //~ ERROR unreachable pattern + _ => {} + } + + match buf { //~ ERROR non-exhaustive + b"AAAA" => {} + } +} diff --git a/src/test/run-pass/match-byte-array-patterns.rs b/src/test/run-pass/match-byte-array-patterns.rs new file mode 100644 index 0000000000000..dbfe588fb0c6a --- /dev/null +++ b/src/test/run-pass/match-byte-array-patterns.rs @@ -0,0 +1,54 @@ +// Copyright 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. + +#![feature(slice_patterns)] + +fn main() { + let buf = &[0u8; 4]; + match buf { + &[0, 1, 0, 0] => unimplemented!(), + b"true" => unimplemented!(), + _ => {} + } + + match buf { + b"true" => unimplemented!(), + &[0, 1, 0, 0] => unimplemented!(), + _ => {} + } + + match buf { + b"true" => unimplemented!(), + &[0, x, 0, 0] => assert_eq!(x, 0), + _ => unimplemented!(), + } + + let buf: &[u8] = buf; + + match buf { + &[0, 1, 0, 0] => unimplemented!(), + &[_] => unimplemented!(), + &[_, _, _, _, _, ..] => unimplemented!(), + b"true" => unimplemented!(), + _ => {} + } + + match buf { + b"true" => unimplemented!(), + &[0, 1, 0, 0] => unimplemented!(), + _ => {} + } + + match buf { + b"true" => unimplemented!(), + &[0, x, 0, 0] => assert_eq!(x, 0), + _ => unimplemented!(), + } +} From 41578507a616affe87f168dbbc6d11437234bafd Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Wed, 26 Oct 2016 23:52:03 +0300 Subject: [PATCH 12/13] flatten nested slice patterns in HAIR construction nested slice patterns have the same functionality as non-nested ones, so flatten them in HAIR construction. Fixes #26158. --- src/librustc_const_eval/pattern.rs | 53 ++++++++++++++++++++++------ src/test/compile-fail/issue-26158.rs | 16 +++++++++ src/test/run-pass/vec-matching.rs | 15 ++++++++ 3 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 src/test/compile-fail/issue-26158.rs diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs index 4bd797afd0682..a6c886533c86c 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_const_eval/pattern.rs @@ -117,6 +117,7 @@ impl<'a, 'gcx, 'tcx> Pattern<'tcx> { if !pcx.errors.is_empty() { span_bug!(pat.span, "encountered errors lowering pattern: {:?}", pcx.errors) } + debug!("Pattern::from_hir({:?}) = {:?}", pat, result); result } } @@ -346,6 +347,40 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { pat.as_ref().map(|p| self.lower_pattern(p)) } + fn flatten_nested_slice_patterns( + &mut self, + prefix: Vec>, + slice: Option>, + suffix: Vec>) + -> (Vec>, Option>, Vec>) + { + let orig_slice = match slice { + Some(orig_slice) => orig_slice, + None => return (prefix, slice, suffix) + }; + let orig_prefix = prefix; + let orig_suffix = suffix; + + // dance because of intentional borrow-checker stupidity. + let kind = *orig_slice.kind; + match kind { + PatternKind::Slice { prefix, slice, mut suffix } | + PatternKind::Array { prefix, slice, mut suffix } => { + let mut orig_prefix = orig_prefix; + + orig_prefix.extend(prefix); + suffix.extend(orig_suffix); + + (orig_prefix, slice, suffix) + } + _ => { + (orig_prefix, Some(Pattern { + kind: box kind, ..orig_slice + }), orig_suffix) + } + } + } + fn slice_or_array_pattern( &mut self, span: Span, @@ -355,24 +390,22 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { suffix: &[P]) -> PatternKind<'tcx> { + let prefix = self.lower_patterns(prefix); + let slice = self.lower_opt_pattern(slice); + let suffix = self.lower_patterns(suffix); + let (prefix, slice, suffix) = + self.flatten_nested_slice_patterns(prefix, slice, suffix); + match ty.sty { ty::TySlice(..) => { // matching a slice or fixed-length array - PatternKind::Slice { - prefix: self.lower_patterns(prefix), - slice: self.lower_opt_pattern(slice), - suffix: self.lower_patterns(suffix), - } + PatternKind::Slice { prefix: prefix, slice: slice, suffix: suffix } } ty::TyArray(_, len) => { // fixed-length array assert!(len >= prefix.len() + suffix.len()); - PatternKind::Array { - prefix: self.lower_patterns(prefix), - slice: self.lower_opt_pattern(slice), - suffix: self.lower_patterns(suffix), - } + PatternKind::Array { prefix: prefix, slice: slice, suffix: suffix } } _ => { diff --git a/src/test/compile-fail/issue-26158.rs b/src/test/compile-fail/issue-26158.rs new file mode 100644 index 0000000000000..54f5313aed16e --- /dev/null +++ b/src/test/compile-fail/issue-26158.rs @@ -0,0 +1,16 @@ +// Copyright 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. + +#![feature(slice_patterns)] + +fn main() { + let x: &[u32] = &[]; + let &[[ref _a, ref _b..]..] = x; //~ ERROR refutable pattern +} diff --git a/src/test/run-pass/vec-matching.rs b/src/test/run-pass/vec-matching.rs index 97006f54cd955..bd0731a555cb6 100644 --- a/src/test/run-pass/vec-matching.rs +++ b/src/test/run-pass/vec-matching.rs @@ -144,6 +144,20 @@ fn e() { assert_eq!(c, 1); } +fn f() { + let x = &[1, 2, 3, 4, 5]; + let [a, [b, [c, ..].., d].., e] = *x; + assert_eq!((a, b, c, d, e), (1, 2, 3, 4, 5)); + + let x: &[isize] = x; + let (a, b, c, d, e) = match *x { + [a, [b, [c, ..].., d].., e] => (a, b, c, d, e), + _ => unimplemented!() + }; + + assert_eq!((a, b, c, d, e), (1, 2, 3, 4, 5)); +} + pub fn main() { a(); b(); @@ -151,4 +165,5 @@ pub fn main() { c(); d(); e(); + f(); } From 3f9ebb48cf462eb72536b726e6f61bdeafa7e5ce Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 27 Oct 2016 00:38:18 +0300 Subject: [PATCH 13/13] add back test for issue #6804 --- src/test/compile-fail/issue-6804.rs | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/test/compile-fail/issue-6804.rs diff --git a/src/test/compile-fail/issue-6804.rs b/src/test/compile-fail/issue-6804.rs new file mode 100644 index 0000000000000..8f2670725ced7 --- /dev/null +++ b/src/test/compile-fail/issue-6804.rs @@ -0,0 +1,34 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] +#![feature(slice_patterns)] +#![allow(dead_code)] +#![deny(illegal_floating_point_constant_pattern)] + +// Matching against NaN should result in a warning + +use std::f64::NAN; + +#[rustc_error] +fn main() { + let x = NAN; + match x { + NAN => {}, //~ ERROR floating point constants cannot be used + //~| WARNING hard error + _ => {}, + }; + + match [x, 1.0] { + [NAN, _] => {}, //~ ERROR floating point constants cannot be used + //~| WARNING hard error + _ => {}, + }; +}