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
Add new builtin: @typeId #19858
Comments
One small request: it would be really nice if this returned |
It could also take inspiration from the |
There is actually an at-comptime solution for fn typeId(comptime T: type) u32 {
return @intFromError(@field(anyerror, @typeName(T)));
} Bad status quo solutions have helped back changes such as this one before so just wanted to share. :) |
@sno2 Using RLS here is a bit tricky. There are two options:
If this proposal is accepted, I definitely think the returned integer should have a fixed size (probably 32 bits). 32 bits is a sweet spot: 16 is too few to represent the number of types which might exist in a large application, but 64 (or usize) is very excessive (in fact, the canonical compiler implementation won't let you have more than 2^32 distinct types today, and I am beyond certain nobody will ever hit this limitation). |
I was more thinking of the compiler counting how many types we have defined and log2 that into an integer type as the Also, |
I can't think of a use case I would ever use this builtin for (it's a bit at odds with my fundamental design philosophy), but for everyone here who seems to have use cases:
That multiplies into a really big number. I would expect many use cases actually only use this builtin when serializing a rather small set of types (and maybe their fields' types, recursively) over particular interfaces. (Then again, maybe this is more of an ergonomics feature than a performance-oriented one? |
Could someone give a solid use-case for this, I have nothing in my head. And never came across situation I need this even remotely. |
The one reason I've wanted it in the past is for safety on const AnyPtr = struct {
type_id: usize, // alternatively, [*:0]u8
ptr: *anyopaque,
pub fn from(item: AnyPtr, comptime T: type, ptr: *T) AnyPtr {
return .{
.type_id = @typeId(T), // alternatively @typeName(T).ptr
.ptr = @ptrCast(@alignCast(ptr)),
};
}
pub fn readAs(item: AnyPtr, comptime T: type) *T {
if(item.type_id != @typeId(T)) unreachable; // alternatively `item.type_id != @typeName(T).ptr`
return @ptrCast(@alignCast(item.ptr));
}
}; |
Basically type checking (see linked any-pointer project) when doing type erasure, then see #19859 where you need to store a user-defined type in a non-generic datastructure (think |
As @MasterQ32 indicated in the original issue, his Of course, all of these issues can be solved with userspace hacks but:
To not use any hacks while obtaining unique type identifiers, you can do something like this, but:
In my opinion, any sort of RTTI-ish solution would greatly benefit from this builtin. I imagine Felix sees it the same way, thus why he opened this issue. About implementation details @rohlem, check out my PR to see how easy it is to implement from the InternPool. In short, the InternPool stores types (and other deduplication-dependent data like default values, memoized calls, etc. though this is not important for this explanation) by inserting them into a |
This made me wonder about how the technical implementation would solve something like this (I'm pretending like fn SelfReferentialStruct(comptime T: type) type {
return struct {
const Self = @This();
const array: [@typeId(Self)] u32 = undefined;
};
} edit: Nevermind. I just checked and there already is a check for similar transitive failures in the compiler. :) |
Am I correct that the idea os basically to split pointer to If that's correct, that's very interesting feature. But I would like to extend it even further. If it's stored separately, we can save this information to disk and restore back. But only if we have stable guarantee not only within one build. Do I'd to take into account this feature too with this proposal. |
After sharing my first terrible pub fn typeId(comptime T: type) u32 {
_ = T;
const fn_name = @src().fn_name;
return std.fmt.parseInt(u32, fn_name[std.mem.lastIndexOfScalar(u8, fn_name, '_').? + 1 ..], 10) catch unreachable;
} This one even exposes the |
This is not practical or even really possible. You can already assign explicit IDs to types manually, through a variety of methods, which is a much better option for serialization usecases. |
Add a new builtin called
@typeId
:This builtin returns a unique integer for each type passed, and will return the same integer for the same type.
The return value must not be consistent inbetween builds, so a second build might return completly different numbers
for the same types.
An alternative variant might return
u32
oru64
to have a stable interface between different platforms.Use cases
Prior art:
User-land implementation
The following version is runtime only, as we can't perform intFromPtr at compiletime:
The text was updated successfully, but these errors were encountered: