Skip to content

Commit

Permalink
better suggestion for int to wide ptr cast
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Mar 9, 2022
1 parent 458262b commit 814c18a
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 814c18a

Please sign in to comment.