Skip to content

Comprison of TypeIds in const context #73900

New issue

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

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

Already on GitHub? # to your account

Open
WaffleLapkin opened this issue Jun 30, 2020 · 9 comments
Open

Comprison of TypeIds in const context #73900

WaffleLapkin opened this issue Jun 30, 2020 · 9 comments
Labels
A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) S-blocked Status: Blocked on something else such as an RFC or other implementation work.

Comments

@WaffleLapkin
Copy link
Member

Is there any way to compare 2 TypeIds in const context? (It's possible to create them in const context, and it's even soon to be stabilized)

What I want is a static check for type (un)equality without auto traits:

// will fail with index out of bounds if T == U
const _: () = [()][(TypeId::of::<T>() == TypeId::of::<U>()) as usize];

(this exact code currently fails to compile with "calls in constants are limited to constant functions, tuple structs and tuple variants" error, because == isn't const)

Maybe it's a good idea to add API like the following to TypeId?

impl TypeId {
    const fn eq(&self, rhs: &Self) -> bool {
        self.t == rhs.t
    }
}

I could work on this, though I haven't seen similar apps in std, so I wanted to ask about it before doing anything.

@KodrAus
Copy link
Contributor

KodrAus commented Jul 1, 2020

There's a snippet in the stabilization PR that compares two type ids, but it does it using match:

const USIZE: TypeId = TypeId::of::<usize>();

match TypeId::of::<Self>() {
    // Use a closure for `usize` that transmutes the generic `Self` to
    // a concrete `usize` and dispatches to `Self::usize`.
    USIZE => |x| Self::usize(unsafe { &*(x as *const Self as *const usize) }),
    // For other types, dispatch to the generic `Self::default`.
    _ => Self::default,
}

@WaffleLapkin
Copy link
Member Author

Oh, I've missed it 🤦 Then I think this issue is not critical at all. Thanks.

However match won't work with generics, you can't write code like this:

const fn type_eq<A: 'static, B: 'static>() -> bool {
    let A = TypeId::of::<A>();
    const B: TypeId = TypeId::of::<B>(); // error[E0401]: can't use generic parameters from outer function
    match A {
        B => true,
        _ => false,
    }
}

(playground)

@nbdd0121
Copy link
Contributor

nbdd0121 commented Jul 1, 2020

I think this is blocked by #67792, which would allow PartialEq implementation of TypeId to be marked as const.

@rustbot modify labels: A-const-fn S-blocked

@rustbot rustbot added A-const-fn S-blocked Status: Blocked on something else such as an RFC or other implementation work. labels Jul 1, 2020
@WaffleLapkin
Copy link
Member Author

We could add stand-alone function and then replace it by const imply. (if we'll do this before the stabilization of stand-alone fns, it won't be even a breaking change)

@rodrimati1992
Copy link
Contributor

rodrimati1992 commented Jul 2, 2020

You can use associated constants to get the TypeId of a generic type:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=50e369ee80dcbceab6563d183cf8c894

#![feature(const_type_id)]

use std::any::{type_name, TypeId};

pub struct GetTypeId<T>(T);

impl<T: 'static> GetTypeId<T> {
    pub const VALUE: TypeId = TypeId::of::<T>();
}

#[macro_export]
macro_rules! typeid {
    ($t:ty) => {
        $crate::GetTypeId::<$t>::VALUE
    };
}

const fn same_type<T: 'static, U: 'static>() -> bool {
    match typeid!(T) {
        typeid!(U) => true,
        _ => false,
    }
}

fn print_if_equal<T: 'static, U: 'static>() {
    if same_type::<T, U>() {
        println!("{} == {}", type_name::<T>(), type_name::<U>());
    } else {
        println!("{} != {}", type_name::<T>(), type_name::<U>());
    }
}

fn main() {
    print_if_equal::<usize, u32>();
    print_if_equal::<usize, usize>();
}

Edit:
I just noticed that the match doesn't work, it prints this:

usize != u32
usize != usize

Reported this in #73976

@WaffleLapkin
Copy link
Member Author

Then I'll go ahead and close this issue, technically comparison can be done. The only problem is a bug reported in #73976

I've actually used similar hack with assoc const, how I could forget... anyway, thanks @rodrimati1992

@nbdd0121
Copy link
Contributor

@WaffleLapkin The pattern in #73976 is now forbidden. However you can use this transmute hack:

#![feature(const_fn)]
#![feature(const_type_id)]

use std::any::TypeId;
use std::any::type_name;
use std::mem::transmute;

const fn same_type<A: 'static, B: 'static>() -> bool {
    unsafe { transmute::<_, u64>(TypeId::of::<A>()) == transmute::<_, u64>(TypeId::of::<B>()) }
}

fn print_if_equal<T: 'static, U: 'static>() {
    if same_type::<T, U>() {
        println!("{} == {}", type_name::<T>(), type_name::<U>());
    } else {
        println!("{} != {}", type_name::<T>(), type_name::<U>());
    }
}

fn main() {
    print_if_equal::<usize, u32>();
    print_if_equal::<usize, usize>();
}

@WaffleLapkin
Copy link
Member Author

@nbdd0121 it's actually a de jure UB since TpeId has neither #[repr(C)] nor #[repr(transparent)], so I don't think it's an "ok" solution.

(reopening the issue since the pattern from #73900 (comment) is now forbidden)

@WaffleLapkin WaffleLapkin reopened this Jul 27, 2020
@raldone01
Copy link
Contributor

See this pr for more information #101698.

@RalfJung RalfJung added A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) and removed A-const-fn labels Dec 1, 2024
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) S-blocked Status: Blocked on something else such as an RFC or other implementation work.
Projects
None yet
Development

No branches or pull requests

7 participants