Skip to content

Commit d8dae4f

Browse files
committed
Perform type inference in range pattern
1 parent 497ee32 commit d8dae4f

File tree

3 files changed

+44
-28
lines changed

3 files changed

+44
-28
lines changed

compiler/rustc_typeck/src/check/pat.rs

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -448,16 +448,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
448448
ti: TopInfo<'tcx>,
449449
) -> Ty<'tcx> {
450450
let calc_side = |opt_expr: Option<&'tcx hir::Expr<'tcx>>| match opt_expr {
451-
None => (None, None),
451+
None => None,
452452
Some(expr) => {
453453
let ty = self.check_expr(expr);
454-
// Check that the end-point is of numeric or char type.
455-
let fail = !(ty.is_numeric() || ty.is_char() || ty.references_error());
456-
(Some(ty), Some((fail, ty, expr.span)))
454+
// Check that the end-point is possibly of numeric or char type.
455+
// The early check here is not for correctness, but rather better
456+
// diagnostics (e.g. when `&str` is being matched, `expected` will
457+
// be peeled to `str` while ty here is still `&str`, if we don't
458+
// err ealy here, a rather confusing unification error will be
459+
// emitted instead).
460+
let fail =
461+
!(ty.is_numeric() || ty.is_char() || ty.is_ty_var() || ty.references_error());
462+
Some((fail, ty, expr.span))
457463
}
458464
};
459-
let (lhs_ty, lhs) = calc_side(lhs);
460-
let (rhs_ty, rhs) = calc_side(rhs);
465+
let mut lhs = calc_side(lhs);
466+
let mut rhs = calc_side(rhs);
461467

462468
if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) {
463469
// There exists a side that didn't meet our criteria that the end-point
@@ -466,25 +472,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
466472
return self.tcx.ty_error();
467473
}
468474

469-
// Now that we know the types can be unified we find the unified type
470-
// and use it to type the entire expression.
471-
let common_type = self.resolve_vars_if_possible(lhs_ty.or(rhs_ty).unwrap_or(expected));
472-
475+
// Unify each side with `expected`.
473476
// Subtyping doesn't matter here, as the value is some kind of scalar.
474-
let demand_eqtype = |x, y| {
475-
if let Some((_, x_ty, x_span)) = x {
477+
let demand_eqtype = |x: &mut _, y| {
478+
if let Some((ref mut fail, x_ty, x_span)) = *x {
476479
if let Some(mut err) = self.demand_eqtype_pat_diag(x_span, expected, x_ty, ti) {
477480
if let Some((_, y_ty, y_span)) = y {
478481
self.endpoint_has_type(&mut err, y_span, y_ty);
479482
}
480483
err.emit();
484+
*fail = true;
481485
};
482486
}
483487
};
484-
demand_eqtype(lhs, rhs);
485-
demand_eqtype(rhs, lhs);
488+
demand_eqtype(&mut lhs, rhs);
489+
demand_eqtype(&mut rhs, lhs);
490+
491+
if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) {
492+
return self.tcx.ty_error();
493+
}
486494

487-
common_type
495+
// Find the unified type and check if it's of numeric or char type again.
496+
// This check is needed if both sides are inference variables.
497+
// We require types to be resolved here so that we emit inference failure
498+
// rather than "_ is not a char or numeric".
499+
let ty = self.structurally_resolved_type(span, expected);
500+
if !(ty.is_numeric() || ty.is_char() || ty.references_error()) {
501+
if let Some((ref mut fail, _, _)) = lhs {
502+
*fail = true;
503+
}
504+
if let Some((ref mut fail, _, _)) = rhs {
505+
*fail = true;
506+
}
507+
self.emit_err_pat_range(span, lhs, rhs);
508+
return self.tcx.ty_error();
509+
}
510+
ty
488511
}
489512

490513
fn endpoint_has_type(&self, err: &mut DiagnosticBuilder<'_>, span: Span, ty: Ty<'_>) {
@@ -511,10 +534,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
511534
E0029,
512535
"only `char` and numeric types are allowed in range patterns"
513536
);
514-
let msg = |ty| format!("this is of type `{}` but it should be `char` or numeric", ty);
537+
let msg = |ty| {
538+
let ty = self.resolve_vars_if_possible(ty);
539+
format!("this is of type `{}` but it should be `char` or numeric", ty)
540+
};
515541
let mut one_side_err = |first_span, first_ty, second: Option<(bool, Ty<'tcx>, Span)>| {
516542
err.span_label(first_span, &msg(first_ty));
517543
if let Some((_, ty, sp)) = second {
544+
let ty = self.resolve_vars_if_possible(ty);
518545
self.endpoint_has_type(&mut err, sp, ty);
519546
}
520547
};

src/test/ui/pattern/patkind-litrange-no-expr.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ enum_number!(Change {
1919
Neg = -1,
2020
Arith = 1 + 1, //~ ERROR arbitrary expressions aren't allowed in patterns
2121
//~| ERROR arbitrary expressions aren't allowed in patterns
22-
//~| ERROR only `char` and numeric types are allowed in range patterns
2322
});
2423

2524
fn main() {}

src/test/ui/pattern/patkind-litrange-no-expr.stderr

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,5 @@ error: arbitrary expressions aren't allowed in patterns
1010
LL | Arith = 1 + 1,
1111
| ^^^^^
1212

13-
error[E0029]: only `char` and numeric types are allowed in range patterns
14-
--> $DIR/patkind-litrange-no-expr.rs:20:13
15-
|
16-
LL | $( $value ..= 42 => Some($name::$variant), )* // PatKind::Range
17-
| -- this is of type `{integer}`
18-
...
19-
LL | Arith = 1 + 1,
20-
| ^^^^^ this is of type `_` but it should be `char` or numeric
21-
22-
error: aborting due to 3 previous errors
13+
error: aborting due to 2 previous errors
2314

24-
For more information about this error, try `rustc --explain E0029`.

0 commit comments

Comments
 (0)