about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRyo Yoshida <low.ryoshida@gmail.com>2022-08-10 19:17:13 +0900
committerRyo Yoshida <low.ryoshida@gmail.com>2022-08-10 19:17:13 +0900
commitffc6b42901fc1f64c812df571110e665de75da45 (patch)
tree76c2a39f30c4b2d9f1680a9519119a2dfad5b9ff
parente1e93c44389d822514e89ab4f2f31be30a7126d6 (diff)
downloadrust-ffc6b42901fc1f64c812df571110e665de75da45.tar.gz
rust-ffc6b42901fc1f64c812df571110e665de75da45.zip
fix: infer byte string pattern as `&[u8]` when matched against slices
-rw-r--r--crates/hir-ty/src/infer/pat.rs33
-rw-r--r--crates/hir-ty/src/tests/patterns.rs45
2 files changed, 74 insertions, 4 deletions
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index 5e7320a5dd3..53259d66dec 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -14,8 +14,9 @@ use crate::{
     consteval::intern_const_scalar,
     infer::{BindingMode, Expectation, InferenceContext, TypeMismatch},
     lower::lower_to_chalk_mutability,
-    static_lifetime, ConcreteConst, ConstValue, Interner, Substitution, Ty, TyBuilder, TyExt,
-    TyKind,
+    primitive::UintTy,
+    static_lifetime, ConcreteConst, ConstValue, Interner, Scalar, Substitution, Ty, TyBuilder,
+    TyExt, TyKind,
 };
 
 use super::PatLike;
@@ -294,7 +295,29 @@ impl<'a> InferenceContext<'a> {
                 let start_ty = self.infer_expr(*start, &Expectation::has_type(expected.clone()));
                 self.infer_expr(*end, &Expectation::has_type(start_ty))
             }
-            Pat::Lit(expr) => self.infer_expr(*expr, &Expectation::has_type(expected.clone())),
+            &Pat::Lit(expr) => {
+                // FIXME: using `Option` here is a workaround until we can use if-let chains in stable.
+                let mut pat_ty = None;
+
+                // Like slice patterns, byte string patterns can denote both `&[u8; N]` and `&[u8]`.
+                if let Expr::Literal(Literal::ByteString(_)) = self.body[expr] {
+                    if let Some((inner, ..)) = expected.as_reference() {
+                        let inner = self.resolve_ty_shallow(inner);
+                        if matches!(inner.kind(Interner), TyKind::Slice(_)) {
+                            let elem_ty = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner);
+                            let slice_ty = TyKind::Slice(elem_ty).intern(Interner);
+                            let ty = TyKind::Ref(Mutability::Not, static_lifetime(), slice_ty)
+                                .intern(Interner);
+                            self.write_expr_ty(expr, ty.clone());
+                            pat_ty = Some(ty);
+                        }
+                    }
+                }
+
+                pat_ty.unwrap_or_else(|| {
+                    self.infer_expr(expr, &Expectation::has_type(expected.clone()))
+                })
+            }
             Pat::Box { inner } => match self.resolve_boxed_box() {
                 Some(box_adt) => {
                     let (inner_ty, alloc_ty) = match expected.as_adt() {
@@ -343,7 +366,9 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
         // FIXME: ConstBlock/Path/Lit might actually evaluate to ref, but inference is unimplemented.
         Pat::Path(..) => true,
         Pat::ConstBlock(..) => true,
-        Pat::Lit(expr) => !matches!(body[*expr], Expr::Literal(Literal::String(..))),
+        Pat::Lit(expr) => {
+            !matches!(body[*expr], Expr::Literal(Literal::String(..) | Literal::ByteString(..)))
+        }
         Pat::Bind {
             mode: BindingAnnotation::Mutable | BindingAnnotation::Unannotated,
             subpat: Some(subpat),
diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs
index 399553356b0..94efe7bc11a 100644
--- a/crates/hir-ty/src/tests/patterns.rs
+++ b/crates/hir-ty/src/tests/patterns.rs
@@ -316,6 +316,51 @@ fn infer_pattern_match_string_literal() {
 }
 
 #[test]
+fn infer_pattern_match_byte_string_literal() {
+    check_infer_with_mismatches(
+        r#"
+        //- minicore: index
+        struct S;
+        impl<T, const N: usize> core::ops::Index<S> for [T; N] {
+            type Output = [u8];
+            fn index(&self, index: core::ops::RangeFull) -> &Self::Output {
+                loop {}
+            }
+        }
+        fn test(v: [u8; 3]) {
+            if let b"foo" = &v[S] {}
+            if let b"foo" = &v {}
+        }
+        "#,
+        expect![[r#"
+            105..109 'self': &[T; N]
+            111..116 'index': {unknown}
+            157..180 '{     ...     }': &[u8]
+            167..174 'loop {}': !
+            172..174 '{}': ()
+            191..192 'v': [u8; 3]
+            203..261 '{     ...v {} }': ()
+            209..233 'if let...[S] {}': ()
+            212..230 'let b"... &v[S]': bool
+            216..222 'b"foo"': &[u8]
+            216..222 'b"foo"': &[u8]
+            225..230 '&v[S]': &[u8]
+            226..227 'v': [u8; 3]
+            226..230 'v[S]': [u8]
+            228..229 'S': S
+            231..233 '{}': ()
+            238..259 'if let... &v {}': ()
+            241..256 'let b"foo" = &v': bool
+            245..251 'b"foo"': &[u8; 3]
+            245..251 'b"foo"': &[u8; 3]
+            254..256 '&v': &[u8; 3]
+            255..256 'v': [u8; 3]
+            257..259 '{}': ()
+        "#]],
+    );
+}
+
+#[test]
 fn infer_pattern_match_or() {
     check_infer_with_mismatches(
         r#"