From 2827aa975257d03f21c75fa27a594079a9da31ba Mon Sep 17 00:00:00 2001 From: b-naber Date: Wed, 21 Jun 2023 17:13:06 +0000 Subject: [PATCH 01/11] try to infer array type from slice pattern --- compiler/rustc_hir_typeck/src/pat.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 8bf95d4bf9a6c..4785564d850f7 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -2024,6 +2024,25 @@ 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> { + 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(tcx.mk_array(inner_ty, len.try_into().unwrap())) + } + /// Type check a slice pattern. /// /// Syntactically, these look like `[pat_0, ..., pat_n]`. @@ -2044,6 +2063,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { def_bm: BindingMode, ti: TopInfo<'tcx>, ) -> Ty<'tcx> { + // If `expected` is an infer ty, we try to equate it to an array if the given pattern + // allows it. See issue #76342 + if let Some(resolved_arr_ty) = self.try_resolve_slice_ty_to_array_ty(before, slice, span) && expected.is_ty_var() { + debug!(?resolved_arr_ty); + self.demand_eqtype(span, expected, resolved_arr_ty); + } + let expected = self.structurally_resolve_type(span, 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];`. From e18e3761ced3695e9d7a920b223a49767dd4f1f6 Mon Sep 17 00:00:00 2001 From: b-naber Date: Wed, 21 Jun 2023 18:01:31 +0000 Subject: [PATCH 02/11] add test, bless tests --- tests/ui/array-slice-vec/infer_array_len.rs | 4 +- .../ui/array-slice-vec/infer_array_len.stderr | 14 ------ .../slice-pat-type-mismatches.rs | 14 +++--- .../slice-pat-type-mismatches.stderr | 18 ++++---- .../slice_destructure_fail.rs | 12 +++-- .../slice_destructure_fail.stderr | 45 +++++++++++++------ tests/ui/pattern/issue-76342.rs | 16 +++++++ 7 files changed, 74 insertions(+), 49 deletions(-) delete mode 100644 tests/ui/array-slice-vec/infer_array_len.stderr create mode 100644 tests/ui/pattern/issue-76342.rs diff --git a/tests/ui/array-slice-vec/infer_array_len.rs b/tests/ui/array-slice-vec/infer_array_len.rs index 22fe7cb883888..547c1f5727f19 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 for [u8; 2] { @@ -13,9 +13,7 @@ impl From 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 c2a509a196344..0000000000000 --- 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 521b898e7fe4b..310c49701ed3e 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,16 @@ 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() + match Default + //~^ ERROR expected value, found trait { - [] => {} //~ 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 70a4cbebeee98..6218bffd50359 100644 --- a/tests/ui/array-slice-vec/slice-pat-type-mismatches.stderr +++ b/tests/ui/array-slice-vec/slice-pat-type-mismatches.stderr @@ -4,6 +4,12 @@ error[E0425]: cannot find value `does_not_exist` in this scope LL | match does_not_exist { | ^^^^^^^^^^^^^^ not found in this scope +error[E0423]: expected value, found trait `Default` + --> $DIR/slice-pat-type-mismatches.rs:33:11 + | +LL | match Default + | ^^^^^^^ not a value + error[E0529]: expected an array or slice, found `String` --> $DIR/slice-pat-type-mismatches.rs:3:9 | @@ -13,7 +19,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 @@ -22,13 +28,7 @@ error[E0528]: pattern requires at least 4 elements but array has 3 LL | [0, 1, 2, 3, x @ ..] => {} | ^^^^^^^^^^^^^^^^^^^^ pattern cannot match array of 3 elements -error[E0282]: type annotations needed - --> $DIR/slice-pat-type-mismatches.rs:34:9 - | -LL | [] => {} - | ^^ cannot infer type - error: aborting due to 5 previous errors -Some errors have detailed explanations: E0282, E0425, E0527, E0528, E0529. -For more information about an error, try `rustc --explain E0282`. +Some errors have detailed explanations: E0423, E0425, E0527, E0528, E0529. +For more information about an error, try `rustc --explain E0423`. diff --git a/tests/ui/destructuring-assignment/slice_destructure_fail.rs b/tests/ui/destructuring-assignment/slice_destructure_fail.rs index 33b09eb349da0..e36557940ee3d 100644 --- a/tests/ui/destructuring-assignment/slice_destructure_fail.rs +++ b/tests/ui/destructuring-assignment/slice_destructure_fail.rs @@ -1,6 +1,10 @@ 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 + //~| ERROR mismatched types + [_] = [1, 2]; + //~^ ERROR pattern requires 1 element but array has 2 + //~| ERROR mismatched types } diff --git a/tests/ui/destructuring-assignment/slice_destructure_fail.stderr b/tests/ui/destructuring-assignment/slice_destructure_fail.stderr index 92c86febac440..e42b9695d4da7 100644 --- a/tests/ui/destructuring-assignment/slice_destructure_fail.stderr +++ b/tests/ui/destructuring-assignment/slice_destructure_fail.stderr @@ -1,23 +1,42 @@ 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[E0308]: mismatched types + --> $DIR/slice_destructure_fail.rs:4:5 + | +LL | [a, a, b] = [1, 2]; + | ^^^^^^^^^ expected an array with a fixed size of 2 elements, found one with 3 elements + | + = note: expected array `[{integer}; 2]` + found array `[_; 3]` 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 + +error[E0308]: mismatched types + --> $DIR/slice_destructure_fail.rs:7:5 + | +LL | [_] = [1, 2]; + | ^^^ expected an array with a fixed size of 2 elements, found one with 1 element | -LL | [a, a, b] = [1, 2]; - | ^^^^^^^^^ expected 2 elements + = note: expected array `[{integer}; 2]` + found array `[_; 1]` error[E0527]: pattern requires 1 element but array has 2 - --> $DIR/slice_destructure_fail.rs:5:3 + --> $DIR/slice_destructure_fail.rs:7:5 | -LL | [_] = [1, 2]; - | ^^^ expected 2 elements +LL | [_] = [1, 2]; + | ^^^ expected 2 elements -error: aborting due to 3 previous errors +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0527`. +Some errors have detailed explanations: E0308, E0527. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/issue-76342.rs b/tests/ui/pattern/issue-76342.rs new file mode 100644 index 0000000000000..2b3d3e522f227 --- /dev/null +++ b/tests/ui/pattern/issue-76342.rs @@ -0,0 +1,16 @@ +// check-pass +#![allow(unused_variables)] +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 main() { + let [a, b, c] = Zeroes.into(); // ERROR: type annotations needed + let [d, e, f]: [_; 3] = Zeroes.into(); // Works great +} From b3413c643b2b00b91ea1200ec17e49b8a161e7de Mon Sep 17 00:00:00 2001 From: b-naber Date: Fri, 23 Jun 2023 12:28:19 +0000 Subject: [PATCH 03/11] only infer array type on irrefutable patterns --- compiler/rustc_hir_typeck/src/_match.rs | 2 +- compiler/rustc_hir_typeck/src/check.rs | 2 +- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 5 +- .../rustc_hir_typeck/src/gather_locals.rs | 31 +++- compiler/rustc_hir_typeck/src/pat.rs | 138 +++++++++++++----- 5 files changed, 138 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index f2a43cc414d3b..55c3cd8f4c634 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 53bae315d7862..1fc1e5aca2b3c 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(¶m.pat, param_ty, ty_span, None); + fcx.check_pat_top(¶m.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 a9610009db1ee..c70de7b8b725a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1,7 +1,7 @@ use crate::coercion::CoerceMany; use crate::errors::SuggestPtrNullMut; use crate::fn_ctxt::arg_matrix::{ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx}; -use crate::gather_locals::Declaration; +use crate::gather_locals::{DeclContext, Declaration}; use crate::method::MethodCallee; use crate::TupleArgumentsFlag::*; use crate::{errors, Expectation::*}; @@ -1474,7 +1474,8 @@ 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); + let decl_ctxt = DeclContext { has_else: decl.els.is_some(), origin: decl.origin }; + self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr, Some(decl_ctxt)); let pat_ty = self.node_ty(decl.pat.hir_id); self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, pat_ty); diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index 4f45a24b216ea..15562d5746672 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -9,6 +9,24 @@ use rustc_span::def_id::LocalDefId; use rustc_span::Span; use rustc_trait_selection::traits; +#[derive(Debug, Copy, Clone)] +pub(super) enum DeclOrigin { + // from an `if let` expression + LetExpr, + // from `let x = ..` + LocalDecl, +} + +/// 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(crate) struct DeclContext { + // whether we're in a let-else context + pub(super) has_else: bool, + pub(super) origin: DeclOrigin, +} + /// 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. @@ -19,19 +37,28 @@ pub(super) struct Declaration<'a> { pub span: Span, pub init: Option<&'a hir::Expr<'a>>, pub els: Option<&'a hir::Block<'a>>, + pub origin: DeclOrigin, } 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, els, origin: DeclOrigin::LocalDecl } } } 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), + els: None, + origin: DeclOrigin::LetExpr, + } } } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 4785564d850f7..5616f67f430f4 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::{DeclContext, DeclOrigin}; use crate::{errors, FnCtxt, RawTy}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; @@ -135,15 +136,16 @@ 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, origin_expr: Option<&'tcx hir::Expr<'tcx>>, + decl_ctxt: Option, ) { let info = TopInfo { expected, origin_expr, span }; - self.check_pat(pat, expected, INITIAL_BM, info); + self.check_pat(pat, expected, INITIAL_BM, info, decl_ctxt); } /// Type check the given `pat` against the `expected` type @@ -158,6 +160,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, def_bm: BindingMode, ti: TopInfo<'tcx>, + decl_ctxt: Option, ) { let path_res = match &pat.kind { PatKind::Path(qpath) => { @@ -173,33 +176,41 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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) - } - PatKind::TupleStruct(ref qpath, subpats, ddpos) => { - self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, def_bm, ti) + self.check_pat_ident(pat, ba, var_id, sub, expected, def_bm, ti, decl_ctxt) } + PatKind::TupleStruct(ref qpath, subpats, ddpos) => self.check_pat_tuple_struct( + pat, qpath, subpats, ddpos, expected, def_bm, ti, decl_ctxt, + ), 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) - } + PatKind::Struct(ref qpath, fields, has_rest_pat) => self.check_pat_struct( + pat, + qpath, + fields, + has_rest_pat, + expected, + def_bm, + ti, + decl_ctxt, + ), PatKind::Or(pats) => { for pat in pats { - self.check_pat(pat, expected, def_bm, ti); + self.check_pat(pat, expected, def_bm, ti, decl_ctxt); } expected } PatKind::Tuple(elements, ddpos) => { - self.check_pat_tuple(pat.span, elements, ddpos, expected, def_bm, ti) + self.check_pat_tuple(pat.span, elements, ddpos, expected, def_bm, ti, decl_ctxt) } - 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) + PatKind::Box(inner) => { + self.check_pat_box(pat.span, inner, expected, def_bm, ti, decl_ctxt) } - PatKind::Slice(before, slice, after) => { - self.check_pat_slice(pat.span, before, slice, after, expected, def_bm, ti) + PatKind::Ref(inner, mutbl) => { + self.check_pat_ref(pat, inner, mutbl, expected, def_bm, ti, decl_ctxt) } + PatKind::Slice(before, slice, after) => self + .check_pat_slice(pat.span, before, slice, after, expected, def_bm, ti, decl_ctxt), }; self.write_ty(pat.hir_id, ty); @@ -582,6 +593,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, def_bm: BindingMode, ti: TopInfo<'tcx>, + decl_ctxt: Option, ) -> Ty<'tcx> { // Determine the binding mode... let bm = match ba { @@ -620,7 +632,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, def_bm, ti, decl_ctxt); } local_ty @@ -845,6 +857,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, def_bm: BindingMode, ti: TopInfo<'tcx>, + decl_ctxt: Option, ) -> Ty<'tcx> { // Resolve the path and check the definition for errors. let (variant, pat_ty) = match self.check_struct_path(qpath, pat.hir_id) { @@ -853,7 +866,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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, def_bm, ti, decl_ctxt); } return err; } @@ -863,7 +876,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.demand_eqtype_pat(pat.span, expected, pat_ty, ti); // 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, + def_bm, + ti, + decl_ctxt, + ) { pat_ty } else { Ty::new_misc_error(self.tcx) @@ -1030,11 +1052,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, def_bm: BindingMode, ti: TopInfo<'tcx>, + decl_ctxt: Option, ) -> Ty<'tcx> { 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), def_bm, ti, decl_ctxt); } }; let report_unexpected_res = |res: Res| { @@ -1100,7 +1123,7 @@ 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, def_bm, ti, decl_ctxt); self.tcx.check_stability( variant.fields[FieldIdx::from_usize(i)].did, @@ -1286,6 +1309,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, def_bm: BindingMode, ti: TopInfo<'tcx>, + decl_ctxt: Option, ) -> Ty<'tcx> { let tcx = self.tcx; let mut expected_len = elements.len(); @@ -1312,12 +1336,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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), def_bm, ti, decl_ctxt); } 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], def_bm, ti, decl_ctxt); } pat_ty } @@ -1332,6 +1356,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { has_rest_pat: bool, def_bm: BindingMode, ti: TopInfo<'tcx>, + decl_ctxt: Option, ) -> bool { let tcx = self.tcx; @@ -1378,7 +1403,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - self.check_pat(field.pat, field_ty, def_bm, ti); + self.check_pat(field.pat, field_ty, def_bm, ti, decl_ctxt); } let mut unmentioned_fields = variant @@ -1943,6 +1968,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, def_bm: BindingMode, ti: TopInfo<'tcx>, + decl_ctxt: Option, ) -> Ty<'tcx> { let tcx = self.tcx; let (box_ty, inner_ty) = match self.check_dereferenceable(span, expected, inner) { @@ -1962,7 +1988,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (err, err) } }; - self.check_pat(inner, inner_ty, def_bm, ti); + self.check_pat(inner, inner_ty, def_bm, ti, decl_ctxt); box_ty } @@ -1975,6 +2001,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, def_bm: BindingMode, ti: TopInfo<'tcx>, + decl_ctxt: Option, ) -> Ty<'tcx> { let tcx = self.tcx; let expected = self.shallow_resolve(expected); @@ -2013,7 +2040,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (err, err) } }; - self.check_pat(inner, inner_ty, def_bm, ti); + self.check_pat(inner, inner_ty, def_bm, ti, decl_ctxt); ref_ty } @@ -2043,6 +2070,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(tcx.mk_array(inner_ty, len.try_into().unwrap())) } + /// 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 decl_allows_array_type_infer(&self, decl_ctxt: Option) -> bool { + if let Some(decl_ctxt) = decl_ctxt { + !decl_ctxt.has_else && matches!(decl_ctxt.origin, DeclOrigin::LocalDecl) + } else { + false + } + } + /// Type check a slice pattern. /// /// Syntactically, these look like `[pat_0, ..., pat_n]`. @@ -2062,12 +2127,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, def_bm: BindingMode, ti: TopInfo<'tcx>, + decl_ctxt: Option, ) -> Ty<'tcx> { - // If `expected` is an infer ty, we try to equate it to an array if the given pattern - // allows it. See issue #76342 - if let Some(resolved_arr_ty) = self.try_resolve_slice_ty_to_array_ty(before, slice, span) && expected.is_ty_var() { - debug!(?resolved_arr_ty); - self.demand_eqtype(span, expected, resolved_arr_ty); + // 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.decl_allows_array_type_infer(decl_ctxt) && 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); @@ -2096,15 +2166,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, def_bm, ti, decl_ctxt); } // 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(), def_bm, ti, decl_ctxt); } // 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, def_bm, ti, decl_ctxt); } inferred } From 39c2785aec339032632fc0c72a5c51017bfa54ae Mon Sep 17 00:00:00 2001 From: b-naber Date: Fri, 30 Jun 2023 11:42:37 +0000 Subject: [PATCH 04/11] add tests for refutable patterns --- tests/ui/pattern/issue-76342.rs | 16 +++++-- tests/ui/pattern/slice-pattern-refutable.rs | 34 ++++++++++++++ .../ui/pattern/slice-pattern-refutable.stderr | 40 ++++++++++++++++ tests/ui/pattern/slice-patterns-ambiguity.rs | 47 +++++++++++++++++++ .../pattern/slice-patterns-ambiguity.stderr | 40 ++++++++++++++++ 5 files changed, 172 insertions(+), 5 deletions(-) create mode 100644 tests/ui/pattern/slice-pattern-refutable.rs create mode 100644 tests/ui/pattern/slice-pattern-refutable.stderr create mode 100644 tests/ui/pattern/slice-patterns-ambiguity.rs create mode 100644 tests/ui/pattern/slice-patterns-ambiguity.stderr diff --git a/tests/ui/pattern/issue-76342.rs b/tests/ui/pattern/issue-76342.rs index 2b3d3e522f227..0d066a12179b9 100644 --- a/tests/ui/pattern/issue-76342.rs +++ b/tests/ui/pattern/issue-76342.rs @@ -2,15 +2,21 @@ #![allow(unused_variables)] struct Zeroes; impl Into<[usize; 2]> for Zeroes { - fn into(self) -> [usize; 2] { [0; 2] } + fn into(self) -> [usize; 2] { + [0; 2] + } } impl Into<[usize; 3]> for Zeroes { - fn into(self) -> [usize; 3] { [0; 3] } + fn into(self) -> [usize; 3] { + [0; 3] + } } impl Into<[usize; 4]> for Zeroes { - fn into(self) -> [usize; 4] { [0; 4] } + fn into(self) -> [usize; 4] { + [0; 4] + } } fn main() { - let [a, b, c] = Zeroes.into(); // ERROR: type annotations needed - let [d, e, f]: [_; 3] = Zeroes.into(); // Works great + let [a, b, c] = Zeroes.into(); + let [d, e, f]: [_; 3] = Zeroes.into(); } diff --git a/tests/ui/pattern/slice-pattern-refutable.rs b/tests/ui/pattern/slice-pattern-refutable.rs new file mode 100644 index 0000000000000..3961da3bf5343 --- /dev/null +++ b/tests/ui/pattern/slice-pattern-refutable.rs @@ -0,0 +1,34 @@ +#![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 0000000000000..cb5503bac391e --- /dev/null +++ b/tests/ui/pattern/slice-pattern-refutable.stderr @@ -0,0 +1,40 @@ +error[E0282]: type annotations needed + --> $DIR/slice-pattern-refutable.rs:12: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:19: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] = >::into(Zeroes) { + | ++++++++++++++++++++++++++ ~ + +error[E0282]: type annotations needed + --> $DIR/slice-pattern-refutable.rs:26: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] = >::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 0000000000000..0fe24b0e572fb --- /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 0000000000000..3ef99d0e2d1bb --- /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] = >::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] = >::into(Zeroes) { + | +++++++++++++++++++++++++++ ~ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0282`. From feb8bb127305f692e4b08b5b4f0eb21a0da79511 Mon Sep 17 00:00:00 2001 From: b-naber Date: Mon, 3 Jul 2023 20:37:56 +0000 Subject: [PATCH 05/11] combine arguments into PatInfo --- compiler/rustc_hir_typeck/src/pat.rs | 194 ++++++++++++++++----------- 1 file changed, 115 insertions(+), 79 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 5616f67f430f4..fbe50966419da 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -78,6 +78,13 @@ struct TopInfo<'tcx> { span: Option, } +#[derive(Copy, Clone)] +struct PatInfo<'tcx> { + binding_mode: BindingMode, + top_info: TopInfo<'tcx>, + decl_ctxt: Option, +} + impl<'tcx> FnCtxt<'_, 'tcx> { fn pattern_cause(&self, ti: TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> { let code = @@ -145,7 +152,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { decl_ctxt: Option, ) { let info = TopInfo { expected, origin_expr, span }; - self.check_pat(pat, expected, INITIAL_BM, info, decl_ctxt); + let pat_info = PatInfo { binding_mode: INITIAL_BM, top_info: info, decl_ctxt }; + self.check_pat(pat, expected, pat_info); } /// Type check the given `pat` against the `expected` type @@ -153,15 +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>, - decl_ctxt: Option, - ) { + #[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, decl_ctxt } = 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)) @@ -175,11 +177,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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, decl_ctxt) - } + PatKind::Binding(ba, var_id, _, sub) => self.check_pat_ident( + pat, + ba, + var_id, + sub, + expected, + PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + ), PatKind::TupleStruct(ref qpath, subpats, ddpos) => self.check_pat_tuple_struct( - pat, qpath, subpats, ddpos, expected, def_bm, ti, decl_ctxt, + pat, + qpath, + subpats, + ddpos, + expected, + PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, ), PatKind::Path(ref qpath) => { self.check_pat_path(pat, qpath, path_res.unwrap(), expected, ti) @@ -190,27 +202,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields, has_rest_pat, expected, - def_bm, - ti, - decl_ctxt, + PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, ), PatKind::Or(pats) => { for pat in pats { - self.check_pat(pat, expected, def_bm, ti, decl_ctxt); + self.check_pat( + pat, + expected, + PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + ); } expected } - PatKind::Tuple(elements, ddpos) => { - self.check_pat_tuple(pat.span, elements, ddpos, expected, def_bm, ti, decl_ctxt) - } - PatKind::Box(inner) => { - self.check_pat_box(pat.span, inner, expected, def_bm, ti, decl_ctxt) - } - PatKind::Ref(inner, mutbl) => { - self.check_pat_ref(pat, inner, mutbl, expected, def_bm, ti, decl_ctxt) - } - PatKind::Slice(before, slice, after) => self - .check_pat_slice(pat.span, before, slice, after, expected, def_bm, ti, decl_ctxt), + PatKind::Tuple(elements, ddpos) => self.check_pat_tuple( + pat.span, + elements, + ddpos, + expected, + PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + ), + PatKind::Box(inner) => self.check_pat_box( + pat.span, + inner, + expected, + PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + ), + PatKind::Ref(inner, mutbl) => self.check_pat_ref( + pat, + inner, + mutbl, + expected, + PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + ), + PatKind::Slice(before, slice, after) => self.check_pat_slice( + pat.span, + before, + slice, + after, + expected, + PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + ), }; self.write_ty(pat.hir_id, ty); @@ -591,10 +622,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { var_id: HirId, sub: Option<&'tcx Pat<'tcx>>, expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - decl_ctxt: Option, + pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { + let PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt } = pat_info; + // Determine the binding mode... let bm = match ba { hir::BindingAnnotation::NONE => def_bm, @@ -632,7 +663,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let Some(p) = sub { - self.check_pat(p, expected, def_bm, ti, decl_ctxt); + self.check_pat(p, expected, PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }); } local_ty @@ -855,10 +886,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields: &'tcx [hir::PatField<'tcx>], has_rest_pat: bool, expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - decl_ctxt: Option, + pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { + let PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt } = pat_info; + // Resolve the path and check the definition for errors. let (variant, pat_ty) = match self.check_struct_path(qpath, pat.hir_id) { Ok(data) => data, @@ -866,7 +897,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let err = Ty::new_error(self.tcx, guar); for field in fields { let ti = ti; - self.check_pat(field.pat, err, def_bm, ti, decl_ctxt); + self.check_pat( + field.pat, + err, + PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + ); } return err; } @@ -882,9 +917,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { variant, fields, has_rest_pat, - def_bm, - ti, - decl_ctxt, + PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, ) { pat_ty } else { @@ -1050,14 +1083,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { subpats: &'tcx [Pat<'tcx>], ddpos: hir::DotDotPos, expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - decl_ctxt: Option, + pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { + let PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt } = 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, decl_ctxt); + self.check_pat( + pat, + Ty::new_error(tcx, e), + PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + ); } }; let report_unexpected_res = |res: Res| { @@ -1123,7 +1159,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, decl_ctxt); + self.check_pat( + subpat, + field_ty, + PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + ); self.tcx.check_stability( variant.fields[FieldIdx::from_usize(i)].did, @@ -1307,9 +1347,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { elements: &'tcx [Pat<'tcx>], ddpos: hir::DotDotPos, expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - decl_ctxt: Option, + pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; let mut expected_len = elements.len(); @@ -1330,18 +1368,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, decl_ctxt); + 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, decl_ctxt); + self.check_pat(elem, element_tys[i], pat_info); } pat_ty } @@ -1354,9 +1394,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>, - decl_ctxt: Option, + pat_info: PatInfo<'tcx>, ) -> bool { let tcx = self.tcx; @@ -1403,7 +1441,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - self.check_pat(field.pat, field_ty, def_bm, ti, decl_ctxt); + self.check_pat(field.pat, field_ty, pat_info); } let mut unmentioned_fields = variant @@ -1966,9 +2004,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, inner: &'tcx Pat<'tcx>, expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - decl_ctxt: Option, + pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; let (box_ty, inner_ty) = match self.check_dereferenceable(span, expected, inner) { @@ -1980,7 +2016,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) => { @@ -1988,7 +2024,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (err, err) } }; - self.check_pat(inner, inner_ty, def_bm, ti, decl_ctxt); + self.check_pat(inner, inner_ty, pat_info); box_ty } @@ -1999,9 +2035,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { inner: &'tcx Pat<'tcx>, mutbl: hir::Mutability, expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - decl_ctxt: Option, + pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; let expected = self.shallow_resolve(expected); @@ -2023,7 +2057,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)` @@ -2040,7 +2079,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (err, err) } }; - self.check_pat(inner, inner_ty, def_bm, ti, decl_ctxt); + self.check_pat(inner, inner_ty, pat_info); ref_ty } @@ -2067,10 +2106,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }; let inner_ty = self.next_ty_var(ty_var_origin); - Some(tcx.mk_array(inner_ty, len.try_into().unwrap())) + Some(Ty::new_array(tcx, inner_ty, len.try_into().unwrap())) } - /// Determines whether we can infer the expected type in the slice pattern to be of type array. + /// 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: /// @@ -2100,7 +2139,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// 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 decl_allows_array_type_infer(&self, decl_ctxt: Option) -> bool { + fn pat_is_irrefutable(&self, decl_ctxt: Option) -> bool { if let Some(decl_ctxt) = decl_ctxt { !decl_ctxt.has_else && matches!(decl_ctxt.origin, DeclOrigin::LocalDecl) } else { @@ -2125,13 +2164,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { slice: Option<&'tcx Pat<'tcx>>, after: &'tcx [Pat<'tcx>], expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - decl_ctxt: Option, + pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { // 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.decl_allows_array_type_infer(decl_ctxt) && expected.is_ty_var() { + if self.pat_is_irrefutable(pat_info.decl_ctxt) && expected.is_ty_var() { if let Some(resolved_arr_ty) = self.try_resolve_slice_ty_to_array_ty(before, slice, span) { @@ -2155,10 +2192,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) } @@ -2166,15 +2202,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, decl_ctxt); + 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, decl_ctxt); + 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, decl_ctxt); + self.check_pat(elt, element_ty, pat_info); } inferred } From 843e2ee5d56907be94a68d3b3b6a2057f8abaaeb Mon Sep 17 00:00:00 2001 From: b-naber Date: Mon, 3 Jul 2023 20:55:37 +0000 Subject: [PATCH 06/11] add test for nested pattern --- tests/ui/pattern/slice-patterns-nested.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/ui/pattern/slice-patterns-nested.rs diff --git a/tests/ui/pattern/slice-patterns-nested.rs b/tests/ui/pattern/slice-patterns-nested.rs new file mode 100644 index 0000000000000..077e0a139544b --- /dev/null +++ b/tests/ui/pattern/slice-patterns-nested.rs @@ -0,0 +1,15 @@ +// check-pass +#![allow(unused_variables)] + +struct Zeroes; +struct Foo(T); + +impl Into<[usize; 3]> for Zeroes { + fn into(self) -> [usize; 3] { + [0; 3] + } +} + +fn main() { + let Foo([a, b, c]) = Foo(Zeroes.into()); +} From 1c217b6d8a55865535d06752a65ba2ae24f7b349 Mon Sep 17 00:00:00 2001 From: b-naber Date: Wed, 5 Jul 2023 20:43:37 +0000 Subject: [PATCH 07/11] move els into DeclOrigin --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 7 +- .../rustc_hir_typeck/src/gather_locals.rs | 37 ++++----- compiler/rustc_hir_typeck/src/pat.rs | 75 ++++++++++--------- 3 files changed, 58 insertions(+), 61 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index c70de7b8b725a..2c1543ca1d55d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1,7 +1,7 @@ use crate::coercion::CoerceMany; use crate::errors::SuggestPtrNullMut; use crate::fn_ctxt::arg_matrix::{ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx}; -use crate::gather_locals::{DeclContext, Declaration}; +use crate::gather_locals::Declaration; use crate::method::MethodCallee; use crate::TupleArgumentsFlag::*; use crate::{errors, Expectation::*}; @@ -1474,12 +1474,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // Type check the pattern. Override if necessary to avoid knock-on errors. - let decl_ctxt = DeclContext { has_else: decl.els.is_some(), origin: decl.origin }; - self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr, Some(decl_ctxt)); + 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_els() { 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 15562d5746672..b2b6ad0d101cf 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -9,22 +9,24 @@ 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 { +pub(super) enum DeclOrigin<'a> { // from an `if let` expression LetExpr, // from `let x = ..` - LocalDecl, + LocalDecl { els: Option<&'a hir::Block<'a>> }, } -/// 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(crate) struct DeclContext { - // whether we're in a let-else context - pub(super) has_else: bool, - pub(super) origin: DeclOrigin, +impl<'a> DeclOrigin<'a> { + pub(super) fn try_get_els(&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]. @@ -36,29 +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, + 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, origin: DeclOrigin::LocalDecl } + 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, - origin: DeclOrigin::LetExpr, - } + 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 fbe50966419da..659223a377cc1 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -1,4 +1,4 @@ -use crate::gather_locals::{DeclContext, DeclOrigin}; +use crate::gather_locals::DeclOrigin; use crate::{errors, FnCtxt, RawTy}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; @@ -79,10 +79,10 @@ struct TopInfo<'tcx> { } #[derive(Copy, Clone)] -struct PatInfo<'tcx> { +struct PatInfo<'tcx, 'a> { binding_mode: BindingMode, top_info: TopInfo<'tcx>, - decl_ctxt: Option, + decl_origin: Option>, } impl<'tcx> FnCtxt<'_, 'tcx> { @@ -149,10 +149,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, span: Option, origin_expr: Option<&'tcx hir::Expr<'tcx>>, - decl_ctxt: Option, + decl_origin: Option>, ) { let info = TopInfo { expected, origin_expr, span }; - let pat_info = PatInfo { binding_mode: INITIAL_BM, top_info: info, decl_ctxt }; + let pat_info = PatInfo { binding_mode: INITIAL_BM, top_info: info, decl_origin }; self.check_pat(pat, expected, pat_info); } @@ -162,8 +162,8 @@ 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, 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, decl_ctxt } = 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, decl_origin } = 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)) @@ -183,7 +183,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { var_id, sub, expected, - PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, ), PatKind::TupleStruct(ref qpath, subpats, ddpos) => self.check_pat_tuple_struct( pat, @@ -191,7 +191,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { subpats, ddpos, expected, - PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, ), PatKind::Path(ref qpath) => { self.check_pat_path(pat, qpath, path_res.unwrap(), expected, ti) @@ -202,14 +202,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields, has_rest_pat, expected, - PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, ), PatKind::Or(pats) => { for pat in pats { self.check_pat( pat, expected, - PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, ); } expected @@ -219,20 +219,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { elements, ddpos, expected, - PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, ), PatKind::Box(inner) => self.check_pat_box( pat.span, inner, expected, - PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, ), PatKind::Ref(inner, mutbl) => self.check_pat_ref( pat, inner, mutbl, expected, - PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, ), PatKind::Slice(before, slice, after) => self.check_pat_slice( pat.span, @@ -240,7 +240,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { slice, after, expected, - PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, ), }; @@ -622,9 +622,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { var_id: HirId, sub: Option<&'tcx Pat<'tcx>>, expected: Ty<'tcx>, - pat_info: PatInfo<'tcx>, + pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { - let PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt } = pat_info; + let PatInfo { binding_mode: def_bm, top_info: ti, decl_origin } = pat_info; // Determine the binding mode... let bm = match ba { @@ -663,7 +663,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let Some(p) = sub { - self.check_pat(p, expected, PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }); + self.check_pat( + p, + expected, + PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, + ); } local_ty @@ -886,9 +890,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields: &'tcx [hir::PatField<'tcx>], has_rest_pat: bool, expected: Ty<'tcx>, - pat_info: PatInfo<'tcx>, + pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { - let PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt } = pat_info; + let PatInfo { binding_mode: def_bm, top_info: ti, decl_origin } = pat_info; // Resolve the path and check the definition for errors. let (variant, pat_ty) = match self.check_struct_path(qpath, pat.hir_id) { @@ -900,7 +904,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_pat( field.pat, err, - PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, ); } return err; @@ -917,7 +921,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { variant, fields, has_rest_pat, - PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, ) { pat_ty } else { @@ -1083,16 +1087,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { subpats: &'tcx [Pat<'tcx>], ddpos: hir::DotDotPos, expected: Ty<'tcx>, - pat_info: PatInfo<'tcx>, + pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { - let PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt } = pat_info; + 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), - PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, ); } }; @@ -1162,7 +1166,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_pat( subpat, field_ty, - PatInfo { binding_mode: def_bm, top_info: ti, decl_ctxt }, + PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, ); self.tcx.check_stability( @@ -1347,7 +1351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { elements: &'tcx [Pat<'tcx>], ddpos: hir::DotDotPos, expected: Ty<'tcx>, - pat_info: PatInfo<'tcx>, + pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { let tcx = self.tcx; let mut expected_len = elements.len(); @@ -1394,7 +1398,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { variant: &'tcx ty::VariantDef, fields: &'tcx [hir::PatField<'tcx>], has_rest_pat: bool, - pat_info: PatInfo<'tcx>, + pat_info: PatInfo<'tcx, '_>, ) -> bool { let tcx = self.tcx; @@ -2004,7 +2008,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, inner: &'tcx Pat<'tcx>, expected: Ty<'tcx>, - pat_info: PatInfo<'tcx>, + pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { let tcx = self.tcx; let (box_ty, inner_ty) = match self.check_dereferenceable(span, expected, inner) { @@ -2035,7 +2039,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { inner: &'tcx Pat<'tcx>, mutbl: hir::Mutability, expected: Ty<'tcx>, - pat_info: PatInfo<'tcx>, + pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { let tcx = self.tcx; let expected = self.shallow_resolve(expected); @@ -2139,9 +2143,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// 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_ctxt: Option) -> bool { - if let Some(decl_ctxt) = decl_ctxt { - !decl_ctxt.has_else && matches!(decl_ctxt.origin, DeclOrigin::LocalDecl) + fn pat_is_irrefutable(&self, decl_origin: Option>) -> bool { + if let Some(decl_origin) = decl_origin { + decl_origin.try_get_els().is_none() + && matches!(decl_origin, DeclOrigin::LocalDecl { .. }) } else { false } @@ -2164,11 +2169,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { slice: Option<&'tcx Pat<'tcx>>, after: &'tcx [Pat<'tcx>], expected: Ty<'tcx>, - pat_info: PatInfo<'tcx>, + pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { // 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_ctxt) && expected.is_ty_var() { + 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) { From 65f92a52bfee3f2d989695b80c36906404fffeca Mon Sep 17 00:00:00 2001 From: b-naber Date: Mon, 10 Jul 2023 22:11:21 +0000 Subject: [PATCH 08/11] address review --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 2 +- .../rustc_hir_typeck/src/gather_locals.rs | 2 +- compiler/rustc_hir_typeck/src/pat.rs | 117 +++++------------- .../slice-pat-type-mismatches.rs | 5 +- .../slice-pat-type-mismatches.stderr | 16 +-- tests/ui/pattern/issue-76342.rs | 3 + tests/ui/pattern/slice-pattern-refutable.rs | 2 + .../ui/pattern/slice-pattern-refutable.stderr | 6 +- 8 files changed, 51 insertions(+), 102 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 2c1543ca1d55d..70ae21d9b983e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1478,7 +1478,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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.origin.try_get_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 b2b6ad0d101cf..ed4c63f171c42 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -21,7 +21,7 @@ pub(super) enum DeclOrigin<'a> { } impl<'a> DeclOrigin<'a> { - pub(super) fn try_get_els(&self) -> Option<&'a hir::Block<'a>> { + pub(super) fn try_get_else(&self) -> Option<&'a hir::Block<'a>> { match self { Self::LocalDecl { els } => *els, Self::LetExpr => None, diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 659223a377cc1..8e14212fd563e 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -163,7 +163,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Conversely, inside this module, `check_pat_top` should never be used. #[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, decl_origin } = pat_info; + 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)) @@ -172,76 +172,39 @@ 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, - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, - ), - PatKind::TupleStruct(ref qpath, subpats, ddpos) => self.check_pat_tuple_struct( - pat, - qpath, - subpats, - ddpos, - expected, - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, - ), + PatKind::Binding(ba, var_id, _, sub) => { + 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, 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, - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, - ), + PatKind::Struct(ref qpath, fields, has_rest_pat) => { + 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, - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, - ); + self.check_pat(pat, expected, pat_info); } expected } - PatKind::Tuple(elements, ddpos) => self.check_pat_tuple( - pat.span, - elements, - ddpos, - expected, - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, - ), - PatKind::Box(inner) => self.check_pat_box( - pat.span, - inner, - expected, - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, - ), - PatKind::Ref(inner, mutbl) => self.check_pat_ref( - pat, - inner, - mutbl, - expected, - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, - ), - PatKind::Slice(before, slice, after) => self.check_pat_slice( - pat.span, - before, - slice, - after, - expected, - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, - ), + PatKind::Tuple(elements, ddpos) => { + 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, pat_info) + } }; self.write_ty(pat.hir_id, ty); @@ -624,7 +587,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { - let PatInfo { binding_mode: def_bm, top_info: ti, decl_origin } = pat_info; + let PatInfo { binding_mode: def_bm, top_info: ti, .. } = pat_info; // Determine the binding mode... let bm = match ba { @@ -663,11 +626,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let Some(p) = sub { - self.check_pat( - p, - expected, - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, - ); + self.check_pat(p, expected, pat_info); } local_ty @@ -892,37 +851,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { - let PatInfo { binding_mode: def_bm, top_info: ti, decl_origin } = pat_info; - // Resolve the path and check the definition for errors. let (variant, pat_ty) = match self.check_struct_path(qpath, pat.hir_id) { Ok(data) => data, Err(guar) => { let err = Ty::new_error(self.tcx, guar); for field in fields { - let ti = ti; - self.check_pat( - field.pat, - err, - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, - ); + 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, - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, - ) { + 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) @@ -2144,11 +2089,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// 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>) -> bool { - if let Some(decl_origin) = decl_origin { - decl_origin.try_get_els().is_none() - && matches!(decl_origin, DeclOrigin::LocalDecl { .. }) - } else { - false + match decl_origin { + Some(DeclOrigin::LocalDecl { els: None }) => true, + Some(DeclOrigin::LocalDecl { els: Some(_) } | DeclOrigin::LetExpr) | None => false, } } @@ -2183,6 +2126,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } 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) => { 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 310c49701ed3e..03a1876fdc5a5 100644 --- a/tests/ui/array-slice-vec/slice-pat-type-mismatches.rs +++ b/tests/ui/array-slice-vec/slice-pat-type-mismatches.rs @@ -30,9 +30,8 @@ fn main() { } fn another_fn_to_avoid_suppression() { - match Default - //~^ ERROR expected value, found trait - { + 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 6218bffd50359..d1d042c477681 100644 --- a/tests/ui/array-slice-vec/slice-pat-type-mismatches.stderr +++ b/tests/ui/array-slice-vec/slice-pat-type-mismatches.stderr @@ -4,12 +4,6 @@ error[E0425]: cannot find value `does_not_exist` in this scope LL | match does_not_exist { | ^^^^^^^^^^^^^^ not found in this scope -error[E0423]: expected value, found trait `Default` - --> $DIR/slice-pat-type-mismatches.rs:33:11 - | -LL | match Default - | ^^^^^^^ not a value - error[E0529]: expected an array or slice, found `String` --> $DIR/slice-pat-type-mismatches.rs:3:9 | @@ -28,7 +22,13 @@ error[E0528]: pattern requires at least 4 elements but array has 3 LL | [0, 1, 2, 3, x @ ..] => {} | ^^^^^^^^^^^^^^^^^^^^ pattern cannot match array of 3 elements +error[E0282]: type annotations needed + --> $DIR/slice-pat-type-mismatches.rs:34:9 + | +LL | [] => {} + | ^^ cannot infer type + error: aborting due to 5 previous errors -Some errors have detailed explanations: E0423, E0425, E0527, E0528, E0529. -For more information about an error, try `rustc --explain E0423`. +Some errors have detailed explanations: E0282, E0425, E0527, E0528, E0529. +For more information about an error, try `rustc --explain E0282`. diff --git a/tests/ui/pattern/issue-76342.rs b/tests/ui/pattern/issue-76342.rs index 0d066a12179b9..e4d3218104e19 100644 --- a/tests/ui/pattern/issue-76342.rs +++ b/tests/ui/pattern/issue-76342.rs @@ -1,4 +1,7 @@ // check-pass + +// Test that we infer the expected type of a pattern to an array of the given length. + #![allow(unused_variables)] struct Zeroes; impl Into<[usize; 2]> for Zeroes { diff --git a/tests/ui/pattern/slice-pattern-refutable.rs b/tests/ui/pattern/slice-pattern-refutable.rs index 3961da3bf5343..1be3c6ef82db5 100644 --- a/tests/ui/pattern/slice-pattern-refutable.rs +++ b/tests/ui/pattern/slice-pattern-refutable.rs @@ -1,3 +1,5 @@ +// 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; diff --git a/tests/ui/pattern/slice-pattern-refutable.stderr b/tests/ui/pattern/slice-pattern-refutable.stderr index cb5503bac391e..df5b58d3e9c63 100644 --- a/tests/ui/pattern/slice-pattern-refutable.stderr +++ b/tests/ui/pattern/slice-pattern-refutable.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed - --> $DIR/slice-pattern-refutable.rs:12:9 + --> $DIR/slice-pattern-refutable.rs:14:9 | LL | let [a, b, c] = Zeroes.into() else { | ^^^^^^^^^ @@ -10,7 +10,7 @@ LL | let [a, b, c]: /* Type */ = Zeroes.into() else { | ++++++++++++ error[E0282]: type annotations needed - --> $DIR/slice-pattern-refutable.rs:19:31 + --> $DIR/slice-pattern-refutable.rs:21:31 | LL | if let [a, b, c] = Zeroes.into() { | --------- ^^^^ @@ -23,7 +23,7 @@ LL | if let [a, b, c] = >::into(Zeroes) { | ++++++++++++++++++++++++++ ~ error[E0282]: type annotations needed - --> $DIR/slice-pattern-refutable.rs:26:31 + --> $DIR/slice-pattern-refutable.rs:28:31 | LL | if let [a, b, c] = Zeroes.into() { | --------- ^^^^ From 96af415476968d91847dde81c8ac907823b0def0 Mon Sep 17 00:00:00 2001 From: b-naber Date: Mon, 17 Jul 2023 21:58:05 +0000 Subject: [PATCH 09/11] try to structurally resolve before try_resolve_slice_ty_to_array_ty --- compiler/rustc_hir_typeck/src/pat.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 8e14212fd563e..e0b7407a5ee2c 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -2114,6 +2114,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'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() { From e1e755311a6ae8a3d15dff80851d00a8b2881d82 Mon Sep 17 00:00:00 2001 From: b-naber Date: Mon, 17 Jul 2023 21:58:37 +0000 Subject: [PATCH 10/11] add more tests --- .../slice_destructure_fail.rs | 2 - .../slice_destructure_fail.stderr | 25 +------- tests/ui/pattern/issue-76342.rs | 25 -------- tests/ui/pattern/slice-array-infer.rs | 27 ++++++++ .../ui/pattern/slice-patterns-irrefutable.rs | 62 +++++++++++++++++++ .../pattern/slice-patterns-irrefutable.stderr | 14 +++++ 6 files changed, 106 insertions(+), 49 deletions(-) delete mode 100644 tests/ui/pattern/issue-76342.rs create mode 100644 tests/ui/pattern/slice-array-infer.rs create mode 100644 tests/ui/pattern/slice-patterns-irrefutable.rs create mode 100644 tests/ui/pattern/slice-patterns-irrefutable.stderr diff --git a/tests/ui/destructuring-assignment/slice_destructure_fail.rs b/tests/ui/destructuring-assignment/slice_destructure_fail.rs index e36557940ee3d..e5bb50030b915 100644 --- a/tests/ui/destructuring-assignment/slice_destructure_fail.rs +++ b/tests/ui/destructuring-assignment/slice_destructure_fail.rs @@ -3,8 +3,6 @@ fn main() { [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 - //~| ERROR mismatched types [_] = [1, 2]; //~^ ERROR pattern requires 1 element but array has 2 - //~| ERROR mismatched types } diff --git a/tests/ui/destructuring-assignment/slice_destructure_fail.stderr b/tests/ui/destructuring-assignment/slice_destructure_fail.stderr index e42b9695d4da7..acf44ceb184fc 100644 --- a/tests/ui/destructuring-assignment/slice_destructure_fail.stderr +++ b/tests/ui/destructuring-assignment/slice_destructure_fail.stderr @@ -6,37 +6,18 @@ LL | [a, .., b, ..] = [0, 1]; | | | previously used here -error[E0308]: mismatched types - --> $DIR/slice_destructure_fail.rs:4:5 - | -LL | [a, a, b] = [1, 2]; - | ^^^^^^^^^ expected an array with a fixed size of 2 elements, found one with 3 elements - | - = note: expected array `[{integer}; 2]` - found array `[_; 3]` - error[E0527]: pattern requires 3 elements but array has 2 --> $DIR/slice_destructure_fail.rs:4:5 | LL | [a, a, b] = [1, 2]; | ^^^^^^^^^ expected 2 elements -error[E0308]: mismatched types - --> $DIR/slice_destructure_fail.rs:7:5 - | -LL | [_] = [1, 2]; - | ^^^ expected an array with a fixed size of 2 elements, found one with 1 element - | - = note: expected array `[{integer}; 2]` - found array `[_; 1]` - error[E0527]: pattern requires 1 element but array has 2 - --> $DIR/slice_destructure_fail.rs:7:5 + --> $DIR/slice_destructure_fail.rs:6:5 | LL | [_] = [1, 2]; | ^^^ expected 2 elements -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0308, E0527. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0527`. diff --git a/tests/ui/pattern/issue-76342.rs b/tests/ui/pattern/issue-76342.rs deleted file mode 100644 index e4d3218104e19..0000000000000 --- a/tests/ui/pattern/issue-76342.rs +++ /dev/null @@ -1,25 +0,0 @@ -// check-pass - -// Test that we infer the expected type of a pattern to an array of the given length. - -#![allow(unused_variables)] -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 main() { - let [a, b, c] = Zeroes.into(); - let [d, e, f]: [_; 3] = Zeroes.into(); -} diff --git a/tests/ui/pattern/slice-array-infer.rs b/tests/ui/pattern/slice-array-infer.rs new file mode 100644 index 0000000000000..f94a3dcfe0a6c --- /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] = >::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-patterns-irrefutable.rs b/tests/ui/pattern/slice-patterns-irrefutable.rs new file mode 100644 index 0000000000000..7be02b44e43d7 --- /dev/null +++ b/tests/ui/pattern/slice-patterns-irrefutable.rs @@ -0,0 +1,62 @@ +// 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 { + let [a, b] = x.try_into()?; + Ok(a + b) +} + +fn default() { + 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(); +} + +struct Foo([T; 2]); + +impl Default for Foo { + 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 0000000000000..fac99534f3e23 --- /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`. From 5a9af37370bc220bc395939d878c64547e3466d0 Mon Sep 17 00:00:00 2001 From: b-naber Date: Fri, 28 Jul 2023 11:20:11 +0000 Subject: [PATCH 11/11] address review --- tests/ui/pattern/slice-patterns-irrefutable.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/ui/pattern/slice-patterns-irrefutable.rs b/tests/ui/pattern/slice-patterns-irrefutable.rs index 7be02b44e43d7..bd230608eb5ca 100644 --- a/tests/ui/pattern/slice-patterns-irrefutable.rs +++ b/tests/ui/pattern/slice-patterns-irrefutable.rs @@ -32,7 +32,7 @@ fn array_try_from(x: &[usize]) -> Result { Ok(a + b) } -fn default() { +fn destructuring_assignment() { let a: i32; let b; [a, b] = Default::default(); @@ -45,6 +45,18 @@ fn test_nested_array() { [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; 2]); impl Default for Foo {