rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
96.71k stars 12.5k forks source link

Tracking issue for `const fn` `type_name` #63084

Open oli-obk opened 5 years ago

oli-obk commented 5 years ago

This is a tracking issue for making and stabilizing type_name as const fn. It is not clear whether this is sound. Needs some T-lang discussion probably, too.

Steps needed:

RalfJung commented 5 years ago

This function can change its output between rustc compilations

Can the output change between compiling a library crate, and a binary crate using that library? Or only when switching rustc versions?

oli-obk commented 5 years ago

Can the output change between compiling a library crate, and a binary crate using that library?

No, we're using global paths now.

Or only when switching rustc versions?

Yes, there needs to be a change in rustc that causes a change in output.

TankhouseAle commented 5 years ago

I might take a look at this. (I implemented the support for using the actual type_name intrinsic in const fn contexts, if you don't remember me, haha.)

I think we definitely know it works at compile time though, don't we? That's what this test I added at the time checks for.

TankhouseAle commented 5 years ago

Sorry it took me a bit to get to this. Just opened a PR.

lambda-fairy commented 5 years ago

How does this break referential transparency? I'd like to see that elaborated upon.

TankhouseAle commented 5 years ago

I might be wrong, but I think the first "checkbox" can be checked since my PR was merged?

@lfairy:

Personally, I disagree, but some people seem to think that despite the fact Rust has fully reified generics, it's somehow a "dangerous" concept for your "average Joe/Jane" to be able to access the (100%-guaranteed-correct) name of a generic parameter.

Something about "Java programmers I used to work with did Some Bad Thing and I don't want Rust programmers to also do Some Bad Thing".

I don't know anything about Java though so I'm probably not overly qualified to get too much into that.

korken89 commented 4 years ago

Hi, is there a stabilization issue for this const-ification?

oli-obk commented 4 years ago

This is the tracking issue, so it will get closed on stabilization. I don't know what the next steps for stabilizing it are though. The discussion around referential transparency needs to be had (although with specialization it'd become obsolete, because then we'd get this feature anyway, but in a less convenient way). I think posting use cases would be very helpful for the discussion. Since this works with feature gates, you can always show a use case with nightly.

korken89 commented 4 years ago

Hi, certainly I can write about a use case that I am working on!

I'm part of the Embedded WG where I am currently working on an instrumentation/logging tool for embedded targets. Here, as you probably know, we have strict requirements on code size as the micro controllers have very little memory. And a huge hog of memory is when strings are placed in memory.

To this end I place formating strings and (I want) type strings in an INFO linker section so they are available in the ELF but not loaded onto the target. That is the following code as an example:

// test1 is some variable
mylog::log!("Look what I got: {}", &test1);

//
// Expands to:
//

const fn get_type_str<T>(_: &T) -> &'static str {
    // type_name needs to be a const fn for this to work
    core::any::type_name::<T>()
}

// ugly hack to tranform `&'static str` -> `[u8; str.len()]`
union Transmute<T: Copy, U: Copy> {
    from: T,
    to: U,
}

const FMT: &'static str = "Look what I got: {}";
const TYP: &'static str = get_type_str(&test1);

// Transform string into byte arrays at compile time so 
// they can be placed in a specific memory region

// Formating string placed in a specific linker section
#[link_section = ".some_info_section"]
static F: [u8; FMT.as_bytes().len()] = unsafe {
    *Transmute::<*const [u8; FMT.len()], &[u8; FMT.as_bytes().len()]> {
        from: FMT.as_ptr() as *const [u8; FMT.as_bytes().len()],
    }
    .to
};

// Type string placed in a specific linker section
#[link_section = ".some_info_section"]
static T: [u8; TYP.as_bytes().len()] = unsafe {
    *Transmute::<*const [u8; TYP.len()], &[u8; TYP.as_bytes().len()]> {
        from: TYP.as_ptr() as *const [u8; TYP.as_bytes().len()],
    }
    .to
};

// Here we send where the strings are stored in the ELF + the data to the host 
write_frame_to_host(&F, &T, &test1)

Link script:

SECTIONS {
  .some_info_section (INFO) :
  {
    *(.some_info_section .some_info_section.*);
  }
}

By doing this the strings are available in the generated ELF but not loaded onto the target, and the type can be reconstructed through DWARF with help of the type string. And with complex types the space used for the type is getting very large for example any type that uses GenericArray (which we use extensively).

Currently the string is placed in .rodata which means that the compiler fully knows that this is a static string, and I made an issue on how to control where strings are placed which can use a trick outlined in here: https://github.com/rust-lang/rust/issues/70239 However this trick only works if the string is available in const context.

This is why I am asking about stabilization here.

That3Percent commented 4 years ago

I would also like to get this stabilized and can present a use-case.

I'm working on firestorm: a low overhead intrusive flame graph profiler. I created firestorm because existing solutions have too much overhead to be useful for Tree-Buf: the self-describing serialization system that produces files smaller than GZip faster than uncompressed formats.

Part of keeping the overhead of firestorm low is to delay string formatting/allocation until outside of the profiled region. So, there is an enum that allows for arbitrary amounts of &'static str to be concatenated. In order to keep the amount of data writing to a minimum during the execution of the profiled region, we only write &'static EventData so that EventData can be large. Supporting concatenation of fn name and struct name for a method is only possible if type_name is a const fn.

TLDR: I need this to be const fn for performance.

That3Percent commented 4 years ago

To elaborate here's code I would like to have be able to compile:

#[macro_export]
macro_rules! profile_method {
    ($($t:tt)*) => {
        let _firestorm_method_guard = {
            const FIRESTORM_STRUCT_NAME: &'static str = ::std::any::type_name::<Self>();
            let event_data: &'static _ = &$crate::internal::EventData::Start(
                $crate::internal::Start::Method {
                    signature: stringify!($($t)*),
                    typ: FIRESTORM_STRUCT_NAME,
                }
            );
            $crate::internal::start(event_data);
            $crate::internal::SpanGuard
        };
    };
}

It seems there are at least 2 problems here though. First, is that type_name is not a const fn. The second is that the compiler complains about the use of Self as the generic for the const fn. I do not really understand why that's a problem when each monomorphized function could have its own value here.

oli-obk commented 4 years ago

@That3Percent I don't think the Self part is something that can be solved at present, even if we stabilize type_name. You can test this out by using nightly and activating the feature gate for const_type_name

That3Percent commented 4 years ago

@oli-obk Yes, you are right. Both issues would need to be resolved to support my use-case in the way that I would like.

oli-obk commented 4 years ago

I found a bug with type_name: type_name works in array lengths of arrays used in associated consts, even if it is type_name::<Self>. This doesn't even work for size_of. The bug is that type_name just returns _ in that case:

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=4404a9b3635b4bf5047c2493ac9a5082

If you comment out the first panic, you get the correct type name.

RalfJung commented 3 years ago

I found a bug with type_name: type_name works in array lengths of arrays used in associated consts, even if it is type_name::. This doesn't even work for size_of. The bug is that typename just returns in that case:

This seems to be fixed now?

korken89 commented 3 years ago

Has there been any progress on the stabilization of type_name?

korken89 commented 3 years ago

Ping again on the status of stabilization?

RalfJung commented 3 years ago

type_id (https://github.com/rust-lang/rust/issues/77125) has a bunch of issues which is why its stabilization actually got reverted; not sure if those issues also apply to type_name.

Hoverbear commented 3 years ago

Would you believe I clicked the wrong link in Rustdoc? Sorry.

TheNeikos commented 1 year ago

Is there any outlook on stabilization/progress towards it?

oli-obk commented 1 year ago

There's a rendering bug and a soundness bug that we need to fix before we can stabilize. Both are linked from the main post

terrarier2111 commented 5 months ago

There's a rendering bug and a soundness bug that we need to fix before we can stabilize. Both are linked from the main post

The soundness bug seems to have been fixed by now, and all other linked issues also seem to be fixed, is there any particular roadblock remaining?

Bromeon commented 2 months ago

With the recent const {} stabilization, this would prove very useful in static assertion messages.

Are there more known roadblocks to const fn type_name<T>()?

tgross35 commented 2 months ago

Started a discussion https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/Const.20.60TypeId.3A.3Aof.60.2C.20.60TypeId.3A.3Amatches.60.2C.20.60type_name.60.2C