Skip to content

Commit

Permalink
Rollup merge of #92150 - compiler-errors:better_usize_to_wide_ptr_cas…
Browse files Browse the repository at this point in the history
…t, r=petrochenkov

Improve suggestion when casting usize to (possibly) wide pointer

I thought #92125 was a wonderful idea, so I went ahead and took a stab at it. Not sure if my approach is the best going forward, but I'm happy with the improvement in the error message.

Iwill definitely address any changes if people are more opinionated with the wordings or want more features.

Also, do I need to add a new error code?

(Fixes #92125)
  • Loading branch information
matthiaskrgr authored Mar 10, 2022
2 parents d7b282b + 814c18a commit 7473750
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 14 deletions.
43 changes: 42 additions & 1 deletion compiler/rustc_typeck/src/check/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,12 @@ pub enum CastError {
NonScalar,
UnknownExprPtrKind,
UnknownCastPtrKind,
/// Cast of int to (possibly) fat raw pointer.
///
/// Argument is the specific name of the metadata in plain words, such as "a vtable"
/// or "a length". If this argument is None, then the metadata is unknown, for example,
/// when we're typechecking a type parameter with a ?Sized bound.
IntToFatCast(Option<&'static str>),
}

impl From<ErrorGuaranteed> for CastError {
Expand Down Expand Up @@ -522,6 +528,35 @@ impl<'a, 'tcx> CastCheck<'tcx> {
.diagnostic()
.emit();
}
CastError::IntToFatCast(known_metadata) => {
let mut err = struct_span_err!(
fcx.tcx.sess,
self.cast_span,
E0606,
"cannot cast `{}` to a pointer that {} wide",
fcx.ty_to_string(self.expr_ty),
if known_metadata.is_some() { "is" } else { "may be" }
);

err.span_label(
self.cast_span,
format!(
"creating a `{}` requires both an address and {}",
self.cast_ty,
known_metadata.unwrap_or("type-specific metadata"),
),
);

if fcx.tcx.sess.is_nightly_build() {
err.span_label(
self.expr.span,
"consider casting this expression to `*const ()`, \
then using `core::ptr::from_raw_parts`",
);
}

err.emit();
}
CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => {
let unknown_cast_to = match e {
CastError::UnknownCastPtrKind => true,
Expand Down Expand Up @@ -900,7 +935,13 @@ impl<'a, 'tcx> CastCheck<'tcx> {
match fcx.pointer_kind(m_cast.ty, self.span)? {
None => Err(CastError::UnknownCastPtrKind),
Some(PointerKind::Thin) => Ok(CastKind::AddrPtrCast),
_ => Err(CastError::IllegalCast),
Some(PointerKind::Vtable(_)) => Err(CastError::IntToFatCast(Some("a vtable"))),
Some(PointerKind::Length) => Err(CastError::IntToFatCast(Some("a length"))),
Some(
PointerKind::OfProjection(_)
| PointerKind::OfOpaque(_, _)
| PointerKind::OfParam(_),
) => Err(CastError::IntToFatCast(None)),
}
}

Expand Down
13 changes: 11 additions & 2 deletions src/test/ui/cast/fat-ptr-cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ fn main() {
q as *const [i32]; //~ ERROR cannot cast

// #21397
let t: *mut (dyn Trait + 'static) = 0 as *mut _; //~ ERROR casting
let mut fail: *const str = 0 as *const str; //~ ERROR casting
let t: *mut (dyn Trait + 'static) = 0 as *mut _;
//~^ ERROR cannot cast `usize` to a pointer that is wide
let mut fail: *const str = 0 as *const str;
//~^ ERROR cannot cast `usize` to a pointer that is wide
let mut fail2: *const str = 0isize as *const str;
//~^ ERROR cannot cast `isize` to a pointer that is wide
}

fn foo<T: ?Sized>() {
let s = 0 as *const T;
//~^ ERROR cannot cast `usize` to a pointer that may be wide
}
34 changes: 27 additions & 7 deletions src/test/ui/cast/fat-ptr-cast.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,39 @@ error[E0607]: cannot cast thin pointer `*const i32` to fat pointer `*const [i32]
LL | q as *const [i32];
| ^^^^^^^^^^^^^^^^^

error[E0606]: casting `usize` as `*mut (dyn Trait + 'static)` is invalid
--> $DIR/fat-ptr-cast.rs:22:41
error[E0606]: cannot cast `usize` to a pointer that is wide
--> $DIR/fat-ptr-cast.rs:22:46
|
LL | let t: *mut (dyn Trait + 'static) = 0 as *mut _;
| ^^^^^^^^^^^
| - ^^^^^^ creating a `*mut (dyn Trait + 'static)` requires both an address and a vtable
| |
| consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts`

error[E0606]: casting `usize` as `*const str` is invalid
--> $DIR/fat-ptr-cast.rs:23:32
error[E0606]: cannot cast `usize` to a pointer that is wide
--> $DIR/fat-ptr-cast.rs:24:37
|
LL | let mut fail: *const str = 0 as *const str;
| ^^^^^^^^^^^^^^^
| - ^^^^^^^^^^ creating a `*const str` requires both an address and a length
| |
| consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts`

error: aborting due to 9 previous errors
error[E0606]: cannot cast `isize` to a pointer that is wide
--> $DIR/fat-ptr-cast.rs:26:43
|
LL | let mut fail2: *const str = 0isize as *const str;
| ------ ^^^^^^^^^^ creating a `*const str` requires both an address and a length
| |
| consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts`

error[E0606]: cannot cast `usize` to a pointer that may be wide
--> $DIR/fat-ptr-cast.rs:31:18
|
LL | let s = 0 as *const T;
| - ^^^^^^^^ creating a `*const T` requires both an address and type-specific metadata
| |
| consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts`

error: aborting due to 11 previous errors

Some errors have detailed explanations: E0605, E0606, E0607.
For more information about an error, try `rustc --explain E0605`.
2 changes: 1 addition & 1 deletion src/test/ui/mismatched_types/cast-rfc0401.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ fn main()
let _ = E::A as *const u8; //~ ERROR is invalid
let _ = 'a' as *const u8; //~ ERROR is invalid

let _ = 42usize as *const [u8]; //~ ERROR is invalid
let _ = 42usize as *const [u8]; //~ ERROR cannot cast `usize` to a pointer that is wide
let _ = v as *const [u8]; //~ ERROR cannot cast
let _ = fat_v as *const dyn Foo; //~ ERROR the size for values of type
let _ = foo as *const str; //~ ERROR is invalid
Expand Down
8 changes: 5 additions & 3 deletions src/test/ui/mismatched_types/cast-rfc0401.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,13 @@ error[E0606]: casting `char` as `*const u8` is invalid
LL | let _ = 'a' as *const u8;
| ^^^^^^^^^^^^^^^^

error[E0606]: casting `usize` as `*const [u8]` is invalid
--> $DIR/cast-rfc0401.rs:51:13
error[E0606]: cannot cast `usize` to a pointer that is wide
--> $DIR/cast-rfc0401.rs:51:24
|
LL | let _ = 42usize as *const [u8];
| ^^^^^^^^^^^^^^^^^^^^^^
| ------- ^^^^^^^^^^^ creating a `*const [u8]` requires both an address and a length
| |
| consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts`

error[E0607]: cannot cast thin pointer `*const u8` to fat pointer `*const [u8]`
--> $DIR/cast-rfc0401.rs:52:13
Expand Down

0 comments on commit 7473750

Please sign in to comment.