about summary refs log tree commit diff
diff options
context:
space:
mode:
authorManish Goregaokar <manishsmail@gmail.com>2020-06-19 09:15:10 -0700
committerGitHub <noreply@github.com>2020-06-19 09:15:10 -0700
commitb285d68f36435b8ee75fc516be85b21eaa6c78c1 (patch)
treea81e8012db6759bf10b43327c1c95066418799c3
parent058971cef3e7ce5925fa910239645f7c793d8dbc (diff)
parent7a89a338239c0d551279b9183b9c2c1a1e7d7e74 (diff)
downloadrust-b285d68f36435b8ee75fc516be85b21eaa6c78c1.tar.gz
rust-b285d68f36435b8ee75fc516be85b21eaa6c78c1.zip
Rollup merge of #73334 - ayazhafiz:err/num-type-cannot-fit, r=estebank
Note numeric literals that can never fit in an expected type

re https://github.com/rust-lang/rust/pull/72380#discussion_r438289385

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
-rw-r--r--src/librustc_typeck/check/demand.rs14
-rw-r--r--src/test/ui/numeric/numeric-cast-no-fix.rs87
-rw-r--r--src/test/ui/numeric/numeric-cast-no-fix.stderr324
-rw-r--r--src/test/ui/repeat_count.stderr4
4 files changed, 428 insertions, 1 deletions
diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index 019b4ca6606..bcfc0cf347c 100644
--- a/src/librustc_typeck/check/demand.rs
+++ b/src/librustc_typeck/check/demand.rs
@@ -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);
 
@@ -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);
@@ -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;
diff --git a/src/test/ui/numeric/numeric-cast-no-fix.rs b/src/test/ui/numeric/numeric-cast-no-fix.rs
new file mode 100644
index 00000000000..63e5f098a25
--- /dev/null
+++ b/src/test/ui/numeric/numeric-cast-no-fix.rs
@@ -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
+}
diff --git a/src/test/ui/numeric/numeric-cast-no-fix.stderr b/src/test/ui/numeric/numeric-cast-no-fix.stderr
new file mode 100644
index 00000000000..4852e7047b4
--- /dev/null
+++ b/src/test/ui/numeric/numeric-cast-no-fix.stderr
@@ -0,0 +1,324 @@
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:10:15
+   |
+LL |     x_usize > -1_isize;
+   |               ^^^^^^^^ expected `usize`, found `isize`
+   |
+   = note: `-1_isize` cannot fit into type `usize`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:12:14
+   |
+LL |     x_u128 > -1_isize;
+   |              ^^^^^^^^ expected `u128`, found `isize`
+   |
+   = note: `-1_isize` cannot fit into type `u128`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:14:13
+   |
+LL |     x_u64 > -1_isize;
+   |             ^^^^^^^^ expected `u64`, found `isize`
+   |
+   = note: `-1_isize` cannot fit into type `u64`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:16:13
+   |
+LL |     x_u32 > -1_isize;
+   |             ^^^^^^^^ expected `u32`, found `isize`
+   |
+   = note: `-1_isize` cannot fit into type `u32`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:18:13
+   |
+LL |     x_u16 > -1_isize;
+   |             ^^^^^^^^ expected `u16`, found `isize`
+   |
+   = note: `-1_isize` cannot fit into type `u16`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:20:12
+   |
+LL |     x_u8 > -1_isize;
+   |            ^^^^^^^^ expected `u8`, found `isize`
+   |
+help: you can convert `x_u8` from `u8` to `isize`, matching the type of `-1_isize`
+   |
+LL |     isize::from(x_u8) > -1_isize;
+   |     ^^^^^^^^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:23:15
+   |
+LL |     x_usize > -1_i128;
+   |               ^^^^^^^ expected `usize`, found `i128`
+   |
+   = note: `-1_i128` cannot fit into type `usize`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:25:14
+   |
+LL |     x_u128 > -1_i128;
+   |              ^^^^^^^ expected `u128`, found `i128`
+   |
+   = note: `-1_i128` cannot fit into type `u128`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:27:13
+   |
+LL |     x_u64 > -1_i128;
+   |             ^^^^^^^ expected `u64`, found `i128`
+   |
+help: you can convert `x_u64` from `u64` to `i128`, matching the type of `-1_i128`
+   |
+LL |     i128::from(x_u64) > -1_i128;
+   |     ^^^^^^^^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:29:13
+   |
+LL |     x_u32 > -1_i128;
+   |             ^^^^^^^ expected `u32`, found `i128`
+   |
+help: you can convert `x_u32` from `u32` to `i128`, matching the type of `-1_i128`
+   |
+LL |     i128::from(x_u32) > -1_i128;
+   |     ^^^^^^^^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:31:13
+   |
+LL |     x_u16 > -1_i128;
+   |             ^^^^^^^ expected `u16`, found `i128`
+   |
+help: you can convert `x_u16` from `u16` to `i128`, matching the type of `-1_i128`
+   |
+LL |     i128::from(x_u16) > -1_i128;
+   |     ^^^^^^^^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:33:12
+   |
+LL |     x_u8 > -1_i128;
+   |            ^^^^^^^ expected `u8`, found `i128`
+   |
+help: you can convert `x_u8` from `u8` to `i128`, matching the type of `-1_i128`
+   |
+LL |     i128::from(x_u8) > -1_i128;
+   |     ^^^^^^^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:36:15
+   |
+LL |     x_usize > -1_i64;
+   |               ^^^^^^ expected `usize`, found `i64`
+   |
+   = note: `-1_i64` cannot fit into type `usize`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:38:14
+   |
+LL |     x_u128 > -1_i64;
+   |              ^^^^^^ expected `u128`, found `i64`
+   |
+   = note: `-1_i64` cannot fit into type `u128`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:40:13
+   |
+LL |     x_u64 > -1_i64;
+   |             ^^^^^^ expected `u64`, found `i64`
+   |
+   = note: `-1_i64` cannot fit into type `u64`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:42:13
+   |
+LL |     x_u32 > -1_i64;
+   |             ^^^^^^ expected `u32`, found `i64`
+   |
+help: you can convert `x_u32` from `u32` to `i64`, matching the type of `-1_i64`
+   |
+LL |     i64::from(x_u32) > -1_i64;
+   |     ^^^^^^^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:44:13
+   |
+LL |     x_u16 > -1_i64;
+   |             ^^^^^^ expected `u16`, found `i64`
+   |
+help: you can convert `x_u16` from `u16` to `i64`, matching the type of `-1_i64`
+   |
+LL |     i64::from(x_u16) > -1_i64;
+   |     ^^^^^^^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:46:12
+   |
+LL |     x_u8 > -1_i64;
+   |            ^^^^^^ expected `u8`, found `i64`
+   |
+help: you can convert `x_u8` from `u8` to `i64`, matching the type of `-1_i64`
+   |
+LL |     i64::from(x_u8) > -1_i64;
+   |     ^^^^^^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:49:15
+   |
+LL |     x_usize > -1_i32;
+   |               ^^^^^^ expected `usize`, found `i32`
+   |
+   = note: `-1_i32` cannot fit into type `usize`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:51:14
+   |
+LL |     x_u128 > -1_i32;
+   |              ^^^^^^ expected `u128`, found `i32`
+   |
+   = note: `-1_i32` cannot fit into type `u128`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:53:13
+   |
+LL |     x_u64 > -1_i32;
+   |             ^^^^^^ expected `u64`, found `i32`
+   |
+   = note: `-1_i32` cannot fit into type `u64`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:55:13
+   |
+LL |     x_u32 > -1_i32;
+   |             ^^^^^^ expected `u32`, found `i32`
+   |
+   = note: `-1_i32` cannot fit into type `u32`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:57:13
+   |
+LL |     x_u16 > -1_i32;
+   |             ^^^^^^ expected `u16`, found `i32`
+   |
+help: you can convert `x_u16` from `u16` to `i32`, matching the type of `-1_i32`
+   |
+LL |     i32::from(x_u16) > -1_i32;
+   |     ^^^^^^^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:59:12
+   |
+LL |     x_u8 > -1_i32;
+   |            ^^^^^^ expected `u8`, found `i32`
+   |
+help: you can convert `x_u8` from `u8` to `i32`, matching the type of `-1_i32`
+   |
+LL |     i32::from(x_u8) > -1_i32;
+   |     ^^^^^^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:62:15
+   |
+LL |     x_usize > -1_i16;
+   |               ^^^^^^ expected `usize`, found `i16`
+   |
+   = note: `-1_i16` cannot fit into type `usize`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:64:14
+   |
+LL |     x_u128 > -1_i16;
+   |              ^^^^^^ expected `u128`, found `i16`
+   |
+   = note: `-1_i16` cannot fit into type `u128`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:66:13
+   |
+LL |     x_u64 > -1_i16;
+   |             ^^^^^^ expected `u64`, found `i16`
+   |
+   = note: `-1_i16` cannot fit into type `u64`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:68:13
+   |
+LL |     x_u32 > -1_i16;
+   |             ^^^^^^ expected `u32`, found `i16`
+   |
+   = note: `-1_i16` cannot fit into type `u32`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:70:13
+   |
+LL |     x_u16 > -1_i16;
+   |             ^^^^^^ expected `u16`, found `i16`
+   |
+   = note: `-1_i16` cannot fit into type `u16`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:72:12
+   |
+LL |     x_u8 > -1_i16;
+   |            ^^^^^^ expected `u8`, found `i16`
+   |
+help: you can convert `x_u8` from `u8` to `i16`, matching the type of `-1_i16`
+   |
+LL |     i16::from(x_u8) > -1_i16;
+   |     ^^^^^^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:75:15
+   |
+LL |     x_usize > -1_i8;
+   |               ^^^^^ expected `usize`, found `i8`
+   |
+   = note: `-1_i8` cannot fit into type `usize`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:77:14
+   |
+LL |     x_u128 > -1_i8;
+   |              ^^^^^ expected `u128`, found `i8`
+   |
+   = note: `-1_i8` cannot fit into type `u128`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:79:13
+   |
+LL |     x_u64 > -1_i8;
+   |             ^^^^^ expected `u64`, found `i8`
+   |
+   = note: `-1_i8` cannot fit into type `u64`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:81:13
+   |
+LL |     x_u32 > -1_i8;
+   |             ^^^^^ expected `u32`, found `i8`
+   |
+   = note: `-1_i8` cannot fit into type `u32`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:83:13
+   |
+LL |     x_u16 > -1_i8;
+   |             ^^^^^ expected `u16`, found `i8`
+   |
+   = note: `-1_i8` cannot fit into type `u16`
+
+error[E0308]: mismatched types
+  --> $DIR/numeric-cast-no-fix.rs:85:12
+   |
+LL |     x_u8 > -1_i8;
+   |            ^^^^^ expected `u8`, found `i8`
+   |
+   = note: `-1_i8` cannot fit into type `u8`
+
+error: aborting due to 36 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/repeat_count.stderr b/src/test/ui/repeat_count.stderr
index 6a081e23d9d..2eab3ebc768 100644
--- a/src/test/ui/repeat_count.stderr
+++ b/src/test/ui/repeat_count.stderr
@@ -39,12 +39,16 @@ error[E0308]: mismatched types
    |
 LL |     let f = [0; -4_isize];
    |                 ^^^^^^^^ expected `usize`, found `isize`
+   |
+   = note: `-4_isize` cannot fit into type `usize`
 
 error[E0308]: mismatched types
   --> $DIR/repeat_count.rs:22:23
    |
 LL |     let f = [0_usize; -1_isize];
    |                       ^^^^^^^^ expected `usize`, found `isize`
+   |
+   = note: `-1_isize` cannot fit into type `usize`
 
 error[E0308]: mismatched types
   --> $DIR/repeat_count.rs:25:17