aboutsummaryrefslogtreecommitdiff
path: root/src/value.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2020-09-30 04:28:19 -0400
committerGitHub <noreply@github.com>2020-09-30 04:28:19 -0400
commitfe117d9961c3622fda5c359733d01de686509af0 (patch)
treef4c3c9282049dff85dcc417f831414cb5ab7c524 /src/value.zig
parentbd449b184a0c9fb824184672b12f90ed2698b77a (diff)
parent3249e5d952cfcecca999391ffc02cce92ff8fcc4 (diff)
downloadzig-fe117d9961c3622fda5c359733d01de686509af0.tar.gz
zig-fe117d9961c3622fda5c359733d01de686509af0.zip
Merge pull request #6250 from ziglang/stage2-zig-cc
move `zig cc`, `zig translate-c`, `zig libc`, main(), and linking from stage1 to stage2
Diffstat (limited to 'src/value.zig')
-rw-r--r--src/value.zig1641
1 files changed, 1641 insertions, 0 deletions
diff --git a/src/value.zig b/src/value.zig
new file mode 100644
index 0000000000..b65aa06bea
--- /dev/null
+++ b/src/value.zig
@@ -0,0 +1,1641 @@
+const std = @import("std");
+const Type = @import("type.zig").Type;
+const log2 = std.math.log2;
+const assert = std.debug.assert;
+const BigIntConst = std.math.big.int.Const;
+const BigIntMutable = std.math.big.int.Mutable;
+const Target = std.Target;
+const Allocator = std.mem.Allocator;
+const Module = @import("Module.zig");
+
+/// This is the raw data, with no bookkeeping, no memory awareness,
+/// no de-duplication, and no type system awareness.
+/// It's important for this type to be small.
+/// 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 Value = 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 const Tag = enum {
+ // The first section of this enum are tags that require no payload.
+ u8_type,
+ i8_type,
+ u16_type,
+ i16_type,
+ u32_type,
+ i32_type,
+ u64_type,
+ i64_type,
+ usize_type,
+ isize_type,
+ c_short_type,
+ c_ushort_type,
+ c_int_type,
+ c_uint_type,
+ c_long_type,
+ c_ulong_type,
+ c_longlong_type,
+ c_ulonglong_type,
+ c_longdouble_type,
+ f16_type,
+ f32_type,
+ f64_type,
+ f128_type,
+ c_void_type,
+ bool_type,
+ void_type,
+ type_type,
+ anyerror_type,
+ comptime_int_type,
+ comptime_float_type,
+ noreturn_type,
+ null_type,
+ undefined_type,
+ fn_noreturn_no_args_type,
+ fn_void_no_args_type,
+ fn_naked_noreturn_no_args_type,
+ fn_ccc_void_no_args_type,
+ single_const_pointer_to_comptime_int_type,
+ const_slice_u8_type,
+ enum_literal_type,
+ anyframe_type,
+
+ undef,
+ zero,
+ one,
+ void_value,
+ unreachable_value,
+ empty_array,
+ null_value,
+ bool_true,
+ bool_false, // See last_no_payload_tag below.
+ // After this, the tag requires a payload.
+
+ ty,
+ int_type,
+ int_u64,
+ int_i64,
+ int_big_positive,
+ int_big_negative,
+ function,
+ variable,
+ ref_val,
+ decl_ref,
+ elem_ptr,
+ bytes,
+ repeated, // the value is a value repeated some number of times
+ float_16,
+ float_32,
+ float_64,
+ float_128,
+ enum_literal,
+ error_set,
+ @"error",
+
+ pub const last_no_payload_tag = Tag.bool_false;
+ pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
+ };
+
+ pub fn initTag(small_tag: Tag) Value {
+ assert(@enumToInt(small_tag) < Tag.no_payload_count);
+ return .{ .tag_if_small_enough = @enumToInt(small_tag) };
+ }
+
+ pub fn initPayload(payload: *Payload) Value {
+ assert(@enumToInt(payload.tag) >= Tag.no_payload_count);
+ return .{ .ptr_otherwise = payload };
+ }
+
+ pub fn tag(self: Value) Tag {
+ if (self.tag_if_small_enough < Tag.no_payload_count) {
+ return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough));
+ } else {
+ return self.ptr_otherwise.tag;
+ }
+ }
+
+ pub fn cast(self: Value, comptime T: type) ?*T {
+ if (self.tag_if_small_enough < Tag.no_payload_count)
+ return null;
+
+ const expected_tag = std.meta.fieldInfo(T, "base").default_value.?.tag;
+ if (self.ptr_otherwise.tag != expected_tag)
+ return null;
+
+ return @fieldParentPtr(T, "base", self.ptr_otherwise);
+ }
+
+ pub fn copy(self: Value, allocator: *Allocator) error{OutOfMemory}!Value {
+ if (self.tag_if_small_enough < Tag.no_payload_count) {
+ return Value{ .tag_if_small_enough = self.tag_if_small_enough };
+ } else switch (self.ptr_otherwise.tag) {
+ .u8_type,
+ .i8_type,
+ .u16_type,
+ .i16_type,
+ .u32_type,
+ .i32_type,
+ .u64_type,
+ .i64_type,
+ .usize_type,
+ .isize_type,
+ .c_short_type,
+ .c_ushort_type,
+ .c_int_type,
+ .c_uint_type,
+ .c_long_type,
+ .c_ulong_type,
+ .c_longlong_type,
+ .c_ulonglong_type,
+ .c_longdouble_type,
+ .f16_type,
+ .f32_type,
+ .f64_type,
+ .f128_type,
+ .c_void_type,
+ .bool_type,
+ .void_type,
+ .type_type,
+ .anyerror_type,
+ .comptime_int_type,
+ .comptime_float_type,
+ .noreturn_type,
+ .null_type,
+ .undefined_type,
+ .fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
+ .fn_naked_noreturn_no_args_type,
+ .fn_ccc_void_no_args_type,
+ .single_const_pointer_to_comptime_int_type,
+ .const_slice_u8_type,
+ .enum_literal_type,
+ .anyframe_type,
+ .undef,
+ .zero,
+ .one,
+ .void_value,
+ .unreachable_value,
+ .empty_array,
+ .null_value,
+ .bool_true,
+ .bool_false,
+ => unreachable,
+
+ .ty => {
+ const payload = @fieldParentPtr(Payload.Ty, "base", self.ptr_otherwise);
+ const new_payload = try allocator.create(Payload.Ty);
+ new_payload.* = .{
+ .base = payload.base,
+ .ty = try payload.ty.copy(allocator),
+ };
+ return Value{ .ptr_otherwise = &new_payload.base };
+ },
+ .int_type => return self.copyPayloadShallow(allocator, Payload.IntType),
+ .int_u64 => return self.copyPayloadShallow(allocator, Payload.Int_u64),
+ .int_i64 => return self.copyPayloadShallow(allocator, Payload.Int_i64),
+ .int_big_positive => {
+ @panic("TODO implement copying of big ints");
+ },
+ .int_big_negative => {
+ @panic("TODO implement copying of big ints");
+ },
+ .function => return self.copyPayloadShallow(allocator, Payload.Function),
+ .variable => return self.copyPayloadShallow(allocator, Payload.Variable),
+ .ref_val => {
+ const payload = @fieldParentPtr(Payload.RefVal, "base", self.ptr_otherwise);
+ const new_payload = try allocator.create(Payload.RefVal);
+ new_payload.* = .{
+ .base = payload.base,
+ .val = try payload.val.copy(allocator),
+ };
+ return Value{ .ptr_otherwise = &new_payload.base };
+ },
+ .decl_ref => return self.copyPayloadShallow(allocator, Payload.DeclRef),
+ .elem_ptr => {
+ const payload = @fieldParentPtr(Payload.ElemPtr, "base", self.ptr_otherwise);
+ const new_payload = try allocator.create(Payload.ElemPtr);
+ new_payload.* = .{
+ .base = payload.base,
+ .array_ptr = try payload.array_ptr.copy(allocator),
+ .index = payload.index,
+ };
+ return Value{ .ptr_otherwise = &new_payload.base };
+ },
+ .bytes => return self.copyPayloadShallow(allocator, Payload.Bytes),
+ .repeated => {
+ const payload = @fieldParentPtr(Payload.Repeated, "base", self.ptr_otherwise);
+ const new_payload = try allocator.create(Payload.Repeated);
+ new_payload.* = .{
+ .base = payload.base,
+ .val = try payload.val.copy(allocator),
+ };
+ return Value{ .ptr_otherwise = &new_payload.base };
+ },
+ .float_16 => return self.copyPayloadShallow(allocator, Payload.Float_16),
+ .float_32 => return self.copyPayloadShallow(allocator, Payload.Float_32),
+ .float_64 => return self.copyPayloadShallow(allocator, Payload.Float_64),
+ .float_128 => return self.copyPayloadShallow(allocator, Payload.Float_128),
+ .enum_literal => {
+ const payload = @fieldParentPtr(Payload.Bytes, "base", self.ptr_otherwise);
+ const new_payload = try allocator.create(Payload.Bytes);
+ new_payload.* = .{
+ .base = payload.base,
+ .data = try allocator.dupe(u8, payload.data),
+ };
+ return Value{ .ptr_otherwise = &new_payload.base };
+ },
+ .@"error" => return self.copyPayloadShallow(allocator, Payload.Error),
+
+ // memory is managed by the declaration
+ .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
+ }
+ }
+
+ fn copyPayloadShallow(self: Value, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Value {
+ const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
+ const new_payload = try allocator.create(T);
+ new_payload.* = payload.*;
+ return Value{ .ptr_otherwise = &new_payload.base };
+ }
+
+ pub fn format(
+ self: Value,
+ comptime fmt: []const u8,
+ options: std.fmt.FormatOptions,
+ out_stream: anytype,
+ ) !void {
+ comptime assert(fmt.len == 0);
+ var val = self;
+ while (true) switch (val.tag()) {
+ .u8_type => return out_stream.writeAll("u8"),
+ .i8_type => return out_stream.writeAll("i8"),
+ .u16_type => return out_stream.writeAll("u16"),
+ .i16_type => return out_stream.writeAll("i16"),
+ .u32_type => return out_stream.writeAll("u32"),
+ .i32_type => return out_stream.writeAll("i32"),
+ .u64_type => return out_stream.writeAll("u64"),
+ .i64_type => return out_stream.writeAll("i64"),
+ .isize_type => return out_stream.writeAll("isize"),
+ .usize_type => return out_stream.writeAll("usize"),
+ .c_short_type => return out_stream.writeAll("c_short"),
+ .c_ushort_type => return out_stream.writeAll("c_ushort"),
+ .c_int_type => return out_stream.writeAll("c_int"),
+ .c_uint_type => return out_stream.writeAll("c_uint"),
+ .c_long_type => return out_stream.writeAll("c_long"),
+ .c_ulong_type => return out_stream.writeAll("c_ulong"),
+ .c_longlong_type => return out_stream.writeAll("c_longlong"),
+ .c_ulonglong_type => return out_stream.writeAll("c_ulonglong"),
+ .c_longdouble_type => return out_stream.writeAll("c_longdouble"),
+ .f16_type => return out_stream.writeAll("f16"),
+ .f32_type => return out_stream.writeAll("f32"),
+ .f64_type => return out_stream.writeAll("f64"),
+ .f128_type => return out_stream.writeAll("f128"),
+ .c_void_type => return out_stream.writeAll("c_void"),
+ .bool_type => return out_stream.writeAll("bool"),
+ .void_type => return out_stream.writeAll("void"),
+ .type_type => return out_stream.writeAll("type"),
+ .anyerror_type => return out_stream.writeAll("anyerror"),
+ .comptime_int_type => return out_stream.writeAll("comptime_int"),
+ .comptime_float_type => return out_stream.writeAll("comptime_float"),
+ .noreturn_type => return out_stream.writeAll("noreturn"),
+ .null_type => return out_stream.writeAll("@Type(.Null)"),
+ .undefined_type => return out_stream.writeAll("@Type(.Undefined)"),
+ .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"),
+ .fn_void_no_args_type => return out_stream.writeAll("fn() void"),
+ .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
+ .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),
+ .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
+ .const_slice_u8_type => return out_stream.writeAll("[]const u8"),
+ .enum_literal_type => return out_stream.writeAll("@Type(.EnumLiteral)"),
+ .anyframe_type => return out_stream.writeAll("anyframe"),
+
+ .null_value => return out_stream.writeAll("null"),
+ .undef => return out_stream.writeAll("undefined"),
+ .zero => return out_stream.writeAll("0"),
+ .one => return out_stream.writeAll("1"),
+ .void_value => return out_stream.writeAll("{}"),
+ .unreachable_value => return out_stream.writeAll("unreachable"),
+ .bool_true => return out_stream.writeAll("true"),
+ .bool_false => return out_stream.writeAll("false"),
+ .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),
+ .int_type => {
+ const int_type = val.cast(Payload.IntType).?;
+ return out_stream.print("{}{}", .{
+ if (int_type.signed) "s" else "u",
+ int_type.bits,
+ });
+ },
+ .int_u64 => return std.fmt.formatIntValue(val.cast(Payload.Int_u64).?.int, "", options, out_stream),
+ .int_i64 => return std.fmt.formatIntValue(val.cast(Payload.Int_i64).?.int, "", options, out_stream),
+ .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}),
+ .int_big_negative => return out_stream.print("{}", .{val.cast(Payload.IntBigNegative).?.asBigInt()}),
+ .function => return out_stream.writeAll("(function)"),
+ .variable => return out_stream.writeAll("(variable)"),
+ .ref_val => {
+ const ref_val = val.cast(Payload.RefVal).?;
+ try out_stream.writeAll("&const ");
+ val = ref_val.val;
+ },
+ .decl_ref => return out_stream.writeAll("(decl ref)"),
+ .elem_ptr => {
+ const elem_ptr = val.cast(Payload.ElemPtr).?;
+ try out_stream.print("&[{}] ", .{elem_ptr.index});
+ val = elem_ptr.array_ptr;
+ },
+ .empty_array => return out_stream.writeAll(".{}"),
+ .enum_literal, .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream),
+ .repeated => {
+ try out_stream.writeAll("(repeated) ");
+ val = val.cast(Payload.Repeated).?.val;
+ },
+ .float_16 => return out_stream.print("{}", .{val.cast(Payload.Float_16).?.val}),
+ .float_32 => return out_stream.print("{}", .{val.cast(Payload.Float_32).?.val}),
+ .float_64 => return out_stream.print("{}", .{val.cast(Payload.Float_64).?.val}),
+ .float_128 => return out_stream.print("{}", .{val.cast(Payload.Float_128).?.val}),
+ .error_set => {
+ const error_set = val.cast(Payload.ErrorSet).?;
+ try out_stream.writeAll("error{");
+ var it = error_set.fields.iterator();
+ while (it.next()) |entry| {
+ try out_stream.print("{},", .{entry.value});
+ }
+ return out_stream.writeAll("}");
+ },
+ .@"error" => return out_stream.print("error.{}", .{val.cast(Payload.Error).?.name}),
+ };
+ }
+
+ /// Asserts that the value is representable as an array of bytes.
+ /// Copies the value into a freshly allocated slice of memory, which is owned by the caller.
+ pub fn toAllocatedBytes(self: Value, allocator: *Allocator) ![]u8 {
+ if (self.cast(Payload.Bytes)) |bytes| {
+ return std.mem.dupe(allocator, u8, bytes.data);
+ }
+ if (self.cast(Payload.Repeated)) |repeated| {
+ @panic("TODO implement toAllocatedBytes for this Value tag");
+ }
+ if (self.cast(Payload.DeclRef)) |declref| {
+ const val = try declref.decl.value();
+ return val.toAllocatedBytes(allocator);
+ }
+ unreachable;
+ }
+
+ /// Asserts that the value is representable as a type.
+ pub fn toType(self: Value, allocator: *Allocator) !Type {
+ return switch (self.tag()) {
+ .ty => self.cast(Payload.Ty).?.ty,
+ .u8_type => Type.initTag(.u8),
+ .i8_type => Type.initTag(.i8),
+ .u16_type => Type.initTag(.u16),
+ .i16_type => Type.initTag(.i16),
+ .u32_type => Type.initTag(.u32),
+ .i32_type => Type.initTag(.i32),
+ .u64_type => Type.initTag(.u64),
+ .i64_type => Type.initTag(.i64),
+ .usize_type => Type.initTag(.usize),
+ .isize_type => Type.initTag(.isize),
+ .c_short_type => Type.initTag(.c_short),
+ .c_ushort_type => Type.initTag(.c_ushort),
+ .c_int_type => Type.initTag(.c_int),
+ .c_uint_type => Type.initTag(.c_uint),
+ .c_long_type => Type.initTag(.c_long),
+ .c_ulong_type => Type.initTag(.c_ulong),
+ .c_longlong_type => Type.initTag(.c_longlong),
+ .c_ulonglong_type => Type.initTag(.c_ulonglong),
+ .c_longdouble_type => Type.initTag(.c_longdouble),
+ .f16_type => Type.initTag(.f16),
+ .f32_type => Type.initTag(.f32),
+ .f64_type => Type.initTag(.f64),
+ .f128_type => Type.initTag(.f128),
+ .c_void_type => Type.initTag(.c_void),
+ .bool_type => Type.initTag(.bool),
+ .void_type => Type.initTag(.void),
+ .type_type => Type.initTag(.type),
+ .anyerror_type => Type.initTag(.anyerror),
+ .comptime_int_type => Type.initTag(.comptime_int),
+ .comptime_float_type => Type.initTag(.comptime_float),
+ .noreturn_type => Type.initTag(.noreturn),
+ .null_type => Type.initTag(.@"null"),
+ .undefined_type => Type.initTag(.@"undefined"),
+ .fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args),
+ .fn_void_no_args_type => Type.initTag(.fn_void_no_args),
+ .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),
+ .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args),
+ .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
+ .const_slice_u8_type => Type.initTag(.const_slice_u8),
+ .enum_literal_type => Type.initTag(.enum_literal),
+ .anyframe_type => Type.initTag(.@"anyframe"),
+
+ .int_type => {
+ const payload = self.cast(Payload.IntType).?;
+ if (payload.signed) {
+ const new = try allocator.create(Type.Payload.IntSigned);
+ new.* = .{ .bits = payload.bits };
+ return Type.initPayload(&new.base);
+ } else {
+ const new = try allocator.create(Type.Payload.IntUnsigned);
+ new.* = .{ .bits = payload.bits };
+ return Type.initPayload(&new.base);
+ }
+ },
+ .error_set => {
+ const payload = self.cast(Payload.ErrorSet).?;
+ const new = try allocator.create(Type.Payload.ErrorSet);
+ new.* = .{ .decl = payload.decl };
+ return Type.initPayload(&new.base);
+ },
+
+ .undef,
+ .zero,
+ .one,
+ .void_value,
+ .unreachable_value,
+ .empty_array,
+ .bool_true,
+ .bool_false,
+ .null_value,
+ .int_u64,
+ .int_i64,
+ .int_big_positive,
+ .int_big_negative,
+ .function,
+ .variable,
+ .ref_val,
+ .decl_ref,
+ .elem_ptr,
+ .bytes,
+ .repeated,
+ .float_16,
+ .float_32,
+ .float_64,
+ .float_128,
+ .enum_literal,
+ .@"error",
+ => unreachable,
+ };
+ }
+
+ /// Asserts the value is an integer.
+ pub fn toBigInt(self: Value, space: *BigIntSpace) BigIntConst {
+ switch (self.tag()) {
+ .ty,
+ .int_type,
+ .u8_type,
+ .i8_type,
+ .u16_type,
+ .i16_type,
+ .u32_type,
+ .i32_type,
+ .u64_type,
+ .i64_type,
+ .usize_type,
+ .isize_type,
+ .c_short_type,
+ .c_ushort_type,
+ .c_int_type,
+ .c_uint_type,
+ .c_long_type,
+ .c_ulong_type,
+ .c_longlong_type,
+ .c_ulonglong_type,
+ .c_longdouble_type,
+ .f16_type,
+ .f32_type,
+ .f64_type,
+ .f128_type,
+ .c_void_type,
+ .bool_type,
+ .void_type,
+ .type_type,
+ .anyerror_type,
+ .comptime_int_type,
+ .comptime_float_type,
+ .noreturn_type,
+ .null_type,
+ .undefined_type,
+ .fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
+ .fn_naked_noreturn_no_args_type,
+ .fn_ccc_void_no_args_type,
+ .single_const_pointer_to_comptime_int_type,
+ .const_slice_u8_type,
+ .enum_literal_type,
+ .anyframe_type,
+ .null_value,
+ .function,
+ .variable,
+ .ref_val,
+ .decl_ref,
+ .elem_ptr,
+ .bytes,
+ .repeated,
+ .float_16,
+ .float_32,
+ .float_64,
+ .float_128,
+ .void_value,
+ .unreachable_value,
+ .empty_array,
+ .enum_literal,
+ .error_set,
+ .@"error",
+ => unreachable,
+
+ .undef => unreachable,
+
+ .zero,
+ .bool_false,
+ => return BigIntMutable.init(&space.limbs, 0).toConst(),
+
+ .one,
+ .bool_true,
+ => return BigIntMutable.init(&space.limbs, 1).toConst(),
+
+ .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),
+ .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),
+ .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(),
+ .int_big_negative => return self.cast(Payload.IntBigPositive).?.asBigInt(),
+ }
+ }
+
+ /// Asserts the value is an integer and it fits in a u64
+ pub fn toUnsignedInt(self: Value) u64 {
+ switch (self.tag()) {
+ .ty,
+ .int_type,
+ .u8_type,
+ .i8_type,
+ .u16_type,
+ .i16_type,
+ .u32_type,
+ .i32_type,
+ .u64_type,
+ .i64_type,
+ .usize_type,
+ .isize_type,
+ .c_short_type,
+ .c_ushort_type,
+ .c_int_type,
+ .c_uint_type,
+ .c_long_type,
+ .c_ulong_type,
+ .c_longlong_type,
+ .c_ulonglong_type,
+ .c_longdouble_type,
+ .f16_type,
+ .f32_type,
+ .f64_type,
+ .f128_type,
+ .c_void_type,
+ .bool_type,
+ .void_type,
+ .type_type,
+ .anyerror_type,
+ .comptime_int_type,
+ .comptime_float_type,
+ .noreturn_type,
+ .null_type,
+ .undefined_type,
+ .fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
+ .fn_naked_noreturn_no_args_type,
+ .fn_ccc_void_no_args_type,
+ .single_const_pointer_to_comptime_int_type,
+ .const_slice_u8_type,
+ .enum_literal_type,
+ .anyframe_type,
+ .null_value,
+ .function,
+ .variable,
+ .ref_val,
+ .decl_ref,
+ .elem_ptr,
+ .bytes,
+ .repeated,
+ .float_16,
+ .float_32,
+ .float_64,
+ .float_128,
+ .void_value,
+ .unreachable_value,
+ .empty_array,
+ .enum_literal,
+ .error_set,
+ .@"error",
+ => unreachable,
+
+ .undef => unreachable,
+
+ .zero,
+ .bool_false,
+ => return 0,
+
+ .one,
+ .bool_true,
+ => return 1,
+
+ .int_u64 => return self.cast(Payload.Int_u64).?.int,
+ .int_i64 => return @intCast(u64, self.cast(Payload.Int_i64).?.int),
+ .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable,
+ .int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().to(u64) catch unreachable,
+ }
+ }
+
+ /// Asserts the value is an integer and it fits in a i64
+ pub fn toSignedInt(self: Value) i64 {
+ switch (self.tag()) {
+ .ty,
+ .int_type,
+ .u8_type,
+ .i8_type,
+ .u16_type,
+ .i16_type,
+ .u32_type,
+ .i32_type,
+ .u64_type,
+ .i64_type,
+ .usize_type,
+ .isize_type,
+ .c_short_type,
+ .c_ushort_type,
+ .c_int_type,
+ .c_uint_type,
+ .c_long_type,
+ .c_ulong_type,
+ .c_longlong_type,
+ .c_ulonglong_type,
+ .c_longdouble_type,
+ .f16_type,
+ .f32_type,
+ .f64_type,
+ .f128_type,
+ .c_void_type,
+ .bool_type,
+ .void_type,
+ .type_type,
+ .anyerror_type,
+ .comptime_int_type,
+ .comptime_float_type,
+ .noreturn_type,
+ .null_type,
+ .undefined_type,
+ .fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
+ .fn_naked_noreturn_no_args_type,
+ .fn_ccc_void_no_args_type,
+ .single_const_pointer_to_comptime_int_type,
+ .const_slice_u8_type,
+ .enum_literal_type,
+ .anyframe_type,
+ .null_value,
+ .function,
+ .variable,
+ .ref_val,
+ .decl_ref,
+ .elem_ptr,
+ .bytes,
+ .repeated,
+ .float_16,
+ .float_32,
+ .float_64,
+ .float_128,
+ .void_value,
+ .unreachable_value,
+ .empty_array,
+ .enum_literal,
+ .error_set,
+ .@"error",
+ => unreachable,
+
+ .undef => unreachable,
+
+ .zero,
+ .bool_false,
+ => return 0,
+
+ .one,
+ .bool_true,
+ => return 1,
+
+ .int_u64 => return @intCast(i64, self.cast(Payload.Int_u64).?.int),
+ .int_i64 => return self.cast(Payload.Int_i64).?.int,
+ .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(i64) catch unreachable,
+ .int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().to(i64) catch unreachable,
+ }
+ }
+
+ pub fn toBool(self: Value) bool {
+ return switch (self.tag()) {
+ .bool_true => true,
+ .bool_false, .zero => false,
+ else => unreachable,
+ };
+ }
+
+ /// Asserts that the value is a float or an integer.
+ pub fn toFloat(self: Value, comptime T: type) T {
+ return switch (self.tag()) {
+ .float_16 => @panic("TODO soft float"),
+ .float_32 => @floatCast(T, self.cast(Payload.Float_32).?.val),
+ .float_64 => @floatCast(T, self.cast(Payload.Float_64).?.val),
+ .float_128 => @floatCast(T, self.cast(Payload.Float_128).?.val),
+
+ .zero => 0,
+ .one => 1,
+ .int_u64 => @intToFloat(T, self.cast(Payload.Int_u64).?.int),
+ .int_i64 => @intToFloat(T, self.cast(Payload.Int_i64).?.int),
+
+ .int_big_positive, .int_big_negative => @panic("big int to f128"),
+ else => unreachable,
+ };
+ }
+
+ /// Asserts the value is an integer and not undefined.
+ /// Returns the number of bits the value requires to represent stored in twos complement form.
+ pub fn intBitCountTwosComp(self: Value) usize {
+ switch (self.tag()) {
+ .ty,
+ .int_type,
+ .u8_type,
+ .i8_type,
+ .u16_type,
+ .i16_type,
+ .u32_type,
+ .i32_type,
+ .u64_type,
+ .i64_type,
+ .usize_type,
+ .isize_type,
+ .c_short_type,
+ .c_ushort_type,
+ .c_int_type,
+ .c_uint_type,
+ .c_long_type,
+ .c_ulong_type,
+ .c_longlong_type,
+ .c_ulonglong_type,
+ .c_longdouble_type,
+ .f16_type,
+ .f32_type,
+ .f64_type,
+ .f128_type,
+ .c_void_type,
+ .bool_type,
+ .void_type,
+ .type_type,
+ .anyerror_type,
+ .comptime_int_type,
+ .comptime_float_type,
+ .noreturn_type,
+ .null_type,
+ .undefined_type,
+ .fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
+ .fn_naked_noreturn_no_args_type,
+ .fn_ccc_void_no_args_type,
+ .single_const_pointer_to_comptime_int_type,
+ .const_slice_u8_type,
+ .enum_literal_type,
+ .anyframe_type,
+ .null_value,
+ .function,
+ .variable,
+ .ref_val,
+ .decl_ref,
+ .elem_ptr,
+ .bytes,
+ .undef,
+ .repeated,
+ .float_16,
+ .float_32,
+ .float_64,
+ .float_128,
+ .void_value,
+ .unreachable_value,
+ .empty_array,
+ .enum_literal,
+ .error_set,
+ .@"error",
+ => unreachable,
+
+ .zero,
+ .bool_false,
+ => return 0,
+
+ .one,
+ .bool_true,
+ => return 1,
+
+ .int_u64 => {
+ const x = self.cast(Payload.Int_u64).?.int;
+ if (x == 0) return 0;
+ return std.math.log2(x) + 1;
+ },
+ .int_i64 => {
+ @panic("TODO implement i64 intBitCountTwosComp");
+ },
+ .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().bitCountTwosComp(),
+ .int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().bitCountTwosComp(),
+ }
+ }
+
+ /// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
+ pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {
+ switch (self.tag()) {
+ .ty,
+ .int_type,
+ .u8_type,
+ .i8_type,
+ .u16_type,
+ .i16_type,
+ .u32_type,
+ .i32_type,
+ .u64_type,
+ .i64_type,
+ .usize_type,
+ .isize_type,
+ .c_short_type,
+ .c_ushort_type,
+ .c_int_type,
+ .c_uint_type,
+ .c_long_type,
+ .c_ulong_type,
+ .c_longlong_type,
+ .c_ulonglong_type,
+ .c_longdouble_type,
+ .f16_type,
+ .f32_type,
+ .f64_type,
+ .f128_type,
+ .c_void_type,
+ .bool_type,
+ .void_type,
+ .type_type,
+ .anyerror_type,
+ .comptime_int_type,
+ .comptime_float_type,
+ .noreturn_type,
+ .null_type,
+ .undefined_type,
+ .fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
+ .fn_naked_noreturn_no_args_type,
+ .fn_ccc_void_no_args_type,
+ .single_const_pointer_to_comptime_int_type,
+ .const_slice_u8_type,
+ .enum_literal_type,
+ .anyframe_type,
+ .null_value,
+ .function,
+ .variable,
+ .ref_val,
+ .decl_ref,
+ .elem_ptr,
+ .bytes,
+ .repeated,
+ .float_16,
+ .float_32,
+ .float_64,
+ .float_128,
+ .void_value,
+ .unreachable_value,
+ .empty_array,
+ .enum_literal,
+ .error_set,
+ .@"error",
+ => unreachable,
+
+ .zero,
+ .undef,
+ .bool_false,
+ => return true,
+
+ .one,
+ .bool_true,
+ => {
+ const info = ty.intInfo(target);
+ if (info.signed) {
+ return info.bits >= 2;
+ } else {
+ return info.bits >= 1;
+ }
+ },
+
+ .int_u64 => switch (ty.zigTypeTag()) {
+ .Int => {
+ const x = self.cast(Payload.Int_u64).?.int;
+ if (x == 0) return true;
+ const info = ty.intInfo(target);
+ const needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signed);
+ return info.bits >= needed_bits;
+ },
+ .ComptimeInt => return true,
+ else => unreachable,
+ },
+ .int_i64 => switch (ty.zigTypeTag()) {
+ .Int => {
+ const x = self.cast(Payload.Int_i64).?.int;
+ if (x == 0) return true;
+ const info = ty.intInfo(target);
+ if (!info.signed and x < 0)
+ return false;
+ @panic("TODO implement i64 intFitsInType");
+ },
+ .ComptimeInt => return true,
+ else => unreachable,
+ },
+ .int_big_positive => switch (ty.zigTypeTag()) {
+ .Int => {
+ const info = ty.intInfo(target);
+ return self.cast(Payload.IntBigPositive).?.asBigInt().fitsInTwosComp(info.signed, info.bits);
+ },
+ .ComptimeInt => return true,
+ else => unreachable,
+ },
+ .int_big_negative => switch (ty.zigTypeTag()) {
+ .Int => {
+ const info = ty.intInfo(target);
+ return self.cast(Payload.IntBigNegative).?.asBigInt().fitsInTwosComp(info.signed, info.bits);
+ },
+ .ComptimeInt => return true,
+ else => unreachable,
+ },
+ }
+ }
+
+ /// Converts an integer or a float to a float.
+ /// Returns `error.Overflow` if the value does not fit in the new type.
+ pub fn floatCast(self: Value, allocator: *Allocator, ty: Type, target: Target) !Value {
+ const dest_bit_count = switch (ty.tag()) {
+ .comptime_float => 128,
+ else => ty.floatBits(target),
+ };
+ switch (dest_bit_count) {
+ 16, 32, 64, 128 => {},
+ else => std.debug.panic("TODO float cast bit count {}\n", .{dest_bit_count}),
+ }
+ if (ty.isInt()) {
+ @panic("TODO int to float");
+ }
+
+ switch (dest_bit_count) {
+ 16 => {
+ @panic("TODO soft float");
+ // var res_payload = Value.Payload.Float_16{.val = self.toFloat(f16)};
+ // if (!self.eql(Value.initPayload(&res_payload.base)))
+ // return error.Overflow;
+ // return Value.initPayload(&res_payload.base).copy(allocator);
+ },
+ 32 => {
+ var res_payload = Value.Payload.Float_32{ .val = self.toFloat(f32) };
+ if (!self.eql(Value.initPayload(&res_payload.base)))
+ return error.Overflow;
+ return Value.initPayload(&res_payload.base).copy(allocator);
+ },
+ 64 => {
+ var res_payload = Value.Payload.Float_64{ .val = self.toFloat(f64) };
+ if (!self.eql(Value.initPayload(&res_payload.base)))
+ return error.Overflow;
+ return Value.initPayload(&res_payload.base).copy(allocator);
+ },
+ 128 => {
+ const float_payload = try allocator.create(Value.Payload.Float_128);
+ float_payload.* = .{ .val = self.toFloat(f128) };
+ return Value.initPayload(&float_payload.base);
+ },
+ else => unreachable,
+ }
+ }
+
+ /// Asserts the value is a float
+ pub fn floatHasFraction(self: Value) bool {
+ return switch (self.tag()) {
+ .ty,
+ .int_type,
+ .u8_type,
+ .i8_type,
+ .u16_type,
+ .i16_type,
+ .u32_type,
+ .i32_type,
+ .u64_type,
+ .i64_type,
+ .usize_type,
+ .isize_type,
+ .c_short_type,
+ .c_ushort_type,
+ .c_int_type,
+ .c_uint_type,
+ .c_long_type,
+ .c_ulong_type,
+ .c_longlong_type,
+ .c_ulonglong_type,
+ .c_longdouble_type,
+ .f16_type,
+ .f32_type,
+ .f64_type,
+ .f128_type,
+ .c_void_type,
+ .bool_type,
+ .void_type,
+ .type_type,
+ .anyerror_type,
+ .comptime_int_type,
+ .comptime_float_type,
+ .noreturn_type,
+ .null_type,
+ .undefined_type,
+ .fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
+ .fn_naked_noreturn_no_args_type,
+ .fn_ccc_void_no_args_type,
+ .single_const_pointer_to_comptime_int_type,
+ .const_slice_u8_type,
+ .enum_literal_type,
+ .anyframe_type,
+ .bool_true,
+ .bool_false,
+ .null_value,
+ .function,
+ .variable,
+ .ref_val,
+ .decl_ref,
+ .elem_ptr,
+ .bytes,
+ .repeated,
+ .undef,
+ .int_u64,
+ .int_i64,
+ .int_big_positive,
+ .int_big_negative,
+ .empty_array,
+ .void_value,
+ .unreachable_value,
+ .enum_literal,
+ .error_set,
+ .@"error",
+ => unreachable,
+
+ .zero,
+ .one,
+ => false,
+
+ .float_16 => @rem(self.cast(Payload.Float_16).?.val, 1) != 0,
+ .float_32 => @rem(self.cast(Payload.Float_32).?.val, 1) != 0,
+ .float_64 => @rem(self.cast(Payload.Float_64).?.val, 1) != 0,
+ // .float_128 => @rem(self.cast(Payload.Float_128).?.val, 1) != 0,
+ .float_128 => @panic("TODO lld: error: undefined symbol: fmodl"),
+ };
+ }
+
+ pub fn orderAgainstZero(lhs: Value) std.math.Order {
+ return switch (lhs.tag()) {
+ .ty,
+ .int_type,
+ .u8_type,
+ .i8_type,
+ .u16_type,
+ .i16_type,
+ .u32_type,
+ .i32_type,
+ .u64_type,
+ .i64_type,
+ .usize_type,
+ .isize_type,
+ .c_short_type,
+ .c_ushort_type,
+ .c_int_type,
+ .c_uint_type,
+ .c_long_type,
+ .c_ulong_type,
+ .c_longlong_type,
+ .c_ulonglong_type,
+ .c_longdouble_type,
+ .f16_type,
+ .f32_type,
+ .f64_type,
+ .f128_type,
+ .c_void_type,
+ .bool_type,
+ .void_type,
+ .type_type,
+ .anyerror_type,
+ .comptime_int_type,
+ .comptime_float_type,
+ .noreturn_type,
+ .null_type,
+ .undefined_type,
+ .fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
+ .fn_naked_noreturn_no_args_type,
+ .fn_ccc_void_no_args_type,
+ .single_const_pointer_to_comptime_int_type,
+ .const_slice_u8_type,
+ .enum_literal_type,
+ .anyframe_type,
+ .null_value,
+ .function,
+ .variable,
+ .ref_val,
+ .decl_ref,
+ .elem_ptr,
+ .bytes,
+ .repeated,
+ .undef,
+ .void_value,
+ .unreachable_value,
+ .empty_array,
+ .enum_literal,
+ .error_set,
+ .@"error",
+ => unreachable,
+
+ .zero,
+ .bool_false,
+ => .eq,
+
+ .one,
+ .bool_true,
+ => .gt,
+
+ .int_u64 => std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),
+ .int_i64 => std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),
+ .int_big_positive => lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),
+ .int_big_negative => lhs.cast(Payload.IntBigNegative).?.asBigInt().orderAgainstScalar(0),
+
+ .float_16 => std.math.order(lhs.cast(Payload.Float_16).?.val, 0),
+ .float_32 => std.math.order(lhs.cast(Payload.Float_32).?.val, 0),
+ .float_64 => std.math.order(lhs.cast(Payload.Float_64).?.val, 0),
+ .float_128 => std.math.order(lhs.cast(Payload.Float_128).?.val, 0),
+ };
+ }
+
+ /// Asserts the value is comparable.
+ pub fn order(lhs: Value, rhs: Value) std.math.Order {
+ const lhs_tag = lhs.tag();
+ const rhs_tag = rhs.tag();
+ const lhs_is_zero = lhs_tag == .zero;
+ const rhs_is_zero = rhs_tag == .zero;
+ if (lhs_is_zero) return rhs.orderAgainstZero().invert();
+ if (rhs_is_zero) return lhs.orderAgainstZero();
+
+ const lhs_float = lhs.isFloat();
+ const rhs_float = rhs.isFloat();
+ if (lhs_float and rhs_float) {
+ if (lhs_tag == rhs_tag) {
+ return switch (lhs.tag()) {
+ .float_16 => return std.math.order(lhs.cast(Payload.Float_16).?.val, rhs.cast(Payload.Float_16).?.val),
+ .float_32 => return std.math.order(lhs.cast(Payload.Float_32).?.val, rhs.cast(Payload.Float_32).?.val),
+ .float_64 => return std.math.order(lhs.cast(Payload.Float_64).?.val, rhs.cast(Payload.Float_64).?.val),
+ .float_128 => return std.math.order(lhs.cast(Payload.Float_128).?.val, rhs.cast(Payload.Float_128).?.val),
+ else => unreachable,
+ };
+ }
+ }
+ if (lhs_float or rhs_float) {
+ const lhs_f128 = lhs.toFloat(f128);
+ const rhs_f128 = rhs.toFloat(f128);
+ return std.math.order(lhs_f128, rhs_f128);
+ }
+
+ var lhs_bigint_space: BigIntSpace = undefined;
+ var rhs_bigint_space: BigIntSpace = undefined;
+ const lhs_bigint = lhs.toBigInt(&lhs_bigint_space);
+ const rhs_bigint = rhs.toBigInt(&rhs_bigint_space);
+ return lhs_bigint.order(rhs_bigint);
+ }
+
+ /// Asserts the value is comparable.
+ pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value) bool {
+ return order(lhs, rhs).compare(op);
+ }
+
+ /// Asserts the value is comparable.
+ pub fn compareWithZero(lhs: Value, op: std.math.CompareOperator) bool {
+ return orderAgainstZero(lhs).compare(op);
+ }
+
+ pub fn eql(a: Value, b: Value) bool {
+ if (a.tag() == b.tag() and a.tag() == .enum_literal) {
+ const a_name = @fieldParentPtr(Payload.Bytes, "base", a.ptr_otherwise).data;
+ const b_name = @fieldParentPtr(Payload.Bytes, "base", b.ptr_otherwise).data;
+ return std.mem.eql(u8, a_name, b_name);
+ }
+ // TODO non numerical comparisons
+ return compare(a, .eq, b);
+ }
+
+ /// Asserts the value is a pointer and dereferences it.
+ /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis.
+ pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value {
+ return switch (self.tag()) {
+ .ty,
+ .int_type,
+ .u8_type,
+ .i8_type,
+ .u16_type,
+ .i16_type,
+ .u32_type,
+ .i32_type,
+ .u64_type,
+ .i64_type,
+ .usize_type,
+ .isize_type,
+ .c_short_type,
+ .c_ushort_type,
+ .c_int_type,
+ .c_uint_type,
+ .c_long_type,
+ .c_ulong_type,
+ .c_longlong_type,
+ .c_ulonglong_type,
+ .c_longdouble_type,
+ .f16_type,
+ .f32_type,
+ .f64_type,
+ .f128_type,
+ .c_void_type,
+ .bool_type,
+ .void_type,
+ .type_type,
+ .anyerror_type,
+ .comptime_int_type,
+ .comptime_float_type,
+ .noreturn_type,
+ .null_type,
+ .undefined_type,
+ .fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
+ .fn_naked_noreturn_no_args_type,
+ .fn_ccc_void_no_args_type,
+ .single_const_pointer_to_comptime_int_type,
+ .const_slice_u8_type,
+ .enum_literal_type,
+ .anyframe_type,
+ .zero,
+ .one,
+ .bool_true,
+ .bool_false,
+ .null_value,
+ .function,
+ .variable,
+ .int_u64,
+ .int_i64,
+ .int_big_positive,
+ .int_big_negative,
+ .bytes,
+ .undef,
+ .repeated,
+ .float_16,
+ .float_32,
+ .float_64,
+ .float_128,
+ .void_value,
+ .unreachable_value,
+ .empty_array,
+ .enum_literal,
+ .error_set,
+ .@"error",
+ => unreachable,
+
+ .ref_val => self.cast(Payload.RefVal).?.val,
+ .decl_ref => self.cast(Payload.DeclRef).?.decl.value(),
+ .elem_ptr => {
+ const elem_ptr = self.cast(Payload.ElemPtr).?;
+ const array_val = try elem_ptr.array_ptr.pointerDeref(allocator);
+ return array_val.elemValue(allocator, elem_ptr.index);
+ },
+ };
+ }
+
+ /// Asserts the value is a single-item pointer to an array, or an array,
+ /// or an unknown-length pointer, and returns the element value at the index.
+ pub fn elemValue(self: Value, allocator: *Allocator, index: usize) error{OutOfMemory}!Value {
+ switch (self.tag()) {
+ .ty,
+ .int_type,
+ .u8_type,
+ .i8_type,
+ .u16_type,
+ .i16_type,
+ .u32_type,
+ .i32_type,
+ .u64_type,
+ .i64_type,
+ .usize_type,
+ .isize_type,
+ .c_short_type,
+ .c_ushort_type,
+ .c_int_type,
+ .c_uint_type,
+ .c_long_type,
+ .c_ulong_type,
+ .c_longlong_type,
+ .c_ulonglong_type,
+ .c_longdouble_type,
+ .f16_type,
+ .f32_type,
+ .f64_type,
+ .f128_type,
+ .c_void_type,
+ .bool_type,
+ .void_type,
+ .type_type,
+ .anyerror_type,
+ .comptime_int_type,
+ .comptime_float_type,
+ .noreturn_type,
+ .null_type,
+ .undefined_type,
+ .fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
+ .fn_naked_noreturn_no_args_type,
+ .fn_ccc_void_no_args_type,
+ .single_const_pointer_to_comptime_int_type,
+ .const_slice_u8_type,
+ .enum_literal_type,
+ .anyframe_type,
+ .zero,
+ .one,
+ .bool_true,
+ .bool_false,
+ .null_value,
+ .function,
+ .variable,
+ .int_u64,
+ .int_i64,
+ .int_big_positive,
+ .int_big_negative,
+ .undef,
+ .elem_ptr,
+ .ref_val,
+ .decl_ref,
+ .float_16,
+ .float_32,
+ .float_64,
+ .float_128,
+ .void_value,
+ .unreachable_value,
+ .enum_literal,
+ .error_set,
+ .@"error",
+ => unreachable,
+
+ .empty_array => unreachable, // out of bounds array index
+
+ .bytes => {
+ const int_payload = try allocator.create(Payload.Int_u64);
+ int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] };
+ return Value.initPayload(&int_payload.base);
+ },
+
+ // No matter the index; all the elements are the same!
+ .repeated => return self.cast(Payload.Repeated).?.val,
+ }
+ }
+
+ /// Returns a pointer to the element value at the index.
+ pub fn elemPtr(self: Value, allocator: *Allocator, index: usize) !Value {
+ const payload = try allocator.create(Payload.ElemPtr);
+ if (self.cast(Payload.ElemPtr)) |elem_ptr| {
+ payload.* = .{ .array_ptr = elem_ptr.array_ptr, .index = elem_ptr.index + index };
+ } else {
+ payload.* = .{ .array_ptr = self, .index = index };
+ }
+ return Value.initPayload(&payload.base);
+ }
+
+ pub fn isUndef(self: Value) bool {
+ return self.tag() == .undef;
+ }
+
+ /// Valid for all types. Asserts the value is not undefined and not unreachable.
+ pub fn isNull(self: Value) bool {
+ return switch (self.tag()) {
+ .ty,
+ .int_type,
+ .u8_type,
+ .i8_type,
+ .u16_type,
+ .i16_type,
+ .u32_type,
+ .i32_type,
+ .u64_type,
+ .i64_type,
+ .usize_type,
+ .isize_type,
+ .c_short_type,
+ .c_ushort_type,
+ .c_int_type,
+ .c_uint_type,
+ .c_long_type,
+ .c_ulong_type,
+ .c_longlong_type,
+ .c_ulonglong_type,
+ .c_longdouble_type,
+ .f16_type,
+ .f32_type,
+ .f64_type,
+ .f128_type,
+ .c_void_type,
+ .bool_type,
+ .void_type,
+ .type_type,
+ .anyerror_type,
+ .comptime_int_type,
+ .comptime_float_type,
+ .noreturn_type,
+ .null_type,
+ .undefined_type,
+ .fn_noreturn_no_args_type,
+ .fn_void_no_args_type,
+ .fn_naked_noreturn_no_args_type,
+ .fn_ccc_void_no_args_type,
+ .single_const_pointer_to_comptime_int_type,
+ .const_slice_u8_type,
+ .enum_literal_type,
+ .anyframe_type,
+ .zero,
+ .one,
+ .empty_array,
+ .bool_true,
+ .bool_false,
+ .function,
+ .variable,
+ .int_u64,
+ .int_i64,
+ .int_big_positive,
+ .int_big_negative,
+ .ref_val,
+ .decl_ref,
+ .elem_ptr,
+ .bytes,
+ .repeated,
+ .float_16,
+ .float_32,
+ .float_64,
+ .float_128,
+ .void_value,
+ .enum_literal,
+ .error_set,
+ .@"error",
+ => false,
+
+ .undef => unreachable,
+ .unreachable_value => unreachable,
+ .null_value => true,
+ };
+ }
+
+ /// Valid for all types. Asserts the value is not undefined.
+ pub fn isFloat(self: Value) bool {
+ return switch (self.tag()) {
+ .undef => unreachable,
+
+ .float_16,
+ .float_32,
+ .float_64,
+ .float_128,
+ => true,
+ else => false,
+ };
+ }
+
+ /// This type is not copyable since it may contain pointers to its inner data.
+ pub const Payload = struct {
+ tag: Tag,
+
+ pub const Int_u64 = struct {
+ base: Payload = Payload{ .tag = .int_u64 },
+ int: u64,
+ };
+
+ pub const Int_i64 = struct {
+ base: Payload = Payload{ .tag = .int_i64 },
+ int: i64,
+ };
+
+ pub const IntBigPositive = struct {
+ base: Payload = Payload{ .tag = .int_big_positive },
+ limbs: []const std.math.big.Limb,
+
+ pub fn asBigInt(self: IntBigPositive) BigIntConst {
+ return BigIntConst{ .limbs = self.limbs, .positive = true };
+ }
+ };
+
+ pub const IntBigNegative = struct {
+ base: Payload = Payload{ .tag = .int_big_negative },
+ limbs: []const std.math.big.Limb,
+
+ pub fn asBigInt(self: IntBigNegative) BigIntConst {
+ return BigIntConst{ .limbs = self.limbs, .positive = false };
+ }
+ };
+
+ pub const Function = struct {
+ base: Payload = Payload{ .tag = .function },
+ func: *Module.Fn,
+ };
+
+ pub const Variable = struct {
+ base: Payload = Payload{ .tag = .variable },
+ variable: *Module.Var,
+ };
+
+ pub const ArraySentinel0_u8_Type = struct {
+ base: Payload = Payload{ .tag = .array_sentinel_0_u8_type },
+ len: u64,
+ };
+
+ /// Represents a pointer to another immutable value.
+ pub const RefVal = struct {
+ base: Payload = Payload{ .tag = .ref_val },
+ val: Value,
+ };
+
+ /// Represents a pointer to a decl, not the value of the decl.
+ pub const DeclRef = struct {
+ base: Payload = Payload{ .tag = .decl_ref },
+ decl: *Module.Decl,
+ };
+
+ pub const ElemPtr = struct {
+ base: Payload = Payload{ .tag = .elem_ptr },
+ array_ptr: Value,
+ index: usize,
+ };
+
+ pub const Bytes = struct {
+ base: Payload = Payload{ .tag = .bytes },
+ data: []const u8,
+ };
+
+ pub const Ty = struct {
+ base: Payload = Payload{ .tag = .ty },
+ ty: Type,
+ };
+
+ pub const IntType = struct {
+ base: Payload = Payload{ .tag = .int_type },
+ bits: u16,
+ signed: bool,
+ };
+
+ pub const Repeated = struct {
+ base: Payload = Payload{ .tag = .ty },
+ /// This value is repeated some number of times. The amount of times to repeat
+ /// is stored externally.
+ val: Value,
+ };
+
+ pub const Float_16 = struct {
+ base: Payload = .{ .tag = .float_16 },
+ val: f16,
+ };
+
+ pub const Float_32 = struct {
+ base: Payload = .{ .tag = .float_32 },
+ val: f32,
+ };
+
+ pub const Float_64 = struct {
+ base: Payload = .{ .tag = .float_64 },
+ val: f64,
+ };
+
+ pub const Float_128 = struct {
+ base: Payload = .{ .tag = .float_128 },
+ val: f128,
+ };
+
+ pub const ErrorSet = struct {
+ base: Payload = .{ .tag = .error_set },
+
+ // TODO revisit this when we have the concept of the error tag type
+ fields: std.StringHashMapUnmanaged(u16),
+ decl: *Module.Decl,
+ };
+
+ pub const Error = struct {
+ base: Payload = .{ .tag = .@"error" },
+
+ // TODO revisit this when we have the concept of the error tag type
+ /// `name` is owned by `Module` and will be valid for the entire
+ /// duration of the compilation.
+ name: []const u8,
+ value: u16,
+ };
+ };
+
+ /// Big enough to fit any non-BigInt value
+ pub const BigIntSpace = struct {
+ /// The +1 is headroom so that operations such as incrementing once or decrementing once
+ /// are possible without using an allocator.
+ limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb,
+ };
+};