zig/src-self-hosted/type.zig

114 lines
4.2 KiB
Zig
Raw Normal View History

const std = @import("std");
const Value = @import("value.zig").Value;
const assert = std.debug.assert;
2020-04-18 16:41:45 -07:00
/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
/// It's important for this struct to be small.
/// It is not copyable since it may contain references to its inner data.
/// Types are not de-duplicated, which helps with multi-threading since it obviates the requirement
/// of obtaining a lock on a global type table, as well as making the
/// garbage collection bookkeeping simpler.
/// This union takes advantage of the fact that the first page of memory
/// is unmapped, giving us 4096 possible enum tags that have no payload.
pub const Type = extern union {
/// If the tag value is less than Tag.no_payload_count, then no pointer
/// dereference is needed.
tag_if_small_enough: usize,
ptr_otherwise: *Payload,
pub fn zigTypeTag(self: Type) std.builtin.TypeId {
switch (self.tag()) {
.int_u8, .int_usize => return .Int,
.array_u8, .array_u8_sentinel_0 => return .Array,
.single_const_pointer => return .Pointer,
}
}
2020-04-18 17:22:54 -07:00
pub fn initTag(comptime small_tag: Tag) Type {
comptime assert(@enumToInt(small_tag) < Tag.no_payload_count);
return .{ .tag_if_small_enough = @enumToInt(small_tag) };
}
2020-04-18 16:41:45 -07:00
pub fn initPayload(payload: *Payload) Type {
assert(@enumToInt(payload.tag) >= Tag.no_payload_count);
return .{ .ptr_otherwise = payload };
}
2020-04-18 16:41:45 -07:00
pub fn tag(self: Type) Tag {
if (self.tag_if_small_enough < Tag.no_payload_count) {
return @intToEnum(self.tag_if_small_enough);
} else {
return self.ptr_otherwise.tag;
}
}
2020-04-18 16:41:45 -07:00
pub fn format(
self: Type,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
out_stream: var,
) !void {
comptime assert(fmt.len == 0);
switch (self.tag()) {
.int_u8 => return out_stream.writeAll("u8"),
.int_usize => return out_stream.writeAll("usize"),
.array_u8_sentinel_0 => {
const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", self.ptr_otherwise);
return out_stream.print("[{}:0]u8", .{payload.len});
},
.array => {
const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);
return out_stream.print("[{}]{}", .{ payload.len, payload.elem_type });
},
.single_const_pointer => {
const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", self.ptr_otherwise);
return out_stream.print("*const {}", .{payload.pointee_type});
},
}
}
2020-04-18 16:41:45 -07:00
/// This enum does not directly correspond to `std.builtin.TypeId` because
/// it has extra enum tags in it, as a way of using less memory. For example,
/// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
/// but with different alignment values, in this data structure they are represented
/// with different enum tags, because the the former requires more payload data than the latter.
/// See `zigTypeTag` for the function that corresponds to `std.builtin.TypeId`.
pub const Tag = enum {
// The first section of this enum are tags that require no payload.
2020-04-18 21:02:03 -07:00
int_comptime,
2020-04-18 16:41:45 -07:00
int_u8,
int_usize,
// Bump this when adding items above.
pub const last_no_payload_tag = Tag.int_usize;
pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
// After this, the tag requires a payload.
array_u8_sentinel_0,
array,
single_const_pointer,
};
pub const Payload = struct {
tag: Tag,
pub const Array_u8_Sentinel0 = struct {
base: Payload = Payload{ .tag = .array_u8_sentinel_0 },
len: u64,
};
2018-07-13 18:56:38 -07:00
2020-04-18 16:41:45 -07:00
pub const Array = struct {
base: Payload = Payload{ .tag = .array },
2020-04-18 16:41:45 -07:00
elem_type: Type,
len: u64,
};
2020-04-18 16:41:45 -07:00
pub const SingleConstPointer = struct {
base: Payload = Payload{ .tag = .single_const_pointer },
2020-04-18 16:41:45 -07:00
pointee_type: Type,
};
};
};