File: dyn-erasure-no-tait.rs

package info (click to toggle)
rustc 1.85.0%2Bdfsg3-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental, forky, sid, trixie
  • size: 893,396 kB
  • sloc: xml: 158,127; python: 35,830; javascript: 19,497; cpp: 19,002; sh: 17,245; ansic: 13,127; asm: 4,376; makefile: 1,051; perl: 29; lisp: 29; ruby: 19; sql: 11
file content (53 lines) | stat: -rw-r--r-- 1,354 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//@ known-bug: #112905
//@ check-pass

// Classified as an issue with implied bounds:
// https://github.com/rust-lang/rust/issues/112905#issuecomment-1757847998

/// Note: this is sound! It's the "type witness" pattern (here, lt witness).
mod some_lib {
    use super::T;

    /// Invariant in `'a` and `'b` for soundness.
    pub struct LtEq<'a, 'b>(::std::marker::PhantomData<*mut Self>);

    impl<'a, 'b> LtEq<'a, 'b> {
        pub fn new() -> LtEq<'a, 'a> {
            LtEq(<_>::default())
        }

        pub fn eq(&self) -> impl 'static + Fn(T<'a>) -> T<'b> {
            |a| unsafe { ::std::mem::transmute::<T<'a>, T<'b>>(a) }
        }
    }
}

use some_lib::LtEq;
use std::{any::Any, cell::Cell};

/// Feel free to choose whatever you want, here.
type T<'lt> = Cell<&'lt str>;

fn exploit<'a, 'b>(a: T<'a>) -> T<'b> {
    let f = LtEq::<'a, 'a>::new().eq();
    let any = Box::new(f) as Box<dyn Any>;

    let new_f = None.map(LtEq::<'a, 'b>::eq);

    fn downcast_a_to_type_of_new_f<F: 'static>(any: Box<dyn Any>, _: Option<F>) -> F {
        *any.downcast().unwrap_or_else(|_| unreachable!())
    }

    let f = downcast_a_to_type_of_new_f(any, new_f);

    f(a)
}

fn main() {
    let r: T<'static> = {
        let local = String::from("…");
        let a: T<'_> = Cell::new(&local[..]);
        exploit(a)
    };
    dbg!(r.get());
}