about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-12-05 04:36:53 +0100
committerMazdak Farrokhzad <twingoow@gmail.com>2019-12-05 06:18:01 +0100
commit4746d37339f77fa4a07be6104e3f6385a8fb67bd (patch)
tree897977d04d950f7ef6e3706f630f0e1b9a7df052
parentaeaaf8f640c919074983edd8366ab3d10321f2b5 (diff)
downloadrust-4746d37339f77fa4a07be6104e3f6385a8fb67bd.tar.gz
rust-4746d37339f77fa4a07be6104e3f6385a8fb67bd.zip
E0023: handle expected != pat-tup-type
-rw-r--r--src/librustc_typeck/check/demand.rs16
-rw-r--r--src/librustc_typeck/check/pat.rs21
-rw-r--r--src/test/ui/issues/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs21
-rw-r--r--src/test/ui/issues/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr22
4 files changed, 70 insertions, 10 deletions
diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index 4331d441aa0..32df6c4636c 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<Span>,
-    ) {
+    ) -> Option<DiagnosticBuilder<'tcx>> {
         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<Span>,
+    ) {
+        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 9dd3bc624a5..71d1cd869a6 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<Pat>],
-        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>(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 00000000000..44bd645598a
--- /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<T>`) 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>(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 00000000000..521dd0256f7
--- /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>(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`.