Skip to content

Commit

Permalink
Rollup merge of rust-lang#73334 - ayazhafiz:err/num-type-cannot-fit, …
Browse files Browse the repository at this point in the history
…r=estebank

Note numeric literals that can never fit in an expected type

re rust-lang#72380 (comment)

Given the toy code

```rust
fn is_positive(n: usize) {
  n > -1_isize;
}
```

We currently get a type mismatch error like the following:

```
error[E0308]: mismatched types
 --> src/main.rs:2:9
  |
2 |     n > -1_isize;
  |         ^^^^^^^^ expected `usize`, found `isize`
  |
help: you can convert an `isize` to `usize` and panic if the converted value wouldn't fit
  |
2 |     n > (-1_isize).try_into().unwrap();
  |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
```

But clearly, `-1` can never fit into a `usize`, so the suggestion will
always panic. A more useful message would tell the user that the value
can never fit in the expected type:

```
error[E0308]: mismatched types
 --> test.rs:2:9
  |
2 |     n > -1_isize;
  |         ^^^^^^^^ expected `usize`, found `isize`
  |
note: `-1_isize` can never fit into `usize`
 --> test.rs:2:9
  |
2 |     n > -1_isize;
  |         ^^^^^^^^
```

Which is what this commit implements.

I only added this check for negative literals because

- Currently we can only perform such a check for literals (constant
  value propagation is outside the scope of the typechecker at this
  point)
- A lint error for out-of-range numeric literals is already emitted

IMO it makes more sense to put this check in librustc_lint, but as far
as I can tell the typecheck pass happens before the lint pass, so I've
added it here.

r? @estebank
  • Loading branch information
Manishearth authored Jun 19, 2020
2 parents 058971c + 7a89a33 commit b285d68
Show file tree
Hide file tree
Showing 4 changed files with 428 additions and 1 deletion.
14 changes: 13 additions & 1 deletion src/librustc_typeck/check/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let literal_is_ty_suffixed = |expr: &hir::Expr<'_>| {
if let hir::ExprKind::Lit(lit) = &expr.kind { lit.node.is_suffixed() } else { false }
};
let is_negative_int =
|expr: &hir::Expr<'_>| matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::UnNeg, ..));
let is_uint = |ty: Ty<'_>| matches!(ty.kind, ty::Uint(..));

let in_const_context = self.tcx.hir().is_inside_const_context(expr.hir_id);

Expand All @@ -807,7 +810,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"you can convert `{}` from `{}` to `{}`, matching the type of `{}`",
lhs_src, expected_ty, checked_ty, src
);
let suggestion = format!("{}::from({})", checked_ty, lhs_src,);
let suggestion = format!("{}::from({})", checked_ty, lhs_src);
(lhs_expr.span, msg, suggestion)
} else {
let msg = format!("{} and panic if the converted value wouldn't fit", msg);
Expand All @@ -822,8 +825,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|err: &mut DiagnosticBuilder<'_>,
found_to_exp_is_fallible: bool,
exp_to_found_is_fallible: bool| {
let always_fallible = found_to_exp_is_fallible
&& (exp_to_found_is_fallible || expected_ty_expr.is_none());
let msg = if literal_is_ty_suffixed(expr) {
&lit_msg
} else if always_fallible && (is_negative_int(expr) && is_uint(expected_ty)) {
// We now know that converting either the lhs or rhs is fallible. Before we
// suggest a fallible conversion, check if the value can never fit in the
// expected type.
let msg = format!("`{}` cannot fit into type `{}`", src, expected_ty);
err.note(&msg);
return;
} else if in_const_context {
// Do not recommend `into` or `try_into` in const contexts.
return;
Expand Down
87 changes: 87 additions & 0 deletions src/test/ui/numeric/numeric-cast-no-fix.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#[allow(unused_must_use)]
fn main() {
let x_usize: usize = 1;
let x_u128: u128 = 2;
let x_u64: u64 = 3;
let x_u32: u32 = 4;
let x_u16: u16 = 5;
let x_u8: u8 = 6;

x_usize > -1_isize;
//~^ ERROR mismatched types
x_u128 > -1_isize;
//~^ ERROR mismatched types
x_u64 > -1_isize;
//~^ ERROR mismatched types
x_u32 > -1_isize;
//~^ ERROR mismatched types
x_u16 > -1_isize;
//~^ ERROR mismatched types
x_u8 > -1_isize;
//~^ ERROR mismatched types

x_usize > -1_i128;
//~^ ERROR mismatched types
x_u128 > -1_i128;
//~^ ERROR mismatched types
x_u64 > -1_i128;
//~^ ERROR mismatched types
x_u32 > -1_i128;
//~^ ERROR mismatched types
x_u16 > -1_i128;
//~^ ERROR mismatched types
x_u8 > -1_i128;
//~^ ERROR mismatched types

x_usize > -1_i64;
//~^ ERROR mismatched types
x_u128 > -1_i64;
//~^ ERROR mismatched types
x_u64 > -1_i64;
//~^ ERROR mismatched types
x_u32 > -1_i64;
//~^ ERROR mismatched types
x_u16 > -1_i64;
//~^ ERROR mismatched types
x_u8 > -1_i64;
//~^ ERROR mismatched types

x_usize > -1_i32;
//~^ ERROR mismatched types
x_u128 > -1_i32;
//~^ ERROR mismatched types
x_u64 > -1_i32;
//~^ ERROR mismatched types
x_u32 > -1_i32;
//~^ ERROR mismatched types
x_u16 > -1_i32;
//~^ ERROR mismatched types
x_u8 > -1_i32;
//~^ ERROR mismatched types

x_usize > -1_i16;
//~^ ERROR mismatched types
x_u128 > -1_i16;
//~^ ERROR mismatched types
x_u64 > -1_i16;
//~^ ERROR mismatched types
x_u32 > -1_i16;
//~^ ERROR mismatched types
x_u16 > -1_i16;
//~^ ERROR mismatched types
x_u8 > -1_i16;
//~^ ERROR mismatched types

x_usize > -1_i8;
//~^ ERROR mismatched types
x_u128 > -1_i8;
//~^ ERROR mismatched types
x_u64 > -1_i8;
//~^ ERROR mismatched types
x_u32 > -1_i8;
//~^ ERROR mismatched types
x_u16 > -1_i8;
//~^ ERROR mismatched types
x_u8 > -1_i8;
//~^ ERROR mismatched types
}
Loading

0 comments on commit b285d68

Please sign in to comment.