arbitrary_self_types + derive_coerce_pointee allows calling methods whose where clauses are violated · Issue #136702 · rust-lang/rust (original) (raw)

Here's an example by @steffahn:

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

use std::any::TypeId; use std:📑:CoercePointee; use std:📑:PhantomData;

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

impl<T: ?Sized> std::ops::Deref for SelfPtr { 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) -> TypeId where Self: 'static; }

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

// no T: 'static bound 🐈‍⬛ necessary fn type_id_of<T: ?Sized>() -> TypeId { SelfPtr(&PhantomData:: as *const (dyn GetTypeId + ') as *const (dyn GetTypeId + 'static)).get_type_id() } fn type_id_of_val<T: ?Sized>(: &T) -> TypeId { type_id_of::() }

fn main() { let mut x = 1234; let reference = &mut x; let tid_of_ref = type_id_of_val(&reference); let closure = || *reference += 1; let tid_of_closure = type_id_of_val(&closure); dbg!(tid_of_ref, tid_of_closure); }

The double as *const dyn GetTypeId as *const dyn GetTypeId is necessary; the second as changes the lifetime something short-lived to 'static. That means we call get_type_id on a type that does not satisfy Self: 'static.

@BoxyUwU has a similar example:

#![feature(arbitrary_self_types, derive_coerce_pointee)]

use std:📑:CoercePointee;

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

use std::ops::Receiver;

impl<T: ?Sized> Receiver for MyPtr { type Target = T; }

trait Trait { fn foo(self: MyPtr) where Self: Send; }

impl Trait for *mut () { fn foo(self: MyPtr) { unreachable!() } }

fn main() { let a = 0x1 as *const *mut (); let a = a as *const dyn Trait as *const (dyn Trait + Send); MyPtr(a).foo(); }

Boxy's example is mitigated by a FCW lint. However, the lifetime example doesn't trigger a lint, and while the code above is "fine", we should at least have a good idea for why this cannot cause UB in other ways. (My gut feeling is that this can cause UB but I haven't tried.)

Cc @rust-lang/types