Background
https://github.com/ziglang/zig/pull/23733 replaced the old @Type builtin with new specialized builtins for reifying different categories of type: for instance, the new @Struct builtin reifies a struct type. In designing these builtins, we went for a "struct-of-arrays" (SoA) style interface, where, for instance, instead of providing a slice of StructField where StructField is struct { name: []const u8, type: type, ... }, you instead provide separate slices for each property: a []const []const u8 containing all field names, a []const type containing all field types, etc. Zig's design generally tries to make SoA patterns like this convenient to work with, for instance through its multi-for loop syntax.
However, the information returned by @typeInfo is currently given in the traditional "array of structs" (AoS) style. This is inconsistent, and it also makes it difficult to directly pass information from @typeInfo directly to (for instance) @Struct, perhaps requiring the user to use helper functions like std.meta.fieldNames to convert between data layouts.
This proposal is to convert std.builtin.Type to represent information in an SoA style, like how the reification builtins accept their arguments. It also aims to increase sharing of data types between the reification builtins (@Struct etc) and std.builtin.Type.
Proposal
Remove the following types:
std.builtin.Type.StructField
std.builtin.Type.Error
std.builtin.Type.EnumField
std.builtin.Type.UnionField
std.builtin.Type.Fn.Param
std.builtin.Type.Declaration
Modify the definitions of following types:
std.builtin.Type.Pointer
std.builtin.Type.Struct
std.builtin.Type.ErrorSet
std.builtin.Type.Enum
std.builtin.Type.Union
std.builtin.Type.Fn
std.builtin.Type.Opaque
Their new definitions are as follows:
pubconstPointer=struct{size:Size,attrs:Attributes,child:type,/// The type of the sentinel is the element type of the pointer, which is/// the value of the `child` field in this struct. However there is no way/// to refer to that type here, so we use `*const anyopaque`./// See also: `sentinel`sentinel_ptr:?*constanyopaque,/// Loads the pointer type's sentinel value from `sentinel_ptr`./// Returns `null` if the pointer type has no sentinel.pubinlinefnsentinel(comptimeptr:Pointer)?ptr.child{constsp:*constptr.child=@ptrCast(@alignCast(ptr.sentinel_ptrorelsereturnnull));returnsp.*;}pubconstSize=enum(u2){one,many,slice,c,};pubconstAttributes=struct{@"const":bool=false,@"volatile":bool=false,@"allowzero":bool=false,@"addrspace":?AddressSpace=null,@"align":?usize=null,};};pubconstStruct=struct{is_tuple:bool,layout:ContainerLayout,/// Always `null` if `layout != .@"packed"`.backing_integer:?type,field_names:[]const[:0]constu8,/// Guaranteed to have the same length as `field_names`.field_types:[]consttype,/// Guaranteed to have the same length as `field_names`.field_attrs:[]constFieldAttributes,decl_names:[]const[:0]constu8,pubconstFieldAttributes=struct{@"comptime":bool=false,/// `null` means the field alignment is not explicitly specified. The field will still/// be aligned to at least `@alignOf` the field type.@"align":?usize=null,/// The type of the default value is the type of this struct field. However, that type/// is not known here, so we use a type-erased pointer instead, which must be cast to/// a pointer to the field type.////// See also: `defaultValue`.default_value_ptr:?*constanyopaque=null,/// Loads the field's default value from `default_value_ptr`./// `FieldType` must exactly match the corresponding element of `Struct.field_types`./// Returns `null` if the field has no default value.pubinlinefndefaultValue(comptimeattrs:FieldAttributes,comptimeFieldType:type)?FieldType{constdp:*constFieldType=@ptrCast(@alignCast(attrs.default_value_ptrorelsereturnnull));returndp.*;}};};pubconstErrorSet=struct{error_names:?[]const[:0]constu8,};pubconstEnum=struct{tag_type:type,mode:Mode,field_names:[]const[:0]constu8,/// Guaranteed to have the same length as `field_names`.field_values:[]constcomptime_int,decl_names:[]const[:0]constu8,pubconstMode=enum{exhaustive,nonexhaustive};};pubconstUnion=struct{layout:ContainerLayout,tag_type:?type,/// Always `null` if `layout != .@"packed"`.backing_integer:?type,field_names:[]const[:0]constu8,/// Guaranteed to have the same length as `field_names`.field_types:[]consttype,/// Guaranteed to have the same length as `field_names`.field_attrs:[]constFieldAttributes,decl_names:[]const[:0]constu8,pubconstFieldAttributes=struct{/// `null` means the field alignment is not explicitly specified. The field will still/// be aligned to at least `@alignOf` the field type.@"align":?usize=null,};};pubconstFn=struct{attrs:Attributes,is_generic:bool,/// `null` means the return type is generic, i.e. it depends on a function argument.return_type:?type,/// A `null` element represents either an `anytype` parameter, or a parameter with a generic/// type, i.e. where the type depends on a previous function argument.param_types:[]const?type,/// Guaranteed to have the same length as `param_types`.param_attrs:[]constParamAttributes,pubconstParamAttributes=struct{@"noalias":bool=false,};pubconstAttributes=struct{@"callconv":CallingConvention=.auto,varargs:bool=false,};};pubconstOpaque=struct{decl_names:[]const[:0]constu8,};
## Background
https://github.com/ziglang/zig/pull/23733 replaced the old `@Type` builtin with new specialized builtins for reifying different categories of type: for instance, the new `@Struct` builtin reifies a struct type. In designing these builtins, we went for a "struct-of-arrays" (SoA) style interface, where, for instance, instead of providing a slice of `StructField` where `StructField` is `struct { name: []const u8, type: type, ... }`, you instead provide separate slices for each property: a `[]const []const u8` containing all field names, a `[]const type` containing all field types, etc. Zig's design generally tries to make SoA patterns like this convenient to work with, for instance through its multi-`for` loop syntax.
However, the information returned by `@typeInfo` is currently given in the traditional "array of structs" (AoS) style. This is inconsistent, and it also makes it difficult to directly pass information from `@typeInfo` directly to (for instance) `@Struct`, perhaps requiring the user to use helper functions like `std.meta.fieldNames` to convert between data layouts.
This proposal is to convert `std.builtin.Type` to represent information in an SoA style, like how the reification builtins accept their arguments. It also aims to increase sharing of data types between the reification builtins (`@Struct` etc) and `std.builtin.Type`.
## Proposal
**Remove** the following types:
* `std.builtin.Type.StructField`
* `std.builtin.Type.Error`
* `std.builtin.Type.EnumField`
* `std.builtin.Type.UnionField`
* `std.builtin.Type.Fn.Param`
* `std.builtin.Type.Declaration`
**Modify** the definitions of following types:
* `std.builtin.Type.Pointer`
* `std.builtin.Type.Struct`
* `std.builtin.Type.ErrorSet`
* `std.builtin.Type.Enum`
* `std.builtin.Type.Union`
* `std.builtin.Type.Fn`
* `std.builtin.Type.Opaque`
Their new definitions are as follows:
```zig
pub const Pointer = struct {
size: Size,
attrs: Attributes,
child: type,
/// The type of the sentinel is the element type of the pointer, which is
/// the value of the `child` field in this struct. However there is no way
/// to refer to that type here, so we use `*const anyopaque`.
/// See also: `sentinel`
sentinel_ptr: ?*const anyopaque,
/// Loads the pointer type's sentinel value from `sentinel_ptr`.
/// Returns `null` if the pointer type has no sentinel.
pub inline fn sentinel(comptime ptr: Pointer) ?ptr.child {
const sp: *const ptr.child = @ptrCast(@alignCast(ptr.sentinel_ptr orelse return null));
return sp.*;
}
pub const Size = enum(u2) {
one,
many,
slice,
c,
};
pub const Attributes = struct {
@"const": bool = false,
@"volatile": bool = false,
@"allowzero": bool = false,
@"addrspace": ?AddressSpace = null,
@"align": ?usize = null,
};
};
pub const Struct = struct {
is_tuple: bool,
layout: ContainerLayout,
/// Always `null` if `layout != .@"packed"`.
backing_integer: ?type,
field_names: []const [:0]const u8,
/// Guaranteed to have the same length as `field_names`.
field_types: []const type,
/// Guaranteed to have the same length as `field_names`.
field_attrs: []const FieldAttributes,
decl_names: []const [:0]const u8,
pub const FieldAttributes = struct {
@"comptime": bool = false,
/// `null` means the field alignment is not explicitly specified. The field will still
/// be aligned to at least `@alignOf` the field type.
@"align": ?usize = null,
/// The type of the default value is the type of this struct field. However, that type
/// is not known here, so we use a type-erased pointer instead, which must be cast to
/// a pointer to the field type.
///
/// See also: `defaultValue`.
default_value_ptr: ?*const anyopaque = null,
/// Loads the field's default value from `default_value_ptr`.
/// `FieldType` must exactly match the corresponding element of `Struct.field_types`.
/// Returns `null` if the field has no default value.
pub inline fn defaultValue(comptime attrs: FieldAttributes, comptime FieldType: type) ?FieldType {
const dp: *const FieldType = @ptrCast(@alignCast(attrs.default_value_ptr orelse return null));
return dp.*;
}
};
};
pub const ErrorSet = struct {
error_names: ?[]const [:0]const u8,
};
pub const Enum = struct {
tag_type: type,
mode: Mode,
field_names: []const [:0]const u8,
/// Guaranteed to have the same length as `field_names`.
field_values: []const comptime_int,
decl_names: []const [:0]const u8,
pub const Mode = enum { exhaustive, nonexhaustive };
};
pub const Union = struct {
layout: ContainerLayout,
tag_type: ?type,
/// Always `null` if `layout != .@"packed"`.
backing_integer: ?type,
field_names: []const [:0]const u8,
/// Guaranteed to have the same length as `field_names`.
field_types: []const type,
/// Guaranteed to have the same length as `field_names`.
field_attrs: []const FieldAttributes,
decl_names: []const [:0]const u8,
pub const FieldAttributes = struct {
/// `null` means the field alignment is not explicitly specified. The field will still
/// be aligned to at least `@alignOf` the field type.
@"align": ?usize = null,
};
};
pub const Fn = struct {
attrs: Attributes,
is_generic: bool,
/// `null` means the return type is generic, i.e. it depends on a function argument.
return_type: ?type,
/// A `null` element represents either an `anytype` parameter, or a parameter with a generic
/// type, i.e. where the type depends on a previous function argument.
param_types: []const ?type,
/// Guaranteed to have the same length as `param_types`.
param_attrs: []const ParamAttributes,
pub const ParamAttributes = struct {
@"noalias": bool = false,
};
pub const Attributes = struct {
@"callconv": CallingConvention = .auto,
varargs: bool = false,
};
};
pub const Opaque = struct {
decl_names: []const [:0]const u8,
};
```