Skip to content

Commit

Permalink
Rollup merge of rust-lang#88090 - nbdd0121:inference, r=nikomatsakis
Browse files Browse the repository at this point in the history
Perform type inference in range pattern

Fix rust-lang#88074
  • Loading branch information
Manishearth authored Oct 5, 2021
2 parents 3cbd39f + 52a0403 commit 537d112
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 28 deletions.
59 changes: 43 additions & 16 deletions compiler/rustc_typeck/src/check/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,16 +449,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ti: TopInfo<'tcx>,
) -> Ty<'tcx> {
let calc_side = |opt_expr: Option<&'tcx hir::Expr<'tcx>>| match opt_expr {
None => (None, None),
None => None,
Some(expr) => {
let ty = self.check_expr(expr);
// Check that the end-point is of numeric or char type.
let fail = !(ty.is_numeric() || ty.is_char() || ty.references_error());
(Some(ty), Some((fail, ty, expr.span)))
// Check that the end-point is possibly of numeric or char type.
// The early check here is not for correctness, but rather better
// diagnostics (e.g. when `&str` is being matched, `expected` will
// be peeled to `str` while ty here is still `&str`, if we don't
// err ealy here, a rather confusing unification error will be
// emitted instead).
let fail =
!(ty.is_numeric() || ty.is_char() || ty.is_ty_var() || ty.references_error());
Some((fail, ty, expr.span))
}
};
let (lhs_ty, lhs) = calc_side(lhs);
let (rhs_ty, rhs) = calc_side(rhs);
let mut lhs = calc_side(lhs);
let mut rhs = calc_side(rhs);

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

// Now that we know the types can be unified we find the unified type
// and use it to type the entire expression.
let common_type = self.resolve_vars_if_possible(lhs_ty.or(rhs_ty).unwrap_or(expected));

// Unify each side with `expected`.
// Subtyping doesn't matter here, as the value is some kind of scalar.
let demand_eqtype = |x, y| {
if let Some((_, x_ty, x_span)) = x {
let demand_eqtype = |x: &mut _, y| {
if let Some((ref mut fail, x_ty, x_span)) = *x {
if let Some(mut err) = self.demand_eqtype_pat_diag(x_span, expected, x_ty, ti) {
if let Some((_, y_ty, y_span)) = y {
self.endpoint_has_type(&mut err, y_span, y_ty);
}
err.emit();
*fail = true;
};
}
};
demand_eqtype(lhs, rhs);
demand_eqtype(rhs, lhs);
demand_eqtype(&mut lhs, rhs);
demand_eqtype(&mut rhs, lhs);

if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) {
return self.tcx.ty_error();
}

common_type
// Find the unified type and check if it's of numeric or char type again.
// This check is needed if both sides are inference variables.
// We require types to be resolved here so that we emit inference failure
// rather than "_ is not a char or numeric".
let ty = self.structurally_resolved_type(span, expected);
if !(ty.is_numeric() || ty.is_char() || ty.references_error()) {
if let Some((ref mut fail, _, _)) = lhs {
*fail = true;
}
if let Some((ref mut fail, _, _)) = rhs {
*fail = true;
}
self.emit_err_pat_range(span, lhs, rhs);
return self.tcx.ty_error();
}
ty
}

fn endpoint_has_type(&self, err: &mut DiagnosticBuilder<'_>, span: Span, ty: Ty<'_>) {
Expand All @@ -512,10 +535,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
E0029,
"only `char` and numeric types are allowed in range patterns"
);
let msg = |ty| format!("this is of type `{}` but it should be `char` or numeric", ty);
let msg = |ty| {
let ty = self.resolve_vars_if_possible(ty);
format!("this is of type `{}` but it should be `char` or numeric", ty)
};
let mut one_side_err = |first_span, first_ty, second: Option<(bool, Ty<'tcx>, Span)>| {
err.span_label(first_span, &msg(first_ty));
if let Some((_, ty, sp)) = second {
let ty = self.resolve_vars_if_possible(ty);
self.endpoint_has_type(&mut err, sp, ty);
}
};
Expand Down
28 changes: 28 additions & 0 deletions src/test/ui/pattern/issue-88074-pat-range-type-inference-err.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
trait Zero {
const ZERO: Self;
}

impl Zero for String {
const ZERO: Self = String::new();
}

fn foo() {
match String::new() {
Zero::ZERO ..= Zero::ZERO => {},
//~^ ERROR only `char` and numeric types are allowed in range patterns
_ => {},
}
}

fn bar() {
match Zero::ZERO {
Zero::ZERO ..= Zero::ZERO => {},
//~^ ERROR type annotations needed [E0282]
_ => {},
}
}

fn main() {
foo();
bar();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0029]: only `char` and numeric types are allowed in range patterns
--> $DIR/issue-88074-pat-range-type-inference-err.rs:11:9
|
LL | Zero::ZERO ..= Zero::ZERO => {},
| ----------^^^^^----------
| | |
| | this is of type `String` but it should be `char` or numeric
| this is of type `String` but it should be `char` or numeric

error[E0282]: type annotations needed
--> $DIR/issue-88074-pat-range-type-inference-err.rs:19:9
|
LL | Zero::ZERO ..= Zero::ZERO => {},
| ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
|
= note: type must be known at this point

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0029, E0282.
For more information about an error, try `rustc --explain E0029`.
16 changes: 16 additions & 0 deletions src/test/ui/pattern/issue-88074-pat-range-type-inference.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// check-pass

trait Zero {
const ZERO: Self;
}

impl Zero for i32 {
const ZERO: Self = 0;
}

fn main() {
match 1 {
Zero::ZERO ..= 1 => {},
_ => {},
}
}
1 change: 0 additions & 1 deletion src/test/ui/pattern/patkind-litrange-no-expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ enum_number!(Change {
Neg = -1,
Arith = 1 + 1, //~ ERROR arbitrary expressions aren't allowed in patterns
//~| ERROR arbitrary expressions aren't allowed in patterns
//~| ERROR only `char` and numeric types are allowed in range patterns
});

fn main() {}
12 changes: 1 addition & 11 deletions src/test/ui/pattern/patkind-litrange-no-expr.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,5 @@ error: arbitrary expressions aren't allowed in patterns
LL | Arith = 1 + 1,
| ^^^^^

error[E0029]: only `char` and numeric types are allowed in range patterns
--> $DIR/patkind-litrange-no-expr.rs:20:13
|
LL | $( $value ..= 42 => Some($name::$variant), )* // PatKind::Range
| -- this is of type `{integer}`
...
LL | Arith = 1 + 1,
| ^^^^^ this is of type `_` but it should be `char` or numeric

error: aborting due to 3 previous errors
error: aborting due to 2 previous errors

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

0 comments on commit 537d112

Please sign in to comment.