zig/src/Zir.zig
Cody Tapscott d060cbbec7 stage2: Keep error return traces alive when storing to const
This change extends the "lifetime" of the error return trace associated
with an error to continue throughout the block of a `const` variable
that it is assigned to.

This is necessary to support patterns like this one in test_runner.zig:
```zig
const result = foo();
if (result) |_| {
    // ... success logic
} else |err| {
    // `foo()` should be included in the error trace here
    return error.TestFailed;
}
```

To make this happen, the majority of the error return trace popping logic
needed to move into Sema, since `const x = foo();` cannot be examined
syntactically to determine whether it modifies the error return trace. We
also have to make sure not to delete pertinent block information before it
makes it to Sema, so that Sema can pop/restore around blocks correctly.

* Why do this only for `const` and not `var`? *

There is room to relax things for `var`, but only a little bit. We could
do the same thing we do for const and keep the error trace alive for the
remainder of the block where the *assignment* happens. Any wider scope
would violate the stack discipline for traces, so it's not viable.

In the end, I decided the most consistent behavior for the user is just
to kill all error return traces assigned to a mutable `var`.
2022-10-21 12:40:29 -07:00

4122 lines
154 KiB
Zig

//! Zig Intermediate Representation. Astgen.zig converts AST nodes to these
//! untyped IR instructions. Next, Sema.zig processes these into AIR.
//! The minimum amount of information needed to represent a list of ZIR instructions.
//! Once this structure is completed, it can be used to generate AIR, followed by
//! machine code, without any memory access into the AST tree token list, node list,
//! or source bytes. Exceptions include:
//! * Compile errors, which may need to reach into these data structures to
//! create a useful report.
//! * In the future, possibly inline assembly, which needs to get parsed and
//! handled by the codegen backend, and errors reported there. However for now,
//! inline assembly is not an exception.
const std = @import("std");
const builtin = @import("builtin");
const mem = std.mem;
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const BigIntConst = std.math.big.int.Const;
const BigIntMutable = std.math.big.int.Mutable;
const Ast = std.zig.Ast;
const Zir = @This();
const Type = @import("type.zig").Type;
const Value = @import("value.zig").Value;
const TypedValue = @import("TypedValue.zig");
const Module = @import("Module.zig");
const LazySrcLoc = Module.LazySrcLoc;
instructions: std.MultiArrayList(Inst).Slice,
/// In order to store references to strings in fewer bytes, we copy all
/// string bytes into here. String bytes can be null. It is up to whomever
/// is referencing the data here whether they want to store both index and length,
/// thus allowing null bytes, or store only index, and use null-termination. The
/// `string_bytes` array is agnostic to either usage.
/// Indexes 0 and 1 are reserved for special cases.
string_bytes: []u8,
/// The meaning of this data is determined by `Inst.Tag` value.
/// The first few indexes are reserved. See `ExtraIndex` for the values.
extra: []u32,
/// The data stored at byte offset 0 when ZIR is stored in a file.
pub const Header = extern struct {
instructions_len: u32,
string_bytes_len: u32,
extra_len: u32,
/// We could leave this as padding, however it triggers a Valgrind warning because
/// we read and write undefined bytes to the file system. This is harmless, but
/// it's essentially free to have a zero field here and makes the warning go away,
/// making it more likely that following Valgrind warnings will be taken seriously.
unused: u32 = 0,
stat_inode: std.fs.File.INode,
stat_size: u64,
stat_mtime: i128,
};
pub const ExtraIndex = enum(u32) {
/// If this is 0, no compile errors. Otherwise there is a `CompileErrors`
/// payload at this index.
compile_errors,
/// If this is 0, this file contains no imports. Otherwise there is a `Imports`
/// payload at this index.
imports,
_,
};
/// Returns the requested data, as well as the new index which is at the start of the
/// trailers for the object.
pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, end: usize } {
const fields = @typeInfo(T).Struct.fields;
var i: usize = index;
var result: T = undefined;
inline for (fields) |field| {
@field(result, field.name) = switch (field.field_type) {
u32 => code.extra[i],
Inst.Ref => @intToEnum(Inst.Ref, code.extra[i]),
i32 => @bitCast(i32, code.extra[i]),
Inst.Call.Flags => @bitCast(Inst.Call.Flags, code.extra[i]),
Inst.BuiltinCall.Flags => @bitCast(Inst.BuiltinCall.Flags, code.extra[i]),
Inst.SwitchBlock.Bits => @bitCast(Inst.SwitchBlock.Bits, code.extra[i]),
Inst.FuncFancy.Bits => @bitCast(Inst.FuncFancy.Bits, code.extra[i]),
else => @compileError("bad field type"),
};
i += 1;
}
return .{
.data = result,
.end = i,
};
}
/// Given an index into `string_bytes` returns the null-terminated string found there.
pub fn nullTerminatedString(code: Zir, index: usize) [:0]const u8 {
var end: usize = index;
while (code.string_bytes[end] != 0) {
end += 1;
}
return code.string_bytes[index..end :0];
}
pub fn refSlice(code: Zir, start: usize, len: usize) []Inst.Ref {
const raw_slice = code.extra[start..][0..len];
// TODO we should be able to directly `@ptrCast` the slice to the other slice type.
return @ptrCast([*]Inst.Ref, raw_slice.ptr)[0..len];
}
pub fn hasCompileErrors(code: Zir) bool {
return code.extra[@enumToInt(ExtraIndex.compile_errors)] != 0;
}
pub fn deinit(code: *Zir, gpa: Allocator) void {
code.instructions.deinit(gpa);
gpa.free(code.string_bytes);
gpa.free(code.extra);
code.* = undefined;
}
/// ZIR is structured so that the outermost "main" struct of any file
/// is always at index 0.
pub const main_struct_inst: Inst.Index = 0;
/// These are untyped instructions generated from an Abstract Syntax Tree.
/// The data here is immutable because it is possible to have multiple
/// analyses on the same ZIR happening at the same time.
pub const Inst = struct {
tag: Tag,
data: Data,
/// These names are used directly as the instruction names in the text format.
/// See `data_field_map` for a list of which `Data` fields are used by each `Tag`.
pub const Tag = enum(u8) {
/// Arithmetic addition, asserts no integer overflow.
/// Uses the `pl_node` union field. Payload is `Bin`.
add,
/// Twos complement wrapping integer addition.
/// Uses the `pl_node` union field. Payload is `Bin`.
addwrap,
/// Saturating addition.
/// Uses the `pl_node` union field. Payload is `Bin`.
add_sat,
/// Arithmetic subtraction. Asserts no integer overflow.
/// Uses the `pl_node` union field. Payload is `Bin`.
sub,
/// Twos complement wrapping integer subtraction.
/// Uses the `pl_node` union field. Payload is `Bin`.
subwrap,
/// Saturating subtraction.
/// Uses the `pl_node` union field. Payload is `Bin`.
sub_sat,
/// Arithmetic multiplication. Asserts no integer overflow.
/// Uses the `pl_node` union field. Payload is `Bin`.
mul,
/// Twos complement wrapping integer multiplication.
/// Uses the `pl_node` union field. Payload is `Bin`.
mulwrap,
/// Saturating multiplication.
/// Uses the `pl_node` union field. Payload is `Bin`.
mul_sat,
/// Implements the `@divExact` builtin.
/// Uses the `pl_node` union field with payload `Bin`.
div_exact,
/// Implements the `@divFloor` builtin.
/// Uses the `pl_node` union field with payload `Bin`.
div_floor,
/// Implements the `@divTrunc` builtin.
/// Uses the `pl_node` union field with payload `Bin`.
div_trunc,
/// Implements the `@mod` builtin.
/// Uses the `pl_node` union field with payload `Bin`.
mod,
/// Implements the `@rem` builtin.
/// Uses the `pl_node` union field with payload `Bin`.
rem,
/// Ambiguously remainder division or modulus. If the computation would possibly have
/// a different value depending on whether the operation is remainder division or modulus,
/// a compile error is emitted. Otherwise the computation is performed.
/// Uses the `pl_node` union field. Payload is `Bin`.
mod_rem,
/// Integer shift-left. Zeroes are shifted in from the right hand side.
/// Uses the `pl_node` union field. Payload is `Bin`.
shl,
/// Implements the `@shlExact` builtin.
/// Uses the `pl_node` union field with payload `Bin`.
shl_exact,
/// Saturating shift-left.
/// Uses the `pl_node` union field. Payload is `Bin`.
shl_sat,
/// Integer shift-right. Arithmetic or logical depending on the signedness of
/// the integer type.
/// Uses the `pl_node` union field. Payload is `Bin`.
shr,
/// Implements the `@shrExact` builtin.
/// Uses the `pl_node` union field with payload `Bin`.
shr_exact,
/// Declares a parameter of the current function. Used for:
/// * debug info
/// * checking shadowing against declarations in the current namespace
/// * parameter type expressions referencing other parameters
/// These occur in the block outside a function body (the same block as
/// contains the func instruction).
/// Uses the `pl_tok` field. Token is the parameter name, payload is a `Param`.
param,
/// Same as `param` except the parameter is marked comptime.
param_comptime,
/// Same as `param` except the parameter is marked anytype.
/// Uses the `str_tok` field. Token is the parameter name. String is the parameter name.
param_anytype,
/// Same as `param` except the parameter is marked both comptime and anytype.
/// Uses the `str_tok` field. Token is the parameter name. String is the parameter name.
param_anytype_comptime,
/// Array concatenation. `a ++ b`
/// Uses the `pl_node` union field. Payload is `Bin`.
array_cat,
/// Array multiplication `a ** b`
/// Uses the `pl_node` union field. Payload is `Bin`.
array_mul,
/// `[N]T` syntax. No source location provided.
/// Uses the `pl_node` union field. Payload is `Bin`. lhs is length, rhs is element type.
array_type,
/// `[N:S]T` syntax. Source location is the array type expression node.
/// Uses the `pl_node` union field. Payload is `ArrayTypeSentinel`.
array_type_sentinel,
/// `@Vector` builtin.
/// Uses the `pl_node` union field with `Bin` payload.
/// lhs is length, rhs is element type.
vector_type,
/// Given an indexable type, returns the type of the element at given index.
/// Uses the `bin` union field. lhs is the indexable type, rhs is the index.
elem_type_index,
/// Given a pointer to an indexable object, returns the len property. This is
/// used by for loops. This instruction also emits a for-loop specific compile
/// error if the indexable object is not indexable.
/// Uses the `un_node` field. The AST node is the for loop node.
indexable_ptr_len,
/// Create a `anyframe->T` type.
/// Uses the `un_node` field.
anyframe_type,
/// Type coercion. No source location attached.
/// Uses the `bin` field.
as,
/// Type coercion to the function's return type.
/// Uses the `pl_node` field. Payload is `As`. AST node could be many things.
as_node,
/// Same as `as_node` but ignores runtime to comptime int error.
as_shift_operand,
/// Bitwise AND. `&`
bit_and,
/// Reinterpret the memory representation of a value as a different type.
/// Uses the pl_node field with payload `Bin`.
bitcast,
/// Bitwise NOT. `~`
/// Uses `un_tok`.
bit_not,
/// Bitwise OR. `|`
bit_or,
/// A labeled block of code, which can return a value.
/// Uses the `pl_node` union field. Payload is `Block`.
block,
/// A list of instructions which are analyzed in the parent context, without
/// generating a runtime block. Must terminate with an "inline" variant of
/// a noreturn instruction.
/// Uses the `pl_node` union field. Payload is `Block`.
block_inline,
/// Implements `suspend {...}`.
/// Uses the `pl_node` union field. Payload is `Block`.
suspend_block,
/// Boolean NOT. See also `bit_not`.
/// Uses the `un_tok` field.
bool_not,
/// Short-circuiting boolean `and`. `lhs` is a boolean `Ref` and the other operand
/// is a block, which is evaluated if `lhs` is `true`.
/// Uses the `bool_br` union field.
bool_br_and,
/// Short-circuiting boolean `or`. `lhs` is a boolean `Ref` and the other operand
/// is a block, which is evaluated if `lhs` is `false`.
/// Uses the `bool_br` union field.
bool_br_or,
/// Return a value from a block.
/// Uses the `break` union field.
/// Uses the source information from previous instruction.
@"break",
/// Return a value from a block. This instruction is used as the terminator
/// of a `block_inline`. It allows using the return value from `Sema.analyzeBody`.
/// This instruction may also be used when it is known that there is only one
/// break instruction in a block, and the target block is the parent.
/// Uses the `break` union field.
break_inline,
/// Checks that comptime control flow does not happen inside a runtime block.
/// Uses the `un_node` union field.
check_comptime_control_flow,
/// Function call.
/// Uses the `pl_node` union field with payload `Call`.
/// AST node is the function call.
call,
/// Implements the `@call` builtin.
/// Uses the `pl_node` union field with payload `BuiltinCall`.
/// AST node is the builtin call.
builtin_call,
/// `<`
/// Uses the `pl_node` union field. Payload is `Bin`.
cmp_lt,
/// `<=`
/// Uses the `pl_node` union field. Payload is `Bin`.
cmp_lte,
/// `==`
/// Uses the `pl_node` union field. Payload is `Bin`.
cmp_eq,
/// `>=`
/// Uses the `pl_node` union field. Payload is `Bin`.
cmp_gte,
/// `>`
/// Uses the `pl_node` union field. Payload is `Bin`.
cmp_gt,
/// `!=`
/// Uses the `pl_node` union field. Payload is `Bin`.
cmp_neq,
/// Coerces a result location pointer to a new element type. It is evaluated "backwards"-
/// as type coercion from the new element type to the old element type.
/// Uses the `pl_node` union field. Payload is `Bin`.
/// LHS is destination element type, RHS is result pointer.
coerce_result_ptr,
/// Conditional branch. Splits control flow based on a boolean condition value.
/// Uses the `pl_node` union field. AST node is an if, while, for, etc.
/// Payload is `CondBr`.
condbr,
/// Same as `condbr`, except the condition is coerced to a comptime value, and
/// only the taken branch is analyzed. The then block and else block must
/// terminate with an "inline" variant of a noreturn instruction.
condbr_inline,
/// Given an operand which is an error union, splits control flow. In
/// case of error, control flow goes into the block that is part of this
/// instruction, which is guaranteed to end with a return instruction
/// and never breaks out of the block.
/// In the case of non-error, control flow proceeds to the next instruction
/// after the `try`, with the result of this instruction being the unwrapped
/// payload value, as if `err_union_payload_unsafe` was executed on the operand.
/// Uses the `pl_node` union field. Payload is `Try`.
@"try",
///// Same as `try` except the operand is coerced to a comptime value, and
///// only the taken branch is analyzed. The block must terminate with an "inline"
///// variant of a noreturn instruction.
//try_inline,
/// Same as `try` except the operand is a pointer and the result is a pointer.
try_ptr,
///// Same as `try_inline` except the operand is a pointer and the result is a pointer.
//try_ptr_inline,
/// An error set type definition. Contains a list of field names.
/// Uses the `pl_node` union field. Payload is `ErrorSetDecl`.
error_set_decl,
error_set_decl_anon,
error_set_decl_func,
/// Declares the beginning of a statement. Used for debug info.
/// Uses the `dbg_stmt` union field. The line and column are offset
/// from the parent declaration.
dbg_stmt,
/// Marks a variable declaration. Used for debug info.
/// Uses the `str_op` union field. The string is the local variable name,
/// and the operand is the pointer to the variable's location. The local
/// may be a const or a var.
dbg_var_ptr,
/// Same as `dbg_var_ptr` but the local is always a const and the operand
/// is the local's value.
dbg_var_val,
/// Marks the beginning of a semantic scope for debug info variables.
dbg_block_begin,
/// Marks the end of a semantic scope for debug info variables.
dbg_block_end,
/// Uses a name to identify a Decl and takes a pointer to it.
/// Uses the `str_tok` union field.
decl_ref,
/// Uses a name to identify a Decl and uses it as a value.
/// Uses the `str_tok` union field.
decl_val,
/// Load the value from a pointer. Assumes `x.*` syntax.
/// Uses `un_node` field. AST node is the `x.*` syntax.
load,
/// Arithmetic division. Asserts no integer overflow.
/// Uses the `pl_node` union field. Payload is `Bin`.
div,
/// Given a pointer to an array, slice, or pointer, returns a pointer to the element at
/// the provided index.
/// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`.
elem_ptr_node,
/// Same as `elem_ptr_node` but used only for for loop.
/// Uses the `pl_node` union field. AST node is the condition of a for loop. Payload is `Bin`.
elem_ptr,
/// Same as `elem_ptr_node` except the index is stored immediately rather than
/// as a reference to another ZIR instruction.
/// Uses the `pl_node` union field. AST node is an element inside array initialization
/// syntax. Payload is `ElemPtrImm`.
elem_ptr_imm,
/// Given an array, slice, or pointer, returns the element at the provided index.
/// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`.
elem_val_node,
/// Same as `elem_val_node` but used only for for loop.
/// Uses the `pl_node` union field. AST node is the condition of a for loop. Payload is `Bin`.
elem_val,
/// Emits a compile error if the operand is not `void`.
/// Uses the `un_node` field.
ensure_result_used,
/// Emits a compile error if an error is ignored.
/// Uses the `un_node` field.
ensure_result_non_error,
/// Emits a compile error error union payload is not void.
ensure_err_union_payload_void,
/// Create a `E!T` type.
/// Uses the `pl_node` field with `Bin` payload.
error_union_type,
/// `error.Foo` syntax. Uses the `str_tok` field of the Data union.
error_value,
/// Implements the `@export` builtin function, based on either an identifier to a Decl,
/// or field access of a Decl. The thing being exported is the Decl.
/// Uses the `pl_node` union field. Payload is `Export`.
@"export",
/// Implements the `@export` builtin function, based on a comptime-known value.
/// The thing being exported is the comptime-known value which is the operand.
/// Uses the `pl_node` union field. Payload is `ExportValue`.
export_value,
/// Given a pointer to a struct or object that contains virtual fields, returns a pointer
/// to the named field. The field name is stored in string_bytes. Used by a.b syntax.
/// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
field_ptr,
/// Same as `field_ptr` but used for struct init.
field_ptr_init,
/// Given a struct or object that contains virtual fields, returns the named field.
/// The field name is stored in string_bytes. Used by a.b syntax.
/// This instruction also accepts a pointer.
/// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
field_val,
/// Given a pointer to a struct or object that contains virtual fields, returns the
/// named field. If there is no named field, searches in the type for a decl that
/// matches the field name. The decl is resolved and we ensure that it's a function
/// which can accept the object as the first parameter, with one pointer fixup. If
/// all of that works, this instruction produces a special "bound function" value
/// which contains both the function and the saved first parameter value.
/// Bound functions may only be used as the function parameter to a `call` or
/// `builtin_call` instruction. Any other use is invalid zir and may crash the compiler.
field_call_bind,
/// Given a pointer to a struct or object that contains virtual fields, returns a pointer
/// to the named field. The field name is a comptime instruction. Used by @field.
/// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
field_ptr_named,
/// Given a struct or object that contains virtual fields, returns the named field.
/// The field name is a comptime instruction. Used by @field.
/// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
field_val_named,
/// Returns a function type, or a function instance, depending on whether
/// the body_len is 0. Calling convention is auto.
/// Uses the `pl_node` union field. `payload_index` points to a `Func`.
func,
/// Same as `func` but has an inferred error set.
func_inferred,
/// Represents a function declaration or function prototype, depending on
/// whether body_len is 0.
/// Uses the `pl_node` union field. `payload_index` points to a `FuncFancy`.
func_fancy,
/// Implements the `@import` builtin.
/// Uses the `str_tok` field.
import,
/// Integer literal that fits in a u64. Uses the `int` union field.
int,
/// Arbitrary sized integer literal. Uses the `str` union field.
int_big,
/// A float literal that fits in a f64. Uses the float union value.
float,
/// A float literal that fits in a f128. Uses the `pl_node` union value.
/// Payload is `Float128`.
float128,
/// Make an integer type out of signedness and bit count.
/// Payload is `int_type`
int_type,
/// Return a boolean false if an optional is null. `x != null`
/// Uses the `un_node` field.
is_non_null,
/// Return a boolean false if an optional is null. `x.* != null`
/// Uses the `un_node` field.
is_non_null_ptr,
/// Return a boolean false if value is an error
/// Uses the `un_node` field.
is_non_err,
/// Return a boolean false if dereferenced pointer is an error
/// Uses the `un_node` field.
is_non_err_ptr,
/// A labeled block of code that loops forever. At the end of the body will have either
/// a `repeat` instruction or a `repeat_inline` instruction.
/// Uses the `pl_node` field. The AST node is either a for loop or while loop.
/// This ZIR instruction is needed because AIR does not (yet?) match ZIR, and Sema
/// needs to emit more than 1 AIR block for this instruction.
/// The payload is `Block`.
loop,
/// Sends runtime control flow back to the beginning of the current block.
/// Uses the `node` field.
repeat,
/// Sends comptime control flow back to the beginning of the current block.
/// Uses the `node` field.
repeat_inline,
/// Merge two error sets into one, `E1 || E2`.
/// Uses the `pl_node` field with payload `Bin`.
merge_error_sets,
/// Turns an R-Value into a const L-Value. In other words, it takes a value,
/// stores it in a memory location, and returns a const pointer to it. If the value
/// is `comptime`, the memory location is global static constant data. Otherwise,
/// the memory location is in the stack frame, local to the scope containing the
/// instruction.
/// Uses the `un_tok` union field.
ref,
/// Sends control flow back to the function's callee.
/// Includes an operand as the return value.
/// Includes an AST node source location.
/// Uses the `un_node` union field.
ret_node,
/// Sends control flow back to the function's callee.
/// The operand is a `ret_ptr` instruction, where the return value can be found.
/// Includes an AST node source location.
/// Uses the `un_node` union field.
ret_load,
/// Sends control flow back to the function's callee.
/// Includes an operand as the return value.
/// Includes a token source location.
/// Uses the `un_tok` union field.
ret_tok,
/// Sends control flow back to the function's callee.
/// The return operand is `error.foo` where `foo` is given by the string.
/// If the current function has an inferred error set, the error given by the
/// name is added to it.
/// Uses the `str_tok` union field.
ret_err_value,
/// A string name is provided which is an anonymous error set value.
/// If the current function has an inferred error set, the error given by the
/// name is added to it.
/// Results in the error code. Note that control flow is not diverted with
/// this instruction; a following 'ret' instruction will do the diversion.
/// Uses the `str_tok` union field.
ret_err_value_code,
/// Obtains a pointer to the return value.
/// Uses the `node` union field.
ret_ptr,
/// Obtains the return type of the in-scope function.
/// Uses the `node` union field.
ret_type,
/// Create a pointer type for overflow arithmetic.
/// TODO remove when doing https://github.com/ziglang/zig/issues/10248
overflow_arithmetic_ptr,
/// Create a pointer type which can have a sentinel, alignment, address space, and/or bit range.
/// Uses the `ptr_type` union field.
ptr_type,
/// Slice operation `lhs[rhs..]`. No sentinel and no end offset.
/// Returns a pointer to the subslice.
/// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceStart`.
slice_start,
/// Slice operation `array_ptr[start..end]`. No sentinel.
/// Returns a pointer to the subslice.
/// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceEnd`.
slice_end,
/// Slice operation `array_ptr[start..end:sentinel]`.
/// Returns a pointer to the subslice.
/// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceSentinel`.
slice_sentinel,
/// Write a value to a pointer. For loading, see `load`.
/// Source location is assumed to be same as previous instruction.
/// Uses the `bin` union field.
store,
/// Same as `store` except provides a source location.
/// Uses the `pl_node` union field. Payload is `Bin`.
store_node,
/// This instruction is not really supposed to be emitted from AstGen; nevertheless it
/// is sometimes emitted due to deficiencies in AstGen. When Sema sees this instruction,
/// it must clean up after AstGen's mess by looking at various context clues and
/// then treating it as one of the following:
/// * no-op
/// * store_to_inferred_ptr
/// * store
/// Uses the `bin` union field with LHS as the pointer to store to.
store_to_block_ptr,
/// Same as `store` but the type of the value being stored will be used to infer
/// the pointer type.
/// Uses the `bin` union field - Astgen.zig depends on the ability to change
/// the tag of an instruction from `store_to_block_ptr` to `store_to_inferred_ptr`
/// without changing the data.
store_to_inferred_ptr,
/// String Literal. Makes an anonymous Decl and then takes a pointer to it.
/// Uses the `str` union field.
str,
/// Arithmetic negation. Asserts no integer overflow.
/// Same as sub with a lhs of 0, split into a separate instruction to save memory.
/// Uses `un_node`.
negate,
/// Twos complement wrapping integer negation.
/// Same as subwrap with a lhs of 0, split into a separate instruction to save memory.
/// Uses `un_node`.
negate_wrap,
/// Returns the type of a value.
/// Uses the `un_node` field.
typeof,
/// Implements `@TypeOf` for one operand.
/// Uses the `pl_node` field.
typeof_builtin,
/// Given a value, look at the type of it, which must be an integer type.
/// Returns the integer type for the RHS of a shift operation.
/// Uses the `un_node` field.
typeof_log2_int_type,
/// Given an integer type, returns the integer type for the RHS of a shift operation.
/// Uses the `un_node` field.
log2_int_type,
/// Asserts control-flow will not reach this instruction (`unreachable`).
/// Uses the `unreachable` union field.
@"unreachable",
/// Bitwise XOR. `^`
/// Uses the `pl_node` union field. Payload is `Bin`.
xor,
/// Create an optional type '?T'
/// Uses the `un_node` field.
optional_type,
/// ?T => T with safety.
/// Given an optional value, returns the payload value, with a safety check that
/// the value is non-null. Used for `orelse`, `if` and `while`.
/// Uses the `un_node` field.
optional_payload_safe,
/// ?T => T without safety.
/// Given an optional value, returns the payload value. No safety checks.
/// Uses the `un_node` field.
optional_payload_unsafe,
/// *?T => *T with safety.
/// Given a pointer to an optional value, returns a pointer to the payload value,
/// with a safety check that the value is non-null. Used for `orelse`, `if` and `while`.
/// Uses the `un_node` field.
optional_payload_safe_ptr,
/// *?T => *T without safety.
/// Given a pointer to an optional value, returns a pointer to the payload value.
/// No safety checks.
/// Uses the `un_node` field.
optional_payload_unsafe_ptr,
/// E!T => T without safety.
/// Given an error union value, returns the payload value. No safety checks.
/// Uses the `un_node` field.
err_union_payload_unsafe,
/// *E!T => *T without safety.
/// Given a pointer to a error union value, returns a pointer to the payload value.
/// No safety checks.
/// Uses the `un_node` field.
err_union_payload_unsafe_ptr,
/// E!T => E without safety.
/// Given an error union value, returns the error code. No safety checks.
/// Uses the `un_node` field.
err_union_code,
/// *E!T => E without safety.
/// Given a pointer to an error union value, returns the error code. No safety checks.
/// Uses the `un_node` field.
err_union_code_ptr,
/// An enum literal. Uses the `str_tok` union field.
enum_literal,
/// A switch expression. Uses the `pl_node` union field.
/// AST node is the switch, payload is `SwitchBlock`.
switch_block,
/// Produces the value that will be switched on. For example, for
/// integers, it returns the integer with no modifications. For tagged unions, it
/// returns the active enum tag.
/// Uses the `un_node` union field.
switch_cond,
/// Same as `switch_cond`, except the input operand is a pointer to
/// what will be switched on.
/// Uses the `un_node` union field.
switch_cond_ref,
/// Produces the capture value for a switch prong.
/// Uses the `switch_capture` field.
/// If the `prong_index` field is max int, it means this is the capture
/// for the else/`_` prong.
switch_capture,
/// Produces the capture value for a switch prong.
/// Result is a pointer to the value.
/// Uses the `switch_capture` field.
/// If the `prong_index` field is max int, it means this is the capture
/// for the else/`_` prong.
switch_capture_ref,
/// Produces the capture value for a switch prong.
/// The prong is one of the multi cases.
/// Uses the `switch_capture` field.
switch_capture_multi,
/// Produces the capture value for a switch prong.
/// The prong is one of the multi cases.
/// Result is a pointer to the value.
/// Uses the `switch_capture` field.
switch_capture_multi_ref,
/// Produces the capture value for an inline switch prong tag capture.
/// Uses the `un_tok` field.
switch_capture_tag,
/// Given a
/// *A returns *A
/// *E!A returns *A
/// *?A returns *A
/// Uses the `un_node` field.
array_base_ptr,
/// Given a
/// *S returns *S
/// *E!S returns *S
/// *?S returns *S
/// Uses the `un_node` field.
field_base_ptr,
/// Checks that the type supports array init syntax.
/// Uses the `un_node` field.
validate_array_init_ty,
/// Checks that the type supports struct init syntax.
/// Uses the `un_node` field.
validate_struct_init_ty,
/// Given a set of `field_ptr` instructions, assumes they are all part of a struct
/// initialization expression, and emits compile errors for duplicate fields
/// as well as missing fields, if applicable.
/// This instruction asserts that there is at least one field_ptr instruction,
/// because it must use one of them to find out the struct type.
/// Uses the `pl_node` field. Payload is `Block`.
validate_struct_init,
/// Same as `validate_struct_init` but additionally communicates that the
/// resulting struct initialization value is within a comptime scope.
validate_struct_init_comptime,
/// Given a set of `elem_ptr_imm` instructions, assumes they are all part of an
/// array initialization expression, and emits a compile error if the number of
/// elements does not match the array type.
/// This instruction asserts that there is at least one `elem_ptr_imm` instruction,
/// because it must use one of them to find out the array type.
/// Uses the `pl_node` field. Payload is `Block`.
validate_array_init,
/// Same as `validate_array_init` but additionally communicates that the
/// resulting array initialization value is within a comptime scope.
validate_array_init_comptime,
/// Check that operand type supports the dereference operand (.*).
/// Uses the `un_node` field.
validate_deref,
/// A struct literal with a specified type, with no fields.
/// Uses the `un_node` field.
struct_init_empty,
/// Given a struct or union, and a field name as a string index,
/// returns the field type. Uses the `pl_node` field. Payload is `FieldType`.
field_type,
/// Given a struct or union, and a field name as a Ref,
/// returns the field type. Uses the `pl_node` field. Payload is `FieldTypeRef`.
field_type_ref,
/// Finalizes a typed struct or union initialization, performs validation, and returns the
/// struct or union value.
/// Uses the `pl_node` field. Payload is `StructInit`.
struct_init,
/// Struct initialization syntax, make the result a pointer.
/// Uses the `pl_node` field. Payload is `StructInit`.
struct_init_ref,
/// Struct initialization without a type.
/// Uses the `pl_node` field. Payload is `StructInitAnon`.
struct_init_anon,
/// Anonymous struct initialization syntax, make the result a pointer.
/// Uses the `pl_node` field. Payload is `StructInitAnon`.
struct_init_anon_ref,
/// Array initialization syntax.
/// Uses the `pl_node` field. Payload is `MultiOp`.
array_init,
/// Anonymous array initialization syntax.
/// Uses the `pl_node` field. Payload is `MultiOp`.
array_init_anon,
/// Array initialization syntax, make the result a pointer.
/// Uses the `pl_node` field. Payload is `MultiOp`.
array_init_ref,
/// Anonymous array initialization syntax, make the result a pointer.
/// Uses the `pl_node` field. Payload is `MultiOp`.
array_init_anon_ref,
/// Implements the `@unionInit` builtin.
/// Uses the `pl_node` field. Payload is `UnionInit`.
union_init,
/// Implements the `@typeInfo` builtin. Uses `un_node`.
type_info,
/// Implements the `@sizeOf` builtin. Uses `un_node`.
size_of,
/// Implements the `@bitSizeOf` builtin. Uses `un_node`.
bit_size_of,
/// Implement builtin `@ptrToInt`. Uses `un_node`.
/// Convert a pointer to a `usize` integer.
ptr_to_int,
/// Emit an error message and fail compilation.
/// Uses the `un_node` field.
compile_error,
/// Changes the maximum number of backwards branches that compile-time
/// code execution can use before giving up and making a compile error.
/// Uses the `un_node` union field.
set_eval_branch_quota,
/// Converts an enum value into an integer. Resulting type will be the tag type
/// of the enum. Uses `un_node`.
enum_to_int,
/// Implement builtin `@alignOf`. Uses `un_node`.
align_of,
/// Implement builtin `@boolToInt`. Uses `un_node`.
bool_to_int,
/// Implement builtin `@embedFile`. Uses `un_node`.
embed_file,
/// Implement builtin `@errorName`. Uses `un_node`.
error_name,
/// Implement builtin `@panic`. Uses `un_node`.
panic,
/// Same as `panic` but forces comptime.
panic_comptime,
/// Implement builtin `@setCold`. Uses `un_node`.
set_cold,
/// Implement builtin `@setRuntimeSafety`. Uses `un_node`.
set_runtime_safety,
/// Implement builtin `@sqrt`. Uses `un_node`.
sqrt,
/// Implement builtin `@sin`. Uses `un_node`.
sin,
/// Implement builtin `@cos`. Uses `un_node`.
cos,
/// Implement builtin `@tan`. Uses `un_node`.
tan,
/// Implement builtin `@exp`. Uses `un_node`.
exp,
/// Implement builtin `@exp2`. Uses `un_node`.
exp2,
/// Implement builtin `@log`. Uses `un_node`.
log,
/// Implement builtin `@log2`. Uses `un_node`.
log2,
/// Implement builtin `@log10`. Uses `un_node`.
log10,
/// Implement builtin `@fabs`. Uses `un_node`.
fabs,
/// Implement builtin `@floor`. Uses `un_node`.
floor,
/// Implement builtin `@ceil`. Uses `un_node`.
ceil,
/// Implement builtin `@trunc`. Uses `un_node`.
trunc,
/// Implement builtin `@round`. Uses `un_node`.
round,
/// Implement builtin `@tagName`. Uses `un_node`.
tag_name,
/// Implement builtin `@typeName`. Uses `un_node`.
type_name,
/// Implement builtin `@Frame`. Uses `un_node`.
frame_type,
/// Implement builtin `@frameSize`. Uses `un_node`.
frame_size,
/// Implements the `@floatToInt` builtin.
/// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
float_to_int,
/// Implements the `@intToFloat` builtin.
/// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
int_to_float,
/// Implements the `@intToPtr` builtin.
/// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
int_to_ptr,
/// Converts an integer into an enum value.
/// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
int_to_enum,
/// Convert a larger float type to any other float type, possibly causing
/// a loss of precision.
/// Uses the `pl_node` field. AST is the `@floatCast` syntax.
/// Payload is `Bin` with lhs as the dest type, rhs the operand.
float_cast,
/// Implements the `@intCast` builtin.
/// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
/// Convert an integer value to another integer type, asserting that the destination type
/// can hold the same mathematical value.
int_cast,
/// Implements the `@ptrCast` builtin.
/// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
ptr_cast,
/// Implements the `@truncate` builtin.
/// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
truncate,
/// Implements the `@alignCast` builtin.
/// Uses `pl_node` with payload `Bin`. `lhs` is dest alignment, `rhs` is operand.
align_cast,
/// Implements the `@hasDecl` builtin.
/// Uses the `pl_node` union field. Payload is `Bin`.
has_decl,
/// Implements the `@hasField` builtin.
/// Uses the `pl_node` union field. Payload is `Bin`.
has_field,
/// Implements the `@clz` builtin. Uses the `un_node` union field.
clz,
/// Implements the `@ctz` builtin. Uses the `un_node` union field.
ctz,
/// Implements the `@popCount` builtin. Uses the `un_node` union field.
pop_count,
/// Implements the `@byteSwap` builtin. Uses the `un_node` union field.
byte_swap,
/// Implements the `@bitReverse` builtin. Uses the `un_node` union field.
bit_reverse,
/// Implements the `@bitOffsetOf` builtin.
/// Uses the `pl_node` union field with payload `Bin`.
bit_offset_of,
/// Implements the `@offsetOf` builtin.
/// Uses the `pl_node` union field with payload `Bin`.
offset_of,
/// Implements the `@splat` builtin.
/// Uses the `pl_node` union field with payload `Bin`.
splat,
/// Implements the `@reduce` builtin.
/// Uses the `pl_node` union field with payload `Bin`.
reduce,
/// Implements the `@shuffle` builtin.
/// Uses the `pl_node` union field with payload `Shuffle`.
shuffle,
/// Implements the `@atomicLoad` builtin.
/// Uses the `pl_node` union field with payload `AtomicLoad`.
atomic_load,
/// Implements the `@atomicRmw` builtin.
/// Uses the `pl_node` union field with payload `AtomicRmw`.
atomic_rmw,
/// Implements the `@atomicStore` builtin.
/// Uses the `pl_node` union field with payload `AtomicStore`.
atomic_store,
/// Implements the `@mulAdd` builtin.
/// Uses the `pl_node` union field with payload `MulAdd`.
/// The addend communicates the type of the builtin.
/// The mulends need to be coerced to the same type.
mul_add,
/// Implements the `@fieldParentPtr` builtin.
/// Uses the `pl_node` union field with payload `FieldParentPtr`.
field_parent_ptr,
/// Implements the `@memcpy` builtin.
/// Uses the `pl_node` union field with payload `Memcpy`.
memcpy,
/// Implements the `@memset` builtin.
/// Uses the `pl_node` union field with payload `Memset`.
memset,
/// Implements the `@min` builtin.
/// Uses the `pl_node` union field with payload `Bin`
min,
/// Implements the `@max` builtin.
/// Uses the `pl_node` union field with payload `Bin`
max,
/// Implements the `@cImport` builtin.
/// Uses the `pl_node` union field with payload `Block`.
c_import,
/// Allocates stack local memory.
/// Uses the `un_node` union field. The operand is the type of the allocated object.
/// The node source location points to a var decl node.
alloc,
/// Same as `alloc` except mutable.
alloc_mut,
/// Allocates comptime-mutable memory.
/// Uses the `un_node` union field. The operand is the type of the allocated object.
/// The node source location points to a var decl node.
alloc_comptime_mut,
/// Same as `alloc` except the type is inferred.
/// Uses the `node` union field.
alloc_inferred,
/// Same as `alloc_inferred` except mutable.
alloc_inferred_mut,
/// Allocates comptime const memory.
/// Uses the `node` union field. The type of the allocated object is inferred.
/// The node source location points to a var decl node.
alloc_inferred_comptime,
/// Same as `alloc_comptime_mut` except the type is inferred.
alloc_inferred_comptime_mut,
/// Each `store_to_inferred_ptr` puts the type of the stored value into a set,
/// and then `resolve_inferred_alloc` triggers peer type resolution on the set.
/// The operand is a `alloc_inferred` or `alloc_inferred_mut` instruction, which
/// is the allocation that needs to have its type inferred.
/// Uses the `un_node` field. The AST node is the var decl.
resolve_inferred_alloc,
/// Turns a pointer coming from an `alloc`, `alloc_inferred`, `alloc_inferred_comptime` or
/// `Extended.alloc` into a constant version of the same pointer.
/// Uses the `un_node` union field.
make_ptr_const,
/// Implements `resume` syntax. Uses `un_node` field.
@"resume",
@"await",
/// When a type or function refers to a comptime value from an outer
/// scope, that forms a closure over comptime value. The outer scope
/// will record a capture of that value, which encodes its current state
/// and marks it to persist. Uses `un_tok` field. Operand is the
/// instruction value to capture.
closure_capture,
/// The inner scope of a closure uses closure_get to retrieve the value
/// stored by the outer scope. Uses `inst_node` field. Operand is the
/// closure_capture instruction ref.
closure_get,
/// A defer statement.
/// Uses the `defer` union field.
@"defer",
/// An errdefer statement with a code.
/// Uses the `err_defer_code` union field.
defer_err_code,
/// Requests that Sema update the saved error return trace index for the enclosing
/// block, if the operand is .none or of an error/error-union type.
/// Uses the `save_err_ret_index` field.
save_err_ret_index,
/// Sets error return trace to zero if no operand is given,
/// otherwise sets the value to the given amount.
/// Uses the `restore_err_ret_index` union field.
restore_err_ret_index,
/// The ZIR instruction tag is one of the `Extended` ones.
/// Uses the `extended` union field.
extended,
/// Returns whether the instruction is one of the control flow "noreturn" types.
/// Function calls do not count.
pub fn isNoReturn(tag: Tag) bool {
return switch (tag) {
.param,
.param_comptime,
.param_anytype,
.param_anytype_comptime,
.add,
.addwrap,
.add_sat,
.alloc,
.alloc_mut,
.alloc_comptime_mut,
.alloc_inferred,
.alloc_inferred_mut,
.alloc_inferred_comptime,
.alloc_inferred_comptime_mut,
.make_ptr_const,
.array_cat,
.array_mul,
.array_type,
.array_type_sentinel,
.vector_type,
.elem_type_index,
.indexable_ptr_len,
.anyframe_type,
.as,
.as_node,
.as_shift_operand,
.bit_and,
.bitcast,
.bit_or,
.block,
.block_inline,
.suspend_block,
.loop,
.bool_br_and,
.bool_br_or,
.bool_not,
.call,
.cmp_lt,
.cmp_lte,
.cmp_eq,
.cmp_gte,
.cmp_gt,
.cmp_neq,
.coerce_result_ptr,
.error_set_decl,
.error_set_decl_anon,
.error_set_decl_func,
.dbg_stmt,
.dbg_var_ptr,
.dbg_var_val,
.dbg_block_begin,
.dbg_block_end,
.decl_ref,
.decl_val,
.load,
.div,
.elem_ptr,
.elem_val,
.elem_ptr_node,
.elem_ptr_imm,
.elem_val_node,
.ensure_result_used,
.ensure_result_non_error,
.ensure_err_union_payload_void,
.@"export",
.export_value,
.field_ptr,
.field_ptr_init,
.field_val,
.field_call_bind,
.field_ptr_named,
.field_val_named,
.func,
.func_inferred,
.func_fancy,
.has_decl,
.int,
.int_big,
.float,
.float128,
.int_type,
.is_non_null,
.is_non_null_ptr,
.is_non_err,
.is_non_err_ptr,
.mod_rem,
.mul,
.mulwrap,
.mul_sat,
.ref,
.shl,
.shl_sat,
.shr,
.store,
.store_node,
.store_to_block_ptr,
.store_to_inferred_ptr,
.str,
.sub,
.subwrap,
.sub_sat,
.negate,
.negate_wrap,
.typeof,
.typeof_builtin,
.xor,
.optional_type,
.optional_payload_safe,
.optional_payload_unsafe,
.optional_payload_safe_ptr,
.optional_payload_unsafe_ptr,
.err_union_payload_unsafe,
.err_union_payload_unsafe_ptr,
.err_union_code,
.err_union_code_ptr,
.ptr_type,
.overflow_arithmetic_ptr,
.enum_literal,
.merge_error_sets,
.error_union_type,
.bit_not,
.error_value,
.slice_start,
.slice_end,
.slice_sentinel,
.import,
.typeof_log2_int_type,
.log2_int_type,
.resolve_inferred_alloc,
.set_eval_branch_quota,
.switch_capture,
.switch_capture_ref,
.switch_capture_multi,
.switch_capture_multi_ref,
.switch_capture_tag,
.switch_block,
.switch_cond,
.switch_cond_ref,
.array_base_ptr,
.field_base_ptr,
.validate_array_init_ty,
.validate_struct_init_ty,
.validate_struct_init,
.validate_struct_init_comptime,
.validate_array_init,
.validate_array_init_comptime,
.validate_deref,
.struct_init_empty,
.struct_init,
.struct_init_ref,
.struct_init_anon,
.struct_init_anon_ref,
.array_init,
.array_init_anon,
.array_init_ref,
.array_init_anon_ref,
.union_init,
.field_type,
.field_type_ref,
.int_to_enum,
.enum_to_int,
.type_info,
.size_of,
.bit_size_of,
.ptr_to_int,
.align_of,
.bool_to_int,
.embed_file,
.error_name,
.set_cold,
.set_runtime_safety,
.sqrt,
.sin,
.cos,
.tan,
.exp,
.exp2,
.log,
.log2,
.log10,
.fabs,
.floor,
.ceil,
.trunc,
.round,
.tag_name,
.type_name,
.frame_type,
.frame_size,
.float_to_int,
.int_to_float,
.int_to_ptr,
.float_cast,
.int_cast,
.ptr_cast,
.truncate,
.align_cast,
.has_field,
.clz,
.ctz,
.pop_count,
.byte_swap,
.bit_reverse,
.div_exact,
.div_floor,
.div_trunc,
.mod,
.rem,
.shl_exact,
.shr_exact,
.bit_offset_of,
.offset_of,
.splat,
.reduce,
.shuffle,
.atomic_load,
.atomic_rmw,
.atomic_store,
.mul_add,
.builtin_call,
.field_parent_ptr,
.max,
.memcpy,
.memset,
.min,
.c_import,
.@"resume",
.@"await",
.ret_err_value_code,
.extended,
.closure_get,
.closure_capture,
.ret_ptr,
.ret_type,
.@"try",
.try_ptr,
//.try_inline,
//.try_ptr_inline,
.@"defer",
.defer_err_code,
.save_err_ret_index,
.restore_err_ret_index,
=> false,
.@"break",
.break_inline,
.condbr,
.condbr_inline,
.compile_error,
.ret_node,
.ret_load,
.ret_tok,
.ret_err_value,
.@"unreachable",
.repeat,
.repeat_inline,
.panic,
.panic_comptime,
.check_comptime_control_flow,
=> true,
};
}
pub fn isParam(tag: Tag) bool {
return switch (tag) {
.param,
.param_comptime,
.param_anytype,
.param_anytype_comptime,
=> true,
else => false,
};
}
/// AstGen uses this to find out if `Ref.void_value` should be used in place
/// of the result of a given instruction. This allows Sema to forego adding
/// the instruction to the map after analysis.
pub fn isAlwaysVoid(tag: Tag, data: Data) bool {
return switch (tag) {
.dbg_stmt,
.dbg_var_ptr,
.dbg_var_val,
.dbg_block_begin,
.dbg_block_end,
.ensure_result_used,
.ensure_result_non_error,
.ensure_err_union_payload_void,
.set_eval_branch_quota,
.atomic_store,
.store,
.store_node,
.store_to_block_ptr,
.store_to_inferred_ptr,
.resolve_inferred_alloc,
.validate_array_init_ty,
.validate_struct_init_ty,
.validate_struct_init,
.validate_struct_init_comptime,
.validate_array_init,
.validate_array_init_comptime,
.validate_deref,
.@"export",
.export_value,
.set_cold,
.set_runtime_safety,
.memcpy,
.memset,
.check_comptime_control_flow,
.@"defer",
.defer_err_code,
.restore_err_ret_index,
.save_err_ret_index,
=> true,
.param,
.param_comptime,
.param_anytype,
.param_anytype_comptime,
.add,
.addwrap,
.add_sat,
.alloc,
.alloc_mut,
.alloc_comptime_mut,
.alloc_inferred,
.alloc_inferred_mut,
.alloc_inferred_comptime,
.alloc_inferred_comptime_mut,
.make_ptr_const,
.array_cat,
.array_mul,
.array_type,
.array_type_sentinel,
.vector_type,
.elem_type_index,
.indexable_ptr_len,
.anyframe_type,
.as,
.as_node,
.as_shift_operand,
.bit_and,
.bitcast,
.bit_or,
.block,
.block_inline,
.suspend_block,
.loop,
.bool_br_and,
.bool_br_or,
.bool_not,
.call,
.cmp_lt,
.cmp_lte,
.cmp_eq,
.cmp_gte,
.cmp_gt,
.cmp_neq,
.coerce_result_ptr,
.error_set_decl,
.error_set_decl_anon,
.error_set_decl_func,
.decl_ref,
.decl_val,
.load,
.div,
.elem_ptr,
.elem_val,
.elem_ptr_node,
.elem_ptr_imm,
.elem_val_node,
.field_ptr,
.field_ptr_init,
.field_val,
.field_call_bind,
.field_ptr_named,
.field_val_named,
.func,
.func_inferred,
.func_fancy,
.has_decl,
.int,
.int_big,
.float,
.float128,
.int_type,
.is_non_null,
.is_non_null_ptr,
.is_non_err,
.is_non_err_ptr,
.mod_rem,
.mul,
.mulwrap,
.mul_sat,
.ref,
.shl,
.shl_sat,
.shr,
.str,
.sub,
.subwrap,
.sub_sat,
.negate,
.negate_wrap,
.typeof,
.typeof_builtin,
.xor,
.optional_type,
.optional_payload_safe,
.optional_payload_unsafe,
.optional_payload_safe_ptr,
.optional_payload_unsafe_ptr,
.err_union_payload_unsafe,
.err_union_payload_unsafe_ptr,
.err_union_code,
.err_union_code_ptr,
.ptr_type,
.overflow_arithmetic_ptr,
.enum_literal,
.merge_error_sets,
.error_union_type,
.bit_not,
.error_value,
.slice_start,
.slice_end,
.slice_sentinel,
.import,
.typeof_log2_int_type,
.log2_int_type,
.switch_capture,
.switch_capture_ref,
.switch_capture_multi,
.switch_capture_multi_ref,
.switch_capture_tag,
.switch_block,
.switch_cond,
.switch_cond_ref,
.array_base_ptr,
.field_base_ptr,
.struct_init_empty,
.struct_init,
.struct_init_ref,
.struct_init_anon,
.struct_init_anon_ref,
.array_init,
.array_init_anon,
.array_init_ref,
.array_init_anon_ref,
.union_init,
.field_type,
.field_type_ref,
.int_to_enum,
.enum_to_int,
.type_info,
.size_of,
.bit_size_of,
.ptr_to_int,
.align_of,
.bool_to_int,
.embed_file,
.error_name,
.sqrt,
.sin,
.cos,
.tan,
.exp,
.exp2,
.log,
.log2,
.log10,
.fabs,
.floor,
.ceil,
.trunc,
.round,
.tag_name,
.type_name,
.frame_type,
.frame_size,
.float_to_int,
.int_to_float,
.int_to_ptr,
.float_cast,
.int_cast,
.ptr_cast,
.truncate,
.align_cast,
.has_field,
.clz,
.ctz,
.pop_count,
.byte_swap,
.bit_reverse,
.div_exact,
.div_floor,
.div_trunc,
.mod,
.rem,
.shl_exact,
.shr_exact,
.bit_offset_of,
.offset_of,
.splat,
.reduce,
.shuffle,
.atomic_load,
.atomic_rmw,
.mul_add,
.builtin_call,
.field_parent_ptr,
.max,
.min,
.c_import,
.@"resume",
.@"await",
.ret_err_value_code,
.closure_get,
.closure_capture,
.@"break",
.break_inline,
.condbr,
.condbr_inline,
.compile_error,
.ret_node,
.ret_load,
.ret_tok,
.ret_err_value,
.ret_ptr,
.ret_type,
.@"unreachable",
.repeat,
.repeat_inline,
.panic,
.panic_comptime,
.@"try",
.try_ptr,
//.try_inline,
//.try_ptr_inline,
=> false,
.extended => switch (data.extended.opcode) {
.breakpoint, .fence => true,
else => false,
},
};
}
/// Used by debug safety-checking code.
pub const data_tags = list: {
@setEvalBranchQuota(2000);
break :list std.enums.directEnumArray(Tag, Data.FieldEnum, 0, .{
.add = .pl_node,
.addwrap = .pl_node,
.add_sat = .pl_node,
.sub = .pl_node,
.subwrap = .pl_node,
.sub_sat = .pl_node,
.mul = .pl_node,
.mulwrap = .pl_node,
.mul_sat = .pl_node,
.param = .pl_tok,
.param_comptime = .pl_tok,
.param_anytype = .str_tok,
.param_anytype_comptime = .str_tok,
.array_cat = .pl_node,
.array_mul = .pl_node,
.array_type = .pl_node,
.array_type_sentinel = .pl_node,
.vector_type = .pl_node,
.elem_type_index = .bin,
.indexable_ptr_len = .un_node,
.anyframe_type = .un_node,
.as = .bin,
.as_node = .pl_node,
.as_shift_operand = .pl_node,
.bit_and = .pl_node,
.bitcast = .pl_node,
.bit_not = .un_node,
.bit_or = .pl_node,
.block = .pl_node,
.block_inline = .pl_node,
.suspend_block = .pl_node,
.bool_not = .un_node,
.bool_br_and = .bool_br,
.bool_br_or = .bool_br,
.@"break" = .@"break",
.break_inline = .@"break",
.check_comptime_control_flow = .un_node,
.call = .pl_node,
.cmp_lt = .pl_node,
.cmp_lte = .pl_node,
.cmp_eq = .pl_node,
.cmp_gte = .pl_node,
.cmp_gt = .pl_node,
.cmp_neq = .pl_node,
.coerce_result_ptr = .pl_node,
.condbr = .pl_node,
.condbr_inline = .pl_node,
.@"try" = .pl_node,
.try_ptr = .pl_node,
//.try_inline = .pl_node,
//.try_ptr_inline = .pl_node,
.error_set_decl = .pl_node,
.error_set_decl_anon = .pl_node,
.error_set_decl_func = .pl_node,
.dbg_stmt = .dbg_stmt,
.dbg_var_ptr = .str_op,
.dbg_var_val = .str_op,
.dbg_block_begin = .tok,
.dbg_block_end = .tok,
.decl_ref = .str_tok,
.decl_val = .str_tok,
.load = .un_node,
.div = .pl_node,
.elem_ptr = .pl_node,
.elem_ptr_node = .pl_node,
.elem_ptr_imm = .pl_node,
.elem_val = .pl_node,
.elem_val_node = .pl_node,
.ensure_result_used = .un_node,
.ensure_result_non_error = .un_node,
.ensure_err_union_payload_void = .un_node,
.error_union_type = .pl_node,
.error_value = .str_tok,
.@"export" = .pl_node,
.export_value = .pl_node,
.field_ptr = .pl_node,
.field_ptr_init = .pl_node,
.field_val = .pl_node,
.field_ptr_named = .pl_node,
.field_val_named = .pl_node,
.field_call_bind = .pl_node,
.func = .pl_node,
.func_inferred = .pl_node,
.func_fancy = .pl_node,
.import = .str_tok,
.int = .int,
.int_big = .str,
.float = .float,
.float128 = .pl_node,
.int_type = .int_type,
.is_non_null = .un_node,
.is_non_null_ptr = .un_node,
.is_non_err = .un_node,
.is_non_err_ptr = .un_node,
.loop = .pl_node,
.repeat = .node,
.repeat_inline = .node,
.merge_error_sets = .pl_node,
.mod_rem = .pl_node,
.ref = .un_tok,
.ret_node = .un_node,
.ret_load = .un_node,
.ret_tok = .un_tok,
.ret_err_value = .str_tok,
.ret_err_value_code = .str_tok,
.ret_ptr = .node,
.ret_type = .node,
.overflow_arithmetic_ptr = .un_node,
.ptr_type = .ptr_type,
.slice_start = .pl_node,
.slice_end = .pl_node,
.slice_sentinel = .pl_node,
.store = .bin,
.store_node = .pl_node,
.store_to_block_ptr = .bin,
.store_to_inferred_ptr = .bin,
.str = .str,
.negate = .un_node,
.negate_wrap = .un_node,
.typeof = .un_node,
.typeof_log2_int_type = .un_node,
.log2_int_type = .un_node,
.@"unreachable" = .@"unreachable",
.xor = .pl_node,
.optional_type = .un_node,
.optional_payload_safe = .un_node,
.optional_payload_unsafe = .un_node,
.optional_payload_safe_ptr = .un_node,
.optional_payload_unsafe_ptr = .un_node,
.err_union_payload_unsafe = .un_node,
.err_union_payload_unsafe_ptr = .un_node,
.err_union_code = .un_node,
.err_union_code_ptr = .un_node,
.enum_literal = .str_tok,
.switch_block = .pl_node,
.switch_cond = .un_node,
.switch_cond_ref = .un_node,
.switch_capture = .switch_capture,
.switch_capture_ref = .switch_capture,
.switch_capture_multi = .switch_capture,
.switch_capture_multi_ref = .switch_capture,
.switch_capture_tag = .un_tok,
.array_base_ptr = .un_node,
.field_base_ptr = .un_node,
.validate_array_init_ty = .pl_node,
.validate_struct_init_ty = .un_node,
.validate_struct_init = .pl_node,
.validate_struct_init_comptime = .pl_node,
.validate_array_init = .pl_node,
.validate_array_init_comptime = .pl_node,
.validate_deref = .un_node,
.struct_init_empty = .un_node,
.field_type = .pl_node,
.field_type_ref = .pl_node,
.struct_init = .pl_node,
.struct_init_ref = .pl_node,
.struct_init_anon = .pl_node,
.struct_init_anon_ref = .pl_node,
.array_init = .pl_node,
.array_init_anon = .pl_node,
.array_init_ref = .pl_node,
.array_init_anon_ref = .pl_node,
.union_init = .pl_node,
.type_info = .un_node,
.size_of = .un_node,
.bit_size_of = .un_node,
.ptr_to_int = .un_node,
.compile_error = .un_node,
.set_eval_branch_quota = .un_node,
.enum_to_int = .un_node,
.align_of = .un_node,
.bool_to_int = .un_node,
.embed_file = .un_node,
.error_name = .un_node,
.panic = .un_node,
.panic_comptime = .un_node,
.set_cold = .un_node,
.set_runtime_safety = .un_node,
.sqrt = .un_node,
.sin = .un_node,
.cos = .un_node,
.tan = .un_node,
.exp = .un_node,
.exp2 = .un_node,
.log = .un_node,
.log2 = .un_node,
.log10 = .un_node,
.fabs = .un_node,
.floor = .un_node,
.ceil = .un_node,
.trunc = .un_node,
.round = .un_node,
.tag_name = .un_node,
.type_name = .un_node,
.frame_type = .un_node,
.frame_size = .un_node,
.float_to_int = .pl_node,
.int_to_float = .pl_node,
.int_to_ptr = .pl_node,
.int_to_enum = .pl_node,
.float_cast = .pl_node,
.int_cast = .pl_node,
.ptr_cast = .pl_node,
.truncate = .pl_node,
.align_cast = .pl_node,
.typeof_builtin = .pl_node,
.has_decl = .pl_node,
.has_field = .pl_node,
.clz = .un_node,
.ctz = .un_node,
.pop_count = .un_node,
.byte_swap = .un_node,
.bit_reverse = .un_node,
.div_exact = .pl_node,
.div_floor = .pl_node,
.div_trunc = .pl_node,
.mod = .pl_node,
.rem = .pl_node,
.shl = .pl_node,
.shl_exact = .pl_node,
.shl_sat = .pl_node,
.shr = .pl_node,
.shr_exact = .pl_node,
.bit_offset_of = .pl_node,
.offset_of = .pl_node,
.splat = .pl_node,
.reduce = .pl_node,
.shuffle = .pl_node,
.atomic_load = .pl_node,
.atomic_rmw = .pl_node,
.atomic_store = .pl_node,
.mul_add = .pl_node,
.builtin_call = .pl_node,
.field_parent_ptr = .pl_node,
.max = .pl_node,
.memcpy = .pl_node,
.memset = .pl_node,
.min = .pl_node,
.c_import = .pl_node,
.alloc = .un_node,
.alloc_mut = .un_node,
.alloc_comptime_mut = .un_node,
.alloc_inferred = .node,
.alloc_inferred_mut = .node,
.alloc_inferred_comptime = .node,
.alloc_inferred_comptime_mut = .node,
.resolve_inferred_alloc = .un_node,
.make_ptr_const = .un_node,
.@"resume" = .un_node,
.@"await" = .un_node,
.closure_capture = .un_tok,
.closure_get = .inst_node,
.@"defer" = .@"defer",
.defer_err_code = .defer_err_code,
.save_err_ret_index = .save_err_ret_index,
.restore_err_ret_index = .restore_err_ret_index,
.extended = .extended,
});
};
// Uncomment to view how many tag slots are available.
//comptime {
// @compileLog("ZIR tags left: ", 256 - @typeInfo(Tag).Enum.fields.len);
//}
};
/// Rarer instructions are here; ones that do not fit in the 8-bit `Tag` enum.
/// `noreturn` instructions may not go here; they must be part of the main `Tag` enum.
pub const Extended = enum(u16) {
/// Declares a global variable.
/// `operand` is payload index to `ExtendedVar`.
/// `small` is `ExtendedVar.Small`.
variable,
/// A struct type definition. Contains references to ZIR instructions for
/// the field types, defaults, and alignments.
/// `operand` is payload index to `StructDecl`.
/// `small` is `StructDecl.Small`.
struct_decl,
/// An enum type definition. Contains references to ZIR instructions for
/// the field value expressions and optional type tag expression.
/// `operand` is payload index to `EnumDecl`.
/// `small` is `EnumDecl.Small`.
enum_decl,
/// A union type definition. Contains references to ZIR instructions for
/// the field types and optional type tag expression.
/// `operand` is payload index to `UnionDecl`.
/// `small` is `UnionDecl.Small`.
union_decl,
/// An opaque type definition. Contains references to decls and captures.
/// `operand` is payload index to `OpaqueDecl`.
/// `small` is `OpaqueDecl.Small`.
opaque_decl,
/// Implements the `@This` builtin.
/// `operand` is `src_node: i32`.
this,
/// Implements the `@returnAddress` builtin.
/// `operand` is `src_node: i32`.
ret_addr,
/// Implements the `@src` builtin.
/// `operand` is payload index to `LineColumn`.
builtin_src,
/// Implements the `@errorReturnTrace` builtin.
/// `operand` is `src_node: i32`.
error_return_trace,
/// Implements the `@frame` builtin.
/// `operand` is `src_node: i32`.
frame,
/// Implements the `@frameAddress` builtin.
/// `operand` is `src_node: i32`.
frame_address,
/// Same as `alloc` from `Tag` but may contain an alignment instruction.
/// `operand` is payload index to `AllocExtended`.
/// `small`:
/// * 0b000X - has type
/// * 0b00X0 - has alignment
/// * 0b0X00 - 1=const, 0=var
/// * 0bX000 - is comptime
alloc,
/// The `@extern` builtin.
/// `operand` is payload index to `BinNode`.
builtin_extern,
/// Inline assembly.
/// `small`:
/// * 0b00000000_000XXXXX - `outputs_len`.
/// * 0b000000XX_XXX00000 - `inputs_len`.
/// * 0b0XXXXX00_00000000 - `clobbers_len`.
/// * 0bX0000000_00000000 - is volatile
/// `operand` is payload index to `Asm`.
@"asm",
/// Same as `asm` except the assembly template is not a string literal but a comptime
/// expression.
/// The `asm_source` field of the Asm is not a null-terminated string
/// but instead a Ref.
asm_expr,
/// Log compile time variables and emit an error message.
/// `operand` is payload index to `NodeMultiOp`.
/// `small` is `operands_len`.
/// The AST node is the compile log builtin call.
compile_log,
/// The builtin `@TypeOf` which returns the type after Peer Type Resolution
/// of one or more params.
/// `operand` is payload index to `NodeMultiOp`.
/// `small` is `operands_len`.
/// The AST node is the builtin call.
typeof_peer,
/// Implements the `@addWithOverflow` builtin.
/// `operand` is payload index to `OverflowArithmetic`.
/// `small` is unused.
add_with_overflow,
/// Implements the `@subWithOverflow` builtin.
/// `operand` is payload index to `OverflowArithmetic`.
/// `small` is unused.
sub_with_overflow,
/// Implements the `@mulWithOverflow` builtin.
/// `operand` is payload index to `OverflowArithmetic`.
/// `small` is unused.
mul_with_overflow,
/// Implements the `@shlWithOverflow` builtin.
/// `operand` is payload index to `OverflowArithmetic`.
/// `small` is unused.
shl_with_overflow,
/// `operand` is payload index to `UnNode`.
c_undef,
/// `operand` is payload index to `UnNode`.
c_include,
/// `operand` is payload index to `BinNode`.
c_define,
/// `operand` is payload index to `UnNode`.
wasm_memory_size,
/// `operand` is payload index to `BinNode`.
wasm_memory_grow,
/// The `@prefetch` builtin.
/// `operand` is payload index to `BinNode`.
prefetch,
/// Given a pointer to a struct or object that contains virtual fields, returns the
/// named field. If there is no named field, searches in the type for a decl that
/// matches the field name. The decl is resolved and we ensure that it's a function
/// which can accept the object as the first parameter, with one pointer fixup. If
/// all of that works, this instruction produces a special "bound function" value
/// which contains both the function and the saved first parameter value.
/// Bound functions may only be used as the function parameter to a `call` or
/// `builtin_call` instruction. Any other use is invalid zir and may crash the compiler.
/// Uses `pl_node` field. The AST node is the `@field` builtin. Payload is FieldNamedNode.
field_call_bind_named,
/// Implements the `@fence` builtin.
/// `operand` is payload index to `UnNode`.
fence,
/// Implement builtin `@setFloatMode`.
/// `operand` is payload index to `UnNode`.
set_float_mode,
/// Implement builtin `@setAlignStack`.
/// `operand` is payload index to `UnNode`.
set_align_stack,
/// Implements the `@errSetCast` builtin.
/// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand.
err_set_cast,
/// `operand` is payload index to `UnNode`.
await_nosuspend,
/// `operand` is `src_node: i32`.
breakpoint,
/// Implements the `@select` builtin.
/// operand` is payload index to `Select`.
select,
/// Implement builtin `@errToInt`.
/// `operand` is payload index to `UnNode`.
error_to_int,
/// Implement builtin `@intToError`.
/// `operand` is payload index to `UnNode`.
int_to_error,
/// Implement builtin `@Type`.
/// `operand` is payload index to `UnNode`.
/// `small` contains `NameStrategy
reify,
/// Implements the `@asyncCall` builtin.
/// `operand` is payload index to `AsyncCall`.
builtin_async_call,
/// Implements the `@cmpxchgStrong` and `@cmpxchgWeak` builtins.
/// `small` 0=>weak 1=>strong
/// `operand` is payload index to `Cmpxchg`.
cmpxchg,
/// Implement the builtin `@addrSpaceCast`
/// `Operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand.
addrspace_cast,
pub const InstData = struct {
opcode: Extended,
small: u16,
operand: u32,
};
};
/// The position of a ZIR instruction within the `Zir` instructions array.
pub const Index = u32;
/// A reference to a TypedValue or ZIR instruction.
///
/// If the Ref has a tag in this enum, it refers to a TypedValue which may be
/// retrieved with Ref.toTypedValue().
///
/// If the value of a Ref does not have a tag, it refers to a ZIR instruction.
///
/// The first values after the the last tag refer to ZIR instructions which may
/// be derived by subtracting `typed_value_map.len`.
///
/// When adding a tag to this enum, consider adding a corresponding entry to
/// `primitives` in astgen.
///
/// The tag type is specified so that it is safe to bitcast between `[]u32`
/// and `[]Ref`.
pub const Ref = enum(u32) {
/// This Ref does not correspond to any ZIR instruction or constant
/// value and may instead be used as a sentinel to indicate null.
none,
u1_type,
u8_type,
i8_type,
u16_type,
i16_type,
u29_type,
u32_type,
i32_type,
u64_type,
i64_type,
u128_type,
i128_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,
f80_type,
f128_type,
anyopaque_type,
bool_type,
void_type,
type_type,
anyerror_type,
comptime_int_type,
comptime_float_type,
noreturn_type,
anyframe_type,
null_type,
undefined_type,
enum_literal_type,
atomic_order_type,
atomic_rmw_op_type,
calling_convention_type,
address_space_type,
float_mode_type,
reduce_op_type,
call_options_type,
prefetch_options_type,
export_options_type,
extern_options_type,
type_info_type,
manyptr_u8_type,
manyptr_const_u8_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,
anyerror_void_error_union_type,
generic_poison_type,
/// `undefined` (untyped)
undef,
/// `0` (comptime_int)
zero,
/// `1` (comptime_int)
one,
/// `{}`
void_value,
/// `unreachable` (noreturn type)
unreachable_value,
/// `null` (untyped)
null_value,
/// `true`
bool_true,
/// `false`
bool_false,
/// `.{}` (untyped)
empty_struct,
/// `0` (usize)
zero_usize,
/// `1` (usize)
one_usize,
/// `std.builtin.CallingConvention.C`
calling_convention_c,
/// `std.builtin.CallingConvention.Inline`
calling_convention_inline,
/// Used for generic parameters where the type and value
/// is not known until generic function instantiation.
generic_poison,
_,
pub const typed_value_map = std.enums.directEnumArray(Ref, TypedValue, 0, .{
.none = undefined,
.u1_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.u1_type),
},
.u8_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.u8_type),
},
.i8_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.i8_type),
},
.u16_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.u16_type),
},
.i16_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.i16_type),
},
.u29_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.u29_type),
},
.u32_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.u32_type),
},
.i32_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.i32_type),
},
.u64_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.u64_type),
},
.i64_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.i64_type),
},
.u128_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.u128_type),
},
.i128_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.i128_type),
},
.usize_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.usize_type),
},
.isize_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.isize_type),
},
.c_short_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.c_short_type),
},
.c_ushort_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.c_ushort_type),
},
.c_int_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.c_int_type),
},
.c_uint_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.c_uint_type),
},
.c_long_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.c_long_type),
},
.c_ulong_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.c_ulong_type),
},
.c_longlong_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.c_longlong_type),
},
.c_ulonglong_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.c_ulonglong_type),
},
.c_longdouble_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.c_longdouble_type),
},
.f16_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.f16_type),
},
.f32_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.f32_type),
},
.f64_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.f64_type),
},
.f80_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.f80_type),
},
.f128_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.f128_type),
},
.anyopaque_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.anyopaque_type),
},
.bool_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.bool_type),
},
.void_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.void_type),
},
.type_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.type_type),
},
.anyerror_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.anyerror_type),
},
.comptime_int_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.comptime_int_type),
},
.comptime_float_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.comptime_float_type),
},
.noreturn_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.noreturn_type),
},
.anyframe_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.anyframe_type),
},
.null_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.null_type),
},
.undefined_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.undefined_type),
},
.fn_noreturn_no_args_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.fn_noreturn_no_args_type),
},
.fn_void_no_args_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.fn_void_no_args_type),
},
.fn_naked_noreturn_no_args_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.fn_naked_noreturn_no_args_type),
},
.fn_ccc_void_no_args_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.fn_ccc_void_no_args_type),
},
.single_const_pointer_to_comptime_int_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.single_const_pointer_to_comptime_int_type),
},
.const_slice_u8_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.const_slice_u8_type),
},
.anyerror_void_error_union_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.anyerror_void_error_union_type),
},
.generic_poison_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.generic_poison_type),
},
.enum_literal_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.enum_literal_type),
},
.manyptr_u8_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.manyptr_u8_type),
},
.manyptr_const_u8_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.manyptr_const_u8_type),
},
.atomic_order_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.atomic_order_type),
},
.atomic_rmw_op_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.atomic_rmw_op_type),
},
.calling_convention_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.calling_convention_type),
},
.address_space_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.address_space_type),
},
.float_mode_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.float_mode_type),
},
.reduce_op_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.reduce_op_type),
},
.call_options_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.call_options_type),
},
.prefetch_options_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.prefetch_options_type),
},
.export_options_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.export_options_type),
},
.extern_options_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.extern_options_type),
},
.type_info_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.type_info_type),
},
.undef = .{
.ty = Type.initTag(.@"undefined"),
.val = Value.initTag(.undef),
},
.zero = .{
.ty = Type.initTag(.comptime_int),
.val = Value.initTag(.zero),
},
.zero_usize = .{
.ty = Type.initTag(.usize),
.val = Value.initTag(.zero),
},
.one = .{
.ty = Type.initTag(.comptime_int),
.val = Value.initTag(.one),
},
.one_usize = .{
.ty = Type.initTag(.usize),
.val = Value.initTag(.one),
},
.void_value = .{
.ty = Type.initTag(.void),
.val = Value.initTag(.void_value),
},
.unreachable_value = .{
.ty = Type.initTag(.noreturn),
.val = Value.initTag(.unreachable_value),
},
.null_value = .{
.ty = Type.initTag(.@"null"),
.val = Value.initTag(.null_value),
},
.bool_true = .{
.ty = Type.initTag(.bool),
.val = Value.initTag(.bool_true),
},
.bool_false = .{
.ty = Type.initTag(.bool),
.val = Value.initTag(.bool_false),
},
.empty_struct = .{
.ty = Type.initTag(.empty_struct_literal),
.val = Value.initTag(.empty_struct_value),
},
.calling_convention_c = .{
.ty = Type.initTag(.calling_convention),
.val = .{ .ptr_otherwise = &calling_convention_c_payload.base },
},
.calling_convention_inline = .{
.ty = Type.initTag(.calling_convention),
.val = .{ .ptr_otherwise = &calling_convention_inline_payload.base },
},
.generic_poison = .{
.ty = Type.initTag(.generic_poison),
.val = Value.initTag(.generic_poison),
},
});
};
/// We would like this to be const but `Value` wants a mutable pointer for
/// its payload field. Nothing should mutate this though.
var calling_convention_c_payload: Value.Payload.U32 = .{
.base = .{ .tag = .enum_field_index },
.data = @enumToInt(std.builtin.CallingConvention.C),
};
/// We would like this to be const but `Value` wants a mutable pointer for
/// its payload field. Nothing should mutate this though.
var calling_convention_inline_payload: Value.Payload.U32 = .{
.base = .{ .tag = .enum_field_index },
.data = @enumToInt(std.builtin.CallingConvention.Inline),
};
/// All instructions have an 8-byte payload, which is contained within
/// this union. `Tag` determines which union field is active, as well as
/// how to interpret the data within.
pub const Data = union {
/// Used for `Tag.extended`. The extended opcode determines the meaning
/// of the `small` and `operand` fields.
extended: Extended.InstData,
/// Used for unary operators, with an AST node source location.
un_node: struct {
/// Offset from Decl AST node index.
src_node: i32,
/// The meaning of this operand depends on the corresponding `Tag`.
operand: Ref,
pub fn src(self: @This()) LazySrcLoc {
return LazySrcLoc.nodeOffset(self.src_node);
}
},
/// Used for unary operators, with a token source location.
un_tok: struct {
/// Offset from Decl AST token index.
src_tok: Ast.TokenIndex,
/// The meaning of this operand depends on the corresponding `Tag`.
operand: Ref,
pub fn src(self: @This()) LazySrcLoc {
return .{ .token_offset = self.src_tok };
}
},
pl_node: struct {
/// Offset from Decl AST node index.
/// `Tag` determines which kind of AST node this points to.
src_node: i32,
/// index into extra.
/// `Tag` determines what lives there.
payload_index: u32,
pub fn src(self: @This()) LazySrcLoc {
return LazySrcLoc.nodeOffset(self.src_node);
}
},
pl_tok: struct {
/// Offset from Decl AST token index.
src_tok: Ast.TokenIndex,
/// index into extra.
/// `Tag` determines what lives there.
payload_index: u32,
pub fn src(self: @This()) LazySrcLoc {
return .{ .token_offset = self.src_tok };
}
},
bin: Bin,
/// For strings which may contain null bytes.
str: struct {
/// Offset into `string_bytes`.
start: u32,
/// Number of bytes in the string.
len: u32,
pub fn get(self: @This(), code: Zir) []const u8 {
return code.string_bytes[self.start..][0..self.len];
}
},
str_tok: struct {
/// Offset into `string_bytes`. Null-terminated.
start: u32,
/// Offset from Decl AST token index.
src_tok: u32,
pub fn get(self: @This(), code: Zir) [:0]const u8 {
return code.nullTerminatedString(self.start);
}
pub fn src(self: @This()) LazySrcLoc {
return .{ .token_offset = self.src_tok };
}
},
/// Offset from Decl AST token index.
tok: Ast.TokenIndex,
/// Offset from Decl AST node index.
node: i32,
int: u64,
float: f64,
ptr_type: struct {
flags: packed struct {
is_allowzero: bool,
is_mutable: bool,
is_volatile: bool,
has_sentinel: bool,
has_align: bool,
has_addrspace: bool,
has_bit_range: bool,
_: u1 = undefined,
},
size: std.builtin.Type.Pointer.Size,
/// Index into extra. See `PtrType`.
payload_index: u32,
},
int_type: struct {
/// Offset from Decl AST node index.
/// `Tag` determines which kind of AST node this points to.
src_node: i32,
signedness: std.builtin.Signedness,
bit_count: u16,
pub fn src(self: @This()) LazySrcLoc {
return LazySrcLoc.nodeOffset(self.src_node);
}
},
bool_br: struct {
lhs: Ref,
/// Points to a `Block`.
payload_index: u32,
},
@"unreachable": struct {
/// Offset from Decl AST node index.
/// `Tag` determines which kind of AST node this points to.
src_node: i32,
force_comptime: bool,
pub fn src(self: @This()) LazySrcLoc {
return LazySrcLoc.nodeOffset(self.src_node);
}
},
@"break": struct {
block_inst: Index,
operand: Ref,
},
switch_capture: struct {
switch_inst: Index,
prong_index: u32,
},
dbg_stmt: LineColumn,
/// Used for unary operators which reference an inst,
/// with an AST node source location.
inst_node: struct {
/// Offset from Decl AST node index.
src_node: i32,
/// The meaning of this operand depends on the corresponding `Tag`.
inst: Index,
pub fn src(self: @This()) LazySrcLoc {
return LazySrcLoc.nodeOffset(self.src_node);
}
},
str_op: struct {
/// Offset into `string_bytes`. Null-terminated.
str: u32,
operand: Ref,
pub fn getStr(self: @This(), zir: Zir) [:0]const u8 {
return zir.nullTerminatedString(self.str);
}
},
@"defer": struct {
index: u32,
len: u32,
},
defer_err_code: struct {
err_code: Ref,
payload_index: u32,
},
save_err_ret_index: struct {
operand: Ref, // If error type (or .none), save new trace index
},
restore_err_ret_index: struct {
block: Ref, // If restored, the index is from this block's entrypoint
operand: Ref, // If non-error (or .none), then restore the index
},
// Make sure we don't accidentally add a field to make this union
// bigger than expected. Note that in Debug builds, Zig is allowed
// to insert a secret field for safety checks.
comptime {
if (builtin.mode != .Debug and builtin.mode != .ReleaseSafe) {
assert(@sizeOf(Data) == 8);
}
}
/// TODO this has to be kept in sync with `Data` which we want to be an untagged
/// union. There is some kind of language awkwardness here and it has to do with
/// deserializing an untagged union (in this case `Data`) from a file, and trying
/// to preserve the hidden safety field.
pub const FieldEnum = enum {
extended,
un_node,
un_tok,
pl_node,
pl_tok,
bin,
str,
str_tok,
tok,
node,
int,
float,
ptr_type,
int_type,
bool_br,
@"unreachable",
@"break",
switch_capture,
dbg_stmt,
inst_node,
str_op,
@"defer",
defer_err_code,
save_err_ret_index,
restore_err_ret_index,
};
};
/// Trailing:
/// 0. Output for every outputs_len
/// 1. Input for every inputs_len
/// 2. clobber: u32 // index into string_bytes (null terminated) for every clobbers_len.
pub const Asm = struct {
src_node: i32,
// null-terminated string index
asm_source: u32,
/// 1 bit for each outputs_len: whether it uses `-> T` or not.
/// 0b0 - operand is a pointer to where to store the output.
/// 0b1 - operand is a type; asm expression has the output as the result.
/// 0b0X is the first output, 0bX0 is the second, etc.
output_type_bits: u32,
pub const Output = struct {
/// index into string_bytes (null terminated)
name: u32,
/// index into string_bytes (null terminated)
constraint: u32,
/// How to interpret this is determined by `output_type_bits`.
operand: Ref,
};
pub const Input = struct {
/// index into string_bytes (null terminated)
name: u32,
/// index into string_bytes (null terminated)
constraint: u32,
operand: Ref,
};
};
/// Trailing:
/// if (ret_body_len == 1) {
/// 0. return_type: Ref
/// }
/// if (ret_body_len > 1) {
/// 1. return_type: Index // for each ret_body_len
/// }
/// 2. body: Index // for each body_len
/// 3. src_locs: SrcLocs // if body_len != 0
pub const Func = struct {
/// If this is 0 it means a void return type.
/// If this is 1 it means return_type is a simple Ref
ret_body_len: u32,
/// Points to the block that contains the param instructions for this function.
param_block: Index,
body_len: u32,
pub const SrcLocs = struct {
/// Line index in the source file relative to the parent decl.
lbrace_line: u32,
/// Line index in the source file relative to the parent decl.
rbrace_line: u32,
/// lbrace_column is least significant bits u16
/// rbrace_column is most significant bits u16
columns: u32,
};
};
/// Trailing:
/// 0. lib_name: u32, // null terminated string index, if has_lib_name is set
/// if (has_align_ref and !has_align_body) {
/// 1. align: Ref,
/// }
/// if (has_align_body) {
/// 2. align_body_len: u32
/// 3. align_body: u32 // for each align_body_len
/// }
/// if (has_addrspace_ref and !has_addrspace_body) {
/// 4. addrspace: Ref,
/// }
/// if (has_addrspace_body) {
/// 5. addrspace_body_len: u32
/// 6. addrspace_body: u32 // for each addrspace_body_len
/// }
/// if (has_section_ref and !has_section_body) {
/// 7. section: Ref,
/// }
/// if (has_section_body) {
/// 8. section_body_len: u32
/// 9. section_body: u32 // for each section_body_len
/// }
/// if (has_cc_ref and !has_cc_body) {
/// 10. cc: Ref,
/// }
/// if (has_cc_body) {
/// 11. cc_body_len: u32
/// 12. cc_body: u32 // for each cc_body_len
/// }
/// if (has_ret_ty_ref and !has_ret_ty_body) {
/// 13. ret_ty: Ref,
/// }
/// if (has_ret_ty_body) {
/// 14. ret_ty_body_len: u32
/// 15. ret_ty_body: u32 // for each ret_ty_body_len
/// }
/// 16. noalias_bits: u32 // if has_any_noalias
/// - each bit starting with LSB corresponds to parameter indexes
/// 17. body: Index // for each body_len
/// 18. src_locs: Func.SrcLocs // if body_len != 0
pub const FuncFancy = struct {
/// Points to the block that contains the param instructions for this function.
param_block: Index,
body_len: u32,
bits: Bits,
/// If both has_cc_ref and has_cc_body are false, it means auto calling convention.
/// If both has_align_ref and has_align_body are false, it means default alignment.
/// If both has_ret_ty_ref and has_ret_ty_body are false, it means void return type.
/// If both has_section_ref and has_section_body are false, it means default section.
/// If both has_addrspace_ref and has_addrspace_body are false, it means default addrspace.
pub const Bits = packed struct {
is_var_args: bool,
is_inferred_error: bool,
is_test: bool,
is_extern: bool,
is_noinline: bool,
has_align_ref: bool,
has_align_body: bool,
has_addrspace_ref: bool,
has_addrspace_body: bool,
has_section_ref: bool,
has_section_body: bool,
has_cc_ref: bool,
has_cc_body: bool,
has_ret_ty_ref: bool,
has_ret_ty_body: bool,
has_lib_name: bool,
has_any_noalias: bool,
_: u15 = undefined,
};
};
/// Trailing:
/// 0. lib_name: u32, // null terminated string index, if has_lib_name is set
/// 1. align: Ref, // if has_align is set
/// 2. init: Ref // if has_init is set
/// The source node is obtained from the containing `block_inline`.
pub const ExtendedVar = struct {
var_type: Ref,
pub const Small = packed struct {
has_lib_name: bool,
has_align: bool,
has_init: bool,
is_extern: bool,
is_threadlocal: bool,
_: u11 = undefined,
};
};
/// This data is stored inside extra, with trailing operands according to `operands_len`.
/// Each operand is a `Ref`.
pub const MultiOp = struct {
operands_len: u32,
};
/// Trailing: operand: Ref, // for each `operands_len` (stored in `small`).
pub const NodeMultiOp = struct {
src_node: i32,
};
/// This data is stored inside extra, with trailing operands according to `body_len`.
/// Each operand is an `Index`.
pub const Block = struct {
body_len: u32,
};
/// Stored inside extra, with trailing arguments according to `args_len`.
/// Implicit 0. arg_0_start: u32, // always same as `args_len`
/// 1. arg_end: u32, // for each `args_len`
/// arg_N_start is the same as arg_N-1_end
pub const Call = struct {
// Note: Flags *must* come first so that unusedResultExpr
// can find it when it goes to modify them.
flags: Flags,
callee: Ref,
pub const Flags = packed struct {
/// std.builtin.CallOptions.Modifier in packed form
pub const PackedModifier = u3;
pub const PackedArgsLen = u27;
packed_modifier: PackedModifier,
ensure_result_used: bool = false,
pop_error_return_trace: bool,
args_len: PackedArgsLen,
comptime {
if (@sizeOf(Flags) != 4 or @bitSizeOf(Flags) != 32)
@compileError("Layout of Call.Flags needs to be updated!");
if (@bitSizeOf(std.builtin.CallOptions.Modifier) != @bitSizeOf(PackedModifier))
@compileError("Call.Flags.PackedModifier needs to be updated!");
}
};
};
pub const TypeOfPeer = struct {
src_node: i32,
body_len: u32,
body_index: u32,
};
pub const BuiltinCall = struct {
// Note: Flags *must* come first so that unusedResultExpr
// can find it when it goes to modify them.
flags: Flags,
options: Ref,
callee: Ref,
args: Ref,
pub const Flags = packed struct {
is_nosuspend: bool,
is_comptime: bool,
ensure_result_used: bool,
_: u29 = undefined,
comptime {
if (@sizeOf(Flags) != 4 or @bitSizeOf(Flags) != 32)
@compileError("Layout of BuiltinCall.Flags needs to be updated!");
}
};
};
/// This data is stored inside extra, with two sets of trailing `Ref`:
/// * 0. the then body, according to `then_body_len`.
/// * 1. the else body, according to `else_body_len`.
pub const CondBr = struct {
condition: Ref,
then_body_len: u32,
else_body_len: u32,
};
/// This data is stored inside extra, trailed by:
/// * 0. body: Index // for each `body_len`.
pub const Try = struct {
/// The error union to unwrap.
operand: Ref,
body_len: u32,
};
/// Stored in extra. Depending on the flags in Data, there will be up to 5
/// trailing Ref fields:
/// 0. sentinel: Ref // if `has_sentinel` flag is set
/// 1. align: Ref // if `has_align` flag is set
/// 2. address_space: Ref // if `has_addrspace` flag is set
/// 3. bit_start: Ref // if `has_bit_range` flag is set
/// 4. host_size: Ref // if `has_bit_range` flag is set
pub const PtrType = struct {
elem_type: Ref,
src_node: i32,
};
pub const ArrayTypeSentinel = struct {
len: Ref,
sentinel: Ref,
elem_type: Ref,
};
pub const SliceStart = struct {
lhs: Ref,
start: Ref,
};
pub const SliceEnd = struct {
lhs: Ref,
start: Ref,
end: Ref,
};
pub const SliceSentinel = struct {
lhs: Ref,
start: Ref,
end: Ref,
sentinel: Ref,
};
/// The meaning of these operands depends on the corresponding `Tag`.
pub const Bin = struct {
lhs: Ref,
rhs: Ref,
};
pub const BinNode = struct {
node: i32,
lhs: Ref,
rhs: Ref,
};
pub const UnNode = struct {
node: i32,
operand: Ref,
};
pub const ElemPtrImm = struct {
ptr: Ref,
index: u32,
};
/// 0. multi_cases_len: u32 // If has_multi_cases is set.
/// 1. else_body { // If has_else or has_under is set.
/// body_len: u32,
/// body member Index for every body_len
/// }
/// 2. scalar_cases: { // for every scalar_cases_len
/// item: Ref,
/// body_len: u32,
/// body member Index for every body_len
/// }
/// 3. multi_cases: { // for every multi_cases_len
/// items_len: u32,
/// ranges_len: u32,
/// body_len: u32,
/// item: Ref // for every items_len
/// ranges: { // for every ranges_len
/// item_first: Ref,
/// item_last: Ref,
/// }
/// body member Index for every body_len
/// }
pub const SwitchBlock = struct {
/// This is always a `switch_cond` or `switch_cond_ref` instruction.
/// If it is a `switch_cond_ref` instruction, bits.is_ref is always true.
/// If it is a `switch_cond` instruction, bits.is_ref is always false.
/// Both `switch_cond` and `switch_cond_ref` return a value, not a pointer,
/// that is useful for the case items, but cannot be used for capture values.
/// For the capture values, Sema is expected to find the operand of this operand
/// and use that.
operand: Ref,
bits: Bits,
pub const Bits = packed struct {
/// If true, one or more prongs have multiple items.
has_multi_cases: bool,
/// If true, there is an else prong. This is mutually exclusive with `has_under`.
has_else: bool,
/// If true, there is an underscore prong. This is mutually exclusive with `has_else`.
has_under: bool,
scalar_cases_len: ScalarCasesLen,
pub const ScalarCasesLen = u29;
pub fn specialProng(bits: Bits) SpecialProng {
const has_else: u2 = @boolToInt(bits.has_else);
const has_under: u2 = @boolToInt(bits.has_under);
return switch ((has_else << 1) | has_under) {
0b00 => .none,
0b01 => .under,
0b10 => .@"else",
0b11 => unreachable,
};
}
};
pub const ScalarProng = struct {
item: Ref,
body: []const Index,
};
/// TODO performance optimization: instead of having this helper method
/// change the definition of switch_capture instruction to store extra_index
/// instead of prong_index. This way, Sema won't be doing O(N^2) iterations
/// over the switch prongs.
pub fn getScalarProng(
self: SwitchBlock,
zir: Zir,
extra_end: usize,
prong_index: usize,
) ScalarProng {
var extra_index: usize = extra_end;
if (self.bits.has_multi_cases) {
extra_index += 1;
}
if (self.bits.specialProng() != .none) {
const body_len = @truncate(u31, zir.extra[extra_index]);
extra_index += 1;
const body = zir.extra[extra_index..][0..body_len];
extra_index += body.len;
}
var scalar_i: usize = 0;
while (true) : (scalar_i += 1) {
const item = @intToEnum(Ref, zir.extra[extra_index]);
extra_index += 1;
const body_len = @truncate(u31, zir.extra[extra_index]);
extra_index += 1;
const body = zir.extra[extra_index..][0..body_len];
extra_index += body.len;
if (scalar_i < prong_index) continue;
return .{
.item = item,
.body = body,
};
}
}
pub const MultiProng = struct {
items: []const Ref,
body: []const Index,
};
pub fn getMultiProng(
self: SwitchBlock,
zir: Zir,
extra_end: usize,
prong_index: usize,
) MultiProng {
// +1 for self.bits.has_multi_cases == true
var extra_index: usize = extra_end + 1;
if (self.bits.specialProng() != .none) {
const body_len = @truncate(u31, zir.extra[extra_index]);
extra_index += 1;
const body = zir.extra[extra_index..][0..body_len];
extra_index += body.len;
}
var scalar_i: usize = 0;
while (scalar_i < self.bits.scalar_cases_len) : (scalar_i += 1) {
extra_index += 1;
const body_len = @truncate(u31, zir.extra[extra_index]);
extra_index += 1;
extra_index += body_len;
}
var multi_i: u32 = 0;
while (true) : (multi_i += 1) {
const items_len = zir.extra[extra_index];
extra_index += 1;
const ranges_len = zir.extra[extra_index];
extra_index += 1;
const body_len = @truncate(u31, zir.extra[extra_index]);
extra_index += 1;
const items = zir.refSlice(extra_index, items_len);
extra_index += items_len;
// Each range has a start and an end.
extra_index += 2 * ranges_len;
const body = zir.extra[extra_index..][0..body_len];
extra_index += body_len;
if (multi_i < prong_index) continue;
return .{
.items = items,
.body = body,
};
}
}
};
pub const Field = struct {
lhs: Ref,
/// Offset into `string_bytes`.
field_name_start: u32,
};
pub const FieldNamed = struct {
lhs: Ref,
field_name: Ref,
};
pub const FieldNamedNode = struct {
node: i32,
lhs: Ref,
field_name: Ref,
};
pub const As = struct {
dest_type: Ref,
operand: Ref,
};
/// Trailing:
/// 0. src_node: i32, // if has_src_node
/// 1. fields_len: u32, // if has_fields_len
/// 2. decls_len: u32, // if has_decls_len
/// 3. backing_int_body_len: u32, // if has_backing_int
/// 4. backing_int_ref: Ref, // if has_backing_int and backing_int_body_len is 0
/// 5. backing_int_body_inst: Inst, // if has_backing_int and backing_int_body_len is > 0
/// 6. decl_bits: u32 // for every 8 decls
/// - sets of 4 bits:
/// 0b000X: whether corresponding decl is pub
/// 0b00X0: whether corresponding decl is exported
/// 0b0X00: whether corresponding decl has an align expression
/// 0bX000: whether corresponding decl has a linksection or an address space expression
/// 7. decl: { // for every decls_len
/// src_hash: [4]u32, // hash of source bytes
/// line: u32, // line number of decl, relative to parent
/// name: u32, // null terminated string index
/// - 0 means comptime or usingnamespace decl.
/// - if name == 0 `is_exported` determines which one: 0=comptime,1=usingnamespace
/// - 1 means test decl with no name.
/// - 2 means that the test is a decltest, doc_comment gives the name of the identifier
/// - if there is a 0 byte at the position `name` indexes, it indicates
/// this is a test decl, and the name starts at `name+1`.
/// value: Index,
/// doc_comment: u32, 0 if no doc comment, if this is a decltest, doc_comment references the decl name in the string table
/// align: Ref, // if corresponding bit is set
/// link_section_or_address_space: { // if corresponding bit is set.
/// link_section: Ref,
/// address_space: Ref,
/// }
/// }
/// 8. flags: u32 // for every 8 fields
/// - sets of 4 bits:
/// 0b000X: whether corresponding field has an align expression
/// 0b00X0: whether corresponding field has a default expression
/// 0b0X00: whether corresponding field is comptime
/// 0bX000: whether corresponding field has a type expression
/// 9. fields: { // for every fields_len
/// field_name: u32,
/// doc_comment: u32, // 0 if no doc comment
/// field_type: Ref, // if corresponding bit is not set. none means anytype.
/// field_type_body_len: u32, // if corresponding bit is set
/// align_body_len: u32, // if corresponding bit is set
/// init_body_len: u32, // if corresponding bit is set
/// }
/// 10. bodies: { // for every fields_len
/// field_type_body_inst: Inst, // for each field_type_body_len
/// align_body_inst: Inst, // for each align_body_len
/// init_body_inst: Inst, // for each init_body_len
/// }
pub const StructDecl = struct {
pub const Small = packed struct {
has_src_node: bool,
has_fields_len: bool,
has_decls_len: bool,
has_backing_int: bool,
known_non_opv: bool,
known_comptime_only: bool,
name_strategy: NameStrategy,
layout: std.builtin.Type.ContainerLayout,
_: u6 = undefined,
};
};
pub const NameStrategy = enum(u2) {
/// Use the same name as the parent declaration name.
/// e.g. `const Foo = struct {...};`.
parent,
/// Use the name of the currently executing comptime function call,
/// with the current parameters. e.g. `ArrayList(i32)`.
func,
/// Create an anonymous name for this declaration.
/// Like this: "ParentDeclName_struct_69"
anon,
/// Use the name specified in the next `dbg_var_{val,ptr}` instruction.
dbg_var,
};
/// Trailing:
/// 0. src_node: i32, // if has_src_node
/// 1. tag_type: Ref, // if has_tag_type
/// 2. body_len: u32, // if has_body_len
/// 3. fields_len: u32, // if has_fields_len
/// 4. decls_len: u32, // if has_decls_len
/// 5. decl_bits: u32 // for every 8 decls
/// - sets of 4 bits:
/// 0b000X: whether corresponding decl is pub
/// 0b00X0: whether corresponding decl is exported
/// 0b0X00: whether corresponding decl has an align expression
/// 0bX000: whether corresponding decl has a linksection or an address space expression
/// 6. decl: { // for every decls_len
/// src_hash: [4]u32, // hash of source bytes
/// line: u32, // line number of decl, relative to parent
/// name: u32, // null terminated string index
/// - 0 means comptime or usingnamespace decl.
/// - if name == 0 `is_exported` determines which one: 0=comptime,1=usingnamespace
/// - 1 means test decl with no name.
/// - if there is a 0 byte at the position `name` indexes, it indicates
/// this is a test decl, and the name starts at `name+1`.
/// value: Index,
/// doc_comment: u32, // 0 if no doc_comment
/// align: Ref, // if corresponding bit is set
/// link_section_or_address_space: { // if corresponding bit is set.
/// link_section: Ref,
/// address_space: Ref,
/// }
/// }
/// 7. inst: Index // for every body_len
/// 8. has_bits: u32 // for every 32 fields
/// - the bit is whether corresponding field has an value expression
/// 9. fields: { // for every fields_len
/// field_name: u32,
/// doc_comment: u32, // 0 if no doc_comment
/// value: Ref, // if corresponding bit is set
/// }
pub const EnumDecl = struct {
pub const Small = packed struct {
has_src_node: bool,
has_tag_type: bool,
has_body_len: bool,
has_fields_len: bool,
has_decls_len: bool,
name_strategy: NameStrategy,
nonexhaustive: bool,
_: u8 = undefined,
};
};
/// Trailing:
/// 0. src_node: i32, // if has_src_node
/// 1. tag_type: Ref, // if has_tag_type
/// 2. body_len: u32, // if has_body_len
/// 3. fields_len: u32, // if has_fields_len
/// 4. decls_len: u32, // if has_decls_len
/// 5. decl_bits: u32 // for every 8 decls
/// - sets of 4 bits:
/// 0b000X: whether corresponding decl is pub
/// 0b00X0: whether corresponding decl is exported
/// 0b0X00: whether corresponding decl has an align expression
/// 0bX000: whether corresponding decl has a linksection or an address space expression
/// 6. decl: { // for every decls_len
/// src_hash: [4]u32, // hash of source bytes
/// line: u32, // line number of decl, relative to parent
/// name: u32, // null terminated string index
/// - 0 means comptime or usingnamespace decl.
/// - if name == 0 `is_exported` determines which one: 0=comptime,1=usingnamespace
/// - 1 means test decl with no name.
/// - if there is a 0 byte at the position `name` indexes, it indicates
/// this is a test decl, and the name starts at `name+1`.
/// value: Index,
/// doc_comment: u32, // 0 if no doc comment
/// align: Ref, // if corresponding bit is set
/// link_section_or_address_space: { // if corresponding bit is set.
/// link_section: Ref,
/// address_space: Ref,
/// }
/// }
/// 7. inst: Index // for every body_len
/// 8. has_bits: u32 // for every 8 fields
/// - sets of 4 bits:
/// 0b000X: whether corresponding field has a type expression
/// 0b00X0: whether corresponding field has a align expression
/// 0b0X00: whether corresponding field has a tag value expression
/// 0bX000: unused
/// 9. fields: { // for every fields_len
/// field_name: u32, // null terminated string index
/// doc_comment: u32, // 0 if no doc comment
/// field_type: Ref, // if corresponding bit is set
/// - if none, means `anytype`.
/// align: Ref, // if corresponding bit is set
/// tag_value: Ref, // if corresponding bit is set
/// }
pub const UnionDecl = struct {
pub const Small = packed struct {
has_src_node: bool,
has_tag_type: bool,
has_body_len: bool,
has_fields_len: bool,
has_decls_len: bool,
name_strategy: NameStrategy,
layout: std.builtin.Type.ContainerLayout,
/// has_tag_type | auto_enum_tag | result
/// -------------------------------------
/// false | false | union { }
/// false | true | union(enum) { }
/// true | true | union(enum(T)) { }
/// true | false | union(T) { }
auto_enum_tag: bool,
_: u6 = undefined,
};
};
/// Trailing:
/// 0. src_node: i32, // if has_src_node
/// 1. decls_len: u32, // if has_decls_len
/// 2. decl_bits: u32 // for every 8 decls
/// - sets of 4 bits:
/// 0b000X: whether corresponding decl is pub
/// 0b00X0: whether corresponding decl is exported
/// 0b0X00: whether corresponding decl has an align expression
/// 0bX000: whether corresponding decl has a linksection or an address space expression
/// 3. decl: { // for every decls_len
/// src_hash: [4]u32, // hash of source bytes
/// line: u32, // line number of decl, relative to parent
/// name: u32, // null terminated string index
/// - 0 means comptime or usingnamespace decl.
/// - if name == 0 `is_exported` determines which one: 0=comptime,1=usingnamespace
/// - 1 means test decl with no name.
/// - if there is a 0 byte at the position `name` indexes, it indicates
/// this is a test decl, and the name starts at `name+1`.
/// value: Index,
/// doc_comment: u32, // 0 if no doc comment,
/// align: Ref, // if corresponding bit is set
/// link_section_or_address_space: { // if corresponding bit is set.
/// link_section: Ref,
/// address_space: Ref,
/// }
/// }
pub const OpaqueDecl = struct {
pub const Small = packed struct {
has_src_node: bool,
has_decls_len: bool,
name_strategy: NameStrategy,
_: u12 = undefined,
};
};
/// Trailing:
/// { // for every fields_len
/// field_name: u32 // null terminated string index
/// doc_comment: u32 // null terminated string index
/// }
pub const ErrorSetDecl = struct {
fields_len: u32,
};
/// A f128 value, broken up into 4 u32 parts.
pub const Float128 = struct {
piece0: u32,
piece1: u32,
piece2: u32,
piece3: u32,
pub fn get(self: Float128) f128 {
const int_bits = @as(u128, self.piece0) |
(@as(u128, self.piece1) << 32) |
(@as(u128, self.piece2) << 64) |
(@as(u128, self.piece3) << 96);
return @bitCast(f128, int_bits);
}
};
/// Trailing is an item per field.
pub const StructInit = struct {
fields_len: u32,
pub const Item = struct {
/// The `field_type` ZIR instruction for this field init.
field_type: Index,
/// The field init expression to be used as the field value.
init: Ref,
};
};
/// Trailing is an Item per field.
/// TODO make this instead array of inits followed by array of names because
/// it will be simpler Sema code and better for CPU cache.
pub const StructInitAnon = struct {
fields_len: u32,
pub const Item = struct {
/// Null-terminated string table index.
field_name: u32,
/// The field init expression to be used as the field value.
init: Ref,
};
};
pub const FieldType = struct {
container_type: Ref,
/// Offset into `string_bytes`, null terminated.
name_start: u32,
};
pub const FieldTypeRef = struct {
container_type: Ref,
field_name: Ref,
};
pub const OverflowArithmetic = struct {
node: i32,
lhs: Ref,
rhs: Ref,
ptr: Ref,
};
pub const Cmpxchg = struct {
node: i32,
ptr: Ref,
expected_value: Ref,
new_value: Ref,
success_order: Ref,
failure_order: Ref,
};
pub const AtomicRmw = struct {
ptr: Ref,
operation: Ref,
operand: Ref,
ordering: Ref,
};
pub const UnionInit = struct {
union_type: Ref,
field_name: Ref,
init: Ref,
};
pub const AtomicStore = struct {
ptr: Ref,
operand: Ref,
ordering: Ref,
};
pub const AtomicLoad = struct {
elem_type: Ref,
ptr: Ref,
ordering: Ref,
};
pub const MulAdd = struct {
mulend1: Ref,
mulend2: Ref,
addend: Ref,
};
pub const FieldParentPtr = struct {
parent_type: Ref,
field_name: Ref,
field_ptr: Ref,
};
pub const Memcpy = struct {
dest: Ref,
source: Ref,
byte_count: Ref,
};
pub const Memset = struct {
dest: Ref,
byte: Ref,
byte_count: Ref,
};
pub const Shuffle = struct {
elem_type: Ref,
a: Ref,
b: Ref,
mask: Ref,
};
pub const Select = struct {
node: i32,
elem_type: Ref,
pred: Ref,
a: Ref,
b: Ref,
};
pub const AsyncCall = struct {
node: i32,
frame_buffer: Ref,
result_ptr: Ref,
fn_ptr: Ref,
args: Ref,
};
/// Trailing: inst: Index // for every body_len
pub const Param = struct {
/// Null-terminated string index.
name: u32,
/// 0 if no doc comment
doc_comment: u32,
/// The body contains the type of the parameter.
body_len: u32,
};
/// Trailing:
/// 0. type_inst: Ref, // if small 0b000X is set
/// 1. align_inst: Ref, // if small 0b00X0 is set
pub const AllocExtended = struct {
src_node: i32,
pub const Small = packed struct {
has_type: bool,
has_align: bool,
is_const: bool,
is_comptime: bool,
_: u12 = undefined,
};
};
pub const Export = struct {
/// If present, this is referring to a Decl via field access, e.g. `a.b`.
/// If omitted, this is referring to a Decl via identifier, e.g. `a`.
namespace: Ref,
/// Null-terminated string index.
decl_name: u32,
options: Ref,
};
pub const ExportValue = struct {
/// The comptime value to export.
operand: Ref,
options: Ref,
};
/// Trailing: `CompileErrors.Item` for each `items_len`.
pub const CompileErrors = struct {
items_len: u32,
/// Trailing: `note_payload_index: u32` for each `notes_len`.
/// It's a payload index of another `Item`.
pub const Item = struct {
/// null terminated string index
msg: u32,
node: Ast.Node.Index,
/// If node is 0 then this will be populated.
token: Ast.TokenIndex,
/// Can be used in combination with `token`.
byte_offset: u32,
/// 0 or a payload index of a `Block`, each is a payload
/// index of another `Item`.
notes: u32,
};
};
/// Trailing: for each `imports_len` there is an Item
pub const Imports = struct {
imports_len: Inst.Index,
pub const Item = struct {
/// null terminated string index
name: u32,
/// points to the import name
token: Ast.TokenIndex,
};
};
pub const LineColumn = struct {
line: u32,
column: u32,
};
pub const ArrayInit = struct {
ty: Ref,
init_count: u32,
};
pub const Src = struct {
node: i32,
line: u32,
column: u32,
};
pub const DeferErrCode = struct {
remapped_err_code: Index,
index: u32,
len: u32,
};
};
pub const SpecialProng = enum { none, @"else", under };
pub const DeclIterator = struct {
extra_index: usize,
bit_bag_index: usize,
cur_bit_bag: u32,
decl_i: u32,
decls_len: u32,
zir: Zir,
pub const Item = struct {
name: [:0]const u8,
sub_index: u32,
};
pub fn next(it: *DeclIterator) ?Item {
if (it.decl_i >= it.decls_len) return null;
if (it.decl_i % 8 == 0) {
it.cur_bit_bag = it.zir.extra[it.bit_bag_index];
it.bit_bag_index += 1;
}
it.decl_i += 1;
const flags = @truncate(u4, it.cur_bit_bag);
it.cur_bit_bag >>= 4;
const sub_index = @intCast(u32, it.extra_index);
it.extra_index += 5; // src_hash(4) + line(1)
const name = it.zir.nullTerminatedString(it.zir.extra[it.extra_index]);
it.extra_index += 3; // name(1) + value(1) + doc_comment(1)
it.extra_index += @truncate(u1, flags >> 2);
it.extra_index += @truncate(u1, flags >> 3);
return Item{
.sub_index = sub_index,
.name = name,
};
}
};
pub fn declIterator(zir: Zir, decl_inst: u32) DeclIterator {
const tags = zir.instructions.items(.tag);
const datas = zir.instructions.items(.data);
switch (tags[decl_inst]) {
// Functions are allowed and yield no iterations.
// There is one case matching this in the extended instruction set below.
.func, .func_inferred, .func_fancy => return declIteratorInner(zir, 0, 0),
.extended => {
const extended = datas[decl_inst].extended;
switch (extended.opcode) {
.struct_decl => {
const small = @bitCast(Inst.StructDecl.Small, extended.small);
var extra_index: usize = extended.operand;
extra_index += @boolToInt(small.has_src_node);
extra_index += @boolToInt(small.has_fields_len);
const decls_len = if (small.has_decls_len) decls_len: {
const decls_len = zir.extra[extra_index];
extra_index += 1;
break :decls_len decls_len;
} else 0;
if (small.has_backing_int) {
const backing_int_body_len = zir.extra[extra_index];
extra_index += 1; // backing_int_body_len
if (backing_int_body_len == 0) {
extra_index += 1; // backing_int_ref
} else {
extra_index += backing_int_body_len; // backing_int_body_inst
}
}
return declIteratorInner(zir, extra_index, decls_len);
},
.enum_decl => {
const small = @bitCast(Inst.EnumDecl.Small, extended.small);
var extra_index: usize = extended.operand;
extra_index += @boolToInt(small.has_src_node);
extra_index += @boolToInt(small.has_tag_type);
extra_index += @boolToInt(small.has_body_len);
extra_index += @boolToInt(small.has_fields_len);
const decls_len = if (small.has_decls_len) decls_len: {
const decls_len = zir.extra[extra_index];
extra_index += 1;
break :decls_len decls_len;
} else 0;
return declIteratorInner(zir, extra_index, decls_len);
},
.union_decl => {
const small = @bitCast(Inst.UnionDecl.Small, extended.small);
var extra_index: usize = extended.operand;
extra_index += @boolToInt(small.has_src_node);
extra_index += @boolToInt(small.has_tag_type);
extra_index += @boolToInt(small.has_body_len);
extra_index += @boolToInt(small.has_fields_len);
const decls_len = if (small.has_decls_len) decls_len: {
const decls_len = zir.extra[extra_index];
extra_index += 1;
break :decls_len decls_len;
} else 0;
return declIteratorInner(zir, extra_index, decls_len);
},
.opaque_decl => {
const small = @bitCast(Inst.OpaqueDecl.Small, extended.small);
var extra_index: usize = extended.operand;
extra_index += @boolToInt(small.has_src_node);
const decls_len = if (small.has_decls_len) decls_len: {
const decls_len = zir.extra[extra_index];
extra_index += 1;
break :decls_len decls_len;
} else 0;
return declIteratorInner(zir, extra_index, decls_len);
},
else => unreachable,
}
},
else => unreachable,
}
}
pub fn declIteratorInner(zir: Zir, extra_index: usize, decls_len: u32) DeclIterator {
const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;
return .{
.zir = zir,
.extra_index = extra_index + bit_bags_count,
.bit_bag_index = extra_index,
.cur_bit_bag = undefined,
.decl_i = 0,
.decls_len = decls_len,
};
}
/// The iterator would have to allocate memory anyway to iterate. So here we populate
/// an ArrayList as the result.
pub fn findDecls(zir: Zir, list: *std.ArrayList(Inst.Index), decl_sub_index: u32) !void {
const block_inst = zir.extra[decl_sub_index + 6];
list.clearRetainingCapacity();
return zir.findDeclsInner(list, block_inst);
}
fn findDeclsInner(
zir: Zir,
list: *std.ArrayList(Inst.Index),
inst: Inst.Index,
) Allocator.Error!void {
const tags = zir.instructions.items(.tag);
const datas = zir.instructions.items(.data);
switch (tags[inst]) {
// Functions instructions are interesting and have a body.
.func,
.func_inferred,
=> {
try list.append(inst);
const inst_data = datas[inst].pl_node;
const extra = zir.extraData(Inst.Func, inst_data.payload_index);
var extra_index: usize = extra.end;
switch (extra.data.ret_body_len) {
0 => {},
1 => extra_index += 1,
else => {
const body = zir.extra[extra_index..][0..extra.data.ret_body_len];
extra_index += body.len;
try zir.findDeclsBody(list, body);
},
}
const body = zir.extra[extra_index..][0..extra.data.body_len];
return zir.findDeclsBody(list, body);
},
.func_fancy => {
try list.append(inst);
const inst_data = datas[inst].pl_node;
const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index);
var extra_index: usize = extra.end;
extra_index += @boolToInt(extra.data.bits.has_lib_name);
if (extra.data.bits.has_align_body) {
const body_len = zir.extra[extra_index];
extra_index += 1;
const body = zir.extra[extra_index..][0..body_len];
try zir.findDeclsBody(list, body);
extra_index += body.len;
} else if (extra.data.bits.has_align_ref) {
extra_index += 1;
}
if (extra.data.bits.has_addrspace_body) {
const body_len = zir.extra[extra_index];
extra_index += 1;
const body = zir.extra[extra_index..][0..body_len];
try zir.findDeclsBody(list, body);
extra_index += body.len;
} else if (extra.data.bits.has_addrspace_ref) {
extra_index += 1;
}
if (extra.data.bits.has_section_body) {
const body_len = zir.extra[extra_index];
extra_index += 1;
const body = zir.extra[extra_index..][0..body_len];
try zir.findDeclsBody(list, body);
extra_index += body.len;
} else if (extra.data.bits.has_section_ref) {
extra_index += 1;
}
if (extra.data.bits.has_cc_body) {
const body_len = zir.extra[extra_index];
extra_index += 1;
const body = zir.extra[extra_index..][0..body_len];
try zir.findDeclsBody(list, body);
extra_index += body.len;
} else if (extra.data.bits.has_cc_ref) {
extra_index += 1;
}
if (extra.data.bits.has_ret_ty_body) {
const body_len = zir.extra[extra_index];
extra_index += 1;
const body = zir.extra[extra_index..][0..body_len];
try zir.findDeclsBody(list, body);
extra_index += body.len;
} else if (extra.data.bits.has_ret_ty_ref) {
extra_index += 1;
}
extra_index += @boolToInt(extra.data.bits.has_any_noalias);
const body = zir.extra[extra_index..][0..extra.data.body_len];
return zir.findDeclsBody(list, body);
},
.extended => {
const extended = datas[inst].extended;
switch (extended.opcode) {
// Decl instructions are interesting but have no body.
// TODO yes they do have a body actually. recurse over them just like block instructions.
.struct_decl,
.union_decl,
.enum_decl,
.opaque_decl,
=> return list.append(inst),
else => return,
}
},
// Block instructions, recurse over the bodies.
.block, .block_inline => {
const inst_data = datas[inst].pl_node;
const extra = zir.extraData(Inst.Block, inst_data.payload_index);
const body = zir.extra[extra.end..][0..extra.data.body_len];
return zir.findDeclsBody(list, body);
},
.condbr, .condbr_inline => {
const inst_data = datas[inst].pl_node;
const extra = zir.extraData(Inst.CondBr, inst_data.payload_index);
const then_body = zir.extra[extra.end..][0..extra.data.then_body_len];
const else_body = zir.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
try zir.findDeclsBody(list, then_body);
try zir.findDeclsBody(list, else_body);
},
.@"try", .try_ptr => {
const inst_data = datas[inst].pl_node;
const extra = zir.extraData(Inst.Try, inst_data.payload_index);
const body = zir.extra[extra.end..][0..extra.data.body_len];
try zir.findDeclsBody(list, body);
},
.switch_block => return findDeclsSwitch(zir, list, inst),
.suspend_block => @panic("TODO iterate suspend block"),
else => return, // Regular instruction, not interesting.
}
}
fn findDeclsSwitch(
zir: Zir,
list: *std.ArrayList(Inst.Index),
inst: Inst.Index,
) Allocator.Error!void {
const inst_data = zir.instructions.items(.data)[inst].pl_node;
const extra = zir.extraData(Inst.SwitchBlock, inst_data.payload_index);
var extra_index: usize = extra.end;
const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
const multi_cases_len = zir.extra[extra_index];
extra_index += 1;
break :blk multi_cases_len;
} else 0;
const special_prong = extra.data.bits.specialProng();
if (special_prong != .none) {
const body_len = @truncate(u31, zir.extra[extra_index]);
extra_index += 1;
const body = zir.extra[extra_index..][0..body_len];
extra_index += body.len;
try zir.findDeclsBody(list, body);
}
{
const scalar_cases_len = extra.data.bits.scalar_cases_len;
var scalar_i: usize = 0;
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
extra_index += 1;
const body_len = @truncate(u31, zir.extra[extra_index]);
extra_index += 1;
const body = zir.extra[extra_index..][0..body_len];
extra_index += body_len;
try zir.findDeclsBody(list, body);
}
}
{
var multi_i: usize = 0;
while (multi_i < multi_cases_len) : (multi_i += 1) {
const items_len = zir.extra[extra_index];
extra_index += 1;
const ranges_len = zir.extra[extra_index];
extra_index += 1;
const body_len = @truncate(u31, zir.extra[extra_index]);
extra_index += 1;
const items = zir.refSlice(extra_index, items_len);
extra_index += items_len;
_ = items;
var range_i: usize = 0;
while (range_i < ranges_len) : (range_i += 1) {
extra_index += 1;
extra_index += 1;
}
const body = zir.extra[extra_index..][0..body_len];
extra_index += body_len;
try zir.findDeclsBody(list, body);
}
}
}
fn findDeclsBody(
zir: Zir,
list: *std.ArrayList(Inst.Index),
body: []const Inst.Index,
) Allocator.Error!void {
for (body) |member| {
try zir.findDeclsInner(list, member);
}
}
pub const FnInfo = struct {
param_body: []const Inst.Index,
param_body_inst: Inst.Index,
ret_ty_body: []const Inst.Index,
body: []const Inst.Index,
ret_ty_ref: Zir.Inst.Ref,
total_params_len: u32,
};
pub fn getParamBody(zir: Zir, fn_inst: Inst.Index) []const u32 {
const tags = zir.instructions.items(.tag);
const datas = zir.instructions.items(.data);
const inst_data = datas[fn_inst].pl_node;
const param_block_index = switch (tags[fn_inst]) {
.func, .func_inferred => blk: {
const extra = zir.extraData(Inst.Func, inst_data.payload_index);
break :blk extra.data.param_block;
},
.func_fancy => blk: {
const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index);
break :blk extra.data.param_block;
},
else => unreachable,
};
const param_block = zir.extraData(Inst.Block, datas[param_block_index].pl_node.payload_index);
return zir.extra[param_block.end..][0..param_block.data.body_len];
}
pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
const tags = zir.instructions.items(.tag);
const datas = zir.instructions.items(.data);
const info: struct {
param_block: Inst.Index,
body: []const Inst.Index,
ret_ty_ref: Inst.Ref,
ret_ty_body: []const Inst.Index,
} = switch (tags[fn_inst]) {
.func, .func_inferred => blk: {
const inst_data = datas[fn_inst].pl_node;
const extra = zir.extraData(Inst.Func, inst_data.payload_index);
var extra_index: usize = extra.end;
var ret_ty_ref: Inst.Ref = .none;
var ret_ty_body: []const Inst.Index = &.{};
switch (extra.data.ret_body_len) {
0 => {
ret_ty_ref = .void_type;
},
1 => {
ret_ty_ref = @intToEnum(Inst.Ref, zir.extra[extra_index]);
extra_index += 1;
},
else => {
ret_ty_body = zir.extra[extra_index..][0..extra.data.ret_body_len];
extra_index += ret_ty_body.len;
},
}
const body = zir.extra[extra_index..][0..extra.data.body_len];
extra_index += body.len;
break :blk .{
.param_block = extra.data.param_block,
.ret_ty_ref = ret_ty_ref,
.ret_ty_body = ret_ty_body,
.body = body,
};
},
.func_fancy => blk: {
const inst_data = datas[fn_inst].pl_node;
const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index);
var extra_index: usize = extra.end;
var ret_ty_ref: Inst.Ref = .void_type;
var ret_ty_body: []const Inst.Index = &.{};
extra_index += @boolToInt(extra.data.bits.has_lib_name);
if (extra.data.bits.has_align_body) {
extra_index += zir.extra[extra_index] + 1;
} else if (extra.data.bits.has_align_ref) {
extra_index += 1;
}
if (extra.data.bits.has_addrspace_body) {
extra_index += zir.extra[extra_index] + 1;
} else if (extra.data.bits.has_addrspace_ref) {
extra_index += 1;
}
if (extra.data.bits.has_section_body) {
extra_index += zir.extra[extra_index] + 1;
} else if (extra.data.bits.has_section_ref) {
extra_index += 1;
}
if (extra.data.bits.has_cc_body) {
extra_index += zir.extra[extra_index] + 1;
} else if (extra.data.bits.has_cc_ref) {
extra_index += 1;
}
if (extra.data.bits.has_ret_ty_body) {
const body_len = zir.extra[extra_index];
extra_index += 1;
ret_ty_body = zir.extra[extra_index..][0..body_len];
extra_index += ret_ty_body.len;
} else if (extra.data.bits.has_ret_ty_ref) {
ret_ty_ref = @intToEnum(Inst.Ref, zir.extra[extra_index]);
extra_index += 1;
}
extra_index += @boolToInt(extra.data.bits.has_any_noalias);
const body = zir.extra[extra_index..][0..extra.data.body_len];
extra_index += body.len;
break :blk .{
.param_block = extra.data.param_block,
.ret_ty_ref = ret_ty_ref,
.ret_ty_body = ret_ty_body,
.body = body,
};
},
else => unreachable,
};
assert(tags[info.param_block] == .block or tags[info.param_block] == .block_inline);
const param_block = zir.extraData(Inst.Block, datas[info.param_block].pl_node.payload_index);
const param_body = zir.extra[param_block.end..][0..param_block.data.body_len];
var total_params_len: u32 = 0;
for (param_body) |inst| {
switch (tags[inst]) {
.param, .param_comptime, .param_anytype, .param_anytype_comptime => {
total_params_len += 1;
},
else => continue,
}
}
return .{
.param_body = param_body,
.param_body_inst = info.param_block,
.ret_ty_body = info.ret_ty_body,
.ret_ty_ref = info.ret_ty_ref,
.body = info.body,
.total_params_len = total_params_len,
};
}
const ref_start_index: u32 = Inst.Ref.typed_value_map.len;
pub fn indexToRef(inst: Inst.Index) Inst.Ref {
return @intToEnum(Inst.Ref, ref_start_index + inst);
}
pub fn refToIndex(inst: Inst.Ref) ?Inst.Index {
const ref_int = @enumToInt(inst);
if (ref_int >= ref_start_index) {
return ref_int - ref_start_index;
} else {
return null;
}
}