Skip to content

Commit afbbd38

Browse files
committed
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
1 parent 06e4768 commit afbbd38

File tree

4 files changed

+121
-1
lines changed

4 files changed

+121
-1
lines changed

src/librustc_typeck/check/demand.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
783783
let literal_is_ty_suffixed = |expr: &hir::Expr<'_>| {
784784
if let hir::ExprKind::Lit(lit) = &expr.kind { lit.node.is_suffixed() } else { false }
785785
};
786+
let is_negative_int =
787+
|expr: &hir::Expr<'_>| matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::UnNeg, ..));
788+
let is_uint = |ty: Ty<'_>| matches!(ty.kind, ty::Uint(..));
786789

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

@@ -807,7 +810,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
807810
"you can convert `{}` from `{}` to `{}`, matching the type of `{}`",
808811
lhs_src, expected_ty, checked_ty, src
809812
);
810-
let suggestion = format!("{}::from({})", checked_ty, lhs_src,);
813+
let suggestion = format!("{}::from({})", checked_ty, lhs_src);
811814
(lhs_expr.span, msg, suggestion)
812815
} else {
813816
let msg = format!("{} and panic if the converted value wouldn't fit", msg);
@@ -822,8 +825,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
822825
|err: &mut DiagnosticBuilder<'_>,
823826
found_to_exp_is_fallible: bool,
824827
exp_to_found_is_fallible: bool| {
828+
let always_fallible = found_to_exp_is_fallible
829+
&& (exp_to_found_is_fallible || expected_ty_expr.is_none());
825830
let msg = if literal_is_ty_suffixed(expr) {
826831
&lit_msg
832+
} else if always_fallible && (is_negative_int(expr) && is_uint(expected_ty)) {
833+
// We now know that converting either the lhs or rhs is fallible. Before we
834+
// suggest a fallible conversion, check if the value can never fit in the
835+
// expected type.
836+
let msg = format!("`{}` can never fit into `{}`", src, expected_ty);
837+
err.span_note(expr.span, &msg);
838+
return;
827839
} else if in_const_context {
828840
// Do not recommend `into` or `try_into` in const contexts.
829841
return;
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#[allow(unused_must_use)]
2+
fn main() {
3+
let x_usize: usize = 1;
4+
let x_u128: u128 = 2;
5+
let x_u64: u64 = 3;
6+
let x_u32: u32 = 4;
7+
let x_u16: u16 = 5;
8+
let x_u8: u8 = 6;
9+
10+
x_usize > -1_isize;
11+
//~^ ERROR mismatched types
12+
x_u128 > -1_isize;
13+
//~^ ERROR mismatched types
14+
x_u64 > -1_isize;
15+
//~^ ERROR mismatched types
16+
x_u32 > -1_isize;
17+
//~^ ERROR mismatched types
18+
x_u16 > -1_isize;
19+
//~^ ERROR mismatched types
20+
x_u8 > -1_isize;
21+
//~^ ERROR mismatched types
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/numeric-cast-no-fix.rs:10:15
3+
|
4+
LL | x_usize > -1_isize;
5+
| ^^^^^^^^ expected `usize`, found `isize`
6+
|
7+
note: `-1_isize` can never fit into `usize`
8+
--> $DIR/numeric-cast-no-fix.rs:10:15
9+
|
10+
LL | x_usize > -1_isize;
11+
| ^^^^^^^^
12+
13+
error[E0308]: mismatched types
14+
--> $DIR/numeric-cast-no-fix.rs:12:14
15+
|
16+
LL | x_u128 > -1_isize;
17+
| ^^^^^^^^ expected `u128`, found `isize`
18+
|
19+
note: `-1_isize` can never fit into `u128`
20+
--> $DIR/numeric-cast-no-fix.rs:12:14
21+
|
22+
LL | x_u128 > -1_isize;
23+
| ^^^^^^^^
24+
25+
error[E0308]: mismatched types
26+
--> $DIR/numeric-cast-no-fix.rs:14:13
27+
|
28+
LL | x_u64 > -1_isize;
29+
| ^^^^^^^^ expected `u64`, found `isize`
30+
|
31+
note: `-1_isize` can never fit into `u64`
32+
--> $DIR/numeric-cast-no-fix.rs:14:13
33+
|
34+
LL | x_u64 > -1_isize;
35+
| ^^^^^^^^
36+
37+
error[E0308]: mismatched types
38+
--> $DIR/numeric-cast-no-fix.rs:16:13
39+
|
40+
LL | x_u32 > -1_isize;
41+
| ^^^^^^^^ expected `u32`, found `isize`
42+
|
43+
note: `-1_isize` can never fit into `u32`
44+
--> $DIR/numeric-cast-no-fix.rs:16:13
45+
|
46+
LL | x_u32 > -1_isize;
47+
| ^^^^^^^^
48+
49+
error[E0308]: mismatched types
50+
--> $DIR/numeric-cast-no-fix.rs:18:13
51+
|
52+
LL | x_u16 > -1_isize;
53+
| ^^^^^^^^ expected `u16`, found `isize`
54+
|
55+
note: `-1_isize` can never fit into `u16`
56+
--> $DIR/numeric-cast-no-fix.rs:18:13
57+
|
58+
LL | x_u16 > -1_isize;
59+
| ^^^^^^^^
60+
61+
error[E0308]: mismatched types
62+
--> $DIR/numeric-cast-no-fix.rs:20:12
63+
|
64+
LL | x_u8 > -1_isize;
65+
| ^^^^^^^^ expected `u8`, found `isize`
66+
|
67+
help: you can convert `x_u8` from `u8` to `isize`, matching the type of `-1_isize`
68+
|
69+
LL | isize::from(x_u8) > -1_isize;
70+
| ^^^^^^^^^^^^^^^^^
71+
72+
error: aborting due to 6 previous errors
73+
74+
For more information about this error, try `rustc --explain E0308`.

src/test/ui/repeat_count.stderr

+12
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,24 @@ error[E0308]: mismatched types
3939
|
4040
LL | let f = [0; -4_isize];
4141
| ^^^^^^^^ expected `usize`, found `isize`
42+
|
43+
note: `-4_isize` can never fit into `usize`
44+
--> $DIR/repeat_count.rs:19:17
45+
|
46+
LL | let f = [0; -4_isize];
47+
| ^^^^^^^^
4248

4349
error[E0308]: mismatched types
4450
--> $DIR/repeat_count.rs:22:23
4551
|
4652
LL | let f = [0_usize; -1_isize];
4753
| ^^^^^^^^ expected `usize`, found `isize`
54+
|
55+
note: `-1_isize` can never fit into `usize`
56+
--> $DIR/repeat_count.rs:22:23
57+
|
58+
LL | let f = [0_usize; -1_isize];
59+
| ^^^^^^^^
4860

4961
error[E0308]: mismatched types
5062
--> $DIR/repeat_count.rs:25:17

0 commit comments

Comments
 (0)