New issue

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

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

Already on GitHub? Sign in to your account

Expose the type_name intrinsic #1428

Open
sfackler opened this Issue Dec 27, 2015 · 83 comments

Comments

Projects
None yet
@sfackler
Copy link
Member

sfackler commented Dec 27, 2015

We have an intrinsic which returns a &'static str naming a type but it's not exposed in a safe/stable manner anywhere: http://doc.rust-lang.org/1.5.0/std/intrinsics/fn.type_name.html

Something like this should suffice:

pub fn type_name<T: Reflect + ?Sized>() -> &'static str {
     unsafe { std::intrinsics::type_name::<T>() }
}

@sfackler sfackler added the T-libs label Dec 27, 2015

@nagisa

This comment has been minimized.

Copy link
Contributor

nagisa commented Dec 29, 2015

What is the usecase for this?

Probably would be good for diagnostics/panic messages in libraries. E.g. .unwrap() would be so much better if it said “called unwrap() on a None variant of Option<MonoType>” IMO. Exposing this would allow making similar messages possible in libraries.

@sfackler why the Reflect bound?

@sfackler

This comment has been minimized.

Copy link
Member

sfackler commented Dec 29, 2015

We use bounds like Any and Reflect to make sure that people are opting into paramicity violations. Getting a name of a type might be harmless enough that we could drop the bound, though.

Thoughts, @rust-lang/libs?

@durka

This comment has been minimized.

Copy link
Contributor

durka commented Dec 30, 2015

I think you would have to drop the bound for that example to be workable, no? Option::<T>::unwrap would need to add a T: Reflect bound and that's a breaking change AFAIK. Edit: guess not.

@Gankro

This comment has been minimized.

Copy link
Contributor

Gankro commented Dec 30, 2015

I would ditch the reflect bound, but I'm not actually that familiar with our current reflection strategy. If this makes it incoherent, that's bad.

I am slightly terrified that people will use this to impl adhoc name-based specialization.

@sfackler

This comment has been minimized.

Copy link
Member

sfackler commented Dec 30, 2015

You can already do that in a less messed up way by comparing TypeIds.

@jonas-schievink

This comment has been minimized.

Copy link
Contributor

jonas-schievink commented Dec 30, 2015

@ticki macros don't have type information and don't work in generic contexts

@aturon

This comment has been minimized.

Copy link
Member

aturon commented Dec 31, 2015

@sfackler It needs the Reflect bound, or else Reflect really isn't doing much for us :)

That said, Reflect is likely going away anyway, if specialization lands.

@jonas-schievink

This comment has been minimized.

Copy link
Contributor

jonas-schievink commented Dec 31, 2015

OTOH mem::size_of already violates parametricity

@aturon

This comment has been minimized.

Copy link
Member

aturon commented Dec 31, 2015

@jonas-schievink Indeed, but in a much "lossier" way. (FWIW, I'm not terribly fond of the whole Reflect business in any case, but until we deprecate it, we should at least not introduce new leaks.)

@brson

This comment has been minimized.

Copy link
Contributor

brson commented Jan 6, 2016

I don't even see anywhere in-tree where this is used except test cases. It was added in rust-lang/rust@e256b7f but even then didn't appear to be used.

Please lets come up with a concrete use case and use it, not just add the feature speculatively.

@sfackler

This comment has been minimized.

Copy link
Member

sfackler commented Jan 6, 2016

@brson I opened this issue because it was a thing I wanted to use for a concrete use case.

Specifically, rust-postgres reports this error when the user attempts to convert a Postgres value to an incompatible Rust value or vice versa, for example a Postgres VARCHAR to a Rust i32, but right now all it can say is that a value of type VARCHAR could not be converted to... something. If type_name was stable, I could stick a &'static str in that error type and be set.

@ranma42

This comment has been minimized.

Copy link
Contributor

ranma42 commented Jan 6, 2016

I would expect more something like

 // no need for Reflection, resolved at compile time, possibly make it even const
pub fn type_name<T>() -> &'static str
 // resolved dynamically, hence needs reflection, no obvious need for size bound
pub fn type_name_of_val<T: Any>(val: &T) -> &'static str

The former would be convenient when generating error messages as pointed out by @nagisa.
I expect the latter to be used in conjunction with Any, in order to provide a better error message when a downcast_{ref,mut} fails (in a way, this would provide a "string" version of TypeId):

pub fn downcast<'a, T: Any>(x: &'a Any) -> Result<&'a T, String> {
    match x.downcast_ref() {
        Some(y) => Ok(y),
        None => Err(format!("got {}, expected {}", type_name_of_val(x), type_name<T>())),
    }
}
@glaebhoerl

This comment has been minimized.

Copy link
Contributor

glaebhoerl commented Jan 7, 2016

@ranma42 You can implement type_name_of_val using type_name without requiring an Any constraint, so I don't think this is the right distinction to make.

One distinction I think we should make is between things which exist for debugging/development purposes and things which are used for implementing "actual/intended program behavior". I think it's totally fine to pierce whatever abstraction boundaries you want in the former case, and not at all in the latter. Of course, how to prevent things intended for the former from being abused for the latter is an interesting question, but even Haskell has Debug.Trace which does IO inside pure code, but "only for debugging purposes".

So that then raises the question of what type_name is for. If it's mainly a debugging aid (I think you could plausibly argue that emitting better error messages falls in this category, even if that's something which is observable runtime behavior), then having it be constraint-free seems fine. On the other hand, if actual program logic is going to rely on it in some way, then I strongly believe it should require a Reflect/Any constraint (this kind of thing is what they exist for).

@ranma42

This comment has been minimized.

Copy link
Contributor

ranma42 commented Jan 7, 2016

@glaebhoerl I might be missing something (in particular, the signature I used on the function is possibly wrong), but in my mind the purpose for type_name_of_val would be to obtain the type of a value wrapped in a fat pointer. This type might depend on the actual execution (for example a &Any reference might point to an Option::<u32> or to a u32 indifferently). How could this be implemented using type_name?

@glaebhoerl

This comment has been minimized.

Copy link
Contributor

glaebhoerl commented Jan 7, 2016

Ah. In that case you probably wanted to write pub fn type_name_of_val(val: &Any) -> &'static str.

@rphmeier

This comment has been minimized.

Copy link

rphmeier commented Jan 15, 2016

I also have a use-case for this (entity component system: asserting that each entity with a certain component has all of that component's dependencies). I can currently print a message along the lines of "Entity {} failed to fulfill all its dependencies", which isn't the most user-friendly.

@durka

This comment has been minimized.

Copy link
Contributor

durka commented Feb 4, 2016

One way to make the "only for debugging purposes" @glaebhoerl wants is to simply enforce it, by exposing a new fn like this:

#[cfg(debug_assertions)]
pub fn debug_typename<T>() -> &'static str {
    unsafe {
        intrinsics::type_name::<T>()
    }
}

#[cfg(not(debug_assertions))]
pub fn debug_typename<T>() -> &'static str {
    "[no type information]"
}

It doesn't need to be a macro, but it could be -- for some reason it feels better to have a macro that changes behavior at runtime (like debug_assert!) than a function. Also, if this function/macro is in core then intrinsics::type_name doesn't have to be stabilized.

@Gankro

This comment has been minimized.

Copy link
Contributor

Gankro commented Feb 4, 2016

@durka sadly this doesn't work quite right because cfgs are statically evaluated during compilation of the library. In this case, the standard library. So they would have to be using a custom "debug std" to see these names. Although perhaps this is what you're going for?

@durka

This comment has been minimized.

Copy link
Contributor

durka commented Feb 4, 2016

@Gankro meh, you're right. So that's the real reason it would be a macro :) -- that way it could expand into a #[cfg(debug_assertions)] in the user crate.

macro_rules! debug_typename {
    ($t:ty) => {
        (
            #[cfg(not(debug_assertions))] "[no type information]",
            #[cfg(debug_assertions)] unsafe { ::std::intrinsics::type_name::<$t>() }
        ).0
    }
}

@brson brson closed this Mar 1, 2016

@brson brson reopened this Mar 1, 2016

@brson

This comment has been minimized.

Copy link
Contributor

brson commented Mar 1, 2016

I wanted to use this recently to add some instrumentation to std. Though that doesn't require stabilization I admit this is useful :)

@jdm

This comment has been minimized.

Copy link

jdm commented May 10, 2016

I found this useful for adding meaningful debug logging to a function that accepts a generic type, where I was only concerned about figuring out if it was being called for certain types.

@sfackler

This comment has been minimized.

Copy link
Member

sfackler commented May 10, 2016

I'd like to add a stable wrapper on top of this, but I'm not totally sure where it should go. std::any seems like the least-wrong place?

@nagisa

This comment has been minimized.

Copy link
Contributor

nagisa commented Jun 29, 2016

We wanted to use this intrinsic before for pass names in MIR pipeline, but it seemed to return fully qualified pathes for foreign types, where all we hoped for was just the name of type.

It should either:

  • have an argument specifying whether the type_name returns fully qualified path; or
  • be renamed; or
  • be fixed to only ever return the type name only.
@sfackler

This comment has been minimized.

Copy link
Member

sfackler commented Jun 29, 2016

It may be overkill, but it could return a struct with the path, name, and generic arguments all split out.

@rphmeier rphmeier referenced this issue Feb 10, 2017

Merged

Serde 0.9 #4508

@mitsuhiko

This comment has been minimized.

Copy link
Contributor

mitsuhiko commented Dec 29, 2018

I still really, really want this intrinsic stabilized but I'm not sure what the path forward here is at this point. Is there something that could be done to move this towards API availability?

@Centril

This comment has been minimized.

Copy link
Contributor

Centril commented Dec 29, 2018

Is there something that could be done to move this towards API availability?

Help with the design and stabilization of specialization.

@mitsuhiko

This comment has been minimized.

Copy link
Contributor

mitsuhiko commented Dec 29, 2018

@Centril so this is blocked until we know that specialization works or does not work, because if specialization does not work parametricity becomes an option again and would prevent this feature from being ever stabilized?

@Centril

This comment has been minimized.

Copy link
Contributor

Centril commented Dec 29, 2018

@mitsuhiko We already lost parametricity for generic functions with 'static bounds on their type parameters. What remains is some portion of parametricity for parameters that don't have these bounds (except for size_of, align_of and such operations that violate parametricity but in relatively harmless ways...).

When we stabilize specialization we lose what remains, and so type_name does not remove any properties from the type system that specialization hasn't already removed. When that happens type_name therefore becomes OK for me to stabilize in some form (we'll still need to work out the right interface for type_name.. maybe something like in #1428 (comment) or something else...).

would prevent this feature from being ever stabilized?

Yes.

@sfackler

This comment has been minimized.

Copy link
Member

sfackler commented Dec 29, 2018

This seems completely orthogonal to specialization - I don't see how this would block on that in any way. There are quite a few non-parametric functions in the standard library already.

@SimonSapin

This comment has been minimized.

Copy link
Contributor

SimonSapin commented Dec 30, 2018

would prevent this feature from being ever stabilized?

Yes.

As far as I understand this is your personal opinion, @Centril. I don’t know of any team’s decision on this either way. My personal opinion is that this feature is more valuable than parametricity, and we should stabilize it regardless of what happens with specialization.

@SimonSapin

This comment has been minimized.

Copy link
Contributor

SimonSapin commented Dec 30, 2018

This is a three-years-old tracking issue for useful functionality that has been around since roughly forever.

This thread has proposals to change the return type to a structure (I assume similar to https://docs.rs/syn/0.15.23/syn/struct.Path.html), or to show the concrete type behind a trait objects. These could be added later as new APIs, so I don’t think they need to block stabilizing what’s already implemented.

The only real concern has been the loss of parametricity, in case specialization never happens.

At this point, I believe there is well enough desire for specialization that we’ll find a way to make it happen, in one form or another, although it may take time. In the mean time, I don’t think we should block already-implemented useful functionality, just in case we potentially give up on specialization entirely.

Regardless of what happens to specialization, we already have Any, mem::discriminant, size_of, and possibly other existing features that weaken/remove parametricity in Rust.

Finally there is already extensive discussion in the Rust around the removal of the Reflect trait and acceptance of specialization about moving away from parametricity, and whether it is actually useful to Rust.

Given this repeated precedent, I think the onus of proof is on people arguing that we should “retain what little we have left” to show why that is valuable.

As such, I am formally proposing that we add a safe stable wrapper function for fn type_name::<T: ?Sized>() -> &'static str (in a module to be determined), despite this concern.

@rfcbot fcp merge

@rfcbot

This comment has been minimized.

Copy link

rfcbot commented Dec 30, 2018

Team member @SimonSapin has proposed to merge this. The next step is review by the rest of the tagged team members:

Concerns:

Once a majority of reviewers approve (and none object), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@dtolnay

This comment has been minimized.

Copy link
Member

dtolnay commented Dec 30, 2018

LGTM as long as we make the same caveat that we do for Debug impls -- the exact representation is subject to change and we will break your code if you expect it not to change.

@bluss

This comment has been minimized.

Copy link

bluss commented Dec 30, 2018

In particular, how will this work with the module part of the name? I suppose it's too early to guarantee any particular path (for example, would the type name of a Vec<i32> be "std::prelude::v1::Vec<i32>" or "std::vec::Vec<i32>" or "alloc::vec::Vec<i32>" ?). This can be a source of de facto breakage in later versions. Imagine, we want to refactor and move the Vec type into private module alloc::vec::internal::Vec and of course reexport it at the old location. Would this "break" the type name?

@dtolnay

This comment has been minimized.

Copy link
Member

dtolnay commented Dec 30, 2018

Potentially surprising: the same type can have different type_names in different places:

#![no_std]
#![feature(core_intrinsics)]

pub fn p() -> &'static str {
    unsafe {
        core::intrinsics::type_name::<dyn Fn()>()
    }
}

pub fn q<T: ?Sized>() -> &'static str {
    unsafe {
        core::intrinsics::type_name::<T>()
    }
}
#![feature(core_intrinsics)]

fn main() {
    println!("{}", lib::p());
    println!("{}", lib::q::<dyn Fn()>());
}
dyn core::ops::Fn()
dyn std::ops::Fn()
@Gankro

This comment has been minimized.

Copy link
Contributor

Gankro commented Dec 30, 2018

As far as I understand, the only use of this feature that we actually want to support is better logging/debugging info, in which case the different names shouldn't be a serious issue. It might make it a bit harder to aggregate crash messages in a large system, but that's pretty niche, I think?

@Centril

This comment has been minimized.

Copy link
Contributor

Centril commented Dec 30, 2018

@nagisa noted that:

FWIW, I have a very incomplete RFC draft lying around for a structural type_name, but it is so low on my TODO list that I’ll probably never get back to it.

which suggests to me that the API proposed for stabilization might not be optimal.

@rfcbot concern is-this-the-right-api?


As @dtolnay and @bluss noted, it's not clear from the proposal what the guarantees around the API type_name are... what changes may the compiler team do to the output of this function? Until this is resolve, let's note:

@rfcbot concern unclear-guarantees-and-underspecified

The results above due to @dtolnay also points at incoherence. In particular, this shows that you cannot rely on type_name giving globally consistent results. That does seem surprising...


@rfcbot concern premature-loss-of-remaining-parametricity

First, I note that @nagisa wrote:

I agree that this functionality definitely does not carry its weight at the moment and there’s no particular rush to stabilise it before the larger, more interesting and impactful features. Sure, it is nice to have, but it does not block anything that was not possible until today in library with a well thought out trait.

I agree with this assessment.

At this point, I believe there is well enough desire for specialization that we’ll find a way to make it happen, in one form or another, although it may take time.
In the mean time, I don’t think we should block already-implemented useful functionality, just in case we potentially give up on specialization entirely.

I do not agree with this way of dealing with type system properties. Specialization has proven itself to be tricky over time wrt. soundness and has problematic interactions with other proposed features. While I'd like for specialization to happen, and I think we should focus efforts into doing that, the risk is too great here.

Regardless of what happens to specialization, we already have Any, mem::discriminant, size_of,
and possibly other existing features that weaken/remove parametricity in Rust.

Yes, we do. And you have essentially type-case for all type parameters bounded (implied or explicitly) by 'static. However, as many do not want to bound their type parameters with 'static, to be more generic,
I believe that parametricity still applies to sufficient extent to be more useful than type_name.

Finally there is already extensive discussion in the Rust around the removal of the Reflect trait and acceptance of specialization about moving away from parametricity, and whether it is actually useful to Rust.

Given this repeated precedent, I think the onus of proof is on people arguing that we should “retain what little we have left” to show why that is valuable.

I've written about this to some extent here: #1428 (comment)
When specialization was first considered, @glaebhoerl noted that:

But the specific concern was whether we understand why Haskellers consider it so valuable, and this continues to hold.

From what I can tell, this concern was never adequately addressed or even at all. Seeing the value of parametricity doesn't just extend to Haskellers. For example, Idris and Agda prohibit pattern matching on Type which means that you have parametricity for types.

I would also note that one specific rationale that Aaron brought up in the discussions on specialization was:

His perspective is that, first of all, parametricity tells you more in a language without side-effects, like Haskell.

Given that we have const fn, this rationale for Rust no longer holds.

Furthermore, as for the usefulness of parametricity, it's not just about cute "free theorems" about knowing that fn ??<T>(x: T) -> T { ?? } is the identity function or will diverge. The value it brings is that more generally you can learn things about a function solely from its signature which makes types as a means of documentation more valuable. This also makes it possible to write a generic algorithm without having to fear whether someone special cased something in a weird way somewhere deep in your call stack. Or as noted in this paper:

Parametricity can be thought of as the dual to abstraction.
Where abstraction hides details about an implementation from the outside world,
parametricity hides details about the outside world from an implementation.

This makes it possible to reduce the overall reasoning-footprint of code.

@sfackler

This comment has been minimized.

Copy link
Member

sfackler commented Dec 30, 2018

which suggests to me that the API proposed for stabilization might not be optimal.

The use case I had in mind for type_name when I added it was very simple - an aid to diagnostics. For example, we could use it to produce errors in rust-postgres that say "unable to convert from the Postgres type TEXT to the Rust type Vec<u32>" rather than "unable to convert from the Postgres type TEXT to some Rust type". I don't really care if that message contains Vec<u32>, std::vec::Vec<u32>, alloc::vec::Vec<u32> or whatever.

None of that requires the ability to crawl the type name's AST, and I do not understand what use cases would want to do that.

While I'd like for specialization to happen, and I think we should focus efforts into doing that, the risk is too great here.

What is the specific, concrete, risk of the existence of this function, and how does that have anything to do with specialization? This API does not use specialization, and cannot be implemented in terms of specialization.

However, as many do not want to bound their type parameters with 'static, to be more generic,
I believe that parametricity still applies to sufficient extent to be more useful than type_name.

What is the specific, concrete, usefulness of parametricity in non-static type parameters today? Are functions with 'static bounds on their generics actually more scary to use in practice? Are there large swaths of code that do bad things by being non-parametric with 'static type bounds?

The value it brings is that more generally you can learn things about a function solely from its signature which makes types as a means of documentation more valuable.

Why should I care how much I can learn from a function's signature in isolation? It's sitting right next to the function's name and the actual documentation!

This also makes it possible to write a generic algorithm without having to fear whether someone special cased something in a weird way somewhere deep in your call stack.

There is an infinite amount of weird things that bad codebases can do. Don't use codebases you don't trust to not do bad things. Someone could system("rm -rf /") deep in my call stack as well, and the type signatures I'm looking at won't tell me that.

@mitsuhiko

This comment has been minimized.

Copy link
Contributor

mitsuhiko commented Dec 30, 2018

My interest is diagnostics and reporting to upstream crash reporters. I’m absolutely okay with types not being guaranteed anything about stability or format. Similar restrictions already apply to function names in the callstack and we have to deal with that.

@glaebhoerl

This comment has been minimized.

Copy link
Contributor

glaebhoerl commented Dec 30, 2018

Is it consensus that this function is only intended for debugging and error-printing purposes, or is that so far only some peoples' opinion?

If it's consensus, maybe we should tweak the API to make it more explicitly apparent. Call it type_name_for_debug or debug_type_name, or put it under some appropriate module (e.g. debug) if one exists (but I don't think it does?), or perhaps make a struct TypeName<T> ZST and implement Debug for it (and perhaps Display in the future, if we ever change our minds about "just for debug").

As I've apparently already written a long time ago, I think "only for debugging" also offers us a principled rationale to ignore the parametricity violation and potentially bridge that impasse. After all, gdb can also trivially be used to violate any abstraction boundary. The purpose of type system properties and restrictions is to regulate the interaction of the program with itself and with its environment; not with the developer. Haskell also has Debug.Trace, which is a "blatant" violation of purity, justified on similar grounds. In both cases it's possible to abuse these for non-debugging-related purposes, but we expect users not to do that, and would tell them so in the documentation.

@SimonSapin

This comment has been minimized.

Copy link
Contributor

SimonSapin commented Dec 30, 2018

I believe that is the consensus among people in this thread who are finding this feature useful and want it stabilized.

FWIW, I have a very incomplete RFC draft lying around for a structural type_name, but it is so low on my TODO list that I’ll probably never get back to it.

which suggests to me that the API proposed for stabilization might not be optimal.

"very incomplete draft lying around that I’ll probably never get back to" does not sound like a strong use case to me.

@nagisa

This comment has been minimized.

Copy link
Contributor

nagisa commented Dec 30, 2018

I believe that is the consensus among people in this thread who are finding this feature useful and want it stabilized.

That seems to be the case, yes. However it is fairly easy to imagine people doing things that are not supported (e.g. trying to parse the type returned from type_name). This was proposed elsewhere at some point, despite general consensus being that the format of type_name is not stable at all.


FWIW, I have a very incomplete RFC draft lying around for a structural type_name, but it is so low on my TODO list that I’ll probably never get back to it.

which suggests to me that the API proposed for stabilization might not be optimal.

"very incomplete draft lying around that I’ll probably never get back to" does not sound like a strong use case to me.

I wrote the chunk of said draft during the all-hands last year, but then found myself distracted by more interesting and wider-reaching problems. I have to allocate my finite time somehow, and type_name was one of the things that ended being cut.

I still do think that some change to the function is necessary before it is stabilised. For all it is worth it could be something similar to what we have done to std::mem::discriminant by having type_name return an opaque wrapper with certain standard trait implementations. That seems like an easy change to make which would keep ourselves open to changing this intrinsic in more ways than otherwise would be viable.

The compiler is a long-time user of this intrinsic. The current use of the intrinsic ended up being post-processed (in a very crude way) to produce the desired output. This is the sort of grey area, where I’m not sure whether it still considered "debugging" or not. If we had to do that in the compiler, I’m sure people will end up doing the same with the stable intrinsic and then come back complaining that stuff broke what their paying customers see when its output changes.

@SimonSapin

This comment has been minimized.

Copy link
Contributor

SimonSapin commented Dec 30, 2018

an opaque wrapper with certain standard trait implementations

The relevant trait would be Debug or Display. This does not feel very different from a &'static str.

@nagisa

This comment has been minimized.

Copy link
Contributor

nagisa commented Dec 30, 2018

It allows to make changes to the type (making it structural, adding methods to change it output, etc) in a backwards-compatible manner down the road.

@mitsuhiko

This comment has been minimized.

Copy link
Contributor

mitsuhiko commented Dec 30, 2018

For what it’s worth I see no benefit in a wrapper trait but it makes using it harder. The fact that it’s a static str right now is really convenient in certain APIs that already require a str.

@mitsuhiko

This comment has been minimized.

Copy link
Contributor

mitsuhiko commented Dec 30, 2018

As an example the new Fail::name returns a &str

@SimonSapin

This comment has been minimized.

Copy link
Contributor

SimonSapin commented Dec 30, 2018

Stabilizing type_name that returns &str does not prevent adding another function later that returns a more complex type.

@Ekleog

This comment has been minimized.

Copy link

Ekleog commented Dec 30, 2018

Returning a type that implements Debug has the advantage that the rule “Debug output is not supposed to be stable” is already well-known, while returning &'static str would require adding another rule explicitly for type_name, and thus increase the likelihood of someone misusing it.

@joshtriplett

This comment has been minimized.

Copy link
Member

joshtriplett commented Dec 30, 2018

I'd love to have this available for debugging purposes.

@scottmcm

This comment has been minimized.

Copy link
Member

scottmcm commented Jan 5, 2019

While I don't really want to start a bikeshed, I didn't see a discussion of where this should live or what it should be called. (I assume we still don't want to stabilize anything new in intrinsics...)

@mitsuhiko

This comment has been minimized.

Copy link
Contributor

mitsuhiko commented Jan 5, 2019

For the std::error evolution we also want to expose a Backtrace object. I feel like these are related to each other. I would propose to add a std::debug or similar for both this and Backtrace once it lands. Once it's in a debug module I think the name type_name for the function is also not bad.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment