Skip to content

Forbid freely casting lifetime bounds of dyn-types #136776

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
cx.add_sized_or_copy_bound_info(err, category, &path);

if let ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: _,
is_implicit_coercion: true,
unsize_to: Some(unsize_ty),
} = category
Expand Down
17 changes: 17 additions & 0 deletions compiler/rustc_borrowck/src/diagnostics/region_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,23 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
self.add_placeholder_from_predicate_note(&mut diag, &path);
self.add_sized_or_copy_bound_info(&mut diag, category, &path);

for constraint in &path {
if let ConstraintCategory::Cast { is_raw_ptr_dyn_type_cast: true, .. } =
constraint.category
{
diag.span_note(
constraint.span,
format!("raw pointer casts of trait objects do not cast away lifetimes"),
);
diag.note(format!(
"this was previously accepted by the compiler but was changed recently"
));
diag.help(format!(
"see <https://github.com/rust-lang/rust/issues/141402> for more information"
));
}
}

self.buffer_error(diag);
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_borrowck/src/region_infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1740,6 +1740,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// should be as limited as possible; the note is prone to false positives and this
// constraint usually isn't best to blame.
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: _,
unsize_to: Some(unsize_ty),
is_implicit_coercion: true,
} if target_region == self.universal_regions().fr_static
Expand Down
74 changes: 61 additions & 13 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1098,15 +1098,23 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.prove_predicate(
ty::ClauseKind::WellFormed(src_ty.into()),
location.to_locations(),
ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None },
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: false,
is_implicit_coercion,
unsize_to: None,
},
);

let src_ty = self.normalize(src_ty, location);
if let Err(terr) = self.sub_types(
src_ty,
*ty,
location.to_locations(),
ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None },
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: false,
is_implicit_coercion,
unsize_to: None,
},
) {
span_mirbug!(
self,
Expand All @@ -1127,7 +1135,11 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.prove_predicate(
ty::ClauseKind::WellFormed(src_ty.into()),
location.to_locations(),
ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None },
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: false,
is_implicit_coercion,
unsize_to: None,
},
);

// The type that we see in the fcx is like
Expand All @@ -1140,7 +1152,11 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
src_ty,
*ty,
location.to_locations(),
ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None },
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: false,
is_implicit_coercion,
unsize_to: None,
},
) {
span_mirbug!(
self,
Expand Down Expand Up @@ -1169,7 +1185,11 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
ty_fn_ptr_from,
*ty,
location.to_locations(),
ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None },
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: false,
is_implicit_coercion,
unsize_to: None,
},
) {
span_mirbug!(
self,
Expand Down Expand Up @@ -1202,7 +1222,11 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
ty_fn_ptr_from,
*ty,
location.to_locations(),
ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None },
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: false,
is_implicit_coercion,
unsize_to: None,
},
) {
span_mirbug!(
self,
Expand Down Expand Up @@ -1231,6 +1255,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
trait_ref,
location.to_locations(),
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: false,
is_implicit_coercion,
unsize_to: Some(unsize_to),
},
Expand All @@ -1256,7 +1281,11 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
*ty_from,
*ty_to,
location.to_locations(),
ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None },
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: false,
is_implicit_coercion,
unsize_to: None,
},
) {
span_mirbug!(
self,
Expand Down Expand Up @@ -1319,7 +1348,11 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
*ty_elem,
*ty_to,
location.to_locations(),
ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None },
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: false,
is_implicit_coercion,
unsize_to: None,
},
) {
span_mirbug!(
self,
Expand Down Expand Up @@ -1467,21 +1500,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
//
// Note that other checks (such as denying `dyn Send` -> `dyn
// Debug`) are in `rustc_hir_typeck`.
if let ty::Dynamic(src_tty, _src_lt, ty::Dyn) = *src_tail.kind()
if let ty::Dynamic(src_tty, src_lt, ty::Dyn) = *src_tail.kind()
&& let ty::Dynamic(dst_tty, dst_lt, ty::Dyn) = *dst_tail.kind()
&& src_tty.principal().is_some()
&& dst_tty.principal().is_some()
{
// Remove auto traits.
// Auto trait checks are handled in `rustc_hir_typeck` as FCW.
// Auto trait checks are handled in `rustc_hir_typeck`.
let src_obj = Ty::new_dynamic(
tcx,
tcx.mk_poly_existential_predicates(
&src_tty.without_auto_traits().collect::<Vec<_>>(),
),
// FIXME: Once we disallow casting `*const dyn Trait + 'short`
// to `*const dyn Trait + 'long`, then this can just be `src_lt`.
dst_lt,
src_lt,
ty::Dyn,
);
let dst_obj = Ty::new_dynamic(
Expand All @@ -1495,11 +1526,28 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {

debug!(?src_tty, ?dst_tty, ?src_obj, ?dst_obj);

// Trait parameters are Invariant, the only part that actually has subtyping
// here is the lifetime bound of the dyn-type.
//
// For example in `dyn Trait<'a> + 'b <: dyn Trait<'c> + 'd` we would require
// that `'a == 'c` but only that `'b: 'd`.
//
// We must not allow freely casting lifetime bounds of dyn-types as it may allow
// for inaccessible VTable methods being callable: #136702
//
// We don't enforce this for casts of principal-less dyn types as their VTables do
// not contain any functions with `Self: 'a` bounds that could start holding after
// a pointer cast.
//
// We also don't enforce this for casts of pointers to pointers to dyn types. E.g.
// `*mut *mut dyn Trait + 'a -> *mut *mut dyn Trait + 'static` is allowed. This is
// fine because there is no actual VTable in play.
self.sub_types(
src_obj,
dst_obj,
location.to_locations(),
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: true,
is_implicit_coercion: false,
unsize_to: None,
},
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/mir/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ pub enum ConstraintCategory<'tcx> {
UseAsStatic,
TypeAnnotation(AnnotationSource),
Cast {
is_raw_ptr_dyn_type_cast: bool,
/// Whether this cast is a coercion that was automatically inserted by the compiler.
is_implicit_coercion: bool,
/// Whether this is an unsizing coercion and if yes, this contains the target type.
Expand Down
7 changes: 5 additions & 2 deletions library/std/src/thread/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -578,8 +578,11 @@ impl Builder {
let main = 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() + Send + 'static)) };
let main = unsafe {
let ptr = Box::into_raw(main) as *mut (dyn FnOnce() + Send + '_);
let ptr: *mut (dyn FnOnce() + Send + 'static) = crate::mem::transmute(ptr);
Box::from_raw(ptr)
};

Ok(JoinInner {
// SAFETY:
Expand Down
11 changes: 11 additions & 0 deletions tests/ui/cast/ptr-ptr-to-ptr-ptr-different-regions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//@ check-pass

trait Trait {
fn foo(&self) {}
}

fn bar<'a>(a: *mut *mut (dyn Trait + 'a)) -> *mut *mut (dyn Trait + 'static) {
a as _
}

fn main() {}
7 changes: 4 additions & 3 deletions tests/ui/cast/ptr-to-ptr-different-regions.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//@ check-pass

// https://github.com/rust-lang/rust/issues/113257

#![deny(trivial_casts)] // The casts here are not trivial.

struct Foo<'a> { a: &'a () }
struct Foo<'a> {
a: &'a (),
}

fn extend_lifetime_very_very_safely<'a>(v: *const Foo<'a>) -> *const Foo<'static> {
// This should pass because raw pointer casts can do anything they want.
Expand All @@ -15,6 +15,7 @@ trait Trait {}

fn assert_static<'a>(ptr: *mut (dyn Trait + 'a)) -> *mut (dyn Trait + 'static) {
ptr as _
//~^ ERROR: lifetime may not live long enough
}

fn main() {
Expand Down
31 changes: 31 additions & 0 deletions tests/ui/cast/ptr-to-ptr-different-regions.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
error: lifetime may not live long enough
--> $DIR/ptr-to-ptr-different-regions.rs:17:5
|
LL | fn assert_static<'a>(ptr: *mut (dyn Trait + 'a)) -> *mut (dyn Trait + 'static) {
| -- lifetime `'a` defined here
LL | ptr as _
| ^^^^^^^^ returning this value requires that `'a` must outlive `'static`
|
= note: requirement occurs because of a mutable pointer to `dyn Trait`
= note: mutable pointers are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
note: raw pointer casts of trait objects do not cast away lifetimes
--> $DIR/ptr-to-ptr-different-regions.rs:17:5
|
LL | ptr as _
| ^^^^^^^^
= note: this was previously accepted by the compiler but was changed recently
= help: see <https://github.com/rust-lang/rust/issues/141402> for more information
help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `ptr`
|
LL - fn assert_static<'a>(ptr: *mut (dyn Trait + 'a)) -> *mut (dyn Trait + 'static) {
LL + fn assert_static<'a>(ptr: *mut (dyn Trait + 'a)) -> *mut (dyn Trait + 'a) {
|
help: alternatively, add an explicit `'static` bound to this reference
|
LL - fn assert_static<'a>(ptr: *mut (dyn Trait + 'a)) -> *mut (dyn Trait + 'static) {
LL + fn assert_static<'a>(ptr: *mut (dyn Trait + 'static)) -> *mut (dyn Trait + 'static) {
|

error: aborting due to 1 previous error

12 changes: 12 additions & 0 deletions tests/ui/cast/ptr-to-ptr-indirect-different-regions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
trait Trait {
fn foo(&self) {}
}

struct MyWrap<T: ?Sized>(T);

fn bar<'a>(a: *mut MyWrap<(dyn Trait + 'a)>) -> *mut MyWrap<(dyn Trait + 'static)> {
a as _
//~^ ERROR: lifetime may not live long enough
}

fn main() {}
31 changes: 31 additions & 0 deletions tests/ui/cast/ptr-to-ptr-indirect-different-regions.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
error: lifetime may not live long enough
--> $DIR/ptr-to-ptr-indirect-different-regions.rs:8:5
|
LL | fn bar<'a>(a: *mut MyWrap<(dyn Trait + 'a)>) -> *mut MyWrap<(dyn Trait + 'static)> {
| -- lifetime `'a` defined here
LL | a as _
| ^^^^^^ returning this value requires that `'a` must outlive `'static`
|
= note: requirement occurs because of a mutable pointer to `MyWrap<dyn Trait>`
= note: mutable pointers are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
note: raw pointer casts of trait objects do not cast away lifetimes
--> $DIR/ptr-to-ptr-indirect-different-regions.rs:8:5
|
LL | a as _
| ^^^^^^
= note: this was previously accepted by the compiler but was changed recently
= help: see <https://github.com/rust-lang/rust/issues/141402> for more information
help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `a`
|
LL - fn bar<'a>(a: *mut MyWrap<(dyn Trait + 'a)>) -> *mut MyWrap<(dyn Trait + 'static)> {
LL + fn bar<'a>(a: *mut MyWrap<(dyn Trait + 'a)>) -> *mut MyWrap<(dyn Trait + 'a)> {
|
help: alternatively, add an explicit `'static` bound to this reference
|
LL - fn bar<'a>(a: *mut MyWrap<(dyn Trait + 'a)>) -> *mut MyWrap<(dyn Trait + 'static)> {
LL + fn bar<'a>(a: *mut MyWrap<(dyn Trait + 'static)>) -> *mut MyWrap<(dyn Trait + 'static)> {
|

error: aborting due to 1 previous error

Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ LL | let _send = unsend as *const S<dyn Cat<'static>>;
= note: requirement occurs because of the type `S<dyn Cat<'_>>`, which makes the generic argument `dyn Cat<'_>` invariant
= note: the struct `S<T>` is invariant over the parameter `T`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
note: raw pointer casts of trait objects do not cast away lifetimes
--> $DIR/ptr-to-trait-obj-different-regions-id-trait.rs:24:17
|
LL | let _send = unsend as *const S<dyn Cat<'static>>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this was previously accepted by the compiler but was changed recently
= help: see <https://github.com/rust-lang/rust/issues/141402> for more information

error: aborting due to 1 previous error

Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ LL | let _send = unsend as *const S<dyn Cat<'static>>;
= note: requirement occurs because of the type `S<dyn Cat<'_>>`, which makes the generic argument `dyn Cat<'_>` invariant
= note: the struct `S<T>` is invariant over the parameter `T`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
note: raw pointer casts of trait objects do not cast away lifetimes
--> $DIR/ptr-to-trait-obj-different-regions-id-trait.rs:24:17
|
LL | let _send = unsend as *const S<dyn Cat<'static>>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this was previously accepted by the compiler but was changed recently
= help: see <https://github.com/rust-lang/rust/issues/141402> for more information

error: aborting due to 1 previous error

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ 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`
|
note: raw pointer casts of trait objects do not cast away lifetimes
--> $DIR/ptr-to-trait-obj-different-regions-lt-ext.rs:12:5
|
LL | x as _
| ^^^^^^
= note: this was previously accepted by the compiler but was changed recently
= help: see <https://github.com/rust-lang/rust/issues/141402> for more information

error: aborting due to 1 previous error

Loading
Loading