From 622ca1dcee3adbed1a5a0edc2e43c362c728c8df Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 22 Jan 2024 17:21:57 +0000 Subject: [PATCH 1/4] Add a test for `*const Tr` to `*const Tr` casts --- .../cast/ptr-to-trait-obj-different-args.rs | 30 +++++++++++++++++++ .../ptr-to-trait-obj-different-args.stderr | 11 +++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/ui/cast/ptr-to-trait-obj-different-args.rs create mode 100644 tests/ui/cast/ptr-to-trait-obj-different-args.stderr diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.rs b/tests/ui/cast/ptr-to-trait-obj-different-args.rs new file mode 100644 index 000000000000..2cb661ba016e --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.rs @@ -0,0 +1,30 @@ +// check-fail +// +// issue: + + +trait A {} +impl A for T {} +trait B {} +impl B for T {} + +trait Trait {} +struct X; +impl Trait for T {} +struct Y; +impl Trait for T {} + +fn main() { + let a: *const dyn A = &(); + let b: *const dyn B = a as _; //~ error: casting `*const dyn A` as `*const dyn B` is invalid + + let x: *const dyn Trait = &(); + let y: *const dyn Trait = x as _; + + _ = (b, y); +} + +fn generic(x: *const dyn Trait, t: *const dyn Trait) { + let _: *const dyn Trait = x as _; + let _: *const dyn Trait = t as _; +} diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.stderr b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr new file mode 100644 index 000000000000..14e8a48ffd92 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr @@ -0,0 +1,11 @@ +error[E0606]: casting `*const dyn A` as `*const dyn B` is invalid + --> $DIR/ptr-to-trait-obj-different-args.rs:19:27 + | +LL | let b: *const dyn B = a as _; + | ^^^^^^ + | + = note: vtable kinds may not match + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0606`. From 3141b789cf32dd6d06734b0b2ba4539cbe8099af Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 22 Jan 2024 17:22:09 +0000 Subject: [PATCH 2/4] Forbid casts of raw pointers to trait objects with the same trait, but different args --- compiler/rustc_hir_typeck/src/cast.rs | 6 ++--- tests/ui/cast/cast-rfc0401-vtable-kinds.rs | 12 --------- .../cast/ptr-to-trait-obj-different-args.rs | 6 ++--- .../ptr-to-trait-obj-different-args.stderr | 26 ++++++++++++++++++- 4 files changed, 31 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index a720a858f3c1..958e460fbd92 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -42,7 +42,7 @@ use rustc_middle::ty::cast::{CastKind, CastTy}; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitableExt, VariantDef}; use rustc_session::lint; -use rustc_span::def_id::{DefId, LOCAL_CRATE}; +use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; @@ -72,7 +72,7 @@ enum PointerKind<'tcx> { /// No metadata attached, ie pointer to sized type or foreign type Thin, /// A trait object - VTable(Option), + VTable(Option>>), /// Slice Length, /// The unsize info of this projection or opaque type @@ -100,7 +100,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(match *t.kind() { ty::Slice(_) | ty::Str => Some(PointerKind::Length), - ty::Dynamic(tty, _, ty::Dyn) => Some(PointerKind::VTable(tty.principal_def_id())), + ty::Dynamic(tty, _, ty::Dyn) => Some(PointerKind::VTable(tty.principal())), ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() { None => Some(PointerKind::Thin), Some(f) => { diff --git a/tests/ui/cast/cast-rfc0401-vtable-kinds.rs b/tests/ui/cast/cast-rfc0401-vtable-kinds.rs index 249481467e64..7d0244e1b21a 100644 --- a/tests/ui/cast/cast-rfc0401-vtable-kinds.rs +++ b/tests/ui/cast/cast-rfc0401-vtable-kinds.rs @@ -16,13 +16,6 @@ impl Foo for () {} impl Foo for u32 { fn foo(&self, _: u32) -> u32 { self+43 } } impl Bar for () {} -unsafe fn round_trip_and_call<'a>(t: *const (dyn Foo+'a)) -> u32 { - let foo_e : *const dyn Foo = t as *const _; - let r_1 = foo_e as *mut dyn Foo; - - (&*r_1).foo(0) -} - #[repr(C)] struct FooS(T); #[repr(C)] @@ -38,11 +31,6 @@ fn tuple_i32_to_u32(u: *const (i32, T)) -> *const (u32, T) { fn main() { - let x = 4u32; - let y : &dyn Foo = &x; - let fl = unsafe { round_trip_and_call(y as *const dyn Foo) }; - assert_eq!(fl, (43+4)); - let s = FooS([0,1,2]); let u: &FooS<[u32]> = &s; let u: *const FooS<[u32]> = u; diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.rs b/tests/ui/cast/ptr-to-trait-obj-different-args.rs index 2cb661ba016e..e1c0c66433dd 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-args.rs +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.rs @@ -19,12 +19,12 @@ 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 = &(); - let y: *const dyn Trait = x as _; + let y: *const dyn Trait = x as _; //~ error: casting `*const dyn Trait` as `*const dyn Trait` is invalid _ = (b, y); } fn generic(x: *const dyn Trait, t: *const dyn Trait) { - let _: *const dyn Trait = x as _; - let _: *const dyn Trait = t as _; + let _: *const dyn Trait = x as _; //~ error: casting `*const (dyn Trait + 'static)` as `*const dyn Trait` is invalid + let _: *const dyn Trait = t as _; //~ error: casting `*const (dyn Trait + 'static)` as `*const dyn Trait` is invalid } diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.stderr b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr index 14e8a48ffd92..cd67b52ad27d 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-args.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr @@ -6,6 +6,30 @@ LL | let b: *const dyn B = a as _; | = note: vtable kinds may not match -error: aborting due to 1 previous error +error[E0606]: casting `*const dyn Trait` as `*const dyn Trait` is invalid + --> $DIR/ptr-to-trait-obj-different-args.rs:22:34 + | +LL | let y: *const dyn Trait = x as _; + | ^^^^^^ + | + = note: vtable kinds may not match + +error[E0606]: casting `*const (dyn Trait + 'static)` as `*const dyn Trait` is invalid + --> $DIR/ptr-to-trait-obj-different-args.rs:28:34 + | +LL | let _: *const dyn Trait = x as _; + | ^^^^^^ + | + = note: vtable kinds may not match + +error[E0606]: casting `*const (dyn Trait + 'static)` as `*const dyn Trait` is invalid + --> $DIR/ptr-to-trait-obj-different-args.rs:29:34 + | +LL | let _: *const dyn Trait = t as _; + | ^^^^^^ + | + = note: vtable kinds may not match + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0606`. From f3de3435e34eeafb51826700ec7e4d046b873e85 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 22 Jan 2024 19:08:19 +0000 Subject: [PATCH 3/4] Add a test for `*const Trait<'a>` to `*const Trait<'b>` casts --- .../ptr-to-trait-obj-different-regions.rs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 tests/ui/cast/ptr-to-trait-obj-different-regions.rs diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions.rs b/tests/ui/cast/ptr-to-trait-obj-different-regions.rs new file mode 100644 index 000000000000..bafa5c95840b --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions.rs @@ -0,0 +1,31 @@ +// check-pass +// +// issue: + +#![feature(arbitrary_self_types)] + +trait Static<'a> { + fn proof(self: *const Self, s: &'a str) -> &'static str; +} + +fn bad_cast<'a>(x: *const dyn Static<'static>) -> *const dyn Static<'a> { + x as _ +} + +impl Static<'static> for () { + fn proof(self: *const Self, s: &'static str) -> &'static str { + s + } +} + +fn extend_lifetime(s: &str) -> &'static str { + bad_cast(&()).proof(s) +} + +fn main() { + let s = String::from("Hello World"); + let slice = extend_lifetime(&s); + println!("Now it exists: {slice}"); + drop(s); + println!("Now it’s gone: {slice}"); +} From 3c3cf174ebb5192ecf5f45fb6f3592fb5189f28c Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 22 Jan 2024 19:17:32 +0000 Subject: [PATCH 4/4] Check lifetimes for some pointer casts Specifically check them for trait objects, i.e. `*const Trait + 'a` -> `*const Trait + b`, etc. --- compiler/rustc_hir_typeck/src/cast.rs | 41 +++++++++++++++---- library/std/src/thread/mod.rs | 6 ++- tests/ui/cast/ptr-to-ptr-different-regions.rs | 6 --- .../ptr-to-trait-obj-different-regions.rs | 4 +- .../ptr-to-trait-obj-different-regions.stderr | 10 +++++ 5 files changed, 49 insertions(+), 18 deletions(-) create mode 100644 tests/ui/cast/ptr-to-trait-obj-different-regions.stderr diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 958e460fbd92..b00de8547656 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -611,14 +611,39 @@ impl<'a, 'tcx> CastCheck<'tcx> { } else { match self.try_coercion_cast(fcx) { Ok(()) => { - if self.expr_ty.is_unsafe_ptr() && self.cast_ty.is_unsafe_ptr() { - // When casting a raw pointer to another raw pointer, we cannot convert the cast into - // a coercion because the pointee types might only differ in regions, which HIR typeck - // cannot distinguish. This would cause us to erroneously discard a cast which will - // lead to a borrowck error like #113257. - // We still did a coercion above to unify inference variables for `ptr as _` casts. - // This does cause us to miss some trivial casts in the trival cast lint. - debug!(" -> PointerCast"); + if let ty::RawPtr(src_pointee) = self.expr_ty.kind() + && let ty::RawPtr(tgt_pointee) = self.cast_ty.kind() + { + if let Ok(Some(src_kind)) = fcx.pointer_kind(src_pointee.ty, self.expr_span) + && let Ok(Some(tgt_kind)) = + fcx.pointer_kind(tgt_pointee.ty, self.cast_span) + { + match (src_kind, tgt_kind) { + // When casting a raw pointer to another raw pointer, we cannot convert the cast into + // a coercion because the pointee types might only differ in regions, which HIR typeck + // cannot distinguish. This would cause us to erroneously discard a cast which will + // lead to a borrowck error like #113257. + // We still did a coercion above to unify inference variables for `ptr as _` casts. + // This does cause us to miss some trivial casts in the trivial cast lint. + (PointerKind::Thin, PointerKind::Thin) + | (PointerKind::Length, PointerKind::Length) => { + debug!(" -> PointerCast"); + } + + // If we are not casting pointers to sized types or slice-ish DSTs + // (handled above), we need to make a coercion cast. This prevents + // casts like `*const dyn Trait<'a> -> *const dyn Trait<'b>` which + // are unsound. + // + // See + (_, _) => { + debug!(" -> CoercionCast"); + fcx.typeck_results + .borrow_mut() + .set_coercion_cast(self.expr.hir_id.local_id); + } + } + } } else { self.trivial_cast_lint(fcx); debug!(" -> CoercionCast"); diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 8498937809e7..deb6674dfa05 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -164,6 +164,7 @@ use crate::ffi::{CStr, CString}; use crate::fmt; use crate::io; use crate::marker::PhantomData; +use crate::mem::transmute; use crate::mem::{self, forget}; use crate::num::NonZeroU64; use crate::num::NonZeroUsize; @@ -545,10 +546,11 @@ impl Builder { scope_data.increment_num_running_threads(); } - let main = Box::new(main); + let main: Box = Box::new(main); // SAFETY: dynamic size and alignment of the Box remain the same. See below for why the // lifetime change is justified. - let main = unsafe { Box::from_raw(Box::into_raw(main) as *mut (dyn FnOnce() + 'static)) }; + let main = + unsafe { transmute::, Box>(main) }; Ok(JoinInner { // SAFETY: diff --git a/tests/ui/cast/ptr-to-ptr-different-regions.rs b/tests/ui/cast/ptr-to-ptr-different-regions.rs index 5592e613ac1e..3b19fc519406 100644 --- a/tests/ui/cast/ptr-to-ptr-different-regions.rs +++ b/tests/ui/cast/ptr-to-ptr-different-regions.rs @@ -11,12 +11,6 @@ fn extend_lifetime_very_very_safely<'a>(v: *const Foo<'a>) -> *const Foo<'static v as *const Foo<'static> } -trait Trait {} - -fn assert_static<'a>(ptr: *mut (dyn Trait + 'a)) -> *mut (dyn Trait + 'static) { - ptr as _ -} - fn main() { let unit = (); let foo = Foo { a: &unit }; diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions.rs b/tests/ui/cast/ptr-to-trait-obj-different-regions.rs index bafa5c95840b..d994723981fb 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-regions.rs +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions.rs @@ -1,4 +1,4 @@ -// check-pass +// check-fail // // issue: @@ -9,7 +9,7 @@ trait Static<'a> { } fn bad_cast<'a>(x: *const dyn Static<'static>) -> *const dyn Static<'a> { - x as _ + x as _ //~ error: lifetime may not live long enough } impl Static<'static> for () { diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions.stderr b/tests/ui/cast/ptr-to-trait-obj-different-regions.stderr new file mode 100644 index 000000000000..b2c681ab7496 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions.rs:12:5 + | +LL | fn bad_cast<'a>(x: *const dyn Static<'static>) -> *const dyn Static<'a> { + | -- lifetime `'a` defined here +LL | x as _ + | ^^^^^^ returning this value requires that `'a` must outlive `'static` + +error: aborting due to 1 previous error +