pub const Node = extern union
pub const Node = 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 {
/// Declarations add themselves to the correct scopes and should not be emitted as this tag.
declaration,
null_literal,
undefined_literal,
/// opaque {}
opaque_literal,
true_literal,
false_literal,
empty_block,
return_void,
zero_literal,
one_literal,
@"unreachable",
void_type,
noreturn_type,
@"anytype",
@"continue",
@"break",
// After this, the tag requires a payload.
integer_literal,
float_literal,
string_literal,
char_literal,
enum_literal,
/// "string"[0..end]
string_slice,
identifier,
@"if",
/// if (!operand) break;
if_not_break,
@"while",
/// while (true) operand
while_true,
@"switch",
/// else => operand,
switch_else,
/// items => body,
switch_prong,
break_val,
@"return",
field_access,
field_builtin,
array_access,
call,
var_decl,
/// const name = struct { init }
wrapped_local,
/// var name = init.*
mut_str,
func,
warning,
@"struct",
@"union",
@"opaque",
@"comptime",
@"defer",
array_init,
tuple,
container_init,
container_init_dot,
/// _ = operand;
discard,
// a + b
add,
// a = b
add_assign,
// c = (a = b)
add_wrap,
add_wrap_assign,
sub,
sub_assign,
sub_wrap,
sub_wrap_assign,
mul,
mul_assign,
mul_wrap,
mul_wrap_assign,
div,
div_assign,
shl,
shl_assign,
shr,
shr_assign,
mod,
mod_assign,
@"and",
@"or",
less_than,
less_than_equal,
greater_than,
greater_than_equal,
equal,
not_equal,
bit_and,
bit_and_assign,
bit_or,
bit_or_assign,
bit_xor,
bit_xor_assign,
array_cat,
ellipsis3,
assign,
/// @intCast(operand)
int_cast,
/// @constCast(operand)
const_cast,
/// @volatileCast(operand)
volatile_cast,
/// @divTrunc(lhs, rhs)
div_trunc,
/// @intFromBool(operand)
int_from_bool,
/// @as(lhs, rhs)
as,
/// @truncate(operand)
truncate,
/// @bitCast(operand)
bit_cast,
/// @floatCast(operand)
float_cast,
/// @intFromFloat(operand)
int_from_float,
/// @floatFromInt(operand)
float_from_int,
/// @ptrFromInt(operand)
ptr_from_int,
/// @intFromPtr(operand)
int_from_ptr,
/// @alignCast(operand)
align_cast,
/// @ptrCast(operand)
ptr_cast,
/// @divExact(lhs, rhs)
div_exact,
/// @offsetOf(lhs, rhs)
offset_of,
/// @splat(operand)
vector_zero_init,
/// @shuffle(type, a, b, mask)
shuffle,
/// @extern(ty, .{ .name = n })
builtin_extern,
/// @byteSwap(operand)
byte_swap,
/// @ceil(operand)
ceil,
/// @cos(operand)
cos,
/// @sin(operand)
sin,
/// @exp(operand)
exp,
/// @exp2(operand)
exp2,
/// @exp10(operand)
exp10,
/// @abs(operand)
abs,
/// @log(operand)
log,
/// @log2(operand)
log2,
/// @log10(operand)
log10,
/// @round(operand)
round,
/// @sqrt(operand)
sqrt,
/// @trunc(operand)
trunc,
/// @floor(operand)
floor,
/// __helpers.<name>(argshelper_call)
helper_call,
/// __helpers.<name>
helper_ref,
asm_simple,
negate,
negate_wrap,
bit_not,
not,
address_of,
/// .?
unwrap,
/// .*
deref,
block,
/// { operand }
block_single,
sizeof,
alignof,
typeof,
typeinfo,
type,
optional_type,
c_pointer,
single_pointer,
array_type,
null_sentinel_array_type,
/// @Vector(lhs, rhs)
vector,
/// @import("std").mem.zeroes(operand)
std_mem_zeroes,
/// @import("std").mem.zeroInit(lhs, rhs)
std_mem_zeroinit,
// pub const name = @compileError(msg);
fail_decl,
// var actual = mangled;
arg_redecl,
/// pub const alias = actual;
alias,
/// const name = init;
var_simple,
/// pub const name = init;
pub_var_simple,
/// pub? const name (: type)? = value
enum_constant,
/// pub inline fn name(params) return_type body
pub_inline_fn,
/// array_type{}
empty_array,
/// @as([count]type, @splat(val))
array_filler,
/// comptime { if (!(lhs)) @compileError(rhs); }
static_assert,
/// __root.<name>
root_ref,
pub const last_no_payload_tag = Tag.@"break";
pub const no_payload_count = @backingInt(last_no_payload_tag) + 1;
pub fn Type(comptime t: Tag) type {
return switch (t) {
.declaration,
.null_literal,
.undefined_literal,
.opaque_literal,
.true_literal,
.false_literal,
.empty_block,
.return_void,
.zero_literal,
.one_literal,
.void_type,
.noreturn_type,
.@"anytype",
.@"continue",
.@"break",
.@"unreachable",
=> @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
.std_mem_zeroes,
.@"return",
.@"comptime",
.@"defer",
.asm_simple,
.negate,
.negate_wrap,
.bit_not,
.not,
.optional_type,
.address_of,
.unwrap,
.deref,
.int_from_ptr,
.empty_array,
.while_true,
.if_not_break,
.switch_else,
.block_single,
.int_from_bool,
.sizeof,
.alignof,
.typeof,
.typeinfo,
.align_cast,
.truncate,
.bit_cast,
.float_cast,
.int_from_float,
.float_from_int,
.ptr_from_int,
.ptr_cast,
.int_cast,
.const_cast,
.volatile_cast,
.vector_zero_init,
.byte_swap,
.ceil,
.cos,
.sin,
.exp,
.exp2,
.exp10,
.abs,
.log,
.log2,
.log10,
.round,
.sqrt,
.trunc,
.floor,
=> Payload.UnOp,
.add,
.add_assign,
.add_wrap,
.add_wrap_assign,
.sub,
.sub_assign,
.sub_wrap,
.sub_wrap_assign,
.mul,
.mul_assign,
.mul_wrap,
.mul_wrap_assign,
.div,
.div_assign,
.shl,
.shl_assign,
.shr,
.shr_assign,
.mod,
.mod_assign,
.@"and",
.@"or",
.less_than,
.less_than_equal,
.greater_than,
.greater_than_equal,
.equal,
.not_equal,
.bit_and,
.bit_and_assign,
.bit_or,
.bit_or_assign,
.bit_xor,
.bit_xor_assign,
.div_trunc,
.as,
.array_cat,
.ellipsis3,
.assign,
.array_access,
.std_mem_zeroinit,
.vector,
.div_exact,
.offset_of,
.static_assert,
.field_builtin,
=> Payload.BinOp,
.integer_literal,
.float_literal,
.string_literal,
.char_literal,
.enum_literal,
.identifier,
.warning,
.type,
=> Payload.Value,
.discard => Payload.Discard,
.@"if" => Payload.If,
.@"while" => Payload.While,
.@"switch", .array_init, .switch_prong => Payload.Switch,
.break_val => Payload.BreakVal,
.call => Payload.Call,
.var_decl => Payload.VarDecl,
.func => Payload.Func,
.@"struct", .@"union", .@"opaque" => Payload.Container,
.tuple => Payload.TupleInit,
.container_init => Payload.ContainerInit,
.container_init_dot => Payload.ContainerInitDot,
.block => Payload.Block,
.c_pointer, .single_pointer => Payload.Pointer,
.array_type, .null_sentinel_array_type => Payload.Array,
.arg_redecl, .alias => Payload.ArgRedecl,
.fail_decl => Payload.FailDecl,
.var_simple, .pub_var_simple, .wrapped_local, .mut_str => Payload.SimpleVarDecl,
.enum_constant => Payload.EnumConstant,
.array_filler => Payload.ArrayFiller,
.pub_inline_fn => Payload.PubInlineFn,
.field_access => Payload.FieldAccess,
.string_slice => Payload.StringSlice,
.shuffle => Payload.Shuffle,
.builtin_extern => Payload.Extern,
.helper_call => Payload.HelperCall,
.helper_ref => Payload.HelperRef,
.root_ref => Payload.RootRef,
};
}
pub fn init(comptime t: Tag) Node {
comptime std.debug.assert(@backingInt(t) < Tag.no_payload_count);
return .{ .tag_if_small_enough = @backingInt(t) };
}
pub fn create(comptime t: Tag, ally: Allocator, data: Data(t)) error{OutOfMemory}!Node {
const ptr = try ally.create(t.Type());
ptr.* = .{
.base = .{ .tag = t },
.data = data,
};
return Node{ .ptr_otherwise = &ptr.base };
}
pub fn Data(comptime t: Tag) type {
return std.meta.fieldInfo(t.Type(), .data).type;
}
};
pub fn tag(self: Node) Tag {
if (self.tag_if_small_enough < Tag.no_payload_count) {
return @fromBackingInt(@intCast(@as(std.meta.Tag(Tag), @intCast(self.tag_if_small_enough))));
} else {
return self.ptr_otherwise.tag;
}
}
pub fn castTag(self: Node, comptime t: Tag) ?*t.Type() {
if (self.tag_if_small_enough < Tag.no_payload_count)
return null;
if (self.ptr_otherwise.tag == t)
return @alignCast(@fieldParentPtr("base", self.ptr_otherwise));
return null;
}
pub fn initPayload(payload: *Payload) Node {
std.debug.assert(@backingInt(payload.tag) >= Tag.no_payload_count);
return .{ .ptr_otherwise = payload };
}
pub fn isNoreturn(node: Node) bool {
return switch (node.tag()) {
.block => {
const block_node = node.castTag(.block).?;
if (block_node.data.stmts.len == 0) return false;
const last = block_node.data.stmts[block_node.data.stmts.len - 1];
return last.isNoreturn();
},
.@"switch" => {
const switch_node = node.castTag(.@"switch").?;
for (switch_node.data.cases) |case| {
const body = if (case.castTag(.switch_else)) |some|
some.data
else if (case.castTag(.switch_prong)) |some|
some.data.cond
else
unreachable;
if (!body.isNoreturn()) return false;
}
return true;
},
.@"return", .return_void => true,
.@"break" => true,
.@"continue" => true,
.@"unreachable" => true,
else => false,
};
}
pub fn isBoolRes(res: Node) bool {
switch (res.tag()) {
.@"or",
.@"and",
.equal,
.not_equal,
.less_than,
.less_than_equal,
.greater_than,
.greater_than_equal,
.not,
.false_literal,
.true_literal,
=> return true,
else => return false,
}
}
}