about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-08-03 11:48:33 +0000
committerbors <bors@rust-lang.org>2023-08-03 11:48:33 +0000
commitfcf3006e0133365ecd26894689c086387edcbecb (patch)
treec2c51f1713a61cf3520fdf1748fa153e63fcfb9d
parentc115ec11d2087050dc12c5c83959979aa98bb3e5 (diff)
parent5a9af37370bc220bc395939d878c64547e3466d0 (diff)
downloadrust-fcf3006e0133365ecd26894689c086387edcbecb.tar.gz
rust-fcf3006e0133365ecd26894689c086387edcbecb.zip
Auto merge of #113199 - b-naber:slice-pattern-type-inference, r=lcnr
Infer type in irrefutable slice patterns with fixed length as array

Fixes https://github.com/rust-lang/rust/issues/76342

In irrefutable slice patterns with a fixed length, we can infer the type as an array type. We now choose to prefer some implementations over others, e.g. in:

```
struct Zeroes;

const ARR: [usize; 2] = [0; 2];
const ARR2: [usize; 2] = [2; 2];

impl Into<&'static [usize; 2]> for Zeroes {
    fn into(self) -> &'static [usize; 2] {
        &ARR
    }
}

impl Into<&'static [usize]> for Zeroes {
    fn into(self) -> &'static [usize] {
        &ARR2
    }
}

fn main() {
    let &[a, b] = Zeroes.into();
}
```

We now prefer the impl candidate `impl Into<&'static [usize; 2]> for Zeroes`, it's not entirely clear to me that this is correct, but given that the slice impl would require a type annotation anyway, this doesn't seem unreasonable.

r? `@lcnr`
-rw-r--r--compiler/rustc_hir_typeck/src/_match.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/check.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/gather_locals.rs26
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs200
-rw-r--r--tests/ui/array-slice-vec/infer_array_len.rs4
-rw-r--r--tests/ui/array-slice-vec/infer_array_len.stderr14
-rw-r--r--tests/ui/array-slice-vec/slice-pat-type-mismatches.rs15
-rw-r--r--tests/ui/array-slice-vec/slice-pat-type-mismatches.stderr2
-rw-r--r--tests/ui/destructuring-assignment/slice_destructure_fail.rs10
-rw-r--r--tests/ui/destructuring-assignment/slice_destructure_fail.stderr22
-rw-r--r--tests/ui/pattern/slice-array-infer.rs27
-rw-r--r--tests/ui/pattern/slice-pattern-refutable.rs36
-rw-r--r--tests/ui/pattern/slice-pattern-refutable.stderr40
-rw-r--r--tests/ui/pattern/slice-patterns-ambiguity.rs47
-rw-r--r--tests/ui/pattern/slice-patterns-ambiguity.stderr40
-rw-r--r--tests/ui/pattern/slice-patterns-irrefutable.rs74
-rw-r--r--tests/ui/pattern/slice-patterns-irrefutable.stderr14
-rw-r--r--tests/ui/pattern/slice-patterns-nested.rs15
19 files changed, 489 insertions, 105 deletions
diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs
index c2d987a95cf..6d926cd8aa1 100644
--- a/compiler/rustc_hir_typeck/src/_match.rs
+++ b/compiler/rustc_hir_typeck/src/_match.rs
@@ -41,7 +41,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // #55810: Type check patterns first so we get types for all bindings.
         let scrut_span = scrut.span.find_ancestor_inside(expr.span).unwrap_or(scrut.span);
         for arm in arms {
-            self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), Some(scrut));
+            self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), Some(scrut), None);
         }
 
         // Now typecheck the blocks.
diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs
index 53bae315d78..1fc1e5aca2b 100644
--- a/compiler/rustc_hir_typeck/src/check.rs
+++ b/compiler/rustc_hir_typeck/src/check.rs
@@ -89,7 +89,7 @@ pub(super) fn check_fn<'a, 'tcx>(
     for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() {
         // Check the pattern.
         let ty_span = try { inputs_hir?.get(idx)?.span };
-        fcx.check_pat_top(&param.pat, param_ty, ty_span, None);
+        fcx.check_pat_top(&param.pat, param_ty, ty_span, None, None);
 
         // Check that argument is Sized.
         if !params_can_be_unsized {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 94c54197294..c63dab63145 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -1463,11 +1463,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
 
         // Type check the pattern. Override if necessary to avoid knock-on errors.
-        self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr);
+        self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr, Some(decl.origin));
         let pat_ty = self.node_ty(decl.pat.hir_id);
         self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, pat_ty);
 
-        if let Some(blk) = decl.els {
+        if let Some(blk) = decl.origin.try_get_else() {
             let previous_diverges = self.diverges.get();
             let else_ty = self.check_block_with_expected(blk, NoExpectation);
             let cause = self.cause(blk.span, ObligationCauseCode::LetElse);
diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs
index 4f45a24b216..ed4c63f171c 100644
--- a/compiler/rustc_hir_typeck/src/gather_locals.rs
+++ b/compiler/rustc_hir_typeck/src/gather_locals.rs
@@ -9,6 +9,26 @@ use rustc_span::def_id::LocalDefId;
 use rustc_span::Span;
 use rustc_trait_selection::traits;
 
+/// Provides context for checking patterns in declarations. More specifically this
+/// allows us to infer array types if the pattern is irrefutable and allows us to infer
+/// the size of the array. See issue #76342.
+#[derive(Debug, Copy, Clone)]
+pub(super) enum DeclOrigin<'a> {
+    // from an `if let` expression
+    LetExpr,
+    // from `let x = ..`
+    LocalDecl { els: Option<&'a hir::Block<'a>> },
+}
+
+impl<'a> DeclOrigin<'a> {
+    pub(super) fn try_get_else(&self) -> Option<&'a hir::Block<'a>> {
+        match self {
+            Self::LocalDecl { els } => *els,
+            Self::LetExpr => None,
+        }
+    }
+}
+
 /// A declaration is an abstraction of [hir::Local] and [hir::Let].
 ///
 /// It must have a hir_id, as this is how we connect gather_locals to the check functions.
@@ -18,20 +38,20 @@ pub(super) struct Declaration<'a> {
     pub ty: Option<&'a hir::Ty<'a>>,
     pub span: Span,
     pub init: Option<&'a hir::Expr<'a>>,
-    pub els: Option<&'a hir::Block<'a>>,
+    pub origin: DeclOrigin<'a>,
 }
 
 impl<'a> From<&'a hir::Local<'a>> for Declaration<'a> {
     fn from(local: &'a hir::Local<'a>) -> Self {
         let hir::Local { hir_id, pat, ty, span, init, els, source: _ } = *local;
-        Declaration { hir_id, pat, ty, span, init, els }
+        Declaration { hir_id, pat, ty, span, init, origin: DeclOrigin::LocalDecl { els } }
     }
 }
 
 impl<'a> From<&'a hir::Let<'a>> for Declaration<'a> {
     fn from(let_expr: &'a hir::Let<'a>) -> Self {
         let hir::Let { hir_id, pat, ty, span, init } = *let_expr;
-        Declaration { hir_id, pat, ty, span, init: Some(init), els: None }
+        Declaration { hir_id, pat, ty, span, init: Some(init), origin: DeclOrigin::LetExpr }
     }
 }
 
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index d8eb8c71b5e..8fc236f46b2 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -1,3 +1,4 @@
+use crate::gather_locals::DeclOrigin;
 use crate::{errors, FnCtxt, RawTy};
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashMap;
@@ -77,6 +78,13 @@ struct TopInfo<'tcx> {
     span: Option<Span>,
 }
 
+#[derive(Copy, Clone)]
+struct PatInfo<'tcx, 'a> {
+    binding_mode: BindingMode,
+    top_info: TopInfo<'tcx>,
+    decl_origin: Option<DeclOrigin<'a>>,
+}
+
 impl<'tcx> FnCtxt<'_, 'tcx> {
     fn pattern_cause(&self, ti: TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> {
         let code =
@@ -135,15 +143,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ///
     /// Otherwise, `Some(span)` represents the span of a type expression
     /// which originated the `expected` type.
-    pub fn check_pat_top(
+    pub(crate) fn check_pat_top(
         &self,
         pat: &'tcx Pat<'tcx>,
         expected: Ty<'tcx>,
         span: Option<Span>,
         origin_expr: Option<&'tcx hir::Expr<'tcx>>,
+        decl_origin: Option<DeclOrigin<'tcx>>,
     ) {
         let info = TopInfo { expected, origin_expr, span };
-        self.check_pat(pat, expected, INITIAL_BM, info);
+        let pat_info = PatInfo { binding_mode: INITIAL_BM, top_info: info, decl_origin };
+        self.check_pat(pat, expected, pat_info);
     }
 
     /// Type check the given `pat` against the `expected` type
@@ -151,14 +161,9 @@ 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(level = "debug", skip(self, ti))]
-    fn check_pat(
-        &self,
-        pat: &'tcx Pat<'tcx>,
-        expected: Ty<'tcx>,
-        def_bm: BindingMode,
-        ti: TopInfo<'tcx>,
-    ) {
+    #[instrument(level = "debug", skip(self, pat_info))]
+    fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>) {
+        let PatInfo { binding_mode: def_bm, top_info: ti, .. } = pat_info;
         let path_res = match &pat.kind {
             PatKind::Path(qpath) => {
                 Some(self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span))
@@ -167,38 +172,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
         let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res));
         let (expected, def_bm) = self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode);
+        let pat_info =
+            PatInfo { binding_mode: def_bm, top_info: ti, decl_origin: pat_info.decl_origin };
 
         let ty = match pat.kind {
             PatKind::Wild => expected,
             PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, ti),
             PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti),
             PatKind::Binding(ba, var_id, _, sub) => {
-                self.check_pat_ident(pat, ba, var_id, sub, expected, def_bm, ti)
+                self.check_pat_ident(pat, ba, var_id, sub, expected, pat_info)
             }
             PatKind::TupleStruct(ref qpath, subpats, ddpos) => {
-                self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, def_bm, ti)
+                self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, pat_info)
             }
             PatKind::Path(ref qpath) => {
                 self.check_pat_path(pat, qpath, path_res.unwrap(), expected, ti)
             }
             PatKind::Struct(ref qpath, fields, has_rest_pat) => {
-                self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, def_bm, ti)
+                self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, pat_info)
             }
             PatKind::Or(pats) => {
                 for pat in pats {
-                    self.check_pat(pat, expected, def_bm, ti);
+                    self.check_pat(pat, expected, pat_info);
                 }
                 expected
             }
             PatKind::Tuple(elements, ddpos) => {
-                self.check_pat_tuple(pat.span, elements, ddpos, expected, def_bm, ti)
-            }
-            PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, def_bm, ti),
-            PatKind::Ref(inner, mutbl) => {
-                self.check_pat_ref(pat, inner, mutbl, expected, def_bm, ti)
+                self.check_pat_tuple(pat.span, elements, ddpos, expected, pat_info)
             }
+            PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info),
+            PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info),
             PatKind::Slice(before, slice, after) => {
-                self.check_pat_slice(pat.span, before, slice, after, expected, def_bm, ti)
+                self.check_pat_slice(pat.span, before, slice, after, expected, pat_info)
             }
         };
 
@@ -580,9 +585,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         var_id: HirId,
         sub: Option<&'tcx Pat<'tcx>>,
         expected: Ty<'tcx>,
-        def_bm: BindingMode,
-        ti: TopInfo<'tcx>,
+        pat_info: PatInfo<'tcx, '_>,
     ) -> Ty<'tcx> {
+        let PatInfo { binding_mode: def_bm, top_info: ti, .. } = pat_info;
+
         // Determine the binding mode...
         let bm = match ba {
             hir::BindingAnnotation::NONE => def_bm,
@@ -620,7 +626,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         if let Some(p) = sub {
-            self.check_pat(p, expected, def_bm, ti);
+            self.check_pat(p, expected, pat_info);
         }
 
         local_ty
@@ -843,8 +849,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         fields: &'tcx [hir::PatField<'tcx>],
         has_rest_pat: bool,
         expected: Ty<'tcx>,
-        def_bm: BindingMode,
-        ti: TopInfo<'tcx>,
+        pat_info: PatInfo<'tcx, '_>,
     ) -> Ty<'tcx> {
         // Resolve the path and check the definition for errors.
         let (variant, pat_ty) = match self.check_struct_path(qpath, pat.hir_id) {
@@ -852,18 +857,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Err(guar) => {
                 let err = Ty::new_error(self.tcx, guar);
                 for field in fields {
-                    let ti = ti;
-                    self.check_pat(field.pat, err, def_bm, ti);
+                    self.check_pat(field.pat, err, pat_info);
                 }
                 return err;
             }
         };
 
         // Type-check the path.
-        self.demand_eqtype_pat(pat.span, expected, pat_ty, ti);
+        self.demand_eqtype_pat(pat.span, expected, pat_ty, pat_info.top_info);
 
         // Type-check subpatterns.
-        if self.check_struct_pat_fields(pat_ty, &pat, variant, fields, has_rest_pat, def_bm, ti) {
+        if self.check_struct_pat_fields(pat_ty, &pat, variant, fields, has_rest_pat, pat_info) {
             pat_ty
         } else {
             Ty::new_misc_error(self.tcx)
@@ -1029,13 +1033,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         subpats: &'tcx [Pat<'tcx>],
         ddpos: hir::DotDotPos,
         expected: Ty<'tcx>,
-        def_bm: BindingMode,
-        ti: TopInfo<'tcx>,
+        pat_info: PatInfo<'tcx, '_>,
     ) -> Ty<'tcx> {
+        let PatInfo { binding_mode: def_bm, top_info: ti, decl_origin } = pat_info;
         let tcx = self.tcx;
         let on_error = |e| {
             for pat in subpats {
-                self.check_pat(pat, Ty::new_error(tcx, e), def_bm, ti);
+                self.check_pat(
+                    pat,
+                    Ty::new_error(tcx, e),
+                    PatInfo { binding_mode: def_bm, top_info: ti, decl_origin },
+                );
             }
         };
         let report_unexpected_res = |res: Res| {
@@ -1101,7 +1109,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) {
                 let field = &variant.fields[FieldIdx::from_usize(i)];
                 let field_ty = self.field_ty(subpat.span, field, args);
-                self.check_pat(subpat, field_ty, def_bm, ti);
+                self.check_pat(
+                    subpat,
+                    field_ty,
+                    PatInfo { binding_mode: def_bm, top_info: ti, decl_origin },
+                );
 
                 self.tcx.check_stability(
                     variant.fields[FieldIdx::from_usize(i)].did,
@@ -1285,8 +1297,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         elements: &'tcx [Pat<'tcx>],
         ddpos: hir::DotDotPos,
         expected: Ty<'tcx>,
-        def_bm: BindingMode,
-        ti: TopInfo<'tcx>,
+        pat_info: PatInfo<'tcx, '_>,
     ) -> Ty<'tcx> {
         let tcx = self.tcx;
         let mut expected_len = elements.len();
@@ -1307,18 +1318,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         });
         let element_tys = tcx.mk_type_list_from_iter(element_tys_iter);
         let pat_ty = Ty::new_tup(tcx, element_tys);
-        if let Some(mut err) = self.demand_eqtype_pat_diag(span, expected, pat_ty, ti) {
+        if let Some(mut err) =
+            self.demand_eqtype_pat_diag(span, expected, pat_ty, pat_info.top_info)
+        {
             let reported = err.emit();
             // Walk subpatterns with an expected type of `err` in this case to silence
             // further errors being emitted when using the bindings. #50333
             let element_tys_iter = (0..max_len).map(|_| Ty::new_error(tcx, reported));
             for (_, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) {
-                self.check_pat(elem, Ty::new_error(tcx, reported), def_bm, ti);
+                self.check_pat(elem, Ty::new_error(tcx, reported), pat_info);
             }
             Ty::new_tup_from_iter(tcx, element_tys_iter)
         } else {
             for (i, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) {
-                self.check_pat(elem, element_tys[i], def_bm, ti);
+                self.check_pat(elem, element_tys[i], pat_info);
             }
             pat_ty
         }
@@ -1331,8 +1344,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         variant: &'tcx ty::VariantDef,
         fields: &'tcx [hir::PatField<'tcx>],
         has_rest_pat: bool,
-        def_bm: BindingMode,
-        ti: TopInfo<'tcx>,
+        pat_info: PatInfo<'tcx, '_>,
     ) -> bool {
         let tcx = self.tcx;
 
@@ -1379,7 +1391,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
             };
 
-            self.check_pat(field.pat, field_ty, def_bm, ti);
+            self.check_pat(field.pat, field_ty, pat_info);
         }
 
         let mut unmentioned_fields = variant
@@ -1937,8 +1949,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         span: Span,
         inner: &'tcx Pat<'tcx>,
         expected: Ty<'tcx>,
-        def_bm: BindingMode,
-        ti: TopInfo<'tcx>,
+        pat_info: PatInfo<'tcx, '_>,
     ) -> Ty<'tcx> {
         let tcx = self.tcx;
         let (box_ty, inner_ty) = match self.check_dereferenceable(span, expected, inner) {
@@ -1950,7 +1961,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     span: inner.span,
                 });
                 let box_ty = Ty::new_box(tcx, inner_ty);
-                self.demand_eqtype_pat(span, expected, box_ty, ti);
+                self.demand_eqtype_pat(span, expected, box_ty, pat_info.top_info);
                 (box_ty, inner_ty)
             }
             Err(guar) => {
@@ -1958,7 +1969,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 (err, err)
             }
         };
-        self.check_pat(inner, inner_ty, def_bm, ti);
+        self.check_pat(inner, inner_ty, pat_info);
         box_ty
     }
 
@@ -1969,8 +1980,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         inner: &'tcx Pat<'tcx>,
         mutbl: hir::Mutability,
         expected: Ty<'tcx>,
-        def_bm: BindingMode,
-        ti: TopInfo<'tcx>,
+        pat_info: PatInfo<'tcx, '_>,
     ) -> Ty<'tcx> {
         let tcx = self.tcx;
         let expected = self.shallow_resolve(expected);
@@ -1992,7 +2002,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         });
                         let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty);
                         debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty);
-                        let err = self.demand_eqtype_pat_diag(pat.span, expected, ref_ty, ti);
+                        let err = self.demand_eqtype_pat_diag(
+                            pat.span,
+                            expected,
+                            ref_ty,
+                            pat_info.top_info,
+                        );
 
                         // Look for a case like `fn foo(&foo: u32)` and suggest
                         // `fn foo(foo: &u32)`
@@ -2009,7 +2024,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 (err, err)
             }
         };
-        self.check_pat(inner, inner_ty, def_bm, ti);
+        self.check_pat(inner, inner_ty, pat_info);
         ref_ty
     }
 
@@ -2020,6 +2035,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         Ty::new_ref(self.tcx, region, mt)
     }
 
+    fn try_resolve_slice_ty_to_array_ty(
+        &self,
+        before: &'tcx [Pat<'tcx>],
+        slice: Option<&'tcx Pat<'tcx>>,
+        span: Span,
+    ) -> Option<Ty<'tcx>> {
+        if !slice.is_none() {
+            return None;
+        }
+
+        let tcx = self.tcx;
+        let len = before.len();
+        let ty_var_origin =
+            TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span };
+        let inner_ty = self.next_ty_var(ty_var_origin);
+
+        Some(Ty::new_array(tcx, inner_ty, len.try_into().unwrap()))
+    }
+
+    /// Used to determines whether we can infer the expected type in the slice pattern to be of type array.
+    /// This is only possible if we're in an irrefutable pattern. If we were to allow this in refutable
+    /// patterns we wouldn't e.g. report ambiguity in the following situation:
+    ///
+    /// ```ignore(rust)
+    /// struct Zeroes;
+    ///    const ARR: [usize; 2] = [0; 2];
+    ///    const ARR2: [usize; 2] = [2; 2];
+    ///
+    ///    impl Into<&'static [usize; 2]> for Zeroes {
+    ///        fn into(self) -> &'static [usize; 2] {
+    ///            &ARR
+    ///        }
+    ///    }
+    ///
+    ///    impl Into<&'static [usize]> for Zeroes {
+    ///        fn into(self) -> &'static [usize] {
+    ///            &ARR2
+    ///        }
+    ///    }
+    ///
+    ///    fn main() {
+    ///        let &[a, b]: &[usize] = Zeroes.into() else {
+    ///           ..
+    ///        };
+    ///    }
+    /// ```
+    ///
+    /// If we're in an irrefutable pattern we prefer the array impl candidate given that
+    /// the slice impl candidate would be be rejected anyway (if no ambiguity existed).
+    fn pat_is_irrefutable(&self, decl_origin: Option<DeclOrigin<'_>>) -> bool {
+        match decl_origin {
+            Some(DeclOrigin::LocalDecl { els: None }) => true,
+            Some(DeclOrigin::LocalDecl { els: Some(_) } | DeclOrigin::LetExpr) | None => false,
+        }
+    }
+
     /// Type check a slice pattern.
     ///
     /// Syntactically, these look like `[pat_0, ..., pat_n]`.
@@ -2037,10 +2108,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         slice: Option<&'tcx Pat<'tcx>>,
         after: &'tcx [Pat<'tcx>],
         expected: Ty<'tcx>,
-        def_bm: BindingMode,
-        ti: TopInfo<'tcx>,
+        pat_info: PatInfo<'tcx, '_>,
     ) -> Ty<'tcx> {
+        let expected = self.try_structurally_resolve_type(span, expected);
+
+        // If the pattern is irrefutable and `expected` is an infer ty, we try to equate it
+        // to an array if the given pattern allows it. See issue #76342
+        if self.pat_is_irrefutable(pat_info.decl_origin) && expected.is_ty_var() {
+            if let Some(resolved_arr_ty) =
+                self.try_resolve_slice_ty_to_array_ty(before, slice, span)
+            {
+                debug!(?resolved_arr_ty);
+                self.demand_eqtype(span, expected, resolved_arr_ty);
+            }
+        }
+
         let expected = self.structurally_resolve_type(span, expected);
+        debug!(?expected);
+
         let (element_ty, opt_slice_ty, inferred) = match *expected.kind() {
             // An array, so we might have something like `let [a, b, c] = [0, 1, 2];`.
             ty::Array(element_ty, len) => {
@@ -2055,10 +2140,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ty::Slice(element_ty) => (element_ty, Some(expected), expected),
             // The expected type must be an array or slice, but was neither, so error.
             _ => {
-                let guar = expected
-                    .error_reported()
-                    .err()
-                    .unwrap_or_else(|| self.error_expected_array_or_slice(span, expected, ti));
+                let guar = expected.error_reported().err().unwrap_or_else(|| {
+                    self.error_expected_array_or_slice(span, expected, pat_info.top_info)
+                });
                 let err = Ty::new_error(self.tcx, guar);
                 (err, Some(err), err)
             }
@@ -2066,15 +2150,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Type check all the patterns before `slice`.
         for elt in before {
-            self.check_pat(elt, element_ty, def_bm, ti);
+            self.check_pat(elt, element_ty, pat_info);
         }
         // Type check the `slice`, if present, against its expected type.
         if let Some(slice) = slice {
-            self.check_pat(slice, opt_slice_ty.unwrap(), def_bm, ti);
+            self.check_pat(slice, opt_slice_ty.unwrap(), pat_info);
         }
         // Type check the elements after `slice`, if present.
         for elt in after {
-            self.check_pat(elt, element_ty, def_bm, ti);
+            self.check_pat(elt, element_ty, pat_info);
         }
         inferred
     }
diff --git a/tests/ui/array-slice-vec/infer_array_len.rs b/tests/ui/array-slice-vec/infer_array_len.rs
index 22fe7cb8838..547c1f5727f 100644
--- a/tests/ui/array-slice-vec/infer_array_len.rs
+++ b/tests/ui/array-slice-vec/infer_array_len.rs
@@ -1,4 +1,4 @@
-// see issue #70529
+// check-pass
 struct A;
 
 impl From<A> for [u8; 2] {
@@ -13,9 +13,7 @@ impl From<A> for [u8; 3] {
     }
 }
 
-
 fn main() {
     let a = A;
     let [_, _] = a.into();
-    //~^ ERROR type annotations needed
 }
diff --git a/tests/ui/array-slice-vec/infer_array_len.stderr b/tests/ui/array-slice-vec/infer_array_len.stderr
deleted file mode 100644
index c2a509a1963..00000000000
--- a/tests/ui/array-slice-vec/infer_array_len.stderr
+++ /dev/null
@@ -1,14 +0,0 @@
-error[E0282]: type annotations needed
-  --> $DIR/infer_array_len.rs:19:9
-   |
-LL |     let [_, _] = a.into();
-   |         ^^^^^^
-   |
-help: consider giving this pattern a type
-   |
-LL |     let [_, _]: /* Type */ = a.into();
-   |               ++++++++++++
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/array-slice-vec/slice-pat-type-mismatches.rs b/tests/ui/array-slice-vec/slice-pat-type-mismatches.rs
index 521b898e7fe..03a1876fdc5 100644
--- a/tests/ui/array-slice-vec/slice-pat-type-mismatches.rs
+++ b/tests/ui/array-slice-vec/slice-pat-type-mismatches.rs
@@ -2,7 +2,7 @@ fn main() {
     match "foo".to_string() {
         ['f', 'o', ..] => {}
         //~^ ERROR expected an array or slice, found `String`
-        _ => { }
+        _ => {}
     };
 
     // Note that this one works with default binding modes.
@@ -15,7 +15,7 @@ fn main() {
     };
 
     match [0, 1, 2] {
-        [0] => {}, //~ ERROR pattern requires
+        [0] => {} //~ ERROR pattern requires
 
         [0, 1, x @ ..] => {
             let a: [_; 1] = x;
@@ -23,14 +23,15 @@ fn main() {
         [0, 1, 2, 3, x @ ..] => {} //~ ERROR pattern requires
     };
 
-    match does_not_exist { //~ ERROR cannot find value `does_not_exist` in this scope
-        [] => {}
+    match does_not_exist {
+        //~^ ERROR cannot find value `does_not_exist` in this scope
+        [] => {} // ERROR cannot find value `does_not_exist` in this scope
     };
 }
 
 fn another_fn_to_avoid_suppression() {
-    match Default::default()
-    {
-        [] => {}  //~ ERROR type annotations needed
+    match Default::default() {
+        [] => {}
+        //~^ ERROR type annotations needed
     };
 }
diff --git a/tests/ui/array-slice-vec/slice-pat-type-mismatches.stderr b/tests/ui/array-slice-vec/slice-pat-type-mismatches.stderr
index 70a4cbebeee..d1d042c4776 100644
--- a/tests/ui/array-slice-vec/slice-pat-type-mismatches.stderr
+++ b/tests/ui/array-slice-vec/slice-pat-type-mismatches.stderr
@@ -13,7 +13,7 @@ LL |         ['f', 'o', ..] => {}
 error[E0527]: pattern requires 1 element but array has 3
   --> $DIR/slice-pat-type-mismatches.rs:18:9
    |
-LL |         [0] => {},
+LL |         [0] => {}
    |         ^^^ expected 3 elements
 
 error[E0528]: pattern requires at least 4 elements but array has 3
diff --git a/tests/ui/destructuring-assignment/slice_destructure_fail.rs b/tests/ui/destructuring-assignment/slice_destructure_fail.rs
index 33b09eb349d..e5bb50030b9 100644
--- a/tests/ui/destructuring-assignment/slice_destructure_fail.rs
+++ b/tests/ui/destructuring-assignment/slice_destructure_fail.rs
@@ -1,6 +1,8 @@
 fn main() {
-  let (mut a, mut b);
-  [a, .., b, ..] = [0, 1]; //~ ERROR `..` can only be used once per slice pattern
-  [a, a, b] = [1, 2]; //~ ERROR pattern requires 3 elements but array has 2
-  [_] = [1, 2]; //~ ERROR pattern requires 1 element but array has 2
+    let (mut a, mut b);
+    [a, .., b, ..] = [0, 1]; //~ ERROR `..` can only be used once per slice pattern
+    [a, a, b] = [1, 2];
+    //~^ ERROR pattern requires 3 elements but array has 2
+    [_] = [1, 2];
+    //~^ ERROR pattern requires 1 element but array has 2
 }
diff --git a/tests/ui/destructuring-assignment/slice_destructure_fail.stderr b/tests/ui/destructuring-assignment/slice_destructure_fail.stderr
index 92c86febac4..acf44ceb184 100644
--- a/tests/ui/destructuring-assignment/slice_destructure_fail.stderr
+++ b/tests/ui/destructuring-assignment/slice_destructure_fail.stderr
@@ -1,22 +1,22 @@
 error: `..` can only be used once per slice pattern
-  --> $DIR/slice_destructure_fail.rs:3:14
+  --> $DIR/slice_destructure_fail.rs:3:16
    |
-LL |   [a, .., b, ..] = [0, 1];
-   |       --     ^^ can only be used once per slice pattern
-   |       |
-   |       previously used here
+LL |     [a, .., b, ..] = [0, 1];
+   |         --     ^^ can only be used once per slice pattern
+   |         |
+   |         previously used here
 
 error[E0527]: pattern requires 3 elements but array has 2
-  --> $DIR/slice_destructure_fail.rs:4:3
+  --> $DIR/slice_destructure_fail.rs:4:5
    |
-LL |   [a, a, b] = [1, 2];
-   |   ^^^^^^^^^ expected 2 elements
+LL |     [a, a, b] = [1, 2];
+   |     ^^^^^^^^^ expected 2 elements
 
 error[E0527]: pattern requires 1 element but array has 2
-  --> $DIR/slice_destructure_fail.rs:5:3
+  --> $DIR/slice_destructure_fail.rs:6:5
    |
-LL |   [_] = [1, 2];
-   |   ^^^ expected 2 elements
+LL |     [_] = [1, 2];
+   |     ^^^ expected 2 elements
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/pattern/slice-array-infer.rs b/tests/ui/pattern/slice-array-infer.rs
new file mode 100644
index 00000000000..f94a3dcfe0a
--- /dev/null
+++ b/tests/ui/pattern/slice-array-infer.rs
@@ -0,0 +1,27 @@
+// check-pass
+
+#![allow(unused_variables)]
+#![feature(generic_arg_infer)]
+
+struct Zeroes;
+impl Into<&'static [usize; 3]> for Zeroes {
+    fn into(self) -> &'static [usize; 3] {
+        &[0; 3]
+    }
+}
+impl Into<[usize; 3]> for Zeroes {
+    fn into(self) -> [usize; 3] {
+        [0; 3]
+    }
+}
+fn main() {
+    let [a, b, c] = Zeroes.into();
+    let [d, e, f] = <Zeroes as Into<&'static [usize; 3]>>::into(Zeroes);
+    let &[g, h, i] = Zeroes.into();
+    let [j, k, l]: [usize; _] = Zeroes.into();
+    let [m, n, o]: &[usize; _] = Zeroes.into();
+
+    // check the binding mode of these patterns:
+    let _: &[usize] = &[a, b, c, g, h, i, j, k, l];
+    let _: &[&usize] = &[d, e, f, m, n, o];
+}
diff --git a/tests/ui/pattern/slice-pattern-refutable.rs b/tests/ui/pattern/slice-pattern-refutable.rs
new file mode 100644
index 00000000000..1be3c6ef82d
--- /dev/null
+++ b/tests/ui/pattern/slice-pattern-refutable.rs
@@ -0,0 +1,36 @@
+// Test that we do not infer the expected types of patterns to an array
+// if we're in a refutable pattern.
+#![allow(unused_variables)]
+
+struct Zeroes;
+
+impl Into<[usize; 3]> for Zeroes {
+    fn into(self) -> [usize; 3] {
+        [0; 3]
+    }
+}
+
+fn let_else() {
+    let [a, b, c] = Zeroes.into() else {
+        //~^ ERROR type annotations needed
+        unreachable!();
+    };
+}
+
+fn if_let() {
+    if let [a, b, c] = Zeroes.into() {
+        //~^ ERROR type annotations needed
+        unreachable!();
+    }
+}
+
+fn if_let_else() {
+    if let [a, b, c] = Zeroes.into() {
+        //~^ ERROR type annotations needed
+        unreachable!();
+    } else {
+        unreachable!();
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/slice-pattern-refutable.stderr b/tests/ui/pattern/slice-pattern-refutable.stderr
new file mode 100644
index 00000000000..df5b58d3e9c
--- /dev/null
+++ b/tests/ui/pattern/slice-pattern-refutable.stderr
@@ -0,0 +1,40 @@
+error[E0282]: type annotations needed
+  --> $DIR/slice-pattern-refutable.rs:14:9
+   |
+LL |     let [a, b, c] = Zeroes.into() else {
+   |         ^^^^^^^^^
+   |
+help: consider giving this pattern a type
+   |
+LL |     let [a, b, c]: /* Type */ = Zeroes.into() else {
+   |                  ++++++++++++
+
+error[E0282]: type annotations needed
+  --> $DIR/slice-pattern-refutable.rs:21:31
+   |
+LL |     if let [a, b, c] = Zeroes.into() {
+   |            ---------          ^^^^
+   |            |
+   |            type must be known at this point
+   |
+help: try using a fully qualified path to specify the expected types
+   |
+LL |     if let [a, b, c] = <Zeroes as Into<T>>::into(Zeroes) {
+   |                        ++++++++++++++++++++++++++      ~
+
+error[E0282]: type annotations needed
+  --> $DIR/slice-pattern-refutable.rs:28:31
+   |
+LL |     if let [a, b, c] = Zeroes.into() {
+   |            ---------          ^^^^
+   |            |
+   |            type must be known at this point
+   |
+help: try using a fully qualified path to specify the expected types
+   |
+LL |     if let [a, b, c] = <Zeroes as Into<T>>::into(Zeroes) {
+   |                        ++++++++++++++++++++++++++      ~
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/pattern/slice-patterns-ambiguity.rs b/tests/ui/pattern/slice-patterns-ambiguity.rs
new file mode 100644
index 00000000000..0fe24b0e572
--- /dev/null
+++ b/tests/ui/pattern/slice-patterns-ambiguity.rs
@@ -0,0 +1,47 @@
+#![allow(unused_variables)]
+
+struct Zeroes;
+
+const ARR: [usize; 2] = [0; 2];
+const ARR2: [usize; 2] = [2; 2];
+
+impl Into<&'static [usize; 2]> for Zeroes {
+    fn into(self) -> &'static [usize; 2] {
+        &ARR
+    }
+}
+
+impl Into<&'static [usize]> for Zeroes {
+    fn into(self) -> &'static [usize] {
+        &ARR2
+    }
+}
+
+fn let_decl() {
+    let &[a, b] = Zeroes.into();
+}
+
+fn let_else() {
+    let &[a, b] = Zeroes.into() else {
+        //~^ ERROR type annotations needed
+        unreachable!();
+    };
+}
+
+fn if_let() {
+    if let &[a, b] = Zeroes.into() {
+        //~^ ERROR type annotations needed
+        unreachable!();
+    }
+}
+
+fn if_let_else() {
+    if let &[a, b] = Zeroes.into() {
+        //~^ ERROR type annotations needed
+        unreachable!();
+    } else {
+        unreachable!();
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/slice-patterns-ambiguity.stderr b/tests/ui/pattern/slice-patterns-ambiguity.stderr
new file mode 100644
index 00000000000..3ef99d0e2d1
--- /dev/null
+++ b/tests/ui/pattern/slice-patterns-ambiguity.stderr
@@ -0,0 +1,40 @@
+error[E0282]: type annotations needed for `&_`
+  --> $DIR/slice-patterns-ambiguity.rs:25:9
+   |
+LL |     let &[a, b] = Zeroes.into() else {
+   |         ^^^^^^^
+   |
+help: consider giving this pattern a type, where the placeholders `_` are specified
+   |
+LL |     let &[a, b]: &_ = Zeroes.into() else {
+   |                ++++
+
+error[E0282]: type annotations needed
+  --> $DIR/slice-patterns-ambiguity.rs:32:29
+   |
+LL |     if let &[a, b] = Zeroes.into() {
+   |             ------          ^^^^
+   |             |
+   |             type must be known at this point
+   |
+help: try using a fully qualified path to specify the expected types
+   |
+LL |     if let &[a, b] = <Zeroes as Into<&_>>::into(Zeroes) {
+   |                      +++++++++++++++++++++++++++      ~
+
+error[E0282]: type annotations needed
+  --> $DIR/slice-patterns-ambiguity.rs:39:29
+   |
+LL |     if let &[a, b] = Zeroes.into() {
+   |             ------          ^^^^
+   |             |
+   |             type must be known at this point
+   |
+help: try using a fully qualified path to specify the expected types
+   |
+LL |     if let &[a, b] = <Zeroes as Into<&_>>::into(Zeroes) {
+   |                      +++++++++++++++++++++++++++      ~
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/pattern/slice-patterns-irrefutable.rs b/tests/ui/pattern/slice-patterns-irrefutable.rs
new file mode 100644
index 00000000000..bd230608eb5
--- /dev/null
+++ b/tests/ui/pattern/slice-patterns-irrefutable.rs
@@ -0,0 +1,74 @@
+// Test that we infer the expected type of a pattern to an array of the given length.
+
+#![allow(unused_variables)]
+
+use std::array::TryFromSliceError;
+use std::convert::TryInto;
+
+struct Zeroes;
+impl Into<[usize; 2]> for Zeroes {
+    fn into(self) -> [usize; 2] {
+        [0; 2]
+    }
+}
+impl Into<[usize; 3]> for Zeroes {
+    fn into(self) -> [usize; 3] {
+        [0; 3]
+    }
+}
+impl Into<[usize; 4]> for Zeroes {
+    fn into(self) -> [usize; 4] {
+        [0; 4]
+    }
+}
+
+fn zeroes_into() {
+    let [a, b, c] = Zeroes.into();
+    let [d, e, f]: [_; 3] = Zeroes.into();
+}
+
+fn array_try_from(x: &[usize]) -> Result<usize, TryFromSliceError> {
+    let [a, b] = x.try_into()?;
+    Ok(a + b)
+}
+
+fn destructuring_assignment() {
+    let a: i32;
+    let b;
+    [a, b] = Default::default();
+}
+
+fn test_nested_array() {
+    let a: [_; 3];
+    let b;
+    //~^ ERROR type annotations needed
+    [a, b] = Default::default();
+}
+
+fn test_nested_array_type_hint() {
+    let a: [_; 3];
+    let b;
+    [a, b] = Default::default();
+    let _: i32 = b[1];
+}
+
+fn test_working_nested_array() {
+    let a: i32;
+    [[a, _, _], _, _] = Default::default();
+}
+
+struct Foo<T>([T; 2]);
+
+impl<T: Default + Copy> Default for Foo<T> {
+    fn default() -> Self {
+        Foo([Default::default(); 2])
+    }
+}
+
+fn field_array() {
+    let a: i32;
+    let b;
+    Foo([a, b]) = Default::default();
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/slice-patterns-irrefutable.stderr b/tests/ui/pattern/slice-patterns-irrefutable.stderr
new file mode 100644
index 00000000000..fac99534f3e
--- /dev/null
+++ b/tests/ui/pattern/slice-patterns-irrefutable.stderr
@@ -0,0 +1,14 @@
+error[E0282]: type annotations needed for `[_; 3]`
+  --> $DIR/slice-patterns-irrefutable.rs:43:9
+   |
+LL |     let b;
+   |         ^
+   |
+help: consider giving `b` an explicit type, where the placeholders `_` are specified
+   |
+LL |     let b: [_; 3];
+   |          ++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/pattern/slice-patterns-nested.rs b/tests/ui/pattern/slice-patterns-nested.rs
new file mode 100644
index 00000000000..077e0a13954
--- /dev/null
+++ b/tests/ui/pattern/slice-patterns-nested.rs
@@ -0,0 +1,15 @@
+// check-pass
+#![allow(unused_variables)]
+
+struct Zeroes;
+struct Foo<T>(T);
+
+impl Into<[usize; 3]> for Zeroes {
+    fn into(self) -> [usize; 3] {
+        [0; 3]
+    }
+}
+
+fn main() {
+    let Foo([a, b, c]) = Foo(Zeroes.into());
+}