aboutsummaryrefslogtreecommitdiff
path: root/src/type.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2020-09-21 18:38:55 -0700
committerAndrew Kelley <andrew@ziglang.org>2020-09-21 18:38:55 -0700
commit528832bd3a2e7b686ee84aef5887df740a6114db (patch)
tree90ccff9faa2ba2604c8538aeec0a147a4b01148c /src/type.zig
parentb9f61d401502f5d221e72c0d0e3bf448b11dcd68 (diff)
downloadzig-528832bd3a2e7b686ee84aef5887df740a6114db.tar.gz
zig-528832bd3a2e7b686ee84aef5887df740a6114db.zip
rename src-self-hosted/ to src/
Diffstat (limited to 'src/type.zig')
-rw-r--r--src/type.zig3075
1 files changed, 3075 insertions, 0 deletions
diff --git a/src/type.zig b/src/type.zig
new file mode 100644
index 0000000000..4966395512
--- /dev/null
+++ b/src/type.zig
@@ -0,0 +1,3075 @@
+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"),
+ }
+ }
+};