mirror of
https://github.com/ziglang/zig.git
synced 2026-01-05 21:13:24 +00:00
* update to the new cache hash API
* std.Target defaultVersionRange moves to std.Target.Os.Tag
* std.Target.Os gains getVersionRange which returns a tagged union
* start the process of splitting Module into Compilation and "zig
module".
- The parts of Module having to do with only compiling zig code are
extracted into ZigModule.zig.
- Next step is to rename Module to Compilation.
- After that rename ZigModule back to Module.
* implement proper cache hash usage when compiling C objects, and
properly manage the file lock of the build artifacts.
* make versions optional to match recent changes to master branch.
* proper cache hash integration for compiling zig code
* proper cache hash integration for linking even when not compiling zig
code.
* ELF LLD linking integrates with the caching system. A comment from
the source code:
Here we want to determine whether we can save time by not invoking LLD when the
output is unchanged. None of the linker options or the object files that are being
linked are in the hash that namespaces the directory we are outputting to. Therefore,
we must hash those now, and the resulting digest will form the "id" of the linking
job we are about to perform.
After a successful link, we store the id in the metadata of a symlink named "id.txt" in
the artifact directory. So, now, we check if this symlink exists, and if it matches
our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD.
* implement disable_c_depfile option
* add tracy to a few more functions
1642 lines
55 KiB
Zig
1642 lines
55 KiB
Zig
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("ZigModule.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,
|
|
};
|
|
};
|