Skip to content

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

Merged
merged 20 commits into from
Oct 19, 2018
Merged

Add std.meta #1662

merged 20 commits into from
Oct 19, 2018

Conversation

tgschultz
Copy link
Contributor

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.

/// Otherwise it returns the type as-is.
pub fn UnwrapContainer(comptime T: type) type
{
comptime
Copy link
Contributor

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

Copy link
Contributor Author

@tgschultz tgschultz Oct 18, 2018

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.



///Returns the active tag of a tagged union
pub fn activeTag(u: var) @TagType(UnwrapContainer(@typeOf(u)))
Copy link
Contributor

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.

Copy link
Contributor Author

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.

}
///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))
Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me.

pub fn containerEql(container_a: var, container_b: var) bool
{
const T = UnwrapContainer(@typeOf(container_a));
debug.assert(UnwrapContainer(@typeOf(container_b)) == T);
Copy link
Contributor

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.


///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
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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.

};

debug.assert(hasField("value")(TestStruct));
debug.assert(hasField("value")(*TestStruct));
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

if(is(builtin.TypeId.Pointer)(T))
{
const info = @typeInfo(T);
return info.Pointer.size != builtin.TypeInfo.Pointer.Size.One;
Copy link
Contributor

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)

Copy link
Contributor

@Hejsil Hejsil left a 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> :)

const TypeId = builtin.TypeId;
const TypeInfo = builtin.TypeInfo;

pub fn UnwrapContainer(comptime T: type) type
Copy link
Contributor

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 :)

Copy link
Contributor Author

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.


///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
Copy link
Contributor

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


//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));
Copy link
Contributor

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

@Hejsil
Copy link
Contributor

Hejsil commented Oct 19, 2018

Other than some of the code not following the style of std, this PR LGTM

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));
Copy link
Contributor

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?

Copy link
Contributor

@Hejsil Hejsil Oct 19, 2018

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.
@Hejsil Hejsil merged commit 2a3fdd5 into ziglang:master Oct 19, 2018
@tgschultz tgschultz deleted the std-meta branch October 20, 2018 15:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants