Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 2c99da5

Browse files
committedJan 22, 2024
Auto merge of rust-lang#120248 - WaffleLapkin:bonk-ptr-object-casts, r=<try>
Make casts of pointers to trait objects stricter This is an attempt to `fix` rust-lang#120222 and rust-lang#120217. cc `@oli-obk` `@compiler-errors` `@lcnr`
2 parents 021861a + 3c3cf17 commit 2c99da5

8 files changed

+146
-31
lines changed
 

‎compiler/rustc_hir_typeck/src/cast.rs

+36-11
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use rustc_middle::ty::cast::{CastKind, CastTy};
4242
use rustc_middle::ty::error::TypeError;
4343
use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitableExt, VariantDef};
4444
use rustc_session::lint;
45-
use rustc_span::def_id::{DefId, LOCAL_CRATE};
45+
use rustc_span::def_id::LOCAL_CRATE;
4646
use rustc_span::symbol::sym;
4747
use rustc_span::Span;
4848
use rustc_trait_selection::infer::InferCtxtExt;
@@ -72,7 +72,7 @@ enum PointerKind<'tcx> {
7272
/// No metadata attached, ie pointer to sized type or foreign type
7373
Thin,
7474
/// A trait object
75-
VTable(Option<DefId>),
75+
VTable(Option<ty::Binder<'tcx, ty::ExistentialTraitRef<'tcx>>>),
7676
/// Slice
7777
Length,
7878
/// The unsize info of this projection or opaque type
@@ -100,7 +100,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
100100

101101
Ok(match *t.kind() {
102102
ty::Slice(_) | ty::Str => Some(PointerKind::Length),
103-
ty::Dynamic(tty, _, ty::Dyn) => Some(PointerKind::VTable(tty.principal_def_id())),
103+
ty::Dynamic(tty, _, ty::Dyn) => Some(PointerKind::VTable(tty.principal())),
104104
ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() {
105105
None => Some(PointerKind::Thin),
106106
Some(f) => {
@@ -611,14 +611,39 @@ impl<'a, 'tcx> CastCheck<'tcx> {
611611
} else {
612612
match self.try_coercion_cast(fcx) {
613613
Ok(()) => {
614-
if self.expr_ty.is_unsafe_ptr() && self.cast_ty.is_unsafe_ptr() {
615-
// When casting a raw pointer to another raw pointer, we cannot convert the cast into
616-
// a coercion because the pointee types might only differ in regions, which HIR typeck
617-
// cannot distinguish. This would cause us to erroneously discard a cast which will
618-
// lead to a borrowck error like #113257.
619-
// We still did a coercion above to unify inference variables for `ptr as _` casts.
620-
// This does cause us to miss some trivial casts in the trival cast lint.
621-
debug!(" -> PointerCast");
614+
if let ty::RawPtr(src_pointee) = self.expr_ty.kind()
615+
&& let ty::RawPtr(tgt_pointee) = self.cast_ty.kind()
616+
{
617+
if let Ok(Some(src_kind)) = fcx.pointer_kind(src_pointee.ty, self.expr_span)
618+
&& let Ok(Some(tgt_kind)) =
619+
fcx.pointer_kind(tgt_pointee.ty, self.cast_span)
620+
{
621+
match (src_kind, tgt_kind) {
622+
// When casting a raw pointer to another raw pointer, we cannot convert the cast into
623+
// a coercion because the pointee types might only differ in regions, which HIR typeck
624+
// cannot distinguish. This would cause us to erroneously discard a cast which will
625+
// lead to a borrowck error like #113257.
626+
// We still did a coercion above to unify inference variables for `ptr as _` casts.
627+
// This does cause us to miss some trivial casts in the trivial cast lint.
628+
(PointerKind::Thin, PointerKind::Thin)
629+
| (PointerKind::Length, PointerKind::Length) => {
630+
debug!(" -> PointerCast");
631+
}
632+
633+
// If we are not casting pointers to sized types or slice-ish DSTs
634+
// (handled above), we need to make a coercion cast. This prevents
635+
// casts like `*const dyn Trait<'a> -> *const dyn Trait<'b>` which
636+
// are unsound.
637+
//
638+
// See <https://github.com/rust-lang/rust/issues/120217>
639+
(_, _) => {
640+
debug!(" -> CoercionCast");
641+
fcx.typeck_results
642+
.borrow_mut()
643+
.set_coercion_cast(self.expr.hir_id.local_id);
644+
}
645+
}
646+
}
622647
} else {
623648
self.trivial_cast_lint(fcx);
624649
debug!(" -> CoercionCast");

‎library/std/src/thread/mod.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ use crate::ffi::{CStr, CString};
164164
use crate::fmt;
165165
use crate::io;
166166
use crate::marker::PhantomData;
167+
use crate::mem::transmute;
167168
use crate::mem::{self, forget};
168169
use crate::num::NonZeroU64;
169170
use crate::num::NonZeroUsize;
@@ -545,10 +546,11 @@ impl Builder {
545546
scope_data.increment_num_running_threads();
546547
}
547548

548-
let main = Box::new(main);
549+
let main: Box<dyn FnOnce() + '_> = Box::new(main);
549550
// SAFETY: dynamic size and alignment of the Box remain the same. See below for why the
550551
// lifetime change is justified.
551-
let main = unsafe { Box::from_raw(Box::into_raw(main) as *mut (dyn FnOnce() + 'static)) };
552+
let main =
553+
unsafe { transmute::<Box<dyn FnOnce() + '_>, Box<dyn FnOnce() + 'static>>(main) };
552554

553555
Ok(JoinInner {
554556
// SAFETY:

‎tests/ui/cast/cast-rfc0401-vtable-kinds.rs

-12
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,6 @@ impl<T> Foo<T> for () {}
1616
impl Foo<u32> for u32 { fn foo(&self, _: u32) -> u32 { self+43 } }
1717
impl Bar for () {}
1818

19-
unsafe fn round_trip_and_call<'a>(t: *const (dyn Foo<u32>+'a)) -> u32 {
20-
let foo_e : *const dyn Foo<u16> = t as *const _;
21-
let r_1 = foo_e as *mut dyn Foo<u32>;
22-
23-
(&*r_1).foo(0)
24-
}
25-
2619
#[repr(C)]
2720
struct FooS<T:?Sized>(T);
2821
#[repr(C)]
@@ -38,11 +31,6 @@ fn tuple_i32_to_u32<T:?Sized>(u: *const (i32, T)) -> *const (u32, T) {
3831

3932

4033
fn main() {
41-
let x = 4u32;
42-
let y : &dyn Foo<u32> = &x;
43-
let fl = unsafe { round_trip_and_call(y as *const dyn Foo<u32>) };
44-
assert_eq!(fl, (43+4));
45-
4634
let s = FooS([0,1,2]);
4735
let u: &FooS<[u32]> = &s;
4836
let u: *const FooS<[u32]> = u;

‎tests/ui/cast/ptr-to-ptr-different-regions.rs

-6
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,6 @@ fn extend_lifetime_very_very_safely<'a>(v: *const Foo<'a>) -> *const Foo<'static
1111
v as *const Foo<'static>
1212
}
1313

14-
trait Trait {}
15-
16-
fn assert_static<'a>(ptr: *mut (dyn Trait + 'a)) -> *mut (dyn Trait + 'static) {
17-
ptr as _
18-
}
19-
2014
fn main() {
2115
let unit = ();
2216
let foo = Foo { a: &unit };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// check-fail
2+
//
3+
// issue: <https://github.com/rust-lang/rust/issues/120222>
4+
5+
6+
trait A {}
7+
impl<T> A for T {}
8+
trait B {}
9+
impl<T> B for T {}
10+
11+
trait Trait<G> {}
12+
struct X;
13+
impl<T> Trait<X> for T {}
14+
struct Y;
15+
impl<T> Trait<Y> for T {}
16+
17+
fn main() {
18+
let a: *const dyn A = &();
19+
let b: *const dyn B = a as _; //~ error: casting `*const dyn A` as `*const dyn B` is invalid
20+
21+
let x: *const dyn Trait<X> = &();
22+
let y: *const dyn Trait<Y> = x as _; //~ error: casting `*const dyn Trait<X>` as `*const dyn Trait<Y>` is invalid
23+
24+
_ = (b, y);
25+
}
26+
27+
fn generic<T>(x: *const dyn Trait<X>, t: *const dyn Trait<T>) {
28+
let _: *const dyn Trait<T> = x as _; //~ error: casting `*const (dyn Trait<X> + 'static)` as `*const dyn Trait<T>` is invalid
29+
let _: *const dyn Trait<X> = t as _; //~ error: casting `*const (dyn Trait<T> + 'static)` as `*const dyn Trait<X>` is invalid
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error[E0606]: casting `*const dyn A` as `*const dyn B` is invalid
2+
--> $DIR/ptr-to-trait-obj-different-args.rs:19:27
3+
|
4+
LL | let b: *const dyn B = a as _;
5+
| ^^^^^^
6+
|
7+
= note: vtable kinds may not match
8+
9+
error[E0606]: casting `*const dyn Trait<X>` as `*const dyn Trait<Y>` is invalid
10+
--> $DIR/ptr-to-trait-obj-different-args.rs:22:34
11+
|
12+
LL | let y: *const dyn Trait<Y> = x as _;
13+
| ^^^^^^
14+
|
15+
= note: vtable kinds may not match
16+
17+
error[E0606]: casting `*const (dyn Trait<X> + 'static)` as `*const dyn Trait<T>` is invalid
18+
--> $DIR/ptr-to-trait-obj-different-args.rs:28:34
19+
|
20+
LL | let _: *const dyn Trait<T> = x as _;
21+
| ^^^^^^
22+
|
23+
= note: vtable kinds may not match
24+
25+
error[E0606]: casting `*const (dyn Trait<T> + 'static)` as `*const dyn Trait<X>` is invalid
26+
--> $DIR/ptr-to-trait-obj-different-args.rs:29:34
27+
|
28+
LL | let _: *const dyn Trait<X> = t as _;
29+
| ^^^^^^
30+
|
31+
= note: vtable kinds may not match
32+
33+
error: aborting due to 4 previous errors
34+
35+
For more information about this error, try `rustc --explain E0606`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// check-fail
2+
//
3+
// issue: <https://github.com/rust-lang/rust/issues/120217>
4+
5+
#![feature(arbitrary_self_types)]
6+
7+
trait Static<'a> {
8+
fn proof(self: *const Self, s: &'a str) -> &'static str;
9+
}
10+
11+
fn bad_cast<'a>(x: *const dyn Static<'static>) -> *const dyn Static<'a> {
12+
x as _ //~ error: lifetime may not live long enough
13+
}
14+
15+
impl Static<'static> for () {
16+
fn proof(self: *const Self, s: &'static str) -> &'static str {
17+
s
18+
}
19+
}
20+
21+
fn extend_lifetime(s: &str) -> &'static str {
22+
bad_cast(&()).proof(s)
23+
}
24+
25+
fn main() {
26+
let s = String::from("Hello World");
27+
let slice = extend_lifetime(&s);
28+
println!("Now it exists: {slice}");
29+
drop(s);
30+
println!("Now it’s gone: {slice}");
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/ptr-to-trait-obj-different-regions.rs:12:5
3+
|
4+
LL | fn bad_cast<'a>(x: *const dyn Static<'static>) -> *const dyn Static<'a> {
5+
| -- lifetime `'a` defined here
6+
LL | x as _
7+
| ^^^^^^ returning this value requires that `'a` must outlive `'static`
8+
9+
error: aborting due to 1 previous error
10+

0 commit comments

Comments
 (0)
Failed to load comments.