Skip to content

Tracking Issue for breaking lifetime changing raw pointer casts of trait objects #141402

Open
@BoxyUwU

Description

@BoxyUwU

This is the tracking issue for the breaking change made in #136776 The goal of this page is describe why this change was made and how you can fix code that is affected by it. It also provides a place to ask questions or register a complaint if you feel the change should not be made.

What is the warning for?

As part of stabilizing the arbitrary_self_types and derive_coerce_pointee we needed to change what raw pointer casts are legal. Specifically, casting *const dyn Trait + 'a to *const dyn Trait + 'b where it is not able to be proven that 'a outlives 'b has become an error.

Why was this change made?

Casting the lifetime bound on trait objects can invalidate the VTable for the trait object allowing for dispatching to methods that should not be callable. When a trait method has a where Self: 'a bound, casting the lifetime bound on a trait object may cause the method to go from uncallable to callable.

Example

#![forbid(unsafe_code)]
#![feature(arbitrary_self_types, derive_coerce_pointee)]

use std::any::TypeId;
use std::marker::{CoercePointee, PhantomData};

#[derive(CoercePointee)]
#[repr(transparent)]
struct SelfPtr<T: ?Sized>(*const T);

impl<T: ?Sized> std::ops::Deref for SelfPtr<T> {
    type Target = T;
    fn deref(&self) -> &T {
        panic!("please don't call me, I just want the `Receiver` impl!");
    }
}

trait GetTypeId {
    fn get_type_id(self: SelfPtr<Self>) -> TypeId
    where
        Self: 'static;
}

impl<T: ?Sized> GetTypeId for PhantomData<T> {
    fn get_type_id(self: SelfPtr<Self>) -> TypeId
    where
        Self: 'static,
    {
        TypeId::of::<T>()
    }
}

// no `T: 'static` bound necessary
fn type_id_of<T: ?Sized>() -> TypeId {
    let ptr = SelfPtr(
        // This line no longer compiles
        &PhantomData::<T> as *const (dyn GetTypeId + '_) as *const (dyn GetTypeId + 'static),
    );
    ptr.get_type_id()
}

Migration

Existing code can be trivially migrated by converting the offending raw pointer cast to a lifetime affecting transmute. See metrics-rs/metrics#564 as an example of how such a migration can be accomplished. It's advised to only do so if actually sure that extending the lifetime of the trait object is sound.

Related Links

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-dyn-traitArea: trait objects, vtable layoutA-raw-pointersArea: raw pointers, MaybeUninit, NonNullC-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCF-arbitrary_self_types`#![feature(arbitrary_self_types)]`F-derive_coerce_pointeeFeature: RFC 3621's oft-renamed implementationT-langRelevant to the language team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions