Skip to content

Commit 6eb6ea9

Browse files
author
Lukas Markeffsky
committed
improve errors for invalid pointer casts
1 parent 3d7f17a commit 6eb6ea9

8 files changed

+95
-75
lines changed

compiler/rustc_hir_typeck/src/cast.rs

+66-31
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,14 @@ use rustc_data_structures::fx::FxHashSet;
3232
use rustc_errors::codes::*;
3333
use rustc_errors::{Applicability, Diag, ErrorGuaranteed};
3434
use rustc_hir::{self as hir, ExprKind};
35+
use rustc_infer::infer::DefineOpaqueTypes;
3536
use rustc_macros::{TypeFoldable, TypeVisitable};
36-
use rustc_middle::bug;
3737
use rustc_middle::mir::Mutability;
3838
use rustc_middle::ty::adjustment::AllowTwoPhase;
3939
use rustc_middle::ty::cast::{CastKind, CastTy};
4040
use rustc_middle::ty::error::TypeError;
4141
use rustc_middle::ty::{self, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, VariantDef};
42+
use rustc_middle::{bug, span_bug};
4243
use rustc_session::lint;
4344
use rustc_span::def_id::LOCAL_CRATE;
4445
use rustc_span::symbol::sym;
@@ -152,12 +153,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
152153
}
153154

154155
#[derive(Copy, Clone, Debug)]
155-
pub enum CastError {
156+
enum CastError<'tcx> {
156157
ErrorGuaranteed(ErrorGuaranteed),
157158

158159
CastToBool,
159160
CastToChar,
160-
DifferingKinds,
161+
DifferingKinds {
162+
src_kind: PointerKind<'tcx>,
163+
dst_kind: PointerKind<'tcx>,
164+
},
161165
/// Cast of thin to fat raw ptr (e.g., `*const () as *const [u8]`).
162166
SizedUnsizedCast,
163167
IllegalCast,
@@ -177,7 +181,7 @@ pub enum CastError {
177181
ForeignNonExhaustiveAdt,
178182
}
179183

180-
impl From<ErrorGuaranteed> for CastError {
184+
impl From<ErrorGuaranteed> for CastError<'_> {
181185
fn from(err: ErrorGuaranteed) -> Self {
182186
CastError::ErrorGuaranteed(err)
183187
}
@@ -251,7 +255,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
251255
}
252256
}
253257

254-
fn report_cast_error(&self, fcx: &FnCtxt<'a, 'tcx>, e: CastError) {
258+
fn report_cast_error(&self, fcx: &FnCtxt<'a, 'tcx>, e: CastError<'tcx>) {
255259
match e {
256260
CastError::ErrorGuaranteed(_) => {
257261
// an error has already been reported
@@ -306,10 +310,26 @@ impl<'a, 'tcx> CastCheck<'tcx> {
306310
CastError::IllegalCast => {
307311
make_invalid_casting_error(self.span, self.expr_ty, self.cast_ty, fcx).emit();
308312
}
309-
CastError::DifferingKinds => {
310-
make_invalid_casting_error(self.span, self.expr_ty, self.cast_ty, fcx)
311-
.with_note("vtable kinds may not match")
312-
.emit();
313+
CastError::DifferingKinds { src_kind, dst_kind } => {
314+
let mut err =
315+
make_invalid_casting_error(self.span, self.expr_ty, self.cast_ty, fcx);
316+
317+
match (src_kind, dst_kind) {
318+
(PointerKind::VTable(_), PointerKind::VTable(_)) => {
319+
err.note("the trait objects may have different vtables");
320+
}
321+
(PointerKind::OfParam(_) | PointerKind::OfAlias(_), _)
322+
| (_, PointerKind::OfParam(_) | PointerKind::OfAlias(_)) => {
323+
err.note("the pointers may have different metadata");
324+
}
325+
(PointerKind::VTable(_), PointerKind::Length)
326+
| (PointerKind::Length, PointerKind::VTable(_)) => {
327+
err.note("the pointers have different metadata");
328+
}
329+
_ => span_bug!(self.span, "unexpected cast error: {e:?}"),
330+
}
331+
332+
err.emit();
313333
}
314334
CastError::CastToBool => {
315335
let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty);
@@ -674,7 +694,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
674694
/// Checks a cast, and report an error if one exists. In some cases, this
675695
/// can return Ok and create type errors in the fcx rather than returning
676696
/// directly. coercion-cast is handled in check instead of here.
677-
fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<CastKind, CastError> {
697+
fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<CastKind, CastError<'tcx>> {
678698
use rustc_middle::ty::cast::CastTy::*;
679699
use rustc_middle::ty::cast::IntTy::*;
680700

@@ -803,27 +823,34 @@ impl<'a, 'tcx> CastCheck<'tcx> {
803823
fcx: &FnCtxt<'a, 'tcx>,
804824
m_src: ty::TypeAndMut<'tcx>,
805825
m_dst: ty::TypeAndMut<'tcx>,
806-
) -> Result<CastKind, CastError> {
826+
) -> Result<CastKind, CastError<'tcx>> {
807827
debug!("check_ptr_ptr_cast m_src={m_src:?} m_dst={m_dst:?}");
808-
// ptr-ptr cast. vtables must match.
828+
// ptr-ptr cast. metadata must match.
809829

810830
let src_kind = fcx.tcx.erase_regions(fcx.pointer_kind(m_src.ty, self.span)?);
811831
let dst_kind = fcx.tcx.erase_regions(fcx.pointer_kind(m_dst.ty, self.span)?);
812832

813-
match (src_kind, dst_kind) {
814-
// We can't cast if target pointer kind is unknown
815-
(_, None) => Err(CastError::UnknownCastPtrKind),
816-
// Cast to thin pointer is OK
817-
(_, Some(PointerKind::Thin)) => Ok(CastKind::PtrPtrCast),
833+
// We can't cast if target pointer kind is unknown
834+
let Some(dst_kind) = dst_kind else {
835+
return Err(CastError::UnknownCastPtrKind);
836+
};
837+
838+
// Cast to thin pointer is OK
839+
if dst_kind == PointerKind::Thin {
840+
return Ok(CastKind::PtrPtrCast);
841+
}
818842

819-
// We can't cast to fat pointer if source pointer kind is unknown
820-
(None, _) => Err(CastError::UnknownExprPtrKind),
843+
// We can't cast to fat pointer if source pointer kind is unknown
844+
let Some(src_kind) = src_kind else {
845+
return Err(CastError::UnknownCastPtrKind);
846+
};
821847

848+
match (src_kind, dst_kind) {
822849
// thin -> fat? report invalid cast (don't complain about vtable kinds)
823-
(Some(PointerKind::Thin), _) => Err(CastError::SizedUnsizedCast),
850+
(PointerKind::Thin, _) => Err(CastError::SizedUnsizedCast),
824851

825852
// trait object -> trait object? need to do additional checks
826-
(Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => {
853+
(PointerKind::VTable(src_tty), PointerKind::VTable(dst_tty)) => {
827854
match (src_tty.principal(), dst_tty.principal()) {
828855
// A<dyn Src<...> + SrcAuto> -> B<dyn Dst<...> + DstAuto>. need to make sure
829856
// - `Src` and `Dst` traits are the same
@@ -839,7 +866,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
839866
// Note that trait upcasting goes through a different mechanism (`coerce_unsized`)
840867
// and is unaffected by this check.
841868
if src_principal.def_id() != dst_principal.def_id() {
842-
return Err(CastError::DifferingKinds);
869+
return Err(CastError::DifferingKinds { src_kind, dst_kind });
843870
}
844871

845872
// We need to reconstruct trait object types.
@@ -865,7 +892,15 @@ impl<'a, 'tcx> CastCheck<'tcx> {
865892
));
866893

867894
// `dyn Src = dyn Dst`, this checks for matching traits/generics
868-
fcx.demand_eqtype(self.span, src_obj, dst_obj);
895+
let cause = fcx.misc(self.span);
896+
if fcx
897+
.at(&cause, fcx.param_env)
898+
.eq(DefineOpaqueTypes::Yes, src_obj, dst_obj)
899+
.map(|infer_ok| fcx.register_infer_ok_obligations(infer_ok))
900+
.is_err()
901+
{
902+
return Err(CastError::DifferingKinds { src_kind, dst_kind });
903+
}
869904

870905
// Check that `SrcAuto` (+auto traits implied by `Src`) is a superset of `DstAuto`.
871906
// Emit an FCW otherwise.
@@ -910,25 +945,25 @@ impl<'a, 'tcx> CastCheck<'tcx> {
910945

911946
// dyn Trait -> dyn Auto? should be ok, but we used to not allow it.
912947
// FIXME: allow this
913-
(Some(_), None) => Err(CastError::DifferingKinds),
948+
(Some(_), None) => Err(CastError::DifferingKinds { src_kind, dst_kind }),
914949

915950
// dyn Auto -> dyn Trait? not ok.
916-
(None, Some(_)) => Err(CastError::DifferingKinds),
951+
(None, Some(_)) => Err(CastError::DifferingKinds { src_kind, dst_kind }),
917952
}
918953
}
919954

920955
// fat -> fat? metadata kinds must match
921-
(Some(src_kind), Some(dst_kind)) if src_kind == dst_kind => Ok(CastKind::PtrPtrCast),
956+
(src_kind, dst_kind) if src_kind == dst_kind => Ok(CastKind::PtrPtrCast),
922957

923-
(_, _) => Err(CastError::DifferingKinds),
958+
(_, _) => Err(CastError::DifferingKinds { src_kind, dst_kind }),
924959
}
925960
}
926961

927962
fn check_fptr_ptr_cast(
928963
&self,
929964
fcx: &FnCtxt<'a, 'tcx>,
930965
m_cast: ty::TypeAndMut<'tcx>,
931-
) -> Result<CastKind, CastError> {
966+
) -> Result<CastKind, CastError<'tcx>> {
932967
// fptr-ptr cast. must be to thin ptr
933968

934969
match fcx.pointer_kind(m_cast.ty, self.span)? {
@@ -942,7 +977,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
942977
&self,
943978
fcx: &FnCtxt<'a, 'tcx>,
944979
m_expr: ty::TypeAndMut<'tcx>,
945-
) -> Result<CastKind, CastError> {
980+
) -> Result<CastKind, CastError<'tcx>> {
946981
// ptr-addr cast. must be from thin ptr
947982

948983
match fcx.pointer_kind(m_expr.ty, self.span)? {
@@ -957,7 +992,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
957992
fcx: &FnCtxt<'a, 'tcx>,
958993
m_expr: ty::TypeAndMut<'tcx>,
959994
m_cast: ty::TypeAndMut<'tcx>,
960-
) -> Result<CastKind, CastError> {
995+
) -> Result<CastKind, CastError<'tcx>> {
961996
// array-ptr-cast: allow mut-to-mut, mut-to-const, const-to-const
962997
if m_expr.mutbl >= m_cast.mutbl {
963998
if let ty::Array(ety, _) = m_expr.ty.kind() {
@@ -992,7 +1027,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
9921027
&self,
9931028
fcx: &FnCtxt<'a, 'tcx>,
9941029
m_cast: TypeAndMut<'tcx>,
995-
) -> Result<CastKind, CastError> {
1030+
) -> Result<CastKind, CastError<'tcx>> {
9961031
// ptr-addr cast. pointer must be thin.
9971032
match fcx.pointer_kind(m_cast.ty, self.span)? {
9981033
None => Err(CastError::UnknownCastPtrKind),

tests/ui/cast/casts-differing-anon.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0606]: casting `*mut impl Debug + ?Sized` as `*mut impl Debug + ?Sized` i
44
LL | b_raw = f_raw as *mut _;
55
| ^^^^^^^^^^^^^^^
66
|
7-
= note: vtable kinds may not match
7+
= note: the pointers may have different metadata
88

99
error: aborting due to 1 previous error
1010

tests/ui/cast/ptr-to-trait-obj-different-args.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,20 @@ fn main() {
1818
let b: *const dyn B = a as _; //~ error: casting `*const dyn A` as `*const dyn B` is invalid
1919

2020
let x: *const dyn Trait<X> = &();
21-
let y: *const dyn Trait<Y> = x as _; //~ error: mismatched types
21+
let y: *const dyn Trait<Y> = x as _; //~ error: casting `*const dyn Trait<X>` as `*const dyn Trait<Y>` is invalid
2222

2323
_ = (b, y);
2424
}
2525

2626
fn generic<T>(x: *const dyn Trait<X>, t: *const dyn Trait<T>) {
27-
let _: *const dyn Trait<T> = x as _; //~ error: mismatched types
28-
let _: *const dyn Trait<X> = t as _; //~ error: mismatched types
27+
let _: *const dyn Trait<T> = x as _; //~ error: casting `*const (dyn Trait<X> + 'static)` as `*const dyn Trait<T>` is invalid
28+
let _: *const dyn Trait<X> = t as _; //~ error: casting `*const (dyn Trait<T> + 'static)` as `*const dyn Trait<X>` is invalid
2929
}
3030

3131
trait Assocked {
3232
type Assoc: ?Sized;
3333
}
3434

3535
fn change_assoc(x: *mut dyn Assocked<Assoc = u8>) -> *mut dyn Assocked<Assoc = u32> {
36-
x as _ //~ error: mismatched types
36+
x as _ //~ error: casting `*mut (dyn Assocked<Assoc = u8> + 'static)` as `*mut (dyn Assocked<Assoc = u32> + 'static)` is invalid
3737
}

tests/ui/cast/ptr-to-trait-obj-different-args.stderr

+14-27
Original file line numberDiff line numberDiff line change
@@ -4,53 +4,40 @@ error[E0606]: casting `*const dyn A` as `*const dyn B` is invalid
44
LL | let b: *const dyn B = a as _;
55
| ^^^^^^
66
|
7-
= note: vtable kinds may not match
7+
= note: the trait objects may have different vtables
88

9-
error[E0308]: mismatched types
9+
error[E0606]: casting `*const dyn Trait<X>` as `*const dyn Trait<Y>` is invalid
1010
--> $DIR/ptr-to-trait-obj-different-args.rs:21:34
1111
|
1212
LL | let y: *const dyn Trait<Y> = x as _;
13-
| ^^^^^^ expected `X`, found `Y`
13+
| ^^^^^^
1414
|
15-
= note: expected trait object `dyn Trait<X>`
16-
found trait object `dyn Trait<Y>`
17-
= 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
15+
= note: the trait objects may have different vtables
1816

19-
error[E0308]: mismatched types
17+
error[E0606]: casting `*const (dyn Trait<X> + 'static)` as `*const dyn Trait<T>` is invalid
2018
--> $DIR/ptr-to-trait-obj-different-args.rs:27:34
2119
|
22-
LL | fn generic<T>(x: *const dyn Trait<X>, t: *const dyn Trait<T>) {
23-
| - found this type parameter
2420
LL | let _: *const dyn Trait<T> = x as _;
25-
| ^^^^^^ expected `X`, found type parameter `T`
21+
| ^^^^^^
2622
|
27-
= note: expected trait object `dyn Trait<X>`
28-
found trait object `dyn Trait<T>`
29-
= 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
23+
= note: the trait objects may have different vtables
3024

31-
error[E0308]: mismatched types
25+
error[E0606]: casting `*const (dyn Trait<T> + 'static)` as `*const dyn Trait<X>` is invalid
3226
--> $DIR/ptr-to-trait-obj-different-args.rs:28:34
3327
|
34-
LL | fn generic<T>(x: *const dyn Trait<X>, t: *const dyn Trait<T>) {
35-
| - expected this type parameter
36-
LL | let _: *const dyn Trait<T> = x as _;
3728
LL | let _: *const dyn Trait<X> = t as _;
38-
| ^^^^^^ expected type parameter `T`, found `X`
29+
| ^^^^^^
3930
|
40-
= note: expected trait object `dyn Trait<T>`
41-
found trait object `dyn Trait<X>`
42-
= 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
31+
= note: the trait objects may have different vtables
4332

44-
error[E0308]: mismatched types
33+
error[E0606]: casting `*mut (dyn Assocked<Assoc = u8> + 'static)` as `*mut (dyn Assocked<Assoc = u32> + 'static)` is invalid
4534
--> $DIR/ptr-to-trait-obj-different-args.rs:36:5
4635
|
4736
LL | x as _
48-
| ^^^^^^ expected `u8`, found `u32`
37+
| ^^^^^^
4938
|
50-
= note: expected trait object `dyn Assocked<Assoc = u8>`
51-
found trait object `dyn Assocked<Assoc = u32>`
39+
= note: the trait objects may have different vtables
5240

5341
error: aborting due to 5 previous errors
5442

55-
Some errors have detailed explanations: E0308, E0606.
56-
For more information about an error, try `rustc --explain E0308`.
43+
For more information about this error, try `rustc --explain E0606`.

tests/ui/cast/ptr-to-trait-obj-wrap-upcast.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0606]: casting `*const (dyn Sub + 'static)` as `*const Wrapper<dyn Super>
44
LL | ptr as _
55
| ^^^^^^^^
66
|
7-
= note: vtable kinds may not match
7+
= note: the trait objects may have different vtables
88

99
error: aborting due to 1 previous error
1010

tests/ui/mismatched_types/cast-rfc0401.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ error[E0606]: casting `*const U` as `*const V` is invalid
44
LL | u as *const V
55
| ^^^^^^^^^^^^^
66
|
7-
= note: vtable kinds may not match
7+
= note: the pointers may have different metadata
88

99
error[E0606]: casting `*const U` as `*const str` is invalid
1010
--> $DIR/cast-rfc0401.rs:8:5
1111
|
1212
LL | u as *const str
1313
| ^^^^^^^^^^^^^^^
1414
|
15-
= note: vtable kinds may not match
15+
= note: the pointers may have different metadata
1616

1717
error[E0609]: no field `f` on type `fn() {main}`
1818
--> $DIR/cast-rfc0401.rs:65:18
@@ -208,15 +208,15 @@ error[E0606]: casting `*const dyn Foo` as `*const [u16]` is invalid
208208
LL | let _ = cf as *const [u16];
209209
| ^^^^^^^^^^^^^^^^^^
210210
|
211-
= note: vtable kinds may not match
211+
= note: the pointers have different metadata
212212

213213
error[E0606]: casting `*const dyn Foo` as `*const dyn Bar` is invalid
214214
--> $DIR/cast-rfc0401.rs:69:13
215215
|
216216
LL | let _ = cf as *const dyn Bar;
217217
| ^^^^^^^^^^^^^^^^^^^^
218218
|
219-
= note: vtable kinds may not match
219+
= note: the trait objects may have different vtables
220220

221221
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
222222
--> $DIR/cast-rfc0401.rs:53:13

tests/ui/traits/upcast_soundness_bug.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ pub fn user2() -> &'static dyn Trait<u8, u16> {
5757
fn main() {
5858
let p: *const dyn Trait<u8, u8> = &();
5959
let p = p as *const dyn Trait<u8, u16>; // <- this is bad!
60-
//~^ error: mismatched types
60+
//~^ error: casting `*const dyn Trait<u8, u8>` as `*const dyn Trait<u8, u16>` is invalid
6161
let p = p as *const dyn Super<u16>; // <- this upcast accesses improper vtable entry
6262
// accessing from L__unnamed_2 the position for the 'Super<u16> vtable (pointer)',
6363
// thus reading 'null pointer for missing_method'
+4-6
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
error[E0308]: mismatched types
1+
error[E0606]: casting `*const dyn Trait<u8, u8>` as `*const dyn Trait<u8, u16>` is invalid
22
--> $DIR/upcast_soundness_bug.rs:59:13
33
|
44
LL | let p = p as *const dyn Trait<u8, u16>; // <- this is bad!
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `u16`
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
7-
= note: expected trait object `dyn Trait<u8, u8>`
8-
found trait object `dyn Trait<u8, u16>`
9-
= 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
7+
= note: the trait objects may have different vtables
108

119
error: aborting due to 1 previous error
1210

13-
For more information about this error, try `rustc --explain E0308`.
11+
For more information about this error, try `rustc --explain E0606`.

0 commit comments

Comments
 (0)