zig/src-self-hosted/value.zig

164 lines
5.4 KiB
Zig
Raw Normal View History

const std = @import("std");
2020-04-18 16:41:45 -07:00
const Type = @import("type.zig").Type;
const log2 = std.math.log2;
const assert = std.debug.assert;
2020-04-17 21:09:43 -07:00
/// 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.
2020-04-18 16:41:45 -07:00
/// 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,
2020-04-17 21:09:43 -07:00
pub const Tag = enum {
2020-04-18 16:41:45 -07:00
// The first section of this enum are tags that require no payload.
2020-04-17 21:09:43 -07:00
void_type,
noreturn_type,
bool_type,
usize_type,
void_value,
noreturn_value,
bool_true,
2020-04-19 17:04:11 -07:00
bool_false, // See last_no_payload_tag below.
2020-04-18 16:41:45 -07:00
// After this, the tag requires a payload.
2020-04-17 21:09:43 -07:00
2020-04-18 16:41:45 -07:00
ty,
2020-04-17 21:09:43 -07:00
int_u64,
int_i64,
function,
ref,
bytes,
2020-04-19 17:04:11 -07:00
pub const last_no_payload_tag = Tag.bool_false;
pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
};
2020-04-18 17:22:54 -07:00
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) };
2020-04-18 16:41:45 -07:00
}
2020-04-18 16:41:45 -07:00
pub fn initPayload(payload: *Payload) Value {
assert(@enumToInt(payload.tag) >= Tag.no_payload_count);
return .{ .ptr_otherwise = payload };
}
2020-04-18 16:41:45 -07:00
pub fn tag(self: Value) Tag {
if (self.tag_if_small_enough < Tag.no_payload_count) {
2020-04-19 17:04:11 -07:00
return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough));
2020-04-18 16:41:45 -07:00
} else {
return self.ptr_otherwise.tag;
}
}
2020-04-19 17:04:11 -07:00
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);
switch (self.tag()) {
.void_type => return out_stream.writeAll("void"),
.noreturn_type => return out_stream.writeAll("noreturn"),
.bool_type => return out_stream.writeAll("bool"),
.usize_type => return out_stream.writeAll("usize"),
.void_value => return out_stream.writeAll("{}"),
.noreturn_value => return out_stream.writeAll("unreachable"),
.bool_true => return out_stream.writeAll("true"),
.bool_false => return out_stream.writeAll("false"),
.ty => return self.cast(Payload.Ty).?.ty.format("", options, out_stream),
.int_u64 => return std.fmt.formatIntValue(self.cast(Payload.Int_u64).?.int, "", options, out_stream),
.int_i64 => return std.fmt.formatIntValue(self.cast(Payload.Int_i64).?.int, "", options, out_stream),
.function => return out_stream.writeAll("(function)"),
.ref => return out_stream.writeAll("(ref)"),
.bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream),
2020-04-19 17:04:11 -07:00
}
}
2020-04-18 16:41:45 -07:00
/// This type is not copyable since it may contain pointers to its inner data.
pub const Payload = struct {
tag: Tag,
2020-04-18 16:41:45 -07:00
pub const Int_u64 = struct {
base: Payload = Payload{ .tag = .int_u64 },
int: u64,
};
2020-04-18 16:41:45 -07:00
pub const Int_i64 = struct {
base: Payload = Payload{ .tag = .int_i64 },
int: i64,
};
pub const Function = struct {
base: Payload = Payload{ .tag = .function },
};
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 },
pointee: *MemoryCell,
};
pub const Bytes = struct {
base: Payload = Payload{ .tag = .bytes },
data: []u8,
};
2018-07-13 18:56:38 -07:00
2020-04-18 16:41:45 -07:00
pub const Ty = struct {
2020-04-19 17:04:11 -07:00
base: Payload = Payload{ .tag = .ty },
ty: Type,
2020-04-18 16:41:45 -07:00
};
2020-04-17 21:09:43 -07:00
};
};
2020-04-18 16:41:45 -07:00
/// 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.
2020-04-17 21:09:43 -07:00
pub const MemoryCell = struct {
parent: Parent,
2020-04-18 16:41:45 -07:00
contents: Value,
2020-04-17 21:09:43 -07:00
pub const Parent = union(enum) {
none,
struct_field: struct {
struct_base: *MemoryCell,
field_index: usize,
2020-04-17 21:09:43 -07:00
},
array_elem: struct {
array_base: *MemoryCell,
elem_index: usize,
2020-04-17 21:09:43 -07:00
},
union_field: *MemoryCell,
err_union_code: *MemoryCell,
err_union_payload: *MemoryCell,
optional_payload: *MemoryCell,
optional_flag: *MemoryCell,
};
};