about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorAriel Ben-Yehuda <ariel.byd@gmail.com>2016-10-26 22:38:22 +0300
committerAriel Ben-Yehuda <ariel.byd@gmail.com>2016-10-26 23:10:30 +0300
commit8d3e89b484bbcd1c2fec60794c196d7148dd9f5b (patch)
tree184f6b4c9720acf8ea0316b3c9ec86f8cbccfc55 /src
parent76fb7d90ecde3659021341779fea598a6daab013 (diff)
downloadrust-8d3e89b484bbcd1c2fec60794c196d7148dd9f5b.tar.gz
rust-8d3e89b484bbcd1c2fec60794c196d7148dd9f5b.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/librustc_const_eval/_match.rs185
-rw-r--r--src/librustc_const_eval/check_match.rs10
-rw-r--r--src/test/compile-fail/match-byte-array-patterns.rs73
-rw-r--r--src/test/run-pass/match-byte-array-patterns.rs54
4 files changed, 272 insertions, 50 deletions
diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs
index d70deb2f862..7f5eb31612c 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<Pattern<'tcx>>,
+    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<Constructor> {
     let used_constructors: Vec<Constructor> =
         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<Constructor> {
+fn all_constructors(_cx: &mut MatchCheckCtxt, pcx: PatternContext) -> Vec<Constructor> {
     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<Constructo
 ///
 /// 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>,
+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<Vec<Constructor>>
@@ -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<Pattern>,
+                                    suffix: &[Pattern])
+                                    -> Result<bool, ErrorReported> {
+    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<Vec<&'a Pattern<'tcx>>>
@@ -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 8021b36975a..9aa1ac62f55 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 00000000000..86323656b87
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(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 00000000000..dbfe588fb0c
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(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!(),
+    }
+}