Skip to content

Commit 9ca8117

Browse files
authored
Rollup merge of #73361 - estebank:non-primitive-cast, r=davidtwco
Tweak "non-primitive cast" error - Suggest borrowing expression if it would allow cast to work. - Suggest using `<Type>::from(<expr>)` when appropriate. - Minor tweak to `;` typo suggestion. Partily address #47136.
2 parents f4b5f58 + e857696 commit 9ca8117

35 files changed

+204
-121
lines changed

src/libcore/convert/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ pub trait Into<T>: Sized {
374374
/// [`Into`]: trait.Into.html
375375
/// [`from`]: trait.From.html#tymethod.from
376376
/// [book]: ../../book/ch09-00-error-handling.html
377+
#[rustc_diagnostic_item = "from_trait"]
377378
#[stable(feature = "rust1", since = "1.0.0")]
378379
#[rustc_on_unimplemented(on(
379380
all(_Self = "&str", T = "std::string::String"),

src/librustc_parse/parser/diagnostics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -961,7 +961,7 @@ impl<'a> Parser<'a> {
961961
self.bump();
962962
let sp = self.prev_token.span;
963963
self.struct_span_err(sp, &msg)
964-
.span_suggestion(sp, "change this to `;`", ";".to_string(), appl)
964+
.span_suggestion_short(sp, "change this to `;`", ";".to_string(), appl)
965965
.emit();
966966
return Ok(());
967967
} else if self.look_ahead(0, |t| {

src/librustc_span/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ symbols! {
345345
from_method,
346346
from_ok,
347347
from_usize,
348+
from_trait,
348349
fundamental,
349350
future,
350351
Future,

src/librustc_typeck/check/cast.rs

+88-19
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ use rustc_middle::ty::subst::SubstsRef;
4343
use rustc_middle::ty::{self, Ty, TypeAndMut, TypeFoldable};
4444
use rustc_session::lint;
4545
use rustc_session::Session;
46+
use rustc_span::symbol::sym;
4647
use rustc_span::Span;
4748
use rustc_trait_selection::traits;
4849
use rustc_trait_selection::traits::error_reporting::report_object_safety_error;
@@ -333,23 +334,87 @@ impl<'a, 'tcx> CastCheck<'tcx> {
333334
"only `u8` can be cast as `char`, not `{}`",
334335
self.expr_ty
335336
)
337+
.span_label(self.span, "invalid cast")
336338
.emit();
337339
}
338340
CastError::NonScalar => {
339-
type_error_struct!(
341+
let mut err = type_error_struct!(
340342
fcx.tcx.sess,
341343
self.span,
342344
self.expr_ty,
343345
E0605,
344346
"non-primitive cast: `{}` as `{}`",
345347
self.expr_ty,
346348
fcx.ty_to_string(self.cast_ty)
347-
)
348-
.note(
349-
"an `as` expression can only be used to convert between \
350-
primitive types. Consider using the `From` trait",
351-
)
352-
.emit();
349+
);
350+
let mut sugg = None;
351+
if let ty::Ref(reg, _, mutbl) = self.cast_ty.kind {
352+
if fcx
353+
.try_coerce(
354+
self.expr,
355+
fcx.tcx.mk_ref(reg, TypeAndMut { ty: self.expr_ty, mutbl }),
356+
self.cast_ty,
357+
AllowTwoPhase::No,
358+
)
359+
.is_ok()
360+
{
361+
sugg = Some(format!("&{}", mutbl.prefix_str()));
362+
}
363+
}
364+
if let Some(sugg) = sugg {
365+
err.span_label(self.span, "invalid cast");
366+
err.span_suggestion_verbose(
367+
self.expr.span.shrink_to_lo(),
368+
"borrow the value for the cast to be valid",
369+
sugg,
370+
Applicability::MachineApplicable,
371+
);
372+
} else if !matches!(
373+
self.cast_ty.kind,
374+
ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..)
375+
) {
376+
let mut label = true;
377+
// Check `impl From<self.expr_ty> for self.cast_ty {}` for accurate suggestion:
378+
if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) {
379+
if let Some(from_trait) = fcx.tcx.get_diagnostic_item(sym::from_trait) {
380+
let ty = fcx.resolve_vars_if_possible(&self.cast_ty);
381+
// Erase regions to avoid panic in `prove_value` when calling
382+
// `type_implements_trait`.
383+
let ty = fcx.tcx.erase_regions(&ty);
384+
let expr_ty = fcx.resolve_vars_if_possible(&self.expr_ty);
385+
let expr_ty = fcx.tcx.erase_regions(&expr_ty);
386+
let ty_params = fcx.tcx.mk_substs_trait(expr_ty, &[]);
387+
// Check for infer types because cases like `Option<{integer}>` would
388+
// panic otherwise.
389+
if !expr_ty.has_infer_types()
390+
&& fcx.tcx.type_implements_trait((
391+
from_trait,
392+
ty,
393+
ty_params,
394+
fcx.param_env,
395+
))
396+
{
397+
label = false;
398+
err.span_suggestion(
399+
self.span,
400+
"consider using the `From` trait instead",
401+
format!("{}::from({})", self.cast_ty, snippet),
402+
Applicability::MaybeIncorrect,
403+
);
404+
}
405+
}
406+
}
407+
let msg = "an `as` expression can only be used to convert between primitive \
408+
types or to coerce to a specific trait object";
409+
if label {
410+
err.span_label(self.span, msg);
411+
} else {
412+
err.note(msg);
413+
}
414+
} else {
415+
err.span_label(self.span, "invalid cast");
416+
}
417+
err.emit();
353418
}
354419
CastError::SizedUnsizedCast => {
355420
use crate::structured_errors::{SizedUnsizedCastError, StructuredDiagnostic};
@@ -370,21 +435,22 @@ impl<'a, 'tcx> CastCheck<'tcx> {
370435
};
371436
let mut err = struct_span_err!(
372437
fcx.tcx.sess,
373-
self.span,
438+
if unknown_cast_to { self.cast_span } else { self.span },
374439
E0641,
375440
"cannot cast {} a pointer of an unknown kind",
376441
if unknown_cast_to { "to" } else { "from" }
377442
);
378-
err.note(
379-
"the type information given here is insufficient to check whether \
380-
the pointer cast is valid",
381-
);
382443
if unknown_cast_to {
383-
err.span_suggestion_short(
384-
self.cast_span,
385-
"consider giving more type information",
386-
String::new(),
387-
Applicability::Unspecified,
444+
err.span_label(self.cast_span, "needs more type information");
445+
err.note(
446+
"the type information given here is insufficient to check whether \
447+
the pointer cast is valid",
448+
);
449+
} else {
450+
err.span_label(
451+
self.span,
452+
"the type information given here is insufficient to check whether \
453+
the pointer cast is valid",
388454
);
389455
}
390456
err.emit();
@@ -438,13 +504,16 @@ impl<'a, 'tcx> CastCheck<'tcx> {
438504
Ok(s) => {
439505
err.span_suggestion(
440506
self.cast_span,
441-
"try casting to a `Box` instead",
507+
"you can cast to a `Box` instead",
442508
format!("Box<{}>", s),
443509
Applicability::MachineApplicable,
444510
);
445511
}
446512
Err(_) => {
447-
err.span_help(self.cast_span, &format!("did you mean `Box<{}>`?", tstr));
513+
err.span_help(
514+
self.cast_span,
515+
&format!("you might have meant `Box<{}>`", tstr),
516+
);
448517
}
449518
}
450519
}

src/test/ui/cast/cast-from-nil.stderr

+1-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `()` as `u32`
22
--> $DIR/cast-from-nil.rs:2:21
33
|
44
LL | fn main() { let u = (assert!(true) as u32); }
5-
| ^^^^^^^^^^^^^^^^^^^^^^
6-
|
7-
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
5+
| ^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
86

97
error: aborting due to previous error
108

src/test/ui/cast/cast-to-bare-fn.stderr

+2-6
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,13 @@ error[E0605]: non-primitive cast: `fn(isize) {foo}` as `extern "C" fn() -> isize
22
--> $DIR/cast-to-bare-fn.rs:5:13
33
|
44
LL | let x = foo as extern "C" fn() -> isize;
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6-
|
7-
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast
86

97
error[E0605]: non-primitive cast: `u64` as `fn(isize) -> (isize, isize)`
108
--> $DIR/cast-to-bare-fn.rs:7:13
119
|
1210
LL | let y = v as extern "Rust" fn(isize) -> (isize, isize);
13-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14-
|
15-
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast
1612

1713
error: aborting due to 2 previous errors
1814

src/test/ui/cast/cast-to-nil.stderr

+1-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `u32` as `()`
22
--> $DIR/cast-to-nil.rs:2:21
33
|
44
LL | fn main() { let u = 0u32 as (); }
5-
| ^^^^^^^^^^
6-
|
7-
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
5+
| ^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
86

97
error: aborting due to previous error
108

src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ error[E0620]: cast to unsized type: `std::boxed::Box<{integer}>` as `dyn std::ma
1212
LL | Box::new(1) as dyn Send;
1313
| ^^^^^^^^^^^^^^^--------
1414
| |
15-
| help: try casting to a `Box` instead: `Box<dyn Send>`
15+
| help: you can cast to a `Box` instead: `Box<dyn Send>`
1616

1717
error: aborting due to 2 previous errors
1818

src/test/ui/closures/closure-no-fn-3.stderr

+1-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `[closure@$DIR/closure-no-fn-3.rs:6:27: 6:37 b
22
--> $DIR/closure-no-fn-3.rs:6:27
33
|
44
LL | let baz: fn() -> u8 = (|| { b }) as fn() -> u8;
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^
6-
|
7-
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast
86

97
error: aborting due to previous error
108

src/test/ui/coercion/coerce-to-bang-cast.stderr

+2-6
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,13 @@ error[E0605]: non-primitive cast: `i32` as `!`
22
--> $DIR/coerce-to-bang-cast.rs:6:13
33
|
44
LL | let y = {return; 22} as !;
5-
| ^^^^^^^^^^^^^^^^^
6-
|
7-
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
5+
| ^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
86

97
error[E0605]: non-primitive cast: `i32` as `!`
108
--> $DIR/coerce-to-bang-cast.rs:11:13
119
|
1210
LL | let y = 22 as !;
13-
| ^^^^^^^
14-
|
15-
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
11+
| ^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
1612

1713
error: aborting due to 2 previous errors
1814

src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ error[E0604]: only `u8` can be cast as `char`, not `i8`
1616
--> $DIR/const-eval-overflow-4b.rs:25:13
1717
|
1818
LL | : [u32; 5i8 as char as usize]
19-
| ^^^^^^^^^^^
19+
| ^^^^^^^^^^^ invalid cast
2020

2121
error: aborting due to 3 previous errors
2222

src/test/ui/error-codes/E0604.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0604]: only `u8` can be cast as `char`, not `u32`
22
--> $DIR/E0604.rs:2:5
33
|
44
LL | 1u32 as char;
5-
| ^^^^^^^^^^^^
5+
| ^^^^^^^^^^^^ invalid cast
66

77
error: aborting due to previous error
88

src/test/ui/error-codes/E0605.stderr

+2-6
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,13 @@ error[E0605]: non-primitive cast: `u8` as `std::vec::Vec<u8>`
22
--> $DIR/E0605.rs:3:5
33
|
44
LL | x as Vec<u8>;
5-
| ^^^^^^^^^^^^
6-
|
7-
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
5+
| ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
86

97
error[E0605]: non-primitive cast: `*const u8` as `&u8`
108
--> $DIR/E0605.rs:6:5
119
|
1210
LL | v as &u8;
13-
| ^^^^^^^^
14-
|
15-
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
11+
| ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
1612

1713
error: aborting due to 2 previous errors
1814

src/test/ui/error-festival.stderr

+2-4
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,13 @@ error[E0604]: only `u8` can be cast as `char`, not `u32`
4242
--> $DIR/error-festival.rs:25:5
4343
|
4444
LL | 0u32 as char;
45-
| ^^^^^^^^^^^^
45+
| ^^^^^^^^^^^^ invalid cast
4646

4747
error[E0605]: non-primitive cast: `u8` as `std::vec::Vec<u8>`
4848
--> $DIR/error-festival.rs:29:5
4949
|
5050
LL | x as Vec<u8>;
51-
| ^^^^^^^^^^^^
52-
|
53-
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
51+
| ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
5452

5553
error[E0054]: cannot cast as `bool`
5654
--> $DIR/error-festival.rs:33:24

src/test/ui/fat-ptr-cast.stderr

+1-3
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ error[E0605]: non-primitive cast: `std::boxed::Box<[i32]>` as `usize`
3434
--> $DIR/fat-ptr-cast.rs:14:5
3535
|
3636
LL | b as usize;
37-
| ^^^^^^^^^^
38-
|
39-
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
37+
| ^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
4038

4139
error[E0606]: casting `*const [i32]` as `usize` is invalid
4240
--> $DIR/fat-ptr-cast.rs:15:5

src/test/ui/issues/issue-10991.stderr

+1-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `()` as `usize`
22
--> $DIR/issue-10991.rs:3:14
33
|
44
LL | let _t = nil as usize;
5-
| ^^^^^^^^^^^^
6-
|
7-
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
5+
| ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
86

97
error: aborting due to previous error
108

src/test/ui/issues/issue-16048.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ impl<'a> Test<'a> for Foo<'a> {
1818
}
1919

2020
impl<'a> NoLifetime for Foo<'a> {
21-
fn get<'p, T : Test<'a>>(&self) -> T {
21+
fn get<'p, T: Test<'a> + From<Foo<'a>>>(&self) -> T {
2222
//~^ ERROR E0195
2323
//~| NOTE lifetimes do not match method in trait
2424
return *self as T;
2525
//~^ ERROR non-primitive cast: `Foo<'a>` as `T`
26-
//~| NOTE an `as` expression can only be used to convert between primitive types.
26+
//~| NOTE an `as` expression can only be used to convert between primitive types
2727
}
2828
}
2929

src/test/ui/issues/issue-16048.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@ error[E0195]: lifetime parameters or bounds on method `get` do not match the tra
44
LL | fn get<'p, T : Test<'p>>(&self) -> T;
55
| ------------------ lifetimes in impl do not match this method in trait
66
...
7-
LL | fn get<'p, T : Test<'a>>(&self) -> T {
8-
| ^^^^^^^^^^^^^^^^^^ lifetimes do not match method in trait
7+
LL | fn get<'p, T: Test<'a> + From<Foo<'a>>>(&self) -> T {
8+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetimes do not match method in trait
99

1010
error[E0605]: non-primitive cast: `Foo<'a>` as `T`
1111
--> $DIR/issue-16048.rs:24:16
1212
|
1313
LL | return *self as T;
14-
| ^^^^^^^^^^
14+
| ^^^^^^^^^^ help: consider using the `From` trait instead: `T::from(*self)`
1515
|
16-
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
16+
= note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
1717

1818
error: aborting due to 2 previous errors
1919

src/test/ui/issues/issue-17441.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ error[E0620]: cast to unsized type: `std::boxed::Box<usize>` as `dyn std::fmt::D
1616
LL | let _bar = Box::new(1_usize) as dyn std::fmt::Debug;
1717
| ^^^^^^^^^^^^^^^^^^^^^-------------------
1818
| |
19-
| help: try casting to a `Box` instead: `Box<dyn std::fmt::Debug>`
19+
| help: you can cast to a `Box` instead: `Box<dyn std::fmt::Debug>`
2020

2121
error[E0620]: cast to unsized type: `usize` as `dyn std::fmt::Debug`
2222
--> $DIR/issue-17441.rs:8:16

src/test/ui/issues/issue-22289.stderr

+5-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ error[E0605]: non-primitive cast: `i32` as `&(dyn std::any::Any + 'static)`
22
--> $DIR/issue-22289.rs:2:5
33
|
44
LL | 0 as &dyn std::any::Any;
5-
| ^^^^^^^^^^^^^^^^^^^^^^^
5+
| ^^^^^^^^^^^^^^^^^^^^^^^ invalid cast
66
|
7-
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
7+
help: borrow the value for the cast to be valid
8+
|
9+
LL | &0 as &dyn std::any::Any;
10+
| ^
811

912
error: aborting due to previous error
1013

0 commit comments

Comments
 (0)