Giving generics traits via unsafe code

Hello! This is a really odd request/question. I need to go from a given generic T to a generic T: MyTrait, so I want to convert it to a type impl MyTrait basically. Making the generic T have a requirement to be MyTrait also isn't really possible...
So I wonder.. from compiler perspective, how hard/impossible would something like this be:

pub unsafe fn typed_downcast<T, trait X>(val: T) -> impl X;

So that I can do this:

impl Trait for Special {
    fn magic<T>(self, val: T) {
        // I know that T always implements MyTrait
        let m(: impl MyTrait) = unsafe { typed_downcast(val) };
        m.run_function();
    }
}

I think this could work because the compiler should (from my understanding, which is not much) be able to just handle it as written down for pre-monomorphization and then error when a type doesn't implement that post monomorphization. Right?

Given the post-monomorphization error you talk about, this sounds like basically a form of specialization. So this is quite similar to `const if` to supress instantiation/codegen of unreachable branches · Issue #3582 · rust-lang/rfcs · GitHub except that you are branching on "does the type implement the trait" rather than "does this constant evaluate to true".

2 Likes

I believe OP doesn't want to branch on whether the type implements the trait or not, they expect to get UB if it doesn't implement it.

1 Like

Yes, correct. I want it to behave more like a hint. (And I especially don’t want it connected with specialization :sob:)

The OP said to "error when a type doesn't implement that post monomorphization". "error" does not sound like "UB" to me. But if UB is desired, that's easy to do by just putting hint::unreachable_unchecked() in the "else" branch of a "does the type implement the trait" check.

By wanting to specialize on whether a type implements a trait or not during monomorphization, you are asking for a form of specialization, whether you like it or not. :wink:

4 Likes

Yeah thus I like calling it „hinting“ more :> I’ve been thinking about if I could do this in stable and have one idea which would be completely unsafe but could work, need to try it tho

Ughh seems impossible.. everything ends in me wanting to transmute something generic :confused:

Along those lines, sometimes because of rust bugs with higher ranked bounds it just fails to see that the type implements the trait, would be good to force it sometimes.

1 Like

I thought about the API a bit and I think something like this makes the most sense considering current language "limitations" (e.g. no generic traits):

impl Trait for Special {
    fn magic<T>(self, val: T) {
        unsafe {
            bind_type!(type MagicT = T + MyTrait); // Declares a new type which "extends" from T and implements MyTrait
            // That step is unsafe as one could bypass generic bounds on a function (like TypeId::of's T has a bound of 'static
        }
        let m(: impl MyTrait) = unsafe { ::core::mem::transmute::<_, MagicT>(val) };
        m.run_function();
    }
}

The MagicT would otherwise have all other characteristics from T, like it's lifetime and all generics.

transmute_unchecked isn't intended to ever be exposed directly — it causes ICEs and/or codegen errors if the size isn't equal, not just UB — but maybe a similar function is worth providing, for the purpose of doing by-value type system crimes.

I know that I've used a helper like this multiple times:

/// Like `transmute`, but for generic code. 
///
/// If `Src` is smaller than `Dst`, errors when monomorphized. 
/// If `Src` is larger than `Dst`, the byte prefix of `src` is transmuted.
///
/// # Safety
///
/// The first `size_of::<Dst>()` bytes of `src` must be a valid `Dst`.
pub unsafe fn transmute_prefix<Src, Dst>(src: Src) -> Dst {
    const { assert!(size_of::<Dst>() <= size_of::<Src>()) };
    unsafe { transmute_copy(&ManuallyDrop::new(src)) }
}

Whut? I’m proposing normal transmute here, which the user should use themselves, as the compiler should infer that both types have the same size (being the same underlying type, just with another trait bound)

There's always transmute_copy :wink:

huh? No the problem is that I need to name the Dst type :sob: That's what I am proposing here all along, somehow making rust aware of a type which is basically another already given type but just hinting it having another trait bound

Sooo.. should I just make an RFC and see where it goes then? O.o

I don’t think a new type is what you want here? You want it to be usable in all the places the existing type is.

I think one solution would be the ability to specify bounds that are assumed to hold when type-checking the inside of a function, but aren't checked when calling the function.

You mean like an „assume this type has that bound“-area?

1 Like

Yeah I get that, but I feel like that's hard to integrate in current rust. Like best I could imagine is making a macro which wraps around such an "area"

Alternatively, the magic macro could wrap the where clause.

You mean like

assume!({
    // ....
} where
    T: MyTrait
);

?

No, on the outside of a function:

unsafe fn do_thing<T>(param: T)
where
     unsafe_assume!(T: Trait),
{
    ...
}
1 Like