about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs35
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs28
2 files changed, 62 insertions, 1 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
index ca8d5bae5e5..83b13460474 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
@@ -6,12 +6,13 @@ use hir_def::{
     expr_store::Body,
     hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, Literal, Pat, PatId},
     path::Path,
+    HasModule,
 };
 use hir_expand::name::Name;
 use stdx::TupleExt;
 
 use crate::{
-    consteval::{try_const_usize, usize_const},
+    consteval::{self, try_const_usize, usize_const},
     infer::{
         coerce::CoerceNever, expr::ExprIsRead, BindingMode, Expectation, InferenceContext,
         TypeMismatch,
@@ -479,6 +480,19 @@ impl InferenceContext<'_> {
         suffix: &[PatId],
         default_bm: BindingMode,
     ) -> Ty {
+        let expected = self.resolve_ty_shallow(expected);
+
+        // If `expected` is an infer ty, we try to equate it to an array if the given pattern
+        // allows it. See issue #16609
+        if expected.is_ty_var() {
+            if let Some(resolved_array_ty) =
+                self.try_resolve_slice_ty_to_array_ty(prefix, suffix, slice)
+            {
+                self.unify(&expected, &resolved_array_ty);
+            }
+        }
+
+        let expected = self.resolve_ty_shallow(&expected);
         let elem_ty = match expected.kind(Interner) {
             TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(),
             _ => self.err_ty(),
@@ -553,6 +567,25 @@ impl InferenceContext<'_> {
             | Pat::Expr(_) => false,
         }
     }
+
+    fn try_resolve_slice_ty_to_array_ty(
+        &mut self,
+        before: &[PatId],
+        suffix: &[PatId],
+        slice: &Option<PatId>,
+    ) -> Option<Ty> {
+        if !slice.is_none() {
+            return None;
+        }
+
+        let len = before.len() + suffix.len();
+        let size =
+            consteval::usize_const(self.db, Some(len as u128), self.owner.krate(self.db.upcast()));
+
+        let elem_ty = self.table.new_type_var();
+        let array_ty = TyKind::Array(elem_ty.clone(), size).intern(Interner);
+        Some(array_ty)
+    }
 }
 
 pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
index 15636604570..9e0920e41ba 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
@@ -3814,3 +3814,31 @@ async fn foo(a: (), b: i32) -> u32 {
         "#,
     );
 }
+
+#[test]
+fn irrefutable_slices() {
+    check_infer(
+        r#"
+//- minicore: from
+struct A;
+
+impl From<A> for [u8; 2] {
+    fn from(a: A) -> Self {
+        [0; 2]
+    }
+}
+impl From<A> for [u8; 3] {
+    fn from(a: A) -> Self {
+        [0; 3]
+    }
+}
+
+
+fn main() {
+    let a = A;
+    let [b, c] = a.into();
+}
+"#,
+        expect![],
+    );
+}