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"), } } };