improve errors for invalid pointer casts
This commit is contained in:
parent
bd31e3ed70
commit
39f66baa68
@ -32,6 +32,7 @@
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{Applicability, Diag, ErrorGuaranteed};
|
||||
use rustc_hir::{self as hir, ExprKind};
|
||||
use rustc_infer::infer::DefineOpaqueTypes;
|
||||
use rustc_macros::{TypeFoldable, TypeVisitable};
|
||||
use rustc_middle::mir::Mutability;
|
||||
use rustc_middle::ty::adjustment::AllowTwoPhase;
|
||||
@ -152,12 +153,15 @@ fn pointer_kind(
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum CastError {
|
||||
enum CastError<'tcx> {
|
||||
ErrorGuaranteed(ErrorGuaranteed),
|
||||
|
||||
CastToBool,
|
||||
CastToChar,
|
||||
DifferingKinds,
|
||||
DifferingKinds {
|
||||
src_kind: PointerKind<'tcx>,
|
||||
dst_kind: PointerKind<'tcx>,
|
||||
},
|
||||
/// Cast of thin to fat raw ptr (e.g., `*const () as *const [u8]`).
|
||||
SizedUnsizedCast,
|
||||
IllegalCast,
|
||||
@ -177,7 +181,7 @@ pub enum CastError {
|
||||
ForeignNonExhaustiveAdt,
|
||||
}
|
||||
|
||||
impl From<ErrorGuaranteed> for CastError {
|
||||
impl From<ErrorGuaranteed> for CastError<'_> {
|
||||
fn from(err: ErrorGuaranteed) -> Self {
|
||||
CastError::ErrorGuaranteed(err)
|
||||
}
|
||||
@ -251,7 +255,7 @@ pub(crate) fn new(
|
||||
}
|
||||
}
|
||||
|
||||
fn report_cast_error(&self, fcx: &FnCtxt<'a, 'tcx>, e: CastError) {
|
||||
fn report_cast_error(&self, fcx: &FnCtxt<'a, 'tcx>, e: CastError<'tcx>) {
|
||||
match e {
|
||||
CastError::ErrorGuaranteed(_) => {
|
||||
// an error has already been reported
|
||||
@ -303,10 +307,52 @@ fn report_cast_error(&self, fcx: &FnCtxt<'a, 'tcx>, e: CastError) {
|
||||
CastError::IllegalCast => {
|
||||
make_invalid_casting_error(self.span, self.expr_ty, self.cast_ty, fcx).emit();
|
||||
}
|
||||
CastError::DifferingKinds => {
|
||||
make_invalid_casting_error(self.span, self.expr_ty, self.cast_ty, fcx)
|
||||
.with_note("vtable kinds may not match")
|
||||
.emit();
|
||||
CastError::DifferingKinds { src_kind, dst_kind } => {
|
||||
let mut err =
|
||||
make_invalid_casting_error(self.span, self.expr_ty, self.cast_ty, fcx);
|
||||
|
||||
match (src_kind, dst_kind) {
|
||||
(PointerKind::VTable(_), PointerKind::VTable(_)) => {
|
||||
err.note("the trait objects may have different vtables");
|
||||
}
|
||||
(
|
||||
PointerKind::OfParam(_) | PointerKind::OfAlias(_),
|
||||
PointerKind::OfParam(_)
|
||||
| PointerKind::OfAlias(_)
|
||||
| PointerKind::VTable(_)
|
||||
| PointerKind::Length,
|
||||
)
|
||||
| (
|
||||
PointerKind::VTable(_) | PointerKind::Length,
|
||||
PointerKind::OfParam(_) | PointerKind::OfAlias(_),
|
||||
) => {
|
||||
err.note("the pointers may have different metadata");
|
||||
}
|
||||
(PointerKind::VTable(_), PointerKind::Length)
|
||||
| (PointerKind::Length, PointerKind::VTable(_)) => {
|
||||
err.note("the pointers have different metadata");
|
||||
}
|
||||
(
|
||||
PointerKind::Thin,
|
||||
PointerKind::Thin
|
||||
| PointerKind::VTable(_)
|
||||
| PointerKind::Length
|
||||
| PointerKind::OfParam(_)
|
||||
| PointerKind::OfAlias(_),
|
||||
)
|
||||
| (
|
||||
PointerKind::VTable(_)
|
||||
| PointerKind::Length
|
||||
| PointerKind::OfParam(_)
|
||||
| PointerKind::OfAlias(_),
|
||||
PointerKind::Thin,
|
||||
)
|
||||
| (PointerKind::Length, PointerKind::Length) => {
|
||||
span_bug!(self.span, "unexpected cast error: {e:?}")
|
||||
}
|
||||
}
|
||||
|
||||
err.emit();
|
||||
}
|
||||
CastError::CastToBool => {
|
||||
let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty);
|
||||
@ -670,7 +716,7 @@ pub fn check(mut self, fcx: &FnCtxt<'a, 'tcx>) {
|
||||
/// Checks a cast, and report an error if one exists. In some cases, this
|
||||
/// can return Ok and create type errors in the fcx rather than returning
|
||||
/// directly. coercion-cast is handled in check instead of here.
|
||||
fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<CastKind, CastError> {
|
||||
fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<CastKind, CastError<'tcx>> {
|
||||
use rustc_middle::ty::cast::CastTy::*;
|
||||
use rustc_middle::ty::cast::IntTy::*;
|
||||
|
||||
@ -798,27 +844,34 @@ fn check_ptr_ptr_cast(
|
||||
fcx: &FnCtxt<'a, 'tcx>,
|
||||
m_src: ty::TypeAndMut<'tcx>,
|
||||
m_dst: ty::TypeAndMut<'tcx>,
|
||||
) -> Result<CastKind, CastError> {
|
||||
) -> Result<CastKind, CastError<'tcx>> {
|
||||
debug!("check_ptr_ptr_cast m_src={m_src:?} m_dst={m_dst:?}");
|
||||
// ptr-ptr cast. vtables must match.
|
||||
// ptr-ptr cast. metadata must match.
|
||||
|
||||
let src_kind = fcx.tcx.erase_regions(fcx.pointer_kind(m_src.ty, self.span)?);
|
||||
let dst_kind = fcx.tcx.erase_regions(fcx.pointer_kind(m_dst.ty, self.span)?);
|
||||
|
||||
// We can't cast if target pointer kind is unknown
|
||||
let Some(dst_kind) = dst_kind else {
|
||||
return Err(CastError::UnknownCastPtrKind);
|
||||
};
|
||||
|
||||
// Cast to thin pointer is OK
|
||||
if dst_kind == PointerKind::Thin {
|
||||
return Ok(CastKind::PtrPtrCast);
|
||||
}
|
||||
|
||||
// We can't cast to fat pointer if source pointer kind is unknown
|
||||
let Some(src_kind) = src_kind else {
|
||||
return Err(CastError::UnknownCastPtrKind);
|
||||
};
|
||||
|
||||
match (src_kind, dst_kind) {
|
||||
// We can't cast if target pointer kind is unknown
|
||||
(_, None) => Err(CastError::UnknownCastPtrKind),
|
||||
// Cast to thin pointer is OK
|
||||
(_, Some(PointerKind::Thin)) => Ok(CastKind::PtrPtrCast),
|
||||
|
||||
// We can't cast to fat pointer if source pointer kind is unknown
|
||||
(None, _) => Err(CastError::UnknownExprPtrKind),
|
||||
|
||||
// thin -> fat? report invalid cast (don't complain about vtable kinds)
|
||||
(Some(PointerKind::Thin), _) => Err(CastError::SizedUnsizedCast),
|
||||
(PointerKind::Thin, _) => Err(CastError::SizedUnsizedCast),
|
||||
|
||||
// trait object -> trait object? need to do additional checks
|
||||
(Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => {
|
||||
(PointerKind::VTable(src_tty), PointerKind::VTable(dst_tty)) => {
|
||||
match (src_tty.principal(), dst_tty.principal()) {
|
||||
// A<dyn Src<...> + SrcAuto> -> B<dyn Dst<...> + DstAuto>. need to make sure
|
||||
// - `Src` and `Dst` traits are the same
|
||||
@ -834,7 +887,7 @@ fn check_ptr_ptr_cast(
|
||||
// Note that trait upcasting goes through a different mechanism (`coerce_unsized`)
|
||||
// and is unaffected by this check.
|
||||
if src_principal.def_id() != dst_principal.def_id() {
|
||||
return Err(CastError::DifferingKinds);
|
||||
return Err(CastError::DifferingKinds { src_kind, dst_kind });
|
||||
}
|
||||
|
||||
// We need to reconstruct trait object types.
|
||||
@ -860,7 +913,16 @@ fn check_ptr_ptr_cast(
|
||||
));
|
||||
|
||||
// `dyn Src = dyn Dst`, this checks for matching traits/generics
|
||||
fcx.demand_eqtype(self.span, src_obj, dst_obj);
|
||||
// This is `demand_eqtype`, but inlined to give a better error.
|
||||
let cause = fcx.misc(self.span);
|
||||
if fcx
|
||||
.at(&cause, fcx.param_env)
|
||||
.eq(DefineOpaqueTypes::Yes, src_obj, dst_obj)
|
||||
.map(|infer_ok| fcx.register_infer_ok_obligations(infer_ok))
|
||||
.is_err()
|
||||
{
|
||||
return Err(CastError::DifferingKinds { src_kind, dst_kind });
|
||||
}
|
||||
|
||||
// Check that `SrcAuto` (+auto traits implied by `Src`) is a superset of `DstAuto`.
|
||||
// Emit an FCW otherwise.
|
||||
@ -905,17 +967,17 @@ fn check_ptr_ptr_cast(
|
||||
|
||||
// dyn Trait -> dyn Auto? should be ok, but we used to not allow it.
|
||||
// FIXME: allow this
|
||||
(Some(_), None) => Err(CastError::DifferingKinds),
|
||||
(Some(_), None) => Err(CastError::DifferingKinds { src_kind, dst_kind }),
|
||||
|
||||
// dyn Auto -> dyn Trait? not ok.
|
||||
(None, Some(_)) => Err(CastError::DifferingKinds),
|
||||
(None, Some(_)) => Err(CastError::DifferingKinds { src_kind, dst_kind }),
|
||||
}
|
||||
}
|
||||
|
||||
// fat -> fat? metadata kinds must match
|
||||
(Some(src_kind), Some(dst_kind)) if src_kind == dst_kind => Ok(CastKind::PtrPtrCast),
|
||||
(src_kind, dst_kind) if src_kind == dst_kind => Ok(CastKind::PtrPtrCast),
|
||||
|
||||
(_, _) => Err(CastError::DifferingKinds),
|
||||
(_, _) => Err(CastError::DifferingKinds { src_kind, dst_kind }),
|
||||
}
|
||||
}
|
||||
|
||||
@ -923,7 +985,7 @@ fn check_fptr_ptr_cast(
|
||||
&self,
|
||||
fcx: &FnCtxt<'a, 'tcx>,
|
||||
m_cast: ty::TypeAndMut<'tcx>,
|
||||
) -> Result<CastKind, CastError> {
|
||||
) -> Result<CastKind, CastError<'tcx>> {
|
||||
// fptr-ptr cast. must be to thin ptr
|
||||
|
||||
match fcx.pointer_kind(m_cast.ty, self.span)? {
|
||||
@ -937,7 +999,7 @@ fn check_ptr_addr_cast(
|
||||
&self,
|
||||
fcx: &FnCtxt<'a, 'tcx>,
|
||||
m_expr: ty::TypeAndMut<'tcx>,
|
||||
) -> Result<CastKind, CastError> {
|
||||
) -> Result<CastKind, CastError<'tcx>> {
|
||||
// ptr-addr cast. must be from thin ptr
|
||||
|
||||
match fcx.pointer_kind(m_expr.ty, self.span)? {
|
||||
@ -952,7 +1014,7 @@ fn check_ref_cast(
|
||||
fcx: &FnCtxt<'a, 'tcx>,
|
||||
m_expr: ty::TypeAndMut<'tcx>,
|
||||
m_cast: ty::TypeAndMut<'tcx>,
|
||||
) -> Result<CastKind, CastError> {
|
||||
) -> Result<CastKind, CastError<'tcx>> {
|
||||
// array-ptr-cast: allow mut-to-mut, mut-to-const, const-to-const
|
||||
if m_expr.mutbl >= m_cast.mutbl {
|
||||
if let ty::Array(ety, _) = m_expr.ty.kind() {
|
||||
@ -987,7 +1049,7 @@ fn check_addr_ptr_cast(
|
||||
&self,
|
||||
fcx: &FnCtxt<'a, 'tcx>,
|
||||
m_cast: TypeAndMut<'tcx>,
|
||||
) -> Result<CastKind, CastError> {
|
||||
) -> Result<CastKind, CastError<'tcx>> {
|
||||
// ptr-addr cast. pointer must be thin.
|
||||
match fcx.pointer_kind(m_cast.ty, self.span)? {
|
||||
None => Err(CastError::UnknownCastPtrKind),
|
||||
|
@ -4,7 +4,7 @@ error[E0606]: casting `*mut impl Debug + ?Sized` as `*mut impl Debug + ?Sized` i
|
||||
LL | b_raw = f_raw as *mut _;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: vtable kinds may not match
|
||||
= note: the pointers may have different metadata
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
@ -18,14 +18,14 @@ fn main() {
|
||||
let b: *const dyn B = a as _; //~ error: casting `*const dyn A` as `*const dyn B` is invalid
|
||||
|
||||
let x: *const dyn Trait<X> = &();
|
||||
let y: *const dyn Trait<Y> = x as _; //~ error: mismatched types
|
||||
let y: *const dyn Trait<Y> = x as _; //~ error: casting `*const dyn Trait<X>` as `*const dyn Trait<Y>` is invalid
|
||||
|
||||
_ = (b, y);
|
||||
}
|
||||
|
||||
fn generic<T>(x: *const dyn Trait<X>, t: *const dyn Trait<T>) {
|
||||
let _: *const dyn Trait<T> = x as _; //~ error: mismatched types
|
||||
let _: *const dyn Trait<X> = t as _; //~ error: mismatched types
|
||||
let _: *const dyn Trait<T> = x as _; //~ error: casting `*const (dyn Trait<X> + 'static)` as `*const dyn Trait<T>` is invalid
|
||||
let _: *const dyn Trait<X> = t as _; //~ error: casting `*const (dyn Trait<T> + 'static)` as `*const dyn Trait<X>` is invalid
|
||||
}
|
||||
|
||||
trait Assocked {
|
||||
@ -33,5 +33,5 @@ trait Assocked {
|
||||
}
|
||||
|
||||
fn change_assoc(x: *mut dyn Assocked<Assoc = u8>) -> *mut dyn Assocked<Assoc = u32> {
|
||||
x as _ //~ error: mismatched types
|
||||
x as _ //~ error: casting `*mut (dyn Assocked<Assoc = u8> + 'static)` as `*mut (dyn Assocked<Assoc = u32> + 'static)` is invalid
|
||||
}
|
||||
|
@ -4,53 +4,40 @@ error[E0606]: casting `*const dyn A` as `*const dyn B` is invalid
|
||||
LL | let b: *const dyn B = a as _;
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: vtable kinds may not match
|
||||
= note: the trait objects may have different vtables
|
||||
|
||||
error[E0308]: mismatched types
|
||||
error[E0606]: casting `*const dyn Trait<X>` as `*const dyn Trait<Y>` is invalid
|
||||
--> $DIR/ptr-to-trait-obj-different-args.rs:21:34
|
||||
|
|
||||
LL | let y: *const dyn Trait<Y> = x as _;
|
||||
| ^^^^^^ expected `X`, found `Y`
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: expected trait object `dyn Trait<X>`
|
||||
found trait object `dyn Trait<Y>`
|
||||
= help: `dyn Trait<Y>` implements `Trait` so you could box the found value and coerce it to the trait object `Box<dyn Trait>`, you will have to change the expected type as well
|
||||
= note: the trait objects may have different vtables
|
||||
|
||||
error[E0308]: mismatched types
|
||||
error[E0606]: casting `*const (dyn Trait<X> + 'static)` as `*const dyn Trait<T>` is invalid
|
||||
--> $DIR/ptr-to-trait-obj-different-args.rs:27:34
|
||||
|
|
||||
LL | fn generic<T>(x: *const dyn Trait<X>, t: *const dyn Trait<T>) {
|
||||
| - found this type parameter
|
||||
LL | let _: *const dyn Trait<T> = x as _;
|
||||
| ^^^^^^ expected `X`, found type parameter `T`
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: expected trait object `dyn Trait<X>`
|
||||
found trait object `dyn Trait<T>`
|
||||
= help: `dyn Trait<T>` implements `Trait` so you could box the found value and coerce it to the trait object `Box<dyn Trait>`, you will have to change the expected type as well
|
||||
= note: the trait objects may have different vtables
|
||||
|
||||
error[E0308]: mismatched types
|
||||
error[E0606]: casting `*const (dyn Trait<T> + 'static)` as `*const dyn Trait<X>` is invalid
|
||||
--> $DIR/ptr-to-trait-obj-different-args.rs:28:34
|
||||
|
|
||||
LL | fn generic<T>(x: *const dyn Trait<X>, t: *const dyn Trait<T>) {
|
||||
| - expected this type parameter
|
||||
LL | let _: *const dyn Trait<T> = x as _;
|
||||
LL | let _: *const dyn Trait<X> = t as _;
|
||||
| ^^^^^^ expected type parameter `T`, found `X`
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: expected trait object `dyn Trait<T>`
|
||||
found trait object `dyn Trait<X>`
|
||||
= help: `dyn Trait<X>` implements `Trait` so you could box the found value and coerce it to the trait object `Box<dyn Trait>`, you will have to change the expected type as well
|
||||
= note: the trait objects may have different vtables
|
||||
|
||||
error[E0308]: mismatched types
|
||||
error[E0606]: casting `*mut (dyn Assocked<Assoc = u8> + 'static)` as `*mut (dyn Assocked<Assoc = u32> + 'static)` is invalid
|
||||
--> $DIR/ptr-to-trait-obj-different-args.rs:36:5
|
||||
|
|
||||
LL | x as _
|
||||
| ^^^^^^ expected `u8`, found `u32`
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: expected trait object `dyn Assocked<Assoc = u8>`
|
||||
found trait object `dyn Assocked<Assoc = u32>`
|
||||
= note: the trait objects may have different vtables
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0308, E0606.
|
||||
For more information about an error, try `rustc --explain E0308`.
|
||||
For more information about this error, try `rustc --explain E0606`.
|
||||
|
@ -4,7 +4,7 @@ error[E0606]: casting `*const (dyn Sub + 'static)` as `*const Wrapper<dyn Super>
|
||||
LL | ptr as _
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= note: vtable kinds may not match
|
||||
= note: the trait objects may have different vtables
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
@ -4,7 +4,7 @@ error[E0606]: casting `*const U` as `*const V` is invalid
|
||||
LL | u as *const V
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: vtable kinds may not match
|
||||
= note: the pointers may have different metadata
|
||||
|
||||
error[E0606]: casting `*const U` as `*const str` is invalid
|
||||
--> $DIR/cast-rfc0401.rs:8:5
|
||||
@ -12,7 +12,7 @@ error[E0606]: casting `*const U` as `*const str` is invalid
|
||||
LL | u as *const str
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: vtable kinds may not match
|
||||
= note: the pointers may have different metadata
|
||||
|
||||
error[E0609]: no field `f` on type `fn() {main}`
|
||||
--> $DIR/cast-rfc0401.rs:65:18
|
||||
@ -208,7 +208,7 @@ error[E0606]: casting `*const dyn Foo` as `*const [u16]` is invalid
|
||||
LL | let _ = cf as *const [u16];
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: vtable kinds may not match
|
||||
= note: the pointers have different metadata
|
||||
|
||||
error[E0606]: casting `*const dyn Foo` as `*const dyn Bar` is invalid
|
||||
--> $DIR/cast-rfc0401.rs:69:13
|
||||
@ -216,7 +216,7 @@ error[E0606]: casting `*const dyn Foo` as `*const dyn Bar` is invalid
|
||||
LL | let _ = cf as *const dyn Bar;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: vtable kinds may not match
|
||||
= note: the trait objects may have different vtables
|
||||
|
||||
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
|
||||
--> $DIR/cast-rfc0401.rs:53:13
|
||||
|
@ -57,7 +57,7 @@ pub fn user2() -> &'static dyn Trait<u8, u16> {
|
||||
fn main() {
|
||||
let p: *const dyn Trait<u8, u8> = &();
|
||||
let p = p as *const dyn Trait<u8, u16>; // <- this is bad!
|
||||
//~^ error: mismatched types
|
||||
//~^ error: casting `*const dyn Trait<u8, u8>` as `*const dyn Trait<u8, u16>` is invalid
|
||||
let p = p as *const dyn Super<u16>; // <- this upcast accesses improper vtable entry
|
||||
// accessing from L__unnamed_2 the position for the 'Super<u16> vtable (pointer)',
|
||||
// thus reading 'null pointer for missing_method'
|
||||
|
@ -1,13 +1,11 @@
|
||||
error[E0308]: mismatched types
|
||||
error[E0606]: casting `*const dyn Trait<u8, u8>` as `*const dyn Trait<u8, u16>` is invalid
|
||||
--> $DIR/upcast_soundness_bug.rs:59:13
|
||||
|
|
||||
LL | let p = p as *const dyn Trait<u8, u16>; // <- this is bad!
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `u16`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: expected trait object `dyn Trait<u8, u8>`
|
||||
found trait object `dyn Trait<u8, u16>`
|
||||
= help: `dyn Trait<u8, u16>` implements `Trait` so you could box the found value and coerce it to the trait object `Box<dyn Trait>`, you will have to change the expected type as well
|
||||
= note: the trait objects may have different vtables
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
||||
For more information about this error, try `rustc --explain E0606`.
|
||||
|
Loading…
Reference in New Issue
Block a user