-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Add std.meta #1662
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 std.meta #1662
Conversation
Sync with upstream master
Sync to latest
Ziggurat somehow did not get updated to latest syntax
f32 float casts somehow not updated to latest syntax
Sync with upstream
std/meta/index.zig
Outdated
/// Otherwise it returns the type as-is. | ||
pub fn UnwrapContainer(comptime T: type) type | ||
{ | ||
comptime |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty sure functions returning type
are always evaluated at comptime
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Chalk it up to paranoia from seeing too many bugs due to something being evaluated as runtime for non-obvious reasons.
std/meta/index.zig
Outdated
|
||
|
||
///Returns the active tag of a tagged union | ||
pub fn activeTag(u: var) @TagType(UnwrapContainer(@typeOf(u))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this function should take pointers and deref it. It should just take the value, and the user is responsible for the deref.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense, given that the compiler should ensure that no copies of large structs occur.
std/meta/index.zig
Outdated
} | ||
///Given a pointer to a single item, returns a slice of the underlying bytes, preserving the | ||
/// pointer's const-ness. | ||
pub fn bytes(ptr: var) BytesReturnType(@typeOf(ptr)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this should be in std.mem
instead. We should also probably return an array pointer instead of a slice, as we know the size of the data we are returning. I have a simular implementation of this here. I think we could have toBytes
and asBytes
where asBytes
return a slice with the correct attributes, and toBytes
just returns a copy of the data as bytes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And maybe we should also have a bytesToValue
and bytesAsValue
that takes an array/array pointer respectively and returns a packed value/pointer to packed value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense to me.
std/meta/index.zig
Outdated
pub fn containerEql(container_a: var, container_b: var) bool | ||
{ | ||
const T = UnwrapContainer(@typeOf(container_a)); | ||
debug.assert(UnwrapContainer(@typeOf(container_b)) == T); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, I don't think we should take pointers. Let the function take values, and let the user deref.
std/meta/index.zig
Outdated
|
||
///Compares two structs or (tagged) unions for equality on a field level | ||
/// so that padding bytes are not relevant. | ||
pub fn containerEql(container_a: var, container_b: var) bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why only containers? We could do a generic eql
that supports all types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually that was the original implementation, but it seemed to me that it might be a bad idea to have such a broad implementation. For instance, the only reasonable way to compare two untagged unions is to use mem.eql
, but that comparison could be wrong. If the user is used to just calling meta.eql
on everything, including untagged unions, that comparison will sometimes fail when it would be expected to succeed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, but we could just make untagged unions call @compileError
. Most types compare equal just fine.
std/meta/trait.zig
Outdated
}; | ||
|
||
debug.assert(hasField("value")(TestStruct)); | ||
debug.assert(hasField("value")(*TestStruct)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know, that .
can access through pointers, but Idk if we really wanna "lie" to the user because of this. The pointer does not have any fields. .
just does a little magic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In generic code, the user probably doesn't care if they were passed a pointer or not, as long as they can call/access some needed function/field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I think you're right. Instead we'll have the user call UnwrapContainer(T) --I am all ears for naming suggestions-- themselves if that's the behavior they want.
std/meta/trait.zig
Outdated
if(is(builtin.TypeId.Pointer)(T)) | ||
{ | ||
const info = @typeInfo(T); | ||
return info.Pointer.size != builtin.TypeInfo.Pointer.Size.One; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can index array pointers (*[N]T
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty good now (only two small things). The trait function will be really useful if we ever get var<func>
:)
std/meta/index.zig
Outdated
const TypeId = builtin.TypeId; | ||
const TypeInfo = builtin.TypeInfo; | ||
|
||
pub fn UnwrapContainer(comptime T: type) type |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now, we probably don't need this anymore :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe. I think it might still be useful for generic code that doesn't care if it is passed a pointer or not as long as it can call/access var.whatever
. I used to do this a lot before #733, so it might not be as useful an idea now. I'll think on it for a bit.
std/meta/index.zig
Outdated
|
||
///Compares two structs or (tagged) unions for equality on a field level | ||
/// so that padding bytes are not relevant. | ||
pub fn containerEql(container_a: var, container_b: @typeOf(container_a)) bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think we probably just want a fully generic equal, if we're gonna have this one
std/meta/index.zig
Outdated
|
||
//This is the only reasonable way to handle an untagged union, | ||
// but it will report a false negative in many circumstances. | ||
return std.mem.eql(u8, mem.asBytes(&a), mem.asBytes(&b)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably better if this is a compiler error, to avoid the bugs, where padding bytes are different
Other than some of the code not following the style of |
std/mem.zig
Outdated
pub fn bytesAsValue(comptime T: type, bytes: var) BytesAsValueReturnType(T, @typeOf(bytes)) | ||
{ | ||
return @ptrCast(BytesAsValueReturnType(T, @typeOf(bytes)), | ||
@alignCast(comptime meta.alignment(T), bytes)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This cast failes on macos, because the bytes we pass in are not aligned with our type. Maybe we should return *align(1) T
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And maybe asBytes
should return *align(comptime alignment(@typeOf(ptr))) [@sizeOf(@typeOf(ptr))]
…Removed to* variations as they are trivially replicated with 2 characters at the as* callsites.
… bytesAsValue alignment match alignment of byte buffer. I'm not 100% sure that's actually a good idea though.
Added the beginning of std.meta from the branch started by @Hejsil and @andrewrk, which is intended to replace a lot of built-in functions over time, as well as std.meta.trait which is a collection of shortcuts to simple type introspections like determining if a type has an fn definition with a specific name.