diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index 3d0f76f7a93eb..d3e9820834ab9 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -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 for CastError { @@ -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, @@ -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)), } } diff --git a/src/test/ui/cast/fat-ptr-cast.rs b/src/test/ui/cast/fat-ptr-cast.rs index a0fad583a1645..b5276dc619bfb 100644 --- a/src/test/ui/cast/fat-ptr-cast.rs +++ b/src/test/ui/cast/fat-ptr-cast.rs @@ -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() { + let s = 0 as *const T; + //~^ ERROR cannot cast `usize` to a pointer that may be wide } diff --git a/src/test/ui/cast/fat-ptr-cast.stderr b/src/test/ui/cast/fat-ptr-cast.stderr index 0b0c288fe3b61..18e7b68ff3c22 100644 --- a/src/test/ui/cast/fat-ptr-cast.stderr +++ b/src/test/ui/cast/fat-ptr-cast.stderr @@ -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`. diff --git a/src/test/ui/mismatched_types/cast-rfc0401.rs b/src/test/ui/mismatched_types/cast-rfc0401.rs index b8d12fb9809ce..57222f45947b4 100644 --- a/src/test/ui/mismatched_types/cast-rfc0401.rs +++ b/src/test/ui/mismatched_types/cast-rfc0401.rs @@ -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 diff --git a/src/test/ui/mismatched_types/cast-rfc0401.stderr b/src/test/ui/mismatched_types/cast-rfc0401.stderr index 6dbf24baf2315..5f11e4ded8004 100644 --- a/src/test/ui/mismatched_types/cast-rfc0401.stderr +++ b/src/test/ui/mismatched_types/cast-rfc0401.stderr @@ -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