Skip to content

Commit

Permalink
Check lifetimes for some pointer casts
Browse files Browse the repository at this point in the history
Specifically check them for trait objects, i.e. `*const Trait + 'a` -> `*const Trait + b`, etc.
  • Loading branch information
WaffleLapkin committed Jan 22, 2024
1 parent f3de343 commit 3c3cf17
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 18 deletions.
41 changes: 33 additions & 8 deletions compiler/rustc_hir_typeck/src/cast.rs
Expand Up @@ -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 <https://github.com/rust-lang/rust/issues/120217>
(_, _) => {
debug!(" -> CoercionCast");
fcx.typeck_results
.borrow_mut()
.set_coercion_cast(self.expr.hir_id.local_id);
}
}
}
} else {
self.trivial_cast_lint(fcx);
debug!(" -> CoercionCast");
Expand Down
6 changes: 4 additions & 2 deletions library/std/src/thread/mod.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -545,10 +546,11 @@ impl Builder {
scope_data.increment_num_running_threads();
}

let main = Box::new(main);
let main: Box<dyn FnOnce() + '_> = 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<dyn FnOnce() + '_>, Box<dyn FnOnce() + 'static>>(main) };

Ok(JoinInner {
// SAFETY:
Expand Down
6 changes: 0 additions & 6 deletions tests/ui/cast/ptr-to-ptr-different-regions.rs
Expand Up @@ -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 };
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/cast/ptr-to-trait-obj-different-regions.rs
@@ -1,4 +1,4 @@
// check-pass
// check-fail
//
// issue: <https://github.com/rust-lang/rust/issues/120217>

Expand All @@ -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 () {
Expand Down
10 changes: 10 additions & 0 deletions 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

0 comments on commit 3c3cf17

Please sign in to comment.