Skip to content

Commit 0b8fb21

Browse files
CentrilMark-Simulacrum
authored andcommitted
E0023: handle expected != pat-tup-type
1 parent dc17e5d commit 0b8fb21

File tree

4 files changed

+70
-10
lines changed

4 files changed

+70
-10
lines changed

src/librustc_typeck/check/demand.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
6565
}
6666
}
6767

68-
pub fn demand_eqtype_pat(
68+
pub fn demand_eqtype_pat_diag(
6969
&self,
7070
cause_span: Span,
7171
expected: Ty<'tcx>,
7272
actual: Ty<'tcx>,
7373
match_expr_span: Option<Span>,
74-
) {
74+
) -> Option<DiagnosticBuilder<'tcx>> {
7575
let cause = if let Some(span) = match_expr_span {
7676
self.cause(
7777
cause_span,
@@ -80,9 +80,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
8080
} else {
8181
self.misc(cause_span)
8282
};
83-
self.demand_eqtype_with_origin(&cause, expected, actual).map(|mut err| err.emit());
83+
self.demand_eqtype_with_origin(&cause, expected, actual)
8484
}
8585

86+
pub fn demand_eqtype_pat(
87+
&self,
88+
cause_span: Span,
89+
expected: Ty<'tcx>,
90+
actual: Ty<'tcx>,
91+
match_expr_span: Option<Span>,
92+
) {
93+
self.demand_eqtype_pat_diag(cause_span, expected, actual, match_expr_span)
94+
.map(|mut err| err.emit());
95+
}
8696

8797
pub fn demand_coerce(&self,
8898
expr: &hir::Expr,

src/librustc_typeck/check/pat.rs

+14-7
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
669669
let pat_ty = pat_ty.fn_sig(tcx).output();
670670
let pat_ty = pat_ty.no_bound_vars().expect("expected fn type");
671671

672-
self.demand_eqtype_pat(pat.span, expected, pat_ty, match_arm_pat_span);
672+
// Type-check the tuple struct pattern against the expected type.
673+
let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, match_arm_pat_span);
674+
let had_err = diag.is_some();
675+
diag.map(|mut err| err.emit());
673676

674677
// Type-check subpatterns.
675678
if subpats.len() == variant.fields.len()
@@ -687,7 +690,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
687690
}
688691
} else {
689692
// Pattern has wrong number of fields.
690-
self.e0023(pat.span, res, qpath, subpats, &variant.fields, expected);
693+
self.e0023(pat.span, res, qpath, subpats, &variant.fields, expected, had_err);
691694
on_error();
692695
return tcx.types.err;
693696
}
@@ -700,8 +703,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
700703
res: Res,
701704
qpath: &hir::QPath,
702705
subpats: &'tcx [P<Pat>],
703-
fields: &[ty::FieldDef],
704-
expected: Ty<'tcx>
706+
fields: &'tcx [ty::FieldDef],
707+
expected: Ty<'tcx>,
708+
had_err: bool,
705709
) {
706710
let subpats_ending = pluralise!(subpats.len());
707711
let fields_ending = pluralise!(fields.len());
@@ -729,9 +733,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
729733
// More generally, the expected type wants a tuple variant with one field of an
730734
// N-arity-tuple, e.g., `V_i((p_0, .., p_N))`. Meanwhile, the user supplied a pattern
731735
// with the subpatterns directly in the tuple variant pattern, e.g., `V_i(p_0, .., p_N)`.
732-
let missing_parenthesis = match expected.kind {
733-
ty::Adt(_, substs) if fields.len() == 1 => {
734-
let field_ty = fields[0].ty(self.tcx, substs);
736+
let missing_parenthesis = match (&expected.kind, fields, had_err) {
737+
// #67037: only do this if we could sucessfully type-check the expected type against
738+
// the tuple struct pattern. Otherwise the substs could get out of range on e.g.,
739+
// `let P() = U;` where `P != U` with `struct P<T>(T);`.
740+
(ty::Adt(_, substs), [field], false) => {
741+
let field_ty = self.field_ty(pat_span, field, substs);
735742
match field_ty.kind {
736743
ty::Tuple(_) => field_ty.tuple_fields().count() == subpats.len(),
737744
_ => false,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Regression test for #67037.
2+
//
3+
// In type checking patterns, E0023 occurs when the tuple pattern and the expected
4+
// tuple pattern have different number of fields. For example, as below, `P()`,
5+
// the tuple struct pattern, has 0 fields, but requires 1 field.
6+
//
7+
// In emitting E0023, we try to see if this is a case of e.g., `Some(a, b, c)` but where
8+
// the scrutinee was of type `Some((a, b, c))`, and suggest that parenthesis be added.
9+
//
10+
// However, we did not account for the expected type being different than the tuple pattern type.
11+
// This caused an issue when the tuple pattern type (`P<T>`) was generic.
12+
// Specifically, we tried deriving the 0th field's type using the `substs` of the expected type.
13+
// When attempting to substitute `T`, there was no such substitution, so "out of range" occured.
14+
15+
struct U {} // 0 type parameters offered
16+
struct P<T>(T); // 1 type parameter wanted
17+
18+
fn main() {
19+
let P() = U {}; //~ ERROR mismatched types
20+
//~^ ERROR this pattern has 0 fields, but the corresponding tuple struct has 1 field
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs:19:9
3+
|
4+
LL | let P() = U {};
5+
| ^^^ expected struct `U`, found struct `P`
6+
|
7+
= note: expected type `U`
8+
found type `P<_>`
9+
10+
error[E0023]: this pattern has 0 fields, but the corresponding tuple struct has 1 field
11+
--> $DIR/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs:19:9
12+
|
13+
LL | struct P<T>(T); // 1 type parameter wanted
14+
| --------------- tuple struct defined here
15+
...
16+
LL | let P() = U {};
17+
| ^^^ expected 1 field, found 0
18+
19+
error: aborting due to 2 previous errors
20+
21+
Some errors have detailed explanations: E0023, E0308.
22+
For more information about an error, try `rustc --explain E0023`.

0 commit comments

Comments
 (0)