Skip to content

Commit 4f6afee

Browse files
Rollup merge of #88090 - nbdd0121:inference, r=nikomatsakis
Perform type inference in range pattern Fix #88074
2 parents 99e6e3f + 52a0403 commit 4f6afee

6 files changed

+109
-28
lines changed

compiler/rustc_typeck/src/check/pat.rs

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

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

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

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

491514
fn endpoint_has_type(&self, err: &mut DiagnosticBuilder<'_>, span: Span, ty: Ty<'_>) {
@@ -512,10 +535,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
512535
E0029,
513536
"only `char` and numeric types are allowed in range patterns"
514537
);
515-
let msg = |ty| format!("this is of type `{}` but it should be `char` or numeric", ty);
538+
let msg = |ty| {
539+
let ty = self.resolve_vars_if_possible(ty);
540+
format!("this is of type `{}` but it should be `char` or numeric", ty)
541+
};
516542
let mut one_side_err = |first_span, first_ty, second: Option<(bool, Ty<'tcx>, Span)>| {
517543
err.span_label(first_span, &msg(first_ty));
518544
if let Some((_, ty, sp)) = second {
545+
let ty = self.resolve_vars_if_possible(ty);
519546
self.endpoint_has_type(&mut err, sp, ty);
520547
}
521548
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
trait Zero {
2+
const ZERO: Self;
3+
}
4+
5+
impl Zero for String {
6+
const ZERO: Self = String::new();
7+
}
8+
9+
fn foo() {
10+
match String::new() {
11+
Zero::ZERO ..= Zero::ZERO => {},
12+
//~^ ERROR only `char` and numeric types are allowed in range patterns
13+
_ => {},
14+
}
15+
}
16+
17+
fn bar() {
18+
match Zero::ZERO {
19+
Zero::ZERO ..= Zero::ZERO => {},
20+
//~^ ERROR type annotations needed [E0282]
21+
_ => {},
22+
}
23+
}
24+
25+
fn main() {
26+
foo();
27+
bar();
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0029]: only `char` and numeric types are allowed in range patterns
2+
--> $DIR/issue-88074-pat-range-type-inference-err.rs:11:9
3+
|
4+
LL | Zero::ZERO ..= Zero::ZERO => {},
5+
| ----------^^^^^----------
6+
| | |
7+
| | this is of type `String` but it should be `char` or numeric
8+
| this is of type `String` but it should be `char` or numeric
9+
10+
error[E0282]: type annotations needed
11+
--> $DIR/issue-88074-pat-range-type-inference-err.rs:19:9
12+
|
13+
LL | Zero::ZERO ..= Zero::ZERO => {},
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
15+
|
16+
= note: type must be known at this point
17+
18+
error: aborting due to 2 previous errors
19+
20+
Some errors have detailed explanations: E0029, E0282.
21+
For more information about an error, try `rustc --explain E0029`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// check-pass
2+
3+
trait Zero {
4+
const ZERO: Self;
5+
}
6+
7+
impl Zero for i32 {
8+
const ZERO: Self = 0;
9+
}
10+
11+
fn main() {
12+
match 1 {
13+
Zero::ZERO ..= 1 => {},
14+
_ => {},
15+
}
16+
}

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

-1
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

+1-11
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)