about summary refs log tree commit diff
diff options
context:
space:
mode:
authoroli <github35764891676564198441@oli-obk.de>2020-11-15 13:04:30 +0000
committeroli <github35764891676564198441@oli-obk.de>2020-11-17 09:07:23 +0000
commita1cdf722f40da3f266496cb7866d271ce370338e (patch)
tree312b4f3e1a924b3bcd27b2c274193370b06e14e7
parent30e49a9ead550551e879af64ba91a0316da1c422 (diff)
downloadrust-a1cdf722f40da3f266496cb7866d271ce370338e.tar.gz
rust-a1cdf722f40da3f266496cb7866d271ce370338e.zip
Fix exhaustiveness in case a byte string literal is used at slice type
-rw-r--r--compiler/rustc_middle/src/ty/context.rs9
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs44
-rw-r--r--compiler/rustc_typeck/src/check/pat.rs8
-rw-r--r--compiler/rustc_typeck/src/check/writeback.rs3
-rw-r--r--src/test/ui/match/type_polymorphic_byte_str_literals.rs36
-rw-r--r--src/test/ui/match/type_polymorphic_byte_str_literals.stderr21
-rw-r--r--src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr4
7 files changed, 113 insertions, 12 deletions
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 3838e1b006f..fdbfafd6142 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -418,6 +418,12 @@ pub struct TypeckResults<'tcx> {
     /// Stores the type, expression, span and optional scope span of all types
     /// that are live across the yield of this generator (if a generator).
     pub generator_interior_types: Vec<GeneratorInteriorTypeCause<'tcx>>,
+
+    /// We sometimes treat byte string literals (which are of type `&[u8; N]`)
+    /// as `&[u8]`, depending on the pattern  in which they are used.
+    /// This hashset records all instances where we behave
+    /// like this to allow `const_to_pat` to reliably handle this situation.
+    pub treat_byte_string_as_slice: ItemLocalSet,
 }
 
 impl<'tcx> TypeckResults<'tcx> {
@@ -443,6 +449,7 @@ impl<'tcx> TypeckResults<'tcx> {
             concrete_opaque_types: Default::default(),
             closure_captures: Default::default(),
             generator_interior_types: Default::default(),
+            treat_byte_string_as_slice: Default::default(),
         }
     }
 
@@ -677,6 +684,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckResults<'tcx> {
             ref concrete_opaque_types,
             ref closure_captures,
             ref generator_interior_types,
+            ref treat_byte_string_as_slice,
         } = *self;
 
         hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
@@ -710,6 +718,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckResults<'tcx> {
             concrete_opaque_types.hash_stable(hcx, hasher);
             closure_captures.hash_stable(hcx, hasher);
             generator_interior_types.hash_stable(hcx, hasher);
+            treat_byte_string_as_slice.hash_stable(hcx, hasher);
         })
     }
 }
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index 6370f8c375b..32fc0f008e9 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -18,6 +18,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
     /// Converts an evaluated constant to a pattern (if possible).
     /// This means aggregate values (like structs and enums) are converted
     /// to a pattern that matches the value (as if you'd compared via structural equality).
+    #[instrument(skip(self))]
     pub(super) fn const_to_pat(
         &self,
         cv: &'tcx ty::Const<'tcx>,
@@ -25,15 +26,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         span: Span,
         mir_structural_match_violation: bool,
     ) -> Pat<'tcx> {
-        debug!("const_to_pat: cv={:#?} id={:?}", cv, id);
-        debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span);
-
         let pat = self.tcx.infer_ctxt().enter(|infcx| {
             let mut convert = ConstToPat::new(self, id, span, infcx);
             convert.to_pat(cv, mir_structural_match_violation)
         });
 
-        debug!("const_to_pat: pat={:?}", pat);
+        debug!(?pat);
         pat
     }
 }
@@ -61,6 +59,8 @@ struct ConstToPat<'a, 'tcx> {
     infcx: InferCtxt<'a, 'tcx>,
 
     include_lint_checks: bool,
+
+    treat_byte_string_as_slice: bool,
 }
 
 mod fallback_to_const_ref {
@@ -88,6 +88,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
         span: Span,
         infcx: InferCtxt<'a, 'tcx>,
     ) -> Self {
+        trace!(?pat_ctxt.typeck_results.hir_owner);
         ConstToPat {
             id,
             span,
@@ -97,6 +98,10 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
             saw_const_match_error: Cell::new(false),
             saw_const_match_lint: Cell::new(false),
             behind_reference: Cell::new(false),
+            treat_byte_string_as_slice: pat_ctxt
+                .typeck_results
+                .treat_byte_string_as_slice
+                .contains(&id.local_id),
         }
     }
 
@@ -153,6 +158,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
         cv: &'tcx ty::Const<'tcx>,
         mir_structural_match_violation: bool,
     ) -> Pat<'tcx> {
+        trace!(self.treat_byte_string_as_slice);
         // This method is just a wrapper handling a validity check; the heavy lifting is
         // performed by the recursive `recur` method, which is not meant to be
         // invoked except by this method.
@@ -384,7 +390,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
                     }
                     PatKind::Wild
                 }
-                // `&str` and `&[u8]` are represented as `ConstValue::Slice`, let's keep using this
+                // `&str` is represented as `ConstValue::Slice`, let's keep using this
                 // optimization for now.
                 ty::Str => PatKind::Constant { value: cv },
                 // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
@@ -393,11 +399,33 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
                 // as slices. This means we turn `&[T; N]` constants into slice patterns, which
                 // has no negative effects on pattern matching, even if we're actually matching on
                 // arrays.
-                ty::Array(..) |
+                ty::Array(..) if !self.treat_byte_string_as_slice => {
+                    let old = self.behind_reference.replace(true);
+                    let array = tcx.deref_const(self.param_env.and(cv));
+                    let val = PatKind::Deref {
+                        subpattern: Pat {
+                            kind: Box::new(PatKind::Array {
+                                prefix: tcx
+                                    .destructure_const(param_env.and(array))
+                                    .fields
+                                    .iter()
+                                    .map(|val| self.recur(val, false))
+                                    .collect::<Result<_, _>>()?,
+                                slice: None,
+                                suffix: vec![],
+                            }),
+                            span,
+                            ty: pointee_ty,
+                        },
+                    };
+                    self.behind_reference.set(old);
+                    val
+                }
+                ty::Array(elem_ty, _) |
                 // Cannot merge this with the catch all branch below, because the `const_deref`
                 // changes the type from slice to array, we need to keep the original type in the
                 // pattern.
-                ty::Slice(..) => {
+                ty::Slice(elem_ty) => {
                     let old = self.behind_reference.replace(true);
                     let array = tcx.deref_const(self.param_env.and(cv));
                     let val = PatKind::Deref {
@@ -413,7 +441,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
                                 suffix: vec![],
                             }),
                             span,
-                            ty: pointee_ty,
+                            ty: tcx.mk_slice(elem_ty),
                         },
                     };
                     self.behind_reference.set(old);
diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs
index 6489b7838d6..fa7898f03e3 100644
--- a/compiler/rustc_typeck/src/check/pat.rs
+++ b/compiler/rustc_typeck/src/check/pat.rs
@@ -149,6 +149,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ///
     /// Outside of this module, `check_pat_top` should always be used.
     /// Conversely, inside this module, `check_pat_top` should never be used.
+    #[instrument(skip(self, ti))]
     fn check_pat(
         &self,
         pat: &'tcx Pat<'tcx>,
@@ -156,8 +157,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         def_bm: BindingMode,
         ti: TopInfo<'tcx>,
     ) {
-        debug!("check_pat(pat={:?},expected={:?},def_bm={:?})", pat, expected, def_bm);
-
         let path_res = match &pat.kind {
             PatKind::Path(qpath) => Some(self.resolve_ty_and_res_ufcs(qpath, pat.hir_id, pat.span)),
             _ => None,
@@ -398,6 +397,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             if let ty::Ref(_, inner_ty, _) = expected.kind() {
                 if matches!(inner_ty.kind(), ty::Slice(_)) {
                     let tcx = self.tcx;
+                    trace!(?lt.hir_id.local_id, "polymorphic byte string lit");
+                    self.typeck_results
+                        .borrow_mut()
+                        .treat_byte_string_as_slice
+                        .insert(lt.hir_id.local_id);
                     pat_ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_slice(tcx.types.u8));
                 }
             }
diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs
index 5363702a5be..9c22459e272 100644
--- a/compiler/rustc_typeck/src/check/writeback.rs
+++ b/compiler/rustc_typeck/src/check/writeback.rs
@@ -70,6 +70,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports);
         wbcx.typeck_results.used_trait_imports = used_trait_imports;
 
+        wbcx.typeck_results.treat_byte_string_as_slice =
+            mem::take(&mut self.typeck_results.borrow_mut().treat_byte_string_as_slice);
+
         wbcx.typeck_results.closure_captures =
             mem::take(&mut self.typeck_results.borrow_mut().closure_captures);
 
diff --git a/src/test/ui/match/type_polymorphic_byte_str_literals.rs b/src/test/ui/match/type_polymorphic_byte_str_literals.rs
new file mode 100644
index 00000000000..cb44c1da76b
--- /dev/null
+++ b/src/test/ui/match/type_polymorphic_byte_str_literals.rs
@@ -0,0 +1,36 @@
+#[deny(unreachable_patterns)]
+
+fn parse_data1(data: &[u8]) -> u32 {
+    match data {
+        b"" => 1,
+        _ => 2,
+    }
+}
+
+fn parse_data2(data: &[u8]) -> u32 {
+    match data { //~ ERROR non-exhaustive patterns: `&[_, ..]` not covered
+        b"" => 1,
+    }
+}
+
+fn parse_data3(data: &[u8; 0]) -> u8 {
+    match data {
+        b"" => 1,
+    }
+}
+
+fn parse_data4(data: &[u8]) -> u8 {
+    match data { //~ ERROR non-exhaustive patterns
+        b"aaa" => 0,
+        [_, _, _] => 1,
+    }
+}
+
+fn parse_data5(data: &[u8; 3]) -> u8 {
+    match data {
+        b"aaa" => 0,
+        [_, _, _] => 1,
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/match/type_polymorphic_byte_str_literals.stderr b/src/test/ui/match/type_polymorphic_byte_str_literals.stderr
new file mode 100644
index 00000000000..6ce53a4f21e
--- /dev/null
+++ b/src/test/ui/match/type_polymorphic_byte_str_literals.stderr
@@ -0,0 +1,21 @@
+error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered
+  --> $DIR/type_polymorphic_byte_str_literals.rs:11:11
+   |
+LL |     match data {
+   |           ^^^^ pattern `&[_, ..]` not covered
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+   = note: the matched value is of type `&[u8]`
+
+error[E0004]: non-exhaustive patterns: `&[]`, `&[_]`, `&[_, _]` and 1 more not covered
+  --> $DIR/type_polymorphic_byte_str_literals.rs:23:11
+   |
+LL |     match data {
+   |           ^^^^ patterns `&[]`, `&[_]`, `&[_, _]` and 1 more not covered
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+   = note: the matched value is of type `&[u8]`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr
index 7968f9713ff..ffc8433403f 100644
--- a/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr
+++ b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr
@@ -7,11 +7,11 @@ LL |     match buf {
    = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
    = note: the matched value is of type `&[u8; 4]`
 
-error[E0004]: non-exhaustive patterns: `&[0_u8..=64_u8, _, _, _]` and `&[66_u8..=u8::MAX, _, _, _]` not covered
+error[E0004]: non-exhaustive patterns: `&[]`, `&[_]`, `&[_, _]` and 2 more not covered
   --> $DIR/match-byte-array-patterns-2.rs:10:11
    |
 LL |     match buf {
-   |           ^^^ patterns `&[0_u8..=64_u8, _, _, _]` and `&[66_u8..=u8::MAX, _, _, _]` not covered
+   |           ^^^ patterns `&[]`, `&[_]`, `&[_, _]` and 2 more not covered
    |
    = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
    = note: the matched value is of type `&[u8]`