mirror of
https://github.com/ziglang/zig.git
synced 2025-12-14 18:23:12 +00:00
Now there are 3 types:
* std.math.big.int.Const
- the memory is immutable, only stores limbs and is_positive
- all methods operating on constant data go here
* std.math.big.int.Mutable
- the memory is mutable, stores capacity in addition to limbs and
is_positive
- methods here have some Mutable parameters and some Const
parameters. These methods expect callers to pre-calculate the
amount of resources required, and asserts that the resources are
available.
* std.math.big.int.Managed
- the memory is mutable and additionally stores an allocator.
- methods here perform the resource calculations for the programmer.
- this is the high level abstraction from before
Each of these 3 types can be converted to the other ones.
You can see the use case for this in the self-hosted compiler, where we
only store limbs, and construct the big ints as needed.
This gets rid of the hack where the allocator was optional and the
notion of "fixed" versions of the struct. Such things are now modeled
with the `big.int.Const` type.
919 lines
31 KiB
Zig
919 lines
31 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;
|
|
|
|
/// This is the raw data, with no bookkeeping, no memory awareness,
|
|
/// no de-duplication, and no type system awareness.
|
|
/// It's important for this struct 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,
|
|
isize_type,
|
|
usize_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,
|
|
fn_naked_noreturn_no_args_type,
|
|
fn_ccc_void_no_args_type,
|
|
single_const_pointer_to_comptime_int_type,
|
|
const_slice_u8_type,
|
|
|
|
undef,
|
|
zero,
|
|
the_one_possible_value, // when the type only has one possible value
|
|
null_value,
|
|
bool_true,
|
|
bool_false, // See last_no_payload_tag below.
|
|
// After this, the tag requires a payload.
|
|
|
|
ty,
|
|
int_u64,
|
|
int_i64,
|
|
int_big_positive,
|
|
int_big_negative,
|
|
function,
|
|
ref,
|
|
ref_val,
|
|
bytes,
|
|
repeated, // the value is a value repeated some number of times
|
|
|
|
pub const last_no_payload_tag = Tag.bool_false;
|
|
pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
|
|
};
|
|
|
|
pub fn initTag(comptime small_tag: Tag) Value {
|
|
comptime 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 format(
|
|
self: Value,
|
|
comptime fmt: []const u8,
|
|
options: std.fmt.FormatOptions,
|
|
out_stream: var,
|
|
) !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"),
|
|
.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"),
|
|
.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"),
|
|
|
|
.null_value => return out_stream.writeAll("null"),
|
|
.undef => return out_stream.writeAll("undefined"),
|
|
.zero => return out_stream.writeAll("0"),
|
|
.the_one_possible_value => return out_stream.writeAll("(one possible value)"),
|
|
.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_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)"),
|
|
.ref => return out_stream.writeAll("(ref)"),
|
|
.ref_val => {
|
|
try out_stream.writeAll("*const ");
|
|
val = val.cast(Payload.RefVal).?.val;
|
|
continue;
|
|
},
|
|
.bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream),
|
|
.repeated => {
|
|
try out_stream.writeAll("(repeated) ");
|
|
val = val.cast(Payload.Repeated).?.val;
|
|
},
|
|
};
|
|
}
|
|
|
|
/// 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) Allocator.Error![]u8 {
|
|
if (self.cast(Payload.Bytes)) |bytes| {
|
|
return std.mem.dupe(allocator, u8, bytes.data);
|
|
}
|
|
unreachable;
|
|
}
|
|
|
|
/// Asserts that the value is representable as a type.
|
|
pub fn toType(self: Value) Type {
|
|
return switch (self.tag()) {
|
|
.ty => self.cast(Payload.Ty).?.ty,
|
|
|
|
.u8_type => Type.initTag(.@"u8"),
|
|
.i8_type => Type.initTag(.@"i8"),
|
|
.isize_type => Type.initTag(.@"isize"),
|
|
.usize_type => Type.initTag(.@"usize"),
|
|
.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"),
|
|
.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),
|
|
|
|
.undef,
|
|
.zero,
|
|
.the_one_possible_value,
|
|
.bool_true,
|
|
.bool_false,
|
|
.null_value,
|
|
.int_u64,
|
|
.int_i64,
|
|
.int_big_positive,
|
|
.int_big_negative,
|
|
.function,
|
|
.ref,
|
|
.ref_val,
|
|
.bytes,
|
|
.repeated,
|
|
=> unreachable,
|
|
};
|
|
}
|
|
|
|
/// Asserts the value is an integer.
|
|
pub fn toBigInt(self: Value, space: *BigIntSpace) BigIntConst {
|
|
switch (self.tag()) {
|
|
.ty,
|
|
.u8_type,
|
|
.i8_type,
|
|
.isize_type,
|
|
.usize_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,
|
|
.fn_naked_noreturn_no_args_type,
|
|
.fn_ccc_void_no_args_type,
|
|
.single_const_pointer_to_comptime_int_type,
|
|
.const_slice_u8_type,
|
|
.bool_true,
|
|
.bool_false,
|
|
.null_value,
|
|
.function,
|
|
.ref,
|
|
.ref_val,
|
|
.bytes,
|
|
.undef,
|
|
.repeated,
|
|
=> unreachable,
|
|
|
|
.the_one_possible_value, // An integer with one possible value is always zero.
|
|
.zero,
|
|
=> return BigIntMutable.init(&space.limbs, 0).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,
|
|
.u8_type,
|
|
.i8_type,
|
|
.isize_type,
|
|
.usize_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,
|
|
.fn_naked_noreturn_no_args_type,
|
|
.fn_ccc_void_no_args_type,
|
|
.single_const_pointer_to_comptime_int_type,
|
|
.const_slice_u8_type,
|
|
.bool_true,
|
|
.bool_false,
|
|
.null_value,
|
|
.function,
|
|
.ref,
|
|
.ref_val,
|
|
.bytes,
|
|
.undef,
|
|
.repeated,
|
|
=> unreachable,
|
|
|
|
.zero,
|
|
.the_one_possible_value, // an integer with one possible value is always zero
|
|
=> return 0,
|
|
|
|
.int_u64 => return self.cast(Payload.Int_u64).?.int,
|
|
.int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.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 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,
|
|
.u8_type,
|
|
.i8_type,
|
|
.isize_type,
|
|
.usize_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,
|
|
.fn_naked_noreturn_no_args_type,
|
|
.fn_ccc_void_no_args_type,
|
|
.single_const_pointer_to_comptime_int_type,
|
|
.const_slice_u8_type,
|
|
.bool_true,
|
|
.bool_false,
|
|
.null_value,
|
|
.function,
|
|
.ref,
|
|
.ref_val,
|
|
.bytes,
|
|
.undef,
|
|
.repeated,
|
|
=> unreachable,
|
|
|
|
.the_one_possible_value, // an integer with one possible value is always zero
|
|
.zero,
|
|
=> return 0,
|
|
|
|
.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,
|
|
.u8_type,
|
|
.i8_type,
|
|
.isize_type,
|
|
.usize_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,
|
|
.fn_naked_noreturn_no_args_type,
|
|
.fn_ccc_void_no_args_type,
|
|
.single_const_pointer_to_comptime_int_type,
|
|
.const_slice_u8_type,
|
|
.bool_true,
|
|
.bool_false,
|
|
.null_value,
|
|
.function,
|
|
.ref,
|
|
.ref_val,
|
|
.bytes,
|
|
.repeated,
|
|
=> unreachable,
|
|
|
|
.zero,
|
|
.undef,
|
|
.the_one_possible_value, // an integer with one possible value is always zero
|
|
=> return true,
|
|
|
|
.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,
|
|
},
|
|
}
|
|
}
|
|
|
|
/// Asserts the value is a float
|
|
pub fn floatHasFraction(self: Value) bool {
|
|
return switch (self.tag()) {
|
|
.ty,
|
|
.u8_type,
|
|
.i8_type,
|
|
.isize_type,
|
|
.usize_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,
|
|
.fn_naked_noreturn_no_args_type,
|
|
.fn_ccc_void_no_args_type,
|
|
.single_const_pointer_to_comptime_int_type,
|
|
.const_slice_u8_type,
|
|
.bool_true,
|
|
.bool_false,
|
|
.null_value,
|
|
.function,
|
|
.ref,
|
|
.ref_val,
|
|
.bytes,
|
|
.repeated,
|
|
.undef,
|
|
.int_u64,
|
|
.int_i64,
|
|
.int_big_positive,
|
|
.int_big_negative,
|
|
.the_one_possible_value,
|
|
=> unreachable,
|
|
|
|
.zero => false,
|
|
};
|
|
}
|
|
|
|
pub fn orderAgainstZero(lhs: Value) std.math.Order {
|
|
switch (lhs.tag()) {
|
|
.ty,
|
|
.u8_type,
|
|
.i8_type,
|
|
.isize_type,
|
|
.usize_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,
|
|
.fn_naked_noreturn_no_args_type,
|
|
.fn_ccc_void_no_args_type,
|
|
.single_const_pointer_to_comptime_int_type,
|
|
.const_slice_u8_type,
|
|
.bool_true,
|
|
.bool_false,
|
|
.null_value,
|
|
.function,
|
|
.ref,
|
|
.ref_val,
|
|
.bytes,
|
|
.repeated,
|
|
.undef,
|
|
=> unreachable,
|
|
|
|
.zero,
|
|
.the_one_possible_value, // an integer with one possible value is always zero
|
|
=> return .eq,
|
|
|
|
.int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),
|
|
.int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),
|
|
.int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),
|
|
.int_big_negative => return lhs.cast(Payload.IntBigNegative).?.asBigInt().orderAgainstScalar(0),
|
|
}
|
|
}
|
|
|
|
/// Asserts the value is comparable.
|
|
pub fn order(lhs: Value, rhs: Value) std.math.Order {
|
|
const lhs_tag = lhs.tag();
|
|
const rhs_tag = lhs.tag();
|
|
const lhs_is_zero = lhs_tag == .zero or lhs_tag == .the_one_possible_value;
|
|
const rhs_is_zero = rhs_tag == .zero or rhs_tag == .the_one_possible_value;
|
|
if (lhs_is_zero) return rhs.orderAgainstZero().invert();
|
|
if (rhs_is_zero) return lhs.orderAgainstZero();
|
|
|
|
// TODO floats
|
|
|
|
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 toBool(self: Value) bool {
|
|
return switch (self.tag()) {
|
|
.bool_true => true,
|
|
.bool_false => false,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
/// Asserts the value is a pointer and dereferences it.
|
|
pub fn pointerDeref(self: Value) Value {
|
|
return switch (self.tag()) {
|
|
.ty,
|
|
.u8_type,
|
|
.i8_type,
|
|
.isize_type,
|
|
.usize_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,
|
|
.fn_naked_noreturn_no_args_type,
|
|
.fn_ccc_void_no_args_type,
|
|
.single_const_pointer_to_comptime_int_type,
|
|
.const_slice_u8_type,
|
|
.zero,
|
|
.bool_true,
|
|
.bool_false,
|
|
.null_value,
|
|
.function,
|
|
.int_u64,
|
|
.int_i64,
|
|
.int_big_positive,
|
|
.int_big_negative,
|
|
.bytes,
|
|
.undef,
|
|
.repeated,
|
|
=> unreachable,
|
|
|
|
.the_one_possible_value => Value.initTag(.the_one_possible_value),
|
|
.ref => self.cast(Payload.Ref).?.cell.contents,
|
|
.ref_val => self.cast(Payload.RefVal).?.val,
|
|
};
|
|
}
|
|
|
|
/// 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 elemValueAt(self: Value, allocator: *Allocator, index: usize) Allocator.Error!Value {
|
|
switch (self.tag()) {
|
|
.ty,
|
|
.u8_type,
|
|
.i8_type,
|
|
.isize_type,
|
|
.usize_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,
|
|
.fn_naked_noreturn_no_args_type,
|
|
.fn_ccc_void_no_args_type,
|
|
.single_const_pointer_to_comptime_int_type,
|
|
.const_slice_u8_type,
|
|
.zero,
|
|
.the_one_possible_value,
|
|
.bool_true,
|
|
.bool_false,
|
|
.null_value,
|
|
.function,
|
|
.int_u64,
|
|
.int_i64,
|
|
.int_big_positive,
|
|
.int_big_negative,
|
|
.undef,
|
|
=> unreachable,
|
|
|
|
.ref => @panic("TODO figure out how MemoryCell works"),
|
|
.ref_val => @panic("TODO figure out how MemoryCell works"),
|
|
|
|
.bytes => {
|
|
const int_payload = try allocator.create(Value.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,
|
|
}
|
|
}
|
|
|
|
pub fn isUndef(self: Value) bool {
|
|
return self.tag() == .undef;
|
|
}
|
|
|
|
/// Valid for all types. Asserts the value is not undefined.
|
|
/// `.the_one_possible_value` is reported as not null.
|
|
pub fn isNull(self: Value) bool {
|
|
return switch (self.tag()) {
|
|
.ty,
|
|
.u8_type,
|
|
.i8_type,
|
|
.isize_type,
|
|
.usize_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,
|
|
.fn_naked_noreturn_no_args_type,
|
|
.fn_ccc_void_no_args_type,
|
|
.single_const_pointer_to_comptime_int_type,
|
|
.const_slice_u8_type,
|
|
.zero,
|
|
.the_one_possible_value,
|
|
.bool_true,
|
|
.bool_false,
|
|
.function,
|
|
.int_u64,
|
|
.int_i64,
|
|
.int_big_positive,
|
|
.int_big_negative,
|
|
.ref,
|
|
.ref_val,
|
|
.bytes,
|
|
.repeated,
|
|
=> false,
|
|
|
|
.undef => unreachable,
|
|
.null_value => true,
|
|
};
|
|
}
|
|
|
|
/// 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 },
|
|
/// Index into the `fns` array of the `ir.Module`
|
|
index: usize,
|
|
};
|
|
|
|
pub const ArraySentinel0_u8_Type = struct {
|
|
base: Payload = Payload{ .tag = .array_sentinel_0_u8_type },
|
|
len: u64,
|
|
};
|
|
|
|
pub const SingleConstPtrType = struct {
|
|
base: Payload = Payload{ .tag = .single_const_ptr_type },
|
|
elem_type: *Type,
|
|
};
|
|
|
|
pub const Ref = struct {
|
|
base: Payload = Payload{ .tag = .ref },
|
|
cell: *MemoryCell,
|
|
};
|
|
|
|
pub const RefVal = struct {
|
|
base: Payload = Payload{ .tag = .ref_val },
|
|
val: Value,
|
|
};
|
|
|
|
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 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,
|
|
};
|
|
};
|
|
|
|
/// 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,
|
|
};
|
|
};
|
|
|
|
/// This is the heart of resource management of the Zig compiler. The Zig compiler uses
|
|
/// stop-the-world mark-and-sweep garbage collection during compilation to manage the resources
|
|
/// associated with evaluating compile-time code and semantic analysis. Each `MemoryCell` represents
|
|
/// a root.
|
|
pub const MemoryCell = struct {
|
|
parent: Parent,
|
|
contents: Value,
|
|
|
|
pub const Parent = union(enum) {
|
|
none,
|
|
struct_field: struct {
|
|
struct_base: *MemoryCell,
|
|
field_index: usize,
|
|
},
|
|
array_elem: struct {
|
|
array_base: *MemoryCell,
|
|
elem_index: usize,
|
|
},
|
|
union_field: *MemoryCell,
|
|
err_union_code: *MemoryCell,
|
|
err_union_payload: *MemoryCell,
|
|
optional_payload: *MemoryCell,
|
|
optional_flag: *MemoryCell,
|
|
};
|
|
};
|