zig/src/ir.cpp

12028 lines
523 KiB
C++

/*
* Copyright (c) 2016 Andrew Kelley
*
* This file is part of zig, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "analyze.hpp"
#include "ast_render.hpp"
#include "error.hpp"
#include "ir.hpp"
#include "ir_print.hpp"
#include "os.hpp"
#include "parseh.hpp"
struct IrExecContext {
ConstExprValue *mem_slot_list;
size_t mem_slot_count;
};
struct LoopStackItem {
IrBasicBlock *break_block;
IrBasicBlock *continue_block;
IrInstruction *is_comptime;
};
struct IrBuilder {
CodeGen *codegen;
IrExecutable *exec;
IrBasicBlock *current_basic_block;
ZigList<LoopStackItem> loop_stack;
};
struct IrAnalyze {
CodeGen *codegen;
IrBuilder old_irb;
IrBuilder new_irb;
IrExecContext exec_context;
ZigList<IrBasicBlock *> old_bb_queue;
size_t block_queue_index;
size_t instruction_index;
TypeTableEntry *explicit_return_type;
ZigList<IrInstruction *> implicit_return_type_list;
IrBasicBlock *const_predecessor_bb;
};
static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope);
static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope,
LValPurpose lval);
static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction);
static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, TypeTableEntry *expected_type);
ConstExprValue *const_ptr_pointee(ConstExprValue *const_val) {
assert(const_val->special == ConstValSpecialStatic);
assert(const_val->data.x_ptr.special != ConstPtrSpecialRuntime);
ConstExprValue *base_ptr = const_val->data.x_ptr.base_ptr;
size_t index = const_val->data.x_ptr.index;
if (index == SIZE_MAX) {
return base_ptr;
} else {
assert(index < base_ptr->data.x_array.size);
return &base_ptr->data.x_array.elements[index];
}
}
static bool ir_should_inline(IrExecutable *exec, Scope *scope) {
if (exec->is_inline)
return true;
while (scope != nullptr) {
if (scope->id == ScopeIdCompTime)
return true;
scope = scope->parent;
}
return false;
}
static void ir_instruction_append(IrBasicBlock *basic_block, IrInstruction *instruction) {
assert(basic_block);
assert(instruction);
basic_block->instruction_list.append(instruction);
}
static size_t exec_next_debug_id(IrExecutable *exec) {
size_t result = exec->next_debug_id;
exec->next_debug_id += 1;
return result;
}
static size_t exec_next_mem_slot(IrExecutable *exec) {
size_t result = exec->mem_slot_count;
exec->mem_slot_count += 1;
return result;
}
static FnTableEntry *exec_fn_entry(IrExecutable *exec) {
return exec->fn_entry;
}
static Buf *exec_c_import_buf(IrExecutable *exec) {
return exec->c_import_buf;
}
static bool value_is_comptime(ConstExprValue *const_val) {
return const_val->special != ConstValSpecialRuntime;
}
static bool instr_is_comptime(IrInstruction *instruction) {
return value_is_comptime(&instruction->value);
}
static bool instr_is_unreachable(IrInstruction *instruction) {
return instruction->value.type && instruction->value.type->id == TypeTableEntryIdUnreachable;
}
static void ir_link_new_instruction(IrInstruction *new_instruction, IrInstruction *old_instruction) {
new_instruction->other = old_instruction;
old_instruction->other = new_instruction;
}
static void ir_link_new_bb(IrBasicBlock *new_bb, IrBasicBlock *old_bb) {
new_bb->other = old_bb;
old_bb->other = new_bb;
}
static void ir_ref_bb(IrBasicBlock *bb) {
bb->ref_count += 1;
}
static void ir_ref_instruction(IrInstruction *instruction, IrBasicBlock *cur_bb) {
assert(instruction->id != IrInstructionIdInvalid);
instruction->ref_count += 1;
if (instruction->owner_bb != cur_bb && !instr_is_comptime(instruction))
ir_ref_bb(instruction->owner_bb);
}
static void ir_ref_var(VariableTableEntry *var) {
var->ref_count += 1;
}
static IrBasicBlock *ir_create_basic_block(IrBuilder *irb, Scope *scope, const char *name_hint) {
IrBasicBlock *result = allocate<IrBasicBlock>(1);
result->scope = scope;
result->name_hint = name_hint;
result->debug_id = exec_next_debug_id(irb->exec);
return result;
}
static IrBasicBlock *ir_build_basic_block(IrBuilder *irb, Scope *scope, const char *name_hint) {
IrBasicBlock *result = ir_create_basic_block(irb, scope, name_hint);
irb->exec->basic_block_list.append(result);
return result;
}
static IrBasicBlock *ir_build_bb_from(IrBuilder *irb, IrBasicBlock *other_bb) {
IrBasicBlock *new_bb = ir_create_basic_block(irb, other_bb->scope, other_bb->name_hint);
ir_link_new_bb(new_bb, other_bb);
return new_bb;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCondBr *) {
return IrInstructionIdCondBr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionBr *) {
return IrInstructionIdBr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSwitchBr *) {
return IrInstructionIdSwitchBr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSwitchVar *) {
return IrInstructionIdSwitchVar;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSwitchTarget *) {
return IrInstructionIdSwitchTarget;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionPhi *) {
return IrInstructionIdPhi;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionUnOp *) {
return IrInstructionIdUnOp;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionBinOp *) {
return IrInstructionIdBinOp;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVar *) {
return IrInstructionIdDeclVar;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadPtr *) {
return IrInstructionIdLoadPtr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionStorePtr *) {
return IrInstructionIdStorePtr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionFieldPtr *) {
return IrInstructionIdFieldPtr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionStructFieldPtr *) {
return IrInstructionIdStructFieldPtr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumFieldPtr *) {
return IrInstructionIdEnumFieldPtr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionElemPtr *) {
return IrInstructionIdElemPtr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionVarPtr *) {
return IrInstructionIdVarPtr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCall *) {
return IrInstructionIdCall;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionConst *) {
return IrInstructionIdConst;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionReturn *) {
return IrInstructionIdReturn;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCast *) {
return IrInstructionIdCast;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionContainerInitList *) {
return IrInstructionIdContainerInitList;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionContainerInitFields *) {
return IrInstructionIdContainerInitFields;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionUnreachable *) {
return IrInstructionIdUnreachable;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeOf *) {
return IrInstructionIdTypeOf;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionToPtrType *) {
return IrInstructionIdToPtrType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrTypeChild *) {
return IrInstructionIdPtrTypeChild;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSetFnTest *) {
return IrInstructionIdSetFnTest;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSetFnVisible *) {
return IrInstructionIdSetFnVisible;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSetDebugSafety *) {
return IrInstructionIdSetDebugSafety;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayType *) {
return IrInstructionIdArrayType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSliceType *) {
return IrInstructionIdSliceType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAsm *) {
return IrInstructionIdAsm;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCompileVar *) {
return IrInstructionIdCompileVar;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSizeOf *) {
return IrInstructionIdSizeOf;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionTestNonNull *) {
return IrInstructionIdTestNonNull;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapMaybe *) {
return IrInstructionIdUnwrapMaybe;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionClz *) {
return IrInstructionIdClz;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCtz *) {
return IrInstructionIdCtz;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumTag *) {
return IrInstructionIdEnumTag;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionGeneratedCode *) {
return IrInstructionIdGeneratedCode;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionImport *) {
return IrInstructionIdImport;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCImport *) {
return IrInstructionIdCImport;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCInclude *) {
return IrInstructionIdCInclude;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCDefine *) {
return IrInstructionIdCDefine;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCUndef *) {
return IrInstructionIdCUndef;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayLen *) {
return IrInstructionIdArrayLen;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionRef *) {
return IrInstructionIdRef;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionStructInit *) {
return IrInstructionIdStructInit;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionMinValue *) {
return IrInstructionIdMinValue;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionMaxValue *) {
return IrInstructionIdMaxValue;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCompileErr *) {
return IrInstructionIdCompileErr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionErrName *) {
return IrInstructionIdErrName;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionEmbedFile *) {
return IrInstructionIdEmbedFile;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchg *) {
return IrInstructionIdCmpxchg;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionFence *) {
return IrInstructionIdFence;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionDivExact *) {
return IrInstructionIdDivExact;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionTruncate *) {
return IrInstructionIdTruncate;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionIntType *) {
return IrInstructionIdIntType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolNot *) {
return IrInstructionIdBoolNot;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAlloca *) {
return IrInstructionIdAlloca;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionMemset *) {
return IrInstructionIdMemset;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionMemcpy *) {
return IrInstructionIdMemcpy;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSlice *) {
return IrInstructionIdSlice;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionMemberCount *) {
return IrInstructionIdMemberCount;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionBreakpoint *) {
return IrInstructionIdBreakpoint;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionReturnAddress *) {
return IrInstructionIdReturnAddress;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameAddress *) {
return IrInstructionIdFrameAddress;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) {
return IrInstructionIdAlignOf;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionOverflowOp *) {
return IrInstructionIdOverflowOp;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionTestErr *) {
return IrInstructionIdTestErr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapErrCode *) {
return IrInstructionIdUnwrapErrCode;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapErrPayload *) {
return IrInstructionIdUnwrapErrPayload;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionMaybeWrap *) {
return IrInstructionIdMaybeWrap;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionErrWrapPayload *) {
return IrInstructionIdErrWrapPayload;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionErrWrapCode *) {
return IrInstructionIdErrWrapCode;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionFnProto *) {
return IrInstructionIdFnProto;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionTestComptime *) {
return IrInstructionIdTestComptime;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionInitEnum *) {
return IrInstructionIdInitEnum;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionPointerReinterpret *) {
return IrInstructionIdPointerReinterpret;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionWidenOrShorten *) {
return IrInstructionIdWidenOrShorten;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrToInt *) {
return IrInstructionIdPtrToInt;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToPtr *) {
return IrInstructionIdIntToPtr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToEnum *) {
return IrInstructionIdIntToEnum;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckSwitchProngs *) {
return IrInstructionIdCheckSwitchProngs;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionTestType *) {
return IrInstructionIdTestType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeName *) {
return IrInstructionIdTypeName;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCanImplicitCast *) {
return IrInstructionIdCanImplicitCast;
}
template<typename T>
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
T *special_instruction = allocate<T>(1);
special_instruction->base.id = ir_instruction_id(special_instruction);
special_instruction->base.scope = scope;
special_instruction->base.source_node = source_node;
special_instruction->base.debug_id = exec_next_debug_id(irb->exec);
special_instruction->base.owner_bb = irb->current_basic_block;
return special_instruction;
}
template<typename T>
static T *ir_build_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
T *special_instruction = ir_create_instruction<T>(irb, scope, source_node);
ir_instruction_append(irb->current_basic_block, &special_instruction->base);
return special_instruction;
}
static IrInstruction *ir_build_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, TypeTableEntry *dest_type,
IrInstruction *value, CastOp cast_op)
{
IrInstructionCast *cast_instruction = ir_build_instruction<IrInstructionCast>(irb, scope, source_node);
cast_instruction->dest_type = dest_type;
cast_instruction->value = value;
cast_instruction->cast_op = cast_op;
ir_ref_instruction(value, irb->current_basic_block);
return &cast_instruction->base;
}
static IrInstruction *ir_build_cond_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *condition,
IrBasicBlock *then_block, IrBasicBlock *else_block, IrInstruction *is_comptime)
{
IrInstructionCondBr *cond_br_instruction = ir_build_instruction<IrInstructionCondBr>(irb, scope, source_node);
cond_br_instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable;
cond_br_instruction->base.value.special = ConstValSpecialStatic;
cond_br_instruction->condition = condition;
cond_br_instruction->then_block = then_block;
cond_br_instruction->else_block = else_block;
cond_br_instruction->is_comptime = is_comptime;
ir_ref_instruction(condition, irb->current_basic_block);
ir_ref_bb(then_block);
ir_ref_bb(else_block);
if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block);
return &cond_br_instruction->base;
}
static IrInstruction *ir_build_cond_br_from(IrBuilder *irb, IrInstruction *old_instruction,
IrInstruction *condition, IrBasicBlock *then_block, IrBasicBlock *else_block, IrInstruction *is_comptime)
{
IrInstruction *new_instruction = ir_build_cond_br(irb, old_instruction->scope, old_instruction->source_node,
condition, then_block, else_block, is_comptime);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_return(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *return_value) {
IrInstructionReturn *return_instruction = ir_build_instruction<IrInstructionReturn>(irb, scope, source_node);
return_instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable;
return_instruction->base.value.special = ConstValSpecialStatic;
return_instruction->value = return_value;
ir_ref_instruction(return_value, irb->current_basic_block);
return &return_instruction->base;
}
static IrInstruction *ir_build_return_from(IrBuilder *irb, IrInstruction *old_instruction,
IrInstruction *return_value)
{
IrInstruction *new_instruction = ir_build_return(irb, old_instruction->scope, old_instruction->source_node, return_value);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_create_const(IrBuilder *irb, Scope *scope, AstNode *source_node,
TypeTableEntry *type_entry, bool depends_on_compile_var)
{
assert(type_entry);
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(irb, scope, source_node);
const_instruction->base.value.type = type_entry;
const_instruction->base.value.special = ConstValSpecialStatic;
const_instruction->base.value.depends_on_compile_var = depends_on_compile_var;
return &const_instruction->base;
}
static IrInstruction *ir_build_const_void(IrBuilder *irb, Scope *scope, AstNode *source_node) {
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
const_instruction->base.value.type = irb->codegen->builtin_types.entry_void;
const_instruction->base.value.special = ConstValSpecialStatic;
return &const_instruction->base;
}
static IrInstruction *ir_build_const_undefined(IrBuilder *irb, Scope *scope, AstNode *source_node) {
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
const_instruction->base.value.special = ConstValSpecialUndef;
const_instruction->base.value.type = irb->codegen->builtin_types.entry_undef;
return &const_instruction->base;
}
static IrInstruction *ir_build_const_uint(IrBuilder *irb, Scope *scope, AstNode *source_node, uint64_t value) {
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
const_instruction->base.value.type = irb->codegen->builtin_types.entry_num_lit_int;
const_instruction->base.value.special = ConstValSpecialStatic;
bignum_init_unsigned(&const_instruction->base.value.data.x_bignum, value);
return &const_instruction->base;
}
static IrInstruction *ir_build_const_bignum(IrBuilder *irb, Scope *scope, AstNode *source_node, BigNum *bignum) {
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
const_instruction->base.value.type = (bignum->kind == BigNumKindInt) ?
irb->codegen->builtin_types.entry_num_lit_int : irb->codegen->builtin_types.entry_num_lit_float;
const_instruction->base.value.special = ConstValSpecialStatic;
const_instruction->base.value.data.x_bignum = *bignum;
return &const_instruction->base;
}
static IrInstruction *ir_build_const_null(IrBuilder *irb, Scope *scope, AstNode *source_node) {
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
const_instruction->base.value.type = irb->codegen->builtin_types.entry_null;
const_instruction->base.value.special = ConstValSpecialStatic;
return &const_instruction->base;
}
static IrInstruction *ir_build_const_usize(IrBuilder *irb, Scope *scope, AstNode *source_node, uint64_t value) {
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
const_instruction->base.value.type = irb->codegen->builtin_types.entry_usize;
const_instruction->base.value.special = ConstValSpecialStatic;
bignum_init_unsigned(&const_instruction->base.value.data.x_bignum, value);
return &const_instruction->base;
}
static IrInstruction *ir_create_const_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
TypeTableEntry *type_entry, bool depends_on_compile_var)
{
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(irb, scope, source_node);
const_instruction->base.value.type = irb->codegen->builtin_types.entry_type;
const_instruction->base.value.special = ConstValSpecialStatic;
const_instruction->base.value.depends_on_compile_var = depends_on_compile_var;
const_instruction->base.value.data.x_type = type_entry;
return &const_instruction->base;
}
static IrInstruction *ir_build_const_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
TypeTableEntry *type_entry, bool depends_on_compile_var)
{
IrInstruction *instruction = ir_create_const_type(irb, scope, source_node, type_entry, depends_on_compile_var);
ir_instruction_append(irb->current_basic_block, instruction);
return instruction;
}
static IrInstruction *ir_build_const_fn(IrBuilder *irb, Scope *scope, AstNode *source_node, FnTableEntry *fn_entry) {
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
const_instruction->base.value.type = fn_entry->type_entry;
const_instruction->base.value.special = ConstValSpecialStatic;
const_instruction->base.value.data.x_fn = fn_entry;
return &const_instruction->base;
}
static IrInstruction *ir_build_const_import(IrBuilder *irb, Scope *scope, AstNode *source_node, ImportTableEntry *import) {
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
const_instruction->base.value.type = irb->codegen->builtin_types.entry_namespace;
const_instruction->base.value.special = ConstValSpecialStatic;
const_instruction->base.value.data.x_import = import;
return &const_instruction->base;
}
static IrInstruction *ir_build_const_scope(IrBuilder *irb, Scope *parent_scope, AstNode *source_node,
Scope *target_scope)
{
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, parent_scope, source_node);
const_instruction->base.value.type = irb->codegen->builtin_types.entry_block;
const_instruction->base.value.special = ConstValSpecialStatic;
const_instruction->base.value.data.x_block = target_scope;
return &const_instruction->base;
}
static IrInstruction *ir_build_const_bool(IrBuilder *irb, Scope *scope, AstNode *source_node, bool value) {
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
const_instruction->base.value.type = irb->codegen->builtin_types.entry_bool;
const_instruction->base.value.special = ConstValSpecialStatic;
const_instruction->base.value.data.x_bool = value;
return &const_instruction->base;
}
static IrInstruction *ir_build_const_bound_fn(IrBuilder *irb, Scope *scope, AstNode *source_node,
FnTableEntry *fn_entry, IrInstruction *first_arg, bool depends_on_compile_var)
{
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
const_instruction->base.value.type = get_bound_fn_type(irb->codegen, fn_entry);
const_instruction->base.value.special = ConstValSpecialStatic;
const_instruction->base.value.depends_on_compile_var = depends_on_compile_var;
const_instruction->base.value.data.x_bound_fn.fn = fn_entry;
const_instruction->base.value.data.x_bound_fn.first_arg = first_arg;
return &const_instruction->base;
}
static IrInstruction *ir_create_const_str_lit(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *str) {
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(irb, scope, source_node);
init_const_str_lit(irb->codegen, &const_instruction->base.value, str);
return &const_instruction->base;
}
static IrInstruction *ir_build_const_str_lit(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *str) {
IrInstruction *instruction = ir_create_const_str_lit(irb, scope, source_node, str);
ir_instruction_append(irb->current_basic_block, instruction);
return instruction;
}
static IrInstruction *ir_build_const_c_str_lit(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *str) {
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
init_const_c_str_lit(irb->codegen, &const_instruction->base.value, str);
return &const_instruction->base;
}
static IrInstruction *ir_build_bin_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrBinOp op_id,
IrInstruction *op1, IrInstruction *op2, bool safety_check_on)
{
IrInstructionBinOp *bin_op_instruction = ir_build_instruction<IrInstructionBinOp>(irb, scope, source_node);
bin_op_instruction->op_id = op_id;
bin_op_instruction->op1 = op1;
bin_op_instruction->op2 = op2;
bin_op_instruction->safety_check_on = safety_check_on;
ir_ref_instruction(op1, irb->current_basic_block);
ir_ref_instruction(op2, irb->current_basic_block);
return &bin_op_instruction->base;
}
static IrInstruction *ir_build_bin_op_from(IrBuilder *irb, IrInstruction *old_instruction, IrBinOp op_id,
IrInstruction *op1, IrInstruction *op2, bool safety_check_on)
{
IrInstruction *new_instruction = ir_build_bin_op(irb, old_instruction->scope,
old_instruction->source_node, op_id, op1, op2, safety_check_on);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_var_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
VariableTableEntry *var, bool is_const)
{
IrInstructionVarPtr *instruction = ir_build_instruction<IrInstructionVarPtr>(irb, scope, source_node);
instruction->var = var;
instruction->is_const = is_const;
ir_ref_var(var);
return &instruction->base;
}
static IrInstruction *ir_build_var_ptr_from(IrBuilder *irb, IrInstruction *old_instruction,
VariableTableEntry *var, bool is_const)
{
IrInstruction *new_instruction = ir_build_var_ptr(irb, old_instruction->scope,
old_instruction->source_node, var, is_const);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_elem_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *array_ptr,
IrInstruction *elem_index, bool safety_check_on)
{
IrInstructionElemPtr *instruction = ir_build_instruction<IrInstructionElemPtr>(irb, scope, source_node);
instruction->array_ptr = array_ptr;
instruction->elem_index = elem_index;
instruction->safety_check_on = safety_check_on;
ir_ref_instruction(array_ptr, irb->current_basic_block);
ir_ref_instruction(elem_index, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_elem_ptr_from(IrBuilder *irb, IrInstruction *old_instruction,
IrInstruction *array_ptr, IrInstruction *elem_index, bool safety_check_on)
{
IrInstruction *new_instruction = ir_build_elem_ptr(irb, old_instruction->scope,
old_instruction->source_node, array_ptr, elem_index, safety_check_on);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *container_ptr, Buf *field_name)
{
IrInstructionFieldPtr *instruction = ir_build_instruction<IrInstructionFieldPtr>(irb, scope, source_node);
instruction->container_ptr = container_ptr;
instruction->field_name = field_name;
ir_ref_instruction(container_ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_struct_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *struct_ptr, TypeStructField *field)
{
IrInstructionStructFieldPtr *instruction = ir_build_instruction<IrInstructionStructFieldPtr>(irb, scope, source_node);
instruction->struct_ptr = struct_ptr;
instruction->field = field;
ir_ref_instruction(struct_ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_struct_field_ptr_from(IrBuilder *irb, IrInstruction *old_instruction,
IrInstruction *struct_ptr, TypeStructField *type_struct_field)
{
IrInstruction *new_instruction = ir_build_struct_field_ptr(irb, old_instruction->scope,
old_instruction->source_node, struct_ptr, type_struct_field);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_enum_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *enum_ptr, TypeEnumField *field)
{
IrInstructionEnumFieldPtr *instruction = ir_build_instruction<IrInstructionEnumFieldPtr>(irb, scope, source_node);
instruction->enum_ptr = enum_ptr;
instruction->field = field;
ir_ref_instruction(enum_ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_enum_field_ptr_from(IrBuilder *irb, IrInstruction *old_instruction,
IrInstruction *enum_ptr, TypeEnumField *type_enum_field)
{
IrInstruction *new_instruction = ir_build_enum_field_ptr(irb, old_instruction->scope,
old_instruction->source_node, enum_ptr, type_enum_field);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node,
FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,
bool is_comptime)
{
IrInstructionCall *call_instruction = ir_build_instruction<IrInstructionCall>(irb, scope, source_node);
call_instruction->fn_entry = fn_entry;
call_instruction->fn_ref = fn_ref;
call_instruction->is_comptime = is_comptime;
call_instruction->args = args;
call_instruction->arg_count = arg_count;
if (fn_ref)
ir_ref_instruction(fn_ref, irb->current_basic_block);
for (size_t i = 0; i < arg_count; i += 1)
ir_ref_instruction(args[i], irb->current_basic_block);
return &call_instruction->base;
}
static IrInstruction *ir_build_call_from(IrBuilder *irb, IrInstruction *old_instruction,
FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,
bool is_comptime)
{
IrInstruction *new_instruction = ir_build_call(irb, old_instruction->scope,
old_instruction->source_node, fn_entry, fn_ref, arg_count, args, is_comptime);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_phi(IrBuilder *irb, Scope *scope, AstNode *source_node,
size_t incoming_count, IrBasicBlock **incoming_blocks, IrInstruction **incoming_values)
{
assert(incoming_count != 0);
assert(incoming_count != SIZE_MAX);
IrInstructionPhi *phi_instruction = ir_build_instruction<IrInstructionPhi>(irb, scope, source_node);
phi_instruction->incoming_count = incoming_count;
phi_instruction->incoming_blocks = incoming_blocks;
phi_instruction->incoming_values = incoming_values;
for (size_t i = 0; i < incoming_count; i += 1) {
ir_ref_bb(incoming_blocks[i]);
ir_ref_instruction(incoming_values[i], irb->current_basic_block);
}
return &phi_instruction->base;
}
static IrInstruction *ir_build_phi_from(IrBuilder *irb, IrInstruction *old_instruction,
size_t incoming_count, IrBasicBlock **incoming_blocks, IrInstruction **incoming_values)
{
IrInstruction *new_instruction = ir_build_phi(irb, old_instruction->scope, old_instruction->source_node,
incoming_count, incoming_blocks, incoming_values);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_create_br(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrBasicBlock *dest_block, IrInstruction *is_comptime)
{
IrInstructionBr *br_instruction = ir_create_instruction<IrInstructionBr>(irb, scope, source_node);
br_instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable;
br_instruction->base.value.special = ConstValSpecialStatic;
br_instruction->dest_block = dest_block;
br_instruction->is_comptime = is_comptime;
ir_ref_bb(dest_block);
if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block);
return &br_instruction->base;
}
static IrInstruction *ir_build_br(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrBasicBlock *dest_block, IrInstruction *is_comptime)
{
IrInstruction *instruction = ir_create_br(irb, scope, source_node, dest_block, is_comptime);
ir_instruction_append(irb->current_basic_block, instruction);
return instruction;
}
static IrInstruction *ir_build_br_from(IrBuilder *irb, IrInstruction *old_instruction, IrBasicBlock *dest_block) {
IrInstruction *new_instruction = ir_build_br(irb, old_instruction->scope, old_instruction->source_node, dest_block, nullptr);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_un_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrUnOp op_id, IrInstruction *value) {
IrInstructionUnOp *br_instruction = ir_build_instruction<IrInstructionUnOp>(irb, scope, source_node);
br_instruction->op_id = op_id;
br_instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &br_instruction->base;
}
static IrInstruction *ir_build_un_op_from(IrBuilder *irb, IrInstruction *old_instruction,
IrUnOp op_id, IrInstruction *value)
{
IrInstruction *new_instruction = ir_build_un_op(irb, old_instruction->scope,
old_instruction->source_node, op_id, value);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_container_init_list(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *container_type, size_t item_count, IrInstruction **items)
{
IrInstructionContainerInitList *container_init_list_instruction =
ir_build_instruction<IrInstructionContainerInitList>(irb, scope, source_node);
container_init_list_instruction->container_type = container_type;
container_init_list_instruction->item_count = item_count;
container_init_list_instruction->items = items;
ir_ref_instruction(container_type, irb->current_basic_block);
for (size_t i = 0; i < item_count; i += 1) {
ir_ref_instruction(items[i], irb->current_basic_block);
}
return &container_init_list_instruction->base;
}
static IrInstruction *ir_build_container_init_list_from(IrBuilder *irb, IrInstruction *old_instruction,
IrInstruction *container_type, size_t item_count, IrInstruction **items)
{
IrInstruction *new_instruction = ir_build_container_init_list(irb, old_instruction->scope,
old_instruction->source_node, container_type, item_count, items);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_container_init_fields(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *container_type, size_t field_count, IrInstructionContainerInitFieldsField *fields)
{
IrInstructionContainerInitFields *container_init_fields_instruction =
ir_build_instruction<IrInstructionContainerInitFields>(irb, scope, source_node);
container_init_fields_instruction->container_type = container_type;
container_init_fields_instruction->field_count = field_count;
container_init_fields_instruction->fields = fields;
ir_ref_instruction(container_type, irb->current_basic_block);
for (size_t i = 0; i < field_count; i += 1) {
ir_ref_instruction(fields[i].value, irb->current_basic_block);
}
return &container_init_fields_instruction->base;
}
static IrInstruction *ir_build_struct_init(IrBuilder *irb, Scope *scope, AstNode *source_node,
TypeTableEntry *struct_type, size_t field_count, IrInstructionStructInitField *fields)
{
IrInstructionStructInit *struct_init_instruction = ir_build_instruction<IrInstructionStructInit>(irb, scope, source_node);
struct_init_instruction->struct_type = struct_type;
struct_init_instruction->field_count = field_count;
struct_init_instruction->fields = fields;
for (size_t i = 0; i < field_count; i += 1)
ir_ref_instruction(fields[i].value, irb->current_basic_block);
return &struct_init_instruction->base;
}
static IrInstruction *ir_build_struct_init_from(IrBuilder *irb, IrInstruction *old_instruction,
TypeTableEntry *struct_type, size_t field_count, IrInstructionStructInitField *fields)
{
IrInstruction *new_instruction = ir_build_struct_init(irb, old_instruction->scope,
old_instruction->source_node, struct_type, field_count, fields);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node) {
IrInstructionUnreachable *unreachable_instruction =
ir_build_instruction<IrInstructionUnreachable>(irb, scope, source_node);
unreachable_instruction->base.value.special = ConstValSpecialStatic;
unreachable_instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable;
return &unreachable_instruction->base;
}
static IrInstruction *ir_build_unreachable_from(IrBuilder *irb, IrInstruction *old_instruction) {
IrInstruction *new_instruction = ir_build_unreachable(irb, old_instruction->scope, old_instruction->source_node);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_store_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *ptr, IrInstruction *value)
{
IrInstructionStorePtr *instruction = ir_build_instruction<IrInstructionStorePtr>(irb, scope, source_node);
instruction->base.value.special = ConstValSpecialStatic;
instruction->base.value.type = irb->codegen->builtin_types.entry_void;
instruction->ptr = ptr;
instruction->value = value;
ir_ref_instruction(ptr, irb->current_basic_block);
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_store_ptr_from(IrBuilder *irb, IrInstruction *old_instruction,
IrInstruction *ptr, IrInstruction *value)
{
IrInstruction *new_instruction = ir_build_store_ptr(irb, old_instruction->scope,
old_instruction->source_node, ptr, value);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_var_decl(IrBuilder *irb, Scope *scope, AstNode *source_node,
VariableTableEntry *var, IrInstruction *var_type, IrInstruction *init_value)
{
IrInstructionDeclVar *decl_var_instruction = ir_build_instruction<IrInstructionDeclVar>(irb, scope, source_node);
decl_var_instruction->base.value.special = ConstValSpecialStatic;
decl_var_instruction->base.value.type = irb->codegen->builtin_types.entry_void;
decl_var_instruction->var = var;
decl_var_instruction->var_type = var_type;
decl_var_instruction->init_value = init_value;
if (var_type) ir_ref_instruction(var_type, irb->current_basic_block);
ir_ref_instruction(init_value, irb->current_basic_block);
return &decl_var_instruction->base;
}
static IrInstruction *ir_build_var_decl_from(IrBuilder *irb, IrInstruction *old_instruction,
VariableTableEntry *var, IrInstruction *var_type, IrInstruction *init_value)
{
IrInstruction *new_instruction = ir_build_var_decl(irb, old_instruction->scope,
old_instruction->source_node, var, var_type, init_value);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_load_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr) {
IrInstructionLoadPtr *instruction = ir_build_instruction<IrInstructionLoadPtr>(irb, scope, source_node);
instruction->ptr = ptr;
ir_ref_instruction(ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_load_ptr_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *ptr) {
IrInstruction *new_instruction = ir_build_load_ptr(irb, old_instruction->scope,
old_instruction->source_node, ptr);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_typeof(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionTypeOf *instruction = ir_build_instruction<IrInstructionTypeOf>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_to_ptr_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionToPtrType *instruction = ir_build_instruction<IrInstructionToPtrType>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_ptr_type_child(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *value)
{
IrInstructionPtrTypeChild *instruction = ir_build_instruction<IrInstructionPtrTypeChild>(
irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_set_fn_test(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *fn_value)
{
IrInstructionSetFnTest *instruction = ir_build_instruction<IrInstructionSetFnTest>(irb, scope, source_node);
instruction->fn_value = fn_value;
ir_ref_instruction(fn_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_set_fn_visible(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *fn_value,
IrInstruction *is_visible)
{
IrInstructionSetFnVisible *instruction = ir_build_instruction<IrInstructionSetFnVisible>(irb, scope, source_node);
instruction->fn_value = fn_value;
instruction->is_visible = is_visible;
ir_ref_instruction(fn_value, irb->current_basic_block);
ir_ref_instruction(is_visible, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_set_debug_safety(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *scope_value, IrInstruction *debug_safety_on)
{
IrInstructionSetDebugSafety *instruction = ir_build_instruction<IrInstructionSetDebugSafety>(irb, scope, source_node);
instruction->scope_value = scope_value;
instruction->debug_safety_on = debug_safety_on;
ir_ref_instruction(scope_value, irb->current_basic_block);
ir_ref_instruction(debug_safety_on, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_array_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *size,
IrInstruction *child_type)
{
IrInstructionArrayType *instruction = ir_build_instruction<IrInstructionArrayType>(irb, scope, source_node);
instruction->size = size;
instruction->child_type = child_type;
ir_ref_instruction(size, irb->current_basic_block);
ir_ref_instruction(child_type, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_slice_type(IrBuilder *irb, Scope *scope, AstNode *source_node, bool is_const,
IrInstruction *child_type)
{
IrInstructionSliceType *instruction = ir_build_instruction<IrInstructionSliceType>(irb, scope, source_node);
instruction->is_const = is_const;
instruction->child_type = child_type;
ir_ref_instruction(child_type, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_asm(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction **input_list,
IrInstruction **output_types, VariableTableEntry **output_vars, size_t return_count, bool has_side_effects)
{
IrInstructionAsm *instruction = ir_build_instruction<IrInstructionAsm>(irb, scope, source_node);
instruction->input_list = input_list;
instruction->output_types = output_types;
instruction->output_vars = output_vars;
instruction->return_count = return_count;
instruction->has_side_effects = has_side_effects;
assert(source_node->type == NodeTypeAsmExpr);
for (size_t i = 0; i < source_node->data.asm_expr.output_list.length; i += 1) {
IrInstruction *output_type = output_types[i];
if (output_type) ir_ref_instruction(output_type, irb->current_basic_block);
}
for (size_t i = 0; i < source_node->data.asm_expr.input_list.length; i += 1) {
IrInstruction *input_value = input_list[i];
ir_ref_instruction(input_value, irb->current_basic_block);
}
return &instruction->base;
}
static IrInstruction *ir_build_asm_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction **input_list,
IrInstruction **output_types, VariableTableEntry **output_vars, size_t return_count, bool has_side_effects)
{
IrInstruction *new_instruction = ir_build_asm(irb, old_instruction->scope,
old_instruction->source_node, input_list, output_types, output_vars, return_count, has_side_effects);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_compile_var(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name) {
IrInstructionCompileVar *instruction = ir_build_instruction<IrInstructionCompileVar>(irb, scope, source_node);
instruction->name = name;
ir_ref_instruction(name, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_size_of(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) {
IrInstructionSizeOf *instruction = ir_build_instruction<IrInstructionSizeOf>(irb, scope, source_node);
instruction->type_value = type_value;
ir_ref_instruction(type_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_test_nonnull(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionTestNonNull *instruction = ir_build_instruction<IrInstructionTestNonNull>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_test_nonnull_from(IrBuilder *irb, IrInstruction *old_instruction,
IrInstruction *value)
{
IrInstruction *new_instruction = ir_build_test_nonnull(irb, old_instruction->scope,
old_instruction->source_node, value);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_unwrap_maybe(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value,
bool safety_check_on)
{
IrInstructionUnwrapMaybe *instruction = ir_build_instruction<IrInstructionUnwrapMaybe>(irb, scope, source_node);
instruction->value = value;
instruction->safety_check_on = safety_check_on;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_unwrap_maybe_from(IrBuilder *irb, IrInstruction *old_instruction,
IrInstruction *value, bool safety_check_on)
{
IrInstruction *new_instruction = ir_build_unwrap_maybe(irb, old_instruction->scope, old_instruction->source_node,
value, safety_check_on);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_maybe_wrap(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionMaybeWrap *instruction = ir_build_instruction<IrInstructionMaybeWrap>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_err_wrap_payload(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionErrWrapPayload *instruction = ir_build_instruction<IrInstructionErrWrapPayload>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_err_wrap_code(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionErrWrapCode *instruction = ir_build_instruction<IrInstructionErrWrapCode>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_clz(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionClz *instruction = ir_build_instruction<IrInstructionClz>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_clz_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) {
IrInstruction *new_instruction = ir_build_clz(irb, old_instruction->scope, old_instruction->source_node, value);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_ctz(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionCtz *instruction = ir_build_instruction<IrInstructionCtz>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_ctz_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) {
IrInstruction *new_instruction = ir_build_ctz(irb, old_instruction->scope, old_instruction->source_node, value);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target_value,
IrBasicBlock *else_block, size_t case_count, IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime)
{
IrInstructionSwitchBr *instruction = ir_build_instruction<IrInstructionSwitchBr>(irb, scope, source_node);
instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable;
instruction->base.value.special = ConstValSpecialStatic;
instruction->target_value = target_value;
instruction->else_block = else_block;
instruction->case_count = case_count;
instruction->cases = cases;
instruction->is_comptime = is_comptime;
ir_ref_instruction(target_value, irb->current_basic_block);
if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block);
ir_ref_bb(else_block);
for (size_t i = 0; i < case_count; i += 1) {
ir_ref_instruction(cases[i].value, irb->current_basic_block);
ir_ref_bb(cases[i].block);
}
return &instruction->base;
}
static IrInstruction *ir_build_switch_br_from(IrBuilder *irb, IrInstruction *old_instruction,
IrInstruction *target_value, IrBasicBlock *else_block, size_t case_count,
IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime)
{
IrInstruction *new_instruction = ir_build_switch_br(irb, old_instruction->scope, old_instruction->source_node,
target_value, else_block, case_count, cases, is_comptime);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_switch_target(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *target_value_ptr)
{
IrInstructionSwitchTarget *instruction = ir_build_instruction<IrInstructionSwitchTarget>(irb, scope, source_node);
instruction->target_value_ptr = target_value_ptr;
ir_ref_instruction(target_value_ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_switch_var(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *target_value_ptr, IrInstruction *prong_value)
{
IrInstructionSwitchVar *instruction = ir_build_instruction<IrInstructionSwitchVar>(irb, scope, source_node);
instruction->target_value_ptr = target_value_ptr;
instruction->prong_value = prong_value;
ir_ref_instruction(target_value_ptr, irb->current_basic_block);
ir_ref_instruction(prong_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_enum_tag(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionEnumTag *instruction = ir_build_instruction<IrInstructionEnumTag>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_enum_tag_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) {
IrInstruction *new_instruction = ir_build_enum_tag(irb, old_instruction->scope,
old_instruction->source_node, value);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_generated_code(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *value)
{
IrInstructionGeneratedCode *instruction = ir_build_instruction<IrInstructionGeneratedCode>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_import(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name) {
IrInstructionImport *instruction = ir_build_instruction<IrInstructionImport>(irb, scope, source_node);
instruction->name = name;
ir_ref_instruction(name, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_array_len(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *array_value) {
IrInstructionArrayLen *instruction = ir_build_instruction<IrInstructionArrayLen>(irb, scope, source_node);
instruction->array_value = array_value;
ir_ref_instruction(array_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_ref(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value,
bool is_const)
{
IrInstructionRef *instruction = ir_build_instruction<IrInstructionRef>(irb, scope, source_node);
instruction->value = value;
instruction->is_const = is_const;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_ref_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value,
bool is_const)
{
IrInstruction *new_instruction = ir_build_ref(irb, old_instruction->scope, old_instruction->source_node,
value, is_const);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_min_value(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionMinValue *instruction = ir_build_instruction<IrInstructionMinValue>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_max_value(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionMaxValue *instruction = ir_build_instruction<IrInstructionMaxValue>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_compile_err(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *msg) {
IrInstructionCompileErr *instruction = ir_build_instruction<IrInstructionCompileErr>(irb, scope, source_node);
instruction->msg = msg;
ir_ref_instruction(msg, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_err_name(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionErrName *instruction = ir_build_instruction<IrInstructionErrName>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_err_name_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) {
IrInstruction *new_instruction = ir_build_err_name(irb, old_instruction->scope,
old_instruction->source_node, value);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_c_import(IrBuilder *irb, Scope *scope, AstNode *source_node) {
IrInstructionCImport *instruction = ir_build_instruction<IrInstructionCImport>(irb, scope, source_node);
return &instruction->base;
}
static IrInstruction *ir_build_c_include(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name) {
IrInstructionCInclude *instruction = ir_build_instruction<IrInstructionCInclude>(irb, scope, source_node);
instruction->name = name;
ir_ref_instruction(name, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_c_define(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name, IrInstruction *value) {
IrInstructionCDefine *instruction = ir_build_instruction<IrInstructionCDefine>(irb, scope, source_node);
instruction->name = name;
instruction->value = value;
ir_ref_instruction(name, irb->current_basic_block);
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_c_undef(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name) {
IrInstructionCUndef *instruction = ir_build_instruction<IrInstructionCUndef>(irb, scope, source_node);
instruction->name = name;
ir_ref_instruction(name, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_embed_file(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name) {
IrInstructionEmbedFile *instruction = ir_build_instruction<IrInstructionEmbedFile>(irb, scope, source_node);
instruction->name = name;
ir_ref_instruction(name, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr,
IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value,
AtomicOrder success_order, AtomicOrder failure_order)
{
IrInstructionCmpxchg *instruction = ir_build_instruction<IrInstructionCmpxchg>(irb, scope, source_node);
instruction->ptr = ptr;
instruction->cmp_value = cmp_value;
instruction->new_value = new_value;
instruction->success_order_value = success_order_value;
instruction->failure_order_value = failure_order_value;
instruction->success_order = success_order;
instruction->failure_order = failure_order;
ir_ref_instruction(ptr, irb->current_basic_block);
ir_ref_instruction(cmp_value, irb->current_basic_block);
ir_ref_instruction(new_value, irb->current_basic_block);
ir_ref_instruction(success_order_value, irb->current_basic_block);
ir_ref_instruction(failure_order_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_cmpxchg_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *ptr,
IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value,
AtomicOrder success_order, AtomicOrder failure_order)
{
IrInstruction *new_instruction = ir_build_cmpxchg(irb, old_instruction->scope, old_instruction->source_node,
ptr, cmp_value, new_value, success_order_value, failure_order_value, success_order, failure_order);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_fence(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *order_value, AtomicOrder order) {
IrInstructionFence *instruction = ir_build_instruction<IrInstructionFence>(irb, scope, source_node);
instruction->order_value = order_value;
instruction->order = order;
ir_ref_instruction(order_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_fence_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *order_value, AtomicOrder order) {
IrInstruction *new_instruction = ir_build_fence(irb, old_instruction->scope, old_instruction->source_node, order_value, order);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_div_exact(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *op1, IrInstruction *op2) {
IrInstructionDivExact *instruction = ir_build_instruction<IrInstructionDivExact>(irb, scope, source_node);
instruction->op1 = op1;
instruction->op2 = op2;
ir_ref_instruction(op1, irb->current_basic_block);
ir_ref_instruction(op2, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_div_exact_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *op1, IrInstruction *op2) {
IrInstruction *new_instruction = ir_build_div_exact(irb, old_instruction->scope, old_instruction->source_node, op1, op2);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_truncate(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) {
IrInstructionTruncate *instruction = ir_build_instruction<IrInstructionTruncate>(irb, scope, source_node);
instruction->dest_type = dest_type;
instruction->target = target;
ir_ref_instruction(dest_type, irb->current_basic_block);
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_truncate_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *dest_type, IrInstruction *target) {
IrInstruction *new_instruction = ir_build_truncate(irb, old_instruction->scope, old_instruction->source_node, dest_type, target);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_int_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *is_signed, IrInstruction *bit_count) {
IrInstructionIntType *instruction = ir_build_instruction<IrInstructionIntType>(irb, scope, source_node);
instruction->is_signed = is_signed;
instruction->bit_count = bit_count;
ir_ref_instruction(is_signed, irb->current_basic_block);
ir_ref_instruction(bit_count, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_bool_not(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionBoolNot *instruction = ir_build_instruction<IrInstructionBoolNot>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_bool_not_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) {
IrInstruction *new_instruction = ir_build_bool_not(irb, old_instruction->scope, old_instruction->source_node, value);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_alloca(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *type_value, IrInstruction *count)
{
IrInstructionAlloca *instruction = ir_build_instruction<IrInstructionAlloca>(irb, scope, source_node);
instruction->type_value = type_value;
instruction->count = count;
ir_ref_instruction(type_value, irb->current_basic_block);
ir_ref_instruction(count, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_alloca_from(IrBuilder *irb, IrInstruction *old_instruction,
IrInstruction *type_value, IrInstruction *count)
{
IrInstruction *new_instruction = ir_build_alloca(irb, old_instruction->scope, old_instruction->source_node, type_value, count);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_memset(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *dest_ptr, IrInstruction *byte, IrInstruction *count)
{
IrInstructionMemset *instruction = ir_build_instruction<IrInstructionMemset>(irb, scope, source_node);
instruction->dest_ptr = dest_ptr;
instruction->byte = byte;
instruction->count = count;
ir_ref_instruction(dest_ptr, irb->current_basic_block);
ir_ref_instruction(byte, irb->current_basic_block);
ir_ref_instruction(count, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_memset_from(IrBuilder *irb, IrInstruction *old_instruction,
IrInstruction *dest_ptr, IrInstruction *byte, IrInstruction *count)
{
IrInstruction *new_instruction = ir_build_memset(irb, old_instruction->scope, old_instruction->source_node, dest_ptr, byte, count);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_memcpy(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *dest_ptr, IrInstruction *src_ptr, IrInstruction *count)
{
IrInstructionMemcpy *instruction = ir_build_instruction<IrInstructionMemcpy>(irb, scope, source_node);
instruction->dest_ptr = dest_ptr;
instruction->src_ptr = src_ptr;
instruction->count = count;
ir_ref_instruction(dest_ptr, irb->current_basic_block);
ir_ref_instruction(src_ptr, irb->current_basic_block);
ir_ref_instruction(count, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_memcpy_from(IrBuilder *irb, IrInstruction *old_instruction,
IrInstruction *dest_ptr, IrInstruction *src_ptr, IrInstruction *count)
{
IrInstruction *new_instruction = ir_build_memcpy(irb, old_instruction->scope, old_instruction->source_node, dest_ptr, src_ptr, count);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_slice(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool is_const, bool safety_check_on)
{
IrInstructionSlice *instruction = ir_build_instruction<IrInstructionSlice>(irb, scope, source_node);
instruction->ptr = ptr;
instruction->start = start;
instruction->end = end;
instruction->is_const = is_const;
instruction->safety_check_on = safety_check_on;
ir_ref_instruction(ptr, irb->current_basic_block);
ir_ref_instruction(start, irb->current_basic_block);
if (end) ir_ref_instruction(end, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_slice_from(IrBuilder *irb, IrInstruction *old_instruction,
IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool is_const, bool safety_check_on)
{
IrInstruction *new_instruction = ir_build_slice(irb, old_instruction->scope,
old_instruction->source_node, ptr, start, end, is_const, safety_check_on);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_member_count(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *container) {
IrInstructionMemberCount *instruction = ir_build_instruction<IrInstructionMemberCount>(irb, scope, source_node);
instruction->container = container;
ir_ref_instruction(container, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_breakpoint(IrBuilder *irb, Scope *scope, AstNode *source_node) {
IrInstructionBreakpoint *instruction = ir_build_instruction<IrInstructionBreakpoint>(irb, scope, source_node);
return &instruction->base;
}
static IrInstruction *ir_build_breakpoint_from(IrBuilder *irb, IrInstruction *old_instruction) {
IrInstruction *new_instruction = ir_build_breakpoint(irb, old_instruction->scope, old_instruction->source_node);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_return_address(IrBuilder *irb, Scope *scope, AstNode *source_node) {
IrInstructionReturnAddress *instruction = ir_build_instruction<IrInstructionReturnAddress>(irb, scope, source_node);
return &instruction->base;
}
static IrInstruction *ir_build_return_address_from(IrBuilder *irb, IrInstruction *old_instruction) {
IrInstruction *new_instruction = ir_build_return_address(irb, old_instruction->scope, old_instruction->source_node);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_frame_address(IrBuilder *irb, Scope *scope, AstNode *source_node) {
IrInstructionFrameAddress *instruction = ir_build_instruction<IrInstructionFrameAddress>(irb, scope, source_node);
return &instruction->base;
}
static IrInstruction *ir_build_frame_address_from(IrBuilder *irb, IrInstruction *old_instruction) {
IrInstruction *new_instruction = ir_build_frame_address(irb, old_instruction->scope, old_instruction->source_node);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2,
IrInstruction *result_ptr, TypeTableEntry *result_ptr_type)
{
IrInstructionOverflowOp *instruction = ir_build_instruction<IrInstructionOverflowOp>(irb, scope, source_node);
instruction->op = op;
instruction->type_value = type_value;
instruction->op1 = op1;
instruction->op2 = op2;
instruction->result_ptr = result_ptr;
instruction->result_ptr_type = result_ptr_type;
ir_ref_instruction(type_value, irb->current_basic_block);
ir_ref_instruction(op1, irb->current_basic_block);
ir_ref_instruction(op2, irb->current_basic_block);
ir_ref_instruction(result_ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_overflow_op_from(IrBuilder *irb, IrInstruction *old_instruction,
IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2,
IrInstruction *result_ptr, TypeTableEntry *result_ptr_type)
{
IrInstruction *new_instruction = ir_build_overflow_op(irb, old_instruction->scope, old_instruction->source_node,
op, type_value, op1, op2, result_ptr, result_ptr_type);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_alignof(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) {
IrInstructionAlignOf *instruction = ir_build_instruction<IrInstructionAlignOf>(irb, scope, source_node);
instruction->type_value = type_value;
ir_ref_instruction(type_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_test_err(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *value)
{
IrInstructionTestErr *instruction = ir_build_instruction<IrInstructionTestErr>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_test_err_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) {
IrInstruction *new_instruction = ir_build_test_err(irb, old_instruction->scope, old_instruction->source_node,
value);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_unwrap_err_code(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *value)
{
IrInstructionUnwrapErrCode *instruction = ir_build_instruction<IrInstructionUnwrapErrCode>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_unwrap_err_code_from(IrBuilder *irb, IrInstruction *old_instruction,
IrInstruction *value)
{
IrInstruction *new_instruction = ir_build_unwrap_err_code(irb, old_instruction->scope,
old_instruction->source_node, value);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_unwrap_err_payload(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *value, bool safety_check_on)
{
IrInstructionUnwrapErrPayload *instruction = ir_build_instruction<IrInstructionUnwrapErrPayload>(irb, scope, source_node);
instruction->value = value;
instruction->safety_check_on = safety_check_on;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_unwrap_err_payload_from(IrBuilder *irb, IrInstruction *old_instruction,
IrInstruction *value, bool safety_check_on)
{
IrInstruction *new_instruction = ir_build_unwrap_err_payload(irb, old_instruction->scope,
old_instruction->source_node, value, safety_check_on);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction **param_types, IrInstruction *return_type)
{
IrInstructionFnProto *instruction = ir_build_instruction<IrInstructionFnProto>(irb, scope, source_node);
instruction->param_types = param_types;
instruction->return_type = return_type;
assert(source_node->type == NodeTypeFnProto);
for (size_t i = 0; i < source_node->data.fn_proto.params.length; i += 1) {
ir_ref_instruction(param_types[i], irb->current_basic_block);
}
ir_ref_instruction(return_type, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_test_comptime(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionTestComptime *instruction = ir_build_instruction<IrInstructionTestComptime>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_init_enum(IrBuilder *irb, Scope *scope, AstNode *source_node,
TypeTableEntry *enum_type, TypeEnumField *field, IrInstruction *init_value)
{
IrInstructionInitEnum *instruction = ir_build_instruction<IrInstructionInitEnum>(irb, scope, source_node);
instruction->enum_type = enum_type;
instruction->field = field;
instruction->init_value = init_value;
ir_ref_instruction(init_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_init_enum_from(IrBuilder *irb, IrInstruction *old_instruction,
TypeTableEntry *enum_type, TypeEnumField *field, IrInstruction *init_value)
{
IrInstruction *new_instruction = ir_build_init_enum(irb, old_instruction->scope, old_instruction->source_node,
enum_type, field, init_value);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_pointer_reinterpret(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *ptr)
{
IrInstructionPointerReinterpret *instruction = ir_build_instruction<IrInstructionPointerReinterpret>(
irb, scope, source_node);
instruction->ptr = ptr;
ir_ref_instruction(ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_widen_or_shorten(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *target)
{
IrInstructionWidenOrShorten *instruction = ir_build_instruction<IrInstructionWidenOrShorten>(
irb, scope, source_node);
instruction->target = target;
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_int_to_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *target)
{
IrInstructionIntToPtr *instruction = ir_build_instruction<IrInstructionIntToPtr>(
irb, scope, source_node);
instruction->target = target;
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_ptr_to_int(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *target)
{
IrInstructionPtrToInt *instruction = ir_build_instruction<IrInstructionPtrToInt>(
irb, scope, source_node);
instruction->target = target;
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_int_to_enum(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *target)
{
IrInstructionIntToEnum *instruction = ir_build_instruction<IrInstructionIntToEnum>(
irb, scope, source_node);
instruction->target = target;
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *target_value, IrInstructionCheckSwitchProngsRange *ranges, size_t range_count)
{
IrInstructionCheckSwitchProngs *instruction = ir_build_instruction<IrInstructionCheckSwitchProngs>(
irb, scope, source_node);
instruction->target_value = target_value;
instruction->ranges = ranges;
instruction->range_count = range_count;
ir_ref_instruction(target_value, irb->current_basic_block);
for (size_t i = 0; i < range_count; i += 1) {
ir_ref_instruction(ranges[i].start, irb->current_basic_block);
ir_ref_instruction(ranges[i].end, irb->current_basic_block);
}
return &instruction->base;
}
static IrInstruction *ir_build_test_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *type_value, TypeTableEntryId type_id)
{
IrInstructionTestType *instruction = ir_build_instruction<IrInstructionTestType>(
irb, scope, source_node);
instruction->type_value = type_value;
instruction->type_id = type_id;
ir_ref_instruction(type_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_type_name(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *type_value)
{
IrInstructionTypeName *instruction = ir_build_instruction<IrInstructionTypeName>(
irb, scope, source_node);
instruction->type_value = type_value;
ir_ref_instruction(type_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_can_implicit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *type_value, IrInstruction *target_value)
{
IrInstructionCanImplicitCast *instruction = ir_build_instruction<IrInstructionCanImplicitCast>(
irb, scope, source_node);
instruction->type_value = type_value;
instruction->target_value = target_value;
ir_ref_instruction(type_value, irb->current_basic_block);
ir_ref_instruction(target_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) {
return nullptr;
}
static IrInstruction *ir_instruction_condbr_get_dep(IrInstructionCondBr *instruction, size_t index) {
switch (index) {
case 0: return instruction->condition;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_switchbr_get_dep(IrInstructionSwitchBr *instruction, size_t index) {
switch (index) {
case 0: return instruction->target_value;
}
size_t case_index = index - 2;
if (case_index < instruction->case_count) return instruction->cases[case_index].value;
return nullptr;
}
static IrInstruction *ir_instruction_switchvar_get_dep(IrInstructionSwitchVar *instruction, size_t index) {
switch (index) {
case 0: return instruction->target_value_ptr;
case 1: return instruction->prong_value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_switchtarget_get_dep(IrInstructionSwitchTarget *instruction, size_t index) {
switch (index) {
case 0: return instruction->target_value_ptr;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_phi_get_dep(IrInstructionPhi *instruction, size_t index) {
if (index < instruction->incoming_count) return instruction->incoming_values[index];
return nullptr;
}
static IrInstruction *ir_instruction_unop_get_dep(IrInstructionUnOp *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_binop_get_dep(IrInstructionBinOp *instruction, size_t index) {
switch (index) {
case 0: return instruction->op1;
case 1: return instruction->op2;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_declvar_get_dep(IrInstructionDeclVar *instruction, size_t index) {
switch (index) {
case 0: return instruction->var_type;
case 1: return instruction->init_value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_loadptr_get_dep(IrInstructionLoadPtr *instruction, size_t index) {
switch (index) {
case 0: return instruction->ptr;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_storeptr_get_dep(IrInstructionStorePtr *instruction, size_t index) {
switch (index) {
case 0: return instruction->ptr;
case 1: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_fieldptr_get_dep(IrInstructionFieldPtr *instruction, size_t index) {
switch (index) {
case 0: return instruction->container_ptr;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_structfieldptr_get_dep(IrInstructionStructFieldPtr *instruction, size_t index) {
switch (index) {
case 0: return instruction->struct_ptr;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_enumfieldptr_get_dep(IrInstructionEnumFieldPtr *instruction, size_t index) {
switch (index) {
case 0: return instruction->enum_ptr;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_elemptr_get_dep(IrInstructionElemPtr *instruction, size_t index) {
switch (index) {
case 0: return instruction->array_ptr;
case 1: return instruction->elem_index;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_varptr_get_dep(IrInstructionVarPtr *instruction, size_t index) {
return nullptr;
}
static IrInstruction *ir_instruction_call_get_dep(IrInstructionCall *instruction, size_t index) {
if (index == 0) return instruction->fn_ref;
size_t arg_index = index - 1;
if (arg_index < instruction->arg_count) return instruction->args[arg_index];
return nullptr;
}
static IrInstruction *ir_instruction_const_get_dep(IrInstructionConst *instruction, size_t index) {
return nullptr;
}
static IrInstruction *ir_instruction_return_get_dep(IrInstructionReturn *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_cast_get_dep(IrInstructionCast *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_containerinitlist_get_dep(IrInstructionContainerInitList *instruction,
size_t index)
{
if (index == 0) return instruction->container_type;
size_t item_index = index - 1;
if (item_index < instruction->item_count) return instruction->items[item_index];
return nullptr;
}
static IrInstruction *ir_instruction_containerinitfields_get_dep(IrInstructionContainerInitFields *instruction,
size_t index)
{
if (index == 0) return instruction->container_type;
size_t field_index = index - 1;
if (field_index < instruction->field_count) return instruction->fields[field_index].value;
return nullptr;
}
static IrInstruction *ir_instruction_structinit_get_dep(IrInstructionStructInit *instruction, size_t index) {
if (index < instruction->field_count) return instruction->fields[index].value;
return nullptr;
}
static IrInstruction *ir_instruction_unreachable_get_dep(IrInstructionUnreachable *instruction, size_t index) {
return nullptr;
}
static IrInstruction *ir_instruction_typeof_get_dep(IrInstructionTypeOf *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_toptrtype_get_dep(IrInstructionToPtrType *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_ptrtypechild_get_dep(IrInstructionPtrTypeChild *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_setfntest_get_dep(IrInstructionSetFnTest *instruction, size_t index) {
switch (index) {
case 0: return instruction->fn_value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_setfnvisible_get_dep(IrInstructionSetFnVisible *instruction, size_t index) {
switch (index) {
case 0: return instruction->fn_value;
case 1: return instruction->is_visible;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_setdebugsafety_get_dep(IrInstructionSetDebugSafety *instruction, size_t index) {
switch (index) {
case 0: return instruction->scope_value;
case 1: return instruction->debug_safety_on;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_arraytype_get_dep(IrInstructionArrayType *instruction, size_t index) {
switch (index) {
case 0: return instruction->size;
case 1: return instruction->child_type;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_slicetype_get_dep(IrInstructionSliceType *instruction, size_t index) {
switch (index) {
case 0: return instruction->child_type;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_asm_get_dep(IrInstructionAsm *instruction, size_t index) {
AstNode *asm_node = instruction->base.source_node;
if (index < asm_node->data.asm_expr.output_list.length) return instruction->output_types[index];
size_t input_index = index - asm_node->data.asm_expr.output_list.length;
if (input_index < asm_node->data.asm_expr.input_list.length) return instruction->input_list[input_index];
return nullptr;
}
static IrInstruction *ir_instruction_compilevar_get_dep(IrInstructionCompileVar *instruction, size_t index) {
switch (index) {
case 0: return instruction->name;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_sizeof_get_dep(IrInstructionSizeOf *instruction, size_t index) {
switch (index) {
case 0: return instruction->type_value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_testnonnull_get_dep(IrInstructionTestNonNull *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_unwrapmaybe_get_dep(IrInstructionUnwrapMaybe *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_maybewrap_get_dep(IrInstructionMaybeWrap *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_enumtag_get_dep(IrInstructionEnumTag *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_clz_get_dep(IrInstructionClz *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_ctz_get_dep(IrInstructionCtz *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_generatedcode_get_dep(IrInstructionGeneratedCode *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_import_get_dep(IrInstructionImport *instruction, size_t index) {
switch (index) {
case 0: return instruction->name;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_cimport_get_dep(IrInstructionCImport *instruction, size_t index) {
return nullptr;
}
static IrInstruction *ir_instruction_cinclude_get_dep(IrInstructionCInclude *instruction, size_t index) {
switch (index) {
case 0: return instruction->name;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_cdefine_get_dep(IrInstructionCDefine *instruction, size_t index) {
switch (index) {
case 0: return instruction->name;
case 1: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_cundef_get_dep(IrInstructionCUndef *instruction, size_t index) {
switch (index) {
case 0: return instruction->name;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_arraylen_get_dep(IrInstructionArrayLen *instruction, size_t index) {
switch (index) {
case 0: return instruction->array_value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_ref_get_dep(IrInstructionRef *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_minvalue_get_dep(IrInstructionMinValue *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_maxvalue_get_dep(IrInstructionMaxValue *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_compileerr_get_dep(IrInstructionCompileErr *instruction, size_t index) {
switch (index) {
case 0: return instruction->msg;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_errname_get_dep(IrInstructionErrName *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_embedfile_get_dep(IrInstructionEmbedFile *instruction, size_t index) {
switch (index) {
case 0: return instruction->name;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_cmpxchg_get_dep(IrInstructionCmpxchg *instruction, size_t index) {
switch (index) {
case 0: return instruction->ptr;
case 1: return instruction->cmp_value;
case 2: return instruction->new_value;
case 3: return instruction->success_order_value;
case 4: return instruction->failure_order_value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_fence_get_dep(IrInstructionFence *instruction, size_t index) {
switch (index) {
case 0: return instruction->order_value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_divexact_get_dep(IrInstructionDivExact *instruction, size_t index) {
switch (index) {
case 0: return instruction->op1;
case 1: return instruction->op2;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_truncate_get_dep(IrInstructionTruncate *instruction, size_t index) {
switch (index) {
case 0: return instruction->dest_type;
case 1: return instruction->target;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_inttype_get_dep(IrInstructionIntType *instruction, size_t index) {
switch (index) {
case 0: return instruction->is_signed;
case 1: return instruction->bit_count;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_boolnot_get_dep(IrInstructionBoolNot *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_alloca_get_dep(IrInstructionAlloca *instruction, size_t index) {
switch (index) {
case 0: return instruction->type_value;
case 1: return instruction->count;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_memset_get_dep(IrInstructionMemset *instruction, size_t index) {
switch (index) {
case 0: return instruction->dest_ptr;
case 1: return instruction->byte;
case 2: return instruction->count;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_memcpy_get_dep(IrInstructionMemcpy *instruction, size_t index) {
switch (index) {
case 0: return instruction->dest_ptr;
case 1: return instruction->src_ptr;
case 2: return instruction->count;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_slice_get_dep(IrInstructionSlice *instruction, size_t index) {
switch (index) {
case 0: return instruction->ptr;
case 1: return instruction->start;
case 2: return instruction->end;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_membercount_get_dep(IrInstructionMemberCount *instruction, size_t index) {
switch (index) {
case 0: return instruction->container;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_breakpoint_get_dep(IrInstructionBreakpoint *instruction, size_t index) {
return nullptr;
}
static IrInstruction *ir_instruction_returnaddress_get_dep(IrInstructionReturnAddress *instruction, size_t index) {
return nullptr;
}
static IrInstruction *ir_instruction_frameaddress_get_dep(IrInstructionFrameAddress *instruction, size_t index) {
return nullptr;
}
static IrInstruction *ir_instruction_alignof_get_dep(IrInstructionAlignOf *instruction, size_t index) {
switch (index) {
case 0: return instruction->type_value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_overflowop_get_dep(IrInstructionOverflowOp *instruction, size_t index) {
switch (index) {
case 0: return instruction->type_value;
case 1: return instruction->op1;
case 2: return instruction->op2;
case 3: return instruction->result_ptr;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_testerr_get_dep(IrInstructionTestErr *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_unwraperrcode_get_dep(IrInstructionUnwrapErrCode *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_unwraperrpayload_get_dep(IrInstructionUnwrapErrPayload *instruction,
size_t index)
{
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_errwrapcode_get_dep(IrInstructionErrWrapCode *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_errwrappayload_get_dep(IrInstructionErrWrapPayload *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_fnproto_get_dep(IrInstructionFnProto *instruction, size_t index) {
if (index == 0) return instruction->return_type;
size_t param_index = index - 1;
if (param_index < instruction->base.source_node->data.fn_proto.params.length) {
return instruction->param_types[param_index];
}
return nullptr;
}
static IrInstruction *ir_instruction_testcomptime_get_dep(IrInstructionTestComptime *instruction, size_t index) {
switch (index) {
case 0: return instruction->value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_initenum_get_dep(IrInstructionInitEnum *instruction, size_t index) {
switch (index) {
case 0: return instruction->init_value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_pointerreinterpret_get_dep(IrInstructionPointerReinterpret *instruction,
size_t index)
{
switch (index) {
case 0: return instruction->ptr;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_widenorshorten_get_dep(IrInstructionWidenOrShorten *instruction, size_t index) {
switch (index) {
case 0: return instruction->target;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_inttoptr_get_dep(IrInstructionIntToPtr *instruction, size_t index) {
switch (index) {
case 0: return instruction->target;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_ptrtoint_get_dep(IrInstructionPtrToInt *instruction, size_t index) {
switch (index) {
case 0: return instruction->target;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_inttoenum_get_dep(IrInstructionIntToEnum *instruction, size_t index) {
switch (index) {
case 0: return instruction->target;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_checkswitchprongs_get_dep(IrInstructionCheckSwitchProngs *instruction,
size_t index)
{
if (index == 0) return instruction->target_value;
size_t range_index = index - 1;
if (range_index < instruction->range_count * 2) {
IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_index / 2];
return (range_index % 2 == 0) ? range->start : range->end;
}
return nullptr;
}
static IrInstruction *ir_instruction_testtype_get_dep(IrInstructionTestType *instruction, size_t index) {
switch (index) {
case 0: return instruction->type_value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_typename_get_dep(IrInstructionTypeName *instruction, size_t index) {
switch (index) {
case 0: return instruction->type_value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_canimplicitcast_get_dep(IrInstructionCanImplicitCast *instruction, size_t index) {
switch (index) {
case 0: return instruction->type_value;
case 1: return instruction->target_value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) {
switch (instruction->id) {
case IrInstructionIdInvalid:
zig_unreachable();
case IrInstructionIdBr:
return ir_instruction_br_get_dep((IrInstructionBr *) instruction, index);
case IrInstructionIdCondBr:
return ir_instruction_condbr_get_dep((IrInstructionCondBr *) instruction, index);
case IrInstructionIdSwitchBr:
return ir_instruction_switchbr_get_dep((IrInstructionSwitchBr *) instruction, index);
case IrInstructionIdSwitchVar:
return ir_instruction_switchvar_get_dep((IrInstructionSwitchVar *) instruction, index);
case IrInstructionIdSwitchTarget:
return ir_instruction_switchtarget_get_dep((IrInstructionSwitchTarget *) instruction, index);
case IrInstructionIdPhi:
return ir_instruction_phi_get_dep((IrInstructionPhi *) instruction, index);
case IrInstructionIdUnOp:
return ir_instruction_unop_get_dep((IrInstructionUnOp *) instruction, index);
case IrInstructionIdBinOp:
return ir_instruction_binop_get_dep((IrInstructionBinOp *) instruction, index);
case IrInstructionIdDeclVar:
return ir_instruction_declvar_get_dep((IrInstructionDeclVar *) instruction, index);
case IrInstructionIdLoadPtr:
return ir_instruction_loadptr_get_dep((IrInstructionLoadPtr *) instruction, index);
case IrInstructionIdStorePtr:
return ir_instruction_storeptr_get_dep((IrInstructionStorePtr *) instruction, index);
case IrInstructionIdFieldPtr:
return ir_instruction_fieldptr_get_dep((IrInstructionFieldPtr *) instruction, index);
case IrInstructionIdStructFieldPtr:
return ir_instruction_structfieldptr_get_dep((IrInstructionStructFieldPtr *) instruction, index);
case IrInstructionIdEnumFieldPtr:
return ir_instruction_enumfieldptr_get_dep((IrInstructionEnumFieldPtr *) instruction, index);
case IrInstructionIdElemPtr:
return ir_instruction_elemptr_get_dep((IrInstructionElemPtr *) instruction, index);
case IrInstructionIdVarPtr:
return ir_instruction_varptr_get_dep((IrInstructionVarPtr *) instruction, index);
case IrInstructionIdCall:
return ir_instruction_call_get_dep((IrInstructionCall *) instruction, index);
case IrInstructionIdConst:
return ir_instruction_const_get_dep((IrInstructionConst *) instruction, index);
case IrInstructionIdReturn:
return ir_instruction_return_get_dep((IrInstructionReturn *) instruction, index);
case IrInstructionIdCast:
return ir_instruction_cast_get_dep((IrInstructionCast *) instruction, index);
case IrInstructionIdContainerInitList:
return ir_instruction_containerinitlist_get_dep((IrInstructionContainerInitList *) instruction, index);
case IrInstructionIdContainerInitFields:
return ir_instruction_containerinitfields_get_dep((IrInstructionContainerInitFields *) instruction, index);
case IrInstructionIdStructInit:
return ir_instruction_structinit_get_dep((IrInstructionStructInit *) instruction, index);
case IrInstructionIdUnreachable:
return ir_instruction_unreachable_get_dep((IrInstructionUnreachable *) instruction, index);
case IrInstructionIdTypeOf:
return ir_instruction_typeof_get_dep((IrInstructionTypeOf *) instruction, index);
case IrInstructionIdToPtrType:
return ir_instruction_toptrtype_get_dep((IrInstructionToPtrType *) instruction, index);
case IrInstructionIdPtrTypeChild:
return ir_instruction_ptrtypechild_get_dep((IrInstructionPtrTypeChild *) instruction, index);
case IrInstructionIdSetFnTest:
return ir_instruction_setfntest_get_dep((IrInstructionSetFnTest *) instruction, index);
case IrInstructionIdSetFnVisible:
return ir_instruction_setfnvisible_get_dep((IrInstructionSetFnVisible *) instruction, index);
case IrInstructionIdSetDebugSafety:
return ir_instruction_setdebugsafety_get_dep((IrInstructionSetDebugSafety *) instruction, index);
case IrInstructionIdArrayType:
return ir_instruction_arraytype_get_dep((IrInstructionArrayType *) instruction, index);
case IrInstructionIdSliceType:
return ir_instruction_slicetype_get_dep((IrInstructionSliceType *) instruction, index);
case IrInstructionIdAsm:
return ir_instruction_asm_get_dep((IrInstructionAsm *) instruction, index);
case IrInstructionIdCompileVar:
return ir_instruction_compilevar_get_dep((IrInstructionCompileVar *) instruction, index);
case IrInstructionIdSizeOf:
return ir_instruction_sizeof_get_dep((IrInstructionSizeOf *) instruction, index);
case IrInstructionIdTestNonNull:
return ir_instruction_testnonnull_get_dep((IrInstructionTestNonNull *) instruction, index);
case IrInstructionIdUnwrapMaybe:
return ir_instruction_unwrapmaybe_get_dep((IrInstructionUnwrapMaybe *) instruction, index);
case IrInstructionIdMaybeWrap:
return ir_instruction_maybewrap_get_dep((IrInstructionMaybeWrap *) instruction, index);
case IrInstructionIdEnumTag:
return ir_instruction_enumtag_get_dep((IrInstructionEnumTag *) instruction, index);
case IrInstructionIdClz:
return ir_instruction_clz_get_dep((IrInstructionClz *) instruction, index);
case IrInstructionIdCtz:
return ir_instruction_ctz_get_dep((IrInstructionCtz *) instruction, index);
case IrInstructionIdGeneratedCode:
return ir_instruction_generatedcode_get_dep((IrInstructionGeneratedCode *) instruction, index);
case IrInstructionIdImport:
return ir_instruction_import_get_dep((IrInstructionImport *) instruction, index);
case IrInstructionIdCImport:
return ir_instruction_cimport_get_dep((IrInstructionCImport *) instruction, index);
case IrInstructionIdCInclude:
return ir_instruction_cinclude_get_dep((IrInstructionCInclude *) instruction, index);
case IrInstructionIdCDefine:
return ir_instruction_cdefine_get_dep((IrInstructionCDefine *) instruction, index);
case IrInstructionIdCUndef:
return ir_instruction_cundef_get_dep((IrInstructionCUndef *) instruction, index);
case IrInstructionIdArrayLen:
return ir_instruction_arraylen_get_dep((IrInstructionArrayLen *) instruction, index);
case IrInstructionIdRef:
return ir_instruction_ref_get_dep((IrInstructionRef *) instruction, index);
case IrInstructionIdMinValue:
return ir_instruction_minvalue_get_dep((IrInstructionMinValue *) instruction, index);
case IrInstructionIdMaxValue:
return ir_instruction_maxvalue_get_dep((IrInstructionMaxValue *) instruction, index);
case IrInstructionIdCompileErr:
return ir_instruction_compileerr_get_dep((IrInstructionCompileErr *) instruction, index);
case IrInstructionIdErrName:
return ir_instruction_errname_get_dep((IrInstructionErrName *) instruction, index);
case IrInstructionIdEmbedFile:
return ir_instruction_embedfile_get_dep((IrInstructionEmbedFile *) instruction, index);
case IrInstructionIdCmpxchg:
return ir_instruction_cmpxchg_get_dep((IrInstructionCmpxchg *) instruction, index);
case IrInstructionIdFence:
return ir_instruction_fence_get_dep((IrInstructionFence *) instruction, index);
case IrInstructionIdDivExact:
return ir_instruction_divexact_get_dep((IrInstructionDivExact *) instruction, index);
case IrInstructionIdTruncate:
return ir_instruction_truncate_get_dep((IrInstructionTruncate *) instruction, index);
case IrInstructionIdIntType:
return ir_instruction_inttype_get_dep((IrInstructionIntType *) instruction, index);
case IrInstructionIdBoolNot:
return ir_instruction_boolnot_get_dep((IrInstructionBoolNot *) instruction, index);
case IrInstructionIdAlloca:
return ir_instruction_alloca_get_dep((IrInstructionAlloca *) instruction, index);
case IrInstructionIdMemset:
return ir_instruction_memset_get_dep((IrInstructionMemset *) instruction, index);
case IrInstructionIdMemcpy:
return ir_instruction_memcpy_get_dep((IrInstructionMemcpy *) instruction, index);
case IrInstructionIdSlice:
return ir_instruction_slice_get_dep((IrInstructionSlice *) instruction, index);
case IrInstructionIdMemberCount:
return ir_instruction_membercount_get_dep((IrInstructionMemberCount *) instruction, index);
case IrInstructionIdBreakpoint:
return ir_instruction_breakpoint_get_dep((IrInstructionBreakpoint *) instruction, index);
case IrInstructionIdReturnAddress:
return ir_instruction_returnaddress_get_dep((IrInstructionReturnAddress *) instruction, index);
case IrInstructionIdFrameAddress:
return ir_instruction_frameaddress_get_dep((IrInstructionFrameAddress *) instruction, index);
case IrInstructionIdAlignOf:
return ir_instruction_alignof_get_dep((IrInstructionAlignOf *) instruction, index);
case IrInstructionIdOverflowOp:
return ir_instruction_overflowop_get_dep((IrInstructionOverflowOp *) instruction, index);
case IrInstructionIdTestErr:
return ir_instruction_testerr_get_dep((IrInstructionTestErr *) instruction, index);
case IrInstructionIdUnwrapErrCode:
return ir_instruction_unwraperrcode_get_dep((IrInstructionUnwrapErrCode *) instruction, index);
case IrInstructionIdUnwrapErrPayload:
return ir_instruction_unwraperrpayload_get_dep((IrInstructionUnwrapErrPayload *) instruction, index);
case IrInstructionIdErrWrapCode:
return ir_instruction_errwrapcode_get_dep((IrInstructionErrWrapCode *) instruction, index);
case IrInstructionIdErrWrapPayload:
return ir_instruction_errwrappayload_get_dep((IrInstructionErrWrapPayload *) instruction, index);
case IrInstructionIdFnProto:
return ir_instruction_fnproto_get_dep((IrInstructionFnProto *) instruction, index);
case IrInstructionIdTestComptime:
return ir_instruction_testcomptime_get_dep((IrInstructionTestComptime *) instruction, index);
case IrInstructionIdInitEnum:
return ir_instruction_initenum_get_dep((IrInstructionInitEnum *) instruction, index);
case IrInstructionIdPointerReinterpret:
return ir_instruction_pointerreinterpret_get_dep((IrInstructionPointerReinterpret *) instruction, index);
case IrInstructionIdWidenOrShorten:
return ir_instruction_widenorshorten_get_dep((IrInstructionWidenOrShorten *) instruction, index);
case IrInstructionIdIntToPtr:
return ir_instruction_inttoptr_get_dep((IrInstructionIntToPtr *) instruction, index);
case IrInstructionIdPtrToInt:
return ir_instruction_ptrtoint_get_dep((IrInstructionPtrToInt *) instruction, index);
case IrInstructionIdIntToEnum:
return ir_instruction_inttoenum_get_dep((IrInstructionIntToEnum *) instruction, index);
case IrInstructionIdCheckSwitchProngs:
return ir_instruction_checkswitchprongs_get_dep((IrInstructionCheckSwitchProngs *) instruction, index);
case IrInstructionIdTestType:
return ir_instruction_testtype_get_dep((IrInstructionTestType *) instruction, index);
case IrInstructionIdTypeName:
return ir_instruction_typename_get_dep((IrInstructionTypeName *) instruction, index);
case IrInstructionIdCanImplicitCast:
return ir_instruction_canimplicitcast_get_dep((IrInstructionCanImplicitCast *) instruction, index);
}
zig_unreachable();
}
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
results[ReturnKindUnconditional] = 0;
results[ReturnKindError] = 0;
results[ReturnKindMaybe] = 0;
while (inner_scope != outer_scope) {
assert(inner_scope);
if (inner_scope->id == ScopeIdDefer) {
AstNode *defer_node = inner_scope->source_node;
assert(defer_node->type == NodeTypeDefer);
ReturnKind defer_kind = defer_node->data.defer.kind;
results[defer_kind] += 1;
}
inner_scope = inner_scope->parent;
}
}
static IrInstruction *ir_mark_gen(IrInstruction *instruction) {
instruction->is_gen = true;
return instruction;
}
static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope,
bool gen_error_defers, bool gen_maybe_defers)
{
Scope *scope = inner_scope;
while (scope != outer_scope) {
if (!scope)
return false;
if (scope->id == ScopeIdDefer) {
AstNode *defer_node = scope->source_node;
assert(defer_node->type == NodeTypeDefer);
ReturnKind defer_kind = defer_node->data.defer.kind;
if (defer_kind == ReturnKindUnconditional ||
(gen_error_defers && defer_kind == ReturnKindError) ||
(gen_maybe_defers && defer_kind == ReturnKindMaybe))
{
AstNode *defer_expr_node = defer_node->data.defer.expr;
ir_gen_node(irb, defer_expr_node, defer_node->data.defer.expr_scope);
}
}
scope = scope->parent;
}
return true;
}
static void ir_set_cursor_at_end(IrBuilder *irb, IrBasicBlock *basic_block) {
assert(basic_block);
irb->current_basic_block = basic_block;
}
static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) {
while (scope) {
if (scope->id == ScopeIdDeferExpr)
return (ScopeDeferExpr *)scope;
if (scope->id == ScopeIdFnDef)
return nullptr;
scope = scope->parent;
}
return nullptr;
}
static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LValPurpose lval) {
assert(node->type == NodeTypeReturnExpr);
FnTableEntry *fn_entry = exec_fn_entry(irb->exec);
if (!fn_entry) {
add_node_error(irb->codegen, node, buf_sprintf("return expression outside function definition"));
return irb->codegen->invalid_instruction;
}
ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(scope);
if (scope_defer_expr) {
if (!scope_defer_expr->reported_err) {
add_node_error(irb->codegen, node, buf_sprintf("cannot return from defer expression"));
scope_defer_expr->reported_err = true;
}
return irb->codegen->invalid_instruction;
}
Scope *outer_scope = irb->exec->begin_scope;
AstNode *expr_node = node->data.return_expr.expr;
switch (node->data.return_expr.kind) {
case ReturnKindUnconditional:
{
IrInstruction *return_value;
if (expr_node) {
return_value = ir_gen_node(irb, expr_node, scope);
if (return_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
} else {
return_value = ir_build_const_void(irb, scope, node);
}
size_t defer_counts[3];
ir_count_defers(irb, scope, outer_scope, defer_counts);
if (defer_counts[ReturnKindError] > 0) {
IrBasicBlock *err_block = ir_build_basic_block(irb, scope, "ErrRetErr");
IrBasicBlock *ok_block = ir_build_basic_block(irb, scope, "ErrRetOk");
IrInstruction *is_err = ir_build_test_err(irb, scope, node, return_value);
IrInstruction *is_comptime;
if (ir_should_inline(irb->exec, scope)) {
is_comptime = ir_build_const_bool(irb, scope, node, true);
} else {
is_comptime = ir_build_test_comptime(irb, scope, node, is_err);
}
ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err, err_block, ok_block, is_comptime));
ir_set_cursor_at_end(irb, err_block);
ir_gen_defers_for_block(irb, scope, outer_scope, true, false);
ir_build_return(irb, scope, node, return_value);
ir_set_cursor_at_end(irb, ok_block);
ir_gen_defers_for_block(irb, scope, outer_scope, false, false);
return ir_build_return(irb, scope, node, return_value);
} else if (defer_counts[ReturnKindMaybe] > 0) {
IrBasicBlock *null_block = ir_build_basic_block(irb, scope, "MaybeRetNull");
IrBasicBlock *ok_block = ir_build_basic_block(irb, scope, "MaybeRetOk");
IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, return_value);
IrInstruction *is_comptime;
if (ir_should_inline(irb->exec, scope)) {
is_comptime = ir_build_const_bool(irb, scope, node, true);
} else {
is_comptime = ir_build_test_comptime(irb, scope, node, is_non_null);
}
ir_mark_gen(ir_build_cond_br(irb, scope, node, is_non_null, ok_block, null_block, is_comptime));
ir_set_cursor_at_end(irb, null_block);
ir_gen_defers_for_block(irb, scope, outer_scope, false, true);
ir_build_return(irb, scope, node, return_value);
ir_set_cursor_at_end(irb, ok_block);
ir_gen_defers_for_block(irb, scope, outer_scope, false, false);
return ir_build_return(irb, scope, node, return_value);
} else {
// generate unconditional defers
ir_gen_defers_for_block(irb, scope, outer_scope, false, false);
return ir_build_return(irb, scope, node, return_value);
}
}
case ReturnKindError:
{
assert(expr_node);
IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPurposeAddressOf);
if (err_union_ptr == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *err_union_val = ir_build_load_ptr(irb, scope, node, err_union_ptr);
IrInstruction *is_err_val = ir_build_test_err(irb, scope, node, err_union_val);
IrBasicBlock *return_block = ir_build_basic_block(irb, scope, "ErrRetReturn");
IrBasicBlock *continue_block = ir_build_basic_block(irb, scope, "ErrRetContinue");
IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, ir_should_inline(irb->exec, scope));
ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime));
ir_set_cursor_at_end(irb, return_block);
ir_gen_defers_for_block(irb, scope, outer_scope, true, false);
IrInstruction *err_val = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr);
ir_build_return(irb, scope, node, err_val);
ir_set_cursor_at_end(irb, continue_block);
IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, scope, node, err_union_ptr, false);
if (lval != LValPurposeNone)
return unwrapped_ptr;
else
return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
}
case ReturnKindMaybe:
{
assert(expr_node);
IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPurposeAddressOf);
if (maybe_val_ptr == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr);
IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_val);
IrBasicBlock *return_block = ir_build_basic_block(irb, scope, "MaybeRetReturn");
IrBasicBlock *continue_block = ir_build_basic_block(irb, scope, "MaybeRetContinue");
IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, ir_should_inline(irb->exec, scope));
ir_mark_gen(ir_build_cond_br(irb, scope, node, is_non_null, continue_block, return_block, is_comptime));
ir_set_cursor_at_end(irb, return_block);
ir_gen_defers_for_block(irb, scope, outer_scope, false, true);
IrInstruction *null = ir_build_const_null(irb, scope, node);
ir_build_return(irb, scope, node, null);
ir_set_cursor_at_end(irb, continue_block);
IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_val_ptr, false);
if (lval != LValPurposeNone)
return unwrapped_ptr;
else
return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
}
}
zig_unreachable();
}
static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_scope,
Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime)
{
VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1);
variable_entry->parent_scope = parent_scope;
variable_entry->shadowable = is_shadowable;
variable_entry->mem_slot_index = SIZE_MAX;
variable_entry->is_comptime = is_comptime;
variable_entry->src_arg_index = SIZE_MAX;
if (name) {
buf_init_from_buf(&variable_entry->name, name);
VariableTableEntry *existing_var = find_variable(codegen, parent_scope, name);
if (existing_var && !existing_var->shadowable) {
ErrorMsg *msg = add_node_error(codegen, node,
buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
variable_entry->value.type = codegen->builtin_types.entry_invalid;
} else {
auto primitive_table_entry = codegen->primitive_type_table.maybe_get(name);
if (primitive_table_entry) {
TypeTableEntry *type = primitive_table_entry->value;
add_node_error(codegen, node,
buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name)));
variable_entry->value.type = codegen->builtin_types.entry_invalid;
} else {
Tld *tld = find_decl(parent_scope, name);
if (tld && tld->id != TldIdVar) {
ErrorMsg *msg = add_node_error(codegen, node,
buf_sprintf("redefinition of '%s'", buf_ptr(name)));
add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition is here"));
variable_entry->value.type = codegen->builtin_types.entry_invalid;
}
}
}
} else {
assert(is_shadowable);
// TODO make this name not actually be in scope. user should be able to make a variable called "_anon"
// might already be solved, let's just make sure it has test coverage
// maybe we put a prefix on this so the debug info doesn't clobber user debug info for same named variables
buf_init_from_str(&variable_entry->name, "_anon");
}
variable_entry->src_is_const = src_is_const;
variable_entry->gen_is_const = gen_is_const;
variable_entry->decl_node = node;
variable_entry->child_scope = create_var_scope(node, parent_scope, variable_entry);
return variable_entry;
}
// Set name to nullptr to make the variable anonymous (not visible to programmer).
// After you call this function var->child_scope has the variable in scope
static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *name,
bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime)
{
VariableTableEntry *var = create_local_var(irb->codegen, node, scope, name, src_is_const, gen_is_const, is_shadowable, is_comptime);
if (is_comptime != nullptr || gen_is_const)
var->mem_slot_index = exec_next_mem_slot(irb->exec);
assert(var->child_scope);
return var;
}
static LabelTableEntry *find_label(IrExecutable *exec, Scope *scope, Buf *name) {
while (scope) {
if (scope->id == ScopeIdBlock) {
ScopeBlock *block_scope = (ScopeBlock *)scope;
auto entry = block_scope->label_table.maybe_get(name);
if (entry)
return entry->value;
}
scope = scope->parent;
}
return nullptr;
}
static ScopeBlock *find_block_scope(IrExecutable *exec, Scope *scope) {
while (scope) {
if (scope->id == ScopeIdBlock)
return (ScopeBlock *)scope;
scope = scope->parent;
}
return nullptr;
}
static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node) {
assert(block_node->type == NodeTypeBlock);
ScopeBlock *scope_block = create_block_scope(block_node, parent_scope);
Scope *outer_block_scope = &scope_block->base;
Scope *child_scope = outer_block_scope;
FnTableEntry *fn_entry = scope_fn_entry(parent_scope);
if (fn_entry && fn_entry->child_scope == parent_scope) {
fn_entry->def_scope = scope_block;
}
IrInstruction *return_value = nullptr;
for (size_t i = 0; i < block_node->data.block.statements.length; i += 1) {
AstNode *statement_node = block_node->data.block.statements.at(i);
if (statement_node->type == NodeTypeLabel) {
Buf *label_name = statement_node->data.label.name;
IrBasicBlock *label_block = ir_build_basic_block(irb, child_scope, buf_ptr(label_name));
LabelTableEntry *label = allocate<LabelTableEntry>(1);
label->decl_node = statement_node;
label->bb = label_block;
irb->exec->all_labels.append(label);
LabelTableEntry *existing_label = find_label(irb->exec, child_scope, label_name);
if (existing_label) {
ErrorMsg *msg = add_node_error(irb->codegen, statement_node,
buf_sprintf("duplicate label name '%s'", buf_ptr(label_name)));
add_error_note(irb->codegen, msg, existing_label->decl_node, buf_sprintf("other label here"));
return irb->codegen->invalid_instruction;
} else {
ScopeBlock *scope_block = find_block_scope(irb->exec, child_scope);
scope_block->label_table.put(label_name, label);
}
if (!return_value || !instr_is_unreachable(return_value)) {
IrInstruction *is_comptime = ir_mark_gen(ir_build_const_bool(irb, child_scope, statement_node,
ir_should_inline(irb->exec, child_scope)));
ir_mark_gen(ir_build_br(irb, child_scope, statement_node, label_block, is_comptime));
}
ir_set_cursor_at_end(irb, label_block);
return_value = nullptr;
continue;
}
if (return_value && instr_is_unreachable(return_value)) {
if (is_node_void_expr(statement_node))
continue;
add_node_error(irb->codegen, statement_node, buf_sprintf("unreachable code"));
}
return_value = ir_gen_node(irb, statement_node, child_scope);
if (statement_node->type == NodeTypeDefer && return_value != irb->codegen->invalid_instruction) {
// defer starts a new scope
child_scope = statement_node->data.defer.child_scope;
assert(child_scope);
} else if (return_value->id == IrInstructionIdDeclVar) {
// variable declarations start a new scope
IrInstructionDeclVar *decl_var_instruction = (IrInstructionDeclVar *)return_value;
child_scope = decl_var_instruction->var->child_scope;
}
}
if (!return_value)
return_value = ir_mark_gen(ir_build_const_void(irb, child_scope, block_node));
if (!instr_is_unreachable(return_value))
ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false, false);
return return_value;
}
static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) {
IrInstruction *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope);
IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
if (op1 == irb->codegen->invalid_instruction || op2 == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
return ir_build_bin_op(irb, scope, node, op_id, op1, op2, true);
}
static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) {
IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPurposeAssign);
IrInstruction *rvalue = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
if (lvalue == irb->codegen->invalid_instruction || rvalue == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
ir_build_store_ptr(irb, scope, node, lvalue, rvalue);
return ir_build_const_void(irb, scope, node);
}
static IrInstruction *ir_gen_assign_op(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) {
IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPurposeAssign);
if (lvalue == irb->codegen->invalid_instruction)
return lvalue;
IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue);
IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
if (op2 == irb->codegen->invalid_instruction)
return op2;
IrInstruction *result = ir_build_bin_op(irb, scope, node, op_id, op1, op2, true);
ir_build_store_ptr(irb, scope, node, lvalue, result);
return ir_build_const_void(irb, scope, node);
}
static IrInstruction *ir_gen_bool_or(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeBinOpExpr);
IrInstruction *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope);
if (val1 == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrBasicBlock *post_val1_block = irb->current_basic_block;
IrInstruction *is_comptime;
if (ir_should_inline(irb->exec, scope)) {
is_comptime = ir_build_const_bool(irb, scope, node, true);
} else {
is_comptime = ir_build_test_comptime(irb, scope, node, val1);
}
// block for when val1 == false
IrBasicBlock *false_block = ir_build_basic_block(irb, scope, "BoolOrFalse");
// block for when val1 == true (don't even evaluate the second part)
IrBasicBlock *true_block = ir_build_basic_block(irb, scope, "BoolOrTrue");
ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime);
ir_set_cursor_at_end(irb, false_block);
IrInstruction *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
if (val2 == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrBasicBlock *post_val2_block = irb->current_basic_block;
ir_build_br(irb, scope, node, true_block, is_comptime);
ir_set_cursor_at_end(irb, true_block);
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
incoming_values[0] = val1;
incoming_values[1] = val2;
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
incoming_blocks[0] = post_val1_block;
incoming_blocks[1] = post_val2_block;
return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values);
}
static IrInstruction *ir_gen_bool_and(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeBinOpExpr);
IrInstruction *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope);
if (val1 == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrBasicBlock *post_val1_block = irb->current_basic_block;
IrInstruction *is_comptime;
if (ir_should_inline(irb->exec, scope)) {
is_comptime = ir_build_const_bool(irb, scope, node, true);
} else {
is_comptime = ir_build_test_comptime(irb, scope, node, val1);
}
// block for when val1 == true
IrBasicBlock *true_block = ir_build_basic_block(irb, scope, "BoolAndTrue");
// block for when val1 == false (don't even evaluate the second part)
IrBasicBlock *false_block = ir_build_basic_block(irb, scope, "BoolAndFalse");
ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime);
ir_set_cursor_at_end(irb, true_block);
IrInstruction *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
if (val2 == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrBasicBlock *post_val2_block = irb->current_basic_block;
ir_build_br(irb, scope, node, false_block, is_comptime);
ir_set_cursor_at_end(irb, false_block);
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
incoming_values[0] = val1;
incoming_values[1] = val2;
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
incoming_blocks[0] = post_val1_block;
incoming_blocks[1] = post_val2_block;
return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values);
}
static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
assert(node->type == NodeTypeBinOpExpr);
AstNode *op1_node = node->data.bin_op_expr.op1;
AstNode *op2_node = node->data.bin_op_expr.op2;
IrInstruction *maybe_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPurposeAddressOf);
if (maybe_ptr == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *maybe_val = ir_build_load_ptr(irb, parent_scope, node, maybe_ptr);
IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_val);
IrInstruction *is_comptime;
if (ir_should_inline(irb->exec, parent_scope)) {
is_comptime = ir_build_const_bool(irb, parent_scope, node, true);
} else {
is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_non_null);
}
IrBasicBlock *ok_block = ir_build_basic_block(irb, parent_scope, "MaybeNonNull");
IrBasicBlock *null_block = ir_build_basic_block(irb, parent_scope, "MaybeNull");
IrBasicBlock *end_block = ir_build_basic_block(irb, parent_scope, "MaybeEnd");
ir_build_cond_br(irb, parent_scope, node, is_non_null, ok_block, null_block, is_comptime);
ir_set_cursor_at_end(irb, null_block);
IrInstruction *null_result = ir_gen_node(irb, op2_node, parent_scope);
if (null_result == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrBasicBlock *after_null_block = irb->current_basic_block;
if (!instr_is_unreachable(null_result))
ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime));
ir_set_cursor_at_end(irb, ok_block);
IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, parent_scope, node, maybe_ptr, false);
IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr);
IrBasicBlock *after_ok_block = irb->current_basic_block;
ir_build_br(irb, parent_scope, node, end_block, is_comptime);
ir_set_cursor_at_end(irb, end_block);
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
incoming_values[0] = null_result;
incoming_values[1] = unwrapped_payload;
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
incoming_blocks[0] = after_null_block;
incoming_blocks[1] = after_ok_block;
return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values);
}
static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeBinOpExpr);
BinOpType bin_op_type = node->data.bin_op_expr.bin_op;
switch (bin_op_type) {
case BinOpTypeInvalid:
zig_unreachable();
case BinOpTypeAssign:
return ir_gen_assign(irb, scope, node);
case BinOpTypeAssignTimes:
return ir_gen_assign_op(irb, scope, node, IrBinOpMult);
case BinOpTypeAssignTimesWrap:
return ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap);
case BinOpTypeAssignDiv:
return ir_gen_assign_op(irb, scope, node, IrBinOpDiv);
case BinOpTypeAssignMod:
return ir_gen_assign_op(irb, scope, node, IrBinOpMod);
case BinOpTypeAssignPlus:
return ir_gen_assign_op(irb, scope, node, IrBinOpAdd);
case BinOpTypeAssignPlusWrap:
return ir_gen_assign_op(irb, scope, node, IrBinOpAddWrap);
case BinOpTypeAssignMinus:
return ir_gen_assign_op(irb, scope, node, IrBinOpSub);
case BinOpTypeAssignMinusWrap:
return ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap);
case BinOpTypeAssignBitShiftLeft:
return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeft);
case BinOpTypeAssignBitShiftLeftWrap:
return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftWrap);
case BinOpTypeAssignBitShiftRight:
return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRight);
case BinOpTypeAssignBitAnd:
return ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd);
case BinOpTypeAssignBitXor:
return ir_gen_assign_op(irb, scope, node, IrBinOpBinXor);
case BinOpTypeAssignBitOr:
return ir_gen_assign_op(irb, scope, node, IrBinOpBinOr);
case BinOpTypeAssignBoolAnd:
return ir_gen_assign_op(irb, scope, node, IrBinOpBoolAnd);
case BinOpTypeAssignBoolOr:
return ir_gen_assign_op(irb, scope, node, IrBinOpBoolOr);
case BinOpTypeBoolOr:
return ir_gen_bool_or(irb, scope, node);
case BinOpTypeBoolAnd:
return ir_gen_bool_and(irb, scope, node);
case BinOpTypeCmpEq:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpEq);
case BinOpTypeCmpNotEq:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpNotEq);
case BinOpTypeCmpLessThan:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessThan);
case BinOpTypeCmpGreaterThan:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterThan);
case BinOpTypeCmpLessOrEq:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessOrEq);
case BinOpTypeCmpGreaterOrEq:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterOrEq);
case BinOpTypeBinOr:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinOr);
case BinOpTypeBinXor:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinXor);
case BinOpTypeBinAnd:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd);
case BinOpTypeBitShiftLeft:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeft);
case BinOpTypeBitShiftLeftWrap:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftWrap);
case BinOpTypeBitShiftRight:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRight);
case BinOpTypeAdd:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd);
case BinOpTypeAddWrap:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpAddWrap);
case BinOpTypeSub:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpSub);
case BinOpTypeSubWrap:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpSubWrap);
case BinOpTypeMult:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpMult);
case BinOpTypeMultWrap:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap);
case BinOpTypeDiv:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpDiv);
case BinOpTypeMod:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpMod);
case BinOpTypeArrayCat:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat);
case BinOpTypeArrayMult:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult);
case BinOpTypeUnwrapMaybe:
return ir_gen_maybe_ok_or(irb, scope, node);
}
zig_unreachable();
}
static IrInstruction *ir_gen_num_lit(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeNumberLiteral);
if (node->data.number_literal.overflow) {
add_node_error(irb->codegen, node, buf_sprintf("number literal too large to be represented in any type"));
return irb->codegen->invalid_instruction;
}
return ir_build_const_bignum(irb, scope, node, node->data.number_literal.bignum);
}
static IrInstruction *ir_gen_char_lit(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeCharLiteral);
return ir_build_const_uint(irb, scope, node, node->data.char_literal.value);
}
static IrInstruction *ir_gen_null_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeNullLiteral);
return ir_build_const_null(irb, scope, node);
}
static IrInstruction *ir_gen_var_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeVarLiteral);
return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_var, false);
}
static IrInstruction *ir_gen_decl_ref(IrBuilder *irb, AstNode *source_node, Tld *tld,
LValPurpose lval, Scope *scope)
{
resolve_top_level_decl(irb->codegen, tld, lval != LValPurposeNone);
if (tld->resolution == TldResolutionInvalid)
return irb->codegen->invalid_instruction;
switch (tld->id) {
case TldIdContainer:
zig_unreachable();
case TldIdVar:
{
TldVar *tld_var = (TldVar *)tld;
VariableTableEntry *var = tld_var->var;
IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, source_node, var, lval == LValPurposeAddressOfConst);
if (lval != LValPurposeNone)
return var_ptr;
else
return ir_build_load_ptr(irb, scope, source_node, var_ptr);
}
case TldIdFn:
{
TldFn *tld_fn = (TldFn *)tld;
FnTableEntry *fn_entry = tld_fn->fn_entry;
assert(fn_entry->type_entry);
IrInstruction *ref_instruction = ir_build_const_fn(irb, scope, source_node, fn_entry);
if (lval != LValPurposeNone)
return ir_build_ref(irb, scope, source_node, ref_instruction, true);
else
return ref_instruction;
}
case TldIdTypeDef:
{
TldTypeDef *tld_typedef = (TldTypeDef *)tld;
TypeTableEntry *typedef_type = tld_typedef->type_entry;
IrInstruction *ref_instruction = ir_build_const_type(irb, scope, source_node, typedef_type, false);
if (lval != LValPurposeNone)
return ir_build_ref(irb, scope, source_node, ref_instruction, true);
else
return ref_instruction;
}
}
zig_unreachable();
}
static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, LValPurpose lval) {
assert(node->type == NodeTypeSymbol);
Buf *variable_name = node->data.symbol_expr.symbol;
auto primitive_table_entry = irb->codegen->primitive_type_table.maybe_get(variable_name);
if (primitive_table_entry) {
IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_table_entry->value, false);
if (lval != LValPurposeNone) {
return ir_build_ref(irb, scope, node, value, lval == LValPurposeAddressOfConst);
} else {
return value;
}
}
VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name);
if (var) {
IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var, lval == LValPurposeAddressOfConst);
if (lval != LValPurposeNone)
return var_ptr;
else
return ir_build_load_ptr(irb, scope, node, var_ptr);
}
Tld *tld = find_decl(scope, variable_name);
if (tld)
return ir_gen_decl_ref(irb, node, tld, lval, scope);
if (node->owner->any_imports_failed) {
// skip the error message since we had a failing import in this file
// if an import breaks we don't need redundant undeclared identifier errors
return irb->codegen->invalid_instruction;
}
// TODO put a variable of same name with invalid type in global scope
// so that future references to this same name will find a variable with an invalid type
add_node_error(irb->codegen, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));
return irb->codegen->invalid_instruction;
}
static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode *node, LValPurpose lval) {
assert(node->type == NodeTypeArrayAccessExpr);
AstNode *array_ref_node = node->data.array_access_expr.array_ref_expr;
IrInstruction *array_ref_instruction = ir_gen_node_extra(irb, array_ref_node, scope,
LValPurposeAddressOf);
if (array_ref_instruction == irb->codegen->invalid_instruction)
return array_ref_instruction;
AstNode *subscript_node = node->data.array_access_expr.subscript;
IrInstruction *subscript_instruction = ir_gen_node(irb, subscript_node, scope);
if (subscript_instruction == irb->codegen->invalid_instruction)
return subscript_instruction;
IrInstruction *ptr_instruction = ir_build_elem_ptr(irb, scope, node, array_ref_instruction,
subscript_instruction, true);
if (lval != LValPurposeNone)
return ptr_instruction;
return ir_build_load_ptr(irb, scope, node, ptr_instruction);
}
static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node, LValPurpose lval) {
assert(node->type == NodeTypeFieldAccessExpr);
AstNode *container_ref_node = node->data.field_access_expr.struct_expr;
Buf *field_name = node->data.field_access_expr.field_name;
IrInstruction *container_ref_instruction = ir_gen_node_extra(irb, container_ref_node, scope,
LValPurposeAddressOf);
if (container_ref_instruction == irb->codegen->invalid_instruction)
return container_ref_instruction;
IrInstruction *ptr_instruction = ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name);
if (lval != LValPurposeNone)
return ptr_instruction;
return ir_build_load_ptr(irb, scope, node, ptr_instruction);
}
static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *node, IrOverflowOp op) {
assert(node->type == NodeTypeFnCallExpr);
AstNode *type_node = node->data.fn_call_expr.params.at(0);
AstNode *op1_node = node->data.fn_call_expr.params.at(1);
AstNode *op2_node = node->data.fn_call_expr.params.at(2);
AstNode *result_ptr_node = node->data.fn_call_expr.params.at(3);
IrInstruction *type_value = ir_gen_node(irb, type_node, scope);
if (type_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *op1 = ir_gen_node(irb, op1_node, scope);
if (op1 == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *op2 = ir_gen_node(irb, op2_node, scope);
if (op2 == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *result_ptr = ir_gen_node(irb, result_ptr_node, scope);
if (result_ptr == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr);
}
static IrInstruction *ir_gen_test_type(IrBuilder *irb, Scope *scope, AstNode *node, TypeTableEntryId type_id) {
assert(node->type == NodeTypeFnCallExpr);
AstNode *type_node = node->data.fn_call_expr.params.at(0);
IrInstruction *type_value = ir_gen_node(irb, type_node, scope);
if (type_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
return ir_build_test_type(irb, scope, node, type_value, type_id);
}
static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeFnCallExpr);
AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
Buf *name = fn_ref_expr->data.symbol_expr.symbol;
auto entry = irb->codegen->builtin_fn_table.maybe_get(name);
if (!entry) {
add_node_error(irb->codegen, node,
buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));
return irb->codegen->invalid_instruction;
}
BuiltinFnEntry *builtin_fn = entry->value;
size_t actual_param_count = node->data.fn_call_expr.params.length;
if (builtin_fn->param_count != actual_param_count) {
add_node_error(irb->codegen, node,
buf_sprintf("expected %zu arguments, found %zu",
builtin_fn->param_count, actual_param_count));
return irb->codegen->invalid_instruction;
}
builtin_fn->ref_count += 1;
switch (builtin_fn->id) {
case BuiltinFnIdInvalid:
zig_unreachable();
case BuiltinFnIdUnreachable:
return ir_build_unreachable(irb, scope, node);
case BuiltinFnIdTypeof:
{
AstNode *arg_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg = ir_gen_node(irb, arg_node, scope);
if (arg == irb->codegen->invalid_instruction)
return arg;
return ir_build_typeof(irb, scope, node, arg);
}
case BuiltinFnIdSetFnTest:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
return ir_build_set_fn_test(irb, scope, node, arg0_value);
}
case BuiltinFnIdSetFnVisible:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;
return ir_build_set_fn_visible(irb, scope, node, arg0_value, arg1_value);
}
case BuiltinFnIdSetDebugSafety:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;
return ir_build_set_debug_safety(irb, scope, node, arg0_value, arg1_value);
}
case BuiltinFnIdCompileVar:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
return ir_build_compile_var(irb, scope, node, arg0_value);
}
case BuiltinFnIdSizeof:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
return ir_build_size_of(irb, scope, node, arg0_value);
}
case BuiltinFnIdCtz:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
return ir_build_ctz(irb, scope, node, arg0_value);
}
case BuiltinFnIdClz:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
return ir_build_clz(irb, scope, node, arg0_value);
}
case BuiltinFnIdGeneratedCode:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
return ir_build_generated_code(irb, scope, node, arg0_value);
}
case BuiltinFnIdImport:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
if (exec_fn_entry(irb->exec)) {
add_node_error(irb->codegen, node, buf_sprintf("import valid only at global scope"));
return irb->codegen->invalid_instruction;
}
return ir_build_import(irb, scope, node, arg0_value);
}
case BuiltinFnIdCImport:
{
if (exec_fn_entry(irb->exec)) {
add_node_error(irb->codegen, node, buf_sprintf("C import valid only at global scope"));
return irb->codegen->invalid_instruction;
}
return ir_build_c_import(irb, scope, node);
}
case BuiltinFnIdCInclude:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
if (!exec_c_import_buf(irb->exec)) {
add_node_error(irb->codegen, node, buf_sprintf("C include valid only inside C import block"));
return irb->codegen->invalid_instruction;
}
return ir_build_c_include(irb, scope, node, arg0_value);
}
case BuiltinFnIdCDefine:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;
if (!exec_c_import_buf(irb->exec)) {
add_node_error(irb->codegen, node, buf_sprintf("C define valid only inside C import block"));
return irb->codegen->invalid_instruction;
}
return ir_build_c_define(irb, scope, node, arg0_value, arg1_value);
}
case BuiltinFnIdCUndef:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
if (!exec_c_import_buf(irb->exec)) {
add_node_error(irb->codegen, node, buf_sprintf("C undef valid only inside C import block"));
return irb->codegen->invalid_instruction;
}
return ir_build_c_undef(irb, scope, node, arg0_value);
}
case BuiltinFnIdMaxValue:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
return ir_build_max_value(irb, scope, node, arg0_value);
}
case BuiltinFnIdMinValue:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
return ir_build_min_value(irb, scope, node, arg0_value);
}
case BuiltinFnIdCompileErr:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
return ir_build_compile_err(irb, scope, node, arg0_value);
}
case BuiltinFnIdErrName:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
return ir_build_err_name(irb, scope, node, arg0_value);
}
case BuiltinFnIdEmbedFile:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
return ir_build_embed_file(irb, scope, node, arg0_value);
}
case BuiltinFnIdCmpExchange:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope);
if (arg2_value == irb->codegen->invalid_instruction)
return arg2_value;
AstNode *arg3_node = node->data.fn_call_expr.params.at(3);
IrInstruction *arg3_value = ir_gen_node(irb, arg3_node, scope);
if (arg3_value == irb->codegen->invalid_instruction)
return arg3_value;
AstNode *arg4_node = node->data.fn_call_expr.params.at(4);
IrInstruction *arg4_value = ir_gen_node(irb, arg4_node, scope);
if (arg4_value == irb->codegen->invalid_instruction)
return arg4_value;
return ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value,
arg2_value, arg3_value, arg4_value,
AtomicOrderUnordered, AtomicOrderUnordered);
}
case BuiltinFnIdFence:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
return ir_build_fence(irb, scope, node, arg0_value, AtomicOrderUnordered);
}
case BuiltinFnIdDivExact:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;
return ir_build_div_exact(irb, scope, node, arg0_value, arg1_value);
}
case BuiltinFnIdTruncate:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;
return ir_build_truncate(irb, scope, node, arg0_value, arg1_value);
}
case BuiltinFnIdIntType:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;
return ir_build_int_type(irb, scope, node, arg0_value, arg1_value);
}
case BuiltinFnIdAlloca:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;
return ir_build_alloca(irb, scope, node, arg0_value, arg1_value);
}
case BuiltinFnIdMemcpy:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope);
if (arg2_value == irb->codegen->invalid_instruction)
return arg2_value;
return ir_build_memcpy(irb, scope, node, arg0_value, arg1_value, arg2_value);
}
case BuiltinFnIdMemset:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope);
if (arg2_value == irb->codegen->invalid_instruction)
return arg2_value;
return ir_build_memset(irb, scope, node, arg0_value, arg1_value, arg2_value);
}
case BuiltinFnIdMemberCount:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
return ir_build_member_count(irb, scope, node, arg0_value);
}
case BuiltinFnIdBreakpoint:
return ir_build_breakpoint(irb, scope, node);
case BuiltinFnIdReturnAddress:
return ir_build_return_address(irb, scope, node);
case BuiltinFnIdFrameAddress:
return ir_build_frame_address(irb, scope, node);
case BuiltinFnIdAlignof:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
return ir_build_alignof(irb, scope, node, arg0_value);
}
case BuiltinFnIdAddWithOverflow:
return ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd);
case BuiltinFnIdSubWithOverflow:
return ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub);
case BuiltinFnIdMulWithOverflow:
return ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul);
case BuiltinFnIdShlWithOverflow:
return ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl);
case BuiltinFnIdTypeName:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
return ir_build_type_name(irb, scope, node, arg0_value);
}
case BuiltinFnIdIsInteger:
return ir_gen_test_type(irb, scope, node, TypeTableEntryIdInt);
case BuiltinFnIdIsFloat:
return ir_gen_test_type(irb, scope, node, TypeTableEntryIdFloat);
case BuiltinFnIdCanImplicitCast:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;
return ir_build_can_implicit_cast(irb, scope, node, arg0_value, arg1_value);
}
}
zig_unreachable();
}
static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeFnCallExpr);
if (node->data.fn_call_expr.is_builtin)
return ir_gen_builtin_fn_call(irb, scope, node);
AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
if (fn_ref == irb->codegen->invalid_instruction)
return fn_ref;
size_t arg_count = node->data.fn_call_expr.params.length;
IrInstruction **args = allocate<IrInstruction*>(arg_count);
for (size_t i = 0; i < arg_count; i += 1) {
AstNode *arg_node = node->data.fn_call_expr.params.at(i);
args[i] = ir_gen_node(irb, arg_node, scope);
if (args[i] == irb->codegen->invalid_instruction)
return args[i];
}
return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false);
}
static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeIfBoolExpr);
IrInstruction *condition = ir_gen_node(irb, node->data.if_bool_expr.condition, scope);
if (condition == irb->codegen->invalid_instruction)
return condition;
IrInstruction *is_comptime;
if (ir_should_inline(irb->exec, scope)) {
is_comptime = ir_build_const_bool(irb, scope, node, true);
} else {
is_comptime = ir_build_test_comptime(irb, scope, node, condition);
}
AstNode *then_node = node->data.if_bool_expr.then_block;
AstNode *else_node = node->data.if_bool_expr.else_node;
IrBasicBlock *then_block = ir_build_basic_block(irb, scope, "Then");
IrBasicBlock *else_block = ir_build_basic_block(irb, scope, "Else");
IrBasicBlock *endif_block = ir_build_basic_block(irb, scope, "EndIf");
ir_build_cond_br(irb, scope, condition->source_node, condition, then_block, else_block, is_comptime);
ir_set_cursor_at_end(irb, then_block);
IrInstruction *then_expr_result = ir_gen_node(irb, then_node, scope);
if (then_expr_result == irb->codegen->invalid_instruction)
return then_expr_result;
IrBasicBlock *after_then_block = irb->current_basic_block;
if (!instr_is_unreachable(then_expr_result))
ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
ir_set_cursor_at_end(irb, else_block);
IrInstruction *else_expr_result;
if (else_node) {
else_expr_result = ir_gen_node(irb, else_node, scope);
if (else_expr_result == irb->codegen->invalid_instruction)
return else_expr_result;
} else {
else_expr_result = ir_build_const_void(irb, scope, node);
}
IrBasicBlock *after_else_block = irb->current_basic_block;
if (!instr_is_unreachable(else_expr_result))
ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
ir_set_cursor_at_end(irb, endif_block);
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
incoming_values[0] = then_expr_result;
incoming_values[1] = else_expr_result;
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
incoming_blocks[0] = after_then_block;
incoming_blocks[1] = after_else_block;
return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values);
}
static IrInstruction *ir_gen_prefix_op_id_lval(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id, LValPurpose lval) {
assert(node->type == NodeTypePrefixOpExpr);
AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval);
if (value == irb->codegen->invalid_instruction)
return value;
return ir_build_un_op(irb, scope, node, op_id, value);
}
static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id) {
return ir_gen_prefix_op_id_lval(irb, scope, node, op_id, LValPurposeNone);
}
static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LValPurpose lval) {
if (lval == LValPurposeNone)
return value;
if (value == irb->codegen->invalid_instruction)
return value;
// We needed a pointer to a value, but we got a value. So we create
// an instruction which just makes a const pointer of it.
return ir_build_ref(irb, scope, value->source_node, value, true);
}
static IrInstruction *ir_gen_address_of(IrBuilder *irb, Scope *scope, AstNode *node, bool is_const, LValPurpose lval) {
assert(node->type == NodeTypePrefixOpExpr);
AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope,
is_const ? LValPurposeAddressOfConst : LValPurposeAddressOf);
if (value == irb->codegen->invalid_instruction)
return value;
return ir_lval_wrap(irb, scope, value, lval);
}
static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode *node, LValPurpose lval) {
assert(node->type == NodeTypePrefixOpExpr);
AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPurposeAddressOf);
if (err_union_ptr == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, scope, node, err_union_ptr, true);
if (payload_ptr == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
if (lval == LValPurposeNone)
return ir_build_load_ptr(irb, scope, node, payload_ptr);
else
return payload_ptr;
}
static IrInstruction *ir_gen_maybe_assert_ok(IrBuilder *irb, Scope *scope, AstNode *node, LValPurpose lval) {
assert(node->type == NodeTypePrefixOpExpr);
AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPurposeAddressOf);
if (maybe_ptr == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true);
if (lval == LValPurposeNone)
return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
else
return unwrapped_ptr;
}
static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypePrefixOpExpr);
AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
IrInstruction *value = ir_gen_node(irb, expr_node, scope);
if (value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
return ir_build_bool_not(irb, scope, node, value);
}
static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNode *node, LValPurpose lval) {
assert(node->type == NodeTypePrefixOpExpr);
PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op;
switch (prefix_op) {
case PrefixOpInvalid:
zig_unreachable();
case PrefixOpBoolNot:
return ir_lval_wrap(irb, scope, ir_gen_bool_not(irb, scope, node), lval);
case PrefixOpBinNot:
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpBinNot), lval);
case PrefixOpNegation:
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation), lval);
case PrefixOpNegationWrap:
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval);
case PrefixOpAddressOf:
return ir_gen_address_of(irb, scope, node, false, lval);
case PrefixOpConstAddressOf:
return ir_gen_address_of(irb, scope, node, true, lval);
case PrefixOpDereference:
return ir_gen_prefix_op_id_lval(irb, scope, node, IrUnOpDereference, lval);
case PrefixOpMaybe:
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpMaybe), lval);
case PrefixOpError:
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpError), lval);
case PrefixOpUnwrapError:
return ir_gen_err_assert_ok(irb, scope, node, lval);
case PrefixOpUnwrapMaybe:
return ir_gen_maybe_assert_ok(irb, scope, node, lval);
}
zig_unreachable();
}
static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeContainerInitExpr);
AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr;
ContainerInitKind kind = container_init_expr->kind;
IrInstruction *container_type = ir_gen_node(irb, container_init_expr->type, scope);
if (container_type == irb->codegen->invalid_instruction)
return container_type;
if (kind == ContainerInitKindStruct) {
size_t field_count = container_init_expr->entries.length;
IrInstructionContainerInitFieldsField *fields = allocate<IrInstructionContainerInitFieldsField>(field_count);
for (size_t i = 0; i < field_count; i += 1) {
AstNode *entry_node = container_init_expr->entries.at(i);
assert(entry_node->type == NodeTypeStructValueField);
Buf *name = entry_node->data.struct_val_field.name;
AstNode *expr_node = entry_node->data.struct_val_field.expr;
IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope);
if (expr_value == irb->codegen->invalid_instruction)
return expr_value;
fields[i].name = name;
fields[i].value = expr_value;
fields[i].source_node = entry_node;
}
return ir_build_container_init_fields(irb, scope, node, container_type, field_count, fields);
} else if (kind == ContainerInitKindArray) {
size_t item_count = container_init_expr->entries.length;
IrInstruction **values = allocate<IrInstruction *>(item_count);
for (size_t i = 0; i < item_count; i += 1) {
AstNode *expr_node = container_init_expr->entries.at(i);
IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope);
if (expr_value == irb->codegen->invalid_instruction)
return expr_value;
values[i] = expr_value;
}
return ir_build_container_init_list(irb, scope, node, container_type, item_count, values);
} else {
zig_unreachable();
}
}
static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeVariableDeclaration);
AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;
IrInstruction *type_instruction;
if (variable_declaration->type != nullptr) {
type_instruction = ir_gen_node(irb, variable_declaration->type, scope);
if (type_instruction == irb->codegen->invalid_instruction)
return type_instruction;
} else {
type_instruction = nullptr;
}
bool is_shadowable = false;
bool is_const = variable_declaration->is_const;
bool is_extern = variable_declaration->is_extern;
IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node,
ir_should_inline(irb->exec, scope) || variable_declaration->is_inline);
VariableTableEntry *var = ir_create_var(irb, node, scope, variable_declaration->symbol,
is_const, is_const, is_shadowable, is_comptime);
// we detect IrInstructionIdDeclVar in gen_block to make sure the next node
// is inside var->child_scope
if (!is_extern && !variable_declaration->expr) {
var->value.type = irb->codegen->builtin_types.entry_invalid;
add_node_error(irb->codegen, node, buf_sprintf("variables must be initialized"));
return irb->codegen->invalid_instruction;
}
IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope);
if (init_value == irb->codegen->invalid_instruction)
return init_value;
return ir_build_var_decl(irb, scope, node, var, type_instruction, init_value);
}
static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeWhileExpr);
AstNode *continue_expr_node = node->data.while_expr.continue_expr;
IrBasicBlock *cond_block = ir_build_basic_block(irb, scope, "WhileCond");
IrBasicBlock *body_block = ir_build_basic_block(irb, scope, "WhileBody");
IrBasicBlock *continue_block = continue_expr_node ?
ir_build_basic_block(irb, scope, "WhileContinue") : cond_block;
IrBasicBlock *end_block = ir_build_basic_block(irb, scope, "WhileEnd");
IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node,
ir_should_inline(irb->exec, scope) || node->data.while_expr.is_inline);
ir_build_br(irb, scope, node, cond_block, is_comptime);
if (continue_expr_node) {
ir_set_cursor_at_end(irb, continue_block);
IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, scope);
if (expr_result == irb->codegen->invalid_instruction)
return expr_result;
if (!instr_is_unreachable(expr_result))
ir_mark_gen(ir_build_br(irb, scope, node, cond_block, is_comptime));
}
ir_set_cursor_at_end(irb, cond_block);
IrInstruction *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope);
if (cond_val == irb->codegen->invalid_instruction)
return cond_val;
if (!instr_is_unreachable(cond_val)) {
ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val,
body_block, end_block, is_comptime));
}
ir_set_cursor_at_end(irb, body_block);
LoopStackItem *loop_stack_item = irb->loop_stack.add_one();
loop_stack_item->break_block = end_block;
loop_stack_item->continue_block = continue_block;
loop_stack_item->is_comptime = is_comptime;
IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, scope);
if (body_result == irb->codegen->invalid_instruction)
return body_result;
irb->loop_stack.pop();
if (!instr_is_unreachable(body_result))
ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime));
ir_set_cursor_at_end(irb, end_block);
return ir_build_const_void(irb, scope, node);
}
static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
assert(node->type == NodeTypeForExpr);
AstNode *array_node = node->data.for_expr.array_expr;
AstNode *elem_node = node->data.for_expr.elem_node;
AstNode *index_node = node->data.for_expr.index_node;
AstNode *body_node = node->data.for_expr.body;
if (!elem_node) {
add_node_error(irb->codegen, node, buf_sprintf("for loop expression missing element parameter"));
return irb->codegen->invalid_instruction;
}
assert(elem_node->type == NodeTypeSymbol);
IrInstruction *array_val_ptr = ir_gen_node_extra(irb, array_node, parent_scope, LValPurposeAddressOf);
if (array_val_ptr == irb->codegen->invalid_instruction)
return array_val_ptr;
IrInstruction *array_val = ir_build_load_ptr(irb, parent_scope, array_node, array_val_ptr);
IrInstruction *pointer_type = ir_build_to_ptr_type(irb, parent_scope, array_node, array_val);
IrInstruction *elem_var_type;
if (node->data.for_expr.elem_is_ptr) {
elem_var_type = pointer_type;
} else {
elem_var_type = ir_build_ptr_type_child(irb, parent_scope, elem_node, pointer_type);
}
IrInstruction *is_comptime = ir_build_const_bool(irb, parent_scope, node,
ir_should_inline(irb->exec, parent_scope) || node->data.for_expr.is_inline);
Scope *child_scope = create_loop_scope(node, parent_scope);
// TODO make it an error to write to element variable or i variable.
Buf *elem_var_name = elem_node->data.symbol_expr.symbol;
VariableTableEntry *elem_var = ir_create_var(irb, elem_node, child_scope, elem_var_name, true, false, false, is_comptime);
child_scope = elem_var->child_scope;
IrInstruction *undefined_value = ir_build_const_undefined(irb, child_scope, elem_node);
ir_build_var_decl(irb, child_scope, elem_node, elem_var, elem_var_type, undefined_value);
IrInstruction *elem_var_ptr = ir_build_var_ptr(irb, child_scope, node, elem_var, false);
AstNode *index_var_source_node;
VariableTableEntry *index_var;
if (index_node) {
index_var_source_node = index_node;
Buf *index_var_name = index_node->data.symbol_expr.symbol;
index_var = ir_create_var(irb, index_node, child_scope, index_var_name, true, false, false, is_comptime);
} else {
index_var_source_node = node;
index_var = ir_create_var(irb, node, child_scope, nullptr, true, false, true, is_comptime);
}
child_scope = index_var->child_scope;
IrInstruction *usize = ir_build_const_type(irb, child_scope, node, irb->codegen->builtin_types.entry_usize, false);
IrInstruction *zero = ir_build_const_usize(irb, child_scope, node, 0);
IrInstruction *one = ir_build_const_usize(irb, child_scope, node, 1);
ir_build_var_decl(irb, child_scope, index_var_source_node, index_var, usize, zero);
IrInstruction *index_ptr = ir_build_var_ptr(irb, child_scope, node, index_var, false);
IrBasicBlock *cond_block = ir_build_basic_block(irb, child_scope, "ForCond");
IrBasicBlock *body_block = ir_build_basic_block(irb, child_scope, "ForBody");
IrBasicBlock *end_block = ir_build_basic_block(irb, child_scope, "ForEnd");
IrBasicBlock *continue_block = ir_build_basic_block(irb, child_scope, "ForContinue");
IrInstruction *len_val = ir_build_array_len(irb, child_scope, node, array_val);
ir_build_br(irb, child_scope, node, cond_block, is_comptime);
ir_set_cursor_at_end(irb, cond_block);
IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_ptr);
IrInstruction *cond = ir_build_bin_op(irb, child_scope, node, IrBinOpCmpLessThan, index_val, len_val, false);
ir_mark_gen(ir_build_cond_br(irb, child_scope, node, cond, body_block, end_block, is_comptime));
ir_set_cursor_at_end(irb, body_block);
IrInstruction *elem_ptr = ir_build_elem_ptr(irb, child_scope, node, array_val_ptr, index_val, false);
IrInstruction *elem_val;
if (node->data.for_expr.elem_is_ptr) {
elem_val = elem_ptr;
} else {
elem_val = ir_build_load_ptr(irb, child_scope, node, elem_ptr);
}
ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, elem_var_ptr, elem_val));
LoopStackItem *loop_stack_item = irb->loop_stack.add_one();
loop_stack_item->break_block = end_block;
loop_stack_item->continue_block = continue_block;
loop_stack_item->is_comptime = is_comptime;
IrInstruction *body_result = ir_gen_node(irb, body_node, child_scope);
irb->loop_stack.pop();
if (!instr_is_unreachable(body_result))
ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime));
ir_set_cursor_at_end(irb, continue_block);
IrInstruction *new_index_val = ir_build_bin_op(irb, child_scope, node, IrBinOpAdd, index_val, one, false);
ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, index_ptr, new_index_val));
ir_build_br(irb, child_scope, node, cond_block, is_comptime);
ir_set_cursor_at_end(irb, end_block);
return ir_build_const_void(irb, child_scope, node);
}
static IrInstruction *ir_gen_this_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeThisLiteral);
if (!scope->parent)
return ir_build_const_import(irb, scope, node, node->owner);
FnTableEntry *fn_entry = scope_get_fn_if_root(scope);
if (fn_entry)
return ir_build_const_fn(irb, scope, node, fn_entry);
if (scope->id == ScopeIdDecls) {
ScopeDecls *decls_scope = (ScopeDecls *)scope;
TypeTableEntry *container_type = decls_scope->container_type;
assert(container_type);
return ir_build_const_type(irb, scope, node, container_type, false);
}
if (scope->id == ScopeIdBlock)
return ir_build_const_scope(irb, scope, node, scope);
zig_unreachable();
}
static IrInstruction *ir_gen_bool_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeBoolLiteral);
return ir_build_const_bool(irb, scope, node, node->data.bool_literal.value);
}
static IrInstruction *ir_gen_string_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeStringLiteral);
if (node->data.string_literal.c) {
return ir_build_const_c_str_lit(irb, scope, node, node->data.string_literal.buf);
} else {
return ir_build_const_str_lit(irb, scope, node, node->data.string_literal.buf);
}
}
static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeArrayType);
AstNode *size_node = node->data.array_type.size;
AstNode *child_type_node = node->data.array_type.child_type;
bool is_const = node->data.array_type.is_const;
if (size_node) {
if (is_const) {
add_node_error(irb->codegen, node, buf_create_from_str("const qualifier invalid on array type"));
return irb->codegen->invalid_instruction;
}
IrInstruction *size_value = ir_gen_node(irb, size_node, scope);
if (size_value == irb->codegen->invalid_instruction)
return size_value;
IrInstruction *child_type = ir_gen_node(irb, child_type_node, scope);
if (child_type == irb->codegen->invalid_instruction)
return child_type;
return ir_build_array_type(irb, scope, node, size_value, child_type);
} else {
IrInstruction *child_type = ir_gen_node(irb, child_type_node, scope);
if (child_type == irb->codegen->invalid_instruction)
return child_type;
return ir_build_slice_type(irb, scope, node, is_const, child_type);
}
}
static IrInstruction *ir_gen_undefined_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeUndefinedLiteral);
return ir_build_const_undefined(irb, scope, node);
}
static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeAsmExpr);
IrInstruction **input_list = allocate<IrInstruction *>(node->data.asm_expr.input_list.length);
IrInstruction **output_types = allocate<IrInstruction *>(node->data.asm_expr.output_list.length);
VariableTableEntry **output_vars = allocate<VariableTableEntry *>(node->data.asm_expr.output_list.length);
size_t return_count = 0;
bool is_volatile = node->data.asm_expr.is_volatile;
if (!is_volatile && node->data.asm_expr.output_list.length == 0) {
add_node_error(irb->codegen, node,
buf_sprintf("assembly expression with no output must be marked volatile"));
return irb->codegen->invalid_instruction;
}
for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1) {
AsmOutput *asm_output = node->data.asm_expr.output_list.at(i);
if (asm_output->return_type) {
return_count += 1;
IrInstruction *return_type = ir_gen_node(irb, asm_output->return_type, scope);
if (return_type == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
if (return_count > 1) {
add_node_error(irb->codegen, node,
buf_sprintf("inline assembly allows up to one output value"));
return irb->codegen->invalid_instruction;
}
output_types[i] = return_type;
} else {
Buf *variable_name = asm_output->variable_name;
VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name);
if (var) {
output_vars[i] = var;
} else {
add_node_error(irb->codegen, node,
buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));
return irb->codegen->invalid_instruction;
}
}
}
for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1) {
AsmInput *asm_input = node->data.asm_expr.input_list.at(i);
IrInstruction *input_value = ir_gen_node(irb, asm_input->expr, scope);
if (input_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
input_list[i] = input_value;
}
return ir_build_asm(irb, scope, node, input_list, output_types, output_vars, return_count, is_volatile);
}
static IrInstruction *ir_gen_if_var_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeIfVarExpr);
AstNodeVariableDeclaration *var_decl = &node->data.if_var_expr.var_decl;
AstNode *expr_node = var_decl->expr;
AstNode *then_node = node->data.if_var_expr.then_block;
AstNode *else_node = node->data.if_var_expr.else_node;
bool var_is_ptr = node->data.if_var_expr.var_is_ptr;
IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPurposeAddressOf);
if (maybe_val_ptr == irb->codegen->invalid_instruction)
return maybe_val_ptr;
IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr);
IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_val);
IrBasicBlock *then_block = ir_build_basic_block(irb, scope, "MaybeThen");
IrBasicBlock *else_block = ir_build_basic_block(irb, scope, "MaybeElse");
IrBasicBlock *endif_block = ir_build_basic_block(irb, scope, "MaybeEndIf");
IrInstruction *is_comptime;
if (ir_should_inline(irb->exec, scope)) {
is_comptime = ir_build_const_bool(irb, scope, node, true);
} else {
is_comptime = ir_build_test_comptime(irb, scope, node, is_non_null);
}
ir_build_cond_br(irb, scope, node, is_non_null, then_block, else_block, is_comptime);
ir_set_cursor_at_end(irb, then_block);
IrInstruction *var_type = nullptr;
if (var_decl->type) {
var_type = ir_gen_node(irb, var_decl->type, scope);
if (var_type == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
}
bool is_shadowable = false;
bool is_const = var_decl->is_const;
VariableTableEntry *var = ir_create_var(irb, node, scope,
var_decl->symbol, is_const, is_const, is_shadowable, is_comptime);
IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, scope, node, maybe_val_ptr, false);
IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, node, var_ptr_value);
ir_build_var_decl(irb, scope, node, var, var_type, var_value);
IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var->child_scope);
if (then_expr_result == irb->codegen->invalid_instruction)
return then_expr_result;
IrBasicBlock *after_then_block = irb->current_basic_block;
if (!instr_is_unreachable(then_expr_result))
ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
ir_set_cursor_at_end(irb, else_block);
IrInstruction *else_expr_result;
if (else_node) {
else_expr_result = ir_gen_node(irb, else_node, scope);
if (else_expr_result == irb->codegen->invalid_instruction)
return else_expr_result;
} else {
else_expr_result = ir_build_const_void(irb, scope, node);
}
IrBasicBlock *after_else_block = irb->current_basic_block;
if (!instr_is_unreachable(else_expr_result))
ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
ir_set_cursor_at_end(irb, endif_block);
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
incoming_values[0] = then_expr_result;
incoming_values[1] = else_expr_result;
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
incoming_blocks[0] = after_then_block;
incoming_blocks[1] = after_else_block;
return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values);
}
static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *switch_node, AstNode *prong_node,
IrBasicBlock *end_block, IrInstruction *is_comptime, IrInstruction *target_value_ptr, IrInstruction *prong_value,
ZigList<IrBasicBlock *> *incoming_blocks, ZigList<IrInstruction *> *incoming_values)
{
assert(switch_node->type == NodeTypeSwitchExpr);
assert(prong_node->type == NodeTypeSwitchProng);
AstNode *expr_node = prong_node->data.switch_prong.expr;
AstNode *var_symbol_node = prong_node->data.switch_prong.var_symbol;
Scope *child_scope;
if (var_symbol_node) {
assert(var_symbol_node->type == NodeTypeSymbol);
Buf *var_name = var_symbol_node->data.symbol_expr.symbol;
bool var_is_ptr = prong_node->data.switch_prong.var_is_ptr;
bool is_shadowable = false;
bool is_const = true;
VariableTableEntry *var = ir_create_var(irb, var_symbol_node, scope,
var_name, is_const, is_const, is_shadowable, is_comptime);
child_scope = var->child_scope;
IrInstruction *var_value;
if (prong_value) {
IrInstruction *var_ptr_value = ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr, prong_value);
var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, var_symbol_node, var_ptr_value);
} else {
var_value = var_is_ptr ? target_value_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, target_value_ptr);
}
IrInstruction *var_type = nullptr; // infer the type
ir_build_var_decl(irb, scope, var_symbol_node, var, var_type, var_value);
} else {
child_scope = scope;
}
IrInstruction *expr_result = ir_gen_node(irb, expr_node, child_scope);
if (expr_result == irb->codegen->invalid_instruction)
return false;
if (!instr_is_unreachable(expr_result))
ir_mark_gen(ir_build_br(irb, scope, switch_node, end_block, is_comptime));
incoming_blocks->append(irb->current_basic_block);
incoming_values->append(expr_result);
return true;
}
static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeSwitchExpr);
AstNode *target_node = node->data.switch_expr.expr;
IrInstruction *target_value_ptr = ir_gen_node_extra(irb, target_node, scope, LValPurposeAddressOf);
if (target_value_ptr == irb->codegen->invalid_instruction)
return target_value_ptr;
IrInstruction *target_value = ir_build_switch_target(irb, scope, node, target_value_ptr);
IrBasicBlock *else_block = ir_build_basic_block(irb, scope, "SwitchElse");
IrBasicBlock *end_block = ir_build_basic_block(irb, scope, "SwitchEnd");
size_t prong_count = node->data.switch_expr.prongs.length;
ZigList<IrInstructionSwitchBrCase> cases = {0};
IrInstruction *is_comptime;
if (ir_should_inline(irb->exec, scope)) {
is_comptime = ir_build_const_bool(irb, scope, node, true);
} else {
is_comptime = ir_build_test_comptime(irb, scope, node, target_value);
}
ZigList<IrInstruction *> incoming_values = {0};
ZigList<IrBasicBlock *> incoming_blocks = {0};
ZigList<IrInstructionCheckSwitchProngsRange> check_ranges = {0};
Scope *comptime_scope = create_comptime_scope(node, scope);
AstNode *else_prong = nullptr;
for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {
AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
size_t prong_item_count = prong_node->data.switch_prong.items.length;
if (prong_item_count == 0) {
if (else_prong) {
ErrorMsg *msg = add_node_error(irb->codegen, prong_node,
buf_sprintf("multiple else prongs in switch expression"));
add_error_note(irb->codegen, msg, else_prong,
buf_sprintf("previous else prong is here"));
return irb->codegen->invalid_instruction;
}
else_prong = prong_node;
IrBasicBlock *prev_block = irb->current_basic_block;
ir_set_cursor_at_end(irb, else_block);
if (!ir_gen_switch_prong_expr(irb, scope, node, prong_node, end_block,
is_comptime, target_value_ptr, nullptr, &incoming_blocks, &incoming_values))
{
return irb->codegen->invalid_instruction;
}
ir_set_cursor_at_end(irb, prev_block);
} else {
if (prong_node->data.switch_prong.any_items_are_range) {
IrInstruction *ok_bit = nullptr;
AstNode *last_item_node = nullptr;
for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) {
AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
last_item_node = item_node;
if (item_node->type == NodeTypeSwitchRange) {
AstNode *start_node = item_node->data.switch_range.start;
AstNode *end_node = item_node->data.switch_range.end;
IrInstruction *start_value = ir_gen_node(irb, start_node, comptime_scope);
if (start_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *end_value = ir_gen_node(irb, end_node, comptime_scope);
if (end_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstructionCheckSwitchProngsRange *check_range = check_ranges.add_one();
check_range->start = start_value;
check_range->end = end_value;
IrInstruction *lower_range_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpGreaterOrEq,
target_value, start_value, false);
IrInstruction *upper_range_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpLessOrEq,
target_value, end_value, false);
IrInstruction *both_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolAnd,
lower_range_ok, upper_range_ok, false);
if (ok_bit) {
ok_bit = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolOr, both_ok, ok_bit, false);
} else {
ok_bit = both_ok;
}
} else {
IrInstruction *item_value = ir_gen_node(irb, item_node, comptime_scope);
if (item_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstructionCheckSwitchProngsRange *check_range = check_ranges.add_one();
check_range->start = item_value;
check_range->end = item_value;
IrInstruction *cmp_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpEq,
item_value, target_value, false);
if (ok_bit) {
ok_bit = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolOr, cmp_ok, ok_bit, false);
} else {
ok_bit = cmp_ok;
}
}
}
IrBasicBlock *range_block_yes = ir_build_basic_block(irb, scope, "SwitchRangeYes");
IrBasicBlock *range_block_no = ir_build_basic_block(irb, scope, "SwitchRangeNo");
assert(ok_bit);
assert(last_item_node);
ir_mark_gen(ir_build_cond_br(irb, scope, last_item_node, ok_bit, range_block_yes,
range_block_no, is_comptime));
ir_set_cursor_at_end(irb, range_block_yes);
if (!ir_gen_switch_prong_expr(irb, scope, node, prong_node, end_block,
is_comptime, target_value_ptr, nullptr, &incoming_blocks, &incoming_values))
{
return irb->codegen->invalid_instruction;
}
ir_set_cursor_at_end(irb, range_block_no);
} else {
IrBasicBlock *prong_block = ir_build_basic_block(irb, scope, "SwitchProng");
IrInstruction *last_item_value = nullptr;
for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) {
AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
assert(item_node->type != NodeTypeSwitchRange);
IrInstruction *item_value = ir_gen_node(irb, item_node, comptime_scope);
if (item_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstructionCheckSwitchProngsRange *check_range = check_ranges.add_one();
check_range->start = item_value;
check_range->end = item_value;
IrInstructionSwitchBrCase *this_case = cases.add_one();
this_case->value = item_value;
this_case->block = prong_block;
last_item_value = item_value;
}
IrInstruction *only_item_value = (prong_item_count == 1) ? last_item_value : nullptr;
IrBasicBlock *prev_block = irb->current_basic_block;
ir_set_cursor_at_end(irb, prong_block);
if (!ir_gen_switch_prong_expr(irb, scope, node, prong_node, end_block,
is_comptime, target_value_ptr, only_item_value, &incoming_blocks, &incoming_values))
{
return irb->codegen->invalid_instruction;
}
ir_set_cursor_at_end(irb, prev_block);
}
}
}
if (!else_prong) {
ir_build_check_switch_prongs(irb, scope, node, target_value, check_ranges.items, check_ranges.length);
}
if (cases.length == 0) {
ir_build_br(irb, scope, node, else_block, is_comptime);
} else {
ir_build_switch_br(irb, scope, node, target_value, else_block, cases.length, cases.items, is_comptime);
}
if (!else_prong) {
ir_set_cursor_at_end(irb, else_block);
ir_build_unreachable(irb, scope, node);
}
ir_set_cursor_at_end(irb, end_block);
assert(incoming_blocks.length == incoming_values.length);
return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
}
static IrInstruction *ir_gen_goto(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeGoto);
// make a placeholder unreachable statement and a note to come back and
// replace the instruction with a branch instruction
IrGotoItem *goto_item = irb->exec->goto_list.add_one();
goto_item->bb = irb->current_basic_block;
goto_item->instruction_index = irb->current_basic_block->instruction_list.length;
goto_item->source_node = node;
goto_item->scope = scope;
// we don't know if we need to generate defer expressions yet
// we do that later when we find out which label we're jumping to.
return ir_build_unreachable(irb, scope, node);
}
static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNode *node, LValPurpose lval) {
assert(node->type == NodeTypeCompTime);
Scope *child_scope = create_comptime_scope(node, parent_scope);
return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval);
}
static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeBreak);
if (irb->loop_stack.length == 0) {
add_node_error(irb->codegen, node,
buf_sprintf("'break' expression outside loop"));
return irb->codegen->invalid_instruction;
}
LoopStackItem *loop_stack_item = &irb->loop_stack.last();
IrInstruction *is_comptime;
if (ir_should_inline(irb->exec, scope)) {
is_comptime = ir_build_const_bool(irb, scope, node, true);
} else {
is_comptime = loop_stack_item->is_comptime;
}
IrBasicBlock *dest_block = loop_stack_item->break_block;
ir_gen_defers_for_block(irb, scope, dest_block->scope, false, false);
return ir_build_br(irb, scope, node, dest_block, is_comptime);
}
static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeContinue);
if (irb->loop_stack.length == 0) {
add_node_error(irb->codegen, node,
buf_sprintf("'continue' expression outside loop"));
return irb->codegen->invalid_instruction;
}
LoopStackItem *loop_stack_item = &irb->loop_stack.last();
IrInstruction *is_comptime;
if (ir_should_inline(irb->exec, scope)) {
is_comptime = ir_build_const_bool(irb, scope, node, true);
} else {
is_comptime = loop_stack_item->is_comptime;
}
IrBasicBlock *dest_block = loop_stack_item->continue_block;
ir_gen_defers_for_block(irb, scope, dest_block->scope, false, false);
return ir_build_br(irb, scope, node, dest_block, is_comptime);
}
static IrInstruction *ir_gen_type_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeTypeLiteral);
return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_type, false);
}
static IrInstruction *ir_gen_error_type(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeErrorType);
return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_pure_error, false);
}
static IrInstruction *ir_gen_defer(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
assert(node->type == NodeTypeDefer);
ScopeDefer *defer_child_scope = create_defer_scope(node, parent_scope);
node->data.defer.child_scope = &defer_child_scope->base;
ScopeDeferExpr *defer_expr_scope = create_defer_expr_scope(node, parent_scope);
node->data.defer.expr_scope = &defer_expr_scope->base;
return ir_build_const_void(irb, parent_scope, node);
}
static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeSliceExpr);
AstNodeSliceExpr *slice_expr = &node->data.slice_expr;
AstNode *array_node = slice_expr->array_ref_expr;
AstNode *start_node = slice_expr->start;
AstNode *end_node = slice_expr->end;
IrInstruction *ptr_value = ir_gen_node(irb, array_node, scope);
if (ptr_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *start_value = ir_gen_node(irb, start_node, scope);
if (ptr_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *end_value;
if (end_node) {
end_value = ir_gen_node(irb, end_node, scope);
if (end_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
} else {
end_value = nullptr;
}
return ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, slice_expr->is_const, true);
}
static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
assert(node->type == NodeTypeUnwrapErrorExpr);
AstNode *op1_node = node->data.unwrap_err_expr.op1;
AstNode *op2_node = node->data.unwrap_err_expr.op2;
AstNode *var_node = node->data.unwrap_err_expr.symbol;
IrInstruction *err_union_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPurposeAddressOf);
if (err_union_ptr == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *err_union_val = ir_build_load_ptr(irb, parent_scope, node, err_union_ptr);
IrInstruction *is_err = ir_build_test_err(irb, parent_scope, node, err_union_val);
IrInstruction *is_comptime;
if (ir_should_inline(irb->exec, parent_scope)) {
is_comptime = ir_build_const_bool(irb, parent_scope, node, true);
} else {
is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_err);
}
IrBasicBlock *ok_block = ir_build_basic_block(irb, parent_scope, "UnwrapErrOk");
IrBasicBlock *err_block = ir_build_basic_block(irb, parent_scope, "UnwrapErrError");
IrBasicBlock *end_block = ir_build_basic_block(irb, parent_scope, "UnwrapErrEnd");
ir_build_cond_br(irb, parent_scope, node, is_err, err_block, ok_block, is_comptime);
ir_set_cursor_at_end(irb, err_block);
Scope *err_scope;
if (var_node) {
assert(var_node->type == NodeTypeSymbol);
IrInstruction *var_type = ir_build_const_type(irb, parent_scope, node,
irb->codegen->builtin_types.entry_pure_error, false);
Buf *var_name = var_node->data.symbol_expr.symbol;
bool is_const = true;
bool is_shadowable = false;
VariableTableEntry *var = ir_create_var(irb, node, parent_scope, var_name,
is_const, is_const, is_shadowable, is_comptime);
err_scope = var->child_scope;
IrInstruction *err_val = ir_build_unwrap_err_code(irb, err_scope, node, err_union_ptr);
ir_build_var_decl(irb, err_scope, var_node, var, var_type, err_val);
} else {
err_scope = parent_scope;
}
IrInstruction *err_result = ir_gen_node(irb, op2_node, err_scope);
if (err_result == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrBasicBlock *after_err_block = irb->current_basic_block;
if (!instr_is_unreachable(err_result))
ir_mark_gen(ir_build_br(irb, err_scope, node, end_block, is_comptime));
ir_set_cursor_at_end(irb, ok_block);
IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, parent_scope, node, err_union_ptr, false);
IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr);
IrBasicBlock *after_ok_block = irb->current_basic_block;
ir_build_br(irb, parent_scope, node, end_block, is_comptime);
ir_set_cursor_at_end(irb, end_block);
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
incoming_values[0] = err_result;
incoming_values[1] = unwrapped_payload;
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
incoming_blocks[0] = after_err_block;
incoming_blocks[1] = after_ok_block;
return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values);
}
static bool render_instance_name_recursive(Buf *name, Scope *outer_scope, Scope *inner_scope) {
if (inner_scope == nullptr || inner_scope == outer_scope) return false;
bool need_comma = render_instance_name_recursive(name, outer_scope, inner_scope->parent);
if (inner_scope->id != ScopeIdVarDecl)
return need_comma;
ScopeVarDecl *var_scope = (ScopeVarDecl *)inner_scope;
if (need_comma)
buf_append_char(name, ',');
render_const_value(name, &var_scope->var->value);
return true;
}
static IrInstruction *ir_gen_container_decl(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
assert(node->type == NodeTypeContainerDecl);
ContainerKind kind = node->data.container_decl.kind;
Buf *name;
if (irb->exec->name) {
name = irb->exec->name;
} else {
FnTableEntry *fn_entry = exec_fn_entry(irb->exec);
if (fn_entry) {
name = buf_alloc();
buf_append_buf(name, &fn_entry->symbol_name);
buf_appendf(name, "(");
render_instance_name_recursive(name, &fn_entry->fndef_scope->base, irb->exec->begin_scope);
buf_appendf(name, ")");
} else {
name = buf_sprintf("(anonymous %s at %s:%zu:%zu)", container_string(kind),
buf_ptr(node->owner->path), node->line + 1, node->column + 1);
}
}
VisibMod visib_mod = VisibModPub;
TldContainer *tld_container = allocate<TldContainer>(1);
init_tld(&tld_container->base, TldIdContainer, name, visib_mod, node, parent_scope);
TypeTableEntry *container_type = get_partial_container_type(irb->codegen, parent_scope, kind, node, buf_ptr(name),
node->data.container_decl.is_extern);
ScopeDecls *child_scope = get_container_scope(container_type);
tld_container->type_entry = container_type;
tld_container->decls_scope = child_scope;
for (size_t i = 0; i < node->data.container_decl.decls.length; i += 1) {
AstNode *child_node = node->data.container_decl.decls.at(i);
scan_decls(irb->codegen, child_scope, child_node);
}
irb->codegen->resolve_queue.append(&tld_container->base);
return ir_build_const_type(irb, parent_scope, node, container_type, false);
}
static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
assert(node->type == NodeTypeFnProto);
size_t param_count = node->data.fn_proto.params.length;
IrInstruction **param_types = allocate<IrInstruction*>(param_count);
for (size_t i = 0; i < param_count; i += 1) {
AstNode *param_node = node->data.fn_proto.params.at(i);
AstNode *type_node = param_node->data.param_decl.type;
IrInstruction *type_value = ir_gen_node(irb, type_node, parent_scope);
if (type_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
param_types[i] = type_value;
}
IrInstruction *return_type = ir_gen_node(irb, node->data.fn_proto.return_type, parent_scope);
if (return_type == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
return ir_build_fn_proto(irb, parent_scope, node, param_types, return_type);
}
static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope,
LValPurpose lval)
{
assert(scope);
switch (node->type) {
case NodeTypeStructValueField:
case NodeTypeRoot:
case NodeTypeParamDecl:
case NodeTypeUse:
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeStructField:
case NodeTypeLabel:
zig_unreachable();
case NodeTypeBlock:
return ir_lval_wrap(irb, scope, ir_gen_block(irb, scope, node), lval);
case NodeTypeBinOpExpr:
return ir_lval_wrap(irb, scope, ir_gen_bin_op(irb, scope, node), lval);
case NodeTypeNumberLiteral:
return ir_lval_wrap(irb, scope, ir_gen_num_lit(irb, scope, node), lval);
case NodeTypeCharLiteral:
return ir_lval_wrap(irb, scope, ir_gen_char_lit(irb, scope, node), lval);
case NodeTypeSymbol:
return ir_gen_symbol(irb, scope, node, lval);
case NodeTypeFnCallExpr:
return ir_lval_wrap(irb, scope, ir_gen_fn_call(irb, scope, node), lval);
case NodeTypeIfBoolExpr:
return ir_lval_wrap(irb, scope, ir_gen_if_bool_expr(irb, scope, node), lval);
case NodeTypePrefixOpExpr:
return ir_gen_prefix_op_expr(irb, scope, node, lval);
case NodeTypeContainerInitExpr:
return ir_lval_wrap(irb, scope, ir_gen_container_init_expr(irb, scope, node), lval);
case NodeTypeVariableDeclaration:
return ir_lval_wrap(irb, scope, ir_gen_var_decl(irb, scope, node), lval);
case NodeTypeWhileExpr:
return ir_lval_wrap(irb, scope, ir_gen_while_expr(irb, scope, node), lval);
case NodeTypeForExpr:
return ir_lval_wrap(irb, scope, ir_gen_for_expr(irb, scope, node), lval);
case NodeTypeArrayAccessExpr:
return ir_gen_array_access(irb, scope, node, lval);
case NodeTypeReturnExpr:
return ir_gen_return(irb, scope, node, lval);
case NodeTypeFieldAccessExpr:
return ir_gen_field_access(irb, scope, node, lval);
case NodeTypeThisLiteral:
return ir_lval_wrap(irb, scope, ir_gen_this_literal(irb, scope, node), lval);
case NodeTypeBoolLiteral:
return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval);
case NodeTypeArrayType:
return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval);
case NodeTypeStringLiteral:
return ir_lval_wrap(irb, scope, ir_gen_string_literal(irb, scope, node), lval);
case NodeTypeUndefinedLiteral:
return ir_lval_wrap(irb, scope, ir_gen_undefined_literal(irb, scope, node), lval);
case NodeTypeAsmExpr:
return ir_lval_wrap(irb, scope, ir_gen_asm_expr(irb, scope, node), lval);
case NodeTypeNullLiteral:
return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval);
case NodeTypeVarLiteral:
return ir_lval_wrap(irb, scope, ir_gen_var_literal(irb, scope, node), lval);
case NodeTypeIfVarExpr:
return ir_lval_wrap(irb, scope, ir_gen_if_var_expr(irb, scope, node), lval);
case NodeTypeSwitchExpr:
return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node), lval);
case NodeTypeGoto:
return ir_lval_wrap(irb, scope, ir_gen_goto(irb, scope, node), lval);
case NodeTypeCompTime:
return ir_gen_comptime(irb, scope, node, lval);
case NodeTypeTypeLiteral:
return ir_lval_wrap(irb, scope, ir_gen_type_literal(irb, scope, node), lval);
case NodeTypeErrorType:
return ir_lval_wrap(irb, scope, ir_gen_error_type(irb, scope, node), lval);
case NodeTypeBreak:
return ir_lval_wrap(irb, scope, ir_gen_break(irb, scope, node), lval);
case NodeTypeContinue:
return ir_lval_wrap(irb, scope, ir_gen_continue(irb, scope, node), lval);
case NodeTypeDefer:
return ir_lval_wrap(irb, scope, ir_gen_defer(irb, scope, node), lval);
case NodeTypeSliceExpr:
return ir_lval_wrap(irb, scope, ir_gen_slice(irb, scope, node), lval);
case NodeTypeUnwrapErrorExpr:
return ir_lval_wrap(irb, scope, ir_gen_err_ok_or(irb, scope, node), lval);
case NodeTypeContainerDecl:
return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval);
case NodeTypeFnProto:
return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval);
case NodeTypeFnDef:
zig_panic("TODO IR gen NodeTypeFnDef");
case NodeTypeFnDecl:
zig_panic("TODO IR gen NodeTypeFnDecl");
case NodeTypeErrorValueDecl:
zig_panic("TODO IR gen NodeTypeErrorValueDecl");
case NodeTypeTypeDecl:
zig_panic("TODO IR gen NodeTypeTypeDecl");
}
zig_unreachable();
}
static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope,
LValPurpose lval)
{
IrInstruction *result = ir_gen_node_raw(irb, node, scope, lval);
irb->exec->invalid = irb->exec->invalid || (result == irb->codegen->invalid_instruction);
return result;
}
static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope) {
return ir_gen_node_extra(irb, node, scope, LValPurposeNone);
}
static bool ir_goto_pass2(IrBuilder *irb) {
for (size_t i = 0; i < irb->exec->goto_list.length; i += 1) {
IrGotoItem *goto_item = &irb->exec->goto_list.at(i);
AstNode *source_node = goto_item->source_node;
// Since a goto will always end a basic block, we move the "current instruction"
// index back to over the placeholder unreachable instruction and begin overwriting
irb->current_basic_block = goto_item->bb;
irb->current_basic_block->instruction_list.resize(goto_item->instruction_index);
Buf *label_name = source_node->data.goto_expr.name;
LabelTableEntry *label = find_label(irb->exec, goto_item->scope, label_name);
if (!label) {
add_node_error(irb->codegen, source_node,
buf_sprintf("no label in scope named '%s'", buf_ptr(label_name)));
return false;
}
label->used = true;
IrInstruction *is_comptime = ir_build_const_bool(irb, goto_item->scope, source_node,
ir_should_inline(irb->exec, goto_item->scope) || source_node->data.goto_expr.is_inline);
if (!ir_gen_defers_for_block(irb, goto_item->scope, label->bb->scope, false, false)) {
add_node_error(irb->codegen, source_node,
buf_sprintf("no label in scope named '%s'", buf_ptr(label_name)));
return false;
}
ir_build_br(irb, goto_item->scope, source_node, label->bb, is_comptime);
}
for (size_t i = 0; i < irb->exec->all_labels.length; i += 1) {
LabelTableEntry *label = irb->exec->all_labels.at(i);
if (!label->used) {
add_node_error(irb->codegen, label->decl_node,
buf_sprintf("label '%s' defined but not used",
buf_ptr(label->decl_node->data.label.name)));
return false;
}
}
return true;
}
bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_executable) {
assert(node->owner);
IrBuilder ir_builder = {0};
IrBuilder *irb = &ir_builder;
irb->codegen = codegen;
irb->exec = ir_executable;
irb->current_basic_block = ir_build_basic_block(irb, scope, "Entry");
// Entry block gets a reference because we enter it to begin.
ir_ref_bb(irb->current_basic_block);
IrInstruction *result = ir_gen_node_extra(irb, node, scope, LValPurposeNone);
assert(result);
if (irb->exec->invalid)
return false;
if (!instr_is_unreachable(result)) {
ir_mark_gen(ir_build_return(irb, scope, result->source_node, result));
}
if (!ir_goto_pass2(irb)) {
irb->exec->invalid = true;
return false;
}
return true;
}
bool ir_gen_fn(CodeGen *codegen, FnTableEntry *fn_entry) {
assert(fn_entry);
IrExecutable *ir_executable = &fn_entry->ir_executable;
AstNode *fn_def_node = fn_entry->fn_def_node;
assert(fn_def_node->type == NodeTypeFnDef);
AstNode *body_node = fn_def_node->data.fn_def.body;
assert(fn_entry->child_scope);
return ir_gen(codegen, body_node, fn_entry->child_scope, ir_executable);
}
static void add_call_stack_errors(CodeGen *codegen, IrExecutable *exec, ErrorMsg *err_msg, int limit) {
if (!exec || !exec->source_node || limit < 0) return;
add_error_note(codegen, err_msg, exec->source_node, buf_sprintf("called from here"));
add_call_stack_errors(codegen, exec->parent_exec, err_msg, limit - 1);
}
static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutable *exec, AstNode *source_node, Buf *msg) {
exec->invalid = true;
ErrorMsg *err_msg = add_node_error(codegen, source_node, msg);
if (exec->parent_exec) {
add_call_stack_errors(codegen, exec, err_msg, 10);
}
return err_msg;
}
static ErrorMsg *ir_add_error_node(IrAnalyze *ira, AstNode *source_node, Buf *msg) {
return exec_add_error_node(ira->codegen, ira->new_irb.exec, source_node, msg);
}
static ErrorMsg *ir_add_error(IrAnalyze *ira, IrInstruction *source_instruction, Buf *msg) {
return ir_add_error_node(ira, source_instruction->source_node, msg);
}
static void ir_add_typedef_err_note(IrAnalyze *ira, ErrorMsg *msg, TypeTableEntry *type_entry) {
if (type_entry->id == TypeTableEntryIdTypeDecl) {
// requires tracking source_node in the typedecl type
zig_panic("TODO add error note about typedecls");
}
}
static IrInstruction *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec) {
IrBasicBlock *bb = exec->basic_block_list.at(0);
for (size_t i = 0; i < bb->instruction_list.length; i += 1) {
IrInstruction *instruction = bb->instruction_list.at(i);
if (instruction->id == IrInstructionIdReturn) {
IrInstructionReturn *ret_inst = (IrInstructionReturn *)instruction;
IrInstruction *value = ret_inst->value;
if (value->value.special == ConstValSpecialRuntime) {
exec_add_error_node(codegen, exec, value->source_node,
buf_sprintf("unable to evaluate constant expression"));
return codegen->invalid_instruction;
}
return value;
} else if (ir_has_side_effects(instruction)) {
exec_add_error_node(codegen, exec, instruction->source_node,
buf_sprintf("unable to evaluate constant expression"));
return codegen->invalid_instruction;
}
}
zig_unreachable();
}
static bool ir_emit_global_runtime_side_effect(IrAnalyze *ira, IrInstruction *source_instruction) {
if (ir_should_inline(ira->new_irb.exec, source_instruction->scope)) {
ir_add_error(ira, source_instruction, buf_sprintf("unable to evaluate constant expression"));
return false;
}
return true;
}
static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruction, TypeTableEntry *other_type) {
TypeTableEntry *other_type_underlying = get_underlying_type(other_type);
if (other_type_underlying->id == TypeTableEntryIdInvalid) {
return false;
}
ConstExprValue *const_val = &instruction->value;
assert(const_val->special != ConstValSpecialRuntime);
if (other_type_underlying->id == TypeTableEntryIdFloat) {
return true;
} else if (other_type_underlying->id == TypeTableEntryIdInt &&
const_val->data.x_bignum.kind == BigNumKindInt)
{
if (bignum_fits_in_bits(&const_val->data.x_bignum, other_type_underlying->data.integral.bit_count,
other_type_underlying->data.integral.is_signed))
{
return true;
}
} else if ((other_type_underlying->id == TypeTableEntryIdNumLitFloat &&
const_val->data.x_bignum.kind == BigNumKindFloat) ||
(other_type_underlying->id == TypeTableEntryIdNumLitInt &&
const_val->data.x_bignum.kind == BigNumKindInt))
{
return true;
}
const char *num_lit_str = (const_val->data.x_bignum.kind == BigNumKindFloat) ? "float" : "integer";
ir_add_error(ira, instruction,
buf_sprintf("%s value %s cannot be implicitly casted to type '%s'",
num_lit_str,
buf_ptr(bignum_to_buf(&const_val->data.x_bignum)),
buf_ptr(&other_type->name)));
return false;
}
enum ImplicitCastMatchResult {
ImplicitCastMatchResultNo,
ImplicitCastMatchResultYes,
ImplicitCastMatchResultReportedError,
};
static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *expected_type,
TypeTableEntry *actual_type, IrInstruction *value)
{
if (types_match_const_cast_only(expected_type, actual_type)) {
return ImplicitCastMatchResultYes;
}
// implicit conversion from anything to var
if (expected_type->id == TypeTableEntryIdVar) {
return ImplicitCastMatchResultYes;
}
// implicit conversion from non maybe type to maybe type
if (expected_type->id == TypeTableEntryIdMaybe &&
ir_types_match_with_implicit_cast(ira, expected_type->data.maybe.child_type, actual_type, value))
{
return ImplicitCastMatchResultYes;
}
// implicit conversion from null literal to maybe type
if (expected_type->id == TypeTableEntryIdMaybe &&
actual_type->id == TypeTableEntryIdNullLit)
{
return ImplicitCastMatchResultYes;
}
// implicit conversion from error child type to error type
if (expected_type->id == TypeTableEntryIdErrorUnion &&
ir_types_match_with_implicit_cast(ira, expected_type->data.error.child_type, actual_type, value))
{
return ImplicitCastMatchResultYes;
}
// implicit conversion from pure error to error union type
if (expected_type->id == TypeTableEntryIdErrorUnion &&
actual_type->id == TypeTableEntryIdPureError)
{
return ImplicitCastMatchResultYes;
}
// implicit widening conversion
if (expected_type->id == TypeTableEntryIdInt &&
actual_type->id == TypeTableEntryIdInt &&
expected_type->data.integral.is_signed == actual_type->data.integral.is_signed &&
expected_type->data.integral.bit_count >= actual_type->data.integral.bit_count)
{
return ImplicitCastMatchResultYes;
}
// small enough unsigned ints can get casted to large enough signed ints
if (expected_type->id == TypeTableEntryIdInt && expected_type->data.integral.is_signed &&
actual_type->id == TypeTableEntryIdInt && !actual_type->data.integral.is_signed &&
expected_type->data.integral.bit_count > actual_type->data.integral.bit_count)
{
return ImplicitCastMatchResultYes;
}
// implicit float widening conversion
if (expected_type->id == TypeTableEntryIdFloat &&
actual_type->id == TypeTableEntryIdFloat &&
expected_type->data.floating.bit_count >= actual_type->data.floating.bit_count)
{
return ImplicitCastMatchResultYes;
}
// implicit array to slice conversion
if (expected_type->id == TypeTableEntryIdStruct &&
expected_type->data.structure.is_slice &&
actual_type->id == TypeTableEntryIdArray &&
types_match_const_cast_only(
expected_type->data.structure.fields[0].type_entry->data.pointer.child_type,
actual_type->data.array.child_type))
{
return ImplicitCastMatchResultYes;
}
// implicit number literal to typed number
if ((actual_type->id == TypeTableEntryIdNumLitFloat ||
actual_type->id == TypeTableEntryIdNumLitInt))
{
if (ir_num_lit_fits_in_other_type(ira, value, expected_type)) {
return ImplicitCastMatchResultYes;
} else {
return ImplicitCastMatchResultReportedError;
}
}
// implicit undefined literal to anything
if (actual_type->id == TypeTableEntryIdUndefLit) {
return ImplicitCastMatchResultYes;
}
// implicitly take a const pointer to something
if (!type_requires_comptime(actual_type)) {
TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true);
if (types_match_const_cast_only(expected_type, const_ptr_actual)) {
return ImplicitCastMatchResultYes;
}
}
return ImplicitCastMatchResultNo;
}
static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, IrInstruction **instructions, size_t instruction_count) {
assert(instruction_count >= 1);
IrInstruction *prev_inst = instructions[0];
if (prev_inst->value.type->id == TypeTableEntryIdInvalid) {
return ira->codegen->builtin_types.entry_invalid;
}
bool any_are_pure_error = (prev_inst->value.type->id == TypeTableEntryIdPureError);
bool any_are_null = (prev_inst->value.type->id == TypeTableEntryIdNullLit);
for (size_t i = 1; i < instruction_count; i += 1) {
IrInstruction *cur_inst = instructions[i];
TypeTableEntry *cur_type = cur_inst->value.type;
TypeTableEntry *prev_type = prev_inst->value.type;
if (cur_type->id == TypeTableEntryIdInvalid) {
return cur_type;
} else if (prev_type->id == TypeTableEntryIdUnreachable) {
prev_inst = cur_inst;
} else if (cur_type->id == TypeTableEntryIdUnreachable) {
continue;
} else if (prev_type->id == TypeTableEntryIdPureError) {
prev_inst = cur_inst;
continue;
} else if (prev_type->id == TypeTableEntryIdNullLit) {
prev_inst = cur_inst;
continue;
} else if (cur_type->id == TypeTableEntryIdPureError) {
any_are_pure_error = true;
continue;
} else if (cur_type->id == TypeTableEntryIdNullLit) {
any_are_null = true;
continue;
} else if (types_match_const_cast_only(prev_type, cur_type)) {
continue;
} else if (types_match_const_cast_only(cur_type, prev_type)) {
prev_inst = cur_inst;
continue;
} else if (prev_type->id == TypeTableEntryIdInt &&
cur_type->id == TypeTableEntryIdInt &&
prev_type->data.integral.is_signed == cur_type->data.integral.is_signed)
{
if (cur_type->data.integral.bit_count > prev_type->data.integral.bit_count) {
prev_inst = cur_inst;
}
continue;
} else if (prev_type->id == TypeTableEntryIdFloat &&
cur_type->id == TypeTableEntryIdFloat)
{
if (cur_type->data.floating.bit_count > prev_type->data.floating.bit_count) {
prev_inst = cur_inst;
}
} else if (prev_type->id == TypeTableEntryIdErrorUnion &&
types_match_const_cast_only(prev_type->data.error.child_type, cur_type))
{
continue;
} else if (cur_type->id == TypeTableEntryIdErrorUnion &&
types_match_const_cast_only(cur_type->data.error.child_type, prev_type))
{
prev_inst = cur_inst;
continue;
} else if (prev_type->id == TypeTableEntryIdNumLitInt ||
prev_type->id == TypeTableEntryIdNumLitFloat)
{
if (ir_num_lit_fits_in_other_type(ira, prev_inst, cur_type)) {
prev_inst = cur_inst;
continue;
} else {
return ira->codegen->builtin_types.entry_invalid;
}
} else if (cur_type->id == TypeTableEntryIdNumLitInt ||
cur_type->id == TypeTableEntryIdNumLitFloat)
{
if (ir_num_lit_fits_in_other_type(ira, cur_inst, prev_type)) {
continue;
} else {
return ira->codegen->builtin_types.entry_invalid;
}
} else {
ErrorMsg *msg = ir_add_error_node(ira, source_node,
buf_sprintf("incompatible types: '%s' and '%s'",
buf_ptr(&prev_type->name), buf_ptr(&cur_type->name)));
add_error_note(ira->codegen, msg, prev_inst->source_node,
buf_sprintf("type '%s' here", buf_ptr(&prev_type->name)));
add_error_note(ira->codegen, msg, cur_inst->source_node,
buf_sprintf("type '%s' here", buf_ptr(&cur_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
}
if (any_are_pure_error && prev_inst->value.type->id != TypeTableEntryIdPureError) {
if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt ||
prev_inst->value.type->id == TypeTableEntryIdNumLitFloat)
{
ir_add_error_node(ira, source_node,
buf_sprintf("unable to make error union out of number literal"));
return ira->codegen->builtin_types.entry_invalid;
} else if (prev_inst->value.type->id == TypeTableEntryIdNullLit) {
ir_add_error_node(ira, source_node,
buf_sprintf("unable to make error union out of null literal"));
return ira->codegen->builtin_types.entry_invalid;
} else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) {
return prev_inst->value.type;
} else {
return get_error_type(ira->codegen, prev_inst->value.type);
}
} else if (any_are_null && prev_inst->value.type->id != TypeTableEntryIdNullLit) {
if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt ||
prev_inst->value.type->id == TypeTableEntryIdNumLitFloat)
{
ir_add_error_node(ira, source_node,
buf_sprintf("unable to make maybe out of number literal"));
return ira->codegen->builtin_types.entry_invalid;
} else if (prev_inst->value.type->id == TypeTableEntryIdMaybe) {
return prev_inst->value.type;
} else {
return get_maybe_type(ira->codegen, prev_inst->value.type);
}
} else {
return prev_inst->value.type;
}
}
static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, TypeTableEntry *type_entry) {
if (type_has_bits(type_entry) && handle_is_ptr(type_entry)) {
FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
assert(fn_entry);
fn_entry->alloca_list.append(instruction);
}
}
static void eval_const_expr_implicit_cast(CastOp cast_op,
ConstExprValue *other_val, TypeTableEntry *other_type,
ConstExprValue *const_val, TypeTableEntry *new_type)
{
const_val->depends_on_compile_var = other_val->depends_on_compile_var;
const_val->special = other_val->special;
assert(other_val != const_val);
switch (cast_op) {
case CastOpNoCast:
zig_unreachable();
case CastOpNoop:
*const_val = *other_val;
const_val->type = new_type;
break;
case CastOpResizeSlice:
case CastOpBytesToSlice:
// can't do it
break;
case CastOpErrToInt:
{
uint64_t value;
if (other_type->id == TypeTableEntryIdErrorUnion) {
value = other_val->data.x_err_union.err ? other_val->data.x_err_union.err->value : 0;
} else if (other_type->id == TypeTableEntryIdPureError) {
value = other_val->data.x_pure_err->value;
} else {
zig_unreachable();
}
bignum_init_unsigned(&const_val->data.x_bignum, value);
const_val->special = ConstValSpecialStatic;
break;
}
case CastOpIntToFloat:
bignum_cast_to_float(&const_val->data.x_bignum, &other_val->data.x_bignum);
const_val->special = ConstValSpecialStatic;
break;
case CastOpFloatToInt:
bignum_cast_to_int(&const_val->data.x_bignum, &other_val->data.x_bignum);
const_val->special = ConstValSpecialStatic;
break;
case CastOpBoolToInt:
bignum_init_unsigned(&const_val->data.x_bignum, other_val->data.x_bool ? 1 : 0);
const_val->special = ConstValSpecialStatic;
break;
}
}
static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
TypeTableEntry *wanted_type, CastOp cast_op, bool need_alloca)
{
if (value->value.special != ConstValSpecialRuntime) {
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
source_instr->source_node, wanted_type, false);
eval_const_expr_implicit_cast(cast_op, &value->value, value->value.type,
&result->value, wanted_type);
return result;
} else {
IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, cast_op);
result->value.type = wanted_type;
if (need_alloca) {
FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
if (fn_entry)
fn_entry->alloca_list.append(result);
}
return result;
}
}
static bool is_slice(TypeTableEntry *type) {
return type->id == TypeTableEntryIdStruct && type->data.structure.is_slice;
}
static bool is_container(TypeTableEntry *type) {
return type->id == TypeTableEntryIdStruct ||
type->id == TypeTableEntryIdEnum ||
type->id == TypeTableEntryIdUnion;
}
static bool is_u8(TypeTableEntry *type) {
return type->id == TypeTableEntryIdInt &&
!type->data.integral.is_signed && type->data.integral.bit_count == 8;
}
static IrBasicBlock *ir_get_new_bb(IrAnalyze *ira, IrBasicBlock *old_bb, IrInstruction *ref_old_instruction) {
assert(old_bb);
if (old_bb->other) {
if (ref_old_instruction == nullptr || old_bb->other->ref_instruction != ref_old_instruction)
return old_bb->other;
}
IrBasicBlock *new_bb = ir_build_bb_from(&ira->new_irb, old_bb);
new_bb->ref_instruction = ref_old_instruction;
// We are about to enqueue old_bb for analysis. Before we do so, look over old_bb's
// instructions and make sure we have enqueued first the blocks which contain
// instructions old_bb depends on.
for (size_t instr_i = 0; instr_i < old_bb->instruction_list.length; instr_i += 1) {
IrInstruction *instruction = old_bb->instruction_list.at(instr_i);
for (size_t dep_i = 0; ; dep_i += 1) {
IrInstruction *dep_instruction = ir_instruction_get_dep(instruction, dep_i);
if (dep_instruction == nullptr)
break;
if (dep_instruction->other)
continue;
if (dep_instruction->owner_bb == old_bb)
continue;
ir_get_new_bb(ira, dep_instruction->owner_bb, nullptr);
}
}
ira->old_bb_queue.append(old_bb);
return new_bb;
}
static void ir_start_bb(IrAnalyze *ira, IrBasicBlock *old_bb, IrBasicBlock *const_predecessor_bb) {
ira->instruction_index = 0;
ira->old_irb.current_basic_block = old_bb;
ira->const_predecessor_bb = const_predecessor_bb;
if (!const_predecessor_bb && old_bb->other)
ira->new_irb.exec->basic_block_list.append(old_bb->other);
}
static void ir_finish_bb(IrAnalyze *ira) {
ira->instruction_index += 1;
while (ira->instruction_index < ira->old_irb.current_basic_block->instruction_list.length) {
IrInstruction *next_instruction = ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index);
if (!next_instruction->is_gen) {
ir_add_error(ira, next_instruction, buf_sprintf("unreachable code"));
break;
}
ira->instruction_index += 1;
}
ira->block_queue_index += 1;
if (ira->block_queue_index < ira->old_bb_queue.length) {
IrBasicBlock *old_bb = ira->old_bb_queue.at(ira->block_queue_index);
assert(old_bb->other);
ira->new_irb.current_basic_block = old_bb->other;
ir_start_bb(ira, old_bb, nullptr);
}
}
static TypeTableEntry *ir_unreach_error(IrAnalyze *ira) {
ira->block_queue_index = SIZE_MAX;
ira->new_irb.exec->invalid = true;
return ira->codegen->builtin_types.entry_unreachable;
}
static bool ir_emit_backward_branch(IrAnalyze *ira, IrInstruction *source_instruction) {
size_t *bbc = ira->new_irb.exec->backward_branch_count;
size_t quota = ira->new_irb.exec->backward_branch_quota;
// If we're already over quota, we've already given an error message for this.
if (*bbc > quota)
return false;
*bbc += 1;
if (*bbc > quota) {
ir_add_error(ira, source_instruction, buf_sprintf("evaluation exceeded %zu backwards branches", quota));
return false;
}
return true;
}
static TypeTableEntry *ir_inline_bb(IrAnalyze *ira, IrInstruction *source_instruction, IrBasicBlock *old_bb) {
if (old_bb->debug_id <= ira->old_irb.current_basic_block->debug_id) {
if (!ir_emit_backward_branch(ira, source_instruction))
return ir_unreach_error(ira);
}
old_bb->other = ira->old_irb.current_basic_block->other;
ir_start_bb(ira, old_bb, ira->old_irb.current_basic_block);
return ira->codegen->builtin_types.entry_unreachable;
}
static TypeTableEntry *ir_finish_anal(IrAnalyze *ira, TypeTableEntry *result_type) {
if (result_type->id == TypeTableEntryIdUnreachable)
ir_finish_bb(ira);
return result_type;
}
static ConstExprValue *ir_build_const_from(IrAnalyze *ira, IrInstruction *old_instruction,
bool depends_on_compile_var)
{
IrInstruction *new_instruction;
if (old_instruction->id == IrInstructionIdVarPtr) {
IrInstructionVarPtr *old_var_ptr_instruction = (IrInstructionVarPtr *)old_instruction;
IrInstructionVarPtr *var_ptr_instruction = ir_create_instruction<IrInstructionVarPtr>(&ira->new_irb,
old_instruction->scope, old_instruction->source_node);
var_ptr_instruction->var = old_var_ptr_instruction->var;
new_instruction = &var_ptr_instruction->base;
} else if (old_instruction->id == IrInstructionIdFieldPtr) {
IrInstructionFieldPtr *field_ptr_instruction = ir_create_instruction<IrInstructionFieldPtr>(&ira->new_irb,
old_instruction->scope, old_instruction->source_node);
new_instruction = &field_ptr_instruction->base;
} else if (old_instruction->id == IrInstructionIdElemPtr) {
IrInstructionElemPtr *elem_ptr_instruction = ir_create_instruction<IrInstructionElemPtr>(&ira->new_irb,
old_instruction->scope, old_instruction->source_node);
new_instruction = &elem_ptr_instruction->base;
} else {
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
old_instruction->scope, old_instruction->source_node);
new_instruction = &const_instruction->base;
}
ir_link_new_instruction(new_instruction, old_instruction);
ConstExprValue *const_val = &new_instruction->value;
const_val->special = ConstValSpecialStatic;
const_val->depends_on_compile_var = depends_on_compile_var;
return const_val;
}
static TypeTableEntry *ir_analyze_void(IrAnalyze *ira, IrInstruction *instruction) {
ir_build_const_from(ira, instruction, false);
return ira->codegen->builtin_types.entry_void;
}
static TypeTableEntry *ir_analyze_const_ptr(IrAnalyze *ira, IrInstruction *instruction,
ConstExprValue *pointee, TypeTableEntry *pointee_type, bool depends_on_compile_var,
ConstPtrSpecial special, bool ptr_is_const)
{
if (pointee_type->id == TypeTableEntryIdMetaType) {
TypeTableEntry *type_entry = pointee->data.x_type;
if (type_entry->id == TypeTableEntryIdUnreachable) {
ir_add_error(ira, instruction, buf_sprintf("pointer to unreachable not allowed"));
return ira->codegen->builtin_types.entry_invalid;
}
ConstExprValue *const_val = ir_build_const_from(ira, instruction,
depends_on_compile_var || pointee->depends_on_compile_var);
type_ensure_zero_bits_known(ira->codegen, type_entry);
const_val->data.x_type = get_pointer_to_type(ira->codegen, type_entry, ptr_is_const);
return pointee_type;
} else {
TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, pointee_type, ptr_is_const);
ConstExprValue *const_val = ir_build_const_from(ira, instruction,
depends_on_compile_var || pointee->depends_on_compile_var);
const_val->data.x_ptr.base_ptr = pointee;
const_val->data.x_ptr.index = SIZE_MAX;
const_val->data.x_ptr.special = special;
return ptr_type;
}
}
static TypeTableEntry *ir_analyze_const_usize(IrAnalyze *ira, IrInstruction *instruction, uint64_t value,
bool depends_on_compile_var)
{
ConstExprValue *const_val = ir_build_const_from(ira, instruction, depends_on_compile_var);
bignum_init_unsigned(&const_val->data.x_bignum, value);
return ira->codegen->builtin_types.entry_usize;
}
enum UndefAllowed {
UndefOk,
UndefBad,
};
static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed) {
switch (value->value.special) {
case ConstValSpecialStatic:
return &value->value;
case ConstValSpecialRuntime:
ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression"));
return nullptr;
case ConstValSpecialUndef:
if (undef_allowed == UndefOk) {
return &value->value;
} else {
ir_add_error(ira, value, buf_sprintf("use of undefined value"));
return nullptr;
}
}
zig_unreachable();
}
IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
TypeTableEntry *expected_type, size_t *backward_branch_count, size_t backward_branch_quota,
FnTableEntry *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name,
IrExecutable *parent_exec)
{
IrExecutable ir_executable = {0};
ir_executable.source_node = source_node;
ir_executable.parent_exec = parent_exec;
ir_executable.name = exec_name;
ir_executable.is_inline = true;
ir_executable.fn_entry = fn_entry;
ir_executable.c_import_buf = c_import_buf;
ir_executable.begin_scope = scope;
ir_gen(codegen, node, scope, &ir_executable);
if (ir_executable.invalid)
return codegen->invalid_instruction;
if (codegen->verbose) {
fprintf(stderr, "\nSource: ");
ast_render(stderr, node, 4);
fprintf(stderr, "\n{ // (IR)\n");
ir_print(stderr, &ir_executable, 4);
fprintf(stderr, "}\n");
}
IrExecutable analyzed_executable = {0};
analyzed_executable.source_node = source_node;
analyzed_executable.parent_exec = parent_exec;
analyzed_executable.name = exec_name;
analyzed_executable.is_inline = true;
analyzed_executable.fn_entry = fn_entry;
analyzed_executable.c_import_buf = c_import_buf;
analyzed_executable.backward_branch_count = backward_branch_count;
analyzed_executable.backward_branch_quota = backward_branch_quota;
analyzed_executable.begin_scope = scope;
TypeTableEntry *result_type = ir_analyze(codegen, &ir_executable, &analyzed_executable, expected_type, node);
if (result_type->id == TypeTableEntryIdInvalid)
return codegen->invalid_instruction;
if (codegen->verbose) {
fprintf(stderr, "{ // (analyzed)\n");
ir_print(stderr, &analyzed_executable, 4);
fprintf(stderr, "}\n");
}
return ir_exec_const_result(codegen, &analyzed_executable);
}
static TypeTableEntry *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value) {
if (type_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
if (type_value->value.type->id != TypeTableEntryIdMetaType) {
ir_add_error(ira, type_value,
buf_sprintf("expected type 'type', found '%s'", buf_ptr(&type_value->value.type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
ConstExprValue *const_val = ir_resolve_const(ira, type_value, UndefBad);
if (!const_val)
return ira->codegen->builtin_types.entry_invalid;
return const_val->data.x_type;
}
static FnTableEntry *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) {
if (fn_value == ira->codegen->invalid_instruction)
return nullptr;
if (fn_value->value.type->id == TypeTableEntryIdInvalid)
return nullptr;
if (fn_value->value.type->id != TypeTableEntryIdFn) {
ir_add_error_node(ira, fn_value->source_node,
buf_sprintf("expected function type, found '%s'", buf_ptr(&fn_value->value.type->name)));
return nullptr;
}
ConstExprValue *const_val = ir_resolve_const(ira, fn_value, UndefBad);
if (!const_val)
return nullptr;
return const_val->data.x_fn;
}
static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) {
assert(wanted_type->id == TypeTableEntryIdMaybe);
if (instr_is_comptime(value)) {
TypeTableEntry *payload_type = wanted_type->data.maybe.child_type;
IrInstruction *casted_payload = ir_implicit_cast(ira, value, payload_type);
if (casted_payload->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->invalid_instruction;
ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
source_instr->scope, source_instr->source_node);
const_instruction->base.value.type = wanted_type;
const_instruction->base.value.special = ConstValSpecialStatic;
const_instruction->base.value.depends_on_compile_var = val->depends_on_compile_var;
const_instruction->base.value.data.x_maybe = val;
return &const_instruction->base;
}
IrInstruction *result = ir_build_maybe_wrap(&ira->new_irb, source_instr->scope, source_instr->source_node, value);
result->value.type = wanted_type;
result->value.data.rh_maybe = RuntimeHintMaybeNonNull;
ir_add_alloca(ira, result, wanted_type);
return result;
}
static IrInstruction *ir_analyze_pointer_reinterpret(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *ptr, TypeTableEntry *wanted_type)
{
if (ptr->value.type->id != TypeTableEntryIdPointer &&
ptr->value.type->id != TypeTableEntryIdMaybe)
{
ir_add_error(ira, ptr, buf_sprintf("expected pointer, found '%s'", buf_ptr(&ptr->value.type->name)));
return ira->codegen->invalid_instruction;
}
if (instr_is_comptime(ptr)) {
ConstExprValue *val = ir_resolve_const(ira, ptr, UndefOk);
if (!val)
return ira->codegen->invalid_instruction;
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
source_instr->scope, source_instr->source_node);
const_instruction->base.value = *val;
const_instruction->base.value.type = wanted_type;
return &const_instruction->base;
}
IrInstruction *result = ir_build_pointer_reinterpret(&ira->new_irb, source_instr->scope,
source_instr->source_node, ptr);
result->value.type = wanted_type;
return result;
}
static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *value, TypeTableEntry *wanted_type)
{
assert(wanted_type->id == TypeTableEntryIdErrorUnion);
if (instr_is_comptime(value)) {
TypeTableEntry *payload_type = wanted_type->data.error.child_type;
IrInstruction *casted_payload = ir_implicit_cast(ira, value, payload_type);
if (casted_payload->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->invalid_instruction;
ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
source_instr->scope, source_instr->source_node);
const_instruction->base.value.type = wanted_type;
const_instruction->base.value.special = ConstValSpecialStatic;
const_instruction->base.value.depends_on_compile_var = val->depends_on_compile_var;
const_instruction->base.value.data.x_err_union.err = nullptr;
const_instruction->base.value.data.x_err_union.payload = val;
return &const_instruction->base;
}
IrInstruction *result = ir_build_err_wrap_payload(&ira->new_irb, source_instr->scope, source_instr->source_node, value);
result->value.type = wanted_type;
result->value.data.rh_error_union = RuntimeHintErrorUnionNonError;
ir_add_alloca(ira, result, wanted_type);
return result;
}
static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) {
assert(wanted_type->id == TypeTableEntryIdErrorUnion);
if (instr_is_comptime(value)) {
ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
source_instr->scope, source_instr->source_node);
const_instruction->base.value.type = wanted_type;
const_instruction->base.value.special = ConstValSpecialStatic;
const_instruction->base.value.depends_on_compile_var = val->depends_on_compile_var;
const_instruction->base.value.data.x_err_union.err = val->data.x_pure_err;
const_instruction->base.value.data.x_err_union.payload = nullptr;
return &const_instruction->base;
}
IrInstruction *result = ir_build_err_wrap_code(&ira->new_irb, source_instr->scope, source_instr->source_node, value);
result->value.type = wanted_type;
result->value.data.rh_error_union = RuntimeHintErrorUnionError;
ir_add_alloca(ira, result, wanted_type);
return result;
}
static IrInstruction *ir_analyze_cast_ref(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) {
if (instr_is_comptime(value)) {
ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
source_instr->scope, source_instr->source_node);
const_instruction->base.value.type = wanted_type;
const_instruction->base.value.special = ConstValSpecialStatic;
const_instruction->base.value.depends_on_compile_var = val->depends_on_compile_var;
const_instruction->base.value.data.x_ptr.base_ptr = val;
const_instruction->base.value.data.x_ptr.index = SIZE_MAX;
return &const_instruction->base;
}
if (value->id == IrInstructionIdLoadPtr) {
IrInstructionLoadPtr *load_ptr_inst = (IrInstructionLoadPtr *)value;
return load_ptr_inst->ptr;
} else {
IrInstruction *new_instruction = ir_build_ref(&ira->new_irb, source_instr->scope,
source_instr->source_node, value, true);
new_instruction->value.type = wanted_type;
TypeTableEntry *child_type = wanted_type->data.pointer.child_type;
if (type_has_bits(child_type)) {
FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
assert(fn_entry);
fn_entry->alloca_list.append(new_instruction);
}
return new_instruction;
}
}
static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) {
assert(wanted_type->id == TypeTableEntryIdMaybe);
assert(instr_is_comptime(value));
ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
assert(val);
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb, source_instr->scope, source_instr->source_node);
const_instruction->base.value.type = wanted_type;
const_instruction->base.value.special = ConstValSpecialStatic;
const_instruction->base.value.depends_on_compile_var = val->depends_on_compile_var;
const_instruction->base.value.data.x_maybe = nullptr;
return &const_instruction->base;
}
static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *array, TypeTableEntry *wanted_type)
{
assert(is_slice(wanted_type));
TypeTableEntry *array_type = array->value.type;
assert(array_type->id == TypeTableEntryIdArray);
if (instr_is_comptime(array)) {
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
source_instr->source_node, wanted_type, false);
init_const_slice(ira->codegen, &result->value, &array->value, 0, array_type->data.array.len, true);
return result;
}
IrInstruction *start = ir_create_const(&ira->new_irb, source_instr->scope,
source_instr->source_node, ira->codegen->builtin_types.entry_usize, false);
init_const_usize(ira->codegen, &start->value, 0);
IrInstruction *end = ir_create_const(&ira->new_irb, source_instr->scope,
source_instr->source_node, ira->codegen->builtin_types.entry_usize, false);
init_const_usize(ira->codegen, &end->value, array_type->data.array.len);
bool is_const;
if (array->id == IrInstructionIdLoadPtr) {
IrInstructionLoadPtr *load_ptr_inst = (IrInstructionLoadPtr *) array;
is_const = load_ptr_inst->ptr->value.type->data.pointer.is_const;
} else {
is_const = true;
}
IrInstruction *result = ir_build_slice(&ira->new_irb, source_instr->scope,
source_instr->source_node, array, start, end, is_const, false);
TypeTableEntry *child_type = array_type->data.array.child_type;
result->value.type = get_slice_type(ira->codegen, child_type, is_const);
ir_add_alloca(ira, result, result->value.type);
return result;
}
static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *target, TypeTableEntry *wanted_type)
{
assert(wanted_type->id == TypeTableEntryIdInt);
if (instr_is_comptime(target)) {
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
source_instr->source_node, wanted_type, val->depends_on_compile_var);
init_const_unsigned_negative(&result->value, wanted_type, val->data.x_enum.tag, false);
return result;
}
IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope,
source_instr->source_node, target);
result->value.type = wanted_type;
return result;
}
static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *target, TypeTableEntry *wanted_type)
{
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
source_instr->source_node, wanted_type, target->value.depends_on_compile_var);
init_const_undefined(ira->codegen, &result->value);
return result;
}
static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *target, TypeTableEntry *wanted_type)
{
assert(wanted_type->id == TypeTableEntryIdInt || wanted_type->id == TypeTableEntryIdFloat);
if (instr_is_comptime(target)) {
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
source_instr->source_node, wanted_type, val->depends_on_compile_var);
result->value = *val;
result->value.type = wanted_type;
return result;
}
IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope,
source_instr->source_node, target);
result->value.type = wanted_type;
return result;
}
static IrInstruction *ir_analyze_ptr_to_int(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *target, TypeTableEntry *wanted_type)
{
assert(wanted_type->id == TypeTableEntryIdInt);
if (instr_is_comptime(target)) {
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
if (val->data.x_ptr.special == ConstPtrSpecialRuntime) {
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
source_instr->source_node, wanted_type, val->depends_on_compile_var);
bignum_init_unsigned(&result->value.data.x_bignum, val->data.x_ptr.index);
return result;
}
}
IrInstruction *result = ir_build_ptr_to_int(&ira->new_irb, source_instr->scope,
source_instr->source_node, target);
result->value.type = wanted_type;
return result;
}
static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *target, TypeTableEntry *wanted_type)
{
assert(wanted_type->id == TypeTableEntryIdPointer);
if (instr_is_comptime(target)) {
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
source_instr->source_node, wanted_type, val->depends_on_compile_var);
result->value.data.x_ptr.base_ptr = nullptr;
result->value.data.x_ptr.index = bignum_to_twos_complement(&val->data.x_bignum);
result->value.data.x_ptr.special = ConstPtrSpecialRuntime;
return result;
}
IrInstruction *result = ir_build_int_to_ptr(&ira->new_irb, source_instr->scope,
source_instr->source_node, target);
result->value.type = wanted_type;
return result;
}
static IrInstruction *ir_analyze_int_lit_to_ptr(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *target, TypeTableEntry *wanted_type)
{
assert(wanted_type->id == TypeTableEntryIdPointer);
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
TypeTableEntry *usize_type = ira->codegen->builtin_types.entry_usize;
if (!ir_num_lit_fits_in_other_type(ira, target, usize_type))
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
source_instr->source_node, wanted_type, val->depends_on_compile_var);
result->value.data.x_ptr.base_ptr = nullptr;
result->value.data.x_ptr.index = bignum_to_twos_complement(&val->data.x_bignum);
result->value.data.x_ptr.special = ConstPtrSpecialRuntime;
return result;
}
static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *target, TypeTableEntry *wanted_type)
{
assert(wanted_type->id == TypeTableEntryIdEnum);
if (instr_is_comptime(target)) {
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
source_instr->source_node, wanted_type, val->depends_on_compile_var);
result->value.data.x_enum.tag = val->data.x_bignum.data.x_uint;
return result;
}
IrInstruction *result = ir_build_int_to_enum(&ira->new_irb, source_instr->scope,
source_instr->source_node, target);
result->value.type = wanted_type;
return result;
}
static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
TypeTableEntry *wanted_type, IrInstruction *value)
{
TypeTableEntry *actual_type = value->value.type;
TypeTableEntry *wanted_type_canon = get_underlying_type(wanted_type);
TypeTableEntry *actual_type_canon = get_underlying_type(actual_type);
TypeTableEntry *isize_type = ira->codegen->builtin_types.entry_isize;
TypeTableEntry *usize_type = ira->codegen->builtin_types.entry_usize;
if (wanted_type_canon->id == TypeTableEntryIdInvalid ||
actual_type_canon->id == TypeTableEntryIdInvalid)
{
return ira->codegen->invalid_instruction;
}
if (wanted_type->id == TypeTableEntryIdVar)
return value;
// explicit match or non-const to const
if (types_match_const_cast_only(wanted_type, actual_type)) {
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false);
}
// explicit cast from bool to int
if (wanted_type_canon->id == TypeTableEntryIdInt &&
actual_type_canon->id == TypeTableEntryIdBool)
{
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpBoolToInt, false);
}
// explicit cast from pointer to isize or usize
if ((wanted_type_canon == isize_type || wanted_type_canon == usize_type) &&
type_is_codegen_pointer(actual_type_canon))
{
return ir_analyze_ptr_to_int(ira, source_instr, value, wanted_type);
}
// explicit cast from isize or usize to pointer
if (wanted_type_canon->id == TypeTableEntryIdPointer &&
(actual_type_canon == isize_type || actual_type_canon == usize_type))
{
return ir_analyze_int_to_ptr(ira, source_instr, value, wanted_type);
}
// explicit cast from number literal to pointer
if (wanted_type_canon->id == TypeTableEntryIdPointer &&
(actual_type_canon->id == TypeTableEntryIdNumLitInt))
{
return ir_analyze_int_lit_to_ptr(ira, source_instr, value, wanted_type);
}
// explicit widening or shortening cast
if ((wanted_type_canon->id == TypeTableEntryIdInt &&
actual_type_canon->id == TypeTableEntryIdInt) ||
(wanted_type_canon->id == TypeTableEntryIdFloat &&
actual_type_canon->id == TypeTableEntryIdFloat))
{
return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
}
// explicit cast from int to float
if (wanted_type_canon->id == TypeTableEntryIdFloat &&
actual_type_canon->id == TypeTableEntryIdInt)
{
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpIntToFloat, false);
}
// explicit cast from float to int
if (wanted_type_canon->id == TypeTableEntryIdInt &&
actual_type_canon->id == TypeTableEntryIdFloat)
{
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpFloatToInt, false);
}
// explicit cast from array to slice
if (is_slice(wanted_type) &&
actual_type->id == TypeTableEntryIdArray &&
types_match_const_cast_only(
wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type,
actual_type->data.array.child_type))
{
return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);
}
// explicit cast from []T to []u8 or []u8 to []T
if (is_slice(wanted_type) && is_slice(actual_type) &&
(is_u8(wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type) ||
is_u8(actual_type->data.structure.fields[0].type_entry->data.pointer.child_type)) &&
(wanted_type->data.structure.fields[0].type_entry->data.pointer.is_const ||
!actual_type->data.structure.fields[0].type_entry->data.pointer.is_const))
{
if (!ir_emit_global_runtime_side_effect(ira, source_instr))
return ira->codegen->invalid_instruction;
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpResizeSlice, true);
}
// explicit cast from [N]u8 to []T
if (is_slice(wanted_type) &&
actual_type->id == TypeTableEntryIdArray &&
is_u8(actual_type->data.array.child_type))
{
if (!ir_emit_global_runtime_side_effect(ira, source_instr))
return ira->codegen->invalid_instruction;
uint64_t child_type_size = type_size(ira->codegen,
wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type);
if (actual_type->data.array.len % child_type_size == 0) {
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpBytesToSlice, true);
} else {
ir_add_error_node(ira, source_instr->source_node,
buf_sprintf("unable to convert %s to %s: size mismatch",
buf_ptr(&actual_type->name), buf_ptr(&wanted_type->name)));
return ira->codegen->invalid_instruction;
}
}
// explicit cast from pointer to another pointer
if ((actual_type->id == TypeTableEntryIdPointer || actual_type->id == TypeTableEntryIdFn) &&
(wanted_type->id == TypeTableEntryIdPointer || wanted_type->id == TypeTableEntryIdFn))
{
return ir_analyze_pointer_reinterpret(ira, source_instr, value, wanted_type);
}
// explicit cast from maybe pointer to another maybe pointer
if (actual_type->id == TypeTableEntryIdMaybe &&
(actual_type->data.maybe.child_type->id == TypeTableEntryIdPointer ||
actual_type->data.maybe.child_type->id == TypeTableEntryIdFn) &&
wanted_type->id == TypeTableEntryIdMaybe &&
(wanted_type->data.maybe.child_type->id == TypeTableEntryIdPointer ||
wanted_type->data.maybe.child_type->id == TypeTableEntryIdFn))
{
return ir_analyze_pointer_reinterpret(ira, source_instr, value, wanted_type);
}
// explicit cast from child type of maybe type to maybe type
if (wanted_type->id == TypeTableEntryIdMaybe) {
if (types_match_const_cast_only(wanted_type->data.maybe.child_type, actual_type)) {
return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
} else if (actual_type->id == TypeTableEntryIdNumLitInt ||
actual_type->id == TypeTableEntryIdNumLitFloat)
{
if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.maybe.child_type)) {
return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
} else {
return ira->codegen->invalid_instruction;
}
}
}
// explicit cast from null literal to maybe type
if (wanted_type->id == TypeTableEntryIdMaybe &&
actual_type->id == TypeTableEntryIdNullLit)
{
return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type);
}
// explicit cast from child type of error type to error type
if (wanted_type->id == TypeTableEntryIdErrorUnion) {
if (types_match_const_cast_only(wanted_type->data.error.child_type, actual_type)) {
return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
} else if (actual_type->id == TypeTableEntryIdNumLitInt ||
actual_type->id == TypeTableEntryIdNumLitFloat)
{
if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error.child_type)) {
return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
} else {
return ira->codegen->invalid_instruction;
}
}
}
// explicit cast from pure error to error union type
if (wanted_type->id == TypeTableEntryIdErrorUnion &&
actual_type->id == TypeTableEntryIdPureError)
{
return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type);
}
// explicit cast from number literal to another type
if (actual_type->id == TypeTableEntryIdNumLitFloat ||
actual_type->id == TypeTableEntryIdNumLitInt)
{
if (ir_num_lit_fits_in_other_type(ira, value, wanted_type_canon)) {
CastOp op;
if ((actual_type->id == TypeTableEntryIdNumLitFloat &&
wanted_type_canon->id == TypeTableEntryIdFloat) ||
(actual_type->id == TypeTableEntryIdNumLitInt &&
wanted_type_canon->id == TypeTableEntryIdInt))
{
op = CastOpNoop;
} else if (wanted_type_canon->id == TypeTableEntryIdInt) {
op = CastOpFloatToInt;
} else if (wanted_type_canon->id == TypeTableEntryIdFloat) {
op = CastOpIntToFloat;
} else {
zig_unreachable();
}
return ir_resolve_cast(ira, source_instr, value, wanted_type, op, false);
} else {
return ira->codegen->invalid_instruction;
}
}
// explicit cast from %void to integer type which can fit it
bool actual_type_is_void_err = actual_type->id == TypeTableEntryIdErrorUnion &&
!type_has_bits(actual_type->data.error.child_type);
bool actual_type_is_pure_err = actual_type->id == TypeTableEntryIdPureError;
if ((actual_type_is_void_err || actual_type_is_pure_err) &&
wanted_type->id == TypeTableEntryIdInt)
{
BigNum bn;
bignum_init_unsigned(&bn, ira->codegen->error_decls.length);
if (bignum_fits_in_bits(&bn, wanted_type->data.integral.bit_count,
wanted_type->data.integral.is_signed))
{
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpErrToInt, false);
} else {
ir_add_error_node(ira, source_instr->source_node,
buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name)));
return ira->codegen->invalid_instruction;
}
}
// explicit cast from integer to enum type with no payload
if (actual_type->id == TypeTableEntryIdInt &&
wanted_type->id == TypeTableEntryIdEnum &&
wanted_type->data.enumeration.gen_field_count == 0)
{
return ir_analyze_int_to_enum(ira, source_instr, value, wanted_type);
}
// explicit cast from enum type with no payload to integer
if (wanted_type->id == TypeTableEntryIdInt &&
actual_type->id == TypeTableEntryIdEnum &&
actual_type->data.enumeration.gen_field_count == 0)
{
return ir_analyze_enum_to_int(ira, source_instr, value, wanted_type);
}
// explicit cast from undefined to anything
if (actual_type->id == TypeTableEntryIdUndefLit) {
return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type);
}
// explicit cast from something to const pointer of it
if (!type_requires_comptime(actual_type)) {
TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true);
if (types_match_const_cast_only(wanted_type, const_ptr_actual)) {
return ir_analyze_cast_ref(ira, source_instr, value, wanted_type);
}
}
ir_add_error_node(ira, source_instr->source_node,
buf_sprintf("invalid cast from type '%s' to '%s'",
buf_ptr(&actual_type->name),
buf_ptr(&wanted_type->name)));
return ira->codegen->invalid_instruction;
}
static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, TypeTableEntry *expected_type) {
assert(value);
assert(value != ira->codegen->invalid_instruction);
assert(!expected_type || expected_type->id != TypeTableEntryIdInvalid);
assert(value->value.type);
assert(value->value.type->id != TypeTableEntryIdInvalid);
if (expected_type == nullptr)
return value; // anything will do
if (expected_type == value->value.type)
return value; // match
if (value->value.type->id == TypeTableEntryIdUnreachable)
return value;
ImplicitCastMatchResult result = ir_types_match_with_implicit_cast(ira, expected_type, value->value.type, value);
switch (result) {
case ImplicitCastMatchResultNo:
ir_add_error(ira, value,
buf_sprintf("expected type '%s', found '%s'",
buf_ptr(&expected_type->name),
buf_ptr(&value->value.type->name)));
return ira->codegen->invalid_instruction;
case ImplicitCastMatchResultYes:
return ir_analyze_cast(ira, value, expected_type, value);
case ImplicitCastMatchResultReportedError:
return ira->codegen->invalid_instruction;
}
zig_unreachable();
}
static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr) {
TypeTableEntry *type_entry = ptr->value.type;
if (type_entry->id == TypeTableEntryIdInvalid) {
return ira->codegen->invalid_instruction;
} else if (type_entry->id == TypeTableEntryIdPointer) {
TypeTableEntry *child_type = type_entry->data.pointer.child_type;
if (ptr->value.special != ConstValSpecialRuntime) {
ConstExprValue *pointee = const_ptr_pointee(&ptr->value);
if (pointee->special != ConstValSpecialRuntime) {
IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope,
source_instruction->source_node, child_type, false);
result->value = *pointee;
result->value.depends_on_compile_var = pointee->depends_on_compile_var ||
ptr->value.depends_on_compile_var;
return result;
}
}
// TODO if the instruction is a get pointer instruction we can skip it
IrInstruction *load_ptr_instruction = ir_build_load_ptr(&ira->new_irb, source_instruction->scope, source_instruction->source_node, ptr);
load_ptr_instruction->value.type = child_type;
return load_ptr_instruction;
} else if (type_entry->id == TypeTableEntryIdMetaType) {
ConstExprValue *ptr_val = ir_resolve_const(ira, ptr, UndefBad);
if (!ptr_val)
return ira->codegen->invalid_instruction;
TypeTableEntry *ptr_type = ptr_val->data.x_type;
if (ptr_type->id == TypeTableEntryIdPointer) {
TypeTableEntry *child_type = ptr_type->data.pointer.child_type;
return ir_create_const_type(&ira->new_irb, source_instruction->scope,
source_instruction->source_node, child_type, ptr_val->depends_on_compile_var);
} else {
ir_add_error(ira, source_instruction,
buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&ptr_type->name)));
return ira->codegen->invalid_instruction;
}
} else {
ir_add_error_node(ira, source_instruction->source_node,
buf_sprintf("attempt to dereference non pointer type '%s'",
buf_ptr(&type_entry->name)));
return ira->codegen->invalid_instruction;
}
}
static TypeTableEntry *ir_analyze_ref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *value,
bool is_const)
{
if (value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
if (instr_is_comptime(value)) {
ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
if (!val)
return ira->codegen->builtin_types.entry_invalid;
return ir_analyze_const_ptr(ira, source_instruction, val, value->value.type,
value->value.depends_on_compile_var, ConstPtrSpecialNone, is_const);
}
TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, value->value.type, true);
FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
assert(fn_entry);
IrInstruction *new_instruction = ir_build_ref_from(&ira->new_irb, source_instruction, value, is_const);
fn_entry->alloca_list.append(new_instruction);
return ptr_type;
}
static bool ir_resolve_usize(IrAnalyze *ira, IrInstruction *value, uint64_t *out) {
if (value->value.type->id == TypeTableEntryIdInvalid)
return false;
IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_usize);
if (casted_value->value.type->id == TypeTableEntryIdInvalid)
return false;
ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
if (!const_val)
return false;
*out = const_val->data.x_bignum.data.x_uint;
return true;
}
static bool ir_resolve_bool(IrAnalyze *ira, IrInstruction *value, bool *out) {
if (value->value.type->id == TypeTableEntryIdInvalid)
return false;
IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_bool);
if (casted_value->value.type->id == TypeTableEntryIdInvalid)
return false;
ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
if (!const_val)
return false;
*out = const_val->data.x_bool;
return true;
}
static bool ir_resolve_comptime(IrAnalyze *ira, IrInstruction *value, bool *out) {
if (!value) {
*out = false;
return true;
}
return ir_resolve_bool(ira, value, out);
}
static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, AtomicOrder *out) {
if (value->value.type->id == TypeTableEntryIdInvalid)
return false;
IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_atomic_order_enum);
if (casted_value->value.type->id == TypeTableEntryIdInvalid)
return false;
ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
if (!const_val)
return false;
*out = (AtomicOrder)const_val->data.x_enum.tag;
return true;
}
static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) {
if (value->value.type->id == TypeTableEntryIdInvalid)
return nullptr;
TypeTableEntry *str_type = get_slice_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true);
IrInstruction *casted_value = ir_implicit_cast(ira, value, str_type);
if (casted_value->value.type->id == TypeTableEntryIdInvalid)
return nullptr;
ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
if (!const_val)
return nullptr;
ConstExprValue *ptr_field = &const_val->data.x_struct.fields[slice_ptr_index];
ConstExprValue *len_field = &const_val->data.x_struct.fields[slice_len_index];
ConstExprValue *array_val = ptr_field->data.x_ptr.base_ptr;
assert(ptr_field->data.x_ptr.index != SIZE_MAX);
size_t len = len_field->data.x_bignum.data.x_uint;
Buf *result = buf_alloc();
buf_resize(result, len);
for (size_t i = 0; i < len; i += 1) {
size_t new_index = ptr_field->data.x_ptr.index + i;
ConstExprValue *char_val = &array_val->data.x_array.elements[new_index];
uint64_t big_c = char_val->data.x_bignum.data.x_uint;
assert(big_c <= UINT8_MAX);
uint8_t c = big_c;
buf_ptr(result)[i] = c;
}
return result;
}
static TypeTableEntry *ir_analyze_instruction_return(IrAnalyze *ira,
IrInstructionReturn *return_instruction)
{
IrInstruction *value = return_instruction->value->other;
if (value->value.type->id == TypeTableEntryIdInvalid)
return ir_unreach_error(ira);
ira->implicit_return_type_list.append(value);
IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->explicit_return_type);
if (casted_value == ira->codegen->invalid_instruction)
return ir_unreach_error(ira);
ir_build_return_from(&ira->new_irb, &return_instruction->base, casted_value);
return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
}
static TypeTableEntry *ir_analyze_instruction_const(IrAnalyze *ira, IrInstructionConst *const_instruction) {
bool depends_on_compile_var = const_instruction->base.value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &const_instruction->base, depends_on_compile_var);
*out_val = const_instruction->base.value;
return const_instruction->base.value.type;
}
static TypeTableEntry *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
IrInstruction *op1 = bin_op_instruction->op1->other;
if (op1->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *op2 = bin_op_instruction->op2->other;
if (op2->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *bool_type = ira->codegen->builtin_types.entry_bool;
IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, bool_type);
if (casted_op1 == ira->codegen->invalid_instruction)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, bool_type);
if (casted_op2 == ira->codegen->invalid_instruction)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *op1_val = &casted_op1->value;
ConstExprValue *op2_val = &casted_op2->value;
if (op1_val->special != ConstValSpecialRuntime && op2_val->special != ConstValSpecialRuntime) {
bool depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base, depends_on_compile_var);
assert(casted_op1->value.type->id == TypeTableEntryIdBool);
assert(casted_op2->value.type->id == TypeTableEntryIdBool);
if (bin_op_instruction->op_id == IrBinOpBoolOr) {
out_val->data.x_bool = op1_val->data.x_bool || op2_val->data.x_bool;
} else if (bin_op_instruction->op_id == IrBinOpBoolAnd) {
out_val->data.x_bool = op1_val->data.x_bool && op2_val->data.x_bool;
} else {
zig_unreachable();
}
return bool_type;
}
ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, bin_op_instruction->op_id,
casted_op1, casted_op2, bin_op_instruction->safety_check_on);
return bool_type;
}
static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
IrInstruction *op1 = bin_op_instruction->op1->other;
IrInstruction *op2 = bin_op_instruction->op2->other;
IrBinOp op_id = bin_op_instruction->op_id;
bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq);
if (is_equality_cmp &&
((op1->value.type->id == TypeTableEntryIdNullLit && op2->value.type->id == TypeTableEntryIdMaybe) ||
(op2->value.type->id == TypeTableEntryIdNullLit && op1->value.type->id == TypeTableEntryIdMaybe) ||
(op1->value.type->id == TypeTableEntryIdNullLit && op2->value.type->id == TypeTableEntryIdNullLit)))
{
bool depends_on_compile_var = op1->value.depends_on_compile_var || op2->value.depends_on_compile_var;
if (op1->value.type->id == TypeTableEntryIdNullLit && op2->value.type->id == TypeTableEntryIdNullLit) {
ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base, depends_on_compile_var);
out_val->data.x_bool = (op_id == IrBinOpCmpEq);
return ira->codegen->builtin_types.entry_bool;
}
IrInstruction *maybe_op;
if (op1->value.type->id == TypeTableEntryIdNullLit) {
maybe_op = op2;
} else if (op2->value.type->id == TypeTableEntryIdNullLit) {
maybe_op = op1;
} else {
zig_unreachable();
}
if (instr_is_comptime(maybe_op)) {
ConstExprValue *maybe_val = ir_resolve_const(ira, maybe_op, UndefBad);
if (!maybe_val)
return ira->codegen->builtin_types.entry_invalid;
bool is_null = (maybe_val->data.x_maybe == nullptr);
ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base, depends_on_compile_var);
out_val->data.x_bool = (op_id == IrBinOpCmpEq) ? is_null : !is_null;
return ira->codegen->builtin_types.entry_bool;
}
IrInstruction *is_non_null = ir_build_test_nonnull(&ira->new_irb, bin_op_instruction->base.scope,
bin_op_instruction->base.source_node, maybe_op);
is_non_null->value.type = ira->codegen->builtin_types.entry_bool;
if (op_id == IrBinOpCmpEq) {
ir_build_bool_not_from(&ira->new_irb, &bin_op_instruction->base, is_non_null);
} else {
ir_link_new_instruction(is_non_null, &bin_op_instruction->base);
}
return ira->codegen->builtin_types.entry_bool;
}
IrInstruction *instructions[] = {op1, op2};
TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, instructions, 2);
if (resolved_type->id == TypeTableEntryIdInvalid)
return resolved_type;
AstNode *source_node = bin_op_instruction->base.source_node;
switch (resolved_type->id) {
case TypeTableEntryIdInvalid:
return ira->codegen->builtin_types.entry_invalid;
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
break;
case TypeTableEntryIdBool:
case TypeTableEntryIdMetaType:
case TypeTableEntryIdVoid:
case TypeTableEntryIdPointer:
case TypeTableEntryIdPureError:
case TypeTableEntryIdFn:
case TypeTableEntryIdTypeDecl:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdArgTuple:
if (!is_equality_cmp) {
ir_add_error_node(ira, source_node,
buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
break;
case TypeTableEntryIdEnum:
if (!is_equality_cmp || resolved_type->data.enumeration.gen_field_count != 0) {
ir_add_error_node(ira, source_node,
buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
break;
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdUnion:
ir_add_error_node(ira, source_node,
buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
return ira->codegen->builtin_types.entry_invalid;
case TypeTableEntryIdEnumTag:
zig_panic("TODO implement comparison for enum tag type");
case TypeTableEntryIdVar:
zig_unreachable();
}
IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, resolved_type);
if (casted_op1 == ira->codegen->invalid_instruction)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, resolved_type);
if (casted_op2 == ira->codegen->invalid_instruction)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *op1_val = &casted_op1->value;
ConstExprValue *op2_val = &casted_op2->value;
if ((value_is_comptime(op1_val) && value_is_comptime(op2_val)) || resolved_type->id == TypeTableEntryIdVoid) {
bool type_can_gt_lt_cmp = (resolved_type->id == TypeTableEntryIdNumLitFloat ||
resolved_type->id == TypeTableEntryIdNumLitInt ||
resolved_type->id == TypeTableEntryIdFloat ||
resolved_type->id == TypeTableEntryIdInt);
bool answer;
if (type_can_gt_lt_cmp) {
bool (*bignum_cmp)(BigNum *, BigNum *);
if (op_id == IrBinOpCmpEq) {
bignum_cmp = bignum_cmp_eq;
} else if (op_id == IrBinOpCmpNotEq) {
bignum_cmp = bignum_cmp_neq;
} else if (op_id == IrBinOpCmpLessThan) {
bignum_cmp = bignum_cmp_lt;
} else if (op_id == IrBinOpCmpGreaterThan) {
bignum_cmp = bignum_cmp_gt;
} else if (op_id == IrBinOpCmpLessOrEq) {
bignum_cmp = bignum_cmp_lte;
} else if (op_id == IrBinOpCmpGreaterOrEq) {
bignum_cmp = bignum_cmp_gte;
} else {
zig_unreachable();
}
answer = bignum_cmp(&op1_val->data.x_bignum, &op2_val->data.x_bignum);
} else {
bool are_equal = resolved_type->id == TypeTableEntryIdVoid || const_values_equal(op1_val, op2_val);
if (op_id == IrBinOpCmpEq) {
answer = are_equal;
} else if (op_id == IrBinOpCmpNotEq) {
answer = !are_equal;
} else {
zig_unreachable();
}
}
bool depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base, depends_on_compile_var);
out_val->data.x_bool = answer;
return ira->codegen->builtin_types.entry_bool;
}
ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id,
casted_op1, casted_op2, bin_op_instruction->safety_check_on);
return ira->codegen->builtin_types.entry_bool;
}
static uint64_t max_unsigned_val(TypeTableEntry *type_entry) {
assert(type_entry->id == TypeTableEntryIdInt);
if (type_entry->data.integral.bit_count == 64) {
return UINT64_MAX;
} else if (type_entry->data.integral.bit_count == 32) {
return UINT32_MAX;
} else if (type_entry->data.integral.bit_count == 16) {
return UINT16_MAX;
} else if (type_entry->data.integral.bit_count == 8) {
return UINT8_MAX;
} else {
zig_unreachable();
}
}
static int ir_eval_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val,
ConstExprValue *out_val, bool (*bignum_fn)(BigNum *, BigNum *, BigNum *),
TypeTableEntry *type, bool wrapping_op)
{
bool is_int = false;
bool is_float = false;
if (bignum_fn == bignum_div || bignum_fn == bignum_mod) {
if (type->id == TypeTableEntryIdInt ||
type->id == TypeTableEntryIdNumLitInt)
{
is_int = true;
} else if (type->id == TypeTableEntryIdFloat ||
type->id == TypeTableEntryIdNumLitFloat)
{
is_float = true;
}
if ((is_int && op2_val->data.x_bignum.data.x_uint == 0) ||
(is_float && op2_val->data.x_bignum.data.x_float == 0.0))
{
return ErrorDivByZero;
}
}
bool overflow = bignum_fn(&out_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum);
if (overflow) {
return ErrorOverflow;
}
if (type->id == TypeTableEntryIdInt && !bignum_fits_in_bits(&out_val->data.x_bignum,
type->data.integral.bit_count, type->data.integral.is_signed))
{
if (wrapping_op) {
if (type->data.integral.is_signed) {
out_val->data.x_bignum.data.x_uint = max_unsigned_val(type) - out_val->data.x_bignum.data.x_uint + 1;
out_val->data.x_bignum.is_negative = !out_val->data.x_bignum.is_negative;
} else if (out_val->data.x_bignum.is_negative) {
out_val->data.x_bignum.data.x_uint = max_unsigned_val(type) - out_val->data.x_bignum.data.x_uint + 1;
out_val->data.x_bignum.is_negative = false;
} else {
bignum_truncate(&out_val->data.x_bignum, type->data.integral.bit_count);
}
} else {
return ErrorOverflow;
}
}
out_val->special = ConstValSpecialStatic;
out_val->depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var;
return 0;
}
static int ir_eval_math_op(ConstExprValue *op1_val, TypeTableEntry *op1_type,
IrBinOp op_id, ConstExprValue *op2_val, TypeTableEntry *op2_type, ConstExprValue *out_val)
{
switch (op_id) {
case IrBinOpInvalid:
case IrBinOpBoolOr:
case IrBinOpBoolAnd:
case IrBinOpCmpEq:
case IrBinOpCmpNotEq:
case IrBinOpCmpLessThan:
case IrBinOpCmpGreaterThan:
case IrBinOpCmpLessOrEq:
case IrBinOpCmpGreaterOrEq:
case IrBinOpArrayCat:
case IrBinOpArrayMult:
zig_unreachable();
case IrBinOpBinOr:
return ir_eval_bignum(op1_val, op2_val, out_val, bignum_or, op1_type, false);
case IrBinOpBinXor:
return ir_eval_bignum(op1_val, op2_val, out_val, bignum_xor, op1_type, false);
case IrBinOpBinAnd:
return ir_eval_bignum(op1_val, op2_val, out_val, bignum_and, op1_type, false);
case IrBinOpBitShiftLeft:
return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shl, op1_type, false);
case IrBinOpBitShiftLeftWrap:
return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shl, op1_type, true);
case IrBinOpBitShiftRight:
return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shr, op1_type, false);
case IrBinOpAdd:
return ir_eval_bignum(op1_val, op2_val, out_val, bignum_add, op1_type, false);
case IrBinOpAddWrap:
return ir_eval_bignum(op1_val, op2_val, out_val, bignum_add, op1_type, true);
case IrBinOpSub:
return ir_eval_bignum(op1_val, op2_val, out_val, bignum_sub, op1_type, false);
case IrBinOpSubWrap:
return ir_eval_bignum(op1_val, op2_val, out_val, bignum_sub, op1_type, true);
case IrBinOpMult:
return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, op1_type, false);
case IrBinOpMultWrap:
return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, op1_type, true);
case IrBinOpDiv:
return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div, op1_type, false);
case IrBinOpMod:
return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mod, op1_type, false);
}
zig_unreachable();
}
static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
IrInstruction *op1 = bin_op_instruction->op1->other;
IrInstruction *op2 = bin_op_instruction->op2->other;
IrInstruction *instructions[] = {op1, op2};
TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, instructions, 2);
if (resolved_type->id == TypeTableEntryIdInvalid)
return resolved_type;
IrBinOp op_id = bin_op_instruction->op_id;
if (resolved_type->id == TypeTableEntryIdInt ||
resolved_type->id == TypeTableEntryIdNumLitInt)
{
// int
} else if ((resolved_type->id == TypeTableEntryIdFloat ||
resolved_type->id == TypeTableEntryIdNumLitFloat) &&
(op_id == IrBinOpAdd ||
op_id == IrBinOpSub ||
op_id == IrBinOpMult ||
op_id == IrBinOpDiv ||
op_id == IrBinOpMod))
{
// float
} else {
AstNode *source_node = bin_op_instruction->base.source_node;
ir_add_error_node(ira, source_node,
buf_sprintf("invalid operands to binary expression: '%s' and '%s'",
buf_ptr(&op1->value.type->name),
buf_ptr(&op2->value.type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, resolved_type);
if (casted_op1 == ira->codegen->invalid_instruction)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, resolved_type);
if (casted_op2 == ira->codegen->invalid_instruction)
return ira->codegen->builtin_types.entry_invalid;
if (casted_op1->value.special != ConstValSpecialRuntime && casted_op2->value.special != ConstValSpecialRuntime) {
ConstExprValue *op1_val = &casted_op1->value;
ConstExprValue *op2_val = &casted_op2->value;
ConstExprValue *out_val = &bin_op_instruction->base.value;
bin_op_instruction->base.other = &bin_op_instruction->base;
int err;
if ((err = ir_eval_math_op(op1_val, resolved_type, op_id, op2_val, resolved_type, out_val))) {
if (err == ErrorDivByZero) {
ir_add_error_node(ira, bin_op_instruction->base.source_node,
buf_sprintf("division by zero is undefined"));
return ira->codegen->builtin_types.entry_invalid;
} else if (err == ErrorOverflow) {
ir_add_error_node(ira, bin_op_instruction->base.source_node,
buf_sprintf("operation caused overflow"));
return ira->codegen->builtin_types.entry_invalid;
}
return ira->codegen->builtin_types.entry_invalid;
}
ir_num_lit_fits_in_other_type(ira, &bin_op_instruction->base, resolved_type);
return resolved_type;
}
ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id,
casted_op1, casted_op2, bin_op_instruction->safety_check_on);
return resolved_type;
}
static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *instruction) {
IrInstruction *op1 = instruction->op1->other;
TypeTableEntry *op1_canon_type = get_underlying_type(op1->value.type);
if (op1_canon_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *op2 = instruction->op2->other;
TypeTableEntry *op2_canon_type = get_underlying_type(op2->value.type);
if (op2_canon_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
if (!op1_val)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad);
if (!op2_val)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *op1_array_val;
size_t op1_array_index;
size_t op1_array_end;
TypeTableEntry *child_type;
if (op1_canon_type->id == TypeTableEntryIdArray) {
child_type = op1_canon_type->data.array.child_type;
op1_array_val = op1_val;
op1_array_index = 0;
op1_array_end = op1_val->data.x_array.size;
} else if (op1_canon_type->id == TypeTableEntryIdPointer &&
op1_canon_type->data.pointer.child_type == ira->codegen->builtin_types.entry_u8 &&
op1_val->data.x_ptr.special == ConstPtrSpecialCStr)
{
child_type = op1_canon_type->data.pointer.child_type;
op1_array_val = op1_val->data.x_ptr.base_ptr;
op1_array_index = op1_val->data.x_ptr.index;
op1_array_end = op1_array_val->data.x_array.size - 1;
} else {
ir_add_error(ira, op1,
buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op1->value.type->name)));
// TODO if meta_type is type decl, add note pointing to type decl declaration
return ira->codegen->builtin_types.entry_invalid;
}
ConstExprValue *op2_array_val;
size_t op2_array_index;
size_t op2_array_end;
if (op2_canon_type->id == TypeTableEntryIdArray) {
if (op2_canon_type->data.array.child_type != child_type) {
ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'",
buf_ptr(&child_type->name),
buf_ptr(&op2->value.type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
op2_array_val = op2_val;
op2_array_index = 0;
op2_array_end = op2_array_val->data.x_array.size;
} else if (op2_canon_type->id == TypeTableEntryIdPointer &&
op2_canon_type->data.pointer.child_type == ira->codegen->builtin_types.entry_u8 &&
op2_val->data.x_ptr.special == ConstPtrSpecialCStr)
{
if (child_type != ira->codegen->builtin_types.entry_u8) {
ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'",
buf_ptr(&child_type->name),
buf_ptr(&op2->value.type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
op2_array_val = op2_val->data.x_ptr.base_ptr;
op2_array_index = op2_val->data.x_ptr.index;
op2_array_end = op2_array_val->data.x_array.size - 1;
} else {
ir_add_error(ira, op2,
buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op2->value.type->name)));
// TODO if meta_type is type decl, add note pointing to type decl declaration
return ira->codegen->builtin_types.entry_invalid;
}
bool depends_on_compile_var = op1->value.depends_on_compile_var || op2->value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
TypeTableEntry *result_type;
ConstExprValue *out_array_val;
size_t new_len = (op1_array_end - op1_array_index) + (op2_array_end - op2_array_index);
if (op1_canon_type->id == TypeTableEntryIdArray || op2_canon_type->id == TypeTableEntryIdArray) {
result_type = get_array_type(ira->codegen, child_type, new_len);
out_array_val = out_val;
} else {
new_len += 1; // null byte
result_type = get_pointer_to_type(ira->codegen, child_type, true);
out_array_val = allocate<ConstExprValue>(1);
out_array_val->special = ConstValSpecialStatic;
out_array_val->type = get_array_type(ira->codegen, child_type, new_len);
out_val->data.x_ptr.base_ptr = out_array_val;
out_val->data.x_ptr.index = 0;
out_val->data.x_ptr.special = ConstPtrSpecialCStr;
}
out_array_val->data.x_array.elements = allocate<ConstExprValue>(new_len);
out_array_val->data.x_array.size = new_len;
size_t next_index = 0;
for (size_t i = op1_array_index; i < op1_array_end; i += 1, next_index += 1) {
out_array_val->data.x_array.elements[next_index] = op1_array_val->data.x_array.elements[i];
}
for (size_t i = op2_array_index; i < op2_array_end; i += 1, next_index += 1) {
out_array_val->data.x_array.elements[next_index] = op2_array_val->data.x_array.elements[i];
}
if (next_index < new_len) {
ConstExprValue *null_byte = &out_array_val->data.x_array.elements[next_index];
init_const_unsigned_negative(null_byte, child_type, 0, false);
next_index += 1;
}
assert(next_index == new_len);
return result_type;
}
static TypeTableEntry *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp *instruction) {
IrInstruction *op1 = instruction->op1->other;
if (op1->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *op2 = instruction->op2->other;
if (op2->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *array_val = ir_resolve_const(ira, op1, UndefBad);
if (!array_val)
return ira->codegen->builtin_types.entry_invalid;
uint64_t mult_amt;
if (!ir_resolve_usize(ira, op2, &mult_amt))
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *array_canon_type = get_underlying_type(op1->value.type);
if (array_canon_type->id != TypeTableEntryIdArray) {
ir_add_error(ira, op1, buf_sprintf("expected array type, found '%s'", buf_ptr(&op1->value.type->name)));
// TODO if meta_type is type decl, add note pointing to type decl declaration
return ira->codegen->builtin_types.entry_invalid;
}
uint64_t old_array_len = array_canon_type->data.array.len;
BigNum array_len;
bignum_init_unsigned(&array_len, old_array_len);
if (bignum_multiply_by_scalar(&array_len, mult_amt)) {
ir_add_error(ira, &instruction->base, buf_sprintf("operation results in overflow"));
return ira->codegen->builtin_types.entry_invalid;
}
bool depends_on_compile_var = op1->value.depends_on_compile_var || op2->value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
uint64_t new_array_len = array_len.data.x_uint;
out_val->data.x_array.size = new_array_len;
out_val->data.x_array.elements = allocate<ConstExprValue>(new_array_len);
uint64_t i = 0;
for (uint64_t x = 0; x < mult_amt; x += 1) {
for (uint64_t y = 0; y < old_array_len; y += 1) {
out_val->data.x_array.elements[i] = array_val->data.x_array.elements[y];
i += 1;
}
}
assert(i == new_array_len);
TypeTableEntry *child_type = array_canon_type->data.array.child_type;
return get_array_type(ira->codegen, child_type, new_array_len);
}
static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
IrBinOp op_id = bin_op_instruction->op_id;
switch (op_id) {
case IrBinOpInvalid:
zig_unreachable();
case IrBinOpBoolOr:
case IrBinOpBoolAnd:
return ir_analyze_bin_op_bool(ira, bin_op_instruction);
case IrBinOpCmpEq:
case IrBinOpCmpNotEq:
case IrBinOpCmpLessThan:
case IrBinOpCmpGreaterThan:
case IrBinOpCmpLessOrEq:
case IrBinOpCmpGreaterOrEq:
return ir_analyze_bin_op_cmp(ira, bin_op_instruction);
case IrBinOpBinOr:
case IrBinOpBinXor:
case IrBinOpBinAnd:
case IrBinOpBitShiftLeft:
case IrBinOpBitShiftLeftWrap:
case IrBinOpBitShiftRight:
case IrBinOpAdd:
case IrBinOpAddWrap:
case IrBinOpSub:
case IrBinOpSubWrap:
case IrBinOpMult:
case IrBinOpMultWrap:
case IrBinOpDiv:
case IrBinOpMod:
return ir_analyze_bin_op_math(ira, bin_op_instruction);
case IrBinOpArrayCat:
return ir_analyze_array_cat(ira, bin_op_instruction);
case IrBinOpArrayMult:
return ir_analyze_array_mult(ira, bin_op_instruction);
}
zig_unreachable();
}
static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDeclVar *decl_var_instruction) {
VariableTableEntry *var = decl_var_instruction->var;
IrInstruction *init_value = decl_var_instruction->init_value->other;
if (init_value->value.type->id == TypeTableEntryIdInvalid) {
var->value.type = ira->codegen->builtin_types.entry_invalid;
return var->value.type;
}
AstNodeVariableDeclaration *variable_declaration = &var->decl_node->data.variable_declaration;
bool is_export = (variable_declaration->visib_mod == VisibModExport);
bool is_extern = variable_declaration->is_extern;
var->ref_count = 0;
TypeTableEntry *explicit_type = nullptr;
IrInstruction *var_type = nullptr;
if (decl_var_instruction->var_type != nullptr) {
var_type = decl_var_instruction->var_type->other;
TypeTableEntry *proposed_type = ir_resolve_type(ira, var_type);
explicit_type = validate_var_type(ira->codegen, var_type->source_node, proposed_type);
if (explicit_type->id == TypeTableEntryIdInvalid) {
var->value.type = ira->codegen->builtin_types.entry_invalid;
return var->value.type;
}
}
AstNode *source_node = decl_var_instruction->base.source_node;
IrInstruction *casted_init_value = ir_implicit_cast(ira, init_value, explicit_type);
TypeTableEntry *result_type = get_underlying_type(casted_init_value->value.type);
switch (result_type->id) {
case TypeTableEntryIdTypeDecl:
zig_unreachable();
case TypeTableEntryIdInvalid:
result_type = ira->codegen->builtin_types.entry_invalid;
break;
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
if (is_export || is_extern || casted_init_value->value.special == ConstValSpecialRuntime) {
ir_add_error_node(ira, source_node, buf_sprintf("unable to infer variable type"));
result_type = ira->codegen->builtin_types.entry_invalid;
}
break;
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdVar:
case TypeTableEntryIdBlock:
case TypeTableEntryIdNullLit:
ir_add_error_node(ira, source_node,
buf_sprintf("variable of type '%s' not allowed", buf_ptr(&result_type->name)));
result_type = ira->codegen->builtin_types.entry_invalid;
break;
case TypeTableEntryIdMetaType:
case TypeTableEntryIdNamespace:
if (casted_init_value->value.special == ConstValSpecialRuntime) {
ir_add_error_node(ira, source_node,
buf_sprintf("variable of type '%s' must be constant", buf_ptr(&result_type->name)));
result_type = ira->codegen->builtin_types.entry_invalid;
}
break;
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdPointer:
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdPureError:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdEnumTag:
case TypeTableEntryIdArgTuple:
// OK
break;
}
var->value.type = result_type;
assert(var->value.type);
if (result_type->id == TypeTableEntryIdInvalid) {
decl_var_instruction->base.other = &decl_var_instruction->base;
return ira->codegen->builtin_types.entry_void;
}
bool is_comptime = ir_get_var_is_comptime(var);
if (casted_init_value->value.special != ConstValSpecialRuntime) {
if (var->mem_slot_index != SIZE_MAX) {
assert(var->mem_slot_index < ira->exec_context.mem_slot_count);
ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index];
*mem_slot = casted_init_value->value;
if (is_comptime) {
ir_build_const_from(ira, &decl_var_instruction->base, false);
return ira->codegen->builtin_types.entry_void;
}
}
} else if (is_comptime) {
ir_add_error(ira, &decl_var_instruction->base,
buf_sprintf("cannot store runtime value in compile time variable"));
var->value.type = ira->codegen->builtin_types.entry_invalid;
return ira->codegen->builtin_types.entry_invalid;
}
ir_build_var_decl_from(&ira->new_irb, &decl_var_instruction->base, var, var_type, casted_init_value);
FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
if (fn_entry)
fn_entry->variable_list.append(var);
return ira->codegen->builtin_types.entry_void;
}
static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node,
IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i)
{
AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(*next_proto_i);
assert(param_decl_node->type == NodeTypeParamDecl);
AstNode *param_type_node = param_decl_node->data.param_decl.type;
TypeTableEntry *param_type = analyze_type_expr(ira->codegen, *exec_scope, param_type_node);
if (param_type->id == TypeTableEntryIdInvalid)
return false;
IrInstruction *casted_arg = ir_implicit_cast(ira, arg, param_type);
if (casted_arg->value.type->id == TypeTableEntryIdInvalid)
return false;
ConstExprValue *arg_val = ir_resolve_const(ira, casted_arg, UndefBad);
if (!arg_val)
return false;
Buf *param_name = param_decl_node->data.param_decl.name;
VariableTableEntry *var = add_variable(ira->codegen, param_decl_node,
*exec_scope, param_name, true, arg_val);
var->value.depends_on_compile_var = true;
*exec_scope = var->child_scope;
*next_proto_i += 1;
return true;
}
static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_node,
IrInstruction *arg, Scope **child_scope, size_t *next_proto_i,
GenericFnTypeId *generic_id, FnTypeId *fn_type_id, IrInstruction **casted_args,
FnTableEntry *impl_fn)
{
AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(*next_proto_i);
assert(param_decl_node->type == NodeTypeParamDecl);
bool is_var_args = param_decl_node->data.param_decl.is_var_args;
bool arg_part_of_generic_id = false;
IrInstruction *casted_arg;
if (is_var_args) {
arg_part_of_generic_id = true;
casted_arg = arg;
} else {
AstNode *param_type_node = param_decl_node->data.param_decl.type;
TypeTableEntry *param_type = analyze_type_expr(ira->codegen, *child_scope, param_type_node);
if (param_type->id == TypeTableEntryIdInvalid)
return false;
bool is_var_type = (param_type->id == TypeTableEntryIdVar);
if (is_var_type) {
arg_part_of_generic_id = true;
casted_arg = arg;
} else {
casted_arg = ir_implicit_cast(ira, arg, param_type);
if (casted_arg->value.type->id == TypeTableEntryIdInvalid)
return false;
}
}
bool comptime_arg = param_decl_node->data.param_decl.is_inline;
ConstExprValue *arg_val;
if (comptime_arg) {
arg_part_of_generic_id = true;
arg_val = ir_resolve_const(ira, casted_arg, UndefBad);
if (!arg_val)
return false;
// This generic function instance could be called with anything, so when this variable is read it
// needs to know that it depends on compile time variable data.
arg_val->depends_on_compile_var = true;
} else {
arg_val = create_const_runtime(casted_arg->value.type);
}
if (arg_part_of_generic_id) {
generic_id->params[generic_id->param_count] = *arg_val;
generic_id->param_count += 1;
}
Buf *param_name = param_decl_node->data.param_decl.name;
if (!is_var_args) {
VariableTableEntry *var = add_variable(ira->codegen, param_decl_node,
*child_scope, param_name, true, arg_val);
var->value.depends_on_compile_var = true;
*child_scope = var->child_scope;
var->shadowable = !comptime_arg;
*next_proto_i += 1;
}
if (!comptime_arg) {
if (type_requires_comptime(casted_arg->value.type)) {
ir_add_error(ira, casted_arg,
buf_sprintf("parameter of type '%s' requires comptime", buf_ptr(&casted_arg->value.type->name)));
return false;
}
casted_args[fn_type_id->param_count] = casted_arg;
FnTypeParamInfo *param_info = &fn_type_id->param_info[fn_type_id->param_count];
param_info->type = casted_arg->value.type;
param_info->is_noalias = param_decl_node->data.param_decl.is_noalias;
impl_fn->param_source_nodes[fn_type_id->param_count] = param_decl_node;
fn_type_id->param_count += 1;
}
return true;
}
static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction,
FnTableEntry *fn_entry, TypeTableEntry *fn_type, IrInstruction *fn_ref,
IrInstruction *first_arg_ptr, bool inline_fn_call)
{
FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
size_t first_arg_1_or_0 = first_arg_ptr ? 1 : 0;
size_t var_args_1_or_0 = fn_type_id->is_var_args ? 1 : 0;
size_t src_param_count = fn_type_id->param_count - var_args_1_or_0;
size_t call_param_count = call_instruction->arg_count + first_arg_1_or_0;
AstNode *source_node = call_instruction->base.source_node;
AstNode *fn_proto_node = fn_entry ? fn_entry->proto_node : nullptr;;
if (fn_type_id->is_var_args) {
if (call_param_count < src_param_count) {
ErrorMsg *msg = ir_add_error_node(ira, source_node,
buf_sprintf("expected at least %zu arguments, found %zu", src_param_count, call_param_count));
if (fn_proto_node) {
add_error_note(ira->codegen, msg, fn_proto_node,
buf_sprintf("declared here"));
}
return ira->codegen->builtin_types.entry_invalid;
}
} else if (src_param_count != call_param_count) {
ErrorMsg *msg = ir_add_error_node(ira, source_node,
buf_sprintf("expected %zu arguments, found %zu", src_param_count, call_param_count));
if (fn_proto_node) {
add_error_note(ira->codegen, msg, fn_proto_node,
buf_sprintf("declared here"));
}
return ira->codegen->builtin_types.entry_invalid;
}
if (inline_fn_call) {
// No special handling is needed for compile time evaluation of generic functions.
if (!fn_entry || fn_entry->type_entry->data.fn.fn_type_id.is_extern) {
ir_add_error(ira, fn_ref, buf_sprintf("unable to evaluate constant expression"));
return ira->codegen->builtin_types.entry_invalid;
}
if (!ir_emit_backward_branch(ira, &call_instruction->base))
return ira->codegen->builtin_types.entry_invalid;
// Fork a scope of the function with known values for the parameters.
Scope *exec_scope = &fn_entry->fndef_scope->base;
size_t next_proto_i = 0;
if (first_arg_ptr) {
IrInstruction *first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr);
if (first_arg->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, first_arg, &exec_scope, &next_proto_i))
return ira->codegen->builtin_types.entry_invalid;
}
for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) {
IrInstruction *old_arg = call_instruction->args[call_i]->other;
if (old_arg->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, old_arg, &exec_scope, &next_proto_i))
return ira->codegen->builtin_types.entry_invalid;
}
AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
TypeTableEntry *return_type = analyze_type_expr(ira->codegen, exec_scope, return_type_node);
if (return_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *result;
auto entry = ira->codegen->memoized_fn_eval_table.maybe_get(exec_scope);
if (entry) {
result = entry->value;
} else {
// Analyze the fn body block like any other constant expression.
AstNode *body_node = fn_entry->fn_def_node->data.fn_def.body;
result = ir_eval_const_value(ira->codegen, exec_scope, body_node, return_type,
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry,
nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec);
if (result->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
ira->codegen->memoized_fn_eval_table.put(exec_scope, result);
}
ConstExprValue *out_val = ir_build_const_from(ira, &call_instruction->base,
result->value.depends_on_compile_var);
*out_val = result->value;
return ir_finish_anal(ira, return_type);
}
if (fn_type->data.fn.is_generic) {
assert(fn_entry);
IrInstruction **casted_args = allocate<IrInstruction *>(call_param_count);
// Fork a scope of the function with known values for the parameters.
Scope *parent_scope = fn_entry->fndef_scope->base.parent;
FnTableEntry *impl_fn = create_fn(fn_proto_node);
impl_fn->param_source_nodes = allocate<AstNode *>(call_param_count);
buf_init_from_buf(&impl_fn->symbol_name, &fn_entry->symbol_name);
impl_fn->fndef_scope = create_fndef_scope(impl_fn->fn_def_node, parent_scope, impl_fn);
impl_fn->child_scope = &impl_fn->fndef_scope->base;
FnTypeId inst_fn_type_id = {0};
init_fn_type_id(&inst_fn_type_id, fn_proto_node, call_param_count);
inst_fn_type_id.param_count = 0;
inst_fn_type_id.is_var_args = false;
// TODO maybe GenericFnTypeId can be replaced with using the child_scope directly
// as the key in generic_table
GenericFnTypeId *generic_id = allocate<GenericFnTypeId>(1);
generic_id->fn_entry = fn_entry;
generic_id->param_count = 0;
generic_id->params = allocate<ConstExprValue>(call_param_count);
size_t next_proto_i = 0;
if (first_arg_ptr) {
IrInstruction *first_arg;
assert(first_arg_ptr->value.type->id == TypeTableEntryIdPointer);
if (handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) {
first_arg = first_arg_ptr;
} else {
first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr);
if (first_arg->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
}
if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, first_arg, &impl_fn->child_scope,
&next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn))
{
return ira->codegen->builtin_types.entry_invalid;
}
}
bool found_first_var_arg = false;
size_t first_var_arg = inst_fn_type_id.param_count;
for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) {
IrInstruction *arg = call_instruction->args[call_i]->other;
if (arg->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i);
assert(param_decl_node->type == NodeTypeParamDecl);
bool is_var_args = param_decl_node->data.param_decl.is_var_args;
if (is_var_args && !found_first_var_arg) {
first_var_arg = inst_fn_type_id.param_count;
found_first_var_arg = true;
}
if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg, &impl_fn->child_scope,
&next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn))
{
return ira->codegen->builtin_types.entry_invalid;
}
}
if (fn_proto_node->data.fn_proto.is_var_args) {
AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i);
Buf *param_name = param_decl_node->data.param_decl.name;
ConstExprValue *var_args_val = create_const_arg_tuple(ira->codegen,
first_var_arg, inst_fn_type_id.param_count);
VariableTableEntry *var = add_variable(ira->codegen, param_decl_node,
impl_fn->child_scope, param_name, true, var_args_val);
var->value.depends_on_compile_var = true;
impl_fn->child_scope = var->child_scope;
}
{
AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
TypeTableEntry *return_type = analyze_type_expr(ira->codegen, impl_fn->child_scope, return_type_node);
if (return_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
inst_fn_type_id.return_type = return_type;
if (type_requires_comptime(return_type)) {
// Throw out our work and call the function as if it were inline.
return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true);
}
}
auto existing_entry = ira->codegen->generic_table.put_unique(generic_id, impl_fn);
if (existing_entry) {
// throw away all our work and use the existing function
impl_fn = existing_entry->value;
} else {
// finish instantiating the function
impl_fn->type_entry = get_fn_type(ira->codegen, &inst_fn_type_id);
if (impl_fn->type_entry->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
impl_fn->ir_executable.source_node = call_instruction->base.source_node;
impl_fn->ir_executable.parent_exec = ira->new_irb.exec;
impl_fn->analyzed_executable.source_node = call_instruction->base.source_node;
impl_fn->analyzed_executable.parent_exec = ira->new_irb.exec;
ira->codegen->fn_protos.append(impl_fn);
ira->codegen->fn_defs.append(impl_fn);
}
size_t impl_param_count = impl_fn->type_entry->data.fn.fn_type_id.param_count;
IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base,
impl_fn, nullptr, impl_param_count, casted_args, false);
TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type;
ir_add_alloca(ira, new_call_instruction, return_type);
return ir_finish_anal(ira, return_type);
}
IrInstruction **casted_args = allocate<IrInstruction *>(call_param_count);
size_t next_arg_index = 0;
if (first_arg_ptr) {
IrInstruction *first_arg;
assert(first_arg_ptr->value.type->id == TypeTableEntryIdPointer);
if (handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) {
first_arg = first_arg_ptr;
} else {
first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr);
if (first_arg->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
}
TypeTableEntry *param_type = fn_type_id->param_info[next_arg_index].type;
if (param_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *casted_arg = ir_implicit_cast(ira, first_arg, param_type);
if (casted_arg->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
casted_args[next_arg_index] = casted_arg;
next_arg_index += 1;
}
for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) {
IrInstruction *old_arg = call_instruction->args[call_i]->other;
if (old_arg->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *casted_arg;
if (next_arg_index < src_param_count) {
TypeTableEntry *param_type = fn_type_id->param_info[next_arg_index].type;
if (param_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
casted_arg = ir_implicit_cast(ira, old_arg, param_type);
if (casted_arg->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
} else {
casted_arg = old_arg;
}
casted_args[next_arg_index] = casted_arg;
next_arg_index += 1;
}
assert(next_arg_index == call_param_count);
TypeTableEntry *return_type = fn_type_id->return_type;
if (return_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base,
fn_entry, fn_ref, call_param_count, casted_args, false);
ir_add_alloca(ira, new_call_instruction, return_type);
return ir_finish_anal(ira, return_type);
}
static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionCall *call_instruction) {
IrInstruction *fn_ref = call_instruction->fn_ref->other;
if (fn_ref->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
bool is_inline = call_instruction->is_comptime ||
ir_should_inline(ira->new_irb.exec, call_instruction->base.scope);
if (is_inline || instr_is_comptime(fn_ref)) {
if (fn_ref->value.type->id == TypeTableEntryIdMetaType) {
TypeTableEntry *dest_type = ir_resolve_type(ira, fn_ref);
if (dest_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
size_t actual_param_count = call_instruction->arg_count;
if (actual_param_count != 1) {
ir_add_error_node(ira, call_instruction->base.source_node,
buf_sprintf("cast expression expects exactly one parameter"));
return ira->codegen->builtin_types.entry_invalid;
}
IrInstruction *arg = call_instruction->args[0]->other;
IrInstruction *cast_instruction = ir_analyze_cast(ira, &call_instruction->base, dest_type, arg);
if (cast_instruction->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
ir_link_new_instruction(cast_instruction, &call_instruction->base);
return ir_finish_anal(ira, cast_instruction->value.type);
} else if (fn_ref->value.type->id == TypeTableEntryIdFn) {
FnTableEntry *fn_table_entry = ir_resolve_fn(ira, fn_ref);
return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,
fn_ref, nullptr, is_inline);
} else if (fn_ref->value.type->id == TypeTableEntryIdBoundFn) {
assert(fn_ref->value.special == ConstValSpecialStatic);
FnTableEntry *fn_table_entry = fn_ref->value.data.x_bound_fn.fn;
IrInstruction *first_arg_ptr = fn_ref->value.data.x_bound_fn.first_arg;
return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,
nullptr, first_arg_ptr, is_inline);
} else {
ir_add_error_node(ira, fn_ref->source_node,
buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
}
if (fn_ref->value.type->id == TypeTableEntryIdFn) {
return ir_analyze_fn_call(ira, call_instruction, nullptr, fn_ref->value.type,
fn_ref, nullptr, false);
} else {
ir_add_error_node(ira, fn_ref->source_node,
buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
}
static TypeTableEntry *ir_analyze_unary_prefix_op_err(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
assert(un_op_instruction->op_id == IrUnOpError);
IrInstruction *value = un_op_instruction->value->other;
TypeTableEntry *meta_type = ir_resolve_type(ira, value);
TypeTableEntry *underlying_meta_type = get_underlying_type(meta_type);
switch (underlying_meta_type->id) {
case TypeTableEntryIdTypeDecl:
zig_unreachable();
case TypeTableEntryIdInvalid:
return ira->codegen->builtin_types.entry_invalid;
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdPointer:
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdPureError:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdEnumTag:
{
ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base,
value->value.depends_on_compile_var);
TypeTableEntry *result_type = get_error_type(ira->codegen, meta_type);
out_val->data.x_type = result_type;
return ira->codegen->builtin_types.entry_type;
}
case TypeTableEntryIdMetaType:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdVar:
case TypeTableEntryIdArgTuple:
ir_add_error_node(ira, un_op_instruction->base.source_node,
buf_sprintf("unable to wrap type '%s' in error type", buf_ptr(&meta_type->name)));
// TODO if meta_type is type decl, add note pointing to type decl declaration
return ira->codegen->builtin_types.entry_invalid;
}
zig_unreachable();
}
static TypeTableEntry *ir_analyze_dereference(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
IrInstruction *value = un_op_instruction->value->other;
TypeTableEntry *ptr_type = value->value.type;
TypeTableEntry *child_type;
if (ptr_type->id == TypeTableEntryIdInvalid) {
return ira->codegen->builtin_types.entry_invalid;
} else if (ptr_type->id == TypeTableEntryIdPointer) {
child_type = ptr_type->data.pointer.child_type;
} else {
ir_add_error_node(ira, un_op_instruction->base.source_node,
buf_sprintf("attempt to dereference non-pointer type '%s'",
buf_ptr(&ptr_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
// this dereference is always an rvalue because in the IR gen we identify lvalue and emit
// one of the ptr instructions
if (value->value.special != ConstValSpecialRuntime) {
ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base, false);
ConstExprValue *pointee = const_ptr_pointee(&value->value);
*out_val = *pointee;
return child_type;
}
ir_build_un_op_from(&ira->new_irb, &un_op_instruction->base, IrUnOpDereference, value);
return child_type;
}
static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
IrInstruction *value = un_op_instruction->value->other;
TypeTableEntry *type_entry = ir_resolve_type(ira, value);
TypeTableEntry *canon_type = get_underlying_type(type_entry);
if (type_is_invalid(canon_type))
return ira->codegen->builtin_types.entry_invalid;
switch (canon_type->id) {
case TypeTableEntryIdInvalid:
case TypeTableEntryIdVar:
case TypeTableEntryIdTypeDecl:
zig_unreachable();
case TypeTableEntryIdMetaType:
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdPointer:
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdPureError:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdEnumTag:
case TypeTableEntryIdArgTuple:
{
ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base,
value->value.depends_on_compile_var);
out_val->data.x_type = get_maybe_type(ira->codegen, type_entry);
return ira->codegen->builtin_types.entry_type;
}
case TypeTableEntryIdUnreachable:
ir_add_error_node(ira, un_op_instruction->base.source_node,
buf_sprintf("type '%s' not nullable", buf_ptr(&type_entry->name)));
// TODO if it's a type decl, put an error note here pointing to the decl
return ira->codegen->builtin_types.entry_invalid;
}
zig_unreachable();
}
static TypeTableEntry *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
IrInstruction *value = un_op_instruction->value->other;
TypeTableEntry *expr_type = value->value.type;
if (expr_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
bool is_wrap_op = (un_op_instruction->op_id == IrUnOpNegationWrap);
if ((expr_type->id == TypeTableEntryIdInt && expr_type->data.integral.is_signed) ||
expr_type->id == TypeTableEntryIdNumLitInt ||
((expr_type->id == TypeTableEntryIdFloat || expr_type->id == TypeTableEntryIdNumLitFloat) && !is_wrap_op))
{
if (instr_is_comptime(value)) {
ConstExprValue *target_const_val = ir_resolve_const(ira, value, UndefBad);
if (!target_const_val)
return ira->codegen->builtin_types.entry_invalid;
bool depends_on_compile_var = value->value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base, depends_on_compile_var);
bignum_negate(&out_val->data.x_bignum, &target_const_val->data.x_bignum);
if (expr_type->id == TypeTableEntryIdFloat ||
expr_type->id == TypeTableEntryIdNumLitFloat ||
expr_type->id == TypeTableEntryIdNumLitInt)
{
return expr_type;
}
bool overflow = !bignum_fits_in_bits(&out_val->data.x_bignum, expr_type->data.integral.bit_count, true);
if (is_wrap_op) {
if (overflow)
out_val->data.x_bignum.is_negative = true;
} else if (overflow) {
ir_add_error(ira, &un_op_instruction->base, buf_sprintf("negation caused overflow"));
return ira->codegen->builtin_types.entry_invalid;
}
return expr_type;
}
ir_build_un_op_from(&ira->new_irb, &un_op_instruction->base, un_op_instruction->op_id, value);
return expr_type;
}
const char *fmt = is_wrap_op ? "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'";
ir_add_error(ira, &un_op_instruction->base, buf_sprintf(fmt, buf_ptr(&expr_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
static TypeTableEntry *ir_analyze_bin_not(IrAnalyze *ira, IrInstructionUnOp *instruction) {
IrInstruction *value = instruction->value->other;
TypeTableEntry *expr_type = value->value.type;
if (expr_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
if (expr_type->id == TypeTableEntryIdInt) {
if (instr_is_comptime(value)) {
ConstExprValue *target_const_val = ir_resolve_const(ira, value, UndefBad);
if (!target_const_val)
return ira->codegen->builtin_types.entry_invalid;
bool depends_on_compile_var = value->value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
bignum_not(&out_val->data.x_bignum, &target_const_val->data.x_bignum,
expr_type->data.integral.bit_count, expr_type->data.integral.is_signed);
return expr_type;
}
ir_build_un_op_from(&ira->new_irb, &instruction->base, IrUnOpBinNot, value);
return expr_type;
}
ir_add_error(ira, &instruction->base,
buf_sprintf("unable to perform binary not operation on type '%s'", buf_ptr(&expr_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
IrUnOp op_id = un_op_instruction->op_id;
switch (op_id) {
case IrUnOpInvalid:
zig_unreachable();
case IrUnOpBinNot:
return ir_analyze_bin_not(ira, un_op_instruction);
case IrUnOpNegation:
case IrUnOpNegationWrap:
return ir_analyze_negation(ira, un_op_instruction);
case IrUnOpDereference:
return ir_analyze_dereference(ira, un_op_instruction);
case IrUnOpMaybe:
return ir_analyze_maybe(ira, un_op_instruction);
case IrUnOpError:
return ir_analyze_unary_prefix_op_err(ira, un_op_instruction);
}
zig_unreachable();
}
static TypeTableEntry *ir_analyze_instruction_br(IrAnalyze *ira, IrInstructionBr *br_instruction) {
IrBasicBlock *old_dest_block = br_instruction->dest_block;
bool is_comptime;
if (!ir_resolve_comptime(ira, br_instruction->is_comptime->other, &is_comptime))
return ir_unreach_error(ira);
if (is_comptime || old_dest_block->ref_count == 1)
return ir_inline_bb(ira, &br_instruction->base, old_dest_block);
IrBasicBlock *new_bb = ir_get_new_bb(ira, old_dest_block, &br_instruction->base);
ir_build_br_from(&ira->new_irb, &br_instruction->base, new_bb);
return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
}
static TypeTableEntry *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructionCondBr *cond_br_instruction) {
IrInstruction *condition = cond_br_instruction->condition->other;
if (condition->value.type->id == TypeTableEntryIdInvalid)
return ir_unreach_error(ira);
bool is_comptime;
if (!ir_resolve_comptime(ira, cond_br_instruction->is_comptime->other, &is_comptime))
return ir_unreach_error(ira);
if (is_comptime || instr_is_comptime(condition)) {
bool cond_is_true;
if (!ir_resolve_bool(ira, condition, &cond_is_true))
return ir_unreach_error(ira);
if (!cond_br_instruction->base.is_gen && !condition->value.depends_on_compile_var &&
!ir_should_inline(ira->new_irb.exec, cond_br_instruction->base.scope))
{
const char *true_or_false = cond_is_true ? "true" : "false";
ir_add_error(ira, &cond_br_instruction->base,
buf_sprintf("condition is always %s; unnecessary if statement", true_or_false));
}
IrBasicBlock *old_dest_block = cond_is_true ?
cond_br_instruction->then_block : cond_br_instruction->else_block;
if (is_comptime || old_dest_block->ref_count == 1)
return ir_inline_bb(ira, &cond_br_instruction->base, old_dest_block);
IrBasicBlock *new_dest_block = ir_get_new_bb(ira, old_dest_block, &cond_br_instruction->base);
ir_build_br_from(&ira->new_irb, &cond_br_instruction->base, new_dest_block);
return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
}
TypeTableEntry *bool_type = ira->codegen->builtin_types.entry_bool;
IrInstruction *casted_condition = ir_implicit_cast(ira, condition, bool_type);
if (casted_condition == ira->codegen->invalid_instruction)
return ir_unreach_error(ira);
assert(cond_br_instruction->then_block != cond_br_instruction->else_block);
IrBasicBlock *new_then_block = ir_get_new_bb(ira, cond_br_instruction->then_block, &cond_br_instruction->base);
IrBasicBlock *new_else_block = ir_get_new_bb(ira, cond_br_instruction->else_block, &cond_br_instruction->base);
ir_build_cond_br_from(&ira->new_irb, &cond_br_instruction->base,
casted_condition, new_then_block, new_else_block, nullptr);
return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
}
static TypeTableEntry *ir_analyze_instruction_unreachable(IrAnalyze *ira,
IrInstructionUnreachable *unreachable_instruction)
{
ir_build_unreachable_from(&ira->new_irb, &unreachable_instruction->base);
return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
}
static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPhi *phi_instruction) {
if (ira->const_predecessor_bb) {
for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) {
IrBasicBlock *predecessor = phi_instruction->incoming_blocks[i];
if (predecessor != ira->const_predecessor_bb)
continue;
IrInstruction *value = phi_instruction->incoming_values[i]->other;
assert(value->value.type);
if (value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
if (value->value.special != ConstValSpecialRuntime) {
ConstExprValue *out_val = ir_build_const_from(ira, &phi_instruction->base, true);
*out_val = value->value;
out_val->depends_on_compile_var = true;
} else {
phi_instruction->base.other = value;
}
return value->value.type;
}
zig_unreachable();
}
ZigList<IrBasicBlock*> new_incoming_blocks = {0};
ZigList<IrInstruction*> new_incoming_values = {0};
for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) {
IrBasicBlock *predecessor = phi_instruction->incoming_blocks[i];
if (predecessor->ref_count == 0)
continue;
IrInstruction *old_value = phi_instruction->incoming_values[i];
assert(old_value);
IrInstruction *new_value = old_value->other;
if (!new_value || new_value->value.type->id == TypeTableEntryIdUnreachable)
continue;
if (new_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
assert(predecessor->other);
new_incoming_blocks.append(predecessor->other);
new_incoming_values.append(new_value);
}
if (new_incoming_blocks.length == 0) {
ir_build_const_from(ira, &phi_instruction->base, true);
return ira->codegen->builtin_types.entry_void;
}
if (new_incoming_blocks.length == 1) {
IrInstruction *first_value = new_incoming_values.at(0);
phi_instruction->base.other = first_value;
return first_value->value.type;
}
TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node,
new_incoming_values.items, new_incoming_values.length);
if (resolved_type->id == TypeTableEntryIdInvalid)
return resolved_type;
if (resolved_type->id == TypeTableEntryIdNumLitFloat ||
resolved_type->id == TypeTableEntryIdNumLitInt ||
resolved_type->id == TypeTableEntryIdNullLit ||
resolved_type->id == TypeTableEntryIdUndefLit)
{
ir_add_error_node(ira, phi_instruction->base.source_node,
buf_sprintf("unable to infer expression type"));
return ira->codegen->builtin_types.entry_invalid;
}
// cast all values to the resolved type. however we can't put cast instructions in front of the phi instruction.
// so we go back and insert the casts as the last instruction in the corresponding predecessor blocks, and
// then make sure the branch instruction is preserved.
IrBasicBlock *cur_bb = ira->new_irb.current_basic_block;
for (size_t i = 0; i < new_incoming_values.length; i += 1) {
IrInstruction *new_value = new_incoming_values.at(i);
IrBasicBlock *predecessor = new_incoming_blocks.at(i);
IrInstruction *branch_instruction = predecessor->instruction_list.pop();
ir_set_cursor_at_end(&ira->new_irb, predecessor);
IrInstruction *casted_value = ir_implicit_cast(ira, new_value, resolved_type);
new_incoming_values.items[i] = casted_value;
predecessor->instruction_list.append(branch_instruction);
}
ir_set_cursor_at_end(&ira->new_irb, cur_bb);
ir_build_phi_from(&ira->new_irb, &phi_instruction->base, new_incoming_blocks.length,
new_incoming_blocks.items, new_incoming_values.items);
return resolved_type;
}
static TypeTableEntry *ir_analyze_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
VariableTableEntry *var, bool is_const_ptr, bool depends_on_compile_var)
{
assert(var->value.type);
if (var->value.type->id == TypeTableEntryIdInvalid)
return var->value.type;
bool is_comptime = ir_get_var_is_comptime(var);
ConstExprValue *mem_slot = nullptr;
FnTableEntry *fn_entry = scope_fn_entry(var->parent_scope);
if (var->src_is_const && var->value.special == ConstValSpecialStatic) {
mem_slot = &var->value;
assert(mem_slot->special != ConstValSpecialRuntime);
} else if (fn_entry) {
// TODO once the analyze code is fully ported over to IR we won't need this SIZE_MAX thing.
if (var->mem_slot_index != SIZE_MAX && (is_comptime || var->gen_is_const))
mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index];
}
if (mem_slot && mem_slot->special != ConstValSpecialRuntime) {
ConstPtrSpecial ptr_special = is_comptime ? ConstPtrSpecialInline : ConstPtrSpecialNone;
bool is_const = (var->value.type->id == TypeTableEntryIdMetaType) ? is_const_ptr : var->src_is_const;
depends_on_compile_var = mem_slot->depends_on_compile_var || depends_on_compile_var || is_comptime;
return ir_analyze_const_ptr(ira, instruction, mem_slot, var->value.type,
depends_on_compile_var, ptr_special, is_const);
} else {
ir_build_var_ptr_from(&ira->new_irb, instruction, var, false);
type_ensure_zero_bits_known(ira->codegen, var->value.type);
return get_pointer_to_type(ira->codegen, var->value.type, var->src_is_const);
}
}
static TypeTableEntry *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructionVarPtr *var_ptr_instruction) {
VariableTableEntry *var = var_ptr_instruction->var;
return ir_analyze_var_ptr(ira, &var_ptr_instruction->base, var, var_ptr_instruction->is_const, false);
}
static VariableTableEntry *get_fn_var_by_index(FnTableEntry *fn_entry, size_t index) {
size_t next_var_i = 0;
FnGenParamInfo *gen_param_info = fn_entry->type_entry->data.fn.gen_param_info;
for (size_t param_i = 0; param_i < index; param_i += 1) {
FnGenParamInfo *info = &gen_param_info[param_i];
if (info->gen_index == SIZE_MAX)
continue;
next_var_i += 1;
}
FnGenParamInfo *info = &gen_param_info[index];
if (info->gen_index == SIZE_MAX)
return nullptr;
return fn_entry->variable_list.at(next_var_i);
}
static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) {
IrInstruction *array_ptr = elem_ptr_instruction->array_ptr->other;
if (array_ptr->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *elem_index = elem_ptr_instruction->elem_index->other;
if (elem_index->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
// This will be a pointer type because elem ptr IR instruction operates on a pointer to a thing.
TypeTableEntry *ptr_type = array_ptr->value.type;
assert(ptr_type->id == TypeTableEntryIdPointer);
TypeTableEntry *array_type = ptr_type->data.pointer.child_type;
TypeTableEntry *return_type;
if (array_type->id == TypeTableEntryIdInvalid) {
return array_type;
} else if (array_type->id == TypeTableEntryIdArray) {
if (array_type->data.array.len == 0) {
ir_add_error_node(ira, elem_ptr_instruction->base.source_node,
buf_sprintf("index 0 outside array of size 0"));
}
TypeTableEntry *child_type = array_type->data.array.child_type;
return_type = get_pointer_to_type(ira->codegen, child_type, false);
} else if (array_type->id == TypeTableEntryIdPointer) {
return_type = array_type;
} else if (is_slice(array_type)) {
return_type = array_type->data.structure.fields[0].type_entry;
} else if (array_type->id == TypeTableEntryIdArgTuple) {
ConstExprValue *ptr_val = ir_resolve_const(ira, array_ptr, UndefBad);
if (!ptr_val)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *args_val = const_ptr_pointee(ptr_val);
size_t start = args_val->data.x_arg_tuple.start_index;
size_t end = args_val->data.x_arg_tuple.end_index;
ConstExprValue *elem_index_val = ir_resolve_const(ira, elem_index, UndefBad);
if (!elem_index_val)
return ira->codegen->builtin_types.entry_invalid;
size_t index = bignum_to_twos_complement(&elem_index_val->data.x_bignum);
size_t len = end - start;
if (index >= len) {
ir_add_error(ira, &elem_ptr_instruction->base,
buf_sprintf("index %zu outside argument list of size %zu", index, len));
return ira->codegen->builtin_types.entry_invalid;
}
size_t abs_index = start + index;
FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
assert(fn_entry);
VariableTableEntry *var = get_fn_var_by_index(fn_entry, abs_index);
bool depends_on_compile_var = array_ptr->value.depends_on_compile_var ||
elem_index->value.depends_on_compile_var;
if (var) {
return ir_analyze_var_ptr(ira, &elem_ptr_instruction->base, var, true, depends_on_compile_var);
} else {
return ir_analyze_const_ptr(ira, &elem_ptr_instruction->base, &ira->codegen->const_void_val,
ira->codegen->builtin_types.entry_void, depends_on_compile_var, ConstPtrSpecialNone, true);
}
} else {
ir_add_error_node(ira, elem_ptr_instruction->base.source_node,
buf_sprintf("array access of non-array type '%s'", buf_ptr(&array_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize;
IrInstruction *casted_elem_index = ir_implicit_cast(ira, elem_index, usize);
if (casted_elem_index == ira->codegen->invalid_instruction)
return ira->codegen->builtin_types.entry_invalid;
bool safety_check_on = elem_ptr_instruction->safety_check_on;
if (instr_is_comptime(casted_elem_index)) {
uint64_t index = casted_elem_index->value.data.x_bignum.data.x_uint;
if (array_type->id == TypeTableEntryIdArray) {
uint64_t array_len = array_type->data.array.len;
if (index >= array_len) {
ir_add_error_node(ira, elem_ptr_instruction->base.source_node,
buf_sprintf("index %" PRIu64 " outside array of size %" PRIu64,
index, array_len));
return ira->codegen->builtin_types.entry_invalid;
}
safety_check_on = false;
}
ConstExprValue *array_ptr_val;
if (array_ptr->value.special != ConstValSpecialRuntime &&
(array_ptr_val = const_ptr_pointee(&array_ptr->value)) &&
array_ptr_val->special != ConstValSpecialRuntime)
{
bool depends_on_compile_var = array_ptr_val->depends_on_compile_var ||
casted_elem_index->value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &elem_ptr_instruction->base, depends_on_compile_var);
if (array_type->id == TypeTableEntryIdPointer) {
size_t offset = array_ptr_val->data.x_ptr.index;
size_t new_index;
size_t mem_size;
size_t old_size;
if (offset == SIZE_MAX) {
new_index = SIZE_MAX;
mem_size = 1;
old_size = 1;
} else {
new_index = offset + index;
mem_size = array_ptr_val->data.x_ptr.base_ptr->data.x_array.size;
old_size = mem_size - offset;
}
if (new_index >= mem_size) {
ir_add_error_node(ira, elem_ptr_instruction->base.source_node,
buf_sprintf("index %" PRIu64 " outside pointer of size %" PRIu64, index, old_size));
return ira->codegen->builtin_types.entry_invalid;
}
out_val->data.x_ptr.base_ptr = array_ptr_val->data.x_ptr.base_ptr;
out_val->data.x_ptr.index = new_index;
} else if (is_slice(array_type)) {
ConstExprValue *ptr_field = &array_ptr_val->data.x_struct.fields[slice_ptr_index];
ConstExprValue *len_field = &array_ptr_val->data.x_struct.fields[slice_len_index];
uint64_t slice_len = len_field->data.x_bignum.data.x_uint;
if (index >= slice_len) {
ir_add_error_node(ira, elem_ptr_instruction->base.source_node,
buf_sprintf("index %" PRIu64 " outside slice of size %" PRIu64,
index, slice_len));
return ira->codegen->builtin_types.entry_invalid;
}
out_val->data.x_ptr.base_ptr = ptr_field->data.x_ptr.base_ptr;
size_t offset = ptr_field->data.x_ptr.index;
if (offset == SIZE_MAX) {
out_val->data.x_ptr.index = SIZE_MAX;
} else {
uint64_t new_index = offset + index;
assert(new_index < ptr_field->data.x_ptr.base_ptr->data.x_array.size);
out_val->data.x_ptr.index = new_index;
}
} else if (array_type->id == TypeTableEntryIdArray) {
out_val->data.x_ptr.base_ptr = array_ptr_val;
out_val->data.x_ptr.index = index;
} else {
zig_unreachable();
}
return return_type;
}
}
ir_build_elem_ptr_from(&ira->new_irb, &elem_ptr_instruction->base, array_ptr,
casted_elem_index, safety_check_on);
return return_type;
}
static TypeTableEntry *ir_analyze_container_member_access_inner(IrAnalyze *ira,
TypeTableEntry *bare_struct_type, Buf *field_name, IrInstructionFieldPtr *field_ptr_instruction,
IrInstruction *container_ptr, TypeTableEntry *container_type)
{
if (!is_slice(bare_struct_type)) {
ScopeDecls *container_scope = get_container_scope(bare_struct_type);
auto entry = container_scope->decl_table.maybe_get(field_name);
Tld *tld = entry ? entry->value : nullptr;
if (tld && tld->id == TldIdFn) {
resolve_top_level_decl(ira->codegen, tld, false);
if (tld->resolution == TldResolutionInvalid)
return ira->codegen->builtin_types.entry_invalid;
TldFn *tld_fn = (TldFn *)tld;
FnTableEntry *fn_entry = tld_fn->fn_entry;
bool depends_on_compile_var = container_ptr->value.depends_on_compile_var;
IrInstruction *bound_fn_value = ir_build_const_bound_fn(&ira->new_irb, field_ptr_instruction->base.scope,
field_ptr_instruction->base.source_node, fn_entry, container_ptr, depends_on_compile_var);
return ir_analyze_ref(ira, &field_ptr_instruction->base, bound_fn_value, true);
}
}
ir_add_error_node(ira, field_ptr_instruction->base.source_node,
buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&bare_struct_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name,
IrInstructionFieldPtr *field_ptr_instruction, IrInstruction *container_ptr, TypeTableEntry *container_type)
{
TypeTableEntry *bare_type = container_ref_type(container_type);
ensure_complete_type(ira->codegen, bare_type);
assert(container_ptr->value.type->id == TypeTableEntryIdPointer);
bool is_const = container_ptr->value.type->data.pointer.is_const;
if (bare_type->id == TypeTableEntryIdStruct) {
if (bare_type->data.structure.is_invalid)
return ira->codegen->builtin_types.entry_invalid;
TypeStructField *field = find_struct_type_field(bare_type, field_name);
if (field) {
if (instr_is_comptime(container_ptr)) {
ConstExprValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad);
if (!ptr_val)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *struct_val = const_ptr_pointee(ptr_val);
if (value_is_comptime(struct_val)) {
ConstExprValue *field_val = &struct_val->data.x_struct.fields[field->src_index];
if (value_is_comptime(field_val)) {
bool depends_on_compile_var = field_val->depends_on_compile_var ||
struct_val->depends_on_compile_var || ptr_val->depends_on_compile_var;
return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, field_val,
field_val->type, depends_on_compile_var, ConstPtrSpecialNone, is_const);
}
}
}
ir_build_struct_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field);
return get_pointer_to_type(ira->codegen, field->type_entry, is_const);
} else {
return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
field_ptr_instruction, container_ptr, container_type);
}
} else if (bare_type->id == TypeTableEntryIdEnum) {
if (bare_type->data.enumeration.is_invalid)
return ira->codegen->builtin_types.entry_invalid;
TypeEnumField *field = find_enum_type_field(bare_type, field_name);
if (field) {
ir_build_enum_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field);
return get_pointer_to_type(ira->codegen, field->type_entry, is_const);
} else {
return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
field_ptr_instruction, container_ptr, container_type);
}
} else if (bare_type->id == TypeTableEntryIdUnion) {
zig_panic("TODO");
} else {
zig_unreachable();
}
}
static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source_instruction, Tld *tld,
bool depends_on_compile_var)
{
bool pointer_only = false;
resolve_top_level_decl(ira->codegen, tld, pointer_only);
if (tld->resolution == TldResolutionInvalid)
return ira->codegen->builtin_types.entry_invalid;
switch (tld->id) {
case TldIdContainer:
zig_unreachable();
case TldIdVar:
{
TldVar *tld_var = (TldVar *)tld;
VariableTableEntry *var = tld_var->var;
return ir_analyze_var_ptr(ira, source_instruction, var, false, depends_on_compile_var);
}
case TldIdFn:
{
TldFn *tld_fn = (TldFn *)tld;
FnTableEntry *fn_entry = tld_fn->fn_entry;
assert(fn_entry->type_entry);
if (fn_entry->type_entry->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
// TODO instead of allocating this every time, put it in the tld value and we can reference
// the same one every time
ConstExprValue *const_val = allocate<ConstExprValue>(1);
const_val->special = ConstValSpecialStatic;
const_val->type = fn_entry->type_entry;
const_val->data.x_fn = fn_entry;
bool ptr_is_const = true;
return ir_analyze_const_ptr(ira, source_instruction, const_val, fn_entry->type_entry,
depends_on_compile_var, ConstPtrSpecialNone, ptr_is_const);
}
case TldIdTypeDef:
{
TldTypeDef *tld_typedef = (TldTypeDef *)tld;
assert(tld_typedef->type_entry);
// TODO instead of allocating this every time, put it in the tld value and we can reference
// the same one every time
ConstExprValue *const_val = allocate<ConstExprValue>(1);
const_val->special = ConstValSpecialStatic;
const_val->type = ira->codegen->builtin_types.entry_type;
const_val->data.x_type = tld_typedef->type_entry;
bool ptr_is_const = true;
return ir_analyze_const_ptr(ira, source_instruction, const_val, ira->codegen->builtin_types.entry_type,
depends_on_compile_var, ConstPtrSpecialNone, ptr_is_const);
}
}
zig_unreachable();
}
static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) {
IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other;
if (container_ptr->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *container_type;
if (container_ptr->value.type->id == TypeTableEntryIdPointer) {
container_type = container_ptr->value.type->data.pointer.child_type;
} else if (container_ptr->value.type->id == TypeTableEntryIdMetaType) {
container_type = container_ptr->value.type;
} else {
zig_unreachable();
}
bool depends_on_compile_var = container_ptr->value.depends_on_compile_var;
Buf *field_name = field_ptr_instruction->field_name;
AstNode *source_node = field_ptr_instruction->base.source_node;
if (container_type->id == TypeTableEntryIdInvalid) {
return container_type;
} else if (is_container_ref(container_type)) {
assert(container_ptr->value.type->id == TypeTableEntryIdPointer);
if (container_type->id == TypeTableEntryIdPointer) {
TypeTableEntry *bare_type = container_ref_type(container_type);
IrInstruction *container_child = ir_get_deref(ira, &field_ptr_instruction->base, container_ptr);
return ir_analyze_container_field_ptr(ira, field_name, field_ptr_instruction, container_child, bare_type);
} else {
return ir_analyze_container_field_ptr(ira, field_name, field_ptr_instruction, container_ptr, container_type);
}
} else if (container_type->id == TypeTableEntryIdArray) {
if (buf_eql_str(field_name, "len")) {
ConstExprValue *len_val = allocate<ConstExprValue>(1);
init_const_usize(ira->codegen, len_val, container_type->data.array.len);
TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize;
bool ptr_is_const = true;
return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, len_val,
usize, false, ConstPtrSpecialNone, ptr_is_const);
} else {
ir_add_error_node(ira, source_node,
buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),
buf_ptr(&container_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
} else if (container_type->id == TypeTableEntryIdArgTuple) {
ConstExprValue *container_ptr_val = ir_resolve_const(ira, container_ptr, UndefBad);
if (!container_ptr_val)
return ira->codegen->builtin_types.entry_invalid;
assert(container_ptr->value.type->id == TypeTableEntryIdPointer);
ConstExprValue *child_val = const_ptr_pointee(container_ptr_val);
if (buf_eql_str(field_name, "len")) {
ConstExprValue *len_val = allocate<ConstExprValue>(1);
size_t len = child_val->data.x_arg_tuple.end_index - child_val->data.x_arg_tuple.start_index;
init_const_usize(ira->codegen, len_val, len);
TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize;
bool ptr_is_const = true;
return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, len_val,
usize, false, ConstPtrSpecialNone, ptr_is_const);
} else {
ir_add_error_node(ira, source_node,
buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),
buf_ptr(&container_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
} else if (container_type->id == TypeTableEntryIdMetaType) {
ConstExprValue *container_ptr_val = ir_resolve_const(ira, container_ptr, UndefBad);
if (!container_ptr_val)
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *child_type;
if (container_ptr->value.type->id == TypeTableEntryIdMetaType) {
TypeTableEntry *ptr_type = container_ptr_val->data.x_type;
assert(ptr_type->id == TypeTableEntryIdPointer);
child_type = ptr_type->data.pointer.child_type;
} else if (container_ptr->value.type->id == TypeTableEntryIdPointer) {
ConstExprValue *child_val = const_ptr_pointee(container_ptr_val);
child_type = child_val->data.x_type;
} else {
zig_unreachable();
}
if (child_type->id == TypeTableEntryIdInvalid) {
return ira->codegen->builtin_types.entry_invalid;
} else if (is_container(child_type)) {
if (child_type->id == TypeTableEntryIdEnum) {
ensure_complete_type(ira->codegen, child_type);
if (child_type->data.enumeration.is_invalid)
return ira->codegen->builtin_types.entry_invalid;
TypeEnumField *field = find_enum_type_field(child_type, field_name);
if (field) {
if (field->type_entry->id == TypeTableEntryIdVoid) {
bool ptr_is_const = true;
return ir_analyze_const_ptr(ira, &field_ptr_instruction->base,
create_const_enum_tag(child_type, field->value), child_type, depends_on_compile_var,
ConstPtrSpecialNone, ptr_is_const);
} else {
bool ptr_is_const = true;
return ir_analyze_const_ptr(ira, &field_ptr_instruction->base,
create_const_unsigned_negative(child_type->data.enumeration.tag_type, field->value, false),
child_type->data.enumeration.tag_type, depends_on_compile_var,
ConstPtrSpecialNone, ptr_is_const);
}
}
}
ScopeDecls *container_scope = get_container_scope(child_type);
auto entry = container_scope->decl_table.maybe_get(field_name);
Tld *tld = entry ? entry->value : nullptr;
if (tld) {
return ir_analyze_decl_ref(ira, &field_ptr_instruction->base, tld, depends_on_compile_var);
}
ir_add_error(ira, &field_ptr_instruction->base,
buf_sprintf("container '%s' has no member called '%s'",
buf_ptr(&child_type->name), buf_ptr(field_name)));
return ira->codegen->builtin_types.entry_invalid;
} else if (child_type->id == TypeTableEntryIdPureError) {
auto err_table_entry = ira->codegen->error_table.maybe_get(field_name);
if (err_table_entry) {
ConstExprValue *const_val = allocate<ConstExprValue>(1);
const_val->special = ConstValSpecialStatic;
const_val->type = child_type;
const_val->data.x_pure_err = err_table_entry->value;
bool ptr_is_const = true;
return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, const_val,
child_type, depends_on_compile_var, ConstPtrSpecialNone, ptr_is_const);
}
ir_add_error(ira, &field_ptr_instruction->base,
buf_sprintf("use of undeclared error value '%s'", buf_ptr(field_name)));
return ira->codegen->builtin_types.entry_invalid;
} else if (child_type->id == TypeTableEntryIdInt) {
if (buf_eql_str(field_name, "bit_count")) {
bool ptr_is_const = true;
return ir_analyze_const_ptr(ira, &field_ptr_instruction->base,
create_const_unsigned_negative(ira->codegen->builtin_types.entry_num_lit_int,
child_type->data.integral.bit_count, false),
ira->codegen->builtin_types.entry_num_lit_int, depends_on_compile_var,
ConstPtrSpecialNone, ptr_is_const);
} else if (buf_eql_str(field_name, "is_signed")) {
bool ptr_is_const = true;
return ir_analyze_const_ptr(ira, &field_ptr_instruction->base,
create_const_bool(ira->codegen, child_type->data.integral.is_signed),
ira->codegen->builtin_types.entry_bool, depends_on_compile_var,
ConstPtrSpecialNone, ptr_is_const);
} else {
ir_add_error(ira, &field_ptr_instruction->base,
buf_sprintf("type '%s' has no member called '%s'",
buf_ptr(&child_type->name), buf_ptr(field_name)));
return ira->codegen->builtin_types.entry_invalid;
}
} else {
ir_add_error(ira, &field_ptr_instruction->base,
buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
} else if (container_type->id == TypeTableEntryIdNamespace) {
assert(container_ptr->value.type->id == TypeTableEntryIdPointer);
ConstExprValue *container_ptr_val = ir_resolve_const(ira, container_ptr, UndefBad);
if (!container_ptr_val)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *namespace_val = const_ptr_pointee(container_ptr_val);
assert(namespace_val->special == ConstValSpecialStatic);
ImportTableEntry *namespace_import = namespace_val->data.x_import;
Tld *tld = find_decl(&namespace_import->decls_scope->base, field_name);
if (!tld) {
// we must now resolve all the use decls
// TODO move this check to find_decl?
for (size_t i = 0; i < namespace_import->use_decls.length; i += 1) {
AstNode *use_decl_node = namespace_import->use_decls.at(i);
if (use_decl_node->data.use.resolution == TldResolutionUnresolved) {
preview_use_decl(ira->codegen, use_decl_node);
}
resolve_use_decl(ira->codegen, use_decl_node);
}
tld = find_decl(&namespace_import->decls_scope->base, field_name);
}
if (tld) {
if (tld->visib_mod == VisibModPrivate &&
tld->import != source_node->owner)
{
ErrorMsg *msg = ir_add_error_node(ira, source_node,
buf_sprintf("'%s' is private", buf_ptr(field_name)));
add_error_note(ira->codegen, msg, tld->source_node, buf_sprintf("declared here"));
return ira->codegen->builtin_types.entry_invalid;
}
return ir_analyze_decl_ref(ira, &field_ptr_instruction->base, tld, depends_on_compile_var);
} else {
const char *import_name = namespace_import->path ? buf_ptr(namespace_import->path) : "(C import)";
ir_add_error_node(ira, source_node,
buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), import_name));
return ira->codegen->builtin_types.entry_invalid;
}
} else {
ir_add_error_node(ira, field_ptr_instruction->base.source_node,
buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
}
static TypeTableEntry *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) {
IrInstruction *ptr = load_ptr_instruction->ptr->other;
IrInstruction *result = ir_get_deref(ira, &load_ptr_instruction->base, ptr);
ir_link_new_instruction(result, &load_ptr_instruction->base);
assert(result->value.type);
return result->value.type;
}
static TypeTableEntry *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *store_ptr_instruction) {
IrInstruction *ptr = store_ptr_instruction->ptr->other;
if (ptr->value.type->id == TypeTableEntryIdInvalid)
return ptr->value.type;
IrInstruction *value = store_ptr_instruction->value->other;
if (value->value.type->id == TypeTableEntryIdInvalid)
return value->value.type;
assert(ptr->value.type->id == TypeTableEntryIdPointer);
if (ptr->value.type->data.pointer.is_const && !store_ptr_instruction->base.is_gen) {
ir_add_error(ira, &store_ptr_instruction->base, buf_sprintf("cannot assign to constant"));
return ira->codegen->builtin_types.entry_invalid;
}
TypeTableEntry *child_type = ptr->value.type->data.pointer.child_type;
IrInstruction *casted_value = ir_implicit_cast(ira, value, child_type);
if (casted_value == ira->codegen->invalid_instruction)
return ira->codegen->builtin_types.entry_invalid;
if (ptr->value.special != ConstValSpecialRuntime) {
bool is_inline = (ptr->value.data.x_ptr.special == ConstPtrSpecialInline);
if (casted_value->value.special != ConstValSpecialRuntime) {
ConstExprValue *dest_val = const_ptr_pointee(&ptr->value);
if (dest_val->special != ConstValSpecialRuntime) {
*dest_val = casted_value->value;
return ir_analyze_void(ira, &store_ptr_instruction->base);
}
}
if (is_inline) {
ir_add_error(ira, &store_ptr_instruction->base,
buf_sprintf("cannot store runtime value in compile time variable"));
return ira->codegen->builtin_types.entry_invalid;
}
}
if (ptr->value.special != ConstValSpecialRuntime) {
// This memory location is transforming from known at compile time to known at runtime.
// We must emit our own var ptr instruction.
// TODO can we delete this code now that we have inline var?
ptr->value.special = ConstValSpecialRuntime;
IrInstruction *new_ptr_inst;
if (ptr->id == IrInstructionIdVarPtr) {
IrInstructionVarPtr *var_ptr_inst = (IrInstructionVarPtr *)ptr;
VariableTableEntry *var = var_ptr_inst->var;
new_ptr_inst = ir_build_var_ptr(&ira->new_irb, store_ptr_instruction->base.scope,
store_ptr_instruction->base.source_node, var, false);
assert(var->mem_slot_index != SIZE_MAX);
ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index];
mem_slot->special = ConstValSpecialRuntime;
} else if (ptr->id == IrInstructionIdFieldPtr) {
zig_panic("TODO");
} else if (ptr->id == IrInstructionIdElemPtr) {
zig_panic("TODO");
} else {
zig_unreachable();
}
new_ptr_inst->value.type = ptr->value.type;
ir_build_store_ptr(&ira->new_irb, store_ptr_instruction->base.scope,
store_ptr_instruction->base.source_node, new_ptr_inst, casted_value);
return ir_analyze_void(ira, &store_ptr_instruction->base);
}
ir_build_store_ptr_from(&ira->new_irb, &store_ptr_instruction->base, ptr, casted_value);
return ira->codegen->builtin_types.entry_void;
}
static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) {
IrInstruction *expr_value = typeof_instruction->value->other;
TypeTableEntry *type_entry = expr_value->value.type;
switch (type_entry->id) {
case TypeTableEntryIdInvalid:
return type_entry;
case TypeTableEntryIdVar:
ir_add_error_node(ira, expr_value->source_node,
buf_sprintf("type '%s' not eligible for @typeOf", buf_ptr(&type_entry->name)));
return ira->codegen->builtin_types.entry_invalid;
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdMetaType:
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdPointer:
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdPureError:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
case TypeTableEntryIdTypeDecl:
case TypeTableEntryIdEnumTag:
case TypeTableEntryIdArgTuple:
{
ConstExprValue *out_val = ir_build_const_from(ira, &typeof_instruction->base, true);
// TODO depends_on_compile_var should be set based on whether the type of the expression
// depends_on_compile_var. but we currently don't have a thing to tell us if the type of
// something depends on a compile var
out_val->data.x_type = type_entry;
return ira->codegen->builtin_types.entry_type;
}
}
zig_unreachable();
}
static TypeTableEntry *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira,
IrInstructionToPtrType *to_ptr_type_instruction)
{
IrInstruction *value = to_ptr_type_instruction->value->other;
TypeTableEntry *type_entry = value->value.type;
if (type_entry->id == TypeTableEntryIdInvalid)
return type_entry;
TypeTableEntry *ptr_type;
if (type_entry->id == TypeTableEntryIdArray) {
ptr_type = get_pointer_to_type(ira->codegen, type_entry->data.array.child_type, false);
} else if (is_slice(type_entry)) {
ptr_type = type_entry->data.structure.fields[0].type_entry;
} else if (type_entry->id == TypeTableEntryIdArgTuple) {
ConstExprValue *arg_tuple_val = ir_resolve_const(ira, value, UndefBad);
if (!arg_tuple_val)
return ira->codegen->builtin_types.entry_invalid;
zig_panic("TODO for loop on var args");
} else {
ir_add_error_node(ira, to_ptr_type_instruction->base.source_node,
buf_sprintf("expected array type, found '%s'", buf_ptr(&type_entry->name)));
return ira->codegen->builtin_types.entry_invalid;
}
ConstExprValue *out_val = ir_build_const_from(ira, &to_ptr_type_instruction->base,
value->value.depends_on_compile_var);
out_val->data.x_type = ptr_type;
return ira->codegen->builtin_types.entry_type;
}
static TypeTableEntry *ir_analyze_instruction_ptr_type_child(IrAnalyze *ira,
IrInstructionPtrTypeChild *ptr_type_child_instruction)
{
IrInstruction *type_value = ptr_type_child_instruction->value->other;
TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
if (type_entry->id == TypeTableEntryIdInvalid)
return type_entry;
// TODO handle typedefs
if (type_entry->id != TypeTableEntryIdPointer) {
ir_add_error_node(ira, ptr_type_child_instruction->base.source_node,
buf_sprintf("expected pointer type, found '%s'", buf_ptr(&type_entry->name)));
return ira->codegen->builtin_types.entry_invalid;
}
ConstExprValue *out_val = ir_build_const_from(ira, &ptr_type_child_instruction->base,
type_value->value.depends_on_compile_var);
out_val->data.x_type = type_entry->data.pointer.child_type;
return ira->codegen->builtin_types.entry_type;
}
static TypeTableEntry *ir_analyze_instruction_set_fn_test(IrAnalyze *ira,
IrInstructionSetFnTest *set_fn_test_instruction)
{
IrInstruction *fn_value = set_fn_test_instruction->fn_value->other;
FnTableEntry *fn_entry = ir_resolve_fn(ira, fn_value);
if (!fn_entry)
return ira->codegen->builtin_types.entry_invalid;
if (!fn_entry->is_test) {
fn_entry->is_test = true;
ira->codegen->test_fn_count += 1;
}
ir_build_const_from(ira, &set_fn_test_instruction->base, false);
return ira->codegen->builtin_types.entry_void;
}
static TypeTableEntry *ir_analyze_instruction_set_fn_visible(IrAnalyze *ira,
IrInstructionSetFnVisible *set_fn_visible_instruction)
{
IrInstruction *fn_value = set_fn_visible_instruction->fn_value->other;
IrInstruction *is_visible_value = set_fn_visible_instruction->is_visible->other;
FnTableEntry *fn_entry = ir_resolve_fn(ira, fn_value);
if (!fn_entry)
return ira->codegen->builtin_types.entry_invalid;
bool want_export;
if (!ir_resolve_bool(ira, is_visible_value, &want_export))
return ira->codegen->builtin_types.entry_invalid;
AstNode *source_node = set_fn_visible_instruction->base.source_node;
if (fn_entry->fn_export_set_node) {
ErrorMsg *msg = ir_add_error_node(ira, source_node,
buf_sprintf("function visibility set twice"));
add_error_note(ira->codegen, msg, fn_entry->fn_export_set_node, buf_sprintf("first set here"));
return ira->codegen->builtin_types.entry_invalid;
}
fn_entry->fn_export_set_node = source_node;
AstNodeFnProto *fn_proto = &fn_entry->proto_node->data.fn_proto;
if (fn_proto->visib_mod != VisibModExport) {
ErrorMsg *msg = ir_add_error_node(ira, source_node,
buf_sprintf("function must be marked export to set function visibility"));
add_error_note(ira->codegen, msg, fn_entry->proto_node, buf_sprintf("function declared here"));
return ira->codegen->builtin_types.entry_invalid;
}
fn_entry->internal_linkage = !want_export;
ir_build_const_from(ira, &set_fn_visible_instruction->base, false);
return ira->codegen->builtin_types.entry_void;
}
static TypeTableEntry *ir_analyze_instruction_set_debug_safety(IrAnalyze *ira,
IrInstructionSetDebugSafety *set_debug_safety_instruction)
{
IrInstruction *target_instruction = set_debug_safety_instruction->scope_value->other;
TypeTableEntry *target_type = target_instruction->value.type;
if (target_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *target_val = ir_resolve_const(ira, target_instruction, UndefBad);
if (!target_val)
return ira->codegen->builtin_types.entry_invalid;
bool *safety_off_ptr;
AstNode **safety_set_node_ptr;
if (target_type->id == TypeTableEntryIdBlock) {
ScopeBlock *block_scope = (ScopeBlock *)target_val->data.x_block;
safety_off_ptr = &block_scope->safety_off;
safety_set_node_ptr = &block_scope->safety_set_node;
} else if (target_type->id == TypeTableEntryIdFn) {
FnTableEntry *target_fn = target_val->data.x_fn;
assert(target_fn->def_scope);
safety_off_ptr = &target_fn->def_scope->safety_off;
safety_set_node_ptr = &target_fn->def_scope->safety_set_node;
} else if (target_type->id == TypeTableEntryIdMetaType) {
ScopeDecls *decls_scope;
TypeTableEntry *type_arg = target_val->data.x_type;
if (type_arg->id == TypeTableEntryIdStruct) {
decls_scope = type_arg->data.structure.decls_scope;
} else if (type_arg->id == TypeTableEntryIdEnum) {
decls_scope = type_arg->data.enumeration.decls_scope;
} else if (type_arg->id == TypeTableEntryIdUnion) {
decls_scope = type_arg->data.unionation.decls_scope;
} else {
ir_add_error_node(ira, target_instruction->source_node,
buf_sprintf("expected scope reference, found type '%s'", buf_ptr(&type_arg->name)));
return ira->codegen->builtin_types.entry_invalid;
}
safety_off_ptr = &decls_scope->safety_off;
safety_set_node_ptr = &decls_scope->safety_set_node;
} else {
ir_add_error_node(ira, target_instruction->source_node,
buf_sprintf("expected scope reference, found type '%s'", buf_ptr(&target_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
IrInstruction *debug_safety_on_value = set_debug_safety_instruction->debug_safety_on->other;
bool want_debug_safety;
if (!ir_resolve_bool(ira, debug_safety_on_value, &want_debug_safety))
return ira->codegen->builtin_types.entry_invalid;
AstNode *source_node = set_debug_safety_instruction->base.source_node;
if (*safety_set_node_ptr) {
ErrorMsg *msg = ir_add_error_node(ira, source_node,
buf_sprintf("function test attribute set twice"));
add_error_note(ira->codegen, msg, *safety_set_node_ptr, buf_sprintf("first set here"));
return ira->codegen->builtin_types.entry_invalid;
}
*safety_set_node_ptr = source_node;
*safety_off_ptr = !want_debug_safety;
ir_build_const_from(ira, &set_debug_safety_instruction->base, false);
return ira->codegen->builtin_types.entry_void;
}
static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,
IrInstructionSliceType *slice_type_instruction)
{
IrInstruction *child_type = slice_type_instruction->child_type->other;
if (child_type->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
bool is_const = slice_type_instruction->is_const;
TypeTableEntry *resolved_child_type = ir_resolve_type(ira, child_type);
TypeTableEntry *canon_child_type = get_underlying_type(resolved_child_type);
switch (canon_child_type->id) {
case TypeTableEntryIdTypeDecl:
zig_unreachable();
case TypeTableEntryIdInvalid:
return ira->codegen->builtin_types.entry_invalid;
case TypeTableEntryIdVar:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdBlock:
case TypeTableEntryIdArgTuple:
ir_add_error_node(ira, slice_type_instruction->base.source_node,
buf_sprintf("slice of type '%s' not allowed", buf_ptr(&resolved_child_type->name)));
// TODO if this is a typedecl, add error note showing the declaration of the type decl
return ira->codegen->builtin_types.entry_invalid;
case TypeTableEntryIdMetaType:
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdPointer:
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdPureError:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdEnumTag:
{
type_ensure_zero_bits_known(ira->codegen, resolved_child_type);
TypeTableEntry *result_type = get_slice_type(ira->codegen, resolved_child_type, is_const);
ConstExprValue *out_val = ir_build_const_from(ira, &slice_type_instruction->base,
child_type->value.depends_on_compile_var);
out_val->data.x_type = result_type;
return ira->codegen->builtin_types.entry_type;
}
}
zig_unreachable();
}
static TypeTableEntry *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAsm *asm_instruction) {
assert(asm_instruction->base.source_node->type == NodeTypeAsmExpr);
if (!ir_emit_global_runtime_side_effect(ira, &asm_instruction->base))
return ira->codegen->builtin_types.entry_invalid;
// TODO validate the output types and variable types
AstNodeAsmExpr *asm_expr = &asm_instruction->base.source_node->data.asm_expr;
IrInstruction **input_list = allocate<IrInstruction *>(asm_expr->input_list.length);
IrInstruction **output_types = allocate<IrInstruction *>(asm_expr->output_list.length);
TypeTableEntry *return_type = ira->codegen->builtin_types.entry_void;
for (size_t i = 0; i < asm_expr->output_list.length; i += 1) {
AsmOutput *asm_output = asm_expr->output_list.at(i);
if (asm_output->return_type) {
output_types[i] = asm_instruction->output_types[i]->other;
return_type = ir_resolve_type(ira, output_types[i]);
if (return_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
}
}
for (size_t i = 0; i < asm_expr->input_list.length; i += 1) {
input_list[i] = asm_instruction->input_list[i]->other;
if (input_list[i]->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
}
ir_build_asm_from(&ira->new_irb, &asm_instruction->base, input_list, output_types,
asm_instruction->output_vars, asm_instruction->return_count, asm_instruction->has_side_effects);
return return_type;
}
static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira,
IrInstructionArrayType *array_type_instruction)
{
IrInstruction *size_value = array_type_instruction->size->other;
uint64_t size;
if (!ir_resolve_usize(ira, size_value, &size))
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *child_type_value = array_type_instruction->child_type->other;
TypeTableEntry *child_type = ir_resolve_type(ira, child_type_value);
TypeTableEntry *canon_child_type = get_underlying_type(child_type);
switch (canon_child_type->id) {
case TypeTableEntryIdTypeDecl:
zig_unreachable();
case TypeTableEntryIdInvalid:
return ira->codegen->builtin_types.entry_invalid;
case TypeTableEntryIdVar:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdBlock:
case TypeTableEntryIdArgTuple:
ir_add_error_node(ira, array_type_instruction->base.source_node,
buf_sprintf("array of type '%s' not allowed", buf_ptr(&child_type->name)));
// TODO if this is a typedecl, add error note showing the declaration of the type decl
return ira->codegen->builtin_types.entry_invalid;
case TypeTableEntryIdMetaType:
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdPointer:
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdPureError:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdEnumTag:
{
TypeTableEntry *result_type = get_array_type(ira->codegen, child_type, size);
bool depends_on_compile_var = child_type_value->value.depends_on_compile_var ||
size_value->value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &array_type_instruction->base,
depends_on_compile_var);
out_val->data.x_type = result_type;
return ira->codegen->builtin_types.entry_type;
}
}
zig_unreachable();
}
static TypeTableEntry *ir_analyze_instruction_compile_var(IrAnalyze *ira,
IrInstructionCompileVar *compile_var_instruction)
{
IrInstruction *name_value = compile_var_instruction->name->other;
Buf *var_name = ir_resolve_str(ira, name_value);
if (!var_name)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *out_val = ir_build_const_from(ira, &compile_var_instruction->base, true);
if (buf_eql_str(var_name, "is_big_endian")) {
out_val->data.x_bool = ira->codegen->is_big_endian;
return ira->codegen->builtin_types.entry_bool;
} else if (buf_eql_str(var_name, "is_release")) {
out_val->data.x_bool = ira->codegen->is_release_build;
return ira->codegen->builtin_types.entry_bool;
} else if (buf_eql_str(var_name, "is_test")) {
out_val->data.x_bool = ira->codegen->is_test_build;
return ira->codegen->builtin_types.entry_bool;
} else if (buf_eql_str(var_name, "os")) {
out_val->data.x_enum.tag = ira->codegen->target_os_index;
return ira->codegen->builtin_types.entry_os_enum;
} else if (buf_eql_str(var_name, "arch")) {
out_val->data.x_enum.tag = ira->codegen->target_arch_index;
return ira->codegen->builtin_types.entry_arch_enum;
} else if (buf_eql_str(var_name, "environ")) {
out_val->data.x_enum.tag = ira->codegen->target_environ_index;
return ira->codegen->builtin_types.entry_environ_enum;
} else if (buf_eql_str(var_name, "object_format")) {
out_val->data.x_enum.tag = ira->codegen->target_oformat_index;
return ira->codegen->builtin_types.entry_oformat_enum;
} else {
ir_add_error_node(ira, name_value->source_node,
buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(var_name)));
return ira->codegen->builtin_types.entry_invalid;
}
zig_unreachable();
}
static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira,
IrInstructionSizeOf *size_of_instruction)
{
IrInstruction *type_value = size_of_instruction->type_value->other;
TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
TypeTableEntry *canon_type_entry = get_underlying_type(type_entry);
switch (canon_type_entry->id) {
case TypeTableEntryIdInvalid:
return ira->codegen->builtin_types.entry_invalid;
case TypeTableEntryIdTypeDecl:
zig_unreachable();
case TypeTableEntryIdVar:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdBlock:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdMetaType:
case TypeTableEntryIdFn:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdArgTuple:
ir_add_error_node(ira, size_of_instruction->base.source_node,
buf_sprintf("no size available for type '%s'", buf_ptr(&type_entry->name)));
// TODO if this is a typedecl, add error note showing the declaration of the type decl
return ira->codegen->builtin_types.entry_invalid;
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdPointer:
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdPureError:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdEnumTag:
{
uint64_t size_in_bytes = type_size(ira->codegen, type_entry);
ConstExprValue *out_val = ir_build_const_from(ira, &size_of_instruction->base,
type_entry->size_depends_on_compile_var);
bignum_init_unsigned(&out_val->data.x_bignum, size_in_bytes);
return ira->codegen->builtin_types.entry_num_lit_int;
}
}
zig_unreachable();
}
static TypeTableEntry *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrInstructionTestNonNull *instruction) {
IrInstruction *value = instruction->value->other;
if (value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *type_entry = value->value.type;
if (type_entry->id == TypeTableEntryIdMaybe) {
if (instr_is_comptime(value)) {
ConstExprValue *maybe_val = ir_resolve_const(ira, value, UndefBad);
if (!maybe_val)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base,
maybe_val->depends_on_compile_var);
out_val->data.x_bool = (maybe_val->data.x_maybe != nullptr);
return ira->codegen->builtin_types.entry_bool;
}
ir_build_test_nonnull_from(&ira->new_irb, &instruction->base, value);
return ira->codegen->builtin_types.entry_bool;
} else if (type_entry->id == TypeTableEntryIdNullLit) {
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, false);
out_val->data.x_bool = false;
return ira->codegen->builtin_types.entry_bool;
} else {
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, false);
out_val->data.x_bool = true;
return ira->codegen->builtin_types.entry_bool;
}
}
static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira,
IrInstructionUnwrapMaybe *unwrap_maybe_instruction)
{
IrInstruction *value = unwrap_maybe_instruction->value->other;
if (value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
// This will be a pointer type because test null IR instruction operates on a pointer to a thing.
TypeTableEntry *ptr_type = value->value.type;
assert(ptr_type->id == TypeTableEntryIdPointer);
TypeTableEntry *type_entry = ptr_type->data.pointer.child_type;
// TODO handle typedef
if (type_entry->id == TypeTableEntryIdInvalid) {
return ira->codegen->builtin_types.entry_invalid;
} else if (type_entry->id != TypeTableEntryIdMaybe) {
ir_add_error_node(ira, unwrap_maybe_instruction->value->source_node,
buf_sprintf("expected nullable type, found '%s'", buf_ptr(&type_entry->name)));
return ira->codegen->builtin_types.entry_invalid;
}
TypeTableEntry *child_type = type_entry->data.maybe.child_type;
TypeTableEntry *result_type = get_pointer_to_type(ira->codegen, child_type, false);
if (instr_is_comptime(value)) {
ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
if (!val)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *maybe_val = val->data.x_ptr.base_ptr;
assert(val->data.x_ptr.index == SIZE_MAX);
if (maybe_val->special != ConstValSpecialRuntime) {
if (!maybe_val->data.x_maybe) {
ir_add_error(ira, &unwrap_maybe_instruction->base, buf_sprintf("unable to unwrap null"));
return ira->codegen->builtin_types.entry_invalid;
}
bool depends_on_compile_var = maybe_val->depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &unwrap_maybe_instruction->base,
depends_on_compile_var);
out_val->data.x_ptr.base_ptr = maybe_val->data.x_maybe;
out_val->data.x_ptr.index = SIZE_MAX;
return result_type;
}
}
ir_build_unwrap_maybe_from(&ira->new_irb, &unwrap_maybe_instruction->base, value,
unwrap_maybe_instruction->safety_check_on);
return result_type;
}
static TypeTableEntry *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstructionCtz *ctz_instruction) {
IrInstruction *value = ctz_instruction->value->other;
if (value->value.type->id == TypeTableEntryIdInvalid) {
return ira->codegen->builtin_types.entry_invalid;
} else if (value->value.type->id == TypeTableEntryIdInt) {
if (value->value.special != ConstValSpecialRuntime) {
uint32_t result = bignum_ctz(&value->value.data.x_bignum,
value->value.type->data.integral.bit_count);
bool depends_on_compile_var = value->value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &ctz_instruction->base,
depends_on_compile_var);
bignum_init_unsigned(&out_val->data.x_bignum, result);
return value->value.type;
}
ir_build_ctz_from(&ira->new_irb, &ctz_instruction->base, value);
return value->value.type;
} else {
ir_add_error_node(ira, ctz_instruction->base.source_node,
buf_sprintf("expected integer type, found '%s'", buf_ptr(&value->value.type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
}
static TypeTableEntry *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstructionClz *clz_instruction) {
IrInstruction *value = clz_instruction->value->other;
if (value->value.type->id == TypeTableEntryIdInvalid) {
return ira->codegen->builtin_types.entry_invalid;
} else if (value->value.type->id == TypeTableEntryIdInt) {
if (value->value.special != ConstValSpecialRuntime) {
uint32_t result = bignum_clz(&value->value.data.x_bignum,
value->value.type->data.integral.bit_count);
bool depends_on_compile_var = value->value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &clz_instruction->base,
depends_on_compile_var);
bignum_init_unsigned(&out_val->data.x_bignum, result);
return value->value.type;
}
ir_build_clz_from(&ira->new_irb, &clz_instruction->base, value);
return value->value.type;
} else {
ir_add_error_node(ira, clz_instruction->base.source_node,
buf_sprintf("expected integer type, found '%s'", buf_ptr(&value->value.type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
}
static IrInstruction *ir_analyze_enum_tag(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value) {
if (value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->invalid_instruction;
if (value->value.type->id != TypeTableEntryIdEnum) {
ir_add_error(ira, source_instr,
buf_sprintf("expected enum type, found '%s'", buf_ptr(&value->value.type->name)));
return ira->codegen->invalid_instruction;
}
if (instr_is_comptime(value)) {
ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
source_instr->scope, source_instr->source_node);
const_instruction->base.value.type = value->value.type->data.enumeration.tag_type;
const_instruction->base.value.special = ConstValSpecialStatic;
const_instruction->base.value.depends_on_compile_var = val->depends_on_compile_var;
bignum_init_unsigned(&const_instruction->base.value.data.x_bignum, val->data.x_enum.tag);
return &const_instruction->base;
}
zig_panic("TODO runtime enum tag instruction");
}
static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira,
IrInstructionSwitchBr *switch_br_instruction)
{
IrInstruction *target_value = switch_br_instruction->target_value->other;
if (target_value->value.type->id == TypeTableEntryIdInvalid)
return ir_unreach_error(ira);
size_t case_count = switch_br_instruction->case_count;
bool is_comptime;
if (!ir_resolve_comptime(ira, switch_br_instruction->is_comptime->other, &is_comptime))
return ira->codegen->builtin_types.entry_invalid;
if (is_comptime || instr_is_comptime(target_value)) {
ConstExprValue *target_val = ir_resolve_const(ira, target_value, UndefBad);
if (!target_val)
return ir_unreach_error(ira);
IrBasicBlock *old_dest_block = switch_br_instruction->else_block;
for (size_t i = 0; i < case_count; i += 1) {
IrInstructionSwitchBrCase *old_case = &switch_br_instruction->cases[i];
IrInstruction *case_value = old_case->value->other;
if (case_value->value.type->id == TypeTableEntryIdInvalid)
return ir_unreach_error(ira);
if (case_value->value.type->id == TypeTableEntryIdEnum) {
case_value = ir_analyze_enum_tag(ira, &switch_br_instruction->base, case_value);
if (case_value->value.type->id == TypeTableEntryIdInvalid)
return ir_unreach_error(ira);
}
IrInstruction *casted_case_value = ir_implicit_cast(ira, case_value, target_value->value.type);
if (casted_case_value->value.type->id == TypeTableEntryIdInvalid)
return ir_unreach_error(ira);
ConstExprValue *case_val = ir_resolve_const(ira, casted_case_value, UndefBad);
if (!case_val)
return ir_unreach_error(ira);
if (const_values_equal(target_val, case_val)) {
old_dest_block = old_case->block;
break;
}
}
if (is_comptime || old_dest_block->ref_count == 1) {
return ir_inline_bb(ira, &switch_br_instruction->base, old_dest_block);
} else {
IrBasicBlock *new_dest_block = ir_get_new_bb(ira, old_dest_block, &switch_br_instruction->base);
ir_build_br_from(&ira->new_irb, &switch_br_instruction->base, new_dest_block);
return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
}
}
IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(case_count);
for (size_t i = 0; i < case_count; i += 1) {
IrInstructionSwitchBrCase *old_case = &switch_br_instruction->cases[i];
IrInstructionSwitchBrCase *new_case = &cases[i];
new_case->block = ir_get_new_bb(ira, old_case->block, &switch_br_instruction->base);
new_case->value = ira->codegen->invalid_instruction;
// Calling ir_get_new_bb set the ref_instruction on the new basic block.
// However a switch br may branch to the same basic block which would trigger an
// incorrect re-generation of the block. So we set it to null here and assign
// it back after the loop.
new_case->block->ref_instruction = nullptr;
IrInstruction *old_value = old_case->value;
IrInstruction *new_value = old_value->other;
if (new_value->value.type->id == TypeTableEntryIdInvalid)
continue;
if (new_value->value.type->id == TypeTableEntryIdEnum) {
new_value = ir_analyze_enum_tag(ira, &switch_br_instruction->base, new_value);
if (new_value->value.type->id == TypeTableEntryIdInvalid)
continue;
}
IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, target_value->value.type);
if (casted_new_value->value.type->id == TypeTableEntryIdInvalid)
continue;
if (!ir_resolve_const(ira, casted_new_value, UndefBad))
continue;
new_case->value = casted_new_value;
}
for (size_t i = 0; i < case_count; i += 1) {
IrInstructionSwitchBrCase *new_case = &cases[i];
new_case->block->ref_instruction = &switch_br_instruction->base;
}
IrBasicBlock *new_else_block = ir_get_new_bb(ira, switch_br_instruction->else_block, &switch_br_instruction->base);
ir_build_switch_br_from(&ira->new_irb, &switch_br_instruction->base,
target_value, new_else_block, case_count, cases, nullptr);
return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
}
static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
IrInstructionSwitchTarget *switch_target_instruction)
{
IrInstruction *target_value_ptr = switch_target_instruction->target_value_ptr->other;
if (target_value_ptr->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
assert(target_value_ptr->value.type->id == TypeTableEntryIdPointer);
TypeTableEntry *target_type = target_value_ptr->value.type->data.pointer.child_type;
bool depends_on_compile_var = target_value_ptr->value.depends_on_compile_var;
ConstExprValue *pointee_val = nullptr;
if (target_value_ptr->value.special != ConstValSpecialRuntime) {
pointee_val = const_ptr_pointee(&target_value_ptr->value);
if (pointee_val->special == ConstValSpecialRuntime)
pointee_val = nullptr;
}
TypeTableEntry *canon_target_type = get_underlying_type(target_type);
ensure_complete_type(ira->codegen, target_type);
switch (canon_target_type->id) {
case TypeTableEntryIdInvalid:
case TypeTableEntryIdVar:
case TypeTableEntryIdTypeDecl:
zig_unreachable();
case TypeTableEntryIdMetaType:
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdPointer:
case TypeTableEntryIdFn:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdPureError:
if (pointee_val) {
ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base,
depends_on_compile_var);
*out_val = *pointee_val;
out_val->type = target_type;
return target_type;
}
ir_build_load_ptr_from(&ira->new_irb, &switch_target_instruction->base, target_value_ptr);
return target_type;
case TypeTableEntryIdEnum:
{
TypeTableEntry *tag_type = target_type->data.enumeration.tag_type;
if (pointee_val) {
ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base,
depends_on_compile_var);
bignum_init_unsigned(&out_val->data.x_bignum, pointee_val->data.x_enum.tag);
return tag_type;
}
IrInstruction *enum_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope,
switch_target_instruction->base.source_node, target_value_ptr);
enum_value->value.type = target_type;
ir_build_enum_tag_from(&ira->new_irb, &switch_target_instruction->base, enum_value);
return tag_type;
}
case TypeTableEntryIdErrorUnion:
// see https://github.com/andrewrk/zig/issues/83
zig_panic("TODO switch on error union");
case TypeTableEntryIdEnumTag:
zig_panic("TODO switch on enum tag type");
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdUnion:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdArgTuple:
ir_add_error(ira, &switch_target_instruction->base,
buf_sprintf("invalid switch target type '%s'", buf_ptr(&target_type->name)));
// TODO if this is a typedecl, add error note showing the declaration of the type decl
return ira->codegen->builtin_types.entry_invalid;
}
zig_unreachable();
}
static TypeTableEntry *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstructionSwitchVar *instruction) {
IrInstruction *target_value_ptr = instruction->target_value_ptr->other;
if (target_value_ptr->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *prong_value = instruction->prong_value->other;
if (prong_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
assert(target_value_ptr->value.type->id == TypeTableEntryIdPointer);
TypeTableEntry *target_type = target_value_ptr->value.type->data.pointer.child_type;
if (target_type->id == TypeTableEntryIdEnum) {
ConstExprValue *prong_val = ir_resolve_const(ira, prong_value, UndefBad);
if (!prong_val)
return ira->codegen->builtin_types.entry_invalid;
TypeEnumField *field = &target_type->data.enumeration.fields[prong_val->data.x_bignum.data.x_uint];
if (prong_value->value.type->id == TypeTableEntryIdEnumTag) {
field = &target_type->data.enumeration.fields[prong_val->data.x_bignum.data.x_uint];
} else if (prong_value->value.type->id == TypeTableEntryIdEnum) {
field = &target_type->data.enumeration.fields[prong_val->data.x_enum.tag];
} else {
zig_unreachable();
}
if (instr_is_comptime(target_value_ptr)) {
ConstExprValue *target_val_ptr = ir_resolve_const(ira, target_value_ptr, UndefBad);
if (!target_value_ptr)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *pointee_val = const_ptr_pointee(target_val_ptr);
if (pointee_val->type->id == TypeTableEntryIdEnum) {
bool depends_on_compile_var = target_value_ptr->value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
out_val->data.x_ptr.base_ptr = pointee_val->data.x_enum.payload;
out_val->data.x_ptr.index = SIZE_MAX;
return get_pointer_to_type(ira->codegen, pointee_val->type, target_value_ptr->value.type->data.pointer.is_const);
} else {
zig_panic("TODO comptime switch var");
}
}
ir_build_enum_field_ptr_from(&ira->new_irb, &instruction->base, target_value_ptr, field);
return get_pointer_to_type(ira->codegen, field->type_entry,
target_value_ptr->value.type->data.pointer.is_const);
} else {
ErrorMsg *msg = ir_add_error(ira, &instruction->base,
buf_sprintf("switch on type '%s' provides no expression parameter", buf_ptr(&target_type->name)));
ir_add_typedef_err_note(ira, msg, target_type);
return ira->codegen->builtin_types.entry_invalid;
}
}
static TypeTableEntry *ir_analyze_instruction_enum_tag(IrAnalyze *ira, IrInstructionEnumTag *enum_tag_instruction) {
IrInstruction *value = enum_tag_instruction->value->other;
IrInstruction *new_instruction = ir_analyze_enum_tag(ira, &enum_tag_instruction->base, value);
ir_link_new_instruction(new_instruction, &enum_tag_instruction->base);
return new_instruction->value.type;
}
static TypeTableEntry *ir_analyze_instruction_generated_code(IrAnalyze *ira, IrInstructionGeneratedCode *instruction) {
IrInstruction *value = instruction->value->other;
if (value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
if (instr_is_comptime(value)) {
ConstExprValue *val = ir_resolve_const(ira, value, UndefOk);
if (!val)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, false);
*out_val = *val;
out_val->depends_on_compile_var = true;
return value->value.type;
}
instruction->base.other = value;
return value->value.type;
}
static TypeTableEntry *ir_analyze_instruction_import(IrAnalyze *ira, IrInstructionImport *import_instruction) {
IrInstruction *name_value = import_instruction->name->other;
Buf *import_target_str = ir_resolve_str(ira, name_value);
if (!import_target_str)
return ira->codegen->builtin_types.entry_invalid;
bool depends_on_compile_var = name_value->value.depends_on_compile_var;
AstNode *source_node = import_instruction->base.source_node;
ImportTableEntry *import = source_node->owner;
Buf *import_target_path;
Buf *search_dir;
assert(import->package);
PackageTableEntry *target_package;
auto package_entry = import->package->package_table.maybe_get(import_target_str);
if (package_entry) {
target_package = package_entry->value;
import_target_path = &target_package->root_src_path;
search_dir = &target_package->root_src_dir;
} else {
// try it as a filename
target_package = import->package;
import_target_path = import_target_str;
search_dir = &import->package->root_src_dir;
}
Buf full_path = BUF_INIT;
os_path_join(search_dir, import_target_path, &full_path);
Buf *import_code = buf_alloc();
Buf *abs_full_path = buf_alloc();
int err;
if ((err = os_path_real(&full_path, abs_full_path))) {
if (err == ErrorFileNotFound) {
ir_add_error_node(ira, source_node,
buf_sprintf("unable to find '%s'", buf_ptr(import_target_path)));
return ira->codegen->builtin_types.entry_invalid;
} else {
ira->codegen->error_during_imports = true;
ir_add_error_node(ira, source_node,
buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err)));
return ira->codegen->builtin_types.entry_invalid;
}
}
auto import_entry = ira->codegen->import_table.maybe_get(abs_full_path);
if (import_entry) {
ConstExprValue *out_val = ir_build_const_from(ira, &import_instruction->base, depends_on_compile_var);
out_val->data.x_import = import_entry->value;
return ira->codegen->builtin_types.entry_namespace;
}
if ((err = os_fetch_file_path(abs_full_path, import_code))) {
if (err == ErrorFileNotFound) {
ir_add_error_node(ira, source_node,
buf_sprintf("unable to find '%s'", buf_ptr(import_target_path)));
return ira->codegen->builtin_types.entry_invalid;
} else {
ir_add_error_node(ira, source_node,
buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err)));
return ira->codegen->builtin_types.entry_invalid;
}
}
ImportTableEntry *target_import = add_source_file(ira->codegen, target_package,
abs_full_path, search_dir, import_target_path, import_code);
scan_decls(ira->codegen, target_import->decls_scope, target_import->root);
ConstExprValue *out_val = ir_build_const_from(ira, &import_instruction->base, depends_on_compile_var);
out_val->data.x_import = target_import;
return ira->codegen->builtin_types.entry_namespace;
}
static TypeTableEntry *ir_analyze_instruction_array_len(IrAnalyze *ira,
IrInstructionArrayLen *array_len_instruction)
{
IrInstruction *array_value = array_len_instruction->array_value->other;
TypeTableEntry *canon_type = get_underlying_type(array_value->value.type);
if (canon_type->id == TypeTableEntryIdInvalid) {
return ira->codegen->builtin_types.entry_invalid;
} else if (canon_type->id == TypeTableEntryIdArray) {
bool depends_on_compile_var = array_value->value.depends_on_compile_var;
return ir_analyze_const_usize(ira, &array_len_instruction->base,
canon_type->data.array.len, depends_on_compile_var);
} else if (is_slice(canon_type)) {
if (array_value->value.special != ConstValSpecialRuntime) {
ConstExprValue *len_val = &array_value->value.data.x_struct.fields[slice_len_index];
if (len_val->special != ConstValSpecialRuntime) {
bool depends_on_compile_var = len_val->depends_on_compile_var;
return ir_analyze_const_usize(ira, &array_len_instruction->base,
len_val->data.x_bignum.data.x_uint, depends_on_compile_var);
}
}
TypeStructField *field = &canon_type->data.structure.fields[slice_len_index];
IrInstruction *len_ptr = ir_build_struct_field_ptr(&ira->new_irb, array_len_instruction->base.scope,
array_len_instruction->base.source_node, array_value, field);
len_ptr->value.type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_usize, true);
ir_build_load_ptr_from(&ira->new_irb, &array_len_instruction->base, len_ptr);
return ira->codegen->builtin_types.entry_usize;
} else {
ir_add_error_node(ira, array_len_instruction->base.source_node,
buf_sprintf("type '%s' has no field 'len'", buf_ptr(&array_value->value.type->name)));
// TODO if this is a typedecl, add error note showing the declaration of the type decl
return ira->codegen->builtin_types.entry_invalid;
}
}
static TypeTableEntry *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionRef *ref_instruction) {
IrInstruction *value = ref_instruction->value->other;
return ir_analyze_ref(ira, &ref_instruction->base, value, ref_instruction->is_const);
}
static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruction *instruction,
TypeTableEntry *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields,
bool depends_on_compile_var)
{
if (container_type->id != TypeTableEntryIdStruct || is_slice(container_type)) {
ir_add_error(ira, instruction,
buf_sprintf("type '%s' does not support struct initialization syntax",
buf_ptr(&container_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
if (!type_is_complete(container_type))
resolve_container_type(ira->codegen, container_type);
size_t actual_field_count = container_type->data.structure.src_field_count;
IrInstruction *first_non_const_instruction = nullptr;
AstNode **field_assign_nodes = allocate<AstNode *>(actual_field_count);
IrInstructionStructInitField *new_fields = allocate<IrInstructionStructInitField>(actual_field_count);
bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope);
ConstExprValue const_val = {};
const_val.special = ConstValSpecialStatic;
const_val.type = container_type;
const_val.depends_on_compile_var = depends_on_compile_var;
const_val.data.x_struct.fields = allocate<ConstExprValue>(actual_field_count);
for (size_t i = 0; i < instr_field_count; i += 1) {
IrInstructionContainerInitFieldsField *field = &fields[i];
IrInstruction *field_value = field->value->other;
if (field_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
TypeStructField *type_field = find_struct_type_field(container_type, field->name);
if (!type_field) {
ir_add_error_node(ira, field->source_node,
buf_sprintf("no member named '%s' in '%s'",
buf_ptr(field->name), buf_ptr(&container_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
if (type_field->type_entry->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *casted_field_value = ir_implicit_cast(ira, field_value, type_field->type_entry);
if (casted_field_value == ira->codegen->invalid_instruction)
return ira->codegen->builtin_types.entry_invalid;
size_t field_index = type_field->src_index;
AstNode *existing_assign_node = field_assign_nodes[field_index];
if (existing_assign_node) {
ErrorMsg *msg = ir_add_error_node(ira, field->source_node, buf_sprintf("duplicate field"));
add_error_note(ira->codegen, msg, existing_assign_node, buf_sprintf("other field here"));
continue;
}
field_assign_nodes[field_index] = field->source_node;
new_fields[field_index].value = casted_field_value;
new_fields[field_index].type_struct_field = type_field;
if (const_val.special == ConstValSpecialStatic) {
if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime) {
ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk);
if (!field_val)
return ira->codegen->builtin_types.entry_invalid;
const_val.data.x_struct.fields[field_index] = *field_val;
const_val.depends_on_compile_var = const_val.depends_on_compile_var || field_val->depends_on_compile_var;
} else {
first_non_const_instruction = casted_field_value;
const_val.special = ConstValSpecialRuntime;
}
}
}
bool any_missing = false;
for (size_t i = 0; i < actual_field_count; i += 1) {
if (!field_assign_nodes[i]) {
ir_add_error_node(ira, instruction->source_node,
buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i].name)));
any_missing = true;
}
}
if (any_missing)
return ira->codegen->builtin_types.entry_invalid;
if (const_val.special == ConstValSpecialStatic) {
ConstExprValue *out_val = ir_build_const_from(ira, instruction, const_val.depends_on_compile_var);
*out_val = const_val;
return container_type;
}
if (is_comptime) {
ir_add_error_node(ira, first_non_const_instruction->source_node,
buf_sprintf("unable to evaluate constant expression"));
return ira->codegen->builtin_types.entry_invalid;
}
IrInstruction *new_instruction = ir_build_struct_init_from(&ira->new_irb, instruction,
container_type, actual_field_count, new_fields);
ir_add_alloca(ira, new_instruction, container_type);
return container_type;
}
static TypeTableEntry *ir_analyze_instruction_container_init_list(IrAnalyze *ira,
IrInstructionContainerInitList *instruction)
{
IrInstruction *container_type_value = instruction->container_type->other;
if (container_type_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
size_t elem_count = instruction->item_count;
if (container_type_value->value.type->id == TypeTableEntryIdMetaType) {
TypeTableEntry *container_type = ir_resolve_type(ira, container_type_value);
if (container_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
bool depends_on_compile_var = container_type_value->value.depends_on_compile_var;
if (container_type->id == TypeTableEntryIdStruct && !is_slice(container_type) && elem_count == 0) {
return ir_analyze_container_init_fields(ira, &instruction->base, container_type,
0, nullptr, depends_on_compile_var);
} else if (is_slice(container_type)) {
TypeTableEntry *pointer_type = container_type->data.structure.fields[slice_ptr_index].type_entry;
assert(pointer_type->id == TypeTableEntryIdPointer);
TypeTableEntry *child_type = pointer_type->data.pointer.child_type;
TypeTableEntry *fixed_size_array_type = get_array_type(ira->codegen, child_type, elem_count);
ConstExprValue const_val = {};
const_val.special = ConstValSpecialStatic;
const_val.type = fixed_size_array_type;
const_val.depends_on_compile_var = depends_on_compile_var;
const_val.data.x_array.elements = allocate<ConstExprValue>(elem_count);
const_val.data.x_array.size = elem_count;
bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope);
IrInstruction **new_items = allocate<IrInstruction *>(elem_count);
IrInstruction *first_non_const_instruction = nullptr;
for (size_t i = 0; i < elem_count; i += 1) {
IrInstruction *arg_value = instruction->items[i]->other;
if (arg_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *casted_arg = ir_implicit_cast(ira, arg_value, child_type);
if (casted_arg == ira->codegen->invalid_instruction)
return ira->codegen->builtin_types.entry_invalid;
new_items[i] = casted_arg;
if (const_val.special == ConstValSpecialStatic) {
if (is_comptime || casted_arg->value.special != ConstValSpecialRuntime) {
ConstExprValue *elem_val = ir_resolve_const(ira, casted_arg, UndefBad);
if (!elem_val)
return ira->codegen->builtin_types.entry_invalid;
const_val.data.x_array.elements[i] = *elem_val;
const_val.depends_on_compile_var = const_val.depends_on_compile_var || elem_val->depends_on_compile_var;
} else {
first_non_const_instruction = casted_arg;
const_val.special = ConstValSpecialRuntime;
}
}
}
if (const_val.special == ConstValSpecialStatic) {
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, const_val.depends_on_compile_var);
*out_val = const_val;
for (size_t i = 0; i < elem_count; i += 1) {
ConstExprValue *elem_val = &out_val->data.x_array.elements[i];
if (elem_val->type->id == TypeTableEntryIdArray) {
elem_val->data.x_array.parent_array = out_val;
elem_val->data.x_array.parent_array_index = i;
}
}
return fixed_size_array_type;
}
if (is_comptime) {
ir_add_error_node(ira, first_non_const_instruction->source_node,
buf_sprintf("unable to evaluate constant expression"));
return ira->codegen->builtin_types.entry_invalid;
}
IrInstruction *new_instruction = ir_build_container_init_list_from(&ira->new_irb, &instruction->base,
container_type_value, elem_count, new_items);
ir_add_alloca(ira, new_instruction, fixed_size_array_type);
return fixed_size_array_type;
} else if (container_type->id == TypeTableEntryIdArray) {
// same as slice init but we make a compile error if the length is wrong
zig_panic("TODO array container init");
} else if (container_type->id == TypeTableEntryIdVoid) {
if (elem_count != 0) {
ir_add_error_node(ira, instruction->base.source_node,
buf_sprintf("void expression expects no arguments"));
return ira->codegen->builtin_types.entry_invalid;
}
return ir_analyze_void(ira, &instruction->base);
} else {
ir_add_error_node(ira, instruction->base.source_node,
buf_sprintf("type '%s' does not support array initialization",
buf_ptr(&container_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
} else if (container_type_value->value.type->id == TypeTableEntryIdEnumTag) {
if (elem_count != 1) {
ir_add_error(ira, &instruction->base, buf_sprintf("enum initialization requires exactly one element"));
return ira->codegen->builtin_types.entry_invalid;
}
ConstExprValue *tag_value = ir_resolve_const(ira, container_type_value, UndefBad);
if (!tag_value)
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *enum_type = container_type_value->value.type->data.enum_tag.enum_type;
uint64_t tag_uint = tag_value->data.x_bignum.data.x_uint;
TypeEnumField *field = &enum_type->data.enumeration.fields[tag_uint];
TypeTableEntry *this_field_type = field->type_entry;
IrInstruction *init_value = instruction->items[0]->other;
IrInstruction *casted_init_value = ir_implicit_cast(ira, init_value, this_field_type);
if (casted_init_value == ira->codegen->invalid_instruction)
return ira->codegen->builtin_types.entry_invalid;
if (instr_is_comptime(casted_init_value)) {
ConstExprValue *init_val = ir_resolve_const(ira, casted_init_value, UndefOk);
if (!init_val)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base,
casted_init_value->value.depends_on_compile_var);
out_val->data.x_enum.tag = tag_uint;
out_val->data.x_enum.payload = init_val;
return enum_type;
}
IrInstruction *new_instruction = ir_build_init_enum_from(&ira->new_irb, &instruction->base,
enum_type, field, casted_init_value);
ir_add_alloca(ira, new_instruction, enum_type);
return enum_type;
} else {
ir_add_error(ira, container_type_value,
buf_sprintf("expected type, found '%s'", buf_ptr(&container_type_value->value.type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
}
static TypeTableEntry *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, IrInstructionContainerInitFields *instruction) {
IrInstruction *container_type_value = instruction->container_type->other;
TypeTableEntry *container_type = ir_resolve_type(ira, container_type_value);
if (type_is_invalid(container_type))
return ira->codegen->builtin_types.entry_invalid;
bool depends_on_compile_var = container_type_value->value.depends_on_compile_var;
return ir_analyze_container_init_fields(ira, &instruction->base, container_type,
instruction->field_count, instruction->fields, depends_on_compile_var);
}
static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_instruction,
IrInstruction *target_type_value, bool is_max)
{
TypeTableEntry *target_type = ir_resolve_type(ira, target_type_value);
bool depends_on_compile_var = target_type_value->value.depends_on_compile_var;
TypeTableEntry *canon_type = get_underlying_type(target_type);
switch (canon_type->id) {
case TypeTableEntryIdInvalid:
return ira->codegen->builtin_types.entry_invalid;
case TypeTableEntryIdInt:
{
ConstExprValue *out_val = ir_build_const_from(ira, source_instruction, depends_on_compile_var);
eval_min_max_value(ira->codegen, canon_type, out_val, is_max);
return ira->codegen->builtin_types.entry_num_lit_int;
}
case TypeTableEntryIdFloat:
{
ConstExprValue *out_val = ir_build_const_from(ira, source_instruction, depends_on_compile_var);
eval_min_max_value(ira->codegen, canon_type, out_val, is_max);
return ira->codegen->builtin_types.entry_num_lit_float;
}
case TypeTableEntryIdBool:
case TypeTableEntryIdVoid:
{
ConstExprValue *out_val = ir_build_const_from(ira, source_instruction, depends_on_compile_var);
eval_min_max_value(ira->codegen, canon_type, out_val, is_max);
return target_type;
}
case TypeTableEntryIdEnumTag:
zig_panic("TODO min/max value for enum tag type");
case TypeTableEntryIdVar:
case TypeTableEntryIdMetaType:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdPointer:
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdPureError:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
case TypeTableEntryIdTypeDecl:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdArgTuple:
{
const char *err_format = is_max ?
"no max value available for type '%s'" :
"no min value available for type '%s'";
ir_add_error(ira, source_instruction,
buf_sprintf(err_format, buf_ptr(&target_type->name)));
// TODO if this is a typedecl, add error note showing the declaration of the type decl
return ira->codegen->builtin_types.entry_invalid;
}
}
zig_unreachable();
}
static TypeTableEntry *ir_analyze_instruction_min_value(IrAnalyze *ira,
IrInstructionMinValue *instruction)
{
return ir_analyze_min_max(ira, &instruction->base, instruction->value->other, false);
}
static TypeTableEntry *ir_analyze_instruction_max_value(IrAnalyze *ira,
IrInstructionMaxValue *instruction)
{
return ir_analyze_min_max(ira, &instruction->base, instruction->value->other, true);
}
static TypeTableEntry *ir_analyze_instruction_compile_err(IrAnalyze *ira,
IrInstructionCompileErr *instruction)
{
IrInstruction *msg_value = instruction->msg->other;
Buf *msg_buf = ir_resolve_str(ira, msg_value);
if (!msg_buf)
return ira->codegen->builtin_types.entry_invalid;
ir_add_error(ira, &instruction->base, msg_buf);
return ira->codegen->builtin_types.entry_invalid;
}
static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstructionErrName *instruction) {
IrInstruction *value = instruction->value->other;
if (value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *casted_value = ir_implicit_cast(ira, value, value->value.type);
if (casted_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *str_type = get_slice_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true);
if (casted_value->value.special == ConstValSpecialStatic) {
ErrorTableEntry *err = casted_value->value.data.x_pure_err;
if (!err->cached_error_name_val) {
ConstExprValue *array_val = create_const_str_lit(ira->codegen, &err->name);
err->cached_error_name_val = create_const_slice(ira->codegen, array_val, 0, buf_len(&err->name), true);
}
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base,
casted_value->value.depends_on_compile_var);
*out_val = *err->cached_error_name_val;
return str_type;
}
ira->codegen->generate_error_name_table = true;
ir_build_err_name_from(&ira->new_irb, &instruction->base, value);
return str_type;
}
static TypeTableEntry *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstructionTypeName *instruction) {
IrInstruction *type_value = instruction->type_value->other;
TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
if (type_entry->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
if (!type_entry->cached_const_name_val) {
type_entry->cached_const_name_val = create_const_str_lit(ira->codegen, &type_entry->name);
}
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base,
type_value->value.depends_on_compile_var);
*out_val = *type_entry->cached_const_name_val;
return out_val->type;
}
static TypeTableEntry *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstructionCImport *instruction) {
AstNode *node = instruction->base.source_node;
assert(node->type == NodeTypeFnCallExpr);
AstNode *block_node = node->data.fn_call_expr.params.at(0);
ScopeCImport *cimport_scope = create_cimport_scope(node, instruction->base.scope);
// Execute the C import block like an inline function
TypeTableEntry *void_type = ira->codegen->builtin_types.entry_void;
IrInstruction *result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type,
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr,
&cimport_scope->buf, block_node, nullptr, nullptr);
if (result->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
find_libc_include_path(ira->codegen);
ImportTableEntry *child_import = allocate<ImportTableEntry>(1);
child_import->decls_scope = create_decls_scope(node, nullptr, nullptr, child_import);
child_import->c_import_node = node;
ZigList<ErrorMsg *> errors = {0};
int err;
if ((err = parse_h_buf(child_import, &errors, &cimport_scope->buf, ira->codegen, node))) {
zig_panic("unable to parse h file: %s\n", err_str(err));
}
if (errors.length > 0) {
ErrorMsg *parent_err_msg = ir_add_error_node(ira, node, buf_sprintf("C import failed"));
for (size_t i = 0; i < errors.length; i += 1) {
ErrorMsg *err_msg = errors.at(i);
err_msg_add_note(parent_err_msg, err_msg);
}
return ira->codegen->builtin_types.entry_invalid;
}
if (ira->codegen->verbose) {
fprintf(stderr, "\nC imports:\n");
fprintf(stderr, "-----------\n");
ast_render_decls(stderr, 4, child_import);
}
// TODO to get fewer false negatives on this, we would need to track this value in
// the ir executable
bool depends_on_compile_var = true;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
out_val->data.x_import = child_import;
return ira->codegen->builtin_types.entry_namespace;
}
static TypeTableEntry *ir_analyze_instruction_c_include(IrAnalyze *ira, IrInstructionCInclude *instruction) {
IrInstruction *name_value = instruction->name->other;
if (name_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
Buf *include_name = ir_resolve_str(ira, name_value);
if (!include_name)
return ira->codegen->builtin_types.entry_invalid;
Buf *c_import_buf = exec_c_import_buf(ira->new_irb.exec);
// We check for this error in pass1
assert(c_import_buf);
buf_appendf(c_import_buf, "#include <%s>\n", buf_ptr(include_name));
ir_build_const_from(ira, &instruction->base, false);
return ira->codegen->builtin_types.entry_void;
}
static TypeTableEntry *ir_analyze_instruction_c_define(IrAnalyze *ira, IrInstructionCDefine *instruction) {
IrInstruction *name = instruction->name->other;
if (name->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
Buf *define_name = ir_resolve_str(ira, name);
if (!define_name)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *value = instruction->value->other;
if (value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
Buf *define_value = ir_resolve_str(ira, value);
if (!define_value)
return ira->codegen->builtin_types.entry_invalid;
Buf *c_import_buf = exec_c_import_buf(ira->new_irb.exec);
// We check for this error in pass1
assert(c_import_buf);
buf_appendf(c_import_buf, "#define %s %s\n", buf_ptr(define_name), buf_ptr(define_value));
ir_build_const_from(ira, &instruction->base, false);
return ira->codegen->builtin_types.entry_void;
}
static TypeTableEntry *ir_analyze_instruction_c_undef(IrAnalyze *ira, IrInstructionCUndef *instruction) {
IrInstruction *name = instruction->name->other;
if (name->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
Buf *undef_name = ir_resolve_str(ira, name);
if (!undef_name)
return ira->codegen->builtin_types.entry_invalid;
Buf *c_import_buf = exec_c_import_buf(ira->new_irb.exec);
// We check for this error in pass1
assert(c_import_buf);
buf_appendf(c_import_buf, "#undef %s\n", buf_ptr(undef_name));
ir_build_const_from(ira, &instruction->base, false);
return ira->codegen->builtin_types.entry_void;
}
static TypeTableEntry *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstructionEmbedFile *instruction) {
IrInstruction *name = instruction->name->other;
if (name->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
Buf *rel_file_path = ir_resolve_str(ira, name);
if (!rel_file_path)
return ira->codegen->builtin_types.entry_invalid;
ImportTableEntry *import = get_scope_import(instruction->base.scope);
// figure out absolute path to resource
Buf source_dir_path = BUF_INIT;
os_path_dirname(import->path, &source_dir_path);
Buf file_path = BUF_INIT;
os_path_resolve(&source_dir_path, rel_file_path, &file_path);
// load from file system into const expr
Buf file_contents = BUF_INIT;
int err;
if ((err = os_fetch_file_path(&file_path, &file_contents))) {
if (err == ErrorFileNotFound) {
ir_add_error(ira, instruction->name, buf_sprintf("unable to find '%s'", buf_ptr(&file_path)));
return ira->codegen->builtin_types.entry_invalid;
} else {
ir_add_error(ira, instruction->name, buf_sprintf("unable to open '%s': %s", buf_ptr(&file_path), err_str(err)));
return ira->codegen->builtin_types.entry_invalid;
}
}
// TODO add dependency on the file we embedded so that we know if it changes
// we'll have to invalidate the cache
bool depends_on_compile_var = true;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
init_const_str_lit(ira->codegen, out_val, &file_contents);
return get_array_type(ira->codegen, ira->codegen->builtin_types.entry_u8, buf_len(&file_contents));
}
static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchg *instruction) {
IrInstruction *ptr = instruction->ptr->other;
if (ptr->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *cmp_value = instruction->cmp_value->other;
if (cmp_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *new_value = instruction->new_value->other;
if (new_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *success_order_value = instruction->success_order_value->other;
if (success_order_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
AtomicOrder success_order;
if (!ir_resolve_atomic_order(ira, success_order_value, &success_order))
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *failure_order_value = instruction->failure_order_value->other;
if (failure_order_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
AtomicOrder failure_order;
if (!ir_resolve_atomic_order(ira, failure_order_value, &failure_order))
return ira->codegen->builtin_types.entry_invalid;
if (ptr->value.type->id != TypeTableEntryIdPointer) {
ir_add_error(ira, instruction->ptr,
buf_sprintf("expected pointer argument, found '%s'", buf_ptr(&ptr->value.type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
TypeTableEntry *child_type = ptr->value.type->data.pointer.child_type;
IrInstruction *casted_cmp_value = ir_implicit_cast(ira, cmp_value, child_type);
if (casted_cmp_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, child_type);
if (casted_new_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
if (success_order < AtomicOrderMonotonic) {
ir_add_error(ira, success_order_value,
buf_sprintf("success atomic ordering must be Monotonic or stricter"));
return ira->codegen->builtin_types.entry_invalid;
}
if (failure_order < AtomicOrderMonotonic) {
ir_add_error(ira, failure_order_value,
buf_sprintf("failure atomic ordering must be Monotonic or stricter"));
return ira->codegen->builtin_types.entry_invalid;
}
if (failure_order > success_order) {
ir_add_error(ira, failure_order_value,
buf_sprintf("failure atomic ordering must be no stricter than success"));
return ira->codegen->builtin_types.entry_invalid;
}
if (failure_order == AtomicOrderRelease || failure_order == AtomicOrderAcqRel) {
ir_add_error(ira, failure_order_value,
buf_sprintf("failure atomic ordering must not be Release or AcqRel"));
return ira->codegen->builtin_types.entry_invalid;
}
ir_build_cmpxchg_from(&ira->new_irb, &instruction->base, ptr, casted_cmp_value, casted_new_value,
success_order_value, failure_order_value, success_order, failure_order);
return ira->codegen->builtin_types.entry_bool;
}
static TypeTableEntry *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstructionFence *instruction) {
IrInstruction *order_value = instruction->order_value->other;
if (order_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
AtomicOrder order;
if (!ir_resolve_atomic_order(ira, order_value, &order))
return ira->codegen->builtin_types.entry_invalid;
ir_build_fence_from(&ira->new_irb, &instruction->base, order_value, order);
return ira->codegen->builtin_types.entry_void;
}
static TypeTableEntry *ir_analyze_instruction_div_exact(IrAnalyze *ira, IrInstructionDivExact *instruction) {
IrInstruction *op1 = instruction->op1->other;
if (op1->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *op2 = instruction->op2->other;
if (op2->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *peer_instructions[] = { op1, op2 };
TypeTableEntry *result_type = ir_resolve_peer_types(ira, instruction->base.source_node, peer_instructions, 2);
if (result_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *canon_type = get_underlying_type(result_type);
if (canon_type->id != TypeTableEntryIdInt &&
canon_type->id != TypeTableEntryIdNumLitInt)
{
ir_add_error(ira, &instruction->base,
buf_sprintf("expected integer type, found '%s'", buf_ptr(&result_type->name)));
// TODO if meta_type is type decl, add note pointing to type decl declaration
return ira->codegen->builtin_types.entry_invalid;
}
IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, result_type);
if (casted_op1->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, result_type);
if (casted_op2->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
if (casted_op1->value.special == ConstValSpecialStatic &&
casted_op2->value.special == ConstValSpecialStatic)
{
ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad);
ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);
assert(op1_val);
assert(op2_val);
if (op1_val->data.x_bignum.data.x_uint == 0) {
ir_add_error(ira, &instruction->base, buf_sprintf("division by zero"));
return ira->codegen->builtin_types.entry_invalid;
}
BigNum remainder;
if (bignum_mod(&remainder, &op1_val->data.x_bignum, &op2_val->data.x_bignum)) {
ir_add_error(ira, &instruction->base, buf_sprintf("integer overflow"));
return ira->codegen->builtin_types.entry_invalid;
}
if (remainder.data.x_uint != 0) {
ir_add_error(ira, &instruction->base, buf_sprintf("exact division had a remainder"));
return ira->codegen->builtin_types.entry_invalid;
}
bool depends_on_compile_var = casted_op1->value.depends_on_compile_var ||
casted_op2->value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
bignum_div(&out_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum);
return result_type;
}
ir_build_div_exact_from(&ira->new_irb, &instruction->base, casted_op1, casted_op2);
return result_type;
}
static TypeTableEntry *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstructionTruncate *instruction) {
IrInstruction *dest_type_value = instruction->dest_type->other;
TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value);
TypeTableEntry *canon_dest_type = get_underlying_type(dest_type);
if (canon_dest_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
if (canon_dest_type->id != TypeTableEntryIdInt &&
canon_dest_type->id != TypeTableEntryIdNumLitInt)
{
ir_add_error(ira, dest_type_value, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name)));
// TODO if meta_type is type decl, add note pointing to type decl declaration
return ira->codegen->builtin_types.entry_invalid;
}
IrInstruction *target = instruction->target->other;
TypeTableEntry *src_type = target->value.type;
TypeTableEntry *canon_src_type = get_underlying_type(src_type);
if (canon_src_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
if (canon_src_type->id != TypeTableEntryIdInt &&
canon_src_type->id != TypeTableEntryIdNumLitInt)
{
ir_add_error(ira, target, buf_sprintf("expected integer type, found '%s'", buf_ptr(&src_type->name)));
// TODO if meta_type is type decl, add note pointing to type decl declaration
return ira->codegen->builtin_types.entry_invalid;
}
if (canon_src_type->data.integral.is_signed != canon_dest_type->data.integral.is_signed) {
const char *sign_str = canon_dest_type->data.integral.is_signed ? "signed" : "unsigned";
ir_add_error(ira, target, buf_sprintf("expected %s integer type, found '%s'", sign_str, buf_ptr(&src_type->name)));
// TODO if meta_type is type decl, add note pointing to type decl declaration
return ira->codegen->builtin_types.entry_invalid;
} else if (canon_src_type->data.integral.bit_count <= canon_dest_type->data.integral.bit_count) {
ir_add_error(ira, target, buf_sprintf("type '%s' has same or fewer bits than destination type '%s'",
buf_ptr(&src_type->name), buf_ptr(&dest_type->name)));
// TODO if meta_type is type decl, add note pointing to type decl declaration
return ira->codegen->builtin_types.entry_invalid;
}
if (target->value.special == ConstValSpecialStatic) {
bool depends_on_compile_var = dest_type_value->value.depends_on_compile_var || target->value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
bignum_init_bignum(&out_val->data.x_bignum, &target->value.data.x_bignum);
bignum_truncate(&out_val->data.x_bignum, canon_dest_type->data.integral.bit_count);
return dest_type;
}
ir_build_truncate_from(&ira->new_irb, &instruction->base, dest_type_value, target);
return dest_type;
}
static TypeTableEntry *ir_analyze_instruction_int_type(IrAnalyze *ira, IrInstructionIntType *instruction) {
IrInstruction *is_signed_value = instruction->is_signed->other;
bool is_signed;
if (!ir_resolve_bool(ira, is_signed_value, &is_signed))
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *bit_count_value = instruction->bit_count->other;
uint64_t bit_count;
if (!ir_resolve_usize(ira, bit_count_value, &bit_count))
return ira->codegen->builtin_types.entry_invalid;
bool depends_on_compile_var = is_signed_value->value.depends_on_compile_var ||
bit_count_value->value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
out_val->data.x_type = get_int_type(ira->codegen, is_signed, bit_count);
return ira->codegen->builtin_types.entry_type;
}
static TypeTableEntry *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstructionBoolNot *instruction) {
IrInstruction *value = instruction->value->other;
if (value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *bool_type = ira->codegen->builtin_types.entry_bool;
IrInstruction *casted_value = ir_implicit_cast(ira, value, bool_type);
if (casted_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
if (casted_value->value.special != ConstValSpecialRuntime) {
bool depends_on_compile_var = casted_value->value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
out_val->data.x_bool = !casted_value->value.data.x_bool;
return bool_type;
}
ir_build_bool_not_from(&ira->new_irb, &instruction->base, casted_value);
return bool_type;
}
static TypeTableEntry *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructionAlloca *instruction) {
IrInstruction *type_value = instruction->type_value->other;
if (type_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *count_value = instruction->count->other;
if (count_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *child_type = ir_resolve_type(ira, type_value);
if (type_requires_comptime(child_type)) {
ir_add_error(ira, type_value,
buf_sprintf("invalid alloca type '%s'", buf_ptr(&child_type->name)));
// TODO if this is a typedecl, add error note showing the declaration of the type decl
return ira->codegen->builtin_types.entry_invalid;
} else {
TypeTableEntry *slice_type = get_slice_type(ira->codegen, child_type, false);
IrInstruction *new_instruction = ir_build_alloca_from(&ira->new_irb, &instruction->base, type_value, count_value);
ir_add_alloca(ira, new_instruction, slice_type);
return slice_type;
}
zig_unreachable();
}
static TypeTableEntry *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructionMemset *instruction) {
IrInstruction *dest_ptr = instruction->dest_ptr->other;
if (dest_ptr->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *byte_value = instruction->byte->other;
if (byte_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *count_value = instruction->count->other;
if (count_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize;
TypeTableEntry *u8 = ira->codegen->builtin_types.entry_u8;
TypeTableEntry *u8_ptr = get_pointer_to_type(ira->codegen, u8, false);
IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr);
if (casted_dest_ptr->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *casted_byte = ir_implicit_cast(ira, byte_value, u8);
if (casted_byte->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *casted_count = ir_implicit_cast(ira, count_value, usize);
if (casted_count->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
if (casted_dest_ptr->value.special == ConstValSpecialStatic &&
casted_byte->value.special == ConstValSpecialStatic &&
casted_count->value.special == ConstValSpecialStatic)
{
ConstExprValue *dest_ptr_val = &casted_dest_ptr->value;
ConstExprValue *dest_elements;
size_t start;
size_t bound_end;
if (dest_ptr_val->data.x_ptr.index == SIZE_MAX) {
dest_elements = dest_ptr_val->data.x_ptr.base_ptr;
start = 0;
bound_end = 1;
} else {
ConstExprValue *array_val = dest_ptr_val->data.x_ptr.base_ptr;
dest_elements = array_val->data.x_array.elements;
start = dest_ptr_val->data.x_ptr.index;
bound_end = array_val->data.x_array.size;
}
size_t count = casted_count->value.data.x_bignum.data.x_uint;
size_t end = start + count;
if (end > bound_end) {
ir_add_error(ira, count_value, buf_sprintf("out of bounds pointer access"));
return ira->codegen->builtin_types.entry_invalid;
}
ConstExprValue *byte_val = &casted_byte->value;
for (size_t i = start; i < end; i += 1) {
dest_elements[i] = *byte_val;
}
ir_build_const_from(ira, &instruction->base, false);
return ira->codegen->builtin_types.entry_void;
}
ir_build_memset_from(&ira->new_irb, &instruction->base, casted_dest_ptr, casted_byte, casted_count);
return ira->codegen->builtin_types.entry_void;
}
static TypeTableEntry *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructionMemcpy *instruction) {
IrInstruction *dest_ptr = instruction->dest_ptr->other;
if (dest_ptr->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *src_ptr = instruction->src_ptr->other;
if (src_ptr->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *count_value = instruction->count->other;
if (count_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize;
TypeTableEntry *u8 = ira->codegen->builtin_types.entry_u8;
TypeTableEntry *u8_ptr_mut = get_pointer_to_type(ira->codegen, u8, false);
TypeTableEntry *u8_ptr_const = get_pointer_to_type(ira->codegen, u8, true);
IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr_mut);
if (casted_dest_ptr->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *casted_src_ptr = ir_implicit_cast(ira, src_ptr, u8_ptr_const);
if (casted_src_ptr->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *casted_count = ir_implicit_cast(ira, count_value, usize);
if (casted_count->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
if (casted_dest_ptr->value.special == ConstValSpecialStatic &&
casted_src_ptr->value.special == ConstValSpecialStatic &&
casted_count->value.special == ConstValSpecialStatic)
{
size_t count = casted_count->value.data.x_bignum.data.x_uint;
ConstExprValue *dest_ptr_val = &casted_dest_ptr->value;
ConstExprValue *dest_elements;
size_t dest_start;
size_t dest_end;
if (dest_ptr_val->data.x_ptr.index == SIZE_MAX) {
dest_elements = dest_ptr_val->data.x_ptr.base_ptr;
dest_start = 0;
dest_end = 1;
} else {
ConstExprValue *array_val = dest_ptr_val->data.x_ptr.base_ptr;
dest_elements = array_val->data.x_array.elements;
dest_start = dest_ptr_val->data.x_ptr.index;
dest_end = array_val->data.x_array.size;
}
if (dest_start + count > dest_end) {
ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds pointer access"));
return ira->codegen->builtin_types.entry_invalid;
}
ConstExprValue *src_ptr_val = &casted_src_ptr->value;
ConstExprValue *src_elements;
size_t src_start;
size_t src_end;
if (src_ptr_val->data.x_ptr.index == SIZE_MAX) {
src_elements = src_ptr_val->data.x_ptr.base_ptr;
src_start = 0;
src_end = 1;
} else {
ConstExprValue *array_val = src_ptr_val->data.x_ptr.base_ptr;
src_elements = array_val->data.x_array.elements;
src_start = src_ptr_val->data.x_ptr.index;
src_end = array_val->data.x_array.size;
}
if (src_start + count > src_end) {
ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds pointer access"));
return ira->codegen->builtin_types.entry_invalid;
}
// TODO check for noalias violations - this should be generalized to work for any function
for (size_t i = 0; i < count; i += 1) {
dest_elements[dest_start + i] = src_elements[src_start + i];
}
ir_build_const_from(ira, &instruction->base, false);
return ira->codegen->builtin_types.entry_void;
}
ir_build_memcpy_from(&ira->new_irb, &instruction->base, casted_dest_ptr, casted_src_ptr, casted_count);
return ira->codegen->builtin_types.entry_void;
}
static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructionSlice *instruction) {
IrInstruction *ptr = instruction->ptr->other;
if (ptr->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *start = instruction->start->other;
if (start->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize;
IrInstruction *casted_start = ir_implicit_cast(ira, start, usize);
if (casted_start->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *end;
if (instruction->end) {
end = instruction->end->other;
if (end->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
end = ir_implicit_cast(ira, end, usize);
if (end->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
} else {
end = nullptr;
}
TypeTableEntry *array_type = get_underlying_type(ptr->value.type);
TypeTableEntry *return_type;
if (array_type->id == TypeTableEntryIdArray) {
return_type = get_slice_type(ira->codegen, array_type->data.array.child_type, instruction->is_const);
} else if (array_type->id == TypeTableEntryIdPointer) {
return_type = get_slice_type(ira->codegen, array_type->data.pointer.child_type, instruction->is_const);
if (!end) {
ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value"));
return ira->codegen->builtin_types.entry_invalid;
}
} else if (is_slice(array_type)) {
return_type = get_slice_type(ira->codegen,
array_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,
instruction->is_const);
} else {
ir_add_error(ira, &instruction->base,
buf_sprintf("slice of non-array type '%s'", buf_ptr(&ptr->value.type->name)));
// TODO if this is a typedecl, add error note showing the declaration of the type decl
return ira->codegen->builtin_types.entry_invalid;
}
if (ptr->value.special == ConstValSpecialStatic &&
casted_start->value.special == ConstValSpecialStatic &&
(!end || end->value.special == ConstValSpecialStatic))
{
bool depends_on_compile_var =
ptr->value.depends_on_compile_var ||
casted_start->value.depends_on_compile_var ||
(end ? end->value.depends_on_compile_var : false);
ConstExprValue *base_ptr;
size_t abs_offset;
size_t rel_end;
if (array_type->id == TypeTableEntryIdArray) {
base_ptr = &ptr->value;
abs_offset = 0;
rel_end = array_type->data.array.len;
} else if (array_type->id == TypeTableEntryIdPointer) {
base_ptr = ptr->value.data.x_ptr.base_ptr;
abs_offset = ptr->value.data.x_ptr.index;
if (abs_offset == SIZE_MAX) {
rel_end = 1;
} else {
rel_end = base_ptr->data.x_array.size - abs_offset;
}
} else if (is_slice(array_type)) {
ConstExprValue *ptr_val = &ptr->value.data.x_struct.fields[slice_ptr_index];
ConstExprValue *len_val = &ptr->value.data.x_struct.fields[slice_len_index];
base_ptr = ptr_val->data.x_ptr.base_ptr;
abs_offset = ptr_val->data.x_ptr.index;
if (ptr_val->data.x_ptr.index == SIZE_MAX) {
rel_end = 1;
} else {
rel_end = len_val->data.x_bignum.data.x_uint;
}
} else {
zig_unreachable();
}
uint64_t start_scalar = casted_start->value.data.x_bignum.data.x_uint;
if (start_scalar > rel_end) {
ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));
return ira->codegen->builtin_types.entry_invalid;
}
uint64_t end_scalar;
if (end) {
end_scalar = end->value.data.x_bignum.data.x_uint;
} else {
end_scalar = rel_end;
}
if (end_scalar > rel_end) {
ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));
return ira->codegen->builtin_types.entry_invalid;
}
if (start_scalar > end_scalar) {
ir_add_error(ira, &instruction->base, buf_sprintf("slice start is greater than end"));
return ira->codegen->builtin_types.entry_invalid;
}
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
out_val->data.x_struct.fields = allocate<ConstExprValue>(2);
ConstExprValue *ptr_val = &out_val->data.x_struct.fields[slice_ptr_index];
size_t index = (abs_offset != SIZE_MAX) ? (abs_offset + start_scalar) : SIZE_MAX;
init_const_ptr(ira->codegen, ptr_val, base_ptr, index, instruction->is_const);
ConstExprValue *len_val = &out_val->data.x_struct.fields[slice_len_index];
init_const_usize(ira->codegen, len_val, end_scalar - start_scalar);
return return_type;
}
IrInstruction *new_instruction = ir_build_slice_from(&ira->new_irb, &instruction->base, ptr,
casted_start, end, instruction->is_const, instruction->safety_check_on);
ir_add_alloca(ira, new_instruction, return_type);
return return_type;
}
static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrInstructionMemberCount *instruction) {
IrInstruction *container = instruction->container->other;
if (container->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *container_type = ir_resolve_type(ira, container);
TypeTableEntry *canon_type = get_underlying_type(container_type);
uint64_t result;
if (canon_type->id == TypeTableEntryIdInvalid) {
return ira->codegen->builtin_types.entry_invalid;
} else if (canon_type->id == TypeTableEntryIdEnum) {
result = canon_type->data.enumeration.src_field_count;
} else if (canon_type->id == TypeTableEntryIdStruct) {
result = canon_type->data.structure.src_field_count;
} else if (canon_type->id == TypeTableEntryIdUnion) {
result = canon_type->data.unionation.src_field_count;
} else {
ir_add_error(ira, &instruction->base, buf_sprintf("no value count available for type '%s'", buf_ptr(&container_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
bool depends_on_compile_var = container->value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
bignum_init_unsigned(&out_val->data.x_bignum, result);
return ira->codegen->builtin_types.entry_num_lit_int;
}
static TypeTableEntry *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstructionBreakpoint *instruction) {
ir_build_breakpoint_from(&ira->new_irb, &instruction->base);
return ira->codegen->builtin_types.entry_void;
}
static TypeTableEntry *ir_analyze_instruction_return_address(IrAnalyze *ira, IrInstructionReturnAddress *instruction) {
ir_build_return_address_from(&ira->new_irb, &instruction->base);
TypeTableEntry *u8 = ira->codegen->builtin_types.entry_u8;
TypeTableEntry *u8_ptr_const = get_pointer_to_type(ira->codegen, u8, true);
return u8_ptr_const;
}
static TypeTableEntry *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrInstructionFrameAddress *instruction) {
ir_build_frame_address_from(&ira->new_irb, &instruction->base);
TypeTableEntry *u8 = ira->codegen->builtin_types.entry_u8;
TypeTableEntry *u8_ptr_const = get_pointer_to_type(ira->codegen, u8, true);
return u8_ptr_const;
}
static TypeTableEntry *ir_analyze_instruction_alignof(IrAnalyze *ira, IrInstructionAlignOf *instruction) {
IrInstruction *type_value = instruction->type_value->other;
if (type_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
if (type_entry->id == TypeTableEntryIdInvalid) {
return ira->codegen->builtin_types.entry_invalid;
} else if (type_entry->id == TypeTableEntryIdUnreachable) {
ir_add_error(ira, instruction->type_value,
buf_sprintf("no align available for type '%s'", buf_ptr(&type_entry->name)));
return ira->codegen->builtin_types.entry_invalid;
} else {
uint64_t align_in_bytes = LLVMABISizeOfType(ira->codegen->target_data_ref, type_entry->type_ref);
bool depends_on_compile_var = type_value->value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
bignum_init_unsigned(&out_val->data.x_bignum, align_in_bytes);
return ira->codegen->builtin_types.entry_num_lit_int;
}
}
static TypeTableEntry *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstructionOverflowOp *instruction) {
IrInstruction *type_value = instruction->type_value->other;
if (type_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *dest_type = ir_resolve_type(ira, type_value);
TypeTableEntry *canon_type = get_underlying_type(dest_type);
if (canon_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
if (canon_type->id != TypeTableEntryIdInt) {
ir_add_error(ira, type_value,
buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name)));
// TODO if this is a typedecl, add error note showing the declaration of the type decl
return ira->codegen->builtin_types.entry_invalid;
}
IrInstruction *op1 = instruction->op1->other;
if (op1->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, dest_type);
if (casted_op1->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *op2 = instruction->op2->other;
if (op2->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, dest_type);
if (casted_op2->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *result_ptr = instruction->result_ptr->other;
if (result_ptr->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *expected_ptr_type = get_pointer_to_type(ira->codegen, dest_type, false);
IrInstruction *casted_result_ptr = ir_implicit_cast(ira, result_ptr, expected_ptr_type);
if (casted_result_ptr->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
if (casted_op1->value.special == ConstValSpecialStatic &&
casted_op2->value.special == ConstValSpecialStatic &&
casted_result_ptr->value.special == ConstValSpecialStatic)
{
bool depends_on_compile_var = type_value->value.depends_on_compile_var ||
casted_op1->value.depends_on_compile_var || casted_op2->value.depends_on_compile_var ||
casted_result_ptr->value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
BigNum *op1_bignum = &casted_op1->value.data.x_bignum;
BigNum *op2_bignum = &casted_op2->value.data.x_bignum;
ConstExprValue *pointee_val = const_ptr_pointee(&casted_result_ptr->value);
BigNum *dest_bignum = &pointee_val->data.x_bignum;
switch (instruction->op) {
case IrOverflowOpAdd:
out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum);
break;
case IrOverflowOpSub:
out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum);
break;
case IrOverflowOpMul:
out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum);
break;
case IrOverflowOpShl:
out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum);
break;
}
if (!bignum_fits_in_bits(dest_bignum, canon_type->data.integral.bit_count,
canon_type->data.integral.is_signed))
{
out_val->data.x_bool = true;
bignum_truncate(dest_bignum, canon_type->data.integral.bit_count);
}
pointee_val->special = ConstValSpecialStatic;
return ira->codegen->builtin_types.entry_bool;
}
ir_build_overflow_op_from(&ira->new_irb, &instruction->base, instruction->op, type_value,
casted_op1, casted_op2, casted_result_ptr, dest_type);
return ira->codegen->builtin_types.entry_bool;
}
static TypeTableEntry *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstructionTestErr *instruction) {
IrInstruction *value = instruction->value->other;
if (value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *non_canon_type = value->value.type;
TypeTableEntry *canon_type = get_underlying_type(non_canon_type);
if (canon_type->id == TypeTableEntryIdInvalid) {
return ira->codegen->builtin_types.entry_invalid;
} else if (canon_type->id == TypeTableEntryIdErrorUnion) {
if (instr_is_comptime(value)) {
ConstExprValue *err_union_val = ir_resolve_const(ira, value, UndefBad);
if (!err_union_val)
return ira->codegen->builtin_types.entry_invalid;
if (err_union_val->special != ConstValSpecialRuntime) {
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base,
err_union_val->depends_on_compile_var);
out_val->data.x_bool = (err_union_val->data.x_err_union.err != nullptr);
return ira->codegen->builtin_types.entry_bool;
}
}
ir_build_test_err_from(&ira->new_irb, &instruction->base, value);
return ira->codegen->builtin_types.entry_bool;
} else if (canon_type->id == TypeTableEntryIdPureError) {
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, false);
out_val->data.x_bool = true;
return ira->codegen->builtin_types.entry_bool;
} else {
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, false);
out_val->data.x_bool = false;
return ira->codegen->builtin_types.entry_bool;
}
}
static TypeTableEntry *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira,
IrInstructionUnwrapErrCode *instruction)
{
IrInstruction *value = instruction->value->other;
if (value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *ptr_type = value->value.type;
// This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing.
assert(ptr_type->id == TypeTableEntryIdPointer);
TypeTableEntry *non_canon_type = ptr_type->data.pointer.child_type;
TypeTableEntry *canon_type = get_underlying_type(non_canon_type);
if (canon_type->id == TypeTableEntryIdInvalid) {
return ira->codegen->builtin_types.entry_invalid;
} else if (canon_type->id == TypeTableEntryIdErrorUnion) {
if (instr_is_comptime(value)) {
ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad);
if (!ptr_val)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *err_union_val = ptr_val->data.x_ptr.base_ptr;
assert(ptr_val->data.x_ptr.index == SIZE_MAX);
if (err_union_val->special != ConstValSpecialRuntime) {
ErrorTableEntry *err = err_union_val->data.x_err_union.err;
assert(err);
bool depends_on_compile_var = ptr_val->depends_on_compile_var || err_union_val->depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
out_val->data.x_pure_err = err;
return ira->codegen->builtin_types.entry_pure_error;
}
}
ir_build_unwrap_err_code_from(&ira->new_irb, &instruction->base, value);
return ira->codegen->builtin_types.entry_pure_error;
} else {
ir_add_error(ira, value,
buf_sprintf("expected error union type, found '%s'", buf_ptr(&non_canon_type->name)));
// TODO if this is a typedecl, add error note showing the declaration of the type decl
return ira->codegen->builtin_types.entry_invalid;
}
}
static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira,
IrInstructionUnwrapErrPayload *instruction)
{
assert(instruction->value->other);
IrInstruction *value = instruction->value->other;
if (value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *ptr_type = value->value.type;
// This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing.
assert(ptr_type->id == TypeTableEntryIdPointer);
TypeTableEntry *non_canon_type = ptr_type->data.pointer.child_type;
TypeTableEntry *canon_type = get_underlying_type(non_canon_type);
if (canon_type->id == TypeTableEntryIdInvalid) {
return ira->codegen->builtin_types.entry_invalid;
} else if (canon_type->id == TypeTableEntryIdErrorUnion) {
TypeTableEntry *child_type = canon_type->data.error.child_type;
TypeTableEntry *result_type = get_pointer_to_type(ira->codegen, child_type, false);
if (instr_is_comptime(value)) {
ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad);
if (!ptr_val)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *err_union_val = ptr_val->data.x_ptr.base_ptr;
assert(ptr_val->data.x_ptr.index == SIZE_MAX);
if (err_union_val->special != ConstValSpecialRuntime) {
ErrorTableEntry *err = err_union_val->data.x_err_union.err;
if (err != nullptr) {
ir_add_error(ira, &instruction->base,
buf_sprintf("unable to unwrap error '%s'", buf_ptr(&err->name)));
return ira->codegen->builtin_types.entry_invalid;
}
bool depends_on_compile_var = ptr_val->depends_on_compile_var || err_union_val->depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
out_val->data.x_ptr.base_ptr = err_union_val->data.x_err_union.payload;
out_val->data.x_ptr.index = SIZE_MAX;
return result_type;
}
}
ir_build_unwrap_err_payload_from(&ira->new_irb, &instruction->base, value, instruction->safety_check_on);
return result_type;
} else {
ir_add_error(ira, value,
buf_sprintf("expected error union type, found '%s'", buf_ptr(&non_canon_type->name)));
// TODO if this is a typedecl, add error note showing the declaration of the type decl
return ira->codegen->builtin_types.entry_invalid;
}
}
static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnProto *instruction) {
AstNode *proto_node = instruction->base.source_node;
assert(proto_node->type == NodeTypeFnProto);
FnTypeId fn_type_id = {0};
init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length);
bool depends_on_compile_var = false;
for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) {
AstNode *param_node = proto_node->data.fn_proto.params.at(fn_type_id.next_param_index);
assert(param_node->type == NodeTypeParamDecl);
IrInstruction *param_type_value = instruction->param_types[fn_type_id.next_param_index]->other;
FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];
param_info->is_noalias = param_node->data.param_decl.is_noalias;
param_info->type = ir_resolve_type(ira, param_type_value);
if (param_info->type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
depends_on_compile_var = depends_on_compile_var || param_type_value->value.depends_on_compile_var;
}
IrInstruction *return_type_value = instruction->return_type->other;
fn_type_id.return_type = ir_resolve_type(ira, return_type_value);
if (fn_type_id.return_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
depends_on_compile_var = depends_on_compile_var || return_type_value->value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
out_val->data.x_type = get_fn_type(ira->codegen, &fn_type_id);
return ira->codegen->builtin_types.entry_type;
}
static TypeTableEntry *ir_analyze_instruction_test_comptime(IrAnalyze *ira, IrInstructionTestComptime *instruction) {
IrInstruction *value = instruction->value->other;
if (value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, false);
out_val->data.x_bool = instr_is_comptime(value);
return ira->codegen->builtin_types.entry_bool;
}
static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
IrInstructionCheckSwitchProngs *instruction)
{
IrInstruction *target_value = instruction->target_value->other;
TypeTableEntry *switch_type = target_value->value.type;
if (switch_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
if (switch_type->id == TypeTableEntryIdEnumTag) {
TypeTableEntry *enum_type = switch_type->data.enum_tag.enum_type;
size_t *field_use_counts = allocate<size_t>(enum_type->data.enumeration.src_field_count);
for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i];
IrInstruction *start_value = range->start->other;
if (start_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *end_value = range->end->other;
if (end_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
size_t start_index;
size_t end_index;
if (start_value->value.type->id == TypeTableEntryIdEnumTag) {
start_index = start_value->value.data.x_bignum.data.x_uint;
} else if (start_value->value.type->id == TypeTableEntryIdEnum) {
start_index = start_value->value.data.x_enum.tag;
} else {
zig_unreachable();
}
if (end_value->value.type->id == TypeTableEntryIdEnumTag) {
end_index = end_value->value.data.x_bignum.data.x_uint;
} else if (end_value->value.type->id == TypeTableEntryIdEnum) {
end_index = end_value->value.data.x_enum.tag;
} else {
zig_unreachable();
}
for (size_t field_index = start_index; field_index <= end_index; field_index += 1) {
field_use_counts[field_index] += 1;
if (field_use_counts[field_index] > 1) {
TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_index];
ir_add_error(ira, start_value,
buf_sprintf("duplicate switch value: '%s.%s'", buf_ptr(&enum_type->name),
buf_ptr(type_enum_field->name)));
}
}
}
for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) {
if (field_use_counts[i] == 0) {
ir_add_error(ira, &instruction->base,
buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&enum_type->name),
buf_ptr(enum_type->data.enumeration.fields[i].name)));
}
}
} else {
// TODO check prongs of types other than enumtag
}
ir_build_const_from(ira, &instruction->base, false);
return ira->codegen->builtin_types.entry_void;
}
static TypeTableEntry *ir_analyze_instruction_test_type(IrAnalyze *ira, IrInstructionTestType *instruction) {
IrInstruction *type_value = instruction->type_value->other;
TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
if (type_entry->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, type_value->value.depends_on_compile_var);
out_val->data.x_bool = (type_entry->id == instruction->type_id);
return ira->codegen->builtin_types.entry_bool;
}
static TypeTableEntry *ir_analyze_instruction_can_implicit_cast(IrAnalyze *ira,
IrInstructionCanImplicitCast *instruction)
{
IrInstruction *type_value = instruction->type_value->other;
TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
if (type_entry->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *target_value = instruction->target_value->other;
if (target_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
ImplicitCastMatchResult result = ir_types_match_with_implicit_cast(ira, type_entry, target_value->value.type,
target_value);
if (result == ImplicitCastMatchResultReportedError) {
zig_panic("TODO refactor implicit cast tester to return bool without reporting errors");
}
// TODO in order to known depends_on_compile_var we have to known if the type of the target
// depends on a compile var
bool depends_on_compile_var = true;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
out_val->data.x_bool = (result == ImplicitCastMatchResultYes);
return ira->codegen->builtin_types.entry_bool;
}
static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
switch (instruction->id) {
case IrInstructionIdInvalid:
case IrInstructionIdPointerReinterpret:
case IrInstructionIdWidenOrShorten:
case IrInstructionIdIntToPtr:
case IrInstructionIdPtrToInt:
case IrInstructionIdIntToEnum:
case IrInstructionIdStructInit:
case IrInstructionIdStructFieldPtr:
case IrInstructionIdEnumFieldPtr:
case IrInstructionIdInitEnum:
zig_unreachable();
case IrInstructionIdReturn:
return ir_analyze_instruction_return(ira, (IrInstructionReturn *)instruction);
case IrInstructionIdConst:
return ir_analyze_instruction_const(ira, (IrInstructionConst *)instruction);
case IrInstructionIdUnOp:
return ir_analyze_instruction_un_op(ira, (IrInstructionUnOp *)instruction);
case IrInstructionIdBinOp:
return ir_analyze_instruction_bin_op(ira, (IrInstructionBinOp *)instruction);
case IrInstructionIdDeclVar:
return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVar *)instruction);
case IrInstructionIdLoadPtr:
return ir_analyze_instruction_load_ptr(ira, (IrInstructionLoadPtr *)instruction);
case IrInstructionIdStorePtr:
return ir_analyze_instruction_store_ptr(ira, (IrInstructionStorePtr *)instruction);
case IrInstructionIdElemPtr:
return ir_analyze_instruction_elem_ptr(ira, (IrInstructionElemPtr *)instruction);
case IrInstructionIdVarPtr:
return ir_analyze_instruction_var_ptr(ira, (IrInstructionVarPtr *)instruction);
case IrInstructionIdFieldPtr:
return ir_analyze_instruction_field_ptr(ira, (IrInstructionFieldPtr *)instruction);
case IrInstructionIdCall:
return ir_analyze_instruction_call(ira, (IrInstructionCall *)instruction);
case IrInstructionIdBr:
return ir_analyze_instruction_br(ira, (IrInstructionBr *)instruction);
case IrInstructionIdCondBr:
return ir_analyze_instruction_cond_br(ira, (IrInstructionCondBr *)instruction);
case IrInstructionIdUnreachable:
return ir_analyze_instruction_unreachable(ira, (IrInstructionUnreachable *)instruction);
case IrInstructionIdPhi:
return ir_analyze_instruction_phi(ira, (IrInstructionPhi *)instruction);
case IrInstructionIdTypeOf:
return ir_analyze_instruction_typeof(ira, (IrInstructionTypeOf *)instruction);
case IrInstructionIdToPtrType:
return ir_analyze_instruction_to_ptr_type(ira, (IrInstructionToPtrType *)instruction);
case IrInstructionIdPtrTypeChild:
return ir_analyze_instruction_ptr_type_child(ira, (IrInstructionPtrTypeChild *)instruction);
case IrInstructionIdSetFnTest:
return ir_analyze_instruction_set_fn_test(ira, (IrInstructionSetFnTest *)instruction);
case IrInstructionIdSetFnVisible:
return ir_analyze_instruction_set_fn_visible(ira, (IrInstructionSetFnVisible *)instruction);
case IrInstructionIdSetDebugSafety:
return ir_analyze_instruction_set_debug_safety(ira, (IrInstructionSetDebugSafety *)instruction);
case IrInstructionIdSliceType:
return ir_analyze_instruction_slice_type(ira, (IrInstructionSliceType *)instruction);
case IrInstructionIdAsm:
return ir_analyze_instruction_asm(ira, (IrInstructionAsm *)instruction);
case IrInstructionIdArrayType:
return ir_analyze_instruction_array_type(ira, (IrInstructionArrayType *)instruction);
case IrInstructionIdCompileVar:
return ir_analyze_instruction_compile_var(ira, (IrInstructionCompileVar *)instruction);
case IrInstructionIdSizeOf:
return ir_analyze_instruction_size_of(ira, (IrInstructionSizeOf *)instruction);
case IrInstructionIdTestNonNull:
return ir_analyze_instruction_test_non_null(ira, (IrInstructionTestNonNull *)instruction);
case IrInstructionIdUnwrapMaybe:
return ir_analyze_instruction_unwrap_maybe(ira, (IrInstructionUnwrapMaybe *)instruction);
case IrInstructionIdClz:
return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction);
case IrInstructionIdCtz:
return ir_analyze_instruction_ctz(ira, (IrInstructionCtz *)instruction);
case IrInstructionIdSwitchBr:
return ir_analyze_instruction_switch_br(ira, (IrInstructionSwitchBr *)instruction);
case IrInstructionIdSwitchTarget:
return ir_analyze_instruction_switch_target(ira, (IrInstructionSwitchTarget *)instruction);
case IrInstructionIdSwitchVar:
return ir_analyze_instruction_switch_var(ira, (IrInstructionSwitchVar *)instruction);
case IrInstructionIdEnumTag:
return ir_analyze_instruction_enum_tag(ira, (IrInstructionEnumTag *)instruction);
case IrInstructionIdGeneratedCode:
return ir_analyze_instruction_generated_code(ira, (IrInstructionGeneratedCode *)instruction);
case IrInstructionIdImport:
return ir_analyze_instruction_import(ira, (IrInstructionImport *)instruction);
case IrInstructionIdArrayLen:
return ir_analyze_instruction_array_len(ira, (IrInstructionArrayLen *)instruction);
case IrInstructionIdRef:
return ir_analyze_instruction_ref(ira, (IrInstructionRef *)instruction);
case IrInstructionIdContainerInitList:
return ir_analyze_instruction_container_init_list(ira, (IrInstructionContainerInitList *)instruction);
case IrInstructionIdContainerInitFields:
return ir_analyze_instruction_container_init_fields(ira, (IrInstructionContainerInitFields *)instruction);
case IrInstructionIdMinValue:
return ir_analyze_instruction_min_value(ira, (IrInstructionMinValue *)instruction);
case IrInstructionIdMaxValue:
return ir_analyze_instruction_max_value(ira, (IrInstructionMaxValue *)instruction);
case IrInstructionIdCompileErr:
return ir_analyze_instruction_compile_err(ira, (IrInstructionCompileErr *)instruction);
case IrInstructionIdErrName:
return ir_analyze_instruction_err_name(ira, (IrInstructionErrName *)instruction);
case IrInstructionIdTypeName:
return ir_analyze_instruction_type_name(ira, (IrInstructionTypeName *)instruction);
case IrInstructionIdCImport:
return ir_analyze_instruction_c_import(ira, (IrInstructionCImport *)instruction);
case IrInstructionIdCInclude:
return ir_analyze_instruction_c_include(ira, (IrInstructionCInclude *)instruction);
case IrInstructionIdCDefine:
return ir_analyze_instruction_c_define(ira, (IrInstructionCDefine *)instruction);
case IrInstructionIdCUndef:
return ir_analyze_instruction_c_undef(ira, (IrInstructionCUndef *)instruction);
case IrInstructionIdEmbedFile:
return ir_analyze_instruction_embed_file(ira, (IrInstructionEmbedFile *)instruction);
case IrInstructionIdCmpxchg:
return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchg *)instruction);
case IrInstructionIdFence:
return ir_analyze_instruction_fence(ira, (IrInstructionFence *)instruction);
case IrInstructionIdDivExact:
return ir_analyze_instruction_div_exact(ira, (IrInstructionDivExact *)instruction);
case IrInstructionIdTruncate:
return ir_analyze_instruction_truncate(ira, (IrInstructionTruncate *)instruction);
case IrInstructionIdIntType:
return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction);
case IrInstructionIdBoolNot:
return ir_analyze_instruction_bool_not(ira, (IrInstructionBoolNot *)instruction);
case IrInstructionIdAlloca:
return ir_analyze_instruction_alloca(ira, (IrInstructionAlloca *)instruction);
case IrInstructionIdMemset:
return ir_analyze_instruction_memset(ira, (IrInstructionMemset *)instruction);
case IrInstructionIdMemcpy:
return ir_analyze_instruction_memcpy(ira, (IrInstructionMemcpy *)instruction);
case IrInstructionIdSlice:
return ir_analyze_instruction_slice(ira, (IrInstructionSlice *)instruction);
case IrInstructionIdMemberCount:
return ir_analyze_instruction_member_count(ira, (IrInstructionMemberCount *)instruction);
case IrInstructionIdBreakpoint:
return ir_analyze_instruction_breakpoint(ira, (IrInstructionBreakpoint *)instruction);
case IrInstructionIdReturnAddress:
return ir_analyze_instruction_return_address(ira, (IrInstructionReturnAddress *)instruction);
case IrInstructionIdFrameAddress:
return ir_analyze_instruction_frame_address(ira, (IrInstructionFrameAddress *)instruction);
case IrInstructionIdAlignOf:
return ir_analyze_instruction_alignof(ira, (IrInstructionAlignOf *)instruction);
case IrInstructionIdOverflowOp:
return ir_analyze_instruction_overflow_op(ira, (IrInstructionOverflowOp *)instruction);
case IrInstructionIdTestErr:
return ir_analyze_instruction_test_err(ira, (IrInstructionTestErr *)instruction);
case IrInstructionIdUnwrapErrCode:
return ir_analyze_instruction_unwrap_err_code(ira, (IrInstructionUnwrapErrCode *)instruction);
case IrInstructionIdUnwrapErrPayload:
return ir_analyze_instruction_unwrap_err_payload(ira, (IrInstructionUnwrapErrPayload *)instruction);
case IrInstructionIdFnProto:
return ir_analyze_instruction_fn_proto(ira, (IrInstructionFnProto *)instruction);
case IrInstructionIdTestComptime:
return ir_analyze_instruction_test_comptime(ira, (IrInstructionTestComptime *)instruction);
case IrInstructionIdCheckSwitchProngs:
return ir_analyze_instruction_check_switch_prongs(ira, (IrInstructionCheckSwitchProngs *)instruction);
case IrInstructionIdTestType:
return ir_analyze_instruction_test_type(ira, (IrInstructionTestType *)instruction);
case IrInstructionIdCanImplicitCast:
return ir_analyze_instruction_can_implicit_cast(ira, (IrInstructionCanImplicitCast *)instruction);
case IrInstructionIdMaybeWrap:
case IrInstructionIdErrWrapCode:
case IrInstructionIdErrWrapPayload:
case IrInstructionIdCast:
zig_panic("TODO analyze more instructions");
}
zig_unreachable();
}
static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction) {
TypeTableEntry *instruction_type = ir_analyze_instruction_nocast(ira, instruction);
instruction->value.type = instruction_type;
if (instruction->other) {
instruction->other->value.type = instruction_type;
} else {
assert(instruction_type->id == TypeTableEntryIdInvalid ||
instruction_type->id == TypeTableEntryIdUnreachable);
instruction->other = instruction;
}
return instruction_type;
}
// This function attempts to evaluate IR code while doing type checking and other analysis.
// It emits a new IrExecutable which is partially evaluated IR code.
TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_exec,
TypeTableEntry *expected_type, AstNode *expected_type_source_node)
{
assert(!old_exec->invalid);
IrAnalyze ir_analyze_data = {};
IrAnalyze *ira = &ir_analyze_data;
ira->codegen = codegen;
ira->explicit_return_type = expected_type;
ira->old_irb.codegen = codegen;
ira->old_irb.exec = old_exec;
ira->new_irb.codegen = codegen;
ira->new_irb.exec = new_exec;
ira->exec_context.mem_slot_count = ira->old_irb.exec->mem_slot_count;
ira->exec_context.mem_slot_list = allocate<ConstExprValue>(ira->exec_context.mem_slot_count);
IrBasicBlock *old_entry_bb = ira->old_irb.exec->basic_block_list.at(0);
IrBasicBlock *new_entry_bb = ir_get_new_bb(ira, old_entry_bb, nullptr);
ir_ref_bb(new_entry_bb);
ira->new_irb.current_basic_block = new_entry_bb;
ira->block_queue_index = 0;
ir_start_bb(ira, old_entry_bb, nullptr);
while (ira->block_queue_index < ira->old_bb_queue.length) {
IrInstruction *old_instruction = ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index);
if (old_instruction->ref_count == 0 && !ir_has_side_effects(old_instruction)) {
ira->instruction_index += 1;
continue;
}
TypeTableEntry *return_type = ir_analyze_instruction(ira, old_instruction);
// unreachable instructions do their own control flow.
if (return_type->id == TypeTableEntryIdUnreachable)
continue;
ira->instruction_index += 1;
}
if (new_exec->invalid) {
return ira->codegen->builtin_types.entry_invalid;
} else if (ira->implicit_return_type_list.length == 0) {
return codegen->builtin_types.entry_unreachable;
} else {
return ir_resolve_peer_types(ira, expected_type_source_node, ira->implicit_return_type_list.items,
ira->implicit_return_type_list.length);
}
}
bool ir_has_side_effects(IrInstruction *instruction) {
switch (instruction->id) {
case IrInstructionIdInvalid:
zig_unreachable();
case IrInstructionIdBr:
case IrInstructionIdCondBr:
case IrInstructionIdSwitchBr:
case IrInstructionIdDeclVar:
case IrInstructionIdStorePtr:
case IrInstructionIdCall:
case IrInstructionIdReturn:
case IrInstructionIdUnreachable:
case IrInstructionIdSetFnTest:
case IrInstructionIdSetFnVisible:
case IrInstructionIdSetDebugSafety:
case IrInstructionIdImport:
case IrInstructionIdCompileErr:
case IrInstructionIdCImport:
case IrInstructionIdCInclude:
case IrInstructionIdCDefine:
case IrInstructionIdCUndef:
case IrInstructionIdCmpxchg:
case IrInstructionIdFence:
case IrInstructionIdMemset:
case IrInstructionIdMemcpy:
case IrInstructionIdBreakpoint:
case IrInstructionIdOverflowOp: // TODO when we support multiple returns this can be side effect free
case IrInstructionIdCheckSwitchProngs:
return true;
case IrInstructionIdPhi:
case IrInstructionIdUnOp:
case IrInstructionIdBinOp:
case IrInstructionIdLoadPtr:
case IrInstructionIdConst:
case IrInstructionIdCast:
case IrInstructionIdContainerInitList:
case IrInstructionIdContainerInitFields:
case IrInstructionIdStructInit:
case IrInstructionIdFieldPtr:
case IrInstructionIdElemPtr:
case IrInstructionIdVarPtr:
case IrInstructionIdTypeOf:
case IrInstructionIdToPtrType:
case IrInstructionIdPtrTypeChild:
case IrInstructionIdArrayLen:
case IrInstructionIdStructFieldPtr:
case IrInstructionIdEnumFieldPtr:
case IrInstructionIdArrayType:
case IrInstructionIdSliceType:
case IrInstructionIdCompileVar:
case IrInstructionIdSizeOf:
case IrInstructionIdTestNonNull:
case IrInstructionIdUnwrapMaybe:
case IrInstructionIdClz:
case IrInstructionIdCtz:
case IrInstructionIdSwitchVar:
case IrInstructionIdSwitchTarget:
case IrInstructionIdEnumTag:
case IrInstructionIdGeneratedCode:
case IrInstructionIdRef:
case IrInstructionIdMinValue:
case IrInstructionIdMaxValue:
case IrInstructionIdErrName:
case IrInstructionIdEmbedFile:
case IrInstructionIdDivExact:
case IrInstructionIdTruncate:
case IrInstructionIdIntType:
case IrInstructionIdBoolNot:
case IrInstructionIdAlloca:
case IrInstructionIdSlice:
case IrInstructionIdMemberCount:
case IrInstructionIdAlignOf:
case IrInstructionIdReturnAddress:
case IrInstructionIdFrameAddress:
case IrInstructionIdTestErr:
case IrInstructionIdUnwrapErrCode:
case IrInstructionIdUnwrapErrPayload:
case IrInstructionIdMaybeWrap:
case IrInstructionIdErrWrapCode:
case IrInstructionIdErrWrapPayload:
case IrInstructionIdFnProto:
case IrInstructionIdTestComptime:
case IrInstructionIdInitEnum:
case IrInstructionIdPointerReinterpret:
case IrInstructionIdWidenOrShorten:
case IrInstructionIdPtrToInt:
case IrInstructionIdIntToPtr:
case IrInstructionIdIntToEnum:
case IrInstructionIdTestType:
case IrInstructionIdTypeName:
case IrInstructionIdCanImplicitCast:
return false;
case IrInstructionIdAsm:
{
IrInstructionAsm *asm_instruction = (IrInstructionAsm *)instruction;
return asm_instruction->has_side_effects;
}
}
zig_unreachable();
}
FnTableEntry *ir_create_inline_fn(CodeGen *codegen, Buf *fn_name, VariableTableEntry *var, Scope *parent_scope) {
FnTableEntry *fn_entry = create_fn_raw(FnInlineAuto, true);
buf_init_from_buf(&fn_entry->symbol_name, fn_name);
fn_entry->fndef_scope = create_fndef_scope(nullptr, parent_scope, fn_entry);
fn_entry->child_scope = &fn_entry->fndef_scope->base;
assert(var->value.type->id == TypeTableEntryIdMaybe);
TypeTableEntry *src_fn_type = var->value.type->data.maybe.child_type;
assert(src_fn_type->id == TypeTableEntryIdFn);
FnTypeId new_fn_type = src_fn_type->data.fn.fn_type_id;
new_fn_type.is_extern = false;
fn_entry->type_entry = get_fn_type(codegen, &new_fn_type);
IrBuilder ir_builder = {0};
IrBuilder *irb = &ir_builder;
irb->codegen = codegen;
irb->exec = &fn_entry->ir_executable;
AstNode *source_node = parent_scope->source_node;
size_t arg_count = fn_entry->type_entry->data.fn.fn_type_id.param_count;
IrInstruction **args = allocate<IrInstruction *>(arg_count);
VariableTableEntry **arg_vars = allocate<VariableTableEntry *>(arg_count);
define_local_param_variables(codegen, fn_entry, arg_vars);
Scope *scope = fn_entry->child_scope;
irb->current_basic_block = ir_build_basic_block(irb, scope, "Entry");
// Entry block gets a reference because we enter it to begin.
ir_ref_bb(irb->current_basic_block);
IrInstruction *maybe_fn_ptr = ir_build_var_ptr(irb, scope, source_node, var, true);
IrInstruction *unwrapped_fn_ptr = ir_build_unwrap_maybe(irb, scope, source_node, maybe_fn_ptr, true);
IrInstruction *fn_ref_instruction = ir_build_load_ptr(irb, scope, source_node, unwrapped_fn_ptr);
for (size_t i = 0; i < arg_count; i += 1) {
IrInstruction *var_ptr_instruction = ir_build_var_ptr(irb, scope, source_node, arg_vars[i], true);
args[i] = ir_build_load_ptr(irb, scope, source_node, var_ptr_instruction);
}
IrInstruction *call_instruction = ir_build_call(irb, scope, source_node, nullptr, fn_ref_instruction,
arg_count, args, false);
ir_build_return(irb, scope, source_node, call_instruction);
if (codegen->verbose) {
fprintf(stderr, "{\n");
ir_print(stderr, &fn_entry->ir_executable, 4);
fprintf(stderr, "}\n");
}
analyze_fn_ir(codegen, fn_entry, nullptr);
codegen->fn_defs.append(fn_entry);
return fn_entry;
}