mirror of
https://github.com/ziglang/zig.git
synced 2025-12-28 17:13:19 +00:00
New ZIR instructions:
* struct_decl_packed
* struct_decl_extern
New TZIR instruction: struct_field_ptr
Introduce `Module.Struct`. It uses `Value` to store default values and
abi alignments.
Implemented Sema.analyzeStructFieldPtr and zirStructDecl.
Some stuff I changed from `@panic("TODO")` to `log.warn("TODO")`.
It's becoming more clear that we need the lazy value mechanism soon;
Type is becoming unruly, and some of these functions have too much logic
given that they don't have any context for memory management or error
reporting.
3836 lines
119 KiB
Zig
3836 lines
119 KiB
Zig
const std = @import("std");
|
|
const Value = @import("value.zig").Value;
|
|
const assert = std.debug.assert;
|
|
const Allocator = std.mem.Allocator;
|
|
const Target = std.Target;
|
|
const Module = @import("Module.zig");
|
|
const log = std.log.scoped(.Type);
|
|
|
|
/// 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,
|
|
.u128,
|
|
.i128,
|
|
.usize,
|
|
.isize,
|
|
.c_short,
|
|
.c_ushort,
|
|
.c_int,
|
|
.c_uint,
|
|
.c_long,
|
|
.c_ulong,
|
|
.c_longlong,
|
|
.c_ulonglong,
|
|
.int_signed,
|
|
.int_unsigned,
|
|
=> return .Int,
|
|
|
|
.f16,
|
|
.f32,
|
|
.f64,
|
|
.f128,
|
|
.c_longdouble,
|
|
=> return .Float,
|
|
|
|
.c_void, .@"opaque" => 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,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
=> 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,
|
|
|
|
.empty_struct => return .Struct,
|
|
.empty_struct_literal => return .Struct,
|
|
.@"struct" => return .Struct,
|
|
|
|
.var_args_param => unreachable, // can be any type
|
|
}
|
|
}
|
|
|
|
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(std.meta.Tag(Tag), self.tag_if_small_enough));
|
|
} else {
|
|
return self.ptr_otherwise.tag;
|
|
}
|
|
}
|
|
|
|
/// Prefer `castTag` to this.
|
|
pub fn cast(self: Type, comptime T: type) ?*T {
|
|
if (@hasField(T, "base_tag")) {
|
|
return base.castTag(T.base_tag);
|
|
}
|
|
if (self.tag_if_small_enough < Tag.no_payload_count) {
|
|
return null;
|
|
}
|
|
inline for (@typeInfo(Tag).Enum.fields) |field| {
|
|
if (field.value < Tag.no_payload_count)
|
|
continue;
|
|
const t = @intToEnum(Tag, field.value);
|
|
if (self.ptr_otherwise.tag == t) {
|
|
if (T == t.Type()) {
|
|
return @fieldParentPtr(T, "base", self.ptr_otherwise);
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
unreachable;
|
|
}
|
|
|
|
pub fn castTag(self: Type, comptime t: Tag) ?*t.Type() {
|
|
if (self.tag_if_small_enough < Tag.no_payload_count)
|
|
return null;
|
|
|
|
if (self.ptr_otherwise.tag == t)
|
|
return @fieldParentPtr(t.Type(), "base", self.ptr_otherwise);
|
|
|
|
return null;
|
|
}
|
|
|
|
pub fn castPointer(self: Type) ?*Payload.ElemType {
|
|
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,
|
|
=> self.cast(Payload.ElemType),
|
|
|
|
.inferred_alloc_const => unreachable,
|
|
.inferred_alloc_mut => unreachable,
|
|
|
|
else => null,
|
|
};
|
|
}
|
|
|
|
pub fn ptrInfo(self: Type) Payload.Pointer {
|
|
switch (self.tag()) {
|
|
.single_const_pointer_to_comptime_int => return .{ .data = .{
|
|
.pointee_type = Type.initTag(.comptime_int),
|
|
.sentinel = null,
|
|
.@"align" = 0,
|
|
.bit_offset = 0,
|
|
.host_size = 0,
|
|
.@"allowzero" = false,
|
|
.mutable = false,
|
|
.@"volatile" = false,
|
|
.size = .One,
|
|
} },
|
|
.const_slice_u8 => return .{ .data = .{
|
|
.pointee_type = Type.initTag(.u8),
|
|
.sentinel = null,
|
|
.@"align" = 0,
|
|
.bit_offset = 0,
|
|
.host_size = 0,
|
|
.@"allowzero" = false,
|
|
.mutable = false,
|
|
.@"volatile" = false,
|
|
.size = .Slice,
|
|
} },
|
|
.single_const_pointer => return .{ .data = .{
|
|
.pointee_type = self.castPointer().?.data,
|
|
.sentinel = null,
|
|
.@"align" = 0,
|
|
.bit_offset = 0,
|
|
.host_size = 0,
|
|
.@"allowzero" = false,
|
|
.mutable = false,
|
|
.@"volatile" = false,
|
|
.size = .One,
|
|
} },
|
|
.single_mut_pointer => return .{ .data = .{
|
|
.pointee_type = self.castPointer().?.data,
|
|
.sentinel = null,
|
|
.@"align" = 0,
|
|
.bit_offset = 0,
|
|
.host_size = 0,
|
|
.@"allowzero" = false,
|
|
.mutable = true,
|
|
.@"volatile" = false,
|
|
.size = .One,
|
|
} },
|
|
.many_const_pointer => return .{ .data = .{
|
|
.pointee_type = self.castPointer().?.data,
|
|
.sentinel = null,
|
|
.@"align" = 0,
|
|
.bit_offset = 0,
|
|
.host_size = 0,
|
|
.@"allowzero" = false,
|
|
.mutable = false,
|
|
.@"volatile" = false,
|
|
.size = .Many,
|
|
} },
|
|
.many_mut_pointer => return .{ .data = .{
|
|
.pointee_type = self.castPointer().?.data,
|
|
.sentinel = null,
|
|
.@"align" = 0,
|
|
.bit_offset = 0,
|
|
.host_size = 0,
|
|
.@"allowzero" = false,
|
|
.mutable = true,
|
|
.@"volatile" = false,
|
|
.size = .Many,
|
|
} },
|
|
.c_const_pointer => return .{ .data = .{
|
|
.pointee_type = self.castPointer().?.data,
|
|
.sentinel = null,
|
|
.@"align" = 0,
|
|
.bit_offset = 0,
|
|
.host_size = 0,
|
|
.@"allowzero" = false,
|
|
.mutable = false,
|
|
.@"volatile" = false,
|
|
.size = .C,
|
|
} },
|
|
.c_mut_pointer => return .{ .data = .{
|
|
.pointee_type = self.castPointer().?.data,
|
|
.sentinel = null,
|
|
.@"align" = 0,
|
|
.bit_offset = 0,
|
|
.host_size = 0,
|
|
.@"allowzero" = false,
|
|
.mutable = true,
|
|
.@"volatile" = false,
|
|
.size = .C,
|
|
} },
|
|
.const_slice => return .{ .data = .{
|
|
.pointee_type = self.castPointer().?.data,
|
|
.sentinel = null,
|
|
.@"align" = 0,
|
|
.bit_offset = 0,
|
|
.host_size = 0,
|
|
.@"allowzero" = false,
|
|
.mutable = false,
|
|
.@"volatile" = false,
|
|
.size = .Slice,
|
|
} },
|
|
.mut_slice => return .{ .data = .{
|
|
.pointee_type = self.castPointer().?.data,
|
|
.sentinel = null,
|
|
.@"align" = 0,
|
|
.bit_offset = 0,
|
|
.host_size = 0,
|
|
.@"allowzero" = false,
|
|
.mutable = true,
|
|
.@"volatile" = false,
|
|
.size = .Slice,
|
|
} },
|
|
|
|
.pointer => return self.castTag(.pointer).?.*,
|
|
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
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 => {
|
|
const info_a = a.ptrInfo().data;
|
|
const info_b = b.ptrInfo().data;
|
|
if (!info_a.pointee_type.eql(info_b.pointee_type))
|
|
return false;
|
|
if (info_a.size != info_b.size)
|
|
return false;
|
|
if (info_a.mutable != info_b.mutable)
|
|
return false;
|
|
if (info_a.@"volatile" != info_b.@"volatile")
|
|
return false;
|
|
if (info_a.@"allowzero" != info_b.@"allowzero")
|
|
return false;
|
|
if (info_a.bit_offset != info_b.bit_offset)
|
|
return false;
|
|
if (info_a.host_size != info_b.host_size)
|
|
return false;
|
|
|
|
const sentinel_a = info_a.sentinel;
|
|
const sentinel_b = info_b.sentinel;
|
|
if (sentinel_a) |sa| {
|
|
if (sentinel_b) |sb| {
|
|
if (!sa.eql(sb))
|
|
return false;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
if (sentinel_b != null)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
},
|
|
.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.signedness == info_b.signedness 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;
|
|
}
|
|
if (a.fnIsVarArgs() != b.fnIsVarArgs())
|
|
return false;
|
|
return true;
|
|
},
|
|
.Optional => {
|
|
var buf_a: Payload.ElemType = undefined;
|
|
var buf_b: Payload.ElemType = 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.signedness);
|
|
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());
|
|
}
|
|
std.hash.autoHash(&hasher, self.fnIsVarArgs());
|
|
},
|
|
.Optional => {
|
|
var buf: Payload.ElemType = 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,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.var_args_param,
|
|
.empty_struct_literal,
|
|
=> unreachable,
|
|
|
|
.array_u8,
|
|
.array_u8_sentinel_0,
|
|
=> return self.copyPayloadShallow(allocator, Payload.Len),
|
|
|
|
.single_const_pointer,
|
|
.single_mut_pointer,
|
|
.many_const_pointer,
|
|
.many_mut_pointer,
|
|
.c_const_pointer,
|
|
.c_mut_pointer,
|
|
.const_slice,
|
|
.mut_slice,
|
|
.optional,
|
|
.optional_single_mut_pointer,
|
|
.optional_single_const_pointer,
|
|
=> return self.copyPayloadShallow(allocator, Payload.ElemType),
|
|
|
|
.int_signed,
|
|
.int_unsigned,
|
|
=> return self.copyPayloadShallow(allocator, Payload.Bits),
|
|
|
|
.array => {
|
|
const payload = self.castTag(.array).?.data;
|
|
return Tag.array.create(allocator, .{
|
|
.len = payload.len,
|
|
.elem_type = try payload.elem_type.copy(allocator),
|
|
});
|
|
},
|
|
.array_sentinel => {
|
|
const payload = self.castTag(.array_sentinel).?.data;
|
|
return Tag.array_sentinel.create(allocator, .{
|
|
.len = payload.len,
|
|
.sentinel = try payload.sentinel.copy(allocator),
|
|
.elem_type = try payload.elem_type.copy(allocator),
|
|
});
|
|
},
|
|
.function => {
|
|
const payload = self.castTag(.function).?.data;
|
|
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);
|
|
}
|
|
return Tag.function.create(allocator, .{
|
|
.return_type = try payload.return_type.copy(allocator),
|
|
.param_types = param_types,
|
|
.cc = payload.cc,
|
|
.is_var_args = payload.is_var_args,
|
|
});
|
|
},
|
|
.pointer => {
|
|
const payload = self.castTag(.pointer).?.data;
|
|
const sent: ?Value = if (payload.sentinel) |some|
|
|
try some.copy(allocator)
|
|
else
|
|
null;
|
|
return Tag.pointer.create(allocator, .{
|
|
.pointee_type = try payload.pointee_type.copy(allocator),
|
|
.sentinel = sent,
|
|
.@"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,
|
|
});
|
|
},
|
|
.error_union => {
|
|
const payload = self.castTag(.error_union).?.data;
|
|
return Tag.error_union.create(allocator, .{
|
|
.error_set = try payload.error_set.copy(allocator),
|
|
.payload = try payload.payload.copy(allocator),
|
|
});
|
|
},
|
|
.error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
|
|
.error_set_single => return self.copyPayloadShallow(allocator, Payload.Name),
|
|
.empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope),
|
|
.@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct),
|
|
.@"opaque" => return self.copyPayloadShallow(allocator, Payload.Opaque),
|
|
}
|
|
}
|
|
|
|
fn copyPayloadShallow(self: Type, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Type {
|
|
const payload = self.cast(T).?;
|
|
const new_payload = try allocator.create(T);
|
|
new_payload.* = payload.*;
|
|
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,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.var_args_param,
|
|
=> 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)"),
|
|
|
|
.empty_struct, .empty_struct_literal => return out_stream.writeAll("struct {}"),
|
|
.@"struct" => return out_stream.writeAll("(struct)"),
|
|
.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 = ty.castTag(.function).?.data;
|
|
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);
|
|
}
|
|
if (payload.is_var_args) {
|
|
if (payload.param_types.len != 0) {
|
|
try out_stream.writeAll(", ");
|
|
}
|
|
try out_stream.writeAll("...");
|
|
}
|
|
try out_stream.writeAll(") callconv(.");
|
|
try out_stream.writeAll(@tagName(payload.cc));
|
|
try out_stream.writeAll(")");
|
|
ty = payload.return_type;
|
|
continue;
|
|
},
|
|
|
|
.array_u8 => {
|
|
const len = ty.castTag(.array_u8).?.data;
|
|
return out_stream.print("[{d}]u8", .{len});
|
|
},
|
|
.array_u8_sentinel_0 => {
|
|
const len = ty.castTag(.array_u8_sentinel_0).?.data;
|
|
return out_stream.print("[{d}:0]u8", .{len});
|
|
},
|
|
.array => {
|
|
const payload = ty.castTag(.array).?.data;
|
|
try out_stream.print("[{d}]", .{payload.len});
|
|
ty = payload.elem_type;
|
|
continue;
|
|
},
|
|
.array_sentinel => {
|
|
const payload = ty.castTag(.array_sentinel).?.data;
|
|
try out_stream.print("[{d}:{}]", .{ payload.len, payload.sentinel });
|
|
ty = payload.elem_type;
|
|
continue;
|
|
},
|
|
.single_const_pointer => {
|
|
const pointee_type = ty.castTag(.single_const_pointer).?.data;
|
|
try out_stream.writeAll("*const ");
|
|
ty = pointee_type;
|
|
continue;
|
|
},
|
|
.single_mut_pointer => {
|
|
const pointee_type = ty.castTag(.single_mut_pointer).?.data;
|
|
try out_stream.writeAll("*");
|
|
ty = pointee_type;
|
|
continue;
|
|
},
|
|
.many_const_pointer => {
|
|
const pointee_type = ty.castTag(.many_const_pointer).?.data;
|
|
try out_stream.writeAll("[*]const ");
|
|
ty = pointee_type;
|
|
continue;
|
|
},
|
|
.many_mut_pointer => {
|
|
const pointee_type = ty.castTag(.many_mut_pointer).?.data;
|
|
try out_stream.writeAll("[*]");
|
|
ty = pointee_type;
|
|
continue;
|
|
},
|
|
.c_const_pointer => {
|
|
const pointee_type = ty.castTag(.c_const_pointer).?.data;
|
|
try out_stream.writeAll("[*c]const ");
|
|
ty = pointee_type;
|
|
continue;
|
|
},
|
|
.c_mut_pointer => {
|
|
const pointee_type = ty.castTag(.c_mut_pointer).?.data;
|
|
try out_stream.writeAll("[*c]");
|
|
ty = pointee_type;
|
|
continue;
|
|
},
|
|
.const_slice => {
|
|
const pointee_type = ty.castTag(.const_slice).?.data;
|
|
try out_stream.writeAll("[]const ");
|
|
ty = pointee_type;
|
|
continue;
|
|
},
|
|
.mut_slice => {
|
|
const pointee_type = ty.castTag(.mut_slice).?.data;
|
|
try out_stream.writeAll("[]");
|
|
ty = pointee_type;
|
|
continue;
|
|
},
|
|
.int_signed => {
|
|
const bits = ty.castTag(.int_signed).?.data;
|
|
return out_stream.print("i{d}", .{bits});
|
|
},
|
|
.int_unsigned => {
|
|
const bits = ty.castTag(.int_unsigned).?.data;
|
|
return out_stream.print("u{d}", .{bits});
|
|
},
|
|
.optional => {
|
|
const child_type = ty.castTag(.optional).?.data;
|
|
try out_stream.writeByte('?');
|
|
ty = child_type;
|
|
continue;
|
|
},
|
|
.optional_single_const_pointer => {
|
|
const pointee_type = ty.castTag(.optional_single_const_pointer).?.data;
|
|
try out_stream.writeAll("?*const ");
|
|
ty = pointee_type;
|
|
continue;
|
|
},
|
|
.optional_single_mut_pointer => {
|
|
const pointee_type = ty.castTag(.optional_single_mut_pointer).?.data;
|
|
try out_stream.writeAll("?*");
|
|
ty = pointee_type;
|
|
continue;
|
|
},
|
|
|
|
.pointer => {
|
|
const payload = ty.castTag(.pointer).?.data;
|
|
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({d}", .{payload.@"align"});
|
|
|
|
if (payload.bit_offset != 0) {
|
|
try out_stream.print(":{d}:{d}", .{ 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 = ty.castTag(.error_union).?.data;
|
|
try payload.error_set.format("", .{}, out_stream);
|
|
try out_stream.writeAll("!");
|
|
ty = payload.payload;
|
|
continue;
|
|
},
|
|
.error_set => {
|
|
const error_set = ty.castTag(.error_set).?.data;
|
|
return out_stream.writeAll(std.mem.spanZ(error_set.owner_decl.name));
|
|
},
|
|
.error_set_single => {
|
|
const name = ty.castTag(.error_set_single).?.data;
|
|
return out_stream.print("error{{{s}}}", .{name});
|
|
},
|
|
.inferred_alloc_const => return out_stream.writeAll("(inferred_alloc_const)"),
|
|
.inferred_alloc_mut => return out_stream.writeAll("(inferred_alloc_mut)"),
|
|
// TODO use declaration name
|
|
.@"opaque" => return out_stream.writeAll("opaque {}"),
|
|
}
|
|
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),
|
|
.inferred_alloc_const => unreachable,
|
|
.inferred_alloc_mut => unreachable,
|
|
else => return Value.Tag.ty.create(allocator, self),
|
|
}
|
|
}
|
|
|
|
pub fn hasCodeGenBits(self: Type) bool {
|
|
return switch (self.tag()) {
|
|
.u8,
|
|
.i8,
|
|
.u16,
|
|
.i16,
|
|
.u32,
|
|
.i32,
|
|
.u64,
|
|
.i64,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
=> true,
|
|
|
|
.@"struct" => {
|
|
// TODO introduce lazy value mechanism
|
|
const struct_obj = self.castTag(.@"struct").?.data;
|
|
for (struct_obj.fields.entries.items) |entry| {
|
|
if (entry.value.ty.hasCodeGenBits())
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
},
|
|
|
|
// 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, .int_unsigned => self.cast(Payload.Bits).?.data != 0,
|
|
|
|
.error_union => {
|
|
const payload = self.castTag(.error_union).?.data;
|
|
return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits();
|
|
},
|
|
|
|
.c_void,
|
|
.void,
|
|
.type,
|
|
.comptime_int,
|
|
.comptime_float,
|
|
.noreturn,
|
|
.@"null",
|
|
.@"undefined",
|
|
.enum_literal,
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.@"opaque",
|
|
=> false,
|
|
|
|
.inferred_alloc_const => unreachable,
|
|
.inferred_alloc_mut => unreachable,
|
|
.var_args_param => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn isNoReturn(self: Type) bool {
|
|
const definitely_correct_result = self.zigTypeTag() == .NoReturn;
|
|
const fast_result = self.tag_if_small_enough == @enumToInt(Tag.noreturn);
|
|
assert(fast_result == definitely_correct_result);
|
|
return fast_result;
|
|
}
|
|
|
|
pub fn ptrAlignment(self: Type, target: Target) u32 {
|
|
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,
|
|
=> return self.cast(Payload.ElemType).?.data.abiAlignment(target),
|
|
|
|
.const_slice_u8 => return 1,
|
|
|
|
.pointer => {
|
|
const ptr_info = self.castTag(.pointer).?.data;
|
|
if (ptr_info.@"align" != 0) {
|
|
return ptr_info.@"align";
|
|
} else {
|
|
return ptr_info.pointee_type.abiAlignment();
|
|
}
|
|
},
|
|
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
/// 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, .armeb => 4,
|
|
.aarch64, .aarch64_32, .aarch64_be => 4,
|
|
.riscv64 => 2,
|
|
else => 1,
|
|
},
|
|
|
|
.i16, .u16 => return 2,
|
|
.i32, .u32 => return 4,
|
|
.i64, .u64 => return 8,
|
|
.u128, .i128 => return 16,
|
|
|
|
.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,
|
|
.pointer,
|
|
=> 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 = self.cast(Payload.Bits).?.data;
|
|
return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
|
|
},
|
|
|
|
.optional => {
|
|
var buf: Payload.ElemType = 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.castTag(.error_union).?.data;
|
|
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");
|
|
},
|
|
|
|
.@"struct" => {
|
|
@panic("TODO abiAlignment struct");
|
|
},
|
|
|
|
.c_void,
|
|
.void,
|
|
.type,
|
|
.comptime_int,
|
|
.comptime_float,
|
|
.noreturn,
|
|
.@"null",
|
|
.@"undefined",
|
|
.enum_literal,
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> 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,
|
|
.empty_struct => unreachable,
|
|
.empty_struct_literal => unreachable,
|
|
.inferred_alloc_const => unreachable,
|
|
.inferred_alloc_mut => unreachable,
|
|
.@"opaque" => unreachable,
|
|
.var_args_param => unreachable,
|
|
|
|
.@"struct" => {
|
|
@panic("TODO abiSize struct");
|
|
},
|
|
|
|
.u8,
|
|
.i8,
|
|
.bool,
|
|
=> return 1,
|
|
|
|
.array_u8 => self.castTag(.array_u8).?.data,
|
|
.array_u8_sentinel_0 => self.castTag(.array_u8_sentinel_0).?.data + 1,
|
|
.array => {
|
|
const payload = self.castTag(.array).?.data;
|
|
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 = self.castTag(.array_sentinel).?.data;
|
|
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,
|
|
.u128, .i128 => return 16,
|
|
|
|
.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 = self.cast(Payload.Bits).?.data;
|
|
return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
|
|
},
|
|
|
|
.optional => {
|
|
var buf: Payload.ElemType = 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.castTag(.error_union).?.data;
|
|
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,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.@"struct",
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> false,
|
|
|
|
.single_const_pointer,
|
|
.single_mut_pointer,
|
|
.single_const_pointer_to_comptime_int,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
=> true,
|
|
|
|
.pointer => self.castTag(.pointer).?.data.size == .One,
|
|
};
|
|
}
|
|
|
|
/// Asserts the `Type` is a pointer.
|
|
pub fn ptrSize(self: Type) std.builtin.TypeInfo.Pointer.Size {
|
|
return switch (self.tag()) {
|
|
.u8,
|
|
.i8,
|
|
.u16,
|
|
.i16,
|
|
.u32,
|
|
.i32,
|
|
.u64,
|
|
.i64,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.optional,
|
|
.optional_single_mut_pointer,
|
|
.optional_single_const_pointer,
|
|
.enum_literal,
|
|
.error_union,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.@"opaque",
|
|
.@"struct",
|
|
.var_args_param,
|
|
=> unreachable,
|
|
|
|
.const_slice,
|
|
.mut_slice,
|
|
.const_slice_u8,
|
|
=> .Slice,
|
|
|
|
.many_const_pointer,
|
|
.many_mut_pointer,
|
|
=> .Many,
|
|
|
|
.c_const_pointer,
|
|
.c_mut_pointer,
|
|
=> .C,
|
|
|
|
.single_const_pointer,
|
|
.single_mut_pointer,
|
|
.single_const_pointer_to_comptime_int,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
=> .One,
|
|
|
|
.pointer => self.castTag(.pointer).?.data.size,
|
|
};
|
|
}
|
|
|
|
pub fn isSlice(self: Type) bool {
|
|
return switch (self.tag()) {
|
|
.u8,
|
|
.i8,
|
|
.u16,
|
|
.i16,
|
|
.u32,
|
|
.i32,
|
|
.u64,
|
|
.i64,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.@"struct",
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> false,
|
|
|
|
.const_slice,
|
|
.mut_slice,
|
|
.const_slice_u8,
|
|
=> true,
|
|
|
|
.pointer => self.castTag(.pointer).?.data.size == .Slice,
|
|
};
|
|
}
|
|
|
|
pub fn isConstPtr(self: Type) bool {
|
|
return switch (self.tag()) {
|
|
.u8,
|
|
.i8,
|
|
.u16,
|
|
.i16,
|
|
.u32,
|
|
.i32,
|
|
.u64,
|
|
.i64,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.@"struct",
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> false,
|
|
|
|
.single_const_pointer,
|
|
.many_const_pointer,
|
|
.c_const_pointer,
|
|
.single_const_pointer_to_comptime_int,
|
|
.const_slice_u8,
|
|
.const_slice,
|
|
=> true,
|
|
|
|
.pointer => !self.castTag(.pointer).?.data.mutable,
|
|
};
|
|
}
|
|
|
|
pub fn isVolatilePtr(self: Type) bool {
|
|
return switch (self.tag()) {
|
|
.u8,
|
|
.i8,
|
|
.u16,
|
|
.i16,
|
|
.u32,
|
|
.i32,
|
|
.u64,
|
|
.i64,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.@"struct",
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> false,
|
|
|
|
.pointer => {
|
|
const payload = self.castTag(.pointer).?.data;
|
|
return payload.@"volatile";
|
|
},
|
|
};
|
|
}
|
|
|
|
pub fn isAllowzeroPtr(self: Type) bool {
|
|
return switch (self.tag()) {
|
|
.u8,
|
|
.i8,
|
|
.u16,
|
|
.i16,
|
|
.u32,
|
|
.i32,
|
|
.u64,
|
|
.i64,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.@"struct",
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> false,
|
|
|
|
.pointer => {
|
|
const payload = self.castTag(.pointer).?.data;
|
|
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.ElemType = 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.ElemType = undefined;
|
|
return ty.optionalChild(&buf).isValidVarType(is_extern);
|
|
},
|
|
.Pointer, .Array => ty = ty.elemType(),
|
|
.ErrorUnion => ty = ty.errorUnionChild(),
|
|
|
|
.Fn => @panic("TODO fn isValidVarType"),
|
|
.Struct => {
|
|
// TODO this is not always correct; introduce lazy value mechanism
|
|
// and here we need to force a resolve of "type requires comptime".
|
|
return true;
|
|
},
|
|
.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 => unreachable,
|
|
.i8 => unreachable,
|
|
.u16 => unreachable,
|
|
.i16 => unreachable,
|
|
.u32 => unreachable,
|
|
.i32 => unreachable,
|
|
.u64 => unreachable,
|
|
.i64 => unreachable,
|
|
.u128 => unreachable,
|
|
.i128 => unreachable,
|
|
.usize => unreachable,
|
|
.isize => unreachable,
|
|
.c_short => unreachable,
|
|
.c_ushort => unreachable,
|
|
.c_int => unreachable,
|
|
.c_uint => unreachable,
|
|
.c_long => unreachable,
|
|
.c_ulong => unreachable,
|
|
.c_longlong => unreachable,
|
|
.c_ulonglong => unreachable,
|
|
.c_longdouble => unreachable,
|
|
.f16 => unreachable,
|
|
.f32 => unreachable,
|
|
.f64 => unreachable,
|
|
.f128 => unreachable,
|
|
.c_void => unreachable,
|
|
.bool => unreachable,
|
|
.void => unreachable,
|
|
.type => unreachable,
|
|
.anyerror => unreachable,
|
|
.comptime_int => unreachable,
|
|
.comptime_float => unreachable,
|
|
.noreturn => unreachable,
|
|
.@"null" => unreachable,
|
|
.@"undefined" => unreachable,
|
|
.fn_noreturn_no_args => unreachable,
|
|
.fn_void_no_args => unreachable,
|
|
.fn_naked_noreturn_no_args => unreachable,
|
|
.fn_ccc_void_no_args => unreachable,
|
|
.function => unreachable,
|
|
.int_unsigned => unreachable,
|
|
.int_signed => unreachable,
|
|
.optional => unreachable,
|
|
.optional_single_const_pointer => unreachable,
|
|
.optional_single_mut_pointer => unreachable,
|
|
.enum_literal => unreachable,
|
|
.error_union => unreachable,
|
|
.anyerror_void_error_union => unreachable,
|
|
.error_set => unreachable,
|
|
.error_set_single => unreachable,
|
|
.@"struct" => unreachable,
|
|
.empty_struct => unreachable,
|
|
.empty_struct_literal => unreachable,
|
|
.inferred_alloc_const => unreachable,
|
|
.inferred_alloc_mut => unreachable,
|
|
.@"opaque" => unreachable,
|
|
.var_args_param => unreachable,
|
|
|
|
.array => self.castTag(.array).?.data.elem_type,
|
|
.array_sentinel => self.castTag(.array_sentinel).?.data.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().?.data,
|
|
.array_u8, .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),
|
|
.single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
|
|
.pointer => self.castTag(.pointer).?.data.pointee_type,
|
|
};
|
|
}
|
|
|
|
/// Asserts that the type is an optional.
|
|
/// Resulting `Type` will have inner memory referencing `buf`.
|
|
pub fn optionalChild(self: Type, buf: *Payload.ElemType) Type {
|
|
return switch (self.tag()) {
|
|
.optional => self.castTag(.optional).?.data,
|
|
.optional_single_mut_pointer => {
|
|
buf.* = .{
|
|
.base = .{ .tag = .single_mut_pointer },
|
|
.data = self.castPointer().?.data,
|
|
};
|
|
return Type.initPayload(&buf.base);
|
|
},
|
|
.optional_single_const_pointer => {
|
|
buf.* = .{
|
|
.base = .{ .tag = .single_const_pointer },
|
|
.data = self.castPointer().?.data,
|
|
};
|
|
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 {
|
|
switch (self.tag()) {
|
|
.optional => return self.castTag(.optional).?.data,
|
|
.optional_single_mut_pointer => {
|
|
return Tag.single_mut_pointer.create(allocator, self.castPointer().?.data);
|
|
},
|
|
.optional_single_const_pointer => {
|
|
return Tag.single_const_pointer.create(allocator, self.castPointer().?.data);
|
|
},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
/// Asserts that the type is an error union.
|
|
pub fn errorUnionChild(self: Type) Type {
|
|
return switch (self.tag()) {
|
|
.anyerror_void_error_union => Type.initTag(.anyerror),
|
|
.error_union => {
|
|
const payload = self.castTag(.error_union).?;
|
|
return payload.data.payload;
|
|
},
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn errorUnionSet(self: Type) Type {
|
|
return switch (self.tag()) {
|
|
.anyerror_void_error_union => Type.initTag(.anyerror),
|
|
.error_union => {
|
|
const payload = self.castTag(.error_union).?;
|
|
return payload.data.error_set;
|
|
},
|
|
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,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.@"struct",
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> unreachable,
|
|
|
|
.array => self.castTag(.array).?.data.len,
|
|
.array_sentinel => self.castTag(.array_sentinel).?.data.len,
|
|
.array_u8 => self.castTag(.array_u8).?.data,
|
|
.array_u8_sentinel_0 => self.castTag(.array_u8_sentinel_0).?.data,
|
|
};
|
|
}
|
|
|
|
/// 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,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.@"struct",
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> 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.castTag(.pointer).?.data.sentinel,
|
|
.array_sentinel => return self.castTag(.array_sentinel).?.data.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,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.@"struct",
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> false,
|
|
|
|
.int_signed,
|
|
.i8,
|
|
.isize,
|
|
.c_short,
|
|
.c_int,
|
|
.c_long,
|
|
.c_longlong,
|
|
.i16,
|
|
.i32,
|
|
.i64,
|
|
.u128,
|
|
.i128,
|
|
=> 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,
|
|
.u128,
|
|
.i128,
|
|
.optional,
|
|
.optional_single_mut_pointer,
|
|
.optional_single_const_pointer,
|
|
.enum_literal,
|
|
.error_union,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.@"struct",
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> 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 { signedness: std.builtin.Signedness, 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,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.@"struct",
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> unreachable,
|
|
|
|
.int_unsigned => .{
|
|
.signedness = .unsigned,
|
|
.bits = self.castTag(.int_unsigned).?.data,
|
|
},
|
|
.int_signed => .{
|
|
.signedness = .signed,
|
|
.bits = self.castTag(.int_signed).?.data,
|
|
},
|
|
.u8 => .{ .signedness = .unsigned, .bits = 8 },
|
|
.i8 => .{ .signedness = .signed, .bits = 8 },
|
|
.u16 => .{ .signedness = .unsigned, .bits = 16 },
|
|
.i16 => .{ .signedness = .signed, .bits = 16 },
|
|
.u32 => .{ .signedness = .unsigned, .bits = 32 },
|
|
.i32 => .{ .signedness = .signed, .bits = 32 },
|
|
.u64 => .{ .signedness = .unsigned, .bits = 64 },
|
|
.i64 => .{ .signedness = .signed, .bits = 64 },
|
|
.u128 => .{ .signedness = .unsigned, .bits = 128 },
|
|
.i128 => .{ .signedness = .signed, .bits = 128 },
|
|
.usize => .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() },
|
|
.isize => .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() },
|
|
.c_short => .{ .signedness = .signed, .bits = CType.short.sizeInBits(target) },
|
|
.c_ushort => .{ .signedness = .unsigned, .bits = CType.ushort.sizeInBits(target) },
|
|
.c_int => .{ .signedness = .signed, .bits = CType.int.sizeInBits(target) },
|
|
.c_uint => .{ .signedness = .unsigned, .bits = CType.uint.sizeInBits(target) },
|
|
.c_long => .{ .signedness = .signed, .bits = CType.long.sizeInBits(target) },
|
|
.c_ulong => .{ .signedness = .unsigned, .bits = CType.ulong.sizeInBits(target) },
|
|
.c_longlong => .{ .signedness = .signed, .bits = CType.longlong.sizeInBits(target) },
|
|
.c_ulonglong => .{ .signedness = .unsigned, .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,
|
|
.u128,
|
|
.i128,
|
|
.optional,
|
|
.optional_single_mut_pointer,
|
|
.optional_single_const_pointer,
|
|
.enum_literal,
|
|
.error_union,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.@"struct",
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> 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 => self.castTag(.function).?.data.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,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.@"struct",
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> 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 = self.castTag(.function).?.data;
|
|
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,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.@"struct",
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> unreachable,
|
|
}
|
|
}
|
|
|
|
/// Asserts the type is a function.
|
|
pub fn fnParamType(self: Type, index: usize) Type {
|
|
switch (self.tag()) {
|
|
.function => {
|
|
const payload = self.castTag(.function).?.data;
|
|
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,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.@"struct",
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> 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 => self.castTag(.function).?.data.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,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.@"struct",
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> 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 => self.castTag(.function).?.data.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,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.@"struct",
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> 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 => self.castTag(.function).?.data.is_var_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,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.@"struct",
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> 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,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.@"struct",
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> 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,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> return null,
|
|
|
|
.@"struct" => {
|
|
log.warn("TODO implement Type.onePossibleValue for structs", .{});
|
|
return null;
|
|
},
|
|
|
|
.empty_struct, .empty_struct_literal => return Value.initTag(.empty_struct_value),
|
|
.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, .int_signed => {
|
|
if (ty.cast(Payload.Bits).?.data == 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,
|
|
=> {
|
|
ty = ty.castPointer().?.data;
|
|
continue;
|
|
},
|
|
.pointer => {
|
|
ty = ty.castTag(.pointer).?.data.pointee_type;
|
|
continue;
|
|
},
|
|
.inferred_alloc_const => unreachable,
|
|
.inferred_alloc_mut => unreachable,
|
|
};
|
|
}
|
|
|
|
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,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.@"struct",
|
|
.empty_struct,
|
|
.empty_struct_literal,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.@"opaque",
|
|
.var_args_param,
|
|
=> return false,
|
|
|
|
.c_const_pointer,
|
|
.c_mut_pointer,
|
|
=> return true,
|
|
|
|
.pointer => self.castTag(.pointer).?.data.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);
|
|
}
|
|
|
|
/// Asserts that the type is a container. (note: ErrorSet is not a container).
|
|
pub fn getContainerScope(self: Type) *Module.Scope.Container {
|
|
return switch (self.tag()) {
|
|
.f16,
|
|
.f32,
|
|
.f64,
|
|
.f128,
|
|
.c_longdouble,
|
|
.comptime_int,
|
|
.comptime_float,
|
|
.u8,
|
|
.i8,
|
|
.u16,
|
|
.i16,
|
|
.u32,
|
|
.i32,
|
|
.u64,
|
|
.i64,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.anyerror_void_error_union,
|
|
.error_set,
|
|
.error_set_single,
|
|
.c_const_pointer,
|
|
.c_mut_pointer,
|
|
.pointer,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.var_args_param,
|
|
.empty_struct_literal,
|
|
=> unreachable,
|
|
|
|
.@"struct" => &self.castTag(.@"struct").?.data.container,
|
|
.empty_struct => self.castTag(.empty_struct).?.data,
|
|
.@"opaque" => &self.castTag(.@"opaque").?.data,
|
|
};
|
|
}
|
|
|
|
/// Asserts that self.zigTypeTag() == .Int.
|
|
pub fn minInt(self: Type, arena: *std.heap.ArenaAllocator, target: Target) !Value {
|
|
assert(self.zigTypeTag() == .Int);
|
|
const info = self.intInfo(target);
|
|
|
|
if (info.signedness == .unsigned) {
|
|
return Value.initTag(.zero);
|
|
}
|
|
|
|
if ((info.bits - 1) <= std.math.maxInt(u6)) {
|
|
const n: i64 = -(@as(i64, 1) << @truncate(u6, info.bits - 1));
|
|
return Value.Tag.int_i64.create(&arena.allocator, n);
|
|
}
|
|
|
|
var res = try std.math.big.int.Managed.initSet(&arena.allocator, 1);
|
|
try res.shiftLeft(res, info.bits - 1);
|
|
res.negate();
|
|
|
|
const res_const = res.toConst();
|
|
if (res_const.positive) {
|
|
return Value.Tag.int_big_positive.create(&arena.allocator, res_const.limbs);
|
|
} else {
|
|
return Value.Tag.int_big_negative.create(&arena.allocator, res_const.limbs);
|
|
}
|
|
}
|
|
|
|
/// Asserts that self.zigTypeTag() == .Int.
|
|
pub fn maxInt(self: Type, arena: *std.heap.ArenaAllocator, target: Target) !Value {
|
|
assert(self.zigTypeTag() == .Int);
|
|
const info = self.intInfo(target);
|
|
|
|
if (info.signedness == .signed and (info.bits - 1) <= std.math.maxInt(u6)) {
|
|
const n: i64 = (@as(i64, 1) << @truncate(u6, info.bits - 1)) - 1;
|
|
return Value.Tag.int_i64.create(&arena.allocator, n);
|
|
} else if (info.signedness == .signed and info.bits <= std.math.maxInt(u6)) {
|
|
const n: u64 = (@as(u64, 1) << @truncate(u6, info.bits)) - 1;
|
|
return Value.Tag.int_u64.create(&arena.allocator, n);
|
|
}
|
|
|
|
var res = try std.math.big.int.Managed.initSet(&arena.allocator, 1);
|
|
try res.shiftLeft(res, info.bits - @boolToInt(info.signedness == .signed));
|
|
const one = std.math.big.int.Const{
|
|
.limbs = &[_]std.math.big.Limb{1},
|
|
.positive = true,
|
|
};
|
|
res.sub(res.toConst(), one) catch unreachable;
|
|
|
|
const res_const = res.toConst();
|
|
if (res_const.positive) {
|
|
return Value.Tag.int_big_positive.create(&arena.allocator, res_const.limbs);
|
|
} else {
|
|
return Value.Tag.int_big_negative.create(&arena.allocator, res_const.limbs);
|
|
}
|
|
}
|
|
|
|
pub fn isExhaustiveEnum(ty: Type) bool {
|
|
return false; // TODO
|
|
}
|
|
|
|
/// 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,
|
|
u128,
|
|
i128,
|
|
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,
|
|
const_slice_u8,
|
|
/// This is a special type for variadic parameters of a function call.
|
|
/// Casts to it will validate that the type can be passed to a c calling convetion function.
|
|
var_args_param,
|
|
/// Same as `empty_struct` except it has an empty namespace.
|
|
empty_struct_literal,
|
|
/// This is a special value that tracks a set of types that have been stored
|
|
/// to an inferred allocation. It does not support most of the normal type queries.
|
|
/// However it does respond to `isConstPtr`, `ptrSize`, `zigTypeTag`, etc.
|
|
inferred_alloc_mut,
|
|
/// Same as `inferred_alloc_mut` but the local is `var` not `const`.
|
|
inferred_alloc_const, // 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,
|
|
error_set,
|
|
error_set_single,
|
|
empty_struct,
|
|
@"opaque",
|
|
@"struct",
|
|
|
|
pub const last_no_payload_tag = Tag.inferred_alloc_const;
|
|
pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
|
|
|
|
pub fn Type(comptime t: Tag) type {
|
|
return switch (t) {
|
|
.u8,
|
|
.i8,
|
|
.u16,
|
|
.i16,
|
|
.u32,
|
|
.i32,
|
|
.u64,
|
|
.i64,
|
|
.u128,
|
|
.i128,
|
|
.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,
|
|
.const_slice_u8,
|
|
.inferred_alloc_const,
|
|
.inferred_alloc_mut,
|
|
.var_args_param,
|
|
.empty_struct_literal,
|
|
=> @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
|
|
|
|
.array_u8,
|
|
.array_u8_sentinel_0,
|
|
=> Payload.Len,
|
|
|
|
.single_const_pointer,
|
|
.single_mut_pointer,
|
|
.many_const_pointer,
|
|
.many_mut_pointer,
|
|
.c_const_pointer,
|
|
.c_mut_pointer,
|
|
.const_slice,
|
|
.mut_slice,
|
|
.optional,
|
|
.optional_single_mut_pointer,
|
|
.optional_single_const_pointer,
|
|
=> Payload.ElemType,
|
|
|
|
.int_signed,
|
|
.int_unsigned,
|
|
=> Payload.Bits,
|
|
|
|
.error_set => Payload.ErrorSet,
|
|
|
|
.array => Payload.Array,
|
|
.array_sentinel => Payload.ArraySentinel,
|
|
.pointer => Payload.Pointer,
|
|
.function => Payload.Function,
|
|
.error_union => Payload.ErrorUnion,
|
|
.error_set_single => Payload.Name,
|
|
.@"opaque" => Payload.Opaque,
|
|
.@"struct" => Payload.Struct,
|
|
.empty_struct => Payload.ContainerScope,
|
|
};
|
|
}
|
|
|
|
pub fn init(comptime t: Tag) Type {
|
|
comptime std.debug.assert(@enumToInt(t) < Tag.no_payload_count);
|
|
return .{ .tag_if_small_enough = @enumToInt(t) };
|
|
}
|
|
|
|
pub fn create(comptime t: Tag, ally: *Allocator, data: Data(t)) error{OutOfMemory}!Type {
|
|
const ptr = try ally.create(t.Type());
|
|
ptr.* = .{
|
|
.base = .{ .tag = t },
|
|
.data = data,
|
|
};
|
|
return Type{ .ptr_otherwise = &ptr.base };
|
|
}
|
|
|
|
pub fn Data(comptime t: Tag) type {
|
|
return std.meta.fieldInfo(t.Type(), .data).field_type;
|
|
}
|
|
};
|
|
|
|
/// The sub-types are named after what fields they contain.
|
|
pub const Payload = struct {
|
|
tag: Tag,
|
|
|
|
pub const Len = struct {
|
|
base: Payload,
|
|
data: u64,
|
|
};
|
|
|
|
pub const Array = struct {
|
|
pub const base_tag = Tag.array;
|
|
|
|
base: Payload = Payload{ .tag = base_tag },
|
|
data: struct {
|
|
len: u64,
|
|
elem_type: Type,
|
|
},
|
|
};
|
|
|
|
pub const ArraySentinel = struct {
|
|
pub const base_tag = Tag.array_sentinel;
|
|
|
|
base: Payload = Payload{ .tag = base_tag },
|
|
data: struct {
|
|
len: u64,
|
|
sentinel: Value,
|
|
elem_type: Type,
|
|
},
|
|
};
|
|
|
|
pub const ElemType = struct {
|
|
base: Payload,
|
|
data: Type,
|
|
};
|
|
|
|
pub const Bits = struct {
|
|
base: Payload,
|
|
data: u16,
|
|
};
|
|
|
|
pub const Function = struct {
|
|
pub const base_tag = Tag.function;
|
|
|
|
base: Payload = Payload{ .tag = base_tag },
|
|
data: struct {
|
|
param_types: []Type,
|
|
return_type: Type,
|
|
cc: std.builtin.CallingConvention,
|
|
is_var_args: bool,
|
|
},
|
|
};
|
|
|
|
pub const ErrorSet = struct {
|
|
pub const base_tag = Tag.error_set;
|
|
|
|
base: Payload = Payload{ .tag = base_tag },
|
|
data: *Module.ErrorSet,
|
|
};
|
|
|
|
pub const Pointer = struct {
|
|
pub const base_tag = Tag.pointer;
|
|
|
|
base: Payload = Payload{ .tag = base_tag },
|
|
data: struct {
|
|
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 {
|
|
pub const base_tag = Tag.error_union;
|
|
|
|
base: Payload = Payload{ .tag = base_tag },
|
|
data: struct {
|
|
error_set: Type,
|
|
payload: Type,
|
|
},
|
|
};
|
|
|
|
pub const Decl = struct {
|
|
base: Payload,
|
|
data: *Module.Decl,
|
|
};
|
|
|
|
pub const Name = struct {
|
|
base: Payload,
|
|
/// memory is owned by `Module`
|
|
data: []const u8,
|
|
};
|
|
|
|
/// Mostly used for namespace like structs with zero fields.
|
|
/// Most commonly used for files.
|
|
pub const ContainerScope = struct {
|
|
base: Payload,
|
|
data: *Module.Scope.Container,
|
|
};
|
|
|
|
pub const Opaque = struct {
|
|
base: Payload = .{ .tag = .@"opaque" },
|
|
data: Module.Scope.Container,
|
|
};
|
|
|
|
pub const Struct = struct {
|
|
base: Payload = .{ .tag = .@"struct" },
|
|
data: *Module.Struct,
|
|
};
|
|
};
|
|
};
|
|
|
|
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,
|
|
.macos,
|
|
.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,
|
|
.opencl,
|
|
.glsl450,
|
|
.vulkan,
|
|
=> @panic("TODO specify the C integer and float type sizes for this OS"),
|
|
}
|
|
}
|
|
};
|