Zig Version
0.17.0-dev.831+431092fa6
Steps to Reproduce, Observed Behavior, and Expected Behavior
@FieldType Semantics
Current Semantics
The section of the manual on @FieldType says:
Given a type and the name of one of its fields, returns the type of that field.
However, the current implementation supports some additional usages not described here. I believe this is an unintentional consequence of @FieldType being built on top of an existing internal feature:
constMyStruct=struct{my_field:u32};comptimeassert(@FieldType(?MyStruct,"my_field")==u32);// unexpectedly compiles & passescomptimeassert(@FieldType(anyerror!MyStruct,"my_field")==u32);// unexpectedly compiles & passes
Additionally, @FieldType doesn't support some usages you might expect it to. I suspect that these omissions weren't intentional, but I could be mistaken:
constarray:[2]u32=@splat(0);constslice:[]constu32=&array;comptimeassert(@field(array,"len")==2);// passescomptimeassert(@field(slice,"len")==2);// passescomptimeassert(@field(slice,"ptr")==slice.ptr);// passescomptimeassert(@FieldType(@TypeOf([2]u32),"len")==usize);// error: expected struct or union; found '[2]u32'comptimeassert(@FieldType(@TypeOf([]constu32),"len")==usize);// error: expected struct or union; found '[]const u32'comptimeassert(@FieldType(@TypeOf([]constu32),"ptr")==[*]constu32);// error: expected struct or union; found '[]const u32'
Should @FieldType be 1:1 with @field?
It's tempting to suggest that @FieldType(foo, "bar") should always be equivalent to @TypeOf(@field(foo, "bar")). However, that would require @FieldType to support operating on pointers, which currently does not:
constmy_struct:*constMyStruct=&.{.my_field=0};comptimeassert(@field(my_struct,"my_field")==0);comptimeassert(@FieldType(*constMyStruct,"my_field")==0);// error: expected struct or union; found '*const main.main.MyStruct'
It makes sense that @field supports this since it's supposed to mimic the . operator.
It would seem odd to me for @FieldType to support operating on pointers, but I don't have a strong argument as to why and could see the value in these being 1:1, so I'm open to having my mind changed.
Pointers To Fields
@field has one additional trick up its sleeve that @FieldType doesn't: you can take the address of the result of @field.
On the flip side, it's not currently possible to reconstruct the type of a pointer to a field using just @FieldType.
A naive attempt would be *@FieldType(MyStruct, "my_field"), but this drops all the pointer attributes.
You could manually reify a new pointer type to add them back, but this would still fail if it was a pointer into a packed struct. Not only is it not currently possile to reify bit pointers, even if it was, short of reimplementing part of the compiler in comptime using reflection info you wouldn't know what offset and byte size to use.
Currently your best try (assuming you don't have an actual pointer to the parent) is to create a dummy parent pointer:
fnFieldPtr(comptimeParentPtr:type,comptimefield_name:[]constu8)type{constptr_info=@typeInfo(ParentPtr).pointer;constptr_align=ptr_info.attrs.@"align"orelse@alignOf(ptr_info.child);constparent_ptr:ParentPtr=@ptrFromInt(ptr_align);return@TypeOf(&@field(parent_ptr,field_name));}
Note to mlugg: I think we concluded this doesn't work yesterday due to an issue relating to alignment. I plugged it back into my use case just now to remind myself why exactly it doesn't work, and, well, it works. I suspect that I either misremember this part of our conversation and it was an earlier try, or we were lead astray by my half finished refactor.
Proposal
I'm interested in others feedback on this topic before making a concrete proposal. Here are my thoughts so far:
- We either need to remove the extra optional/error set functionality from
@FieldType or document that it exists. I believe it's unintentional and should be removed.
- I am undecided on whether
@FieldType should operate on pointers the way @field does. Having @FieldType and @field be 1:1 could provide utility, but having @FieldType operate on pointers seems odd.
- We should consider either adding the example
FieldPtr implementation to the standard library, or adding a builtin that achieves the same thing.
CC @jacobly @mlugg
### Zig Version
0.17.0-dev.831+431092fa6
### Steps to Reproduce, Observed Behavior, and Expected Behavior
# `@FieldType` Semantics
## Current Semantics
The section of the manual on `@FieldType` says:
```
Given a type and the name of one of its fields, returns the type of that field.
```
However, the current implementation supports some additional usages not described here. I believe this is an unintentional consequence of `@FieldType` being built on top of an existing internal feature:
```zig
const MyStruct = struct { my_field: u32 };
comptime assert(@FieldType(?MyStruct, "my_field") == u32); // unexpectedly compiles & passes
comptime assert(@FieldType(anyerror!MyStruct, "my_field") == u32); // unexpectedly compiles & passes
```
Additionally, `@FieldType` doesn't support some usages you might expect it to. I suspect that these omissions weren't intentional, but I could be mistaken:
```zig
const array: [2]u32 = @splat(0);
const slice: []const u32 = &array;
comptime assert(@field(array, "len") == 2); // passes
comptime assert(@field(slice, "len") == 2); // passes
comptime assert(@field(slice, "ptr") == slice.ptr); // passes
comptime assert(@FieldType(@TypeOf([2]u32), "len") == usize); // error: expected struct or union; found '[2]u32'
comptime assert(@FieldType(@TypeOf([]const u32), "len") == usize); // error: expected struct or union; found '[]const u32'
comptime assert(@FieldType(@TypeOf([]const u32), "ptr") == [*]const u32); // error: expected struct or union; found '[]const u32'
```
## Should `@FieldType` be 1:1 with `@field`?
It's tempting to suggest that `@FieldType(foo, "bar")` should always be equivalent to `@TypeOf(@field(foo, "bar"))`. However, that would require `@FieldType` to support operating on pointers, which currently does not:
```zig
const my_struct: *const MyStruct = &.{ .my_field = 0 };
comptime assert(@field(my_struct, "my_field") == 0);
comptime assert(@FieldType(*const MyStruct, "my_field") == 0); // error: expected struct or union; found '*const main.main.MyStruct'
```
It makes sense that `@field` supports this since it's supposed to mimic the `.` operator.
It would seem odd to me for `@FieldType` to support operating on pointers, but I don't have a strong argument as to why and could see the value in these being 1:1, so I'm open to having my mind changed.
## Pointers To Fields
`@field` has one additional trick up its sleeve that `@FieldType` doesn't: you can take the address of the result of `@field`.
On the flip side, it's not currently possible to reconstruct the type of a pointer to a field using just `@FieldType`.
A naive attempt would be `*@FieldType(MyStruct, "my_field")`, but this drops all the pointer attributes.
You could manually reify a new pointer type to add them back, but this would still fail if it was a pointer into a packed struct. Not only is it not currently possile to reify bit pointers, even if it was, short of reimplementing part of the compiler in comptime using reflection info you wouldn't know what offset and byte size to use.
Currently your best try (assuming you don't have an actual pointer to the parent) is to create a dummy parent pointer:
```zig
fn FieldPtr(comptime ParentPtr: type, comptime field_name: []const u8) type {
const ptr_info = @typeInfo(ParentPtr).pointer;
const ptr_align = ptr_info.attrs.@"align" orelse @alignOf(ptr_info.child);
const parent_ptr: ParentPtr = @ptrFromInt(ptr_align);
return @TypeOf(&@field(parent_ptr, field_name));
}
```
*Note to mlugg: I think we concluded this doesn't work yesterday due to an issue relating to alignment. I plugged it back into my use case just now to remind myself why exactly it doesn't work, and, well, it works. I suspect that I either misremember this part of our conversation and it was an earlier try, or we were lead astray by my half finished refactor.*
## Proposal
I'm interested in others feedback on this topic before making a concrete proposal. Here are my thoughts so far:
* We either need to remove the extra optional/error set functionality from `@FieldType` or document that it exists. I believe it's unintentional and should be removed.
* I am undecided on whether `@FieldType` should operate on pointers the way `@field` does. Having `@FieldType` and `@field` be 1:1 could provide utility, but having `@FieldType` operate on pointers seems odd.
* We should consider either adding the example `FieldPtr` implementation to the standard library, or adding a builtin that achieves the same thing.
***
CC @jacobly @mlugg