From 4746d37339f77fa4a07be6104e3f6385a8fb67bd Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Thu, 5 Dec 2019 04:36:53 +0100 Subject: [PATCH] E0023: handle expected != pat-tup-type --- src/librustc_typeck/check/demand.rs | 16 +++++++++++--- src/librustc_typeck/check/pat.rs | 21 ++++++++++++------ ...67037-pat-tup-scrut-ty-diff-less-fields.rs | 21 ++++++++++++++++++ ...7-pat-tup-scrut-ty-diff-less-fields.stderr | 22 +++++++++++++++++++ 4 files changed, 70 insertions(+), 10 deletions(-) create mode 100644 src/test/ui/issues/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs create mode 100644 src/test/ui/issues/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 4331d441aa0d2..32df6c4636c2d 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -65,13 +65,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub fn demand_eqtype_pat( + pub fn demand_eqtype_pat_diag( &self, cause_span: Span, expected: Ty<'tcx>, actual: Ty<'tcx>, match_expr_span: Option, - ) { + ) -> Option> { let cause = if let Some(span) = match_expr_span { self.cause( cause_span, @@ -80,9 +80,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { self.misc(cause_span) }; - self.demand_eqtype_with_origin(&cause, expected, actual).map(|mut err| err.emit()); + self.demand_eqtype_with_origin(&cause, expected, actual) } + pub fn demand_eqtype_pat( + &self, + cause_span: Span, + expected: Ty<'tcx>, + actual: Ty<'tcx>, + match_expr_span: Option, + ) { + self.demand_eqtype_pat_diag(cause_span, expected, actual, match_expr_span) + .map(|mut err| err.emit()); + } pub fn demand_coerce(&self, expr: &hir::Expr, diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs index 9dd3bc624a51a..71d1cd869a6a2 100644 --- a/src/librustc_typeck/check/pat.rs +++ b/src/librustc_typeck/check/pat.rs @@ -703,7 +703,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let pat_ty = pat_ty.fn_sig(tcx).output(); let pat_ty = pat_ty.no_bound_vars().expect("expected fn type"); - self.demand_eqtype_pat(pat.span, expected, pat_ty, match_arm_pat_span); + // Type-check the tuple struct pattern against the expected type. + let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, match_arm_pat_span); + let had_err = diag.is_some(); + diag.map(|mut err| err.emit()); // Type-check subpatterns. if subpats.len() == variant.fields.len() @@ -721,7 +724,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } else { // Pattern has wrong number of fields. - self.e0023(pat.span, res, qpath, subpats, &variant.fields, expected); + self.e0023(pat.span, res, qpath, subpats, &variant.fields, expected, had_err); on_error(); return tcx.types.err; } @@ -734,8 +737,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { res: Res, qpath: &hir::QPath, subpats: &'tcx [P], - fields: &[ty::FieldDef], - expected: Ty<'tcx> + fields: &'tcx [ty::FieldDef], + expected: Ty<'tcx>, + had_err: bool, ) { let subpats_ending = pluralize!(subpats.len()); let fields_ending = pluralize!(fields.len()); @@ -763,9 +767,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // More generally, the expected type wants a tuple variant with one field of an // N-arity-tuple, e.g., `V_i((p_0, .., p_N))`. Meanwhile, the user supplied a pattern // with the subpatterns directly in the tuple variant pattern, e.g., `V_i(p_0, .., p_N)`. - let missing_parenthesis = match expected.kind { - ty::Adt(_, substs) if fields.len() == 1 => { - let field_ty = fields[0].ty(self.tcx, substs); + let missing_parenthesis = match (&expected.kind, fields, had_err) { + // #67037: only do this if we could sucessfully type-check the expected type against + // the tuple struct pattern. Otherwise the substs could get out of range on e.g., + // `let P() = U;` where `P != U` with `struct P(T);`. + (ty::Adt(_, substs), [field], false) => { + let field_ty = self.field_ty(pat_span, field, substs); match field_ty.kind { ty::Tuple(_) => field_ty.tuple_fields().count() == subpats.len(), _ => false, diff --git a/src/test/ui/issues/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs b/src/test/ui/issues/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs new file mode 100644 index 0000000000000..44bd645598ae0 --- /dev/null +++ b/src/test/ui/issues/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs @@ -0,0 +1,21 @@ +// Regression test for #67037. +// +// In type checking patterns, E0023 occurs when the tuple pattern and the expected +// tuple pattern have different number of fields. For example, as below, `P()`, +// the tuple struct pattern, has 0 fields, but requires 1 field. +// +// In emitting E0023, we try to see if this is a case of e.g., `Some(a, b, c)` but where +// the scrutinee was of type `Some((a, b, c))`, and suggest that parenthesis be added. +// +// However, we did not account for the expected type being different than the tuple pattern type. +// This caused an issue when the tuple pattern type (`P`) was generic. +// Specifically, we tried deriving the 0th field's type using the `substs` of the expected type. +// When attempting to substitute `T`, there was no such substitution, so "out of range" occured. + +struct U {} // 0 type parameters offered +struct P(T); // 1 type parameter wanted + +fn main() { + let P() = U {}; //~ ERROR mismatched types + //~^ ERROR this pattern has 0 fields, but the corresponding tuple struct has 1 field +} diff --git a/src/test/ui/issues/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr b/src/test/ui/issues/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr new file mode 100644 index 0000000000000..521dd0256f7d5 --- /dev/null +++ b/src/test/ui/issues/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr @@ -0,0 +1,22 @@ +error[E0308]: mismatched types + --> $DIR/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs:19:9 + | +LL | let P() = U {}; + | ^^^ expected struct `U`, found struct `P` + | + = note: expected struct `U` + found struct `P<_>` + +error[E0023]: this pattern has 0 fields, but the corresponding tuple struct has 1 field + --> $DIR/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs:19:9 + | +LL | struct P(T); // 1 type parameter wanted + | --------------- tuple struct defined here +... +LL | let P() = U {}; + | ^^^ expected 1 field, found 0 + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0023, E0308. +For more information about an error, try `rustc --explain E0023`.