diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index c2d987a95cf71..6d926cd8aa132 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 94c54197294fd..c63dab631452e 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 4f45a24b216ea..ed4c63f171c42 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 d8eb8c71b5ea3..8fc236f46b226 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, } +#[derive(Copy, Clone)] +struct PatInfo<'tcx, 'a> { + binding_mode: BindingMode, + top_info: TopInfo<'tcx>, + decl_origin: Option>, +} + 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, origin_expr: Option<&'tcx hir::Expr<'tcx>>, + decl_origin: Option>, ) { 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> { + 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>) -> 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 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..03a1876fdc5a5 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 70a4cbebeee98..d1d042c477681 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 33b09eb349da0..e5bb50030b915 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 92c86febac440..acf44ceb184fc 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 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-pattern-refutable.rs b/tests/ui/pattern/slice-pattern-refutable.rs new file mode 100644 index 0000000000000..1be3c6ef82db5 --- /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 0000000000000..df5b58d3e9c63 --- /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] = >::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] = >::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`. diff --git a/tests/ui/pattern/slice-patterns-irrefutable.rs b/tests/ui/pattern/slice-patterns-irrefutable.rs new file mode 100644 index 0000000000000..bd230608eb5ca --- /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 { + 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; 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`. 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()); +}