zig/src/type.zig

3076 lines
96 KiB
Zig

const std = @import("std");
const Value = @import("value.zig").Value;
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const Target = std.Target;
const Module = @import("Module.zig");
/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
/// It's important for this type to be small.
/// Types are not de-duplicated, which helps with multi-threading since it obviates the requirement
/// of obtaining a lock on a global type table, as well as making the
/// garbage collection bookkeeping simpler.
/// This union takes advantage of the fact that the first page of memory
/// is unmapped, giving us 4096 possible enum tags that have no payload.
pub const Type = extern union {
/// If the tag value is less than Tag.no_payload_count, then no pointer
/// dereference is needed.
tag_if_small_enough: usize,
ptr_otherwise: *Payload,
pub fn zigTypeTag(self: Type) std.builtin.TypeId {
switch (self.tag()) {
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.c_longdouble,
.int_signed,
.int_unsigned,
=> return .Int,
.f16,
.f32,
.f64,
.f128,
=> return .Float,
.c_void => return .Opaque,
.bool => return .Bool,
.void => return .Void,
.type => return .Type,
.error_set, .error_set_single, .anyerror => return .ErrorSet,
.comptime_int => return .ComptimeInt,
.comptime_float => return .ComptimeFloat,
.noreturn => return .NoReturn,
.@"null" => return .Null,
.@"undefined" => return .Undefined,
.fn_noreturn_no_args => return .Fn,
.fn_void_no_args => return .Fn,
.fn_naked_noreturn_no_args => return .Fn,
.fn_ccc_void_no_args => return .Fn,
.function => return .Fn,
.array, .array_u8_sentinel_0, .array_u8, .array_sentinel => return .Array,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.pointer,
=> return .Pointer,
.optional,
.optional_single_const_pointer,
.optional_single_mut_pointer,
=> return .Optional,
.enum_literal => return .EnumLiteral,
.anyerror_void_error_union, .error_union => return .ErrorUnion,
.anyframe_T, .@"anyframe" => return .AnyFrame,
}
}
pub fn initTag(comptime small_tag: Tag) Type {
comptime assert(@enumToInt(small_tag) < Tag.no_payload_count);
return .{ .tag_if_small_enough = @enumToInt(small_tag) };
}
pub fn initPayload(payload: *Payload) Type {
assert(@enumToInt(payload.tag) >= Tag.no_payload_count);
return .{ .ptr_otherwise = payload };
}
pub fn tag(self: Type) Tag {
if (self.tag_if_small_enough < Tag.no_payload_count) {
return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough));
} else {
return self.ptr_otherwise.tag;
}
}
pub fn cast(self: Type, comptime T: type) ?*T {
if (self.tag_if_small_enough < Tag.no_payload_count)
return null;
const expected_tag = std.meta.fieldInfo(T, "base").default_value.?.tag;
if (self.ptr_otherwise.tag != expected_tag)
return null;
return @fieldParentPtr(T, "base", self.ptr_otherwise);
}
pub fn castPointer(self: Type) ?*Payload.PointerSimple {
return switch (self.tag()) {
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.optional_single_const_pointer,
.optional_single_mut_pointer,
=> @fieldParentPtr(Payload.PointerSimple, "base", self.ptr_otherwise),
else => null,
};
}
pub fn eql(a: Type, b: Type) bool {
// As a shortcut, if the small tags / addresses match, we're done.
if (a.tag_if_small_enough == b.tag_if_small_enough)
return true;
const zig_tag_a = a.zigTypeTag();
const zig_tag_b = b.zigTypeTag();
if (zig_tag_a != zig_tag_b)
return false;
switch (zig_tag_a) {
.EnumLiteral => return true,
.Type => return true,
.Void => return true,
.Bool => return true,
.NoReturn => return true,
.ComptimeFloat => return true,
.ComptimeInt => return true,
.Undefined => return true,
.Null => return true,
.AnyFrame => {
return a.elemType().eql(b.elemType());
},
.Pointer => {
// Hot path for common case:
if (a.castPointer()) |a_payload| {
if (b.castPointer()) |b_payload| {
return a.tag() == b.tag() and eql(a_payload.pointee_type, b_payload.pointee_type);
}
}
const is_slice_a = isSlice(a);
const is_slice_b = isSlice(b);
if (is_slice_a != is_slice_b)
return false;
@panic("TODO implement more pointer Type equality comparison");
},
.Int => {
// Detect that e.g. u64 != usize, even if the bits match on a particular target.
const a_is_named_int = a.isNamedInt();
const b_is_named_int = b.isNamedInt();
if (a_is_named_int != b_is_named_int)
return false;
if (a_is_named_int)
return a.tag() == b.tag();
// Remaining cases are arbitrary sized integers.
// The target will not be branched upon, because we handled target-dependent cases above.
const info_a = a.intInfo(@as(Target, undefined));
const info_b = b.intInfo(@as(Target, undefined));
return info_a.signed == info_b.signed and info_a.bits == info_b.bits;
},
.Array => {
if (a.arrayLen() != b.arrayLen())
return false;
if (!a.elemType().eql(b.elemType()))
return false;
const sentinel_a = a.sentinel();
const sentinel_b = b.sentinel();
if (sentinel_a) |sa| {
if (sentinel_b) |sb| {
return sa.eql(sb);
} else {
return false;
}
} else {
return sentinel_b == null;
}
},
.Fn => {
if (!a.fnReturnType().eql(b.fnReturnType()))
return false;
if (a.fnCallingConvention() != b.fnCallingConvention())
return false;
const a_param_len = a.fnParamLen();
const b_param_len = b.fnParamLen();
if (a_param_len != b_param_len)
return false;
var i: usize = 0;
while (i < a_param_len) : (i += 1) {
if (!a.fnParamType(i).eql(b.fnParamType(i)))
return false;
}
return true;
},
.Optional => {
var buf_a: Payload.PointerSimple = undefined;
var buf_b: Payload.PointerSimple = undefined;
return a.optionalChild(&buf_a).eql(b.optionalChild(&buf_b));
},
.Float,
.Struct,
.ErrorUnion,
.ErrorSet,
.Enum,
.Union,
.BoundFn,
.Opaque,
.Frame,
.Vector,
=> std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),
}
}
pub fn hash(self: Type) u64 {
var hasher = std.hash.Wyhash.init(0);
const zig_type_tag = self.zigTypeTag();
std.hash.autoHash(&hasher, zig_type_tag);
switch (zig_type_tag) {
.Type,
.Void,
.Bool,
.NoReturn,
.ComptimeFloat,
.ComptimeInt,
.Undefined,
.Null,
=> {}, // The zig type tag is all that is needed to distinguish.
.Pointer => {
// TODO implement more pointer type hashing
},
.Int => {
// Detect that e.g. u64 != usize, even if the bits match on a particular target.
if (self.isNamedInt()) {
std.hash.autoHash(&hasher, self.tag());
} else {
// Remaining cases are arbitrary sized integers.
// The target will not be branched upon, because we handled target-dependent cases above.
const info = self.intInfo(@as(Target, undefined));
std.hash.autoHash(&hasher, info.signed);
std.hash.autoHash(&hasher, info.bits);
}
},
.Array => {
std.hash.autoHash(&hasher, self.arrayLen());
std.hash.autoHash(&hasher, self.elemType().hash());
// TODO hash array sentinel
},
.Fn => {
std.hash.autoHash(&hasher, self.fnReturnType().hash());
std.hash.autoHash(&hasher, self.fnCallingConvention());
const params_len = self.fnParamLen();
std.hash.autoHash(&hasher, params_len);
var i: usize = 0;
while (i < params_len) : (i += 1) {
std.hash.autoHash(&hasher, self.fnParamType(i).hash());
}
},
.Optional => {
var buf: Payload.PointerSimple = undefined;
std.hash.autoHash(&hasher, self.optionalChild(&buf).hash());
},
.Float,
.Struct,
.ErrorUnion,
.ErrorSet,
.Enum,
.Union,
.BoundFn,
.Opaque,
.Frame,
.AnyFrame,
.Vector,
.EnumLiteral,
=> {
// TODO implement more type hashing
},
}
return hasher.final();
}
pub fn copy(self: Type, allocator: *Allocator) error{OutOfMemory}!Type {
if (self.tag_if_small_enough < Tag.no_payload_count) {
return Type{ .tag_if_small_enough = self.tag_if_small_enough };
} else switch (self.ptr_otherwise.tag) {
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.c_longdouble,
.c_void,
.f16,
.f32,
.f64,
.f128,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.enum_literal,
.anyerror_void_error_union,
.@"anyframe",
=> unreachable,
.array_u8_sentinel_0 => return self.copyPayloadShallow(allocator, Payload.Array_u8_Sentinel0),
.array_u8 => return self.copyPayloadShallow(allocator, Payload.Array_u8),
.array => {
const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);
const new_payload = try allocator.create(Payload.Array);
new_payload.* = .{
.base = payload.base,
.len = payload.len,
.elem_type = try payload.elem_type.copy(allocator),
};
return Type{ .ptr_otherwise = &new_payload.base };
},
.array_sentinel => {
const payload = @fieldParentPtr(Payload.ArraySentinel, "base", self.ptr_otherwise);
const new_payload = try allocator.create(Payload.ArraySentinel);
new_payload.* = .{
.base = payload.base,
.len = payload.len,
.sentinel = try payload.sentinel.copy(allocator),
.elem_type = try payload.elem_type.copy(allocator),
};
return Type{ .ptr_otherwise = &new_payload.base };
},
.int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned),
.int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned),
.function => {
const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
const new_payload = try allocator.create(Payload.Function);
const param_types = try allocator.alloc(Type, payload.param_types.len);
for (payload.param_types) |param_type, i| {
param_types[i] = try param_type.copy(allocator);
}
new_payload.* = .{
.base = payload.base,
.return_type = try payload.return_type.copy(allocator),
.param_types = param_types,
.cc = payload.cc,
};
return Type{ .ptr_otherwise = &new_payload.base };
},
.optional => return self.copyPayloadSingleField(allocator, Payload.Optional, "child_type"),
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.optional_single_mut_pointer,
.optional_single_const_pointer,
=> return self.copyPayloadSingleField(allocator, Payload.PointerSimple, "pointee_type"),
.anyframe_T => return self.copyPayloadSingleField(allocator, Payload.AnyFrame, "return_type"),
.pointer => {
const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
const new_payload = try allocator.create(Payload.Pointer);
new_payload.* = .{
.base = payload.base,
.pointee_type = try payload.pointee_type.copy(allocator),
.sentinel = if (payload.sentinel) |some| try some.copy(allocator) else null,
.@"align" = payload.@"align",
.bit_offset = payload.bit_offset,
.host_size = payload.host_size,
.@"allowzero" = payload.@"allowzero",
.mutable = payload.mutable,
.@"volatile" = payload.@"volatile",
.size = payload.size,
};
return Type{ .ptr_otherwise = &new_payload.base };
},
.error_union => {
const payload = @fieldParentPtr(Payload.ErrorUnion, "base", self.ptr_otherwise);
const new_payload = try allocator.create(Payload.ErrorUnion);
new_payload.* = .{
.base = payload.base,
.error_set = try payload.error_set.copy(allocator),
.payload = try payload.payload.copy(allocator),
};
return Type{ .ptr_otherwise = &new_payload.base };
},
.error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
.error_set_single => return self.copyPayloadShallow(allocator, Payload.ErrorSetSingle),
}
}
fn copyPayloadShallow(self: Type, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Type {
const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
const new_payload = try allocator.create(T);
new_payload.* = payload.*;
return Type{ .ptr_otherwise = &new_payload.base };
}
fn copyPayloadSingleField(self: Type, allocator: *Allocator, comptime T: type, comptime field_name: []const u8) error{OutOfMemory}!Type {
const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
const new_payload = try allocator.create(T);
new_payload.base = payload.base;
@field(new_payload, field_name) = try @field(payload, field_name).copy(allocator);
return Type{ .ptr_otherwise = &new_payload.base };
}
pub fn format(
self: Type,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
out_stream: anytype,
) @TypeOf(out_stream).Error!void {
comptime assert(fmt.len == 0);
var ty = self;
while (true) {
const t = ty.tag();
switch (t) {
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.c_longdouble,
.c_void,
.f16,
.f32,
.f64,
.f128,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
=> return out_stream.writeAll(@tagName(t)),
.enum_literal => return out_stream.writeAll("@Type(.EnumLiteral)"),
.@"null" => return out_stream.writeAll("@Type(.Null)"),
.@"undefined" => return out_stream.writeAll("@Type(.Undefined)"),
.@"anyframe" => return out_stream.writeAll("anyframe"),
.anyerror_void_error_union => return out_stream.writeAll("anyerror!void"),
.const_slice_u8 => return out_stream.writeAll("[]const u8"),
.fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),
.fn_void_no_args => return out_stream.writeAll("fn() void"),
.fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
.fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"),
.single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"),
.function => {
const payload = @fieldParentPtr(Payload.Function, "base", ty.ptr_otherwise);
try out_stream.writeAll("fn(");
for (payload.param_types) |param_type, i| {
if (i != 0) try out_stream.writeAll(", ");
try param_type.format("", .{}, out_stream);
}
try out_stream.writeAll(") ");
ty = payload.return_type;
continue;
},
.anyframe_T => {
const payload = @fieldParentPtr(Payload.AnyFrame, "base", ty.ptr_otherwise);
try out_stream.print("anyframe->", .{});
ty = payload.return_type;
continue;
},
.array_u8 => {
const payload = @fieldParentPtr(Payload.Array_u8, "base", ty.ptr_otherwise);
return out_stream.print("[{}]u8", .{payload.len});
},
.array_u8_sentinel_0 => {
const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", ty.ptr_otherwise);
return out_stream.print("[{}:0]u8", .{payload.len});
},
.array => {
const payload = @fieldParentPtr(Payload.Array, "base", ty.ptr_otherwise);
try out_stream.print("[{}]", .{payload.len});
ty = payload.elem_type;
continue;
},
.array_sentinel => {
const payload = @fieldParentPtr(Payload.ArraySentinel, "base", ty.ptr_otherwise);
try out_stream.print("[{}:{}]", .{ payload.len, payload.sentinel });
ty = payload.elem_type;
continue;
},
.single_const_pointer => {
const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
try out_stream.writeAll("*const ");
ty = payload.pointee_type;
continue;
},
.single_mut_pointer => {
const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
try out_stream.writeAll("*");
ty = payload.pointee_type;
continue;
},
.many_const_pointer => {
const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
try out_stream.writeAll("[*]const ");
ty = payload.pointee_type;
continue;
},
.many_mut_pointer => {
const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
try out_stream.writeAll("[*]");
ty = payload.pointee_type;
continue;
},
.c_const_pointer => {
const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
try out_stream.writeAll("[*c]const ");
ty = payload.pointee_type;
continue;
},
.c_mut_pointer => {
const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
try out_stream.writeAll("[*c]");
ty = payload.pointee_type;
continue;
},
.const_slice => {
const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
try out_stream.writeAll("[]const ");
ty = payload.pointee_type;
continue;
},
.mut_slice => {
const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
try out_stream.writeAll("[]");
ty = payload.pointee_type;
continue;
},
.int_signed => {
const payload = @fieldParentPtr(Payload.IntSigned, "base", ty.ptr_otherwise);
return out_stream.print("i{}", .{payload.bits});
},
.int_unsigned => {
const payload = @fieldParentPtr(Payload.IntUnsigned, "base", ty.ptr_otherwise);
return out_stream.print("u{}", .{payload.bits});
},
.optional => {
const payload = @fieldParentPtr(Payload.Optional, "base", ty.ptr_otherwise);
try out_stream.writeByte('?');
ty = payload.child_type;
continue;
},
.optional_single_const_pointer => {
const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
try out_stream.writeAll("?*const ");
ty = payload.pointee_type;
continue;
},
.optional_single_mut_pointer => {
const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
try out_stream.writeAll("?*");
ty = payload.pointee_type;
continue;
},
.pointer => {
const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
if (payload.sentinel) |some| switch (payload.size) {
.One, .C => unreachable,
.Many => try out_stream.print("[*:{}]", .{some}),
.Slice => try out_stream.print("[:{}]", .{some}),
} else switch (payload.size) {
.One => try out_stream.writeAll("*"),
.Many => try out_stream.writeAll("[*]"),
.C => try out_stream.writeAll("[*c]"),
.Slice => try out_stream.writeAll("[]"),
}
if (payload.@"align" != 0) {
try out_stream.print("align({}", .{payload.@"align"});
if (payload.bit_offset != 0) {
try out_stream.print(":{}:{}", .{ payload.bit_offset, payload.host_size });
}
try out_stream.writeAll(") ");
}
if (!payload.mutable) try out_stream.writeAll("const ");
if (payload.@"volatile") try out_stream.writeAll("volatile ");
if (payload.@"allowzero") try out_stream.writeAll("allowzero ");
ty = payload.pointee_type;
continue;
},
.error_union => {
const payload = @fieldParentPtr(Payload.ErrorUnion, "base", ty.ptr_otherwise);
try payload.error_set.format("", .{}, out_stream);
try out_stream.writeAll("!");
ty = payload.payload;
continue;
},
.error_set => {
const payload = @fieldParentPtr(Payload.ErrorSet, "base", ty.ptr_otherwise);
return out_stream.writeAll(std.mem.spanZ(payload.decl.name));
},
.error_set_single => {
const payload = @fieldParentPtr(Payload.ErrorSetSingle, "base", ty.ptr_otherwise);
return out_stream.print("error{{{}}}", .{payload.name});
},
}
unreachable;
}
}
pub fn toValue(self: Type, allocator: *Allocator) Allocator.Error!Value {
switch (self.tag()) {
.u8 => return Value.initTag(.u8_type),
.i8 => return Value.initTag(.i8_type),
.u16 => return Value.initTag(.u16_type),
.i16 => return Value.initTag(.i16_type),
.u32 => return Value.initTag(.u32_type),
.i32 => return Value.initTag(.i32_type),
.u64 => return Value.initTag(.u64_type),
.i64 => return Value.initTag(.i64_type),
.usize => return Value.initTag(.usize_type),
.isize => return Value.initTag(.isize_type),
.c_short => return Value.initTag(.c_short_type),
.c_ushort => return Value.initTag(.c_ushort_type),
.c_int => return Value.initTag(.c_int_type),
.c_uint => return Value.initTag(.c_uint_type),
.c_long => return Value.initTag(.c_long_type),
.c_ulong => return Value.initTag(.c_ulong_type),
.c_longlong => return Value.initTag(.c_longlong_type),
.c_ulonglong => return Value.initTag(.c_ulonglong_type),
.c_longdouble => return Value.initTag(.c_longdouble_type),
.c_void => return Value.initTag(.c_void_type),
.f16 => return Value.initTag(.f16_type),
.f32 => return Value.initTag(.f32_type),
.f64 => return Value.initTag(.f64_type),
.f128 => return Value.initTag(.f128_type),
.bool => return Value.initTag(.bool_type),
.void => return Value.initTag(.void_type),
.type => return Value.initTag(.type_type),
.anyerror => return Value.initTag(.anyerror_type),
.comptime_int => return Value.initTag(.comptime_int_type),
.comptime_float => return Value.initTag(.comptime_float_type),
.noreturn => return Value.initTag(.noreturn_type),
.@"null" => return Value.initTag(.null_type),
.@"undefined" => return Value.initTag(.undefined_type),
.fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type),
.fn_void_no_args => return Value.initTag(.fn_void_no_args_type),
.fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),
.fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type),
.single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),
.const_slice_u8 => return Value.initTag(.const_slice_u8_type),
.enum_literal => return Value.initTag(.enum_literal_type),
else => {
const ty_payload = try allocator.create(Value.Payload.Ty);
ty_payload.* = .{ .ty = self };
return Value.initPayload(&ty_payload.base);
},
}
}
pub fn hasCodeGenBits(self: Type) bool {
return switch (self.tag()) {
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.c_longdouble,
.f16,
.f32,
.f64,
.f128,
.bool,
.anyerror,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.function,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.array_u8_sentinel_0,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> true,
// TODO lazy types
.array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,
.array_u8 => self.arrayLen() != 0,
.array_sentinel, .single_const_pointer, .single_mut_pointer, .many_const_pointer, .many_mut_pointer, .c_const_pointer, .c_mut_pointer, .const_slice, .mut_slice, .pointer => self.elemType().hasCodeGenBits(),
.int_signed => self.cast(Payload.IntSigned).?.bits != 0,
.int_unsigned => self.cast(Payload.IntUnsigned).?.bits != 0,
.error_union => {
const payload = self.cast(Payload.ErrorUnion).?;
return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits();
},
.c_void,
.void,
.type,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.enum_literal,
=> false,
};
}
pub fn isNoReturn(self: Type) bool {
return self.zigTypeTag() == .NoReturn;
}
/// Asserts that hasCodeGenBits() is true.
pub fn abiAlignment(self: Type, target: Target) u32 {
return switch (self.tag()) {
.u8,
.i8,
.bool,
.array_u8_sentinel_0,
.array_u8,
=> return 1,
.fn_noreturn_no_args, // represents machine code; not a pointer
.fn_void_no_args, // represents machine code; not a pointer
.fn_naked_noreturn_no_args, // represents machine code; not a pointer
.fn_ccc_void_no_args, // represents machine code; not a pointer
.function, // represents machine code; not a pointer
=> return switch (target.cpu.arch) {
.arm => 4,
.riscv64 => 2,
else => 1,
},
.i16, .u16 => return 2,
.i32, .u32 => return 4,
.i64, .u64 => return 8,
.isize,
.usize,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.optional_single_const_pointer,
.optional_single_mut_pointer,
.@"anyframe",
.anyframe_T,
=> return @divExact(target.cpu.arch.ptrBitWidth(), 8),
.pointer => {
const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
if (payload.@"align" != 0) return payload.@"align";
return @divExact(target.cpu.arch.ptrBitWidth(), 8);
},
.c_short => return @divExact(CType.short.sizeInBits(target), 8),
.c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8),
.c_int => return @divExact(CType.int.sizeInBits(target), 8),
.c_uint => return @divExact(CType.uint.sizeInBits(target), 8),
.c_long => return @divExact(CType.long.sizeInBits(target), 8),
.c_ulong => return @divExact(CType.ulong.sizeInBits(target), 8),
.c_longlong => return @divExact(CType.longlong.sizeInBits(target), 8),
.c_ulonglong => return @divExact(CType.ulonglong.sizeInBits(target), 8),
.f16 => return 2,
.f32 => return 4,
.f64 => return 8,
.f128 => return 16,
.c_longdouble => return 16,
.error_set,
.error_set_single,
.anyerror_void_error_union,
.anyerror,
=> return 2, // TODO revisit this when we have the concept of the error tag type
.array, .array_sentinel => return self.elemType().abiAlignment(target),
.int_signed, .int_unsigned => {
const bits: u16 = if (self.cast(Payload.IntSigned)) |pl|
pl.bits
else if (self.cast(Payload.IntUnsigned)) |pl|
pl.bits
else
unreachable;
return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
},
.optional => {
var buf: Payload.PointerSimple = undefined;
const child_type = self.optionalChild(&buf);
if (!child_type.hasCodeGenBits()) return 1;
if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr())
return @divExact(target.cpu.arch.ptrBitWidth(), 8);
return child_type.abiAlignment(target);
},
.error_union => {
const payload = self.cast(Payload.ErrorUnion).?;
if (!payload.error_set.hasCodeGenBits()) {
return payload.payload.abiAlignment(target);
} else if (!payload.payload.hasCodeGenBits()) {
return payload.error_set.abiAlignment(target);
}
@panic("TODO abiAlignment error union");
},
.c_void,
.void,
.type,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.enum_literal,
=> unreachable,
};
}
/// Asserts the type has the ABI size already resolved.
pub fn abiSize(self: Type, target: Target) u64 {
return switch (self.tag()) {
.fn_noreturn_no_args => unreachable, // represents machine code; not a pointer
.fn_void_no_args => unreachable, // represents machine code; not a pointer
.fn_naked_noreturn_no_args => unreachable, // represents machine code; not a pointer
.fn_ccc_void_no_args => unreachable, // represents machine code; not a pointer
.function => unreachable, // represents machine code; not a pointer
.c_void => unreachable,
.void => unreachable,
.type => unreachable,
.comptime_int => unreachable,
.comptime_float => unreachable,
.noreturn => unreachable,
.@"null" => unreachable,
.@"undefined" => unreachable,
.enum_literal => unreachable,
.single_const_pointer_to_comptime_int => unreachable,
.u8,
.i8,
.bool,
=> return 1,
.array_u8 => @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", self.ptr_otherwise).len,
.array_u8_sentinel_0 => @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", self.ptr_otherwise).len + 1,
.array => {
const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);
const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target));
return payload.len * elem_size;
},
.array_sentinel => {
const payload = @fieldParentPtr(Payload.ArraySentinel, "base", self.ptr_otherwise);
const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target));
return (payload.len + 1) * elem_size;
},
.i16, .u16 => return 2,
.i32, .u32 => return 4,
.i64, .u64 => return 8,
.@"anyframe", .anyframe_T, .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
.const_slice,
.mut_slice,
=> {
if (self.elemType().hasCodeGenBits()) return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2;
return @divExact(target.cpu.arch.ptrBitWidth(), 8);
},
.const_slice_u8 => return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2,
.optional_single_const_pointer,
.optional_single_mut_pointer,
=> {
if (self.elemType().hasCodeGenBits()) return 1;
return @divExact(target.cpu.arch.ptrBitWidth(), 8);
},
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.pointer,
=> {
if (self.elemType().hasCodeGenBits()) return 0;
return @divExact(target.cpu.arch.ptrBitWidth(), 8);
},
.c_short => return @divExact(CType.short.sizeInBits(target), 8),
.c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8),
.c_int => return @divExact(CType.int.sizeInBits(target), 8),
.c_uint => return @divExact(CType.uint.sizeInBits(target), 8),
.c_long => return @divExact(CType.long.sizeInBits(target), 8),
.c_ulong => return @divExact(CType.ulong.sizeInBits(target), 8),
.c_longlong => return @divExact(CType.longlong.sizeInBits(target), 8),
.c_ulonglong => return @divExact(CType.ulonglong.sizeInBits(target), 8),
.f16 => return 2,
.f32 => return 4,
.f64 => return 8,
.f128 => return 16,
.c_longdouble => return 16,
.error_set,
.error_set_single,
.anyerror_void_error_union,
.anyerror,
=> return 2, // TODO revisit this when we have the concept of the error tag type
.int_signed, .int_unsigned => {
const bits: u16 = if (self.cast(Payload.IntSigned)) |pl|
pl.bits
else if (self.cast(Payload.IntUnsigned)) |pl|
pl.bits
else
unreachable;
return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
},
.optional => {
var buf: Payload.PointerSimple = undefined;
const child_type = self.optionalChild(&buf);
if (!child_type.hasCodeGenBits()) return 1;
if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr())
return @divExact(target.cpu.arch.ptrBitWidth(), 8);
// Optional types are represented as a struct with the child type as the first
// field and a boolean as the second. Since the child type's abi alignment is
// guaranteed to be >= that of bool's (1 byte) the added size is exactly equal
// to the child type's ABI alignment.
return child_type.abiAlignment(target) + child_type.abiSize(target);
},
.error_union => {
const payload = self.cast(Payload.ErrorUnion).?;
if (!payload.error_set.hasCodeGenBits() and !payload.payload.hasCodeGenBits()) {
return 0;
} else if (!payload.error_set.hasCodeGenBits()) {
return payload.payload.abiSize(target);
} else if (!payload.payload.hasCodeGenBits()) {
return payload.error_set.abiSize(target);
}
@panic("TODO abiSize error union");
},
};
}
pub fn isSinglePointer(self: Type) bool {
return switch (self.tag()) {
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.c_longdouble,
.f16,
.f32,
.f64,
.f128,
.c_void,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.array,
.array_sentinel,
.array_u8,
.array_u8_sentinel_0,
.const_slice_u8,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.function,
.int_unsigned,
.int_signed,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> false,
.single_const_pointer,
.single_mut_pointer,
.single_const_pointer_to_comptime_int,
=> true,
.pointer => self.cast(Payload.Pointer).?.size == .One,
};
}
pub fn isSlice(self: Type) bool {
return switch (self.tag()) {
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.c_longdouble,
.f16,
.f32,
.f64,
.f128,
.c_void,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.array,
.array_sentinel,
.array_u8,
.array_u8_sentinel_0,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.single_const_pointer_to_comptime_int,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.function,
.int_unsigned,
.int_signed,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> false,
.const_slice,
.mut_slice,
.const_slice_u8,
=> true,
.pointer => self.cast(Payload.Pointer).?.size == .Slice,
};
}
pub fn isConstPtr(self: Type) bool {
return switch (self.tag()) {
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.c_longdouble,
.f16,
.f32,
.f64,
.f128,
.c_void,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.array,
.array_sentinel,
.array_u8,
.array_u8_sentinel_0,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.function,
.int_unsigned,
.int_signed,
.single_mut_pointer,
.many_mut_pointer,
.c_mut_pointer,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.mut_slice,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> false,
.single_const_pointer,
.many_const_pointer,
.c_const_pointer,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.const_slice,
=> true,
.pointer => !self.cast(Payload.Pointer).?.mutable,
};
}
pub fn isVolatilePtr(self: Type) bool {
return switch (self.tag()) {
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.c_longdouble,
.f16,
.f32,
.f64,
.f128,
.c_void,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.array,
.array_sentinel,
.array_u8,
.array_u8_sentinel_0,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.function,
.int_unsigned,
.int_signed,
.single_mut_pointer,
.single_const_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> false,
.pointer => {
const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
return payload.@"volatile";
},
};
}
pub fn isAllowzeroPtr(self: Type) bool {
return switch (self.tag()) {
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.c_longdouble,
.f16,
.f32,
.f64,
.f128,
.c_void,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.array,
.array_sentinel,
.array_u8,
.array_u8_sentinel_0,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.function,
.int_unsigned,
.int_signed,
.single_mut_pointer,
.single_const_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> false,
.pointer => {
const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
return payload.@"allowzero";
},
};
}
/// Asserts that the type is an optional
pub fn isPtrLikeOptional(self: Type) bool {
switch (self.tag()) {
.optional_single_const_pointer, .optional_single_mut_pointer => return true,
.optional => {
var buf: Payload.PointerSimple = undefined;
const child_type = self.optionalChild(&buf);
// optionals of zero sized pointers behave like bools
if (!child_type.hasCodeGenBits()) return false;
return child_type.zigTypeTag() == .Pointer and !child_type.isCPtr();
},
else => unreachable,
}
}
/// Returns if type can be used for a runtime variable
pub fn isValidVarType(self: Type, is_extern: bool) bool {
var ty = self;
while (true) switch (ty.zigTypeTag()) {
.Bool,
.Int,
.Float,
.ErrorSet,
.Enum,
.Frame,
.AnyFrame,
.Vector,
=> return true,
.Opaque => return is_extern,
.BoundFn,
.ComptimeFloat,
.ComptimeInt,
.EnumLiteral,
.NoReturn,
.Type,
.Void,
.Undefined,
.Null,
=> return false,
.Optional => {
var buf: Payload.PointerSimple = undefined;
return ty.optionalChild(&buf).isValidVarType(is_extern);
},
.Pointer, .Array => ty = ty.elemType(),
.ErrorUnion => @panic("TODO fn isValidVarType"),
.Fn => @panic("TODO fn isValidVarType"),
.Struct => @panic("TODO struct isValidVarType"),
.Union => @panic("TODO union isValidVarType"),
};
}
/// Asserts the type is a pointer or array type.
pub fn elemType(self: Type) Type {
return switch (self.tag()) {
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.c_longdouble,
.f16,
.f32,
.f64,
.f128,
.c_void,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.function,
.int_unsigned,
.int_signed,
.optional,
.optional_single_const_pointer,
.optional_single_mut_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> unreachable,
.array => self.cast(Payload.Array).?.elem_type,
.array_sentinel => self.cast(Payload.ArraySentinel).?.elem_type,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
=> self.castPointer().?.pointee_type,
.array_u8, .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),
.single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
.pointer => self.cast(Payload.Pointer).?.pointee_type,
};
}
/// Asserts that the type is an optional.
pub fn optionalChild(self: Type, buf: *Payload.PointerSimple) Type {
return switch (self.tag()) {
.optional => self.cast(Payload.Optional).?.child_type,
.optional_single_mut_pointer => {
buf.* = .{
.base = .{ .tag = .single_mut_pointer },
.pointee_type = self.castPointer().?.pointee_type,
};
return Type.initPayload(&buf.base);
},
.optional_single_const_pointer => {
buf.* = .{
.base = .{ .tag = .single_const_pointer },
.pointee_type = self.castPointer().?.pointee_type,
};
return Type.initPayload(&buf.base);
},
else => unreachable,
};
}
/// Asserts that the type is an optional.
/// Same as `optionalChild` but allocates the buffer if needed.
pub fn optionalChildAlloc(self: Type, allocator: *Allocator) !Type {
return switch (self.tag()) {
.optional => self.cast(Payload.Optional).?.child_type,
.optional_single_mut_pointer, .optional_single_const_pointer => {
const payload = try allocator.create(Payload.PointerSimple);
payload.* = .{
.base = .{
.tag = if (self.tag() == .optional_single_const_pointer)
.single_const_pointer
else
.single_mut_pointer,
},
.pointee_type = self.castPointer().?.pointee_type,
};
return Type.initPayload(&payload.base);
},
else => unreachable,
};
}
/// Asserts the type is an array or vector.
pub fn arrayLen(self: Type) u64 {
return switch (self.tag()) {
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.c_longdouble,
.f16,
.f32,
.f64,
.f128,
.c_void,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.function,
.pointer,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.int_unsigned,
.int_signed,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> unreachable,
.array => self.cast(Payload.Array).?.len,
.array_sentinel => self.cast(Payload.ArraySentinel).?.len,
.array_u8 => self.cast(Payload.Array_u8).?.len,
.array_u8_sentinel_0 => self.cast(Payload.Array_u8_Sentinel0).?.len,
};
}
/// Asserts the type is an array, pointer or vector.
pub fn sentinel(self: Type) ?Value {
return switch (self.tag()) {
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.c_longdouble,
.f16,
.f32,
.f64,
.f128,
.c_void,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.function,
.const_slice,
.mut_slice,
.const_slice_u8,
.int_unsigned,
.int_signed,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> unreachable,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.single_const_pointer_to_comptime_int,
.array,
.array_u8,
=> return null,
.pointer => return self.cast(Payload.Pointer).?.sentinel,
.array_sentinel => return self.cast(Payload.ArraySentinel).?.sentinel,
.array_u8_sentinel_0 => return Value.initTag(.zero),
};
}
/// Returns true if and only if the type is a fixed-width integer.
pub fn isInt(self: Type) bool {
return self.isSignedInt() or self.isUnsignedInt();
}
/// Returns true if and only if the type is a fixed-width, signed integer.
pub fn isSignedInt(self: Type) bool {
return switch (self.tag()) {
.f16,
.f32,
.f64,
.f128,
.c_longdouble,
.c_void,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.function,
.array,
.array_sentinel,
.array_u8,
.array_u8_sentinel_0,
.pointer,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.int_unsigned,
.u8,
.usize,
.c_ushort,
.c_uint,
.c_ulong,
.c_ulonglong,
.u16,
.u32,
.u64,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> false,
.int_signed,
.i8,
.isize,
.c_short,
.c_int,
.c_long,
.c_longlong,
.i16,
.i32,
.i64,
=> true,
};
}
/// Returns true if and only if the type is a fixed-width, unsigned integer.
pub fn isUnsignedInt(self: Type) bool {
return switch (self.tag()) {
.f16,
.f32,
.f64,
.f128,
.c_longdouble,
.c_void,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.function,
.array,
.array_sentinel,
.array_u8,
.array_u8_sentinel_0,
.pointer,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.int_signed,
.i8,
.isize,
.c_short,
.c_int,
.c_long,
.c_longlong,
.i16,
.i32,
.i64,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> false,
.int_unsigned,
.u8,
.usize,
.c_ushort,
.c_uint,
.c_ulong,
.c_ulonglong,
.u16,
.u32,
.u64,
=> true,
};
}
/// Asserts the type is an integer.
pub fn intInfo(self: Type, target: Target) struct { signed: bool, bits: u16 } {
return switch (self.tag()) {
.f16,
.f32,
.f64,
.f128,
.c_longdouble,
.c_void,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.function,
.array,
.array_sentinel,
.array_u8,
.array_u8_sentinel_0,
.pointer,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> unreachable,
.int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits },
.int_signed => .{ .signed = true, .bits = self.cast(Payload.IntSigned).?.bits },
.u8 => .{ .signed = false, .bits = 8 },
.i8 => .{ .signed = true, .bits = 8 },
.u16 => .{ .signed = false, .bits = 16 },
.i16 => .{ .signed = true, .bits = 16 },
.u32 => .{ .signed = false, .bits = 32 },
.i32 => .{ .signed = true, .bits = 32 },
.u64 => .{ .signed = false, .bits = 64 },
.i64 => .{ .signed = true, .bits = 64 },
.usize => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() },
.isize => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() },
.c_short => .{ .signed = true, .bits = CType.short.sizeInBits(target) },
.c_ushort => .{ .signed = false, .bits = CType.ushort.sizeInBits(target) },
.c_int => .{ .signed = true, .bits = CType.int.sizeInBits(target) },
.c_uint => .{ .signed = false, .bits = CType.uint.sizeInBits(target) },
.c_long => .{ .signed = true, .bits = CType.long.sizeInBits(target) },
.c_ulong => .{ .signed = false, .bits = CType.ulong.sizeInBits(target) },
.c_longlong => .{ .signed = true, .bits = CType.longlong.sizeInBits(target) },
.c_ulonglong => .{ .signed = false, .bits = CType.ulonglong.sizeInBits(target) },
};
}
pub fn isNamedInt(self: Type) bool {
return switch (self.tag()) {
.f16,
.f32,
.f64,
.f128,
.c_longdouble,
.c_void,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.function,
.array,
.array_sentinel,
.array_u8,
.array_u8_sentinel_0,
.pointer,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.int_unsigned,
.int_signed,
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> false,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
=> true,
};
}
pub fn isFloat(self: Type) bool {
return switch (self.tag()) {
.f16,
.f32,
.f64,
.f128,
.c_longdouble,
=> true,
else => false,
};
}
/// Asserts the type is a fixed-size float.
pub fn floatBits(self: Type, target: Target) u16 {
return switch (self.tag()) {
.f16 => 16,
.f32 => 32,
.f64 => 64,
.f128 => 128,
.c_longdouble => CType.longdouble.sizeInBits(target),
else => unreachable,
};
}
/// Asserts the type is a function.
pub fn fnParamLen(self: Type) usize {
return switch (self.tag()) {
.fn_noreturn_no_args => 0,
.fn_void_no_args => 0,
.fn_naked_noreturn_no_args => 0,
.fn_ccc_void_no_args => 0,
.function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).param_types.len,
.f16,
.f32,
.f64,
.f128,
.c_longdouble,
.c_void,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.array,
.array_sentinel,
.array_u8,
.array_u8_sentinel_0,
.pointer,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.int_unsigned,
.int_signed,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> unreachable,
};
}
/// Asserts the type is a function. The length of the slice must be at least the length
/// given by `fnParamLen`.
pub fn fnParamTypes(self: Type, types: []Type) void {
switch (self.tag()) {
.fn_noreturn_no_args => return,
.fn_void_no_args => return,
.fn_naked_noreturn_no_args => return,
.fn_ccc_void_no_args => return,
.function => {
const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
std.mem.copy(Type, types, payload.param_types);
},
.f16,
.f32,
.f64,
.f128,
.c_longdouble,
.c_void,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.array,
.array_sentinel,
.array_u8,
.array_u8_sentinel_0,
.pointer,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.int_unsigned,
.int_signed,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> unreachable,
}
}
/// Asserts the type is a function.
pub fn fnParamType(self: Type, index: usize) Type {
switch (self.tag()) {
.function => {
const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
return payload.param_types[index];
},
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.f16,
.f32,
.f64,
.f128,
.c_longdouble,
.c_void,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.array,
.array_sentinel,
.array_u8,
.array_u8_sentinel_0,
.pointer,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.int_unsigned,
.int_signed,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> unreachable,
}
}
/// Asserts the type is a function.
pub fn fnReturnType(self: Type) Type {
return switch (self.tag()) {
.fn_noreturn_no_args => Type.initTag(.noreturn),
.fn_naked_noreturn_no_args => Type.initTag(.noreturn),
.fn_void_no_args,
.fn_ccc_void_no_args,
=> Type.initTag(.void),
.function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).return_type,
.f16,
.f32,
.f64,
.f128,
.c_longdouble,
.c_void,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.array,
.array_sentinel,
.array_u8,
.array_u8_sentinel_0,
.pointer,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.int_unsigned,
.int_signed,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> unreachable,
};
}
/// Asserts the type is a function.
pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention {
return switch (self.tag()) {
.fn_noreturn_no_args => .Unspecified,
.fn_void_no_args => .Unspecified,
.fn_naked_noreturn_no_args => .Naked,
.fn_ccc_void_no_args => .C,
.function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).cc,
.f16,
.f32,
.f64,
.f128,
.c_longdouble,
.c_void,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.array,
.array_sentinel,
.array_u8,
.array_u8_sentinel_0,
.pointer,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.int_unsigned,
.int_signed,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> unreachable,
};
}
/// Asserts the type is a function.
pub fn fnIsVarArgs(self: Type) bool {
return switch (self.tag()) {
.fn_noreturn_no_args => false,
.fn_void_no_args => false,
.fn_naked_noreturn_no_args => false,
.fn_ccc_void_no_args => false,
.function => false,
.f16,
.f32,
.f64,
.f128,
.c_longdouble,
.c_void,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.array,
.array_sentinel,
.array_u8,
.array_u8_sentinel_0,
.pointer,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.int_unsigned,
.int_signed,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> unreachable,
};
}
pub fn isNumeric(self: Type) bool {
return switch (self.tag()) {
.f16,
.f32,
.f64,
.f128,
.c_longdouble,
.comptime_int,
.comptime_float,
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.int_unsigned,
.int_signed,
=> true,
.c_void,
.bool,
.void,
.type,
.anyerror,
.noreturn,
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.function,
.array,
.array_sentinel,
.array_u8,
.array_u8_sentinel_0,
.pointer,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> false,
};
}
pub fn onePossibleValue(self: Type) ?Value {
var ty = self;
while (true) switch (ty.tag()) {
.f16,
.f32,
.f64,
.f128,
.c_longdouble,
.comptime_int,
.comptime_float,
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.bool,
.type,
.anyerror,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.function,
.single_const_pointer_to_comptime_int,
.array_sentinel,
.array_u8_sentinel_0,
.const_slice_u8,
.const_slice,
.mut_slice,
.c_void,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.anyerror_void_error_union,
.anyframe_T,
.@"anyframe",
.error_union,
.error_set,
.error_set_single,
=> return null,
.void => return Value.initTag(.void_value),
.noreturn => return Value.initTag(.unreachable_value),
.@"null" => return Value.initTag(.null_value),
.@"undefined" => return Value.initTag(.undef),
.int_unsigned => {
if (ty.cast(Payload.IntUnsigned).?.bits == 0) {
return Value.initTag(.zero);
} else {
return null;
}
},
.int_signed => {
if (ty.cast(Payload.IntSigned).?.bits == 0) {
return Value.initTag(.zero);
} else {
return null;
}
},
.array, .array_u8 => {
if (ty.arrayLen() == 0)
return Value.initTag(.empty_array);
ty = ty.elemType();
continue;
},
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.single_const_pointer,
.single_mut_pointer,
=> {
const ptr = ty.castPointer().?;
ty = ptr.pointee_type;
continue;
},
.pointer => {
ty = ty.cast(Payload.Pointer).?.pointee_type;
continue;
},
};
}
pub fn isCPtr(self: Type) bool {
return switch (self.tag()) {
.f16,
.f32,
.f64,
.f128,
.c_longdouble,
.comptime_int,
.comptime_float,
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.bool,
.type,
.anyerror,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.function,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.c_void,
.void,
.noreturn,
.@"null",
.@"undefined",
.int_unsigned,
.int_signed,
.array,
.array_sentinel,
.array_u8,
.array_u8_sentinel_0,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.const_slice,
.mut_slice,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> return false,
.c_const_pointer,
.c_mut_pointer,
=> return true,
.pointer => self.cast(Payload.Pointer).?.size == .C,
};
}
pub fn isIndexable(self: Type) bool {
const zig_tag = self.zigTypeTag();
// TODO tuples are indexable
return zig_tag == .Array or zig_tag == .Vector or self.isSlice() or
(self.isSinglePointer() and self.elemType().zigTypeTag() == .Array);
}
/// This enum does not directly correspond to `std.builtin.TypeId` because
/// it has extra enum tags in it, as a way of using less memory. For example,
/// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
/// but with different alignment values, in this data structure they are represented
/// with different enum tags, because the the former requires more payload data than the latter.
/// See `zigTypeTag` for the function that corresponds to `std.builtin.TypeId`.
pub const Tag = enum {
// The first section of this enum are tags that require no payload.
u8,
i8,
u16,
i16,
u32,
i32,
u64,
i64,
usize,
isize,
c_short,
c_ushort,
c_int,
c_uint,
c_long,
c_ulong,
c_longlong,
c_ulonglong,
c_longdouble,
f16,
f32,
f64,
f128,
c_void,
bool,
void,
type,
anyerror,
comptime_int,
comptime_float,
noreturn,
enum_literal,
@"null",
@"undefined",
fn_noreturn_no_args,
fn_void_no_args,
fn_naked_noreturn_no_args,
fn_ccc_void_no_args,
single_const_pointer_to_comptime_int,
anyerror_void_error_union,
@"anyframe",
const_slice_u8, // See last_no_payload_tag below.
// After this, the tag requires a payload.
array_u8,
array_u8_sentinel_0,
array,
array_sentinel,
pointer,
single_const_pointer,
single_mut_pointer,
many_const_pointer,
many_mut_pointer,
c_const_pointer,
c_mut_pointer,
const_slice,
mut_slice,
int_signed,
int_unsigned,
function,
optional,
optional_single_mut_pointer,
optional_single_const_pointer,
error_union,
anyframe_T,
error_set,
error_set_single,
pub const last_no_payload_tag = Tag.const_slice_u8;
pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
};
pub const Payload = struct {
tag: Tag,
pub const Array_u8_Sentinel0 = struct {
base: Payload = Payload{ .tag = .array_u8_sentinel_0 },
len: u64,
};
pub const Array_u8 = struct {
base: Payload = Payload{ .tag = .array_u8 },
len: u64,
};
pub const Array = struct {
base: Payload = Payload{ .tag = .array },
len: u64,
elem_type: Type,
};
pub const ArraySentinel = struct {
base: Payload = Payload{ .tag = .array_sentinel },
len: u64,
sentinel: Value,
elem_type: Type,
};
pub const PointerSimple = struct {
base: Payload,
pointee_type: Type,
};
pub const IntSigned = struct {
base: Payload = Payload{ .tag = .int_signed },
bits: u16,
};
pub const IntUnsigned = struct {
base: Payload = Payload{ .tag = .int_unsigned },
bits: u16,
};
pub const Function = struct {
base: Payload = Payload{ .tag = .function },
param_types: []Type,
return_type: Type,
cc: std.builtin.CallingConvention,
};
pub const Optional = struct {
base: Payload = Payload{ .tag = .optional },
child_type: Type,
};
pub const Pointer = struct {
base: Payload = .{ .tag = .pointer },
pointee_type: Type,
sentinel: ?Value,
/// If zero use pointee_type.AbiAlign()
@"align": u32,
bit_offset: u16,
host_size: u16,
@"allowzero": bool,
mutable: bool,
@"volatile": bool,
size: std.builtin.TypeInfo.Pointer.Size,
};
pub const ErrorUnion = struct {
base: Payload = .{ .tag = .error_union },
error_set: Type,
payload: Type,
};
pub const AnyFrame = struct {
base: Payload = .{ .tag = .anyframe_T },
return_type: Type,
};
pub const ErrorSet = struct {
base: Payload = .{ .tag = .error_set },
decl: *Module.Decl,
};
pub const ErrorSetSingle = struct {
base: Payload = .{ .tag = .error_set_single },
/// memory is owned by `Module`
name: []const u8,
};
};
};
pub const CType = enum {
short,
ushort,
int,
uint,
long,
ulong,
longlong,
ulonglong,
longdouble,
pub fn sizeInBits(self: CType, target: Target) u16 {
const arch = target.cpu.arch;
switch (target.os.tag) {
.freestanding, .other => switch (target.cpu.arch) {
.msp430 => switch (self) {
.short,
.ushort,
.int,
.uint,
=> return 16,
.long,
.ulong,
=> return 32,
.longlong,
.ulonglong,
=> return 64,
.longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
},
else => switch (self) {
.short,
.ushort,
=> return 16,
.int,
.uint,
=> return 32,
.long,
.ulong,
=> return target.cpu.arch.ptrBitWidth(),
.longlong,
.ulonglong,
=> return 64,
.longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
},
},
.linux,
.macosx,
.freebsd,
.netbsd,
.dragonfly,
.openbsd,
.wasi,
.emscripten,
=> switch (self) {
.short,
.ushort,
=> return 16,
.int,
.uint,
=> return 32,
.long,
.ulong,
=> return target.cpu.arch.ptrBitWidth(),
.longlong,
.ulonglong,
=> return 64,
.longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
},
.windows, .uefi => switch (self) {
.short,
.ushort,
=> return 16,
.int,
.uint,
.long,
.ulong,
=> return 32,
.longlong,
.ulonglong,
=> return 64,
.longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
},
.ios => switch (self) {
.short,
.ushort,
=> return 16,
.int,
.uint,
=> return 32,
.long,
.ulong,
.longlong,
.ulonglong,
=> return 64,
.longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
},
.ananas,
.cloudabi,
.fuchsia,
.kfreebsd,
.lv2,
.solaris,
.haiku,
.minix,
.rtems,
.nacl,
.cnk,
.aix,
.cuda,
.nvcl,
.amdhsa,
.ps4,
.elfiamcu,
.tvos,
.watchos,
.mesa3d,
.contiki,
.amdpal,
.hermit,
.hurd,
=> @panic("TODO specify the C integer and float type sizes for this OS"),
}
}
};