zig/src/ir.cpp

23468 lines
1.0 MiB

/*
* 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 "range_set.hpp"
#include "softfloat.hpp"
#include "translate_c.hpp"
#include "util.hpp"
#include <errno.h>
struct IrExecContext {
ZigList<ConstExprValue *> mem_slot_list;
};
struct IrBuilder {
CodeGen *codegen;
IrExecutable *exec;
IrBasicBlock *current_basic_block;
};
struct IrAnalyze {
CodeGen *codegen;
IrBuilder old_irb;
IrBuilder new_irb;
IrExecContext exec_context;
size_t old_bb_index;
size_t instruction_index;
ZigType *explicit_return_type;
AstNode *explicit_return_type_source_node;
ZigList<IrInstruction *> src_implicit_return_type_list;
IrBasicBlock *const_predecessor_bb;
};
enum ConstCastResultId {
ConstCastResultIdOk,
ConstCastResultIdInvalid,
ConstCastResultIdErrSet,
ConstCastResultIdErrSetGlobal,
ConstCastResultIdPointerChild,
ConstCastResultIdSliceChild,
ConstCastResultIdOptionalChild,
ConstCastResultIdErrorUnionPayload,
ConstCastResultIdErrorUnionErrorSet,
ConstCastResultIdFnAlign,
ConstCastResultIdFnCC,
ConstCastResultIdFnVarArgs,
ConstCastResultIdFnIsGeneric,
ConstCastResultIdFnReturnType,
ConstCastResultIdFnArgCount,
ConstCastResultIdFnGenericArgCount,
ConstCastResultIdFnArg,
ConstCastResultIdFnArgNoAlias,
ConstCastResultIdType,
ConstCastResultIdUnresolvedInferredErrSet,
ConstCastResultIdAsyncAllocatorType,
ConstCastResultIdBadAllowsZero,
};
struct ConstCastOnly;
struct ConstCastArg {
size_t arg_index;
ZigType *actual_param_type;
ZigType *expected_param_type;
ConstCastOnly *child;
};
struct ConstCastArgNoAlias {
size_t arg_index;
};
struct ConstCastOptionalMismatch;
struct ConstCastPointerMismatch;
struct ConstCastSliceMismatch;
struct ConstCastErrUnionErrSetMismatch;
struct ConstCastErrUnionPayloadMismatch;
struct ConstCastErrSetMismatch;
struct ConstCastTypeMismatch;
struct ConstCastBadAllowsZero;
struct ConstCastOnly {
ConstCastResultId id;
union {
ConstCastErrSetMismatch *error_set_mismatch;
ConstCastPointerMismatch *pointer_mismatch;
ConstCastSliceMismatch *slice_mismatch;
ConstCastOptionalMismatch *optional;
ConstCastErrUnionPayloadMismatch *error_union_payload;
ConstCastErrUnionErrSetMismatch *error_union_error_set;
ConstCastTypeMismatch *type_mismatch;
ConstCastOnly *return_type;
ConstCastOnly *async_allocator_type;
ConstCastOnly *null_wrap_ptr_child;
ConstCastArg fn_arg;
ConstCastArgNoAlias arg_no_alias;
ConstCastBadAllowsZero *bad_allows_zero;
} data;
};
struct ConstCastTypeMismatch {
ZigType *wanted_type;
ZigType *actual_type;
};
struct ConstCastOptionalMismatch {
ConstCastOnly child;
ZigType *wanted_child;
ZigType *actual_child;
};
struct ConstCastPointerMismatch {
ConstCastOnly child;
ZigType *wanted_child;
ZigType *actual_child;
};
struct ConstCastSliceMismatch {
ConstCastOnly child;
ZigType *wanted_child;
ZigType *actual_child;
};
struct ConstCastErrUnionErrSetMismatch {
ConstCastOnly child;
ZigType *wanted_err_set;
ZigType *actual_err_set;
};
struct ConstCastErrUnionPayloadMismatch {
ConstCastOnly child;
ZigType *wanted_payload;
ZigType *actual_payload;
};
struct ConstCastErrSetMismatch {
ZigList<ErrorTableEntry *> missing_errors;
};
struct ConstCastBadAllowsZero {
ZigType *wanted_type;
ZigType *actual_type;
};
enum UndefAllowed {
UndefOk,
UndefBad,
};
static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope);
static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval);
static IrInstruction *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction);
static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, ZigType *expected_type);
static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr);
static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutable *exec, AstNode *source_node, Buf *msg);
static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name,
IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type);
static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ZigVar *var);
static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op);
static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval);
static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align);
static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align);
static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ConstExprValue *val);
static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val);
static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node,
ConstExprValue *out_val, ConstExprValue *ptr_val);
static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr,
ZigType *dest_type, IrInstruction *dest_type_src, bool safety_check_on);
static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed);
static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs);
static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align);
static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, ZigType *type_entry);
static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
ZigType *ptr_type);
static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
ZigType *dest_type);
static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) {
assert(get_src_ptr_type(const_val->type) != nullptr);
assert(const_val->special == ConstValSpecialStatic);
ConstExprValue *result;
switch (type_has_one_possible_value(g, const_val->type->data.pointer.child_type)) {
case OnePossibleValueInvalid:
zig_unreachable();
case OnePossibleValueYes:
result = create_const_vals(1);
result->type = const_val->type->data.pointer.child_type;
result->special = ConstValSpecialStatic;
return result;
case OnePossibleValueNo:
break;
}
switch (const_val->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
zig_unreachable();
case ConstPtrSpecialRef:
result = const_val->data.x_ptr.data.ref.pointee;
break;
case ConstPtrSpecialBaseArray: {
ConstExprValue *array_val = const_val->data.x_ptr.data.base_array.array_val;
expand_undef_array(g, array_val);
result = &array_val->data.x_array.data.s_none.elements[const_val->data.x_ptr.data.base_array.elem_index];
break;
}
case ConstPtrSpecialBaseStruct: {
ConstExprValue *struct_val = const_val->data.x_ptr.data.base_struct.struct_val;
result = &struct_val->data.x_struct.fields[const_val->data.x_ptr.data.base_struct.field_index];
break;
}
case ConstPtrSpecialBaseErrorUnionCode:
result = const_val->data.x_ptr.data.base_err_union_code.err_union_val->data.x_err_union.error_set;
break;
case ConstPtrSpecialBaseErrorUnionPayload:
result = const_val->data.x_ptr.data.base_err_union_payload.err_union_val->data.x_err_union.payload;
break;
case ConstPtrSpecialBaseOptionalPayload:
result = const_val->data.x_ptr.data.base_optional_payload.optional_val->data.x_optional;
break;
case ConstPtrSpecialNull:
result = const_val;
break;
case ConstPtrSpecialHardCodedAddr:
zig_unreachable();
case ConstPtrSpecialDiscard:
zig_unreachable();
case ConstPtrSpecialFunction:
zig_unreachable();
}
assert(result != nullptr);
return result;
}
static bool is_opt_err_set(ZigType *ty) {
return ty->id == ZigTypeIdErrorSet ||
(ty->id == ZigTypeIdOptional && ty->data.maybe.child_type->id == ZigTypeIdErrorSet);
}
// This function returns true when you can change the type of a ConstExprValue and the
// value remains meaningful.
static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) {
if (a == b)
return true;
if (get_codegen_ptr_type(a) != nullptr && get_codegen_ptr_type(b) != nullptr)
return true;
if (is_opt_err_set(a) && is_opt_err_set(b))
return true;
if (a->id != b->id)
return false;
switch (a->id) {
case ZigTypeIdInvalid:
case ZigTypeIdUnreachable:
zig_unreachable();
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdPointer:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdBoundFn:
case ZigTypeIdErrorSet:
case ZigTypeIdOpaque:
return true;
case ZigTypeIdFloat:
return a->data.floating.bit_count == b->data.floating.bit_count;
case ZigTypeIdInt:
return a->data.integral.is_signed == b->data.integral.is_signed;
case ZigTypeIdArray:
case ZigTypeIdStruct:
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdEnum:
case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdArgTuple:
case ZigTypeIdPromise:
case ZigTypeIdVector:
return false;
}
zig_unreachable();
}
static bool ir_should_inline(IrExecutable *exec, Scope *scope) {
if (exec->is_inline)
return true;
while (scope != nullptr) {
if (scope->id == ScopeIdCompTime)
return true;
if (scope->id == ScopeIdFnDef)
break;
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 ZigFn *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 == ZigTypeIdUnreachable;
}
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(ZigVar *var) {
var->ref_count += 1;
}
ZigType *ir_analyze_type_expr(IrAnalyze *ira, Scope *scope, AstNode *node) {
ConstExprValue *result = ir_eval_const_value(ira->codegen, scope, node, ira->codegen->builtin_types.entry_type,
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, nullptr,
node, nullptr, ira->new_irb.exec, nullptr);
if (type_is_invalid(result->type))
return ira->codegen->builtin_types.entry_invalid;
assert(result->special != ConstValSpecialRuntime);
return result->data.x_type;
}
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_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(IrInstructionDeclVarSrc *) {
return IrInstructionIdDeclVarSrc;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVarGen *) {
return IrInstructionIdDeclVarGen;
}
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(IrInstructionExport *) {
return IrInstructionIdExport;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadPtr *) {
return IrInstructionIdLoadPtr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadPtrGen *) {
return IrInstructionIdLoadPtrGen;
}
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(IrInstructionUnionFieldPtr *) {
return IrInstructionIdUnionFieldPtr;
}
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(IrInstructionResizeSlice *) {
return IrInstructionIdResizeSlice;
}
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(IrInstructionSetCold *) {
return IrInstructionIdSetCold;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSetRuntimeSafety *) {
return IrInstructionIdSetRuntimeSafety;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSetFloatMode *) {
return IrInstructionIdSetFloatMode;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayType *) {
return IrInstructionIdArrayType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionPromiseType *) {
return IrInstructionIdPromiseType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSliceType *) {
return IrInstructionIdSliceType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionGlobalAsm *) {
return IrInstructionIdGlobalAsm;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAsm *) {
return IrInstructionIdAsm;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSizeOf *) {
return IrInstructionIdSizeOf;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionTestNonNull *) {
return IrInstructionIdTestNonNull;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionOptionalUnwrapPtr *) {
return IrInstructionIdOptionalUnwrapPtr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionClz *) {
return IrInstructionIdClz;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCtz *) {
return IrInstructionIdCtz;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionPopCount *) {
return IrInstructionIdPopCount;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionTag *) {
return IrInstructionIdUnionTag;
}
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(IrInstructionRef *) {
return IrInstructionIdRef;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionStructInit *) {
return IrInstructionIdStructInit;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionInit *) {
return IrInstructionIdUnionInit;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCompileErr *) {
return IrInstructionIdCompileErr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCompileLog *) {
return IrInstructionIdCompileLog;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionErrName *) {
return IrInstructionIdErrName;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionEmbedFile *) {
return IrInstructionIdEmbedFile;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchgSrc *) {
return IrInstructionIdCmpxchgSrc;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchgGen *) {
return IrInstructionIdCmpxchgGen;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionFence *) {
return IrInstructionIdFence;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionTruncate *) {
return IrInstructionIdTruncate;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionIntCast *) {
return IrInstructionIdIntCast;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatCast *) {
return IrInstructionIdFloatCast;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionErrSetCast *) {
return IrInstructionIdErrSetCast;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionToBytes *) {
return IrInstructionIdToBytes;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionFromBytes *) {
return IrInstructionIdFromBytes;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToFloat *) {
return IrInstructionIdIntToFloat;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatToInt *) {
return IrInstructionIdFloatToInt;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolToInt *) {
return IrInstructionIdBoolToInt;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionIntType *) {
return IrInstructionIdIntType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorType *) {
return IrInstructionIdVectorType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolNot *) {
return IrInstructionIdBoolNot;
}
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(IrInstructionMemberType *) {
return IrInstructionIdMemberType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionMemberName *) {
return IrInstructionIdMemberName;
}
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(IrInstructionHandle *) {
return IrInstructionIdHandle;
}
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(IrInstructionOptionalWrap *) {
return IrInstructionIdOptionalWrap;
}
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(IrInstructionPtrCastSrc *) {
return IrInstructionIdPtrCastSrc;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCastGen *) {
return IrInstructionIdPtrCastGen;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionBitCast *) {
return IrInstructionIdBitCast;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionBitCastGen *) {
return IrInstructionIdBitCastGen;
}
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(IrInstructionEnumToInt *) {
return IrInstructionIdEnumToInt;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToErr *) {
return IrInstructionIdIntToErr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionErrToInt *) {
return IrInstructionIdErrToInt;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckSwitchProngs *) {
return IrInstructionIdCheckSwitchProngs;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckStatementIsVoid *) {
return IrInstructionIdCheckStatementIsVoid;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeName *) {
return IrInstructionIdTypeName;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclRef *) {
return IrInstructionIdDeclRef;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionPanic *) {
return IrInstructionIdPanic;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionTagName *) {
return IrInstructionIdTagName;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionTagType *) {
return IrInstructionIdTagType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionFieldParentPtr *) {
return IrInstructionIdFieldParentPtr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionByteOffsetOf *) {
return IrInstructionIdByteOffsetOf;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionBitOffsetOf *) {
return IrInstructionIdBitOffsetOf;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeInfo *) {
return IrInstructionIdTypeInfo;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeId *) {
return IrInstructionIdTypeId;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSetEvalBranchQuota *) {
return IrInstructionIdSetEvalBranchQuota;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrType *) {
return IrInstructionIdPtrType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignCast *) {
return IrInstructionIdAlignCast;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionOpaqueType *) {
return IrInstructionIdOpaqueType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSetAlignStack *) {
return IrInstructionIdSetAlignStack;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionArgType *) {
return IrInstructionIdArgType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorReturnTrace *) {
return IrInstructionIdErrorReturnTrace;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorUnion *) {
return IrInstructionIdErrorUnion;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCancel *) {
return IrInstructionIdCancel;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionGetImplicitAllocator *) {
return IrInstructionIdGetImplicitAllocator;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroId *) {
return IrInstructionIdCoroId;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroAlloc *) {
return IrInstructionIdCoroAlloc;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroSize *) {
return IrInstructionIdCoroSize;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroBegin *) {
return IrInstructionIdCoroBegin;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroAllocFail *) {
return IrInstructionIdCoroAllocFail;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroSuspend *) {
return IrInstructionIdCoroSuspend;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroEnd *) {
return IrInstructionIdCoroEnd;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroFree *) {
return IrInstructionIdCoroFree;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroResume *) {
return IrInstructionIdCoroResume;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroSave *) {
return IrInstructionIdCoroSave;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroPromise *) {
return IrInstructionIdCoroPromise;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroAllocHelper *) {
return IrInstructionIdCoroAllocHelper;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicRmw *) {
return IrInstructionIdAtomicRmw;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicLoad *) {
return IrInstructionIdAtomicLoad;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionPromiseResultType *) {
return IrInstructionIdPromiseResultType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAwaitBookkeeping *) {
return IrInstructionIdAwaitBookkeeping;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSaveErrRetAddr *) {
return IrInstructionIdSaveErrRetAddr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAddImplicitReturnType *) {
return IrInstructionIdAddImplicitReturnType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionMergeErrRetTraces *) {
return IrInstructionIdMergeErrRetTraces;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTracePtr *) {
return IrInstructionIdMarkErrRetTracePtr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSqrt *) {
return IrInstructionIdSqrt;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionBswap *) {
return IrInstructionIdBswap;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionBitReverse *) {
return IrInstructionIdBitReverse;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScope *) {
return IrInstructionIdCheckRuntimeScope;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorToArray *) {
return IrInstructionIdVectorToArray;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayToVector *) {
return IrInstructionIdArrayToVector;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertZero *) {
return IrInstructionIdAssertZero;
}
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;
special_instruction->base.value.global_refs = allocate<ConstGlobalRefs>(1);
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, ZigType *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 != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block);
return &cond_br_instruction->base;
}
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_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;
bigint_init_unsigned(&const_instruction->base.value.data.x_bigint, value);
return &const_instruction->base;
}
static IrInstruction *ir_build_const_bigint(IrBuilder *irb, Scope *scope, AstNode *source_node, BigInt *bigint) {
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;
bigint_init_bigint(&const_instruction->base.value.data.x_bigint, bigint);
return &const_instruction->base;
}
static IrInstruction *ir_build_const_bigfloat(IrBuilder *irb, Scope *scope, AstNode *source_node, BigFloat *bigfloat) {
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
const_instruction->base.value.type = irb->codegen->builtin_types.entry_num_lit_float;
const_instruction->base.value.special = ConstValSpecialStatic;
bigfloat_init_bigfloat(&const_instruction->base.value.data.x_bigfloat, bigfloat);
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;
bigint_init_unsigned(&const_instruction->base.value.data.x_bigint, value);
return &const_instruction->base;
}
static IrInstruction *ir_build_const_u8(IrBuilder *irb, Scope *scope, AstNode *source_node, uint8_t value) {
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
const_instruction->base.value.type = irb->codegen->builtin_types.entry_u8;
const_instruction->base.value.special = ConstValSpecialStatic;
bigint_init_unsigned(&const_instruction->base.value.data.x_bigint, value);
return &const_instruction->base;
}
static IrInstruction *ir_create_const_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
ZigType *type_entry)
{
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.data.x_type = type_entry;
return &const_instruction->base;
}
static IrInstruction *ir_build_const_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
ZigType *type_entry)
{
IrInstruction *instruction = ir_create_const_type(irb, scope, source_node, type_entry);
ir_instruction_append(irb->current_basic_block, instruction);
return instruction;
}
static IrInstruction *ir_create_const_fn(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigFn *fn_entry) {
IrInstructionConst *const_instruction = ir_create_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_ptr.data.fn.fn_entry = fn_entry;
const_instruction->base.value.data.x_ptr.mut = ConstPtrMutComptimeConst;
const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialFunction;
return &const_instruction->base;
}
static IrInstruction *ir_build_const_import(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigType *import) {
IrInstructionConst *const_instruction = ir_build_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.data.x_type = import;
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_enum_literal(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *name) {
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
const_instruction->base.value.type = irb->codegen->builtin_types.entry_enum_literal;
const_instruction->base.value.special = ConstValSpecialStatic;
const_instruction->base.value.data.x_enum_literal = name;
return &const_instruction->base;
}
static IrInstruction *ir_build_const_bound_fn(IrBuilder *irb, Scope *scope, AstNode *source_node,
ZigFn *fn_entry, IrInstruction *first_arg)
{
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.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_var_ptr_x(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var,
ScopeFnDef *crossed_fndef_scope)
{
IrInstructionVarPtr *instruction = ir_build_instruction<IrInstructionVarPtr>(irb, scope, source_node);
instruction->var = var;
instruction->crossed_fndef_scope = crossed_fndef_scope;
ir_ref_var(var);
return &instruction->base;
}
static IrInstruction *ir_build_var_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var) {
return ir_build_var_ptr_x(irb, scope, source_node, var, nullptr);
}
static IrInstruction *ir_build_elem_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *array_ptr,
IrInstruction *elem_index, bool safety_check_on, PtrLen ptr_len)
{
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;
instruction->ptr_len = ptr_len;
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_field_ptr_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *container_ptr, IrInstruction *field_name_expr)
{
IrInstructionFieldPtr *instruction = ir_build_instruction<IrInstructionFieldPtr>(irb, scope, source_node);
instruction->container_ptr = container_ptr;
instruction->field_name_buffer = nullptr;
instruction->field_name_expr = field_name_expr;
ir_ref_instruction(container_ptr, irb->current_basic_block);
ir_ref_instruction(field_name_expr, irb->current_basic_block);
return &instruction->base;
}
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_buffer = field_name;
instruction->field_name_expr = nullptr;
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_union_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *union_ptr, TypeUnionField *field)
{
IrInstructionUnionFieldPtr *instruction = ir_build_instruction<IrInstructionUnionFieldPtr>(irb, scope, source_node);
instruction->union_ptr = union_ptr;
instruction->field = field;
ir_ref_instruction(union_ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node,
ZigFn *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,
bool is_comptime, FnInline fn_inline, bool is_async, IrInstruction *async_allocator,
IrInstruction *new_stack)
{
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->fn_inline = fn_inline;
call_instruction->args = args;
call_instruction->arg_count = arg_count;
call_instruction->is_async = is_async;
call_instruction->async_allocator = async_allocator;
call_instruction->new_stack = new_stack;
if (fn_ref != nullptr) 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);
if (async_allocator != nullptr) ir_ref_instruction(async_allocator, irb->current_basic_block);
if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block);
return &call_instruction->base;
}
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_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_ptr_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *child_type, bool is_const, bool is_volatile, PtrLen ptr_len,
IrInstruction *align_value, uint32_t bit_offset_start, uint32_t host_int_bytes, bool is_allow_zero)
{
IrInstructionPtrType *ptr_type_of_instruction = ir_build_instruction<IrInstructionPtrType>(irb, scope, source_node);
ptr_type_of_instruction->align_value = align_value;
ptr_type_of_instruction->child_type = child_type;
ptr_type_of_instruction->is_const = is_const;
ptr_type_of_instruction->is_volatile = is_volatile;
ptr_type_of_instruction->ptr_len = ptr_len;
ptr_type_of_instruction->bit_offset_start = bit_offset_start;
ptr_type_of_instruction->host_int_bytes = host_int_bytes;
ptr_type_of_instruction->is_allow_zero = is_allow_zero;
if (align_value) ir_ref_instruction(align_value, irb->current_basic_block);
ir_ref_instruction(child_type, irb->current_basic_block);
return &ptr_type_of_instruction->base;
}
static IrInstruction *ir_build_un_op_lval(IrBuilder *irb, Scope *scope, AstNode *source_node, IrUnOp op_id,
IrInstruction *value, LVal lval)
{
IrInstructionUnOp *instruction = ir_build_instruction<IrInstructionUnOp>(irb, scope, source_node);
instruction->op_id = op_id;
instruction->value = value;
instruction->lval = lval;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_un_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrUnOp op_id,
IrInstruction *value)
{
return ir_build_un_op_lval(irb, scope, source_node, op_id, value, LValNone);
}
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;
if (container_type != nullptr) 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_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,
ZigType *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_union_init(IrBuilder *irb, Scope *scope, AstNode *source_node,
ZigType *union_type, TypeUnionField *field, IrInstruction *init_value)
{
IrInstructionUnionInit *union_init_instruction = ir_build_instruction<IrInstructionUnionInit>(irb, scope, source_node);
union_init_instruction->union_type = union_type;
union_init_instruction->field = field;
union_init_instruction->init_value = init_value;
ir_ref_instruction(init_value, irb->current_basic_block);
return &union_init_instruction->base;
}
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_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_var_decl_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
ZigVar *var, IrInstruction *var_type, IrInstruction *align_value, IrInstruction *init_value)
{
IrInstructionDeclVarSrc *decl_var_instruction = ir_build_instruction<IrInstructionDeclVarSrc>(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->align_value = align_value;
decl_var_instruction->init_value = init_value;
if (var_type != nullptr) ir_ref_instruction(var_type, irb->current_basic_block);
if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
ir_ref_instruction(init_value, irb->current_basic_block);
return &decl_var_instruction->base;
}
static IrInstruction *ir_build_var_decl_gen(IrAnalyze *ira, IrInstruction *source_instruction,
ZigVar *var, IrInstruction *init_value)
{
IrInstructionDeclVarGen *decl_var_instruction = ir_build_instruction<IrInstructionDeclVarGen>(&ira->new_irb,
source_instruction->scope, source_instruction->source_node);
decl_var_instruction->base.value.special = ConstValSpecialStatic;
decl_var_instruction->base.value.type = ira->codegen->builtin_types.entry_void;
decl_var_instruction->var = var;
decl_var_instruction->init_value = init_value;
ir_ref_instruction(init_value, ira->new_irb.current_basic_block);
return &decl_var_instruction->base;
}
static IrInstruction *ir_build_resize_slice(IrAnalyze *ira, IrInstruction *source_instruction,
IrInstruction *operand, ZigType *ty)
{
IrInstructionResizeSlice *instruction = ir_build_instruction<IrInstructionResizeSlice>(&ira->new_irb,
source_instruction->scope, source_instruction->source_node);
instruction->base.value.type = ty;
instruction->operand = operand;
ir_ref_instruction(operand, ira->new_irb.current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_export(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *name, IrInstruction *target, IrInstruction *linkage)
{
IrInstructionExport *export_instruction = ir_build_instruction<IrInstructionExport>(
irb, scope, source_node);
export_instruction->base.value.special = ConstValSpecialStatic;
export_instruction->base.value.type = irb->codegen->builtin_types.entry_void;
export_instruction->name = name;
export_instruction->target = target;
export_instruction->linkage = linkage;
ir_ref_instruction(name, irb->current_basic_block);
ir_ref_instruction(target, irb->current_basic_block);
if (linkage) ir_ref_instruction(linkage, irb->current_basic_block);
return &export_instruction->base;
}
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_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 *ptr) {
IrInstructionToPtrType *instruction = ir_build_instruction<IrInstructionToPtrType>(irb, scope, source_node);
instruction->ptr = ptr;
ir_ref_instruction(ptr, 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_cold(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *is_cold) {
IrInstructionSetCold *instruction = ir_build_instruction<IrInstructionSetCold>(irb, scope, source_node);
instruction->is_cold = is_cold;
ir_ref_instruction(is_cold, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_set_runtime_safety(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *safety_on)
{
IrInstructionSetRuntimeSafety *instruction = ir_build_instruction<IrInstructionSetRuntimeSafety>(irb, scope, source_node);
instruction->safety_on = safety_on;
ir_ref_instruction(safety_on, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_set_float_mode(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *mode_value)
{
IrInstructionSetFloatMode *instruction = ir_build_instruction<IrInstructionSetFloatMode>(irb, scope, source_node);
instruction->mode_value = mode_value;
ir_ref_instruction(mode_value, 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_promise_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *payload_type)
{
IrInstructionPromiseType *instruction = ir_build_instruction<IrInstructionPromiseType>(irb, scope, source_node);
instruction->payload_type = payload_type;
if (payload_type != nullptr) ir_ref_instruction(payload_type, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_slice_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *child_type, bool is_const, bool is_volatile, IrInstruction *align_value, bool is_allow_zero)
{
IrInstructionSliceType *instruction = ir_build_instruction<IrInstructionSliceType>(irb, scope, source_node);
instruction->is_const = is_const;
instruction->is_volatile = is_volatile;
instruction->child_type = child_type;
instruction->align_value = align_value;
instruction->is_allow_zero = is_allow_zero;
ir_ref_instruction(child_type, irb->current_basic_block);
if (align_value) ir_ref_instruction(align_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_global_asm(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *asm_code) {
IrInstructionGlobalAsm *instruction = ir_build_instruction<IrInstructionGlobalAsm>(irb, scope, source_node);
instruction->asm_code = asm_code;
return &instruction->base;
}
static IrInstruction *ir_build_asm(IrBuilder *irb, Scope *scope, AstNode *source_node,
Buf *asm_template, AsmToken *token_list, size_t token_list_len,
IrInstruction **input_list, IrInstruction **output_types, ZigVar **output_vars, size_t return_count,
bool has_side_effects)
{
IrInstructionAsm *instruction = ir_build_instruction<IrInstructionAsm>(irb, scope, source_node);
instruction->asm_template = asm_template;
instruction->token_list = token_list;
instruction->token_list_len = token_list_len;
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_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_optional_unwrap_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *base_ptr, bool safety_check_on)
{
IrInstructionOptionalUnwrapPtr *instruction = ir_build_instruction<IrInstructionOptionalUnwrapPtr>(irb, scope, source_node);
instruction->base_ptr = base_ptr;
instruction->safety_check_on = safety_check_on;
ir_ref_instruction(base_ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_maybe_wrap(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionOptionalWrap *instruction = ir_build_instruction<IrInstructionOptionalWrap>(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_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_pop_count(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionPopCount *instruction = ir_build_instruction<IrInstructionPopCount>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
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,
IrInstruction *switch_prongs_void)
{
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;
instruction->switch_prongs_void = switch_prongs_void;
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);
if (switch_prongs_void) ir_ref_instruction(switch_prongs_void, irb->current_basic_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_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_union_tag(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionUnionTag *instruction = ir_build_instruction<IrInstructionUnionTag>(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_ref(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value,
bool is_const, bool is_volatile)
{
IrInstructionRef *instruction = ir_build_instruction<IrInstructionRef>(irb, scope, source_node);
instruction->value = value;
instruction->is_const = is_const;
instruction->is_volatile = is_volatile;
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_compile_log(IrBuilder *irb, Scope *scope, AstNode *source_node,
size_t msg_count, IrInstruction **msg_list)
{
IrInstructionCompileLog *instruction = ir_build_instruction<IrInstructionCompileLog>(irb, scope, source_node);
instruction->msg_count = msg_count;
instruction->msg_list = msg_list;
for (size_t i = 0; i < msg_count; i += 1) {
ir_ref_instruction(msg_list[i], 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_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_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *type_value, IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value,
IrInstruction *success_order_value, IrInstruction *failure_order_value,
bool is_weak)
{
IrInstructionCmpxchgSrc *instruction = ir_build_instruction<IrInstructionCmpxchgSrc>(irb, scope, source_node);
instruction->type_value = type_value;
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->is_weak = is_weak;
ir_ref_instruction(type_value, irb->current_basic_block);
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_gen(IrAnalyze *ira, IrInstruction *source_instruction,
IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value,
AtomicOrder success_order, AtomicOrder failure_order, bool is_weak)
{
IrInstructionCmpxchgGen *instruction = ir_build_instruction<IrInstructionCmpxchgGen>(&ira->new_irb,
source_instruction->scope, source_instruction->source_node);
instruction->ptr = ptr;
instruction->cmp_value = cmp_value;
instruction->new_value = new_value;
instruction->success_order = success_order;
instruction->failure_order = failure_order;
instruction->is_weak = is_weak;
ir_ref_instruction(ptr, ira->new_irb.current_basic_block);
ir_ref_instruction(cmp_value, ira->new_irb.current_basic_block);
ir_ref_instruction(new_value, ira->new_irb.current_basic_block);
return &instruction->base;
}
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_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_int_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) {
IrInstructionIntCast *instruction = ir_build_instruction<IrInstructionIntCast>(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_float_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) {
IrInstructionFloatCast *instruction = ir_build_instruction<IrInstructionFloatCast>(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_err_set_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) {
IrInstructionErrSetCast *instruction = ir_build_instruction<IrInstructionErrSetCast>(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_to_bytes(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target) {
IrInstructionToBytes *instruction = ir_build_instruction<IrInstructionToBytes>(irb, scope, source_node);
instruction->target = target;
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_from_bytes(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_child_type, IrInstruction *target) {
IrInstructionFromBytes *instruction = ir_build_instruction<IrInstructionFromBytes>(irb, scope, source_node);
instruction->dest_child_type = dest_child_type;
instruction->target = target;
ir_ref_instruction(dest_child_type, irb->current_basic_block);
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_int_to_float(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) {
IrInstructionIntToFloat *instruction = ir_build_instruction<IrInstructionIntToFloat>(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_float_to_int(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) {
IrInstructionFloatToInt *instruction = ir_build_instruction<IrInstructionFloatToInt>(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_bool_to_int(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target) {
IrInstructionBoolToInt *instruction = ir_build_instruction<IrInstructionBoolToInt>(irb, scope, source_node);
instruction->target = target;
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
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_vector_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *len,
IrInstruction *elem_type)
{
IrInstructionVectorType *instruction = ir_build_instruction<IrInstructionVectorType>(irb, scope, source_node);
instruction->len = len;
instruction->elem_type = elem_type;
ir_ref_instruction(len, irb->current_basic_block);
ir_ref_instruction(elem_type, 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_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_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_slice(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool safety_check_on)
{
IrInstructionSlice *instruction = ir_build_instruction<IrInstructionSlice>(irb, scope, source_node);
instruction->ptr = ptr;
instruction->start = start;
instruction->end = end;
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_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_member_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *container_type, IrInstruction *member_index)
{
IrInstructionMemberType *instruction = ir_build_instruction<IrInstructionMemberType>(irb, scope, source_node);
instruction->container_type = container_type;
instruction->member_index = member_index;
ir_ref_instruction(container_type, irb->current_basic_block);
ir_ref_instruction(member_index, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_member_name(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *container_type, IrInstruction *member_index)
{
IrInstructionMemberName *instruction = ir_build_instruction<IrInstructionMemberName>(irb, scope, source_node);
instruction->container_type = container_type;
instruction->member_index = member_index;
ir_ref_instruction(container_type, irb->current_basic_block);
ir_ref_instruction(member_index, 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_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_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_handle(IrBuilder *irb, Scope *scope, AstNode *source_node) {
IrInstructionHandle *instruction = ir_build_instruction<IrInstructionHandle>(irb, scope, source_node);
return &instruction->base;
}
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, ZigType *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_align_of(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_unwrap_err_code(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *err_union)
{
IrInstructionUnwrapErrCode *instruction = ir_build_instruction<IrInstructionUnwrapErrCode>(irb, scope, source_node);
instruction->err_union = err_union;
ir_ref_instruction(err_union, irb->current_basic_block);
return &instruction->base;
}
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_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction **param_types, IrInstruction *align_value, IrInstruction *return_type,
IrInstruction *async_allocator_type_value, bool is_var_args)
{
IrInstructionFnProto *instruction = ir_build_instruction<IrInstructionFnProto>(irb, scope, source_node);
instruction->param_types = param_types;
instruction->align_value = align_value;
instruction->return_type = return_type;
instruction->async_allocator_type_value = async_allocator_type_value;
instruction->is_var_args = is_var_args;
assert(source_node->type == NodeTypeFnProto);
size_t param_count = source_node->data.fn_proto.params.length;
if (is_var_args) param_count -= 1;
for (size_t i = 0; i < param_count; i += 1) {
if (param_types[i] != nullptr) ir_ref_instruction(param_types[i], irb->current_basic_block);
}
if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
if (async_allocator_type_value != nullptr) ir_ref_instruction(async_allocator_type_value, 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_ptr_cast_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *dest_type, IrInstruction *ptr, bool safety_check_on)
{
IrInstructionPtrCastSrc *instruction = ir_build_instruction<IrInstructionPtrCastSrc>(
irb, scope, source_node);
instruction->dest_type = dest_type;
instruction->ptr = ptr;
instruction->safety_check_on = safety_check_on;
ir_ref_instruction(dest_type, irb->current_basic_block);
ir_ref_instruction(ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInstruction *source_instruction,
ZigType *ptr_type, IrInstruction *ptr, bool safety_check_on)
{
IrInstructionPtrCastGen *instruction = ir_build_instruction<IrInstructionPtrCastGen>(
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
instruction->base.value.type = ptr_type;
instruction->ptr = ptr;
instruction->safety_check_on = safety_check_on;
ir_ref_instruction(ptr, ira->new_irb.current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_load_ptr_gen(IrAnalyze *ira, IrInstruction *source_instruction,
IrInstruction *ptr, ZigType *ty)
{
IrInstructionLoadPtrGen *instruction = ir_build_instruction<IrInstructionLoadPtrGen>(
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
instruction->base.value.type = ty;
instruction->ptr = ptr;
ir_ref_instruction(ptr, ira->new_irb.current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_bit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *dest_type, IrInstruction *value)
{
IrInstructionBitCast *instruction = ir_build_instruction<IrInstructionBitCast>(
irb, scope, source_node);
instruction->dest_type = dest_type;
instruction->value = value;
ir_ref_instruction(dest_type, irb->current_basic_block);
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_bit_cast_gen(IrAnalyze *ira, IrInstruction *source_instruction,
IrInstruction *operand, ZigType *ty)
{
IrInstructionBitCastGen *instruction = ir_build_instruction<IrInstructionBitCastGen>(
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
instruction->base.value.type = ty;
instruction->operand = operand;
ir_ref_instruction(operand, ira->new_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 *dest_type, IrInstruction *target)
{
IrInstructionIntToPtr *instruction = ir_build_instruction<IrInstructionIntToPtr>(
irb, scope, source_node);
instruction->dest_type = dest_type;
instruction->target = target;
if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block);
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 *dest_type, IrInstruction *target)
{
IrInstructionIntToEnum *instruction = ir_build_instruction<IrInstructionIntToEnum>(
irb, scope, source_node);
instruction->dest_type = dest_type;
instruction->target = target;
if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block);
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_enum_to_int(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *target)
{
IrInstructionEnumToInt *instruction = ir_build_instruction<IrInstructionEnumToInt>(
irb, scope, source_node);
instruction->target = target;
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_int_to_err(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *target)
{
IrInstructionIntToErr *instruction = ir_build_instruction<IrInstructionIntToErr>(
irb, scope, source_node);
instruction->target = target;
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_err_to_int(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *target)
{
IrInstructionErrToInt *instruction = ir_build_instruction<IrInstructionErrToInt>(
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,
bool have_else_prong)
{
IrInstructionCheckSwitchProngs *instruction = ir_build_instruction<IrInstructionCheckSwitchProngs>(
irb, scope, source_node);
instruction->target_value = target_value;
instruction->ranges = ranges;
instruction->range_count = range_count;
instruction->have_else_prong = have_else_prong;
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_check_statement_is_void(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction* statement_value)
{
IrInstructionCheckStatementIsVoid *instruction = ir_build_instruction<IrInstructionCheckStatementIsVoid>(
irb, scope, source_node);
instruction->statement_value = statement_value;
ir_ref_instruction(statement_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_decl_ref(IrBuilder *irb, Scope *scope, AstNode *source_node,
Tld *tld, LVal lval)
{
IrInstructionDeclRef *instruction = ir_build_instruction<IrInstructionDeclRef>(
irb, scope, source_node);
instruction->tld = tld;
instruction->lval = lval;
return &instruction->base;
}
static IrInstruction *ir_build_panic(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *msg) {
IrInstructionPanic *instruction = ir_build_instruction<IrInstructionPanic>(irb, scope, source_node);
instruction->base.value.special = ConstValSpecialStatic;
instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable;
instruction->msg = msg;
ir_ref_instruction(msg, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_tag_name(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *target)
{
IrInstructionTagName *instruction = ir_build_instruction<IrInstructionTagName>(irb, scope, source_node);
instruction->target = target;
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_tag_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *target)
{
IrInstructionTagType *instruction = ir_build_instruction<IrInstructionTagType>(irb, scope, source_node);
instruction->target = target;
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_field_parent_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *type_value, IrInstruction *field_name, IrInstruction *field_ptr, TypeStructField *field)
{
IrInstructionFieldParentPtr *instruction = ir_build_instruction<IrInstructionFieldParentPtr>(
irb, scope, source_node);
instruction->type_value = type_value;
instruction->field_name = field_name;
instruction->field_ptr = field_ptr;
instruction->field = field;
ir_ref_instruction(type_value, irb->current_basic_block);
ir_ref_instruction(field_name, irb->current_basic_block);
ir_ref_instruction(field_ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_byte_offset_of(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *type_value, IrInstruction *field_name)
{
IrInstructionByteOffsetOf *instruction = ir_build_instruction<IrInstructionByteOffsetOf>(irb, scope, source_node);
instruction->type_value = type_value;
instruction->field_name = field_name;
ir_ref_instruction(type_value, irb->current_basic_block);
ir_ref_instruction(field_name, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_bit_offset_of(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *type_value, IrInstruction *field_name)
{
IrInstructionBitOffsetOf *instruction = ir_build_instruction<IrInstructionBitOffsetOf>(irb, scope, source_node);
instruction->type_value = type_value;
instruction->field_name = field_name;
ir_ref_instruction(type_value, irb->current_basic_block);
ir_ref_instruction(field_name, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_type_info(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *type_value) {
IrInstructionTypeInfo *instruction = ir_build_instruction<IrInstructionTypeInfo>(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_type_id(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *type_value)
{
IrInstructionTypeId *instruction = ir_build_instruction<IrInstructionTypeId>(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_set_eval_branch_quota(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *new_quota)
{
IrInstructionSetEvalBranchQuota *instruction = ir_build_instruction<IrInstructionSetEvalBranchQuota>(irb, scope, source_node);
instruction->new_quota = new_quota;
ir_ref_instruction(new_quota, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_align_cast(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *align_bytes, IrInstruction *target)
{
IrInstructionAlignCast *instruction = ir_build_instruction<IrInstructionAlignCast>(irb, scope, source_node);
instruction->align_bytes = align_bytes;
instruction->target = target;
if (align_bytes) ir_ref_instruction(align_bytes, irb->current_basic_block);
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_opaque_type(IrBuilder *irb, Scope *scope, AstNode *source_node) {
IrInstructionOpaqueType *instruction = ir_build_instruction<IrInstructionOpaqueType>(irb, scope, source_node);
return &instruction->base;
}
static IrInstruction *ir_build_set_align_stack(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *align_bytes)
{
IrInstructionSetAlignStack *instruction = ir_build_instruction<IrInstructionSetAlignStack>(irb, scope, source_node);
instruction->align_bytes = align_bytes;
ir_ref_instruction(align_bytes, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *fn_type, IrInstruction *arg_index)
{
IrInstructionArgType *instruction = ir_build_instruction<IrInstructionArgType>(irb, scope, source_node);
instruction->fn_type = fn_type;
instruction->arg_index = arg_index;
ir_ref_instruction(fn_type, irb->current_basic_block);
ir_ref_instruction(arg_index, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstructionErrorReturnTrace::Optional optional) {
IrInstructionErrorReturnTrace *instruction = ir_build_instruction<IrInstructionErrorReturnTrace>(irb, scope, source_node);
instruction->optional = optional;
return &instruction->base;
}
static IrInstruction *ir_build_error_union(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *err_set, IrInstruction *payload)
{
IrInstructionErrorUnion *instruction = ir_build_instruction<IrInstructionErrorUnion>(irb, scope, source_node);
instruction->err_set = err_set;
instruction->payload = payload;
ir_ref_instruction(err_set, irb->current_basic_block);
ir_ref_instruction(payload, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_cancel(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *target)
{
IrInstructionCancel *instruction = ir_build_instruction<IrInstructionCancel>(irb, scope, source_node);
instruction->target = target;
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_get_implicit_allocator(IrBuilder *irb, Scope *scope, AstNode *source_node,
ImplicitAllocatorId id)
{
IrInstructionGetImplicitAllocator *instruction = ir_build_instruction<IrInstructionGetImplicitAllocator>(irb, scope, source_node);
instruction->id = id;
return &instruction->base;
}
static IrInstruction *ir_build_coro_id(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *promise_ptr) {
IrInstructionCoroId *instruction = ir_build_instruction<IrInstructionCoroId>(irb, scope, source_node);
instruction->promise_ptr = promise_ptr;
ir_ref_instruction(promise_ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_coro_alloc(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *coro_id) {
IrInstructionCoroAlloc *instruction = ir_build_instruction<IrInstructionCoroAlloc>(irb, scope, source_node);
instruction->coro_id = coro_id;
ir_ref_instruction(coro_id, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_coro_size(IrBuilder *irb, Scope *scope, AstNode *source_node) {
IrInstructionCoroSize *instruction = ir_build_instruction<IrInstructionCoroSize>(irb, scope, source_node);
return &instruction->base;
}
static IrInstruction *ir_build_coro_begin(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *coro_id, IrInstruction *coro_mem_ptr) {
IrInstructionCoroBegin *instruction = ir_build_instruction<IrInstructionCoroBegin>(irb, scope, source_node);
instruction->coro_id = coro_id;
instruction->coro_mem_ptr = coro_mem_ptr;
ir_ref_instruction(coro_id, irb->current_basic_block);
ir_ref_instruction(coro_mem_ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_coro_alloc_fail(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *err_val) {
IrInstructionCoroAllocFail *instruction = ir_build_instruction<IrInstructionCoroAllocFail>(irb, scope, source_node);
instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable;
instruction->base.value.special = ConstValSpecialStatic;
instruction->err_val = err_val;
ir_ref_instruction(err_val, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_coro_suspend(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *save_point, IrInstruction *is_final)
{
IrInstructionCoroSuspend *instruction = ir_build_instruction<IrInstructionCoroSuspend>(irb, scope, source_node);
instruction->save_point = save_point;
instruction->is_final = is_final;
if (save_point != nullptr) ir_ref_instruction(save_point, irb->current_basic_block);
ir_ref_instruction(is_final, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_coro_end(IrBuilder *irb, Scope *scope, AstNode *source_node) {
IrInstructionCoroEnd *instruction = ir_build_instruction<IrInstructionCoroEnd>(irb, scope, source_node);
return &instruction->base;
}
static IrInstruction *ir_build_coro_free(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *coro_id, IrInstruction *coro_handle)
{
IrInstructionCoroFree *instruction = ir_build_instruction<IrInstructionCoroFree>(irb, scope, source_node);
instruction->coro_id = coro_id;
instruction->coro_handle = coro_handle;
ir_ref_instruction(coro_id, irb->current_basic_block);
ir_ref_instruction(coro_handle, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_coro_resume(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *awaiter_handle)
{
IrInstructionCoroResume *instruction = ir_build_instruction<IrInstructionCoroResume>(irb, scope, source_node);
instruction->awaiter_handle = awaiter_handle;
ir_ref_instruction(awaiter_handle, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_coro_save(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *coro_handle)
{
IrInstructionCoroSave *instruction = ir_build_instruction<IrInstructionCoroSave>(irb, scope, source_node);
instruction->coro_handle = coro_handle;
ir_ref_instruction(coro_handle, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_coro_promise(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *coro_handle)
{
IrInstructionCoroPromise *instruction = ir_build_instruction<IrInstructionCoroPromise>(irb, scope, source_node);
instruction->coro_handle = coro_handle;
ir_ref_instruction(coro_handle, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_coro_alloc_helper(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *realloc_fn, IrInstruction *coro_size)
{
IrInstructionCoroAllocHelper *instruction = ir_build_instruction<IrInstructionCoroAllocHelper>(irb, scope, source_node);
instruction->realloc_fn = realloc_fn;
instruction->coro_size = coro_size;
ir_ref_instruction(realloc_fn, irb->current_basic_block);
ir_ref_instruction(coro_size, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_atomic_rmw(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *operand_type, IrInstruction *ptr, IrInstruction *op, IrInstruction *operand,
IrInstruction *ordering, AtomicRmwOp resolved_op, AtomicOrder resolved_ordering)
{
IrInstructionAtomicRmw *instruction = ir_build_instruction<IrInstructionAtomicRmw>(irb, scope, source_node);
instruction->operand_type = operand_type;
instruction->ptr = ptr;
instruction->op = op;
instruction->operand = operand;
instruction->ordering = ordering;
instruction->resolved_op = resolved_op;
instruction->resolved_ordering = resolved_ordering;
if (operand_type != nullptr) ir_ref_instruction(operand_type, irb->current_basic_block);
ir_ref_instruction(ptr, irb->current_basic_block);
if (op != nullptr) ir_ref_instruction(op, irb->current_basic_block);
ir_ref_instruction(operand, irb->current_basic_block);
if (ordering != nullptr) ir_ref_instruction(ordering, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_atomic_load(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *operand_type, IrInstruction *ptr,
IrInstruction *ordering, AtomicOrder resolved_ordering)
{
IrInstructionAtomicLoad *instruction = ir_build_instruction<IrInstructionAtomicLoad>(irb, scope, source_node);
instruction->operand_type = operand_type;
instruction->ptr = ptr;
instruction->ordering = ordering;
instruction->resolved_ordering = resolved_ordering;
if (operand_type != nullptr) ir_ref_instruction(operand_type, irb->current_basic_block);
ir_ref_instruction(ptr, irb->current_basic_block);
if (ordering != nullptr) ir_ref_instruction(ordering, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_promise_result_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *promise_type)
{
IrInstructionPromiseResultType *instruction = ir_build_instruction<IrInstructionPromiseResultType>(irb, scope, source_node);
instruction->promise_type = promise_type;
ir_ref_instruction(promise_type, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_await_bookkeeping(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *promise_result_type)
{
IrInstructionAwaitBookkeeping *instruction = ir_build_instruction<IrInstructionAwaitBookkeeping>(irb, scope, source_node);
instruction->promise_result_type = promise_result_type;
ir_ref_instruction(promise_result_type, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_save_err_ret_addr(IrBuilder *irb, Scope *scope, AstNode *source_node) {
IrInstructionSaveErrRetAddr *instruction = ir_build_instruction<IrInstructionSaveErrRetAddr>(irb, scope, source_node);
return &instruction->base;
}
static IrInstruction *ir_build_add_implicit_return_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *value)
{
IrInstructionAddImplicitReturnType *instruction = ir_build_instruction<IrInstructionAddImplicitReturnType>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_merge_err_ret_traces(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *coro_promise_ptr, IrInstruction *src_err_ret_trace_ptr, IrInstruction *dest_err_ret_trace_ptr)
{
IrInstructionMergeErrRetTraces *instruction = ir_build_instruction<IrInstructionMergeErrRetTraces>(irb, scope, source_node);
instruction->coro_promise_ptr = coro_promise_ptr;
instruction->src_err_ret_trace_ptr = src_err_ret_trace_ptr;
instruction->dest_err_ret_trace_ptr = dest_err_ret_trace_ptr;
ir_ref_instruction(coro_promise_ptr, irb->current_basic_block);
ir_ref_instruction(src_err_ret_trace_ptr, irb->current_basic_block);
ir_ref_instruction(dest_err_ret_trace_ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *err_ret_trace_ptr) {
IrInstructionMarkErrRetTracePtr *instruction = ir_build_instruction<IrInstructionMarkErrRetTracePtr>(irb, scope, source_node);
instruction->err_ret_trace_ptr = err_ret_trace_ptr;
ir_ref_instruction(err_ret_trace_ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_sqrt(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) {
IrInstructionSqrt *instruction = ir_build_instruction<IrInstructionSqrt>(irb, scope, source_node);
instruction->type = type;
instruction->op = op;
if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block);
ir_ref_instruction(op, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_bswap(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) {
IrInstructionBswap *instruction = ir_build_instruction<IrInstructionBswap>(irb, scope, source_node);
instruction->type = type;
instruction->op = op;
if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block);
ir_ref_instruction(op, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_bit_reverse(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) {
IrInstructionBitReverse *instruction = ir_build_instruction<IrInstructionBitReverse>(irb, scope, source_node);
instruction->type = type;
instruction->op = op;
if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block);
ir_ref_instruction(op, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *scope_is_comptime, IrInstruction *is_comptime) {
IrInstructionCheckRuntimeScope *instruction = ir_build_instruction<IrInstructionCheckRuntimeScope>(irb, scope, source_node);
instruction->scope_is_comptime = scope_is_comptime;
instruction->is_comptime = is_comptime;
ir_ref_instruction(scope_is_comptime, irb->current_basic_block);
ir_ref_instruction(is_comptime, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_vector_to_array(IrAnalyze *ira, IrInstruction *source_instruction,
IrInstruction *vector, ZigType *result_type)
{
IrInstructionVectorToArray *instruction = ir_build_instruction<IrInstructionVectorToArray>(&ira->new_irb,
source_instruction->scope, source_instruction->source_node);
instruction->base.value.type = result_type;
instruction->vector = vector;
ir_ref_instruction(vector, ira->new_irb.current_basic_block);
ir_add_alloca(ira, &instruction->base, result_type);
return &instruction->base;
}
static IrInstruction *ir_build_array_to_vector(IrAnalyze *ira, IrInstruction *source_instruction,
IrInstruction *array, ZigType *result_type)
{
IrInstructionArrayToVector *instruction = ir_build_instruction<IrInstructionArrayToVector>(&ira->new_irb,
source_instruction->scope, source_instruction->source_node);
instruction->base.value.type = result_type;
instruction->array = array;
ir_ref_instruction(array, ira->new_irb.current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_assert_zero(IrAnalyze *ira, IrInstruction *source_instruction,
IrInstruction *target)
{
IrInstructionAssertZero *instruction = ir_build_instruction<IrInstructionAssertZero>(&ira->new_irb,
source_instruction->scope, source_instruction->source_node);
instruction->base.value.type = ira->codegen->builtin_types.entry_void;
instruction->target = target;
ir_ref_instruction(target, ira->new_irb.current_basic_block);
return &instruction->base;
}
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
results[ReturnKindUnconditional] = 0;
results[ReturnKindError] = 0;
Scope *scope = inner_scope;
while (scope != outer_scope) {
assert(scope);
switch (scope->id) {
case ScopeIdDefer: {
AstNode *defer_node = scope->source_node;
assert(defer_node->type == NodeTypeDefer);
ReturnKind defer_kind = defer_node->data.defer.kind;
results[defer_kind] += 1;
scope = scope->parent;
continue;
}
case ScopeIdDecls:
case ScopeIdFnDef:
return;
case ScopeIdBlock:
case ScopeIdVarDecl:
case ScopeIdLoop:
case ScopeIdSuspend:
case ScopeIdCompTime:
case ScopeIdRuntime:
scope = scope->parent;
continue;
case ScopeIdDeferExpr:
case ScopeIdCImport:
case ScopeIdCoroPrelude:
zig_unreachable();
}
}
}
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) {
Scope *scope = inner_scope;
bool is_noreturn = false;
while (scope != outer_scope) {
if (!scope)
return is_noreturn;
switch (scope->id) {
case 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))
{
AstNode *defer_expr_node = defer_node->data.defer.expr;
Scope *defer_expr_scope = defer_node->data.defer.expr_scope;
IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope);
if (defer_expr_value != irb->codegen->invalid_instruction) {
if (defer_expr_value->value.type != nullptr &&
defer_expr_value->value.type->id == ZigTypeIdUnreachable)
{
is_noreturn = true;
} else {
ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node,
defer_expr_value));
}
}
}
scope = scope->parent;
continue;
}
case ScopeIdDecls:
case ScopeIdFnDef:
return is_noreturn;
case ScopeIdBlock:
case ScopeIdVarDecl:
case ScopeIdLoop:
case ScopeIdSuspend:
case ScopeIdCompTime:
case ScopeIdRuntime:
scope = scope->parent;
continue;
case ScopeIdDeferExpr:
case ScopeIdCImport:
case ScopeIdCoroPrelude:
zig_unreachable();
}
}
return is_noreturn;
}
static void ir_set_cursor_at_end(IrBuilder *irb, IrBasicBlock *basic_block) {
assert(basic_block);
irb->current_basic_block = basic_block;
}
static void ir_set_cursor_at_end_and_append_block(IrBuilder *irb, IrBasicBlock *basic_block) {
irb->exec->basic_block_list.append(basic_block);
ir_set_cursor_at_end(irb, basic_block);
}
static ScopeSuspend *get_scope_suspend(Scope *scope) {
while (scope) {
if (scope->id == ScopeIdSuspend)
return (ScopeSuspend *)scope;
if (scope->id == ScopeIdFnDef)
return nullptr;
scope = scope->parent;
}
return nullptr;
}
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 bool exec_is_async(IrExecutable *exec) {
ZigFn *fn_entry = exec_fn_entry(exec);
return fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync;
}
static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *return_value,
bool is_generated_code)
{
ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, return_value));
bool is_async = exec_is_async(irb->exec);
if (!is_async) {
IrInstruction *return_inst = ir_build_return(irb, scope, node, return_value);
return_inst->is_gen = is_generated_code;
return return_inst;
}
IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "Suspended");
IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, scope, "NotSuspended");
IrBasicBlock *store_awaiter_block = ir_create_basic_block(irb, scope, "StoreAwaiter");
IrBasicBlock *check_canceled_block = ir_create_basic_block(irb, scope, "CheckCanceled");
IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111
IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise);
IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false);
IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value);
IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, ptr_mask, nullptr,
AtomicRmwOp_or, AtomicOrderSeqCst);
IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false);
IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false);
ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, suspended_block);
ir_build_unreachable(irb, scope, node);
ir_set_cursor_at_end_and_append_block(irb, not_suspended_block);
IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false);
// if we ever add null checking safety to the ptrtoint instruction, it needs to be disabled here
IrInstruction *have_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false);
ir_build_cond_br(irb, scope, node, have_await_handle, store_awaiter_block, check_canceled_block, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, store_awaiter_block);
IrInstruction *await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, await_handle_addr);
ir_build_store_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, await_handle);
ir_build_br(irb, scope, node, irb->exec->coro_normal_final, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, check_canceled_block);
IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
return ir_build_cond_br(irb, scope, node, is_canceled_bool, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, is_comptime);
}
static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
assert(node->type == NodeTypeReturnExpr);
ZigFn *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) {
// Temporarily set this so that if we return a type it gets the name of the function
ZigFn *prev_name_fn = irb->exec->name_fn;
irb->exec->name_fn = exec_fn_entry(irb->exec);
return_value = ir_gen_node(irb, expr_node, scope);
irb->exec->name_fn = prev_name_fn;
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[2];
ir_count_defers(irb, scope, outer_scope, defer_counts);
bool have_err_defers = defer_counts[ReturnKindError] > 0;
if (have_err_defers || irb->codegen->have_err_ret_tracing) {
IrBasicBlock *err_block = ir_create_basic_block(irb, scope, "ErrRetErr");
IrBasicBlock *ok_block = ir_create_basic_block(irb, scope, "ErrRetOk");
if (!have_err_defers) {
ir_gen_defers_for_block(irb, scope, outer_scope, false);
}
IrInstruction *is_err = ir_build_test_err(irb, scope, node, return_value);
bool should_inline = ir_should_inline(irb->exec, scope);
IrInstruction *is_comptime;
if (should_inline) {
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));
IrBasicBlock *ret_stmt_block = ir_create_basic_block(irb, scope, "RetStmt");
ir_set_cursor_at_end_and_append_block(irb, err_block);
if (have_err_defers) {
ir_gen_defers_for_block(irb, scope, outer_scope, true);
}
if (irb->codegen->have_err_ret_tracing && !should_inline) {
ir_build_save_err_ret_addr(irb, scope, node);
}
ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, ok_block);
if (have_err_defers) {
ir_gen_defers_for_block(irb, scope, outer_scope, false);
}
ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, ret_stmt_block);
return ir_gen_async_return(irb, scope, node, return_value, false);
} else {
// generate unconditional defers
ir_gen_defers_for_block(irb, scope, outer_scope, false);
return ir_gen_async_return(irb, scope, node, return_value, false);
}
}
case ReturnKindError:
{
assert(expr_node);
IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr);
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_create_basic_block(irb, scope, "ErrRetReturn");
IrBasicBlock *continue_block = ir_create_basic_block(irb, scope, "ErrRetContinue");
IrInstruction *is_comptime;
bool should_inline = ir_should_inline(irb->exec, scope);
if (should_inline) {
is_comptime = ir_build_const_bool(irb, scope, node, true);
} else {
is_comptime = ir_build_test_comptime(irb, scope, node, is_err_val);
}
ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime));
ir_set_cursor_at_end_and_append_block(irb, return_block);
if (!ir_gen_defers_for_block(irb, scope, outer_scope, true)) {
IrInstruction *err_val = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr);
if (irb->codegen->have_err_ret_tracing && !should_inline) {
ir_build_save_err_ret_addr(irb, scope, node);
}
ir_gen_async_return(irb, scope, node, err_val, false);
}
ir_set_cursor_at_end_and_append_block(irb, continue_block);
IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, scope, node, err_union_ptr, false);
if (lval == LValPtr)
return unwrapped_ptr;
else
return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
}
}
zig_unreachable();
}
static ZigVar *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,
bool skip_name_check)
{
ZigVar *variable_entry = allocate<ZigVar>(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;
variable_entry->const_value = create_const_vals(1);
if (is_comptime != nullptr) {
is_comptime->ref_count += 1;
}
if (name) {
buf_init_from_buf(&variable_entry->name, name);
if (!skip_name_check) {
ZigVar *existing_var = find_variable(codegen, parent_scope, name, nullptr);
if (existing_var && !existing_var->shadowable) {
if (existing_var->var_type == nullptr || !type_is_invalid(existing_var->var_type)) {
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->var_type = codegen->builtin_types.entry_invalid;
} else {
ZigType *type;
if (get_primitive_type(codegen, name, &type) != ErrorPrimitiveTypeNotFound) {
add_node_error(codegen, node,
buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name)));
variable_entry->var_type = codegen->builtin_types.entry_invalid;
} else {
Tld *tld = find_decl(codegen, parent_scope, name);
if (tld != nullptr) {
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->var_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(codegen, 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 ZigVar *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)
{
bool is_underscored = name ? buf_eql_str(name, "_") : false;
ZigVar *var = create_local_var(irb->codegen, node, scope,
(is_underscored ? nullptr : name), src_is_const, gen_is_const,
(is_underscored ? true : is_shadowable), is_comptime, false);
if (is_comptime != nullptr || gen_is_const) {
var->mem_slot_index = exec_next_mem_slot(irb->exec);
var->owner_exec = irb->exec;
}
assert(var->child_scope);
return var;
}
static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node) {
assert(block_node->type == NodeTypeBlock);
ZigList<IrInstruction *> incoming_values = {0};
ZigList<IrBasicBlock *> incoming_blocks = {0};
ScopeBlock *scope_block = create_block_scope(irb->codegen, block_node, parent_scope);
Scope *outer_block_scope = &scope_block->base;
Scope *child_scope = outer_block_scope;
ZigFn *fn_entry = scope_fn_entry(parent_scope);
if (fn_entry && fn_entry->child_scope == parent_scope) {
fn_entry->def_scope = scope_block;
}
if (block_node->data.block.statements.length == 0) {
// {}
return ir_build_const_void(irb, child_scope, block_node);
}
if (block_node->data.block.name != nullptr) {
scope_block->incoming_blocks = &incoming_blocks;
scope_block->incoming_values = &incoming_values;
scope_block->end_block = ir_create_basic_block(irb, parent_scope, "BlockEnd");
scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node,
ir_should_inline(irb->exec, parent_scope));
}
bool is_continuation_unreachable = false;
IrInstruction *noreturn_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);
IrInstruction *statement_value = ir_gen_node(irb, statement_node, child_scope);
is_continuation_unreachable = instr_is_unreachable(statement_value);
if (is_continuation_unreachable) {
// keep the last noreturn statement value around in case we need to return it
noreturn_return_value = statement_value;
}
if (statement_node->type == NodeTypeDefer && statement_value != irb->codegen->invalid_instruction) {
// defer starts a new scope
child_scope = statement_node->data.defer.child_scope;
assert(child_scope);
} else if (statement_value->id == IrInstructionIdDeclVarSrc) {
// variable declarations start a new scope
IrInstructionDeclVarSrc *decl_var_instruction = (IrInstructionDeclVarSrc *)statement_value;
child_scope = decl_var_instruction->var->child_scope;
} else if (statement_value != irb->codegen->invalid_instruction && !is_continuation_unreachable) {
// this statement's value must be void
ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, statement_node, statement_value));
}
}
if (is_continuation_unreachable) {
assert(noreturn_return_value != nullptr);
if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) {
return noreturn_return_value;
}
ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block);
return ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
} else {
incoming_blocks.append(irb->current_basic_block);
incoming_values.append(ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node)));
}
if (block_node->data.block.name != nullptr) {
ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false);
ir_mark_gen(ir_build_br(irb, parent_scope, block_node, scope_block->end_block, scope_block->is_comptime));
ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block);
return ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
} else {
ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false);
return ir_mark_gen(ir_mark_gen(ir_build_const_void(irb, child_scope, block_node)));
}
}
static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) {
Scope *inner_scope = scope;
if (op_id == IrBinOpArrayCat || op_id == IrBinOpArrayMult) {
inner_scope = create_comptime_scope(irb->codegen, node, scope);
}
IrInstruction *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, inner_scope);
IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, inner_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, LValPtr);
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, LValPtr);
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_create_basic_block(irb, scope, "BoolOrFalse");
// block for when val1 == true (don't even evaluate the second part)
IrBasicBlock *true_block = ir_create_basic_block(irb, scope, "BoolOrTrue");
ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime);
ir_set_cursor_at_end_and_append_block(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_and_append_block(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_create_basic_block(irb, scope, "BoolAndTrue");
// block for when val1 == false (don't even evaluate the second part)
IrBasicBlock *false_block = ir_create_basic_block(irb, scope, "BoolAndFalse");
ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime);
ir_set_cursor_at_end_and_append_block(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_and_append_block(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_orelse(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, LValPtr);
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_create_basic_block(irb, parent_scope, "OptionalNonNull");
IrBasicBlock *null_block = ir_create_basic_block(irb, parent_scope, "OptionalNull");
IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "OptionalEnd");
ir_build_cond_br(irb, parent_scope, node, is_non_null, ok_block, null_block, is_comptime);
ir_set_cursor_at_end_and_append_block(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_and_append_block(irb, ok_block);
IrInstruction *unwrapped_ptr = ir_build_optional_unwrap_ptr(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_and_append_block(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_error_union(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 *err_set = ir_gen_node(irb, op1_node, parent_scope);
if (err_set == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *payload = ir_gen_node(irb, op2_node, parent_scope);
if (payload == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
return ir_build_error_union(irb, parent_scope, node, err_set, payload);
}
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, IrBinOpDivUnspecified);
case BinOpTypeAssignMod:
return ir_gen_assign_op(irb, scope, node, IrBinOpRemUnspecified);
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, IrBinOpBitShiftLeftLossy);
case BinOpTypeAssignBitShiftRight:
return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRightLossy);
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 BinOpTypeAssignMergeErrorSets:
return ir_gen_assign_op(irb, scope, node, IrBinOpMergeErrorSets);
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, IrBinOpBitShiftLeftLossy);
case BinOpTypeBitShiftRight:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRightLossy);
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, IrBinOpDivUnspecified);
case BinOpTypeMod:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpRemUnspecified);
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 BinOpTypeMergeErrorSets:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets);
case BinOpTypeUnwrapOptional:
return ir_gen_orelse(irb, scope, node);
case BinOpTypeErrorUnion:
return ir_gen_error_union(irb, scope, node);
}
zig_unreachable();
}
static IrInstruction *ir_gen_int_lit(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeIntLiteral);
return ir_build_const_bigint(irb, scope, node, node->data.int_literal.bigint);
}
static IrInstruction *ir_gen_float_lit(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeFloatLiteral);
if (node->data.float_literal.overflow) {
add_node_error(irb->codegen, node, buf_sprintf("float literal out of range of any type"));
return irb->codegen->invalid_instruction;
}
return ir_build_const_bigfloat(irb, scope, node, node->data.float_literal.bigfloat);
}
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 void populate_invalid_variable_in_scope(CodeGen *g, Scope *scope, AstNode *node, Buf *var_name) {
ScopeDecls *scope_decls = nullptr;
while (scope != nullptr) {
if (scope->id == ScopeIdDecls) {
scope_decls = reinterpret_cast<ScopeDecls *>(scope);
}
scope = scope->parent;
}
TldVar *tld_var = allocate<TldVar>(1);
init_tld(&tld_var->base, TldIdVar, var_name, VisibModPub, node, &scope_decls->base);
tld_var->base.resolution = TldResolutionInvalid;
tld_var->var = add_variable(g, node, &scope_decls->base, var_name, false,
&g->invalid_instruction->value, &tld_var->base, g->builtin_types.entry_invalid);
scope_decls->decl_table.put(var_name, &tld_var->base);
}
static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
Error err;
assert(node->type == NodeTypeSymbol);
Buf *variable_name = node->data.symbol_expr.symbol;
if (buf_eql_str(variable_name, "_") && lval == LValPtr) {
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node);
const_instruction->base.value.type = get_pointer_to_type(irb->codegen,
irb->codegen->builtin_types.entry_void, false);
const_instruction->base.value.special = ConstValSpecialStatic;
const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialDiscard;
return &const_instruction->base;
}
ZigType *primitive_type;
if ((err = get_primitive_type(irb->codegen, variable_name, &primitive_type))) {
if (err == ErrorOverflow) {
add_node_error(irb->codegen, node,
buf_sprintf("primitive integer type '%s' exceeds maximum bit width of 65535",
buf_ptr(variable_name)));
return irb->codegen->invalid_instruction;
}
assert(err == ErrorPrimitiveTypeNotFound);
} else {
IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_type);
if (lval == LValPtr) {
return ir_build_ref(irb, scope, node, value, false, false);
} else {
return value;
}
}
ScopeFnDef *crossed_fndef_scope;
ZigVar *var = find_variable(irb->codegen, scope, variable_name, &crossed_fndef_scope);
if (var) {
IrInstruction *var_ptr = ir_build_var_ptr_x(irb, scope, node, var, crossed_fndef_scope);
if (lval == LValPtr)
return var_ptr;
else
return ir_build_load_ptr(irb, scope, node, var_ptr);
}
Tld *tld = find_decl(irb->codegen, scope, variable_name);
if (tld)
return ir_build_decl_ref(irb, scope, node, tld, lval);
if (get_container_scope(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;
}
// 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
populate_invalid_variable_in_scope(irb->codegen, scope, node, variable_name);
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, LVal 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, LValPtr);
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, PtrLenSingle);
if (lval == LValPtr)
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) {
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, LValPtr);
if (container_ref_instruction == irb->codegen->invalid_instruction)
return container_ref_instruction;
return ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name);
}
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_this(IrBuilder *irb, Scope *orig_scope, AstNode *node) {
for (Scope *it_scope = orig_scope; it_scope != nullptr; it_scope = it_scope->parent) {
if (it_scope->id == ScopeIdDecls) {
ScopeDecls *decls_scope = (ScopeDecls *)it_scope;
ZigType *container_type = decls_scope->container_type;
if (container_type != nullptr) {
return ir_build_const_type(irb, orig_scope, node, container_type);
} else {
return ir_build_const_import(irb, orig_scope, node, decls_scope->import);
}
}
}
zig_unreachable();
}
static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
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) { // new built in not found
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 != SIZE_MAX && builtin_fn->param_count != actual_param_count) {
add_node_error(irb->codegen, node,
buf_sprintf("expected %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize,
builtin_fn->param_count, actual_param_count));
return irb->codegen->invalid_instruction;
}
bool is_async = exec_is_async(irb->exec);
switch (builtin_fn->id) {
case BuiltinFnIdInvalid:
zig_unreachable();
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;
IrInstruction *type_of = ir_build_typeof(irb, scope, node, arg);
return ir_lval_wrap(irb, scope, type_of, lval);
}
case BuiltinFnIdSetCold:
{
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;
IrInstruction *set_cold = ir_build_set_cold(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, set_cold, lval);
}
case BuiltinFnIdSetRuntimeSafety:
{
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;
IrInstruction *set_safety = ir_build_set_runtime_safety(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, set_safety, lval);
}
case BuiltinFnIdSetFloatMode:
{
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;
IrInstruction *set_float_mode = ir_build_set_float_mode(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, set_float_mode, lval);
}
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;
IrInstruction *size_of = ir_build_size_of(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, size_of, lval);
}
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;
IrInstruction *ctz = ir_build_ctz(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, ctz, lval);
}
case BuiltinFnIdPopCount:
{
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;
IrInstruction *instr = ir_build_pop_count(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, instr, lval);
}
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;
IrInstruction *clz = ir_build_clz(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, clz, lval);
}
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;
IrInstruction *import = ir_build_import(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, import, lval);
}
case BuiltinFnIdCImport:
{
IrInstruction *c_import = ir_build_c_import(irb, scope, node);
return ir_lval_wrap(irb, scope, c_import, lval);
}
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;
}
IrInstruction *c_include = ir_build_c_include(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, c_include, lval);
}
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;
}
IrInstruction *c_define = ir_build_c_define(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, c_define, lval);
}
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;
}
IrInstruction *c_undef = ir_build_c_undef(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, c_undef, lval);
}
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;
IrInstruction *compile_err = ir_build_compile_err(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, compile_err, lval);
}
case BuiltinFnIdCompileLog:
{
IrInstruction **args = allocate<IrInstruction*>(actual_param_count);
for (size_t i = 0; i < actual_param_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 irb->codegen->invalid_instruction;
}
IrInstruction *compile_log = ir_build_compile_log(irb, scope, node, actual_param_count, args);
return ir_lval_wrap(irb, scope, compile_log, lval);
}
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;
IrInstruction *err_name = ir_build_err_name(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, err_name, lval);
}
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;
IrInstruction *embed_file = ir_build_embed_file(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, embed_file, lval);
}
case BuiltinFnIdCmpxchgWeak:
case BuiltinFnIdCmpxchgStrong:
{
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;
AstNode *arg5_node = node->data.fn_call_expr.params.at(5);
IrInstruction *arg5_value = ir_gen_node(irb, arg5_node, scope);
if (arg5_value == irb->codegen->invalid_instruction)
return arg5_value;
IrInstruction *cmpxchg = ir_build_cmpxchg_src(irb, scope, node, arg0_value, arg1_value,
arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak));
return ir_lval_wrap(irb, scope, cmpxchg, lval);
}
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;
IrInstruction *fence = ir_build_fence(irb, scope, node, arg0_value, AtomicOrderUnordered);
return ir_lval_wrap(irb, scope, fence, lval);
}
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;
IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivExact, arg0_value, arg1_value, true);
return ir_lval_wrap(irb, scope, bin_op, lval);
}
case BuiltinFnIdDivTrunc:
{
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;
IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivTrunc, arg0_value, arg1_value, true);
return ir_lval_wrap(irb, scope, bin_op, lval);
}
case BuiltinFnIdDivFloor:
{
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;
IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivFloor, arg0_value, arg1_value, true);
return ir_lval_wrap(irb, scope, bin_op, lval);
}
case BuiltinFnIdRem:
{
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;
IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemRem, arg0_value, arg1_value, true);
return ir_lval_wrap(irb, scope, bin_op, lval);
}
case BuiltinFnIdMod:
{
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;
IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true);
return ir_lval_wrap(irb, scope, bin_op, lval);
}
case BuiltinFnIdSqrt:
{
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;
IrInstruction *ir_sqrt = ir_build_sqrt(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, ir_sqrt, lval);
}
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;
IrInstruction *truncate = ir_build_truncate(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, truncate, lval);
}
case BuiltinFnIdIntCast:
{
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;
IrInstruction *result = ir_build_int_cast(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, result, lval);
}
case BuiltinFnIdFloatCast:
{
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;
IrInstruction *result = ir_build_float_cast(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, result, lval);
}
case BuiltinFnIdErrSetCast:
{
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;
IrInstruction *result = ir_build_err_set_cast(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, result, lval);
}
case BuiltinFnIdFromBytes:
{
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;
IrInstruction *result = ir_build_from_bytes(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, result, lval);
}
case BuiltinFnIdToBytes:
{
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;
IrInstruction *result = ir_build_to_bytes(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, result, lval);
}
case BuiltinFnIdIntToFloat:
{
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;
IrInstruction *result = ir_build_int_to_float(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, result, lval);
}
case BuiltinFnIdFloatToInt:
{
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;
IrInstruction *result = ir_build_float_to_int(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, result, lval);
}
case BuiltinFnIdErrToInt:
{
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;
IrInstruction *result = ir_build_err_to_int(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, result, lval);
}
case BuiltinFnIdIntToErr:
{
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;
IrInstruction *result = ir_build_int_to_err(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, result, lval);
}
case BuiltinFnIdBoolToInt:
{
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;
IrInstruction *result = ir_build_bool_to_int(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, result, lval);
}
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;
IrInstruction *int_type = ir_build_int_type(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, int_type, lval);
}
case BuiltinFnIdVectorType:
{
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;
IrInstruction *vector_type = ir_build_vector_type(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, vector_type, lval);
}
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;
IrInstruction *ir_memcpy = ir_build_memcpy(irb, scope, node, arg0_value, arg1_value, arg2_value);
return ir_lval_wrap(irb, scope, ir_memcpy, lval);
}
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;
IrInstruction *ir_memset = ir_build_memset(irb, scope, node, arg0_value, arg1_value, arg2_value);
return ir_lval_wrap(irb, scope, ir_memset, lval);
}
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;
IrInstruction *member_count = ir_build_member_count(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, member_count, lval);
}
case BuiltinFnIdMemberType:
{
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;
IrInstruction *member_type = ir_build_member_type(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, member_type, lval);
}
case BuiltinFnIdMemberName:
{
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;
IrInstruction *member_name = ir_build_member_name(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, member_name, lval);
}
case BuiltinFnIdField:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LValPtr);
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;
IrInstruction *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node, arg0_value, arg1_value);
if (lval == LValPtr)
return ptr_instruction;
return ir_build_load_ptr(irb, scope, node, ptr_instruction);
}
case BuiltinFnIdTypeInfo:
{
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;
IrInstruction *type_info = ir_build_type_info(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, type_info, lval);
}
case BuiltinFnIdBreakpoint:
return ir_lval_wrap(irb, scope, ir_build_breakpoint(irb, scope, node), lval);
case BuiltinFnIdReturnAddress:
return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval);
case BuiltinFnIdFrameAddress:
return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval);
case BuiltinFnIdHandle:
if (!irb->exec->fn_entry) {
add_node_error(irb->codegen, node, buf_sprintf("@handle() called outside of function definition"));
return irb->codegen->invalid_instruction;
}
if (!is_async) {
add_node_error(irb->codegen, node, buf_sprintf("@handle() in non-async function"));
return irb->codegen->invalid_instruction;
}
return ir_lval_wrap(irb, scope, ir_build_handle(irb, scope, node), lval);
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;
IrInstruction *align_of = ir_build_align_of(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, align_of, lval);
}
case BuiltinFnIdAddWithOverflow:
return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd), lval);
case BuiltinFnIdSubWithOverflow:
return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub), lval);
case BuiltinFnIdMulWithOverflow:
return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval);
case BuiltinFnIdShlWithOverflow:
return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval);
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;
IrInstruction *type_name = ir_build_type_name(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, type_name, lval);
}
case BuiltinFnIdPanic:
{
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;
IrInstruction *panic = ir_build_panic(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, panic, lval);
}
case BuiltinFnIdPtrCast:
{
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;
IrInstruction *ptr_cast = ir_build_ptr_cast_src(irb, scope, node, arg0_value, arg1_value, true);
return ir_lval_wrap(irb, scope, ptr_cast, lval);
}
case BuiltinFnIdBitCast:
{
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;
IrInstruction *bit_cast = ir_build_bit_cast(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, bit_cast, lval);
}
case BuiltinFnIdIntToPtr:
{
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;
IrInstruction *int_to_ptr = ir_build_int_to_ptr(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, int_to_ptr, lval);
}
case BuiltinFnIdPtrToInt:
{
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;
IrInstruction *ptr_to_int = ir_build_ptr_to_int(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, ptr_to_int, lval);
}
case BuiltinFnIdTagName:
{
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;
IrInstruction *actual_tag = ir_build_union_tag(irb, scope, node, arg0_value);
IrInstruction *tag_name = ir_build_tag_name(irb, scope, node, actual_tag);
return ir_lval_wrap(irb, scope, tag_name, lval);
}
case BuiltinFnIdTagType:
{
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;
IrInstruction *tag_type = ir_build_tag_type(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, tag_type, lval);
}
case BuiltinFnIdFieldParentPtr:
{
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;
IrInstruction *field_parent_ptr = ir_build_field_parent_ptr(irb, scope, node, arg0_value, arg1_value, arg2_value, nullptr);
return ir_lval_wrap(irb, scope, field_parent_ptr, lval);
}
case BuiltinFnIdByteOffsetOf:
{
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;
IrInstruction *offset_of = ir_build_byte_offset_of(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, offset_of, lval);
}
case BuiltinFnIdBitOffsetOf:
{
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;
IrInstruction *offset_of = ir_build_bit_offset_of(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, offset_of, lval);
}
case BuiltinFnIdInlineCall:
case BuiltinFnIdNoInlineCall:
{
if (node->data.fn_call_expr.params.length == 0) {
add_node_error(irb->codegen, node, buf_sprintf("expected at least 1 argument, found 0"));
return irb->codegen->invalid_instruction;
}
AstNode *fn_ref_node = node->data.fn_call_expr.params.at(0);
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 - 1;
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 + 1);
args[i] = ir_gen_node(irb, arg_node, scope);
if (args[i] == irb->codegen->invalid_instruction)
return args[i];
}
FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever;
IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr, nullptr);
return ir_lval_wrap(irb, scope, call, lval);
}
case BuiltinFnIdNewStackCall:
{
if (node->data.fn_call_expr.params.length == 0) {
add_node_error(irb->codegen, node, buf_sprintf("expected at least 1 argument, found 0"));
return irb->codegen->invalid_instruction;
}
AstNode *new_stack_node = node->data.fn_call_expr.params.at(0);
IrInstruction *new_stack = ir_gen_node(irb, new_stack_node, scope);
if (new_stack == irb->codegen->invalid_instruction)
return new_stack;
AstNode *fn_ref_node = node->data.fn_call_expr.params.at(1);
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 - 2;
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 + 2);
args[i] = ir_gen_node(irb, arg_node, scope);
if (args[i] == irb->codegen->invalid_instruction)
return args[i];
}
IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, false, nullptr, new_stack);
return ir_lval_wrap(irb, scope, call, lval);
}
case BuiltinFnIdTypeId:
{
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;
IrInstruction *type_id = ir_build_type_id(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, type_id, lval);
}
case BuiltinFnIdShlExact:
{
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;
IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, arg0_value, arg1_value, true);
return ir_lval_wrap(irb, scope, bin_op, lval);
}
case BuiltinFnIdShrExact:
{
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;
IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, arg0_value, arg1_value, true);
return ir_lval_wrap(irb, scope, bin_op, lval);
}
case BuiltinFnIdSetEvalBranchQuota:
{
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;
IrInstruction *set_eval_branch_quota = ir_build_set_eval_branch_quota(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, set_eval_branch_quota, lval);
}
case BuiltinFnIdAlignCast:
{
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;
IrInstruction *align_cast = ir_build_align_cast(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, align_cast, lval);
}
case BuiltinFnIdOpaqueType:
{
IrInstruction *opaque_type = ir_build_opaque_type(irb, scope, node);
return ir_lval_wrap(irb, scope, opaque_type, lval);
}
case BuiltinFnIdThis:
{
IrInstruction *this_inst = ir_gen_this(irb, scope, node);
return ir_lval_wrap(irb, scope, this_inst, lval);
}
case BuiltinFnIdSetAlignStack:
{
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;
IrInstruction *set_align_stack = ir_build_set_align_stack(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, set_align_stack, lval);
}
case BuiltinFnIdArgType:
{
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;
IrInstruction *arg_type = ir_build_arg_type(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, arg_type, lval);
}
case BuiltinFnIdExport:
{
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;
IrInstruction *ir_export = ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value);
return ir_lval_wrap(irb, scope, ir_export, lval);
}
case BuiltinFnIdErrorReturnTrace:
{
IrInstruction *error_return_trace = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::Null);
return ir_lval_wrap(irb, scope, error_return_trace, lval);
}
case BuiltinFnIdAtomicRmw:
{
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_atomic_rmw(irb, scope, node, arg0_value, arg1_value, arg2_value, arg3_value,
arg4_value,
// these 2 values don't mean anything since we passed non-null values for other args
AtomicRmwOp_xchg, AtomicOrderMonotonic);
}
case BuiltinFnIdAtomicLoad:
{
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_atomic_load(irb, scope, node, arg0_value, arg1_value, arg2_value,
// this value does not mean anything since we passed non-null values for other arg
AtomicOrderMonotonic);
}
case BuiltinFnIdIntToEnum:
{
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;
IrInstruction *result = ir_build_int_to_enum(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, result, lval);
}
case BuiltinFnIdEnumToInt:
{
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;
IrInstruction *result = ir_build_enum_to_int(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, result, lval);
}
case BuiltinFnIdBswap:
{
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;
IrInstruction *result = ir_build_bswap(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, result, lval);
}
case BuiltinFnIdBitReverse:
{
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;
IrInstruction *result = ir_build_bit_reverse(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, result, lval);
}
}
zig_unreachable();
}
static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
assert(node->type == NodeTypeFnCallExpr);
if (node->data.fn_call_expr.is_builtin)
return ir_gen_builtin_fn_call(irb, scope, node, lval);
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];
}
bool is_async = node->data.fn_call_expr.is_async;
IrInstruction *async_allocator = nullptr;
if (is_async) {
if (node->data.fn_call_expr.async_allocator) {
async_allocator = ir_gen_node(irb, node->data.fn_call_expr.async_allocator, scope);
if (async_allocator == irb->codegen->invalid_instruction)
return async_allocator;
}
}
IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto,
is_async, async_allocator, nullptr);
return ir_lval_wrap(irb, scope, fn_call, lval);
}
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 irb->codegen->invalid_instruction;
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_create_basic_block(irb, scope, "Then");
IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "Else");
IrBasicBlock *endif_block = ir_create_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_and_append_block(irb, then_block);
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
IrInstruction *then_expr_result = ir_gen_node(irb, then_node, subexpr_scope);
if (then_expr_result == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
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_and_append_block(irb, else_block);
IrInstruction *else_expr_result;
if (else_node) {
else_expr_result = ir_gen_node(irb, else_node, subexpr_scope);
if (else_expr_result == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
} 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_and_append_block(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, LVal 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, LValNone);
}
static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval) {
if (lval != LValPtr)
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, false, false);
}
static PtrLen star_token_to_ptr_len(TokenId token_id) {
switch (token_id) {
case TokenIdStar:
case TokenIdStarStar:
return PtrLenSingle;
case TokenIdBracketStarBracket:
return PtrLenUnknown;
case TokenIdBracketStarCBracket:
return PtrLenC;
default:
zig_unreachable();
}
}
static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypePointerType);
PtrLen ptr_len = star_token_to_ptr_len(node->data.pointer_type.star_token->id);
bool is_const = node->data.pointer_type.is_const;
bool is_volatile = node->data.pointer_type.is_volatile;
bool is_allow_zero = node->data.pointer_type.allow_zero_token != nullptr;
AstNode *expr_node = node->data.pointer_type.op_expr;
AstNode *align_expr = node->data.pointer_type.align_expr;
IrInstruction *align_value;
if (align_expr != nullptr) {
align_value = ir_gen_node(irb, align_expr, scope);
if (align_value == irb->codegen->invalid_instruction)
return align_value;
} else {
align_value = nullptr;
}
IrInstruction *child_type = ir_gen_node(irb, expr_node, scope);
if (child_type == irb->codegen->invalid_instruction)
return child_type;
uint32_t bit_offset_start = 0;
if (node->data.pointer_type.bit_offset_start != nullptr) {
if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_start, 32, false)) {
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_start, 10);
exec_add_error_node(irb->codegen, irb->exec, node,
buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf)));
return irb->codegen->invalid_instruction;
}
bit_offset_start = bigint_as_unsigned(node->data.pointer_type.bit_offset_start);
}
uint32_t host_int_bytes = 0;
if (node->data.pointer_type.host_int_bytes != nullptr) {
if (!bigint_fits_in_bits(node->data.pointer_type.host_int_bytes, 32, false)) {
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, node->data.pointer_type.host_int_bytes, 10);
exec_add_error_node(irb->codegen, irb->exec, node,
buf_sprintf("value %s too large for u32 byte count", buf_ptr(val_buf)));
return irb->codegen->invalid_instruction;
}
host_int_bytes = bigint_as_unsigned(node->data.pointer_type.host_int_bytes);
}
if (host_int_bytes != 0 && bit_offset_start >= host_int_bytes * 8) {
exec_add_error_node(irb->codegen, irb->exec, node,
buf_sprintf("bit offset starts after end of host integer"));
return irb->codegen->invalid_instruction;
}
return ir_build_ptr_type(irb, scope, node, child_type, is_const, is_volatile,
ptr_len, align_value, bit_offset_start, host_int_bytes, is_allow_zero);
}
static IrInstruction *ir_gen_catch_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node,
LVal lval)
{
IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr);
if (err_union_ptr == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, scope, source_node, err_union_ptr, true);
if (payload_ptr == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
if (lval == LValPtr)
return payload_ptr;
return ir_build_load_ptr(irb, scope, source_node, payload_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, LVal 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 PrefixOpOptional:
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval);
case PrefixOpAddrOf: {
AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LValPtr), 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;
if (buf_eql_str(variable_declaration->symbol, "_")) {
add_node_error(irb->codegen, node, buf_sprintf("`_` is not a declarable symbol"));
return irb->codegen->invalid_instruction;
}
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_comptime);
ZigVar *var = ir_create_var(irb, node, scope, variable_declaration->symbol,
is_const, is_const, is_shadowable, is_comptime);
// we detect IrInstructionIdDeclVarSrc in gen_block to make sure the next node
// is inside var->child_scope
if (!is_extern && !variable_declaration->expr) {
var->var_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 *align_value = nullptr;
if (variable_declaration->align_expr != nullptr) {
align_value = ir_gen_node(irb, variable_declaration->align_expr, scope);
if (align_value == irb->codegen->invalid_instruction)
return align_value;
}
if (variable_declaration->section_expr != nullptr) {
add_node_error(irb->codegen, variable_declaration->section_expr,
buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));
}
if (variable_declaration->threadlocal_tok != nullptr) {
add_token_error(irb->codegen, node->owner, variable_declaration->threadlocal_tok,
buf_sprintf("function-local variable '%s' cannot be threadlocal", buf_ptr(variable_declaration->symbol)));
}
// Temporarily set the name of the IrExecutable to the VariableDeclaration
// so that the struct or enum from the init expression inherits the name.
Buf *old_exec_name = irb->exec->name;
irb->exec->name = variable_declaration->symbol;
IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope);
irb->exec->name = old_exec_name;
if (init_value == irb->codegen->invalid_instruction)
return init_value;
return ir_build_var_decl_src(irb, scope, node, var, type_instruction, align_value, 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;
AstNode *else_node = node->data.while_expr.else_node;
IrBasicBlock *cond_block = ir_create_basic_block(irb, scope, "WhileCond");
IrBasicBlock *body_block = ir_create_basic_block(irb, scope, "WhileBody");
IrBasicBlock *continue_block = continue_expr_node ?
ir_create_basic_block(irb, scope, "WhileContinue") : cond_block;
IrBasicBlock *end_block = ir_create_basic_block(irb, scope, "WhileEnd");
IrBasicBlock *else_block = else_node ?
ir_create_basic_block(irb, scope, "WhileElse") : end_block;
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);
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
Buf *var_symbol = node->data.while_expr.var_symbol;
Buf *err_symbol = node->data.while_expr.err_symbol;
if (err_symbol != nullptr) {
ir_set_cursor_at_end_and_append_block(irb, cond_block);
Scope *payload_scope;
AstNode *symbol_node = node; // TODO make more accurate
ZigVar *payload_var;
if (var_symbol) {
// TODO make it an error to write to payload variable
payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol,
true, false, false, is_comptime);
payload_scope = payload_var->child_scope;
} else {
payload_scope = subexpr_scope;
}
IrInstruction *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope, LValPtr);
if (err_val_ptr == irb->codegen->invalid_instruction)
return err_val_ptr;
IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr);
IrInstruction *is_err = ir_build_test_err(irb, scope, node->data.while_expr.condition, err_val);
IrBasicBlock *after_cond_block = irb->current_basic_block;
IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
if (!instr_is_unreachable(is_err)) {
ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_err,
else_block, body_block, is_comptime));
}
ir_set_cursor_at_end_and_append_block(irb, body_block);
if (var_symbol) {
IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, payload_scope, symbol_node,
err_val_ptr, false);
IrInstruction *var_value = node->data.while_expr.var_is_ptr ?
var_ptr_value : ir_build_load_ptr(irb, payload_scope, symbol_node, var_ptr_value);
ir_build_var_decl_src(irb, payload_scope, symbol_node, payload_var, nullptr, nullptr, var_value);
}
ZigList<IrInstruction *> incoming_values = {0};
ZigList<IrBasicBlock *> incoming_blocks = {0};
ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, payload_scope);
loop_scope->break_block = end_block;
loop_scope->continue_block = continue_block;
loop_scope->is_comptime = is_comptime;
loop_scope->incoming_blocks = &incoming_blocks;
loop_scope->incoming_values = &incoming_values;
IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base);
if (body_result == irb->codegen->invalid_instruction)
return body_result;
if (!instr_is_unreachable(body_result)) {
ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, node->data.while_expr.body, body_result));
ir_mark_gen(ir_build_br(irb, payload_scope, node, continue_block, is_comptime));
}
if (continue_expr_node) {
ir_set_cursor_at_end_and_append_block(irb, continue_block);
IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, payload_scope);
if (expr_result == irb->codegen->invalid_instruction)
return expr_result;
if (!instr_is_unreachable(expr_result)) {
ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, continue_expr_node, expr_result));
ir_mark_gen(ir_build_br(irb, payload_scope, node, cond_block, is_comptime));
}
}
ir_set_cursor_at_end_and_append_block(irb, else_block);
assert(else_node != nullptr);
// TODO make it an error to write to error variable
AstNode *err_symbol_node = else_node; // TODO make more accurate
ZigVar *err_var = ir_create_var(irb, err_symbol_node, scope, err_symbol,
true, false, false, is_comptime);
Scope *err_scope = err_var->child_scope;
IrInstruction *err_var_value = ir_build_unwrap_err_code(irb, err_scope, err_symbol_node, err_val_ptr);
ir_build_var_decl_src(irb, err_scope, symbol_node, err_var, nullptr, nullptr, err_var_value);
IrInstruction *else_result = ir_gen_node(irb, else_node, err_scope);
if (else_result == irb->codegen->invalid_instruction)
return else_result;
if (!instr_is_unreachable(else_result))
ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
IrBasicBlock *after_else_block = irb->current_basic_block;
ir_set_cursor_at_end_and_append_block(irb, end_block);
if (else_result) {
incoming_blocks.append(after_else_block);
incoming_values.append(else_result);
} else {
incoming_blocks.append(after_cond_block);
incoming_values.append(void_else_result);
}
return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
} else if (var_symbol != nullptr) {
ir_set_cursor_at_end_and_append_block(irb, cond_block);
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
// TODO make it an error to write to payload variable
AstNode *symbol_node = node; // TODO make more accurate
ZigVar *payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol,
true, false, false, is_comptime);
Scope *child_scope = payload_var->child_scope;
IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope, LValPtr);
if (maybe_val_ptr == irb->codegen->invalid_instruction)
return maybe_val_ptr;
IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr);
IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node->data.while_expr.condition, maybe_val);
IrBasicBlock *after_cond_block = irb->current_basic_block;
IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
if (!instr_is_unreachable(is_non_null)) {
ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_non_null,
body_block, else_block, is_comptime));
}
ir_set_cursor_at_end_and_append_block(irb, body_block);
IrInstruction *var_ptr_value = ir_build_optional_unwrap_ptr(irb, child_scope, symbol_node, maybe_val_ptr, false);
IrInstruction *var_value = node->data.while_expr.var_is_ptr ?
var_ptr_value : ir_build_load_ptr(irb, child_scope, symbol_node, var_ptr_value);
ir_build_var_decl_src(irb, child_scope, symbol_node, payload_var, nullptr, nullptr, var_value);
ZigList<IrInstruction *> incoming_values = {0};
ZigList<IrBasicBlock *> incoming_blocks = {0};
ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope);
loop_scope->break_block = end_block;
loop_scope->continue_block = continue_block;
loop_scope->is_comptime = is_comptime;
loop_scope->incoming_blocks = &incoming_blocks;
loop_scope->incoming_values = &incoming_values;
IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base);
if (body_result == irb->codegen->invalid_instruction)
return body_result;
if (!instr_is_unreachable(body_result)) {
ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.while_expr.body, body_result));
ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime));
}
if (continue_expr_node) {
ir_set_cursor_at_end_and_append_block(irb, continue_block);
IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, child_scope);
if (expr_result == irb->codegen->invalid_instruction)
return expr_result;
if (!instr_is_unreachable(expr_result)) {
ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, continue_expr_node, expr_result));
ir_mark_gen(ir_build_br(irb, child_scope, node, cond_block, is_comptime));
}
}
IrInstruction *else_result = nullptr;
if (else_node) {
ir_set_cursor_at_end_and_append_block(irb, else_block);
else_result = ir_gen_node(irb, else_node, scope);
if (else_result == irb->codegen->invalid_instruction)
return else_result;
if (!instr_is_unreachable(else_result))
ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
}
IrBasicBlock *after_else_block = irb->current_basic_block;
ir_set_cursor_at_end_and_append_block(irb, end_block);
if (else_result) {
incoming_blocks.append(after_else_block);
incoming_values.append(else_result);
} else {
incoming_blocks.append(after_cond_block);
incoming_values.append(void_else_result);
}
return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
} else {
ir_set_cursor_at_end_and_append_block(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;
IrBasicBlock *after_cond_block = irb->current_basic_block;
IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
if (!instr_is_unreachable(cond_val)) {
ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val,
body_block, else_block, is_comptime));
}
ir_set_cursor_at_end_and_append_block(irb, body_block);
ZigList<IrInstruction *> incoming_values = {0};
ZigList<IrBasicBlock *> incoming_blocks = {0};
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, subexpr_scope);
loop_scope->break_block = end_block;
loop_scope->continue_block = continue_block;
loop_scope->is_comptime = is_comptime;
loop_scope->incoming_blocks = &incoming_blocks;
loop_scope->incoming_values = &incoming_values;
IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base);
if (body_result == irb->codegen->invalid_instruction)
return body_result;
if (!instr_is_unreachable(body_result)) {
ir_mark_gen(ir_build_check_statement_is_void(irb, scope, node->data.while_expr.body, body_result));
ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime));
}
if (continue_expr_node) {
ir_set_cursor_at_end_and_append_block(irb, continue_block);
IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, subexpr_scope);
if (expr_result == irb->codegen->invalid_instruction)
return expr_result;
if (!instr_is_unreachable(expr_result)) {
ir_mark_gen(ir_build_check_statement_is_void(irb, scope, continue_expr_node, expr_result));
ir_mark_gen(ir_build_br(irb, scope, node, cond_block, is_comptime));
}
}
IrInstruction *else_result = nullptr;
if (else_node) {
ir_set_cursor_at_end_and_append_block(irb, else_block);
else_result = ir_gen_node(irb, else_node, subexpr_scope);
if (else_result == irb->codegen->invalid_instruction)
return else_result;
if (!instr_is_unreachable(else_result))
ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
}
IrBasicBlock *after_else_block = irb->current_basic_block;
ir_set_cursor_at_end_and_append_block(irb, end_block);
if (else_result) {
incoming_blocks.append(after_else_block);
incoming_values.append(else_result);
} else {
incoming_blocks.append(after_cond_block);
incoming_values.append(void_else_result);
}
return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
}
}
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;
AstNode *else_node = node->data.for_expr.else_node;
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, LValPtr);
if (array_val_ptr == irb->codegen->invalid_instruction)
return array_val_ptr;
IrInstruction *pointer_type = ir_build_to_ptr_type(irb, parent_scope, array_node, array_val_ptr);
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);
// TODO make it an error to write to element variable or i variable.
Buf *elem_var_name = elem_node->data.symbol_expr.symbol;
ZigVar *elem_var = ir_create_var(irb, elem_node, parent_scope, elem_var_name, true, false, false, is_comptime);
Scope *child_scope = elem_var->child_scope;
IrInstruction *undefined_value = ir_build_const_undefined(irb, child_scope, elem_node);
ir_build_var_decl_src(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, undefined_value);
IrInstruction *elem_var_ptr = ir_build_var_ptr(irb, child_scope, node, elem_var);
AstNode *index_var_source_node;
ZigVar *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);
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_src(irb, child_scope, index_var_source_node, index_var, usize, nullptr, zero);
IrInstruction *index_ptr = ir_build_var_ptr(irb, child_scope, node, index_var);
IrBasicBlock *cond_block = ir_create_basic_block(irb, child_scope, "ForCond");
IrBasicBlock *body_block = ir_create_basic_block(irb, child_scope, "ForBody");
IrBasicBlock *end_block = ir_create_basic_block(irb, child_scope, "ForEnd");
IrBasicBlock *else_block = else_node ? ir_create_basic_block(irb, child_scope, "ForElse") : end_block;
IrBasicBlock *continue_block = ir_create_basic_block(irb, child_scope, "ForContinue");
Buf *len_field_name = buf_create_from_str("len");
IrInstruction *len_ref = ir_build_field_ptr(irb, child_scope, node, array_val_ptr, len_field_name);
IrInstruction *len_val = ir_build_load_ptr(irb, child_scope, node, len_ref);
ir_build_br(irb, child_scope, node, cond_block, is_comptime);
ir_set_cursor_at_end_and_append_block(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);
IrBasicBlock *after_cond_block = irb->current_basic_block;
IrInstruction *void_else_value = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, parent_scope, node));
ir_mark_gen(ir_build_cond_br(irb, child_scope, node, cond, body_block, else_block, is_comptime));
ir_set_cursor_at_end_and_append_block(irb, body_block);
IrInstruction *elem_ptr = ir_build_elem_ptr(irb, child_scope, node, array_val_ptr, index_val, false, PtrLenSingle);
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));
ZigList<IrInstruction *> incoming_values = {0};
ZigList<IrBasicBlock *> incoming_blocks = {0};
ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope);
loop_scope->break_block = end_block;
loop_scope->continue_block = continue_block;
loop_scope->is_comptime = is_comptime;
loop_scope->incoming_blocks = &incoming_blocks;
loop_scope->incoming_values = &incoming_values;
IrInstruction *body_result = ir_gen_node(irb, body_node, &loop_scope->base);
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_and_append_block(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);
IrInstruction *else_result = nullptr;
if (else_node) {
ir_set_cursor_at_end_and_append_block(irb, else_block);
else_result = ir_gen_node(irb, else_node, parent_scope);
if (else_result == irb->codegen->invalid_instruction)
return else_result;
if (!instr_is_unreachable(else_result))
ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime));
}
IrBasicBlock *after_else_block = irb->current_basic_block;
ir_set_cursor_at_end_and_append_block(irb, end_block);
if (else_result) {
incoming_blocks.append(after_else_block);
incoming_values.append(else_result);
} else {
incoming_blocks.append(after_cond_block);
incoming_values.append(void_else_value);
}
return ir_build_phi(irb, parent_scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
}
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_enum_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeEnumLiteral);
Buf *name = &node->data.enum_literal.identifier->data.str_lit.str;
return ir_build_const_enum_literal(irb, scope, node, name);
}
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;
bool is_volatile = node->data.array_type.is_volatile;
bool is_allow_zero = node->data.array_type.allow_zero_token != nullptr;
AstNode *align_expr = node->data.array_type.align_expr;
Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope);
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;
}
if (is_volatile) {
add_node_error(irb->codegen, node, buf_create_from_str("volatile qualifier invalid on array type"));
return irb->codegen->invalid_instruction;
}
if (is_allow_zero) {
add_node_error(irb->codegen, node, buf_create_from_str("allowzero qualifier invalid on array type"));
return irb->codegen->invalid_instruction;
}
if (align_expr != nullptr) {
add_node_error(irb->codegen, node, buf_create_from_str("align qualifier invalid on array type"));
return irb->codegen->invalid_instruction;
}
IrInstruction *size_value = ir_gen_node(irb, size_node, comptime_scope);
if (size_value == irb->codegen->invalid_instruction)
return size_value;
IrInstruction *child_type = ir_gen_node(irb, child_type_node, comptime_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 *align_value;
if (align_expr != nullptr) {
align_value = ir_gen_node(irb, align_expr, comptime_scope);
if (align_value == irb->codegen->invalid_instruction)
return align_value;
} else {
align_value = nullptr;
}
IrInstruction *child_type = ir_gen_node(irb, child_type_node, comptime_scope);
if (child_type == irb->codegen->invalid_instruction)
return child_type;
return ir_build_slice_type(irb, scope, node, child_type, is_const, is_volatile, align_value, is_allow_zero);
}
}
static IrInstruction *ir_gen_promise_type(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypePromiseType);
AstNode *payload_type_node = node->data.promise_type.payload_type;
IrInstruction *payload_type_value = nullptr;
if (payload_type_node != nullptr) {
payload_type_value = ir_gen_node(irb, payload_type_node, scope);
if (payload_type_value == irb->codegen->invalid_instruction)
return payload_type_value;
}
return ir_build_promise_type(irb, scope, node, payload_type_value);
}
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 Error parse_asm_template(IrBuilder *irb, AstNode *source_node, Buf *asm_template,
ZigList<AsmToken> *tok_list)
{
// TODO Connect the errors in this function back up to the actual source location
// rather than just the token. https://github.com/ziglang/zig/issues/2080
enum State {
StateStart,
StatePercent,
StateTemplate,
StateVar,
};
assert(tok_list->length == 0);
AsmToken *cur_tok = nullptr;
enum State state = StateStart;
for (size_t i = 0; i < buf_len(asm_template); i += 1) {
uint8_t c = *((uint8_t*)buf_ptr(asm_template) + i);
switch (state) {
case StateStart:
if (c == '%') {
tok_list->add_one();
cur_tok = &tok_list->last();
cur_tok->id = AsmTokenIdPercent;
cur_tok->start = i;
state = StatePercent;
} else {
tok_list->add_one();
cur_tok = &tok_list->last();
cur_tok->id = AsmTokenIdTemplate;
cur_tok->start = i;
state = StateTemplate;
}
break;
case StatePercent:
if (c == '%') {
cur_tok->end = i;
state = StateStart;
} else if (c == '[') {
cur_tok->id = AsmTokenIdVar;
state = StateVar;
} else if (c == '=') {
cur_tok->id = AsmTokenIdUniqueId;
cur_tok->end = i;
state = StateStart;
} else {
add_node_error(irb->codegen, source_node,
buf_create_from_str("expected a '%' or '['"));
return ErrorSemanticAnalyzeFail;
}
break;
case StateTemplate:
if (c == '%') {
cur_tok->end = i;
i -= 1;
cur_tok = nullptr;
state = StateStart;
}
break;
case StateVar:
if (c == ']') {
cur_tok->end = i;
state = StateStart;
} else if ((c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') ||
(c == '_'))
{
// do nothing
} else {
add_node_error(irb->codegen, source_node,
buf_sprintf("invalid substitution character: '%c'", c));
return ErrorSemanticAnalyzeFail;
}
break;
}
}
switch (state) {
case StateStart:
break;
case StatePercent:
case StateVar:
add_node_error(irb->codegen, source_node, buf_sprintf("unexpected end of assembly template"));
return ErrorSemanticAnalyzeFail;
case StateTemplate:
cur_tok->end = buf_len(asm_template);
break;
}
return ErrorNone;
}
static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
Error err;
assert(node->type == NodeTypeAsmExpr);
AstNodeAsmExpr *asm_expr = &node->data.asm_expr;
bool is_volatile = asm_expr->volatile_token != nullptr;
bool in_fn_scope = (scope_fn_entry(scope) != nullptr);
Buf *template_buf = &asm_expr->asm_template->data.str_lit.str;
if (!in_fn_scope) {
if (is_volatile) {
add_token_error(irb->codegen, node->owner, asm_expr->volatile_token,
buf_sprintf("volatile is meaningless on global assembly"));
return irb->codegen->invalid_instruction;
}
if (asm_expr->output_list.length != 0 || asm_expr->input_list.length != 0 ||
asm_expr->clobber_list.length != 0)
{
add_node_error(irb->codegen, node,
buf_sprintf("global assembly cannot have inputs, outputs, or clobbers"));
return irb->codegen->invalid_instruction;
}
return ir_build_global_asm(irb, scope, node, template_buf);
}
ZigList<AsmToken> tok_list = {};
if ((err = parse_asm_template(irb, node, template_buf, &tok_list))) {
return irb->codegen->invalid_instruction;
}
IrInstruction **input_list = allocate<IrInstruction *>(asm_expr->input_list.length);
IrInstruction **output_types = allocate<IrInstruction *>(asm_expr->output_list.length);
ZigVar **output_vars = allocate<ZigVar *>(asm_expr->output_list.length);
size_t return_count = 0;
if (!is_volatile && 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 < asm_expr->output_list.length; i += 1) {
AsmOutput *asm_output = 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;
// TODO there is some duplication here with ir_gen_symbol. I need to do a full audit of how
// inline assembly works. https://github.com/ziglang/zig/issues/215
ZigVar *var = find_variable(irb->codegen, scope, variable_name, nullptr);
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;
}
}
const char modifier = *buf_ptr(asm_output->constraint);
if (modifier != '=') {
add_node_error(irb->codegen, node,
buf_sprintf("invalid modifier starting output constraint for '%s': '%c', only '=' is supported."
" Compiler TODO: see https://github.com/ziglang/zig/issues/215",
buf_ptr(asm_output->asm_symbolic_name), modifier));
return irb->codegen->invalid_instruction;
}
}
for (size_t i = 0; i < asm_expr->input_list.length; i += 1) {
AsmInput *asm_input = 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, template_buf, tok_list.items, tok_list.length,
input_list, output_types, output_vars, return_count, is_volatile);
}
static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeIfOptional);
Buf *var_symbol = node->data.test_expr.var_symbol;
AstNode *expr_node = node->data.test_expr.target_node;
AstNode *then_node = node->data.test_expr.then_node;
AstNode *else_node = node->data.test_expr.else_node;
bool var_is_ptr = node->data.test_expr.var_is_ptr;
IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr);
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_create_basic_block(irb, scope, "OptionalThen");
IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "OptionalElse");
IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "OptionalEndIf");
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_and_append_block(irb, then_block);
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
Scope *var_scope;
if (var_symbol) {
IrInstruction *var_type = nullptr;
bool is_shadowable = false;
bool is_const = true;
ZigVar *var = ir_create_var(irb, node, subexpr_scope,
var_symbol, is_const, is_const, is_shadowable, is_comptime);
IrInstruction *var_ptr_value = ir_build_optional_unwrap_ptr(irb, subexpr_scope, node, maybe_val_ptr, false);
IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value);
ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_value);
var_scope = var->child_scope;
} else {
var_scope = subexpr_scope;
}
IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_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_and_append_block(irb, else_block);
IrInstruction *else_expr_result;
if (else_node) {
else_expr_result = ir_gen_node(irb, else_node, subexpr_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_and_append_block(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_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeIfErrorExpr);
AstNode *target_node = node->data.if_err_expr.target_node;
AstNode *then_node = node->data.if_err_expr.then_node;
AstNode *else_node = node->data.if_err_expr.else_node;
bool var_is_ptr = node->data.if_err_expr.var_is_ptr;
bool var_is_const = true;
Buf *var_symbol = node->data.if_err_expr.var_symbol;
Buf *err_symbol = node->data.if_err_expr.err_symbol;
IrInstruction *err_val_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr);
if (err_val_ptr == irb->codegen->invalid_instruction)
return err_val_ptr;
IrInstruction *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr);
IrInstruction *is_err = ir_build_test_err(irb, scope, node, err_val);
IrBasicBlock *ok_block = ir_create_basic_block(irb, scope, "TryOk");
IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "TryElse");
IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "TryEnd");
bool force_comptime = ir_should_inline(irb->exec, scope);
IrInstruction *is_comptime = force_comptime ? ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, is_err);
ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, ok_block);
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
Scope *var_scope;
if (var_symbol) {
IrInstruction *var_type = nullptr;
bool is_shadowable = false;
IrInstruction *var_is_comptime = force_comptime ? ir_build_const_bool(irb, subexpr_scope, node, true) : ir_build_test_comptime(irb, subexpr_scope, node, err_val);
ZigVar *var = ir_create_var(irb, node, subexpr_scope,
var_symbol, var_is_const, var_is_const, is_shadowable, var_is_comptime);
IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, subexpr_scope, node, err_val_ptr, false);
IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value);
ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_value);
var_scope = var->child_scope;
} else {
var_scope = subexpr_scope;
}
IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_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_and_append_block(irb, else_block);
IrInstruction *else_expr_result;
if (else_node) {
Scope *err_var_scope;
if (err_symbol) {
IrInstruction *var_type = nullptr;
bool is_shadowable = false;
bool is_const = true;
ZigVar *var = ir_create_var(irb, node, subexpr_scope,
err_symbol, is_const, is_const, is_shadowable, is_comptime);
IrInstruction *var_value = ir_build_unwrap_err_code(irb, subexpr_scope, node, err_val_ptr);
ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_value);
err_var_scope = var->child_scope;
} else {
err_var_scope = subexpr_scope;
}
else_expr_result = ir_gen_node(irb, else_node, err_var_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_and_append_block(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 *var_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;
ZigVar *var = ir_create_var(irb, var_symbol_node, scope,
var_name, is_const, is_const, is_shadowable, var_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_src(irb, scope, var_symbol_node, var, var_type, nullptr, 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, LValPtr);
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_create_basic_block(irb, scope, "SwitchElse");
IrBasicBlock *end_block = ir_create_basic_block(irb, scope, "SwitchEnd");
size_t prong_count = node->data.switch_expr.prongs.length;
ZigList<IrInstructionSwitchBrCase> cases = {0};
IrInstruction *is_comptime;
IrInstruction *var_is_comptime;
if (ir_should_inline(irb->exec, scope)) {
is_comptime = ir_build_const_bool(irb, scope, node, true);
var_is_comptime = is_comptime;
} else {
is_comptime = ir_build_test_comptime(irb, scope, node, target_value);
var_is_comptime = ir_build_test_comptime(irb, scope, node, target_value_ptr);
}
ZigList<IrInstruction *> incoming_values = {0};
ZigList<IrBasicBlock *> incoming_blocks = {0};
ZigList<IrInstructionCheckSwitchProngsRange> check_ranges = {0};
// First do the else and the ranges
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
Scope *comptime_scope = create_comptime_scope(irb->codegen, 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_and_append_block(irb, else_block);
if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block,
is_comptime, var_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_create_basic_block(irb, scope, "SwitchRangeYes");
IrBasicBlock *range_block_no = ir_create_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_and_append_block(irb, range_block_yes);
if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block,
is_comptime, var_is_comptime, target_value_ptr, nullptr, &incoming_blocks, &incoming_values))
{
return irb->codegen->invalid_instruction;
}
ir_set_cursor_at_end_and_append_block(irb, range_block_no);
}
}
// next do the non-else non-ranges
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)
continue;
if (prong_node->data.switch_prong.any_items_are_range)
continue;
IrBasicBlock *prong_block = ir_create_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_and_append_block(irb, prong_block);
if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block,
is_comptime, var_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);
}
IrInstruction *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value, check_ranges.items, check_ranges.length,
else_prong != nullptr);
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, switch_prongs_void);
}
if (!else_prong) {
ir_set_cursor_at_end_and_append_block(irb, else_block);
ir_build_unreachable(irb, scope, node);
}
ir_set_cursor_at_end_and_append_block(irb, end_block);
assert(incoming_blocks.length == incoming_values.length);
if (incoming_blocks.length == 0) {
return ir_build_const_void(irb, scope, node);
} else {
return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
}
}
static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval) {
assert(node->type == NodeTypeCompTime);
Scope *child_scope = create_comptime_scope(irb->codegen, node, parent_scope);
return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval);
}
static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scope, AstNode *node, ScopeBlock *block_scope) {
IrInstruction *is_comptime;
if (ir_should_inline(irb->exec, break_scope)) {
is_comptime = ir_build_const_bool(irb, break_scope, node, true);
} else {
is_comptime = block_scope->is_comptime;
}
IrInstruction *result_value;
if (node->data.break_expr.expr) {
result_value = ir_gen_node(irb, node->data.break_expr.expr, break_scope);
if (result_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
} else {
result_value = ir_build_const_void(irb, break_scope, node);
}
IrBasicBlock *dest_block = block_scope->end_block;
ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false);
block_scope->incoming_blocks->append(irb->current_basic_block);
block_scope->incoming_values->append(result_value);
return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
}
static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) {
assert(node->type == NodeTypeBreak);
// Search up the scope. We'll find one of these things first:
// * function definition scope or global scope => error, break outside loop
// * defer expression scope => error, cannot break out of defer expression
// * loop scope => OK
// * (if it's a labeled break) labeled block => OK
Scope *search_scope = break_scope;
ScopeLoop *loop_scope;
for (;;) {
if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) {
if (node->data.break_expr.name != nullptr) {
add_node_error(irb->codegen, node, buf_sprintf("label not found: '%s'", buf_ptr(node->data.break_expr.name)));
return irb->codegen->invalid_instruction;
} else {
add_node_error(irb->codegen, node, buf_sprintf("break expression outside loop"));
return irb->codegen->invalid_instruction;
}
} else if (search_scope->id == ScopeIdDeferExpr) {
add_node_error(irb->codegen, node, buf_sprintf("cannot break out of defer expression"));
return irb->codegen->invalid_instruction;
} else if (search_scope->id == ScopeIdLoop) {
ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope;
if (node->data.break_expr.name == nullptr ||
(this_loop_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_loop_scope->name)))
{
loop_scope = this_loop_scope;
break;
}
} else if (search_scope->id == ScopeIdBlock) {
ScopeBlock *this_block_scope = (ScopeBlock *)search_scope;
if (node->data.break_expr.name != nullptr &&
(this_block_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_block_scope->name)))
{
assert(this_block_scope->end_block != nullptr);
return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);
}
} else if (search_scope->id == ScopeIdSuspend) {
add_node_error(irb->codegen, node, buf_sprintf("cannot break out of suspend block"));
return irb->codegen->invalid_instruction;
}
search_scope = search_scope->parent;
}
IrInstruction *is_comptime;
if (ir_should_inline(irb->exec, break_scope)) {
is_comptime = ir_build_const_bool(irb, break_scope, node, true);
} else {
is_comptime = loop_scope->is_comptime;
}
IrInstruction *result_value;
if (node->data.break_expr.expr) {
result_value = ir_gen_node(irb, node->data.break_expr.expr, break_scope);
if (result_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
} else {
result_value = ir_build_const_void(irb, break_scope, node);
}
IrBasicBlock *dest_block = loop_scope->break_block;
ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false);
loop_scope->incoming_blocks->append(irb->current_basic_block);
loop_scope->incoming_values->append(result_value);
return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
}
static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, AstNode *node) {
assert(node->type == NodeTypeContinue);
// Search up the scope. We'll find one of these things first:
// * function definition scope or global scope => error, break outside loop
// * defer expression scope => error, cannot break out of defer expression
// * loop scope => OK
ZigList<ScopeRuntime *> runtime_scopes = {};
Scope *search_scope = continue_scope;
ScopeLoop *loop_scope;
for (;;) {
if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) {
if (node->data.continue_expr.name != nullptr) {
add_node_error(irb->codegen, node, buf_sprintf("labeled loop not found: '%s'", buf_ptr(node->data.continue_expr.name)));
return irb->codegen->invalid_instruction;
} else {
add_node_error(irb->codegen, node, buf_sprintf("continue expression outside loop"));
return irb->codegen->invalid_instruction;
}
} else if (search_scope->id == ScopeIdDeferExpr) {
add_node_error(irb->codegen, node, buf_sprintf("cannot continue out of defer expression"));
return irb->codegen->invalid_instruction;
} else if (search_scope->id == ScopeIdLoop) {
ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope;
if (node->data.continue_expr.name == nullptr ||
(this_loop_scope->name != nullptr && buf_eql_buf(node->data.continue_expr.name, this_loop_scope->name)))
{
loop_scope = this_loop_scope;
break;
}
} else if (search_scope->id == ScopeIdRuntime) {
ScopeRuntime *scope_runtime = (ScopeRuntime *)search_scope;
runtime_scopes.append(scope_runtime);
}
search_scope = search_scope->parent;
}
IrInstruction *is_comptime;
if (ir_should_inline(irb->exec, continue_scope)) {
is_comptime = ir_build_const_bool(irb, continue_scope, node, true);
} else {
is_comptime = loop_scope->is_comptime;
}
for (size_t i = 0; i < runtime_scopes.length; i += 1) {
ScopeRuntime *scope_runtime = runtime_scopes.at(i);
ir_mark_gen(ir_build_check_runtime_scope(irb, continue_scope, node, scope_runtime->is_comptime, is_comptime));
}
IrBasicBlock *dest_block = loop_scope->continue_block;
ir_gen_defers_for_block(irb, continue_scope, dest_block->scope, false);
return ir_mark_gen(ir_build_br(irb, continue_scope, node, dest_block, is_comptime));
}
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_global_error_set);
}
static IrInstruction *ir_gen_defer(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
assert(node->type == NodeTypeDefer);
ScopeDefer *defer_child_scope = create_defer_scope(irb->codegen, node, parent_scope);
node->data.defer.child_scope = &defer_child_scope->base;
ScopeDeferExpr *defer_expr_scope = create_defer_expr_scope(irb->codegen, 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_extra(irb, array_node, scope, LValPtr);
if (ptr_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *start_value = ir_gen_node(irb, start_node, scope);
if (start_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, true);
}
static IrInstruction *ir_gen_catch(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;
if (op2_node->type == NodeTypeUnreachable) {
if (var_node != nullptr) {
assert(var_node->type == NodeTypeSymbol);
Buf *var_name = var_node->data.symbol_expr.symbol;
add_node_error(irb->codegen, var_node, buf_sprintf("unused variable: '%s'", buf_ptr(var_name)));
return irb->codegen->invalid_instruction;
}
return ir_gen_catch_unreachable(irb, parent_scope, node, op1_node, LValNone);
}
IrInstruction *err_union_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr);
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_create_basic_block(irb, parent_scope, "UnwrapErrOk");
IrBasicBlock *err_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrError");
IrBasicBlock *end_block = ir_create_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_and_append_block(irb, err_block);
Scope *err_scope;
if (var_node) {
assert(var_node->type == NodeTypeSymbol);
Buf *var_name = var_node->data.symbol_expr.symbol;
bool is_const = true;
bool is_shadowable = false;
ZigVar *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_src(irb, err_scope, var_node, var, nullptr, nullptr, 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_and_append_block(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_and_append_block(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(CodeGen *codegen, 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(codegen, 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, ',');
// TODO: const ptr reinterpret here to make the var type agree with the value?
render_const_value(codegen, name, var_scope->var->const_value);
return true;
}
static Buf *get_anon_type_name(CodeGen *codegen, IrExecutable *exec, const char *kind_name,
Scope *scope, AstNode *source_node, Buf *out_bare_name)
{
if (exec->name) {
ZigType *import = get_scope_import(scope);
Buf *namespace_name = buf_create_from_buf(&import->name);
if (buf_len(namespace_name) != 0) buf_append_char(namespace_name, NAMESPACE_SEP_CHAR);
buf_append_buf(namespace_name, exec->name);
buf_init_from_buf(out_bare_name, exec->name);
return namespace_name;
} else if (exec->name_fn != nullptr) {
Buf *name = buf_alloc();
buf_append_buf(name, &exec->name_fn->symbol_name);
buf_appendf(name, "(");
render_instance_name_recursive(codegen, name, &exec->name_fn->fndef_scope->base, exec->begin_scope);
buf_appendf(name, ")");
buf_init_from_buf(out_bare_name, name);
return name;
} else {
ZigType *import = get_scope_import(scope);
Buf *namespace_name = buf_create_from_buf(&import->name);
if (buf_len(namespace_name) != 0) buf_append_char(namespace_name, NAMESPACE_SEP_CHAR);
buf_appendf(namespace_name, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize, kind_name,
source_node->line + 1, source_node->column + 1);
buf_init_from_buf(out_bare_name, namespace_name);
return namespace_name;
}
}
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 *bare_name = buf_alloc();
Buf *name = get_anon_type_name(irb->codegen, irb->exec, container_string(kind), parent_scope, node, bare_name);
ContainerLayout layout = node->data.container_decl.layout;
ZigType *container_type = get_partial_container_type(irb->codegen, parent_scope,
kind, node, buf_ptr(name), bare_name, layout);
ScopeDecls *child_scope = get_container_scope(container_type);
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);
}
TldContainer *tld_container = allocate<TldContainer>(1);
init_tld(&tld_container->base, TldIdContainer, bare_name, VisibModPub, node, parent_scope);
tld_container->type_entry = container_type;
tld_container->decls_scope = child_scope;
irb->codegen->resolve_queue.append(&tld_container->base);
// Add this to the list to mark as invalid if analyzing this exec fails.
irb->exec->tld_list.append(&tld_container->base);
return ir_build_const_type(irb, parent_scope, node, container_type);
}
// errors should be populated with set1's values
static ZigType *get_error_set_union(CodeGen *g, ErrorTableEntry **errors, ZigType *set1, ZigType *set2) {
assert(set1->id == ZigTypeIdErrorSet);
assert(set2->id == ZigTypeIdErrorSet);
ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
err_set_type->size_in_bits = g->builtin_types.entry_global_error_set->size_in_bits;
err_set_type->abi_align = g->builtin_types.entry_global_error_set->abi_align;
err_set_type->abi_size = g->builtin_types.entry_global_error_set->abi_size;
buf_resize(&err_set_type->name, 0);
buf_appendf(&err_set_type->name, "error{");
for (uint32_t i = 0, count = set1->data.error_set.err_count; i < count; i += 1) {
assert(errors[set1->data.error_set.errors[i]->value] == set1->data.error_set.errors[i]);
}
uint32_t count = set1->data.error_set.err_count;
for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) {
ErrorTableEntry *error_entry = set2->data.error_set.errors[i];
if (errors[error_entry->value] == nullptr) {
count += 1;
}
}
err_set_type->data.error_set.err_count = count;
err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(count);
for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) {
ErrorTableEntry *error_entry = set1->data.error_set.errors[i];
buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name));
err_set_type->data.error_set.errors[i] = error_entry;
}
uint32_t index = set1->data.error_set.err_count;
for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) {
ErrorTableEntry *error_entry = set2->data.error_set.errors[i];
if (errors[error_entry->value] == nullptr) {
errors[error_entry->value] = error_entry;
buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name));
err_set_type->data.error_set.errors[index] = error_entry;
index += 1;
}
}
assert(index == count);
assert(count != 0);
buf_appendf(&err_set_type->name, "}");
return err_set_type;
}
static ZigType *make_err_set_with_one_item(CodeGen *g, Scope *parent_scope, AstNode *node,
ErrorTableEntry *err_entry)
{
ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
buf_resize(&err_set_type->name, 0);
buf_appendf(&err_set_type->name, "error{%s}", buf_ptr(&err_entry->name));
err_set_type->size_in_bits = g->builtin_types.entry_global_error_set->size_in_bits;
err_set_type->abi_align = g->builtin_types.entry_global_error_set->abi_align;
err_set_type->abi_size = g->builtin_types.entry_global_error_set->abi_size;
err_set_type->data.error_set.err_count = 1;
err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(1);
err_set_type->data.error_set.errors[0] = err_entry;
return err_set_type;
}
static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
assert(node->type == NodeTypeErrorSetDecl);
uint32_t err_count = node->data.err_set_decl.decls.length;
Buf bare_name = BUF_INIT;
Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error", parent_scope, node, &bare_name);
ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
buf_init_from_buf(&err_set_type->name, type_name);
err_set_type->data.error_set.err_count = err_count;
err_set_type->size_in_bits = irb->codegen->builtin_types.entry_global_error_set->size_in_bits;
err_set_type->abi_align = irb->codegen->builtin_types.entry_global_error_set->abi_align;
err_set_type->abi_size = irb->codegen->builtin_types.entry_global_error_set->abi_size;
err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(err_count);
ErrorTableEntry **errors = allocate<ErrorTableEntry *>(irb->codegen->errors_by_index.length + err_count);
for (uint32_t i = 0; i < err_count; i += 1) {
AstNode *symbol_node = node->data.err_set_decl.decls.at(i);
assert(symbol_node->type == NodeTypeSymbol);
Buf *err_name = symbol_node->data.symbol_expr.symbol;
ErrorTableEntry *err = allocate<ErrorTableEntry>(1);
err->decl_node = symbol_node;
buf_init_from_buf(&err->name, err_name);
auto existing_entry = irb->codegen->error_table.put_unique(err_name, err);
if (existing_entry) {
err->value = existing_entry->value->value;
} else {
size_t error_value_count = irb->codegen->errors_by_index.length;
assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)irb->codegen->err_tag_type->data.integral.bit_count));
err->value = error_value_count;
irb->codegen->errors_by_index.append(err);
}
err_set_type->data.error_set.errors[i] = err;
ErrorTableEntry *prev_err = errors[err->value];
if (prev_err != nullptr) {
ErrorMsg *msg = add_node_error(irb->codegen, err->decl_node, buf_sprintf("duplicate error: '%s'", buf_ptr(&err->name)));
add_error_note(irb->codegen, msg, prev_err->decl_node, buf_sprintf("other error here"));
return irb->codegen->invalid_instruction;
}
errors[err->value] = err;
}
free(errors);
return ir_build_const_type(irb, parent_scope, node, err_set_type);
}
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);
bool is_var_args = false;
for (size_t i = 0; i < param_count; i += 1) {
AstNode *param_node = node->data.fn_proto.params.at(i);
if (param_node->data.param_decl.is_var_args) {
is_var_args = true;
break;
}
if (param_node->data.param_decl.var_token == nullptr) {
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;
} else {
param_types[i] = nullptr;
}
}
IrInstruction *align_value = nullptr;
if (node->data.fn_proto.align_expr != nullptr) {
align_value = ir_gen_node(irb, node->data.fn_proto.align_expr, parent_scope);
if (align_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
}
IrInstruction *return_type;
if (node->data.fn_proto.return_var_token == nullptr) {
if (node->data.fn_proto.return_type == nullptr) {
return_type = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_void);
} else {
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;
}
} else {
add_node_error(irb->codegen, node,
buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447"));
return irb->codegen->invalid_instruction;
//return_type = nullptr;
}
IrInstruction *async_allocator_type_value = nullptr;
if (node->data.fn_proto.async_allocator_type != nullptr) {
async_allocator_type_value = ir_gen_node(irb, node->data.fn_proto.async_allocator_type, parent_scope);
if (async_allocator_type_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
}
return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type,
async_allocator_type_value, is_var_args);
}
static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode *node,
IrInstruction *target_inst, bool cancel_non_suspended, bool cancel_awaited)
{
IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "CancelDone");
IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled");
IrBasicBlock *pre_return_block = ir_create_basic_block(irb, scope, "PreReturn");
IrBasicBlock *post_return_block = ir_create_basic_block(irb, scope, "PostReturn");
IrBasicBlock *do_cancel_block = ir_create_basic_block(irb, scope, "DoCancel");
IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false);
IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node,
get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void));
IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111
IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100
IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
// TODO relies on Zig not re-ordering fields
IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst,
false);
IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst);
Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
atomic_state_field_name);
// set the is_canceled bit
IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
usize_type_val, atomic_state_ptr, nullptr, is_canceled_mask, nullptr,
AtomicRmwOp_or, AtomicOrderSeqCst);
IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, not_canceled_block);
IrInstruction *awaiter_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false);
IrInstruction *is_returned_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, awaiter_addr, ptr_mask, false);
ir_build_cond_br(irb, scope, node, is_returned_bool, post_return_block, pre_return_block, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, post_return_block);
if (cancel_awaited) {
ir_build_br(irb, scope, node, do_cancel_block, is_comptime);
} else {
IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false);
IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false);
ir_build_cond_br(irb, scope, node, is_awaited_bool, done_block, do_cancel_block, is_comptime);
}
ir_set_cursor_at_end_and_append_block(irb, pre_return_block);
if (cancel_awaited) {
if (cancel_non_suspended) {
ir_build_br(irb, scope, node, do_cancel_block, is_comptime);
} else {
IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false);
IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false);
ir_build_cond_br(irb, scope, node, is_suspended_bool, do_cancel_block, done_block, is_comptime);
}
} else {
ir_build_br(irb, scope, node, done_block, is_comptime);
}
ir_set_cursor_at_end_and_append_block(irb, do_cancel_block);
ir_build_cancel(irb, scope, node, target_inst);
ir_build_br(irb, scope, node, done_block, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, done_block);
return ir_build_const_void(irb, scope, node);
}
static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeCancel);
IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, scope);
if (target_inst == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
return ir_gen_cancel_target(irb, scope, node, target_inst, false, true);
}
static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode *node,
IrInstruction *target_inst)
{
IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "ResumeDone");
IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled");
IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "IsSuspended");
IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, scope, "IsNotSuspended");
IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
IrInstruction *and_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, is_suspended_mask);
IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false);
IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node,
get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void));
// TODO relies on Zig not re-ordering fields
IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst,
false);
IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst);
Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
atomic_state_field_name);
// clear the is_suspended bit
IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
usize_type_val, atomic_state_ptr, nullptr, and_mask, nullptr,
AtomicRmwOp_and, AtomicOrderSeqCst);
IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, not_canceled_block);
IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false);
IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false);
ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, not_suspended_block);
ir_build_unreachable(irb, scope, node);
ir_set_cursor_at_end_and_append_block(irb, suspended_block);
ir_build_coro_resume(irb, scope, node, target_inst);
ir_build_br(irb, scope, node, done_block, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, done_block);
return ir_build_const_void(irb, scope, node);
}
static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeResume);
IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, scope);
if (target_inst == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
return ir_gen_resume_target(irb, scope, node, target_inst);
}
static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeAwaitExpr);
IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, scope);
if (target_inst == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
ZigFn *fn_entry = exec_fn_entry(irb->exec);
if (!fn_entry) {
add_node_error(irb->codegen, node, buf_sprintf("await outside function definition"));
return irb->codegen->invalid_instruction;
}
if (fn_entry->type_entry->data.fn.fn_type_id.cc != CallingConventionAsync) {
add_node_error(irb->codegen, node, buf_sprintf("await in non-async function"));
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 await inside defer expression"));
scope_defer_expr->reported_err = true;
}
return irb->codegen->invalid_instruction;
}
Scope *outer_scope = irb->exec->begin_scope;
IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, target_inst);
Buf *result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);
IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name);
if (irb->codegen->have_err_ret_tracing) {
IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull);
Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME);
IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name);
ir_build_store_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr);
}
IrBasicBlock *already_awaited_block = ir_create_basic_block(irb, scope, "AlreadyAwaited");
IrBasicBlock *not_awaited_block = ir_create_basic_block(irb, scope, "NotAwaited");
IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled");
IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, scope, "YesSuspend");
IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, scope, "NoSuspend");
IrBasicBlock *merge_block = ir_create_basic_block(irb, scope, "MergeSuspend");
IrBasicBlock *cleanup_block = ir_create_basic_block(irb, scope, "SuspendCleanup");
IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "SuspendResume");
IrBasicBlock *cancel_target_block = ir_create_basic_block(irb, scope, "CancelTarget");
IrBasicBlock *do_cancel_block = ir_create_basic_block(irb, scope, "DoCancel");
IrBasicBlock *do_defers_block = ir_create_basic_block(irb, scope, "DoDefers");
IrBasicBlock *destroy_block = ir_create_basic_block(irb, scope, "DestroyBlock");
IrBasicBlock *my_suspended_block = ir_create_basic_block(irb, scope, "AlreadySuspended");
IrBasicBlock *my_not_suspended_block = ir_create_basic_block(irb, scope, "NotAlreadySuspended");
IrBasicBlock *do_suspend_block = ir_create_basic_block(irb, scope, "DoSuspend");
Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
atomic_state_field_name);
IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise);
IrInstruction *const_bool_false = ir_build_const_bool(irb, scope, node, false);
IrInstruction *undefined_value = ir_build_const_undefined(irb, scope, node);
IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111
IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100
IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
ZigVar *result_var = ir_create_var(irb, node, scope, nullptr,
false, false, true, const_bool_false);
IrInstruction *target_promise_type = ir_build_typeof(irb, scope, node, target_inst);
IrInstruction *promise_result_type = ir_build_promise_result_type(irb, scope, node, target_promise_type);
ir_build_await_bookkeeping(irb, scope, node, promise_result_type);
ir_build_var_decl_src(irb, scope, node, result_var, promise_result_type, nullptr, undefined_value);
IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, scope, node, result_var);
ir_build_store_ptr(irb, scope, node, result_ptr_field_ptr, my_result_var_ptr);
IrInstruction *save_token = ir_build_coro_save(irb, scope, node, irb->exec->coro_handle);
IrInstruction *coro_handle_addr = ir_build_ptr_to_int(irb, scope, node, irb->exec->coro_handle);
IrInstruction *mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, coro_handle_addr, await_mask, false);
IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
usize_type_val, atomic_state_ptr, nullptr, mask_bits, nullptr,
AtomicRmwOp_or, AtomicOrderSeqCst);
IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false);
IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false);
ir_build_cond_br(irb, scope, node, is_awaited_bool, already_awaited_block, not_awaited_block, const_bool_false);
ir_set_cursor_at_end_and_append_block(irb, already_awaited_block);
ir_build_unreachable(irb, scope, node);
ir_set_cursor_at_end_and_append_block(irb, not_awaited_block);
IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false);
IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false);
IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
ir_build_cond_br(irb, scope, node, is_canceled_bool, cancel_target_block, not_canceled_block, const_bool_false);
ir_set_cursor_at_end_and_append_block(irb, not_canceled_block);
ir_build_cond_br(irb, scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false);
ir_set_cursor_at_end_and_append_block(irb, cancel_target_block);
ir_build_cancel(irb, scope, node, target_inst);
ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false));
ir_set_cursor_at_end_and_append_block(irb, no_suspend_block);
if (irb->codegen->have_err_ret_tracing) {
Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME);
IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_field_name);
IrInstruction *dest_err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull);
ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr);
}
Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);
IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name);
// If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to,
// because we're about to destroy the memory. So we store it into our result variable.
IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr);
ir_build_store_ptr(irb, scope, node, my_result_var_ptr, no_suspend_result);
ir_build_cancel(irb, scope, node, target_inst);
ir_build_br(irb, scope, node, merge_block, const_bool_false);
ir_set_cursor_at_end_and_append_block(irb, yes_suspend_block);
IrInstruction *my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr,
AtomicRmwOp_or, AtomicOrderSeqCst);
IrInstruction *my_is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_suspended_mask, false);
IrInstruction *my_is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_is_suspended_value, zero, false);
ir_build_cond_br(irb, scope, node, my_is_suspended_bool, my_suspended_block, my_not_suspended_block, const_bool_false);
ir_set_cursor_at_end_and_append_block(irb, my_suspended_block);
ir_build_unreachable(irb, scope, node);
ir_set_cursor_at_end_and_append_block(irb, my_not_suspended_block);
IrInstruction *my_is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_canceled_mask, false);
IrInstruction *my_is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_is_canceled_value, zero, false);
ir_build_cond_br(irb, scope, node, my_is_canceled_bool, cleanup_block, do_suspend_block, const_bool_false);
ir_set_cursor_at_end_and_append_block(irb, do_suspend_block);
IrInstruction *suspend_code = ir_build_coro_suspend(irb, scope, node, save_token, const_bool_false);
IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(2);
cases[0].value = ir_build_const_u8(irb, scope, node, 0);
cases[0].block = resume_block;
cases[1].value = ir_build_const_u8(irb, scope, node, 1);
cases[1].block = destroy_block;
ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block,
2, cases, const_bool_false, nullptr);
ir_set_cursor_at_end_and_append_block(irb, destroy_block);
ir_gen_cancel_target(irb, scope, node, target_inst, false, true);
ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false));
ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
IrInstruction *my_mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, ptr_mask, is_canceled_mask, false);
IrInstruction *b_my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, my_mask_bits, nullptr,
AtomicRmwOp_or, AtomicOrderSeqCst);
IrInstruction *my_await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, b_my_prev_atomic_value, ptr_mask, false);
IrInstruction *dont_have_my_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, my_await_handle_addr, zero, false);
IrInstruction *dont_destroy_ourselves = ir_build_bin_op(irb, scope, node, IrBinOpBoolAnd, dont_have_my_await_handle, is_canceled_bool, false);
ir_build_cond_br(irb, scope, node, dont_have_my_await_handle, do_defers_block, do_cancel_block, const_bool_false);
ir_set_cursor_at_end_and_append_block(irb, do_cancel_block);
IrInstruction *my_await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, my_await_handle_addr);
ir_gen_cancel_target(irb, scope, node, my_await_handle, true, false);
ir_mark_gen(ir_build_br(irb, scope, node, do_defers_block, const_bool_false));
ir_set_cursor_at_end_and_append_block(irb, do_defers_block);
ir_gen_defers_for_block(irb, scope, outer_scope, true);
ir_mark_gen(ir_build_cond_br(irb, scope, node, dont_destroy_ourselves, irb->exec->coro_early_final, irb->exec->coro_final_cleanup_block, const_bool_false));
ir_set_cursor_at_end_and_append_block(irb, resume_block);
ir_build_br(irb, scope, node, merge_block, const_bool_false);
ir_set_cursor_at_end_and_append_block(irb, merge_block);
return ir_build_load_ptr(irb, scope, node, my_result_var_ptr);
}
static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
assert(node->type == NodeTypeSuspend);
ZigFn *fn_entry = exec_fn_entry(irb->exec);
if (!fn_entry) {
add_node_error(irb->codegen, node, buf_sprintf("suspend outside function definition"));
return irb->codegen->invalid_instruction;
}
if (fn_entry->type_entry->data.fn.fn_type_id.cc != CallingConventionAsync) {
add_node_error(irb->codegen, node, buf_sprintf("suspend in non-async function"));
return irb->codegen->invalid_instruction;
}
ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope);
if (scope_defer_expr) {
if (!scope_defer_expr->reported_err) {
ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside defer expression"));
add_error_note(irb->codegen, msg, scope_defer_expr->base.source_node, buf_sprintf("defer here"));
scope_defer_expr->reported_err = true;
}
return irb->codegen->invalid_instruction;
}
ScopeSuspend *existing_suspend_scope = get_scope_suspend(parent_scope);
if (existing_suspend_scope) {
if (!existing_suspend_scope->reported_err) {
ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside suspend block"));
add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("other suspend block here"));
existing_suspend_scope->reported_err = true;
}
return irb->codegen->invalid_instruction;
}
Scope *outer_scope = irb->exec->begin_scope;
IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup");
IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume");
IrBasicBlock *suspended_block = ir_create_basic_block(irb, parent_scope, "AlreadySuspended");
IrBasicBlock *canceled_block = ir_create_basic_block(irb, parent_scope, "IsCanceled");
IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, parent_scope, "NotCanceled");
IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, parent_scope, "NotAlreadySuspended");
IrBasicBlock *cancel_awaiter_block = ir_create_basic_block(irb, parent_scope, "CancelAwaiter");
IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_promise);
IrInstruction *const_bool_true = ir_build_const_bool(irb, parent_scope, node, true);
IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false);
IrInstruction *usize_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_usize);
IrInstruction *is_canceled_mask = ir_build_const_usize(irb, parent_scope, node, 0x1); // 0b001
IrInstruction *is_suspended_mask = ir_build_const_usize(irb, parent_scope, node, 0x2); // 0b010
IrInstruction *zero = ir_build_const_usize(irb, parent_scope, node, 0);
IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, parent_scope, node, 0x7); // 0b111
IrInstruction *ptr_mask = ir_build_un_op(irb, parent_scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, parent_scope, node,
usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr,
AtomicRmwOp_or, AtomicOrderSeqCst);
IrInstruction *is_canceled_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
IrInstruction *is_canceled_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
ir_build_cond_br(irb, parent_scope, node, is_canceled_bool, canceled_block, not_canceled_block, const_bool_false);
ir_set_cursor_at_end_and_append_block(irb, canceled_block);
IrInstruction *await_handle_addr = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false);
IrInstruction *have_await_handle = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false);
IrBasicBlock *post_canceled_block = irb->current_basic_block;
ir_build_cond_br(irb, parent_scope, node, have_await_handle, cancel_awaiter_block, cleanup_block, const_bool_false);
ir_set_cursor_at_end_and_append_block(irb, cancel_awaiter_block);
IrInstruction *await_handle = ir_build_int_to_ptr(irb, parent_scope, node, promise_type_val, await_handle_addr);
ir_gen_cancel_target(irb, parent_scope, node, await_handle, true, false);
IrBasicBlock *post_cancel_awaiter_block = irb->current_basic_block;
ir_build_br(irb, parent_scope, node, cleanup_block, const_bool_false);
ir_set_cursor_at_end_and_append_block(irb, not_canceled_block);
IrInstruction *is_suspended_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false);
IrInstruction *is_suspended_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false);
ir_build_cond_br(irb, parent_scope, node, is_suspended_bool, suspended_block, not_suspended_block, const_bool_false);
ir_set_cursor_at_end_and_append_block(irb, suspended_block);
ir_build_unreachable(irb, parent_scope, node);
ir_set_cursor_at_end_and_append_block(irb, not_suspended_block);
IrInstruction *suspend_code;
if (node->data.suspend.block == nullptr) {
suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false);
} else {
Scope *child_scope;
ScopeSuspend *suspend_scope = create_suspend_scope(irb->codegen, node, parent_scope);
suspend_scope->resume_block = resume_block;
child_scope = &suspend_scope->base;
IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle);
ir_gen_node(irb, node->data.suspend.block, child_scope);
suspend_code = ir_mark_gen(ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false));
}
IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(2);
cases[0].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 0));
cases[0].block = resume_block;
cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1));
cases[1].block = canceled_block;
ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,
2, cases, const_bool_false, nullptr));
ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
incoming_blocks[0] = post_canceled_block;
incoming_values[0] = const_bool_true;
incoming_blocks[1] = post_cancel_awaiter_block;
incoming_values[1] = const_bool_false;
IrInstruction *destroy_ourselves = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values);
ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);
ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, destroy_ourselves, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, const_bool_false));
ir_set_cursor_at_end_and_append_block(irb, resume_block);
return ir_mark_gen(ir_build_const_void(irb, parent_scope, node));
}
static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope,
LVal lval)
{
assert(scope);
switch (node->type) {
case NodeTypeStructValueField:
case NodeTypeParamDecl:
case NodeTypeUse:
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeStructField:
case NodeTypeFnDef:
case NodeTypeTestDecl:
zig_unreachable();
case NodeTypeBlock:
return ir_lval_wrap(irb, scope, ir_gen_block(irb, scope, node), lval);
case NodeTypeGroupedExpr:
return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval);
case NodeTypeBinOpExpr:
return ir_lval_wrap(irb, scope, ir_gen_bin_op(irb, scope, node), lval);
case NodeTypeIntLiteral:
return ir_lval_wrap(irb, scope, ir_gen_int_lit(irb, scope, node), lval);
case NodeTypeFloatLiteral:
return ir_lval_wrap(irb, scope, ir_gen_float_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_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:
{
IrInstruction *ptr_instruction = ir_gen_field_access(irb, scope, node);
if (ptr_instruction == irb->codegen->invalid_instruction)
return ptr_instruction;
if (lval == LValPtr)
return ptr_instruction;
return ir_build_load_ptr(irb, scope, node, ptr_instruction);
}
case NodeTypePtrDeref: {
AstNode *expr_node = node->data.ptr_deref_expr.target;
IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval);
if (value == irb->codegen->invalid_instruction)
return value;
// We essentially just converted any lvalue from &(x.*) to (&x).*;
// this inhibits checking that x is a pointer later, so we directly
// record whether the pointer check is needed
return ir_build_un_op_lval(irb, scope, node, IrUnOpDereference, value, lval);
}
case NodeTypeUnwrapOptional: {
AstNode *expr_node = node->data.unwrap_optional.expr;
IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr);
if (maybe_ptr == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, maybe_ptr, true);
if (lval == LValPtr)
return unwrapped_ptr;
return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
}
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 NodeTypePointerType:
return ir_lval_wrap(irb, scope, ir_gen_pointer_type(irb, scope, node), lval);
case NodeTypePromiseType:
return ir_lval_wrap(irb, scope, ir_gen_promise_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 NodeTypeIfErrorExpr:
return ir_lval_wrap(irb, scope, ir_gen_if_err_expr(irb, scope, node), lval);
case NodeTypeIfOptional:
return ir_lval_wrap(irb, scope, ir_gen_if_optional_expr(irb, scope, node), lval);
case NodeTypeSwitchExpr:
return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node), lval);
case NodeTypeCompTime:
return ir_gen_comptime(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 NodeTypeUnreachable:
return ir_lval_wrap(irb, scope, ir_build_unreachable(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_catch(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 NodeTypeErrorSetDecl:
return ir_lval_wrap(irb, scope, ir_gen_err_set_decl(irb, scope, node), lval);
case NodeTypeCancel:
return ir_lval_wrap(irb, scope, ir_gen_cancel(irb, scope, node), lval);
case NodeTypeResume:
return ir_lval_wrap(irb, scope, ir_gen_resume(irb, scope, node), lval);
case NodeTypeAwaitExpr:
return ir_lval_wrap(irb, scope, ir_gen_await_expr(irb, scope, node), lval);
case NodeTypeSuspend:
return ir_lval_wrap(irb, scope, ir_gen_suspend(irb, scope, node), lval);
case NodeTypeEnumLiteral:
return ir_lval_wrap(irb, scope, ir_gen_enum_literal(irb, scope, node), lval);
}
zig_unreachable();
}
static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal 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, LValNone);
}
static void invalidate_exec(IrExecutable *exec) {
if (exec->invalid)
return;
exec->invalid = true;
for (size_t i = 0; i < exec->tld_list.length; i += 1) {
exec->tld_list.items[i]->resolution = TldResolutionInvalid;
}
if (exec->source_exec != nullptr)
invalidate_exec(exec->source_exec);
}
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;
IrBasicBlock *entry_block = ir_create_basic_block(irb, scope, "Entry");
ir_set_cursor_at_end_and_append_block(irb, entry_block);
// Entry block gets a reference because we enter it to begin.
ir_ref_bb(irb->current_basic_block);
ZigFn *fn_entry = exec_fn_entry(irb->exec);
bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync;
IrInstruction *coro_id;
IrInstruction *u8_ptr_type;
IrInstruction *const_bool_false;
IrInstruction *coro_promise_ptr;
IrInstruction *err_ret_trace_ptr;
ZigType *return_type;
Buf *result_ptr_field_name;
ZigVar *coro_size_var;
if (is_async) {
// create the coro promise
Scope *coro_scope = create_coro_prelude_scope(irb->codegen, node, scope);
const_bool_false = ir_build_const_bool(irb, coro_scope, node, false);
ZigVar *promise_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);
return_type = fn_entry->type_entry->data.fn.fn_type_id.return_type;
IrInstruction *undef = ir_build_const_undefined(irb, coro_scope, node);
ZigType *coro_frame_type = get_promise_frame_type(irb->codegen, return_type);
IrInstruction *coro_frame_type_value = ir_build_const_type(irb, coro_scope, node, coro_frame_type);
// TODO mark this var decl as "no safety" e.g. disable initializing the undef value to 0xaa
ir_build_var_decl_src(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, undef);
coro_promise_ptr = ir_build_var_ptr(irb, coro_scope, node, promise_var);
ZigVar *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);
IrInstruction *null_value = ir_build_const_null(irb, coro_scope, node);
IrInstruction *await_handle_type_val = ir_build_const_type(irb, coro_scope, node,
get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise));
ir_build_var_decl_src(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, null_value);
irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, coro_scope, node, await_handle_var);
u8_ptr_type = ir_build_const_type(irb, coro_scope, node,
get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_u8, false));
IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type,
coro_promise_ptr, false);
coro_id = ir_build_coro_id(irb, coro_scope, node, promise_as_u8_ptr);
coro_size_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);
IrInstruction *coro_size = ir_build_coro_size(irb, coro_scope, node);
ir_build_var_decl_src(irb, coro_scope, node, coro_size_var, nullptr, nullptr, coro_size);
IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, coro_scope, node,
ImplicitAllocatorIdArg);
irb->exec->coro_allocator_var = ir_create_var(irb, node, coro_scope, nullptr, true, true, true, const_bool_false);
ir_build_var_decl_src(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr);
Buf *realloc_field_name = buf_create_from_str(ASYNC_REALLOC_FIELD_NAME);
IrInstruction *realloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, realloc_field_name);
IrInstruction *realloc_fn = ir_build_load_ptr(irb, coro_scope, node, realloc_fn_ptr);
IrInstruction *maybe_coro_mem_ptr = ir_build_coro_alloc_helper(irb, coro_scope, node, realloc_fn, coro_size);
IrInstruction *alloc_result_is_ok = ir_build_test_nonnull(irb, coro_scope, node, maybe_coro_mem_ptr);
IrBasicBlock *alloc_err_block = ir_create_basic_block(irb, coro_scope, "AllocError");
IrBasicBlock *alloc_ok_block = ir_create_basic_block(irb, coro_scope, "AllocOk");
ir_build_cond_br(irb, coro_scope, node, alloc_result_is_ok, alloc_ok_block, alloc_err_block, const_bool_false);
ir_set_cursor_at_end_and_append_block(irb, alloc_err_block);
// we can return undefined here, because the caller passes a pointer to the error struct field
// in the error union result, and we populate it in case of allocation failure.
ir_build_return(irb, coro_scope, node, undef);
ir_set_cursor_at_end_and_append_block(irb, alloc_ok_block);
IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr,
false);
irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr);
Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
irb->exec->atomic_state_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
atomic_state_field_name);
IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
ir_build_store_ptr(irb, scope, node, irb->exec->atomic_state_field_ptr, zero);
Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);
irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name);
result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);
irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name);
ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, irb->exec->coro_result_field_ptr);
if (irb->codegen->have_err_ret_tracing) {
// initialize the error return trace
Buf *return_addresses_field_name = buf_create_from_str(RETURN_ADDRESSES_FIELD_NAME);
IrInstruction *return_addresses_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, return_addresses_field_name);
Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME);
err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_field_name);
ir_build_mark_err_ret_trace_ptr(irb, scope, node, err_ret_trace_ptr);
// coordinate with builtin.zig
Buf *index_name = buf_create_from_str("index");
IrInstruction *index_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, index_name);
ir_build_store_ptr(irb, scope, node, index_ptr, zero);
Buf *instruction_addresses_name = buf_create_from_str("instruction_addresses");
IrInstruction *addrs_slice_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, instruction_addresses_name);
IrInstruction *slice_value = ir_build_slice(irb, scope, node, return_addresses_ptr, zero, nullptr, false);
ir_build_store_ptr(irb, scope, node, addrs_slice_ptr, slice_value);
}
irb->exec->coro_early_final = ir_create_basic_block(irb, scope, "CoroEarlyFinal");
irb->exec->coro_normal_final = ir_create_basic_block(irb, scope, "CoroNormalFinal");
irb->exec->coro_suspend_block = ir_create_basic_block(irb, scope, "Suspend");
irb->exec->coro_final_cleanup_block = ir_create_basic_block(irb, scope, "FinalCleanup");
}
IrInstruction *result = ir_gen_node_extra(irb, node, scope, LValNone);
assert(result);
if (irb->exec->invalid)
return false;
if (!instr_is_unreachable(result)) {
// no need for save_err_ret_addr because this cannot return error
ir_gen_async_return(irb, scope, result->source_node, result, true);
}
if (is_async) {
IrBasicBlock *invalid_resume_block = ir_create_basic_block(irb, scope, "InvalidResume");
IrBasicBlock *check_free_block = ir_create_basic_block(irb, scope, "CheckFree");
ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_early_final);
IrInstruction *const_bool_true = ir_build_const_bool(irb, scope, node, true);
IrInstruction *suspend_code = ir_build_coro_suspend(irb, scope, node, nullptr, const_bool_true);
IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(2);
cases[0].value = ir_build_const_u8(irb, scope, node, 0);
cases[0].block = invalid_resume_block;
cases[1].value = ir_build_const_u8(irb, scope, node, 1);
cases[1].block = irb->exec->coro_final_cleanup_block;
ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr);
ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_suspend_block);
ir_build_coro_end(irb, scope, node);
ir_build_return(irb, scope, node, irb->exec->coro_handle);
ir_set_cursor_at_end_and_append_block(irb, invalid_resume_block);
ir_build_unreachable(irb, scope, node);
ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_normal_final);
if (type_has_bits(return_type)) {
IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node,
get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8,
false, false, PtrLenUnknown, 0, 0, 0, false));
IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr);
IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len,
result_ptr, false);
IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node,
u8_ptr_type_unknown_len, irb->exec->coro_result_field_ptr, false);
IrInstruction *return_type_inst = ir_build_const_type(irb, scope, node,
fn_entry->type_entry->data.fn.fn_type_id.return_type);
IrInstruction *size_of_ret_val = ir_build_size_of(irb, scope, node, return_type_inst);
ir_build_memcpy(irb, scope, node, result_ptr_as_u8_ptr, return_value_ptr_as_u8_ptr, size_of_ret_val);
}
if (irb->codegen->have_err_ret_tracing) {
Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME);
IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name);
IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr);
ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr, dest_err_ret_trace_ptr);
}
// Before we destroy the coroutine frame, we need to load the target promise into
// a register or local variable which does not get spilled into the frame,
// otherwise llvm tries to access memory inside the destroyed frame.
IrInstruction *unwrapped_await_handle_ptr = ir_build_optional_unwrap_ptr(irb, scope, node,
irb->exec->await_handle_var_ptr, false);
IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr);
ir_build_br(irb, scope, node, check_free_block, const_bool_false);
ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_final_cleanup_block);
ir_build_br(irb, scope, node, check_free_block, const_bool_false);
ir_set_cursor_at_end_and_append_block(irb, check_free_block);
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
incoming_blocks[0] = irb->exec->coro_final_cleanup_block;
incoming_values[0] = const_bool_false;
incoming_blocks[1] = irb->exec->coro_normal_final;
incoming_values[1] = const_bool_true;
IrInstruction *resume_awaiter = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values);
IrBasicBlock **merge_incoming_blocks = allocate<IrBasicBlock *>(2);
IrInstruction **merge_incoming_values = allocate<IrInstruction *>(2);
merge_incoming_blocks[0] = irb->exec->coro_final_cleanup_block;
merge_incoming_values[0] = ir_build_const_undefined(irb, scope, node);
merge_incoming_blocks[1] = irb->exec->coro_normal_final;
merge_incoming_values[1] = await_handle_in_block;
IrInstruction *awaiter_handle = ir_build_phi(irb, scope, node, 2, merge_incoming_blocks, merge_incoming_values);
Buf *shrink_field_name = buf_create_from_str(ASYNC_SHRINK_FIELD_NAME);
IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node,
ImplicitAllocatorIdLocalVar);
IrInstruction *shrink_fn_ptr = ir_build_field_ptr(irb, scope, node, implicit_allocator_ptr, shrink_field_name);
IrInstruction *shrink_fn = ir_build_load_ptr(irb, scope, node, shrink_fn_ptr);
IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
IrInstruction *coro_mem_ptr_maybe = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle);
IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node,
get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8,
false, false, PtrLenUnknown, 0, 0, 0, false));
IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len,
coro_mem_ptr_maybe, false);
IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false);
IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var);
IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr);
IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false);
size_t arg_count = 5;
IrInstruction **args = allocate<IrInstruction *>(arg_count);
args[0] = implicit_allocator_ptr; // self
args[1] = mem_slice; // old_mem
args[2] = ir_build_const_usize(irb, scope, node, 8); // old_align
// TODO: intentional memory leak here. If this is set to 0 then there is an issue where a coroutine
// calls the function and it frees its own stack frame, but then the return value is a slice, which
// is implemented as an sret struct. writing to the return pointer causes invalid memory write.
// We could work around it by having a global helper function which has a void return type
// and calling that instead. But instead this hack will suffice until I rework coroutines to be
// non-allocating. Basically coroutines are not supported right now until they are reworked.
args[3] = ir_build_const_usize(irb, scope, node, 1); // new_size
args[4] = ir_build_const_usize(irb, scope, node, 1); // new_align
ir_build_call(irb, scope, node, nullptr, shrink_fn, arg_count, args, false, FnInlineAuto, false, nullptr, nullptr);
IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "Resume");
ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false);
ir_set_cursor_at_end_and_append_block(irb, resume_block);
ir_gen_resume_target(irb, scope, node, awaiter_handle);
ir_build_br(irb, scope, node, irb->exec->coro_suspend_block, const_bool_false);
}
return true;
}
bool ir_gen_fn(CodeGen *codegen, ZigFn *fn_entry) {
assert(fn_entry);
IrExecutable *ir_executable = &fn_entry->ir_executable;
AstNode *body_node = fn_entry->body_node;
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) {
invalidate_exec(exec);
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 *opt_ir_add_error_node(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, Buf *msg) {
if (ira != nullptr)
return exec_add_error_node(codegen, ira->new_irb.exec, source_node, msg);
else
return add_node_error(codegen, 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);
}
// This function takes a comptime ptr and makes the child const value conform to the type
// described by the pointer.
static Error eval_comptime_ptr_reinterpret(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node,
ConstExprValue *ptr_val)
{
Error err;
assert(ptr_val->type->id == ZigTypeIdPointer);
ConstExprValue tmp = {};
tmp.special = ConstValSpecialStatic;
tmp.type = ptr_val->type->data.pointer.child_type;
if ((err = ir_read_const_ptr(ira, codegen, source_node, &tmp, ptr_val)))
return err;
ConstExprValue *child_val = const_ptr_pointee_unchecked(codegen, ptr_val);
copy_const_val(child_val, &tmp, false);
return ErrorNone;
}
ConstExprValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ConstExprValue *const_val,
AstNode *source_node)
{
Error err;
ConstExprValue *val = const_ptr_pointee_unchecked(codegen, const_val);
assert(val != nullptr);
assert(const_val->type->id == ZigTypeIdPointer);
ZigType *expected_type = const_val->type->data.pointer.child_type;
if (!types_have_same_zig_comptime_repr(val->type, expected_type)) {
if ((err = eval_comptime_ptr_reinterpret(ira, codegen, source_node, const_val)))
return nullptr;
return const_ptr_pointee_unchecked(codegen, const_val);
}
return val;
}
static ConstExprValue *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->value;
}
return &value->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->value;
}
}
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 const_val_fits_in_num_lit(ConstExprValue *const_val, ZigType *num_lit_type) {
return ((num_lit_type->id == ZigTypeIdComptimeFloat &&
(const_val->type->id == ZigTypeIdFloat || const_val->type->id == ZigTypeIdComptimeFloat)) ||
(num_lit_type->id == ZigTypeIdComptimeInt &&
(const_val->type->id == ZigTypeIdInt || const_val->type->id == ZigTypeIdComptimeInt)));
}
static bool float_has_fraction(ConstExprValue *const_val) {
if (const_val->type->id == ZigTypeIdComptimeFloat) {
return bigfloat_has_fraction(&const_val->data.x_bigfloat);
} else if (const_val->type->id == ZigTypeIdFloat) {
switch (const_val->type->data.floating.bit_count) {
case 16:
{
float16_t floored = f16_roundToInt(const_val->data.x_f16, softfloat_round_minMag, false);
return !f16_eq(floored, const_val->data.x_f16);
}
case 32:
return floorf(const_val->data.x_f32) != const_val->data.x_f32;
case 64:
return floor(const_val->data.x_f64) != const_val->data.x_f64;
case 128:
{
float128_t floored;
f128M_roundToInt(&const_val->data.x_f128, softfloat_round_minMag, false, &floored);
return !f128M_eq(&floored, &const_val->data.x_f128);
}
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static void float_append_buf(Buf *buf, ConstExprValue *const_val) {
if (const_val->type->id == ZigTypeIdComptimeFloat) {
bigfloat_append_buf(buf, &const_val->data.x_bigfloat);
} else if (const_val->type->id == ZigTypeIdFloat) {
switch (const_val->type->data.floating.bit_count) {
case 16:
buf_appendf(buf, "%f", zig_f16_to_double(const_val->data.x_f16));
break;
case 32:
buf_appendf(buf, "%f", const_val->data.x_f32);
break;
case 64:
buf_appendf(buf, "%f", const_val->data.x_f64);
break;
case 128:
{
// TODO actual implementation
const size_t extra_len = 100;
size_t old_len = buf_len(buf);
buf_resize(buf, old_len + extra_len);
float64_t f64_value = f128M_to_f64(&const_val->data.x_f128);
double double_value;
memcpy(&double_value, &f64_value, sizeof(double));
int len = snprintf(buf_ptr(buf) + old_len, extra_len, "%f", double_value);
assert(len > 0);
buf_resize(buf, old_len + len);
break;
}
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static void float_init_bigint(BigInt *bigint, ConstExprValue *const_val) {
if (const_val->type->id == ZigTypeIdComptimeFloat) {
bigint_init_bigfloat(bigint, &const_val->data.x_bigfloat);
} else if (const_val->type->id == ZigTypeIdFloat) {
switch (const_val->type->data.floating.bit_count) {
case 16:
{
double x = zig_f16_to_double(const_val->data.x_f16);
if (x >= 0) {
bigint_init_unsigned(bigint, (uint64_t)x);
} else {
bigint_init_unsigned(bigint, (uint64_t)-x);
bigint->is_negative = true;
}
break;
}
case 32:
if (const_val->data.x_f32 >= 0) {
bigint_init_unsigned(bigint, (uint64_t)(const_val->data.x_f32));
} else {
bigint_init_unsigned(bigint, (uint64_t)(-const_val->data.x_f32));
bigint->is_negative = true;
}
break;
case 64:
if (const_val->data.x_f64 >= 0) {
bigint_init_unsigned(bigint, (uint64_t)(const_val->data.x_f64));
} else {
bigint_init_unsigned(bigint, (uint64_t)(-const_val->data.x_f64));
bigint->is_negative = true;
}
break;
case 128:
{
BigFloat tmp_float;
bigfloat_init_128(&tmp_float, const_val->data.x_f128);
bigint_init_bigfloat(bigint, &tmp_float);
}
break;
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static void float_init_bigfloat(ConstExprValue *dest_val, BigFloat *bigfloat) {
if (dest_val->type->id == ZigTypeIdComptimeFloat) {
bigfloat_init_bigfloat(&dest_val->data.x_bigfloat, bigfloat);
} else if (dest_val->type->id == ZigTypeIdFloat) {
switch (dest_val->type->data.floating.bit_count) {
case 16:
dest_val->data.x_f16 = bigfloat_to_f16(bigfloat);
break;
case 32:
dest_val->data.x_f32 = bigfloat_to_f32(bigfloat);
break;
case 64:
dest_val->data.x_f64 = bigfloat_to_f64(bigfloat);
break;
case 80:
zig_panic("TODO");
case 128:
dest_val->data.x_f128 = bigfloat_to_f128(bigfloat);
break;
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static void float_init_f16(ConstExprValue *dest_val, float16_t x) {
if (dest_val->type->id == ZigTypeIdComptimeFloat) {
bigfloat_init_16(&dest_val->data.x_bigfloat, x);
} else if (dest_val->type->id == ZigTypeIdFloat) {
switch (dest_val->type->data.floating.bit_count) {
case 16:
dest_val->data.x_f16 = x;
break;
case 32:
dest_val->data.x_f32 = zig_f16_to_double(x);
break;
case 64:
dest_val->data.x_f64 = zig_f16_to_double(x);
break;
case 128:
f16_to_f128M(x, &dest_val->data.x_f128);
break;
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static void float_init_f32(ConstExprValue *dest_val, float x) {
if (dest_val->type->id == ZigTypeIdComptimeFloat) {
bigfloat_init_32(&dest_val->data.x_bigfloat, x);
} else if (dest_val->type->id == ZigTypeIdFloat) {
switch (dest_val->type->data.floating.bit_count) {
case 16:
dest_val->data.x_f16 = zig_double_to_f16(x);
break;
case 32:
dest_val->data.x_f32 = x;
break;
case 64:
dest_val->data.x_f64 = x;
break;
case 128:
{
float32_t x_f32;
memcpy(&x_f32, &x, sizeof(float));
f32_to_f128M(x_f32, &dest_val->data.x_f128);
break;
}
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static void float_init_f64(ConstExprValue *dest_val, double x) {
if (dest_val->type->id == ZigTypeIdComptimeFloat) {
bigfloat_init_64(&dest_val->data.x_bigfloat, x);
} else if (dest_val->type->id == ZigTypeIdFloat) {
switch (dest_val->type->data.floating.bit_count) {
case 16:
dest_val->data.x_f16 = zig_double_to_f16(x);
break;
case 32:
dest_val->data.x_f32 = x;
break;
case 64:
dest_val->data.x_f64 = x;
break;
case 128:
{
float64_t x_f64;
memcpy(&x_f64, &x, sizeof(double));
f64_to_f128M(x_f64, &dest_val->data.x_f128);
break;
}
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static void float_init_f128(ConstExprValue *dest_val, float128_t x) {
if (dest_val->type->id == ZigTypeIdComptimeFloat) {
bigfloat_init_128(&dest_val->data.x_bigfloat, x);
} else if (dest_val->type->id == ZigTypeIdFloat) {
switch (dest_val->type->data.floating.bit_count) {
case 16:
dest_val->data.x_f16 = f128M_to_f16(&x);
break;
case 32:
{
float32_t f32_val = f128M_to_f32(&x);
memcpy(&dest_val->data.x_f32, &f32_val, sizeof(float));
break;
}
case 64:
{
float64_t f64_val = f128M_to_f64(&x);
memcpy(&dest_val->data.x_f64, &f64_val, sizeof(double));
break;
}
case 128:
{
memcpy(&dest_val->data.x_f128, &x, sizeof(float128_t));
break;
}
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static void float_init_float(ConstExprValue *dest_val, ConstExprValue *src_val) {
if (src_val->type->id == ZigTypeIdComptimeFloat) {
float_init_bigfloat(dest_val, &src_val->data.x_bigfloat);
} else if (src_val->type->id == ZigTypeIdFloat) {
switch (src_val->type->data.floating.bit_count) {
case 16:
float_init_f16(dest_val, src_val->data.x_f16);
break;
case 32:
float_init_f32(dest_val, src_val->data.x_f32);
break;
case 64:
float_init_f64(dest_val, src_val->data.x_f64);
break;
case 128:
float_init_f128(dest_val, src_val->data.x_f128);
break;
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static bool float_is_nan(ConstExprValue *op) {
if (op->type->id == ZigTypeIdComptimeFloat) {
return bigfloat_is_nan(&op->data.x_bigfloat);
} else if (op->type->id == ZigTypeIdFloat) {
switch (op->type->data.floating.bit_count) {
case 16:
return f16_isSignalingNaN(op->data.x_f16);
case 32:
return op->data.x_f32 != op->data.x_f32;
case 64:
return op->data.x_f64 != op->data.x_f64;
case 128:
return f128M_isSignalingNaN(&op->data.x_f128);
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static Cmp float_cmp(ConstExprValue *op1, ConstExprValue *op2) {
assert(op1->type == op2->type);
if (op1->type->id == ZigTypeIdComptimeFloat) {
return bigfloat_cmp(&op1->data.x_bigfloat, &op2->data.x_bigfloat);
} else if (op1->type->id == ZigTypeIdFloat) {
switch (op1->type->data.floating.bit_count) {
case 16:
if (f16_lt(op1->data.x_f16, op2->data.x_f16)) {
return CmpLT;
} else if (f16_lt(op2->data.x_f16, op1->data.x_f16)) {
return CmpGT;
} else {
return CmpEQ;
}
case 32:
if (op1->data.x_f32 > op2->data.x_f32) {
return CmpGT;
} else if (op1->data.x_f32 < op2->data.x_f32) {
return CmpLT;
} else {
return CmpEQ;
}
case 64:
if (op1->data.x_f64 > op2->data.x_f64) {
return CmpGT;
} else if (op1->data.x_f64 < op2->data.x_f64) {
return CmpLT;
} else {
return CmpEQ;
}
case 128:
if (f128M_lt(&op1->data.x_f128, &op2->data.x_f128)) {
return CmpLT;
} else if (f128M_eq(&op1->data.x_f128, &op2->data.x_f128)) {
return CmpEQ;
} else {
return CmpGT;
}
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static Cmp float_cmp_zero(ConstExprValue *op) {
if (op->type->id == ZigTypeIdComptimeFloat) {
return bigfloat_cmp_zero(&op->data.x_bigfloat);
} else if (op->type->id == ZigTypeIdFloat) {
switch (op->type->data.floating.bit_count) {
case 16:
{
const float16_t zero = zig_double_to_f16(0);
if (f16_lt(op->data.x_f16, zero)) {
return CmpLT;
} else if (f16_lt(zero, op->data.x_f16)) {
return CmpGT;
} else {
return CmpEQ;
}
}
case 32:
if (op->data.x_f32 < 0.0) {
return CmpLT;
} else if (op->data.x_f32 > 0.0) {
return CmpGT;
} else {
return CmpEQ;
}
case 64:
if (op->data.x_f64 < 0.0) {
return CmpLT;
} else if (op->data.x_f64 > 0.0) {
return CmpGT;
} else {
return CmpEQ;
}
case 128:
float128_t zero_float;
ui32_to_f128M(0, &zero_float);
if (f128M_lt(&op->data.x_f128, &zero_float)) {
return CmpLT;
} else if (f128M_eq(&op->data.x_f128, &zero_float)) {
return CmpEQ;
} else {
return CmpGT;
}
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static void float_add(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) {
assert(op1->type == op2->type);
out_val->type = op1->type;
if (op1->type->id == ZigTypeIdComptimeFloat) {
bigfloat_add(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
} else if (op1->type->id == ZigTypeIdFloat) {
switch (op1->type->data.floating.bit_count) {
case 16:
out_val->data.x_f16 = f16_add(op1->data.x_f16, op2->data.x_f16);
return;
case 32:
out_val->data.x_f32 = op1->data.x_f32 + op2->data.x_f32;
return;
case 64:
out_val->data.x_f64 = op1->data.x_f64 + op2->data.x_f64;
return;
case 128:
f128M_add(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128);
return;
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static void float_sub(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) {
assert(op1->type == op2->type);
out_val->type = op1->type;
if (op1->type->id == ZigTypeIdComptimeFloat) {
bigfloat_sub(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
} else if (op1->type->id == ZigTypeIdFloat) {
switch (op1->type->data.floating.bit_count) {
case 16:
out_val->data.x_f16 = f16_sub(op1->data.x_f16, op2->data.x_f16);
return;
case 32:
out_val->data.x_f32 = op1->data.x_f32 - op2->data.x_f32;
return;
case 64:
out_val->data.x_f64 = op1->data.x_f64 - op2->data.x_f64;
return;
case 128:
f128M_sub(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128);
return;
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static void float_mul(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) {
assert(op1->type == op2->type);
out_val->type = op1->type;
if (op1->type->id == ZigTypeIdComptimeFloat) {
bigfloat_mul(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
} else if (op1->type->id == ZigTypeIdFloat) {
switch (op1->type->data.floating.bit_count) {
case 16:
out_val->data.x_f16 = f16_mul(op1->data.x_f16, op2->data.x_f16);
return;
case 32:
out_val->data.x_f32 = op1->data.x_f32 * op2->data.x_f32;
return;
case 64:
out_val->data.x_f64 = op1->data.x_f64 * op2->data.x_f64;
return;
case 128:
f128M_mul(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128);
return;
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static void float_div(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) {
assert(op1->type == op2->type);
out_val->type = op1->type;
if (op1->type->id == ZigTypeIdComptimeFloat) {
bigfloat_div(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
} else if (op1->type->id == ZigTypeIdFloat) {
switch (op1->type->data.floating.bit_count) {
case 16:
out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16);
return;
case 32:
out_val->data.x_f32 = op1->data.x_f32 / op2->data.x_f32;
return;
case 64:
out_val->data.x_f64 = op1->data.x_f64 / op2->data.x_f64;
return;
case 128:
f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128);
return;
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static void float_div_trunc(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) {
assert(op1->type == op2->type);
out_val->type = op1->type;
if (op1->type->id == ZigTypeIdComptimeFloat) {
bigfloat_div_trunc(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
} else if (op1->type->id == ZigTypeIdFloat) {
switch (op1->type->data.floating.bit_count) {
case 16:
out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16);
out_val->data.x_f16 = f16_roundToInt(out_val->data.x_f16, softfloat_round_minMag, false);
return;
case 32:
out_val->data.x_f32 = truncf(op1->data.x_f32 / op2->data.x_f32);
return;
case 64:
out_val->data.x_f64 = trunc(op1->data.x_f64 / op2->data.x_f64);
return;
case 128:
f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128);
f128M_roundToInt(&out_val->data.x_f128, softfloat_round_minMag, false, &out_val->data.x_f128);
return;
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static void float_div_floor(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) {
assert(op1->type == op2->type);
out_val->type = op1->type;
if (op1->type->id == ZigTypeIdComptimeFloat) {
bigfloat_div_floor(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
} else if (op1->type->id == ZigTypeIdFloat) {
switch (op1->type->data.floating.bit_count) {
case 16:
out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16);
out_val->data.x_f16 = f16_roundToInt(out_val->data.x_f16, softfloat_round_min, false);
return;
case 32:
out_val->data.x_f32 = floorf(op1->data.x_f32 / op2->data.x_f32);
return;
case 64:
out_val->data.x_f64 = floor(op1->data.x_f64 / op2->data.x_f64);
return;
case 128:
f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128);
f128M_roundToInt(&out_val->data.x_f128, softfloat_round_min, false, &out_val->data.x_f128);
return;
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static void float_rem(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) {
assert(op1->type == op2->type);
out_val->type = op1->type;
if (op1->type->id == ZigTypeIdComptimeFloat) {
bigfloat_rem(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
} else if (op1->type->id == ZigTypeIdFloat) {
switch (op1->type->data.floating.bit_count) {
case 16:
out_val->data.x_f16 = f16_rem(op1->data.x_f16, op2->data.x_f16);
return;
case 32:
out_val->data.x_f32 = fmodf(op1->data.x_f32, op2->data.x_f32);
return;
case 64:
out_val->data.x_f64 = fmod(op1->data.x_f64, op2->data.x_f64);
return;
case 128:
f128M_rem(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128);
return;
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
// c = a - b * trunc(a / b)
static float16_t zig_f16_mod(float16_t a, float16_t b) {
float16_t c;
c = f16_div(a, b);
c = f16_roundToInt(c, softfloat_round_min, true);
c = f16_mul(b, c);
c = f16_sub(a, c);
return c;
}
// c = a - b * trunc(a / b)
static void zig_f128M_mod(const float128_t* a, const float128_t* b, float128_t* c) {
f128M_div(a, b, c);
f128M_roundToInt(c, softfloat_round_min, true, c);
f128M_mul(b, c, c);
f128M_sub(a, c, c);
}
static void float_mod(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) {
assert(op1->type == op2->type);
out_val->type = op1->type;
if (op1->type->id == ZigTypeIdComptimeFloat) {
bigfloat_mod(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
} else if (op1->type->id == ZigTypeIdFloat) {
switch (op1->type->data.floating.bit_count) {
case 16:
out_val->data.x_f16 = zig_f16_mod(op1->data.x_f16, op2->data.x_f16);
return;
case 32:
out_val->data.x_f32 = fmodf(fmodf(op1->data.x_f32, op2->data.x_f32) + op2->data.x_f32, op2->data.x_f32);
return;
case 64:
out_val->data.x_f64 = fmod(fmod(op1->data.x_f64, op2->data.x_f64) + op2->data.x_f64, op2->data.x_f64);
return;
case 128:
zig_f128M_mod(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128);
return;
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static void float_negate(ConstExprValue *out_val, ConstExprValue *op) {
out_val->type = op->type;
if (op->type->id == ZigTypeIdComptimeFloat) {
bigfloat_negate(&out_val->data.x_bigfloat, &op->data.x_bigfloat);
} else if (op->type->id == ZigTypeIdFloat) {
switch (op->type->data.floating.bit_count) {
case 16:
{
const float16_t zero = zig_double_to_f16(0);
out_val->data.x_f16 = f16_sub(zero, op->data.x_f16);
return;
}
case 32:
out_val->data.x_f32 = -op->data.x_f32;
return;
case 64:
out_val->data.x_f64 = -op->data.x_f64;
return;
case 128:
float128_t zero_f128;
ui32_to_f128M(0, &zero_f128);
f128M_sub(&zero_f128, &op->data.x_f128, &out_val->data.x_f128);
return;
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
void float_write_ieee597(ConstExprValue *op, uint8_t *buf, bool is_big_endian) {
if (op->type->id == ZigTypeIdFloat) {
switch (op->type->data.floating.bit_count) {
case 16:
memcpy(buf, &op->data.x_f16, 2); // TODO wrong when compiler is big endian
return;
case 32:
memcpy(buf, &op->data.x_f32, 4); // TODO wrong when compiler is big endian
return;
case 64:
memcpy(buf, &op->data.x_f64, 8); // TODO wrong when compiler is big endian
return;
case 128:
memcpy(buf, &op->data.x_f128, 16); // TODO wrong when compiler is big endian
return;
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
void float_read_ieee597(ConstExprValue *val, uint8_t *buf, bool is_big_endian) {
if (val->type->id == ZigTypeIdFloat) {
switch (val->type->data.floating.bit_count) {
case 16:
memcpy(&val->data.x_f16, buf, 2); // TODO wrong when compiler is big endian
return;
case 32:
memcpy(&val->data.x_f32, buf, 4); // TODO wrong when compiler is big endian
return;
case 64:
memcpy(&val->data.x_f64, buf, 8); // TODO wrong when compiler is big endian
return;
case 128:
memcpy(&val->data.x_f128, buf, 16); // TODO wrong when compiler is big endian
return;
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruction, ZigType *other_type,
bool explicit_cast)
{
if (type_is_invalid(other_type)) {
return false;
}
ConstExprValue *const_val = ir_resolve_const(ira, instruction, UndefBad);
assert(const_val != nullptr);
bool const_val_is_int = (const_val->type->id == ZigTypeIdInt || const_val->type->id == ZigTypeIdComptimeInt);
bool const_val_is_float = (const_val->type->id == ZigTypeIdFloat || const_val->type->id == ZigTypeIdComptimeFloat);
assert(const_val_is_int || const_val_is_float);
if (other_type->id == ZigTypeIdFloat) {
if (const_val->type->id == ZigTypeIdComptimeInt || const_val->type->id == ZigTypeIdComptimeFloat) {
return true;
}
if (const_val->type->id == ZigTypeIdInt) {
BigFloat tmp_bf;
bigfloat_init_bigint(&tmp_bf, &const_val->data.x_bigint);
BigFloat orig_bf;
switch (other_type->data.floating.bit_count) {
case 16: {
float16_t tmp = bigfloat_to_f16(&tmp_bf);
bigfloat_init_16(&orig_bf, tmp);
break;
}
case 32: {
float tmp = bigfloat_to_f32(&tmp_bf);
bigfloat_init_32(&orig_bf, tmp);
break;
}
case 64: {
double tmp = bigfloat_to_f64(&tmp_bf);
bigfloat_init_64(&orig_bf, tmp);
break;
}
case 80:
zig_panic("TODO");
case 128: {
float128_t tmp = bigfloat_to_f128(&tmp_bf);
bigfloat_init_128(&orig_bf, tmp);
break;
}
default:
zig_unreachable();
}
BigInt orig_bi;
bigint_init_bigfloat(&orig_bi, &orig_bf);
if (bigint_cmp(&orig_bi, &const_val->data.x_bigint) == CmpEQ) {
return true;
}
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &const_val->data.x_bigint, 10);
ir_add_error(ira, instruction,
buf_sprintf("integer value %s has no representation in type '%s'",
buf_ptr(val_buf),
buf_ptr(&other_type->name)));
return false;
}
if (other_type->data.floating.bit_count >= const_val->type->data.floating.bit_count) {
return true;
}
switch (other_type->data.floating.bit_count) {
case 16:
switch (const_val->type->data.floating.bit_count) {
case 32: {
float16_t tmp = zig_double_to_f16(const_val->data.x_f32);
float orig = zig_f16_to_double(tmp);
if (const_val->data.x_f32 == orig) {
return true;
}
break;
}
case 64: {
float16_t tmp = zig_double_to_f16(const_val->data.x_f64);
double orig = zig_f16_to_double(tmp);
if (const_val->data.x_f64 == orig) {
return true;
}
break;
}
case 80:
zig_panic("TODO");
case 128: {
float16_t tmp = f128M_to_f16(&const_val->data.x_f128);
float128_t orig;
f16_to_f128M(tmp, &orig);
if (f128M_eq(&orig, &const_val->data.x_f128)) {
return true;
}
break;
}
default:
zig_unreachable();
}
break;
case 32:
switch (const_val->type->data.floating.bit_count) {
case 64: {
float tmp = const_val->data.x_f64;
double orig = tmp;
if (const_val->data.x_f64 == orig) {
return true;
}
break;
}
case 80:
zig_panic("TODO");
case 128: {
float32_t tmp = f128M_to_f32(&const_val->data.x_f128);
float128_t orig;
f32_to_f128M(tmp, &orig);
if (f128M_eq(&orig, &const_val->data.x_f128)) {
return true;
}
break;
}
default:
zig_unreachable();
}
break;
case 64:
switch (const_val->type->data.floating.bit_count) {
case 80:
zig_panic("TODO");
case 128: {
float64_t tmp = f128M_to_f64(&const_val->data.x_f128);
float128_t orig;
f64_to_f128M(tmp, &orig);
if (f128M_eq(&orig, &const_val->data.x_f128)) {
return true;
}
break;
}
default:
zig_unreachable();
}
break;
case 80:
assert(const_val->type->data.floating.bit_count == 128);
zig_panic("TODO");
case 128:
return true;
default:
zig_unreachable();
}
Buf *val_buf = buf_alloc();
float_append_buf(val_buf, const_val);
ir_add_error(ira, instruction,
buf_sprintf("cast of value %s to type '%s' loses information",
buf_ptr(val_buf),
buf_ptr(&other_type->name)));
return false;
} else if (other_type->id == ZigTypeIdInt && const_val_is_int) {
if (!other_type->data.integral.is_signed && const_val->data.x_bigint.is_negative) {
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &const_val->data.x_bigint, 10);
ir_add_error(ira, instruction,
buf_sprintf("cannot cast negative value %s to unsigned integer type '%s'",
buf_ptr(val_buf),
buf_ptr(&other_type->name)));
return false;
}
if (bigint_fits_in_bits(&const_val->data.x_bigint, other_type->data.integral.bit_count,
other_type->data.integral.is_signed))
{
return true;
}
} else if (const_val_fits_in_num_lit(const_val, other_type)) {
return true;
} else if (other_type->id == ZigTypeIdOptional) {
ZigType *child_type = other_type->data.maybe.child_type;
if (const_val_fits_in_num_lit(const_val, child_type)) {
return true;
} else if (child_type->id == ZigTypeIdInt && const_val_is_int) {
if (!child_type->data.integral.is_signed && const_val->data.x_bigint.is_negative) {
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &const_val->data.x_bigint, 10);
ir_add_error(ira, instruction,
buf_sprintf("cannot cast negative value %s to unsigned integer type '%s'",
buf_ptr(val_buf),
buf_ptr(&child_type->name)));
return false;
}
if (bigint_fits_in_bits(&const_val->data.x_bigint,
child_type->data.integral.bit_count,
child_type->data.integral.is_signed))
{
return true;
}
} else if (child_type->id == ZigTypeIdFloat && const_val_is_float) {
return true;
}
}
if (explicit_cast && (other_type->id == ZigTypeIdInt || other_type->id == ZigTypeIdComptimeInt) &&
const_val_is_float)
{
if (float_has_fraction(const_val)) {
Buf *val_buf = buf_alloc();
float_append_buf(val_buf, const_val);
ir_add_error(ira, instruction,
buf_sprintf("fractional component prevents float value %s from being casted to type '%s'",
buf_ptr(val_buf),
buf_ptr(&other_type->name)));
return false;
} else {
if (other_type->id == ZigTypeIdComptimeInt) {
return true;
} else {
BigInt bigint;
float_init_bigint(&bigint, const_val);
if (bigint_fits_in_bits(&bigint, other_type->data.integral.bit_count,
other_type->data.integral.is_signed))
{
return true;
}
}
}
}
const char *num_lit_str;
Buf *val_buf = buf_alloc();
if (const_val_is_float) {
num_lit_str = "float";
float_append_buf(val_buf, const_val);
} else {
num_lit_str = "integer";
bigint_append_buf(val_buf, &const_val->data.x_bigint, 10);
}
ir_add_error(ira, instruction,
buf_sprintf("%s value %s cannot be implicitly casted to type '%s'",
num_lit_str,
buf_ptr(val_buf),
buf_ptr(&other_type->name)));
return false;
}
static bool is_slice(ZigType *type) {
return type->id == ZigTypeIdStruct && type->data.structure.is_slice;
}
static bool slice_is_const(ZigType *type) {
assert(is_slice(type));
return type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const;
}
static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigType *set2,
AstNode *source_node)
{
assert(set1->id == ZigTypeIdErrorSet);
assert(set2->id == ZigTypeIdErrorSet);
if (!resolve_inferred_error_set(ira->codegen, set1, source_node)) {
return ira->codegen->builtin_types.entry_invalid;
}
if (!resolve_inferred_error_set(ira->codegen, set2, source_node)) {
return ira->codegen->builtin_types.entry_invalid;
}
if (type_is_global_error_set(set1)) {
return set2;
}
if (type_is_global_error_set(set2)) {
return set1;
}
ErrorTableEntry **errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);
for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) {
ErrorTableEntry *error_entry = set1->data.error_set.errors[i];
assert(errors[error_entry->value] == nullptr);
errors[error_entry->value] = error_entry;
}
ZigList<ErrorTableEntry *> intersection_list = {};
ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
buf_resize(&err_set_type->name, 0);
buf_appendf(&err_set_type->name, "error{");
for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) {
ErrorTableEntry *error_entry = set2->data.error_set.errors[i];
ErrorTableEntry *existing_entry = errors[error_entry->value];
if (existing_entry != nullptr) {
intersection_list.append(existing_entry);
buf_appendf(&err_set_type->name, "%s,", buf_ptr(&existing_entry->name));
}
}
free(errors);
err_set_type->data.error_set.err_count = intersection_list.length;
err_set_type->data.error_set.errors = intersection_list.items;
err_set_type->size_in_bits = ira->codegen->builtin_types.entry_global_error_set->size_in_bits;
err_set_type->abi_align = ira->codegen->builtin_types.entry_global_error_set->abi_align;
err_set_type->abi_size = ira->codegen->builtin_types.entry_global_error_set->abi_size;
buf_appendf(&err_set_type->name, "}");
return err_set_type;
}
static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted_type,
ZigType *actual_type, AstNode *source_node, bool wanted_is_mutable)
{
CodeGen *g = ira->codegen;
ConstCastOnly result = {};
result.id = ConstCastResultIdOk;
Error err;
if (wanted_type == actual_type)
return result;
// If pointers have the same representation in memory, they can be "const-casted".
// `const` attribute can be gained
// `volatile` attribute can be gained
// `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer)
// but only if !wanted_is_mutable
// alignment can be decreased
// bit offset attributes must match exactly
// PtrLenSingle/PtrLenUnknown must match exactly, but PtrLenC matches either one
ZigType *wanted_ptr_type = get_src_ptr_type(wanted_type);
ZigType *actual_ptr_type = get_src_ptr_type(actual_type);
bool wanted_allows_zero = ptr_allows_addr_zero(wanted_type);
bool actual_allows_zero = ptr_allows_addr_zero(actual_type);
bool wanted_is_c_ptr = wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC;
bool actual_is_c_ptr = actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenC;
bool wanted_opt_or_ptr = wanted_ptr_type != nullptr &&
(wanted_type->id == ZigTypeIdPointer || wanted_type->id == ZigTypeIdOptional);
bool actual_opt_or_ptr = actual_ptr_type != nullptr &&
(actual_type->id == ZigTypeIdPointer || actual_type->id == ZigTypeIdOptional);
if (wanted_opt_or_ptr && actual_opt_or_ptr) {
ConstCastOnly child = types_match_const_cast_only(ira, wanted_ptr_type->data.pointer.child_type,
actual_ptr_type->data.pointer.child_type, source_node, !wanted_ptr_type->data.pointer.is_const);
if (child.id == ConstCastResultIdInvalid)
return child;
if (child.id != ConstCastResultIdOk) {
result.id = ConstCastResultIdPointerChild;
result.data.pointer_mismatch = allocate_nonzero<ConstCastPointerMismatch>(1);
result.data.pointer_mismatch->child = child;
result.data.pointer_mismatch->wanted_child = wanted_ptr_type->data.pointer.child_type;
result.data.pointer_mismatch->actual_child = actual_ptr_type->data.pointer.child_type;
return result;
}
bool ok_allows_zero = (wanted_allows_zero &&
(actual_allows_zero || !wanted_is_mutable)) ||
(!wanted_allows_zero && !actual_allows_zero);
if (!ok_allows_zero) {
result.id = ConstCastResultIdBadAllowsZero;
result.data.bad_allows_zero = allocate_nonzero<ConstCastBadAllowsZero>(1);
result.data.bad_allows_zero->wanted_type = wanted_type;
result.data.bad_allows_zero->actual_type = actual_type;
return result;
}
if ((err = type_resolve(g, actual_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) {
result.id = ConstCastResultIdInvalid;
return result;
}
if ((err = type_resolve(g, wanted_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) {
result.id = ConstCastResultIdInvalid;
return result;
}
bool ptr_lens_equal = actual_ptr_type->data.pointer.ptr_len == wanted_ptr_type->data.pointer.ptr_len;
if ((ptr_lens_equal || wanted_is_c_ptr || actual_is_c_ptr) &&
type_has_bits(wanted_type) == type_has_bits(actual_type) &&
(!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) &&
(!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) &&
actual_ptr_type->data.pointer.bit_offset_in_host == wanted_ptr_type->data.pointer.bit_offset_in_host &&
actual_ptr_type->data.pointer.host_int_bytes == wanted_ptr_type->data.pointer.host_int_bytes &&
get_ptr_align(ira->codegen, actual_ptr_type) >= get_ptr_align(ira->codegen, wanted_ptr_type))
{
return result;
}
}
// slice const
if (is_slice(wanted_type) && is_slice(actual_type)) {
ZigType *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry;
ZigType *wanted_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
if ((err = type_resolve(g, actual_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) {
result.id = ConstCastResultIdInvalid;
return result;
}
if ((err = type_resolve(g, wanted_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) {
result.id = ConstCastResultIdInvalid;
return result;
}
if ((!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) &&
(!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) &&
actual_ptr_type->data.pointer.bit_offset_in_host == wanted_ptr_type->data.pointer.bit_offset_in_host &&
actual_ptr_type->data.pointer.host_int_bytes == wanted_ptr_type->data.pointer.host_int_bytes &&
get_ptr_align(g, actual_ptr_type) >= get_ptr_align(g, wanted_ptr_type))
{
ConstCastOnly child = types_match_const_cast_only(ira, wanted_ptr_type->data.pointer.child_type,
actual_ptr_type->data.pointer.child_type, source_node, !wanted_ptr_type->data.pointer.is_const);
if (child.id == ConstCastResultIdInvalid)
return child;
if (child.id != ConstCastResultIdOk) {
result.id = ConstCastResultIdSliceChild;
result.data.slice_mismatch = allocate_nonzero<ConstCastSliceMismatch>(1);
result.data.slice_mismatch->child = child;
result.data.slice_mismatch->actual_child = actual_ptr_type->data.pointer.child_type;
result.data.slice_mismatch->wanted_child = wanted_ptr_type->data.pointer.child_type;
}
return result;
}
}
// maybe
if (wanted_type->id == ZigTypeIdOptional && actual_type->id == ZigTypeIdOptional) {
ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.maybe.child_type,
actual_type->data.maybe.child_type, source_node, wanted_is_mutable);
if (child.id == ConstCastResultIdInvalid)
return child;
if (child.id != ConstCastResultIdOk) {
result.id = ConstCastResultIdOptionalChild;
result.data.optional = allocate_nonzero<ConstCastOptionalMismatch>(1);
result.data.optional->child = child;
result.data.optional->wanted_child = wanted_type->data.maybe.child_type;
result.data.optional->actual_child = actual_type->data.maybe.child_type;
}
return result;
}
// error union
if (wanted_type->id == ZigTypeIdErrorUnion && actual_type->id == ZigTypeIdErrorUnion) {
ConstCastOnly payload_child = types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type,
actual_type->data.error_union.payload_type, source_node, wanted_is_mutable);
if (payload_child.id == ConstCastResultIdInvalid)
return payload_child;
if (payload_child.id != ConstCastResultIdOk) {
result.id = ConstCastResultIdErrorUnionPayload;
result.data.error_union_payload = allocate_nonzero<ConstCastErrUnionPayloadMismatch>(1);
result.data.error_union_payload->child = payload_child;
result.data.error_union_payload->wanted_payload = wanted_type->data.error_union.payload_type;
result.data.error_union_payload->actual_payload = actual_type->data.error_union.payload_type;
return result;
}
ConstCastOnly error_set_child = types_match_const_cast_only(ira, wanted_type->data.error_union.err_set_type,
actual_type->data.error_union.err_set_type, source_node, wanted_is_mutable);
if (error_set_child.id == ConstCastResultIdInvalid)
return error_set_child;
if (error_set_child.id != ConstCastResultIdOk) {
result.id = ConstCastResultIdErrorUnionErrorSet;
result.data.error_union_error_set = allocate_nonzero<ConstCastErrUnionErrSetMismatch>(1);
result.data.error_union_error_set->child = error_set_child;
result.data.error_union_error_set->wanted_err_set = wanted_type->data.error_union.err_set_type;
result.data.error_union_error_set->actual_err_set = actual_type->data.error_union.err_set_type;
return result;
}
return result;
}
// error set
if (wanted_type->id == ZigTypeIdErrorSet && actual_type->id == ZigTypeIdErrorSet) {
ZigType *contained_set = actual_type;
ZigType *container_set = wanted_type;
// if the container set is inferred, then this will always work.
if (container_set->data.error_set.infer_fn != nullptr) {
return result;
}
// if the container set is the global one, it will always work.
if (type_is_global_error_set(container_set)) {
return result;
}
if (!resolve_inferred_error_set(ira->codegen, contained_set, source_node)) {
result.id = ConstCastResultIdUnresolvedInferredErrSet;
return result;
}
if (type_is_global_error_set(contained_set)) {
result.id = ConstCastResultIdErrSetGlobal;
return result;
}
ErrorTableEntry **errors = allocate<ErrorTableEntry *>(g->errors_by_index.length);
for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) {
ErrorTableEntry *error_entry = container_set->data.error_set.errors[i];
assert(errors[error_entry->value] == nullptr);
errors[error_entry->value] = error_entry;
}
for (uint32_t i = 0; i < contained_set->data.error_set.err_count; i += 1) {
ErrorTableEntry *contained_error_entry = contained_set->data.error_set.errors[i];
ErrorTableEntry *error_entry = errors[contained_error_entry->value];
if (error_entry == nullptr) {
if (result.id == ConstCastResultIdOk) {
result.id = ConstCastResultIdErrSet;
result.data.error_set_mismatch = allocate<ConstCastErrSetMismatch>(1);
}
result.data.error_set_mismatch->missing_errors.append(contained_error_entry);
}
}
free(errors);
return result;
}
if (wanted_type == ira->codegen->builtin_types.entry_promise &&
actual_type->id == ZigTypeIdPromise)
{
return result;
}
// fn
if (wanted_type->id == ZigTypeIdFn &&
actual_type->id == ZigTypeIdFn)
{
if (wanted_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) {
result.id = ConstCastResultIdFnAlign;
return result;
}
if (wanted_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) {
result.id = ConstCastResultIdFnCC;
return result;
}
if (wanted_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) {
result.id = ConstCastResultIdFnVarArgs;
return result;
}
if (wanted_type->data.fn.is_generic != actual_type->data.fn.is_generic) {
result.id = ConstCastResultIdFnIsGeneric;
return result;
}
if (!wanted_type->data.fn.is_generic &&
actual_type->data.fn.fn_type_id.return_type->id != ZigTypeIdUnreachable)
{
ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.fn.fn_type_id.return_type,
actual_type->data.fn.fn_type_id.return_type, source_node, false);
if (child.id == ConstCastResultIdInvalid)
return child;
if (child.id != ConstCastResultIdOk) {
result.id = ConstCastResultIdFnReturnType;
result.data.return_type = allocate_nonzero<ConstCastOnly>(1);
*result.data.return_type = child;
return result;
}
}
if (!wanted_type->data.fn.is_generic && wanted_type->data.fn.fn_type_id.cc == CallingConventionAsync) {
ConstCastOnly child = types_match_const_cast_only(ira,
actual_type->data.fn.fn_type_id.async_allocator_type,
wanted_type->data.fn.fn_type_id.async_allocator_type,
source_node, false);
if (child.id == ConstCastResultIdInvalid)
return child;
if (child.id != ConstCastResultIdOk) {
result.id = ConstCastResultIdAsyncAllocatorType;
result.data.async_allocator_type = allocate_nonzero<ConstCastOnly>(1);
*result.data.async_allocator_type = child;
return result;
}
}
if (wanted_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) {
result.id = ConstCastResultIdFnArgCount;
return result;
}
if (wanted_type->data.fn.fn_type_id.next_param_index != actual_type->data.fn.fn_type_id.next_param_index) {
result.id = ConstCastResultIdFnGenericArgCount;
return result;
}
assert(wanted_type->data.fn.is_generic ||
wanted_type->data.fn.fn_type_id.next_param_index == wanted_type->data.fn.fn_type_id.param_count);
for (size_t i = 0; i < wanted_type->data.fn.fn_type_id.next_param_index; i += 1) {
// note it's reversed for parameters
FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i];
FnTypeParamInfo *expected_param_info = &wanted_type->data.fn.fn_type_id.param_info[i];
ConstCastOnly arg_child = types_match_const_cast_only(ira, actual_param_info->type,
expected_param_info->type, source_node, false);
if (arg_child.id == ConstCastResultIdInvalid)
return arg_child;
if (arg_child.id != ConstCastResultIdOk) {
result.id = ConstCastResultIdFnArg;
result.data.fn_arg.arg_index = i;
result.data.fn_arg.actual_param_type = actual_param_info->type;
result.data.fn_arg.expected_param_type = expected_param_info->type;
result.data.fn_arg.child = allocate_nonzero<ConstCastOnly>(1);
*result.data.fn_arg.child = arg_child;
return result;
}
if (expected_param_info->is_noalias != actual_param_info->is_noalias) {
result.id = ConstCastResultIdFnArgNoAlias;
result.data.arg_no_alias.arg_index = i;
return result;
}
}
return result;
}
result.id = ConstCastResultIdType;
result.data.type_mismatch = allocate_nonzero<ConstCastTypeMismatch>(1);
result.data.type_mismatch->wanted_type = wanted_type;
result.data.type_mismatch->actual_type = actual_type;
return result;
}
static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *errors_count) {
size_t old_errors_count = *errors_count;
*errors_count = g->errors_by_index.length;
*errors = reallocate(*errors, old_errors_count, *errors_count);
}
static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigType *expected_type,
IrInstruction **instructions, size_t instruction_count)
{
Error err;
assert(instruction_count >= 1);
IrInstruction *prev_inst = instructions[0];
if (type_is_invalid(prev_inst->value.type)) {
return ira->codegen->builtin_types.entry_invalid;
}
ErrorTableEntry **errors = nullptr;
size_t errors_count = 0;
ZigType *err_set_type = nullptr;
if (prev_inst->value.type->id == ZigTypeIdErrorSet) {
if (!resolve_inferred_error_set(ira->codegen, prev_inst->value.type, prev_inst->source_node)) {
return ira->codegen->builtin_types.entry_invalid;
}
if (type_is_global_error_set(prev_inst->value.type)) {
err_set_type = ira->codegen->builtin_types.entry_global_error_set;
} else {
err_set_type = prev_inst->value.type;
update_errors_helper(ira->codegen, &errors, &errors_count);
for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
assert(errors[error_entry->value] == nullptr);
errors[error_entry->value] = error_entry;
}
}
}
bool any_are_null = (prev_inst->value.type->id == ZigTypeIdNull);
bool convert_to_const_slice = false;
for (size_t i = 1; i < instruction_count; i += 1) {
IrInstruction *cur_inst = instructions[i];
ZigType *cur_type = cur_inst->value.type;
ZigType *prev_type = prev_inst->value.type;
if (type_is_invalid(cur_type)) {
return cur_type;
}
if (prev_type->id == ZigTypeIdUnreachable) {
prev_inst = cur_inst;
continue;
}
if (cur_type->id == ZigTypeIdUnreachable) {
continue;
}
if (prev_type->id == ZigTypeIdErrorSet) {
assert(err_set_type != nullptr);
if (cur_type->id == ZigTypeIdErrorSet) {
if (type_is_global_error_set(err_set_type)) {
continue;
}
if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) {
return ira->codegen->builtin_types.entry_invalid;
}
if (type_is_global_error_set(cur_type)) {
err_set_type = ira->codegen->builtin_types.entry_global_error_set;
prev_inst = cur_inst;
continue;
}
// number of declared errors might have increased now
update_errors_helper(ira->codegen, &errors, &errors_count);
// if err_set_type is a superset of cur_type, keep err_set_type.
// if cur_type is a superset of err_set_type, switch err_set_type to cur_type
bool prev_is_superset = true;
for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) {
ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i];
ErrorTableEntry *error_entry = errors[contained_error_entry->value];
if (error_entry == nullptr) {
prev_is_superset = false;
break;
}
}
if (prev_is_superset) {
continue;
}
// unset everything in errors
for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
errors[error_entry->value] = nullptr;
}
for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) {
assert(errors[i] == nullptr);
}
for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) {
ErrorTableEntry *error_entry = cur_type->data.error_set.errors[i];
assert(errors[error_entry->value] == nullptr);
errors[error_entry->value] = error_entry;
}
bool cur_is_superset = true;
for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
ErrorTableEntry *contained_error_entry = err_set_type->data.error_set.errors[i];
ErrorTableEntry *error_entry = errors[contained_error_entry->value];
if (error_entry == nullptr) {
cur_is_superset = false;
break;
}
}
if (cur_is_superset) {
err_set_type = cur_type;
prev_inst = cur_inst;
assert(errors != nullptr);
continue;
}
// neither of them are supersets. so we invent a new error set type that is a union of both of them
err_set_type = get_error_set_union(ira->codegen, errors, cur_type, err_set_type);
assert(errors != nullptr);
continue;
} else if (cur_type->id == ZigTypeIdErrorUnion) {
if (type_is_global_error_set(err_set_type)) {
prev_inst = cur_inst;
continue;
}
ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type;
if (!resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) {
return ira->codegen->builtin_types.entry_invalid;
}
if (type_is_global_error_set(cur_err_set_type)) {
err_set_type = ira->codegen->builtin_types.entry_global_error_set;
prev_inst = cur_inst;
continue;
}
update_errors_helper(ira->codegen, &errors, &errors_count);
// test if err_set_type is a subset of cur_type's error set
// unset everything in errors
for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
errors[error_entry->value] = nullptr;
}
for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) {
assert(errors[i] == nullptr);
}
for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) {
ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i];
assert(errors[error_entry->value] == nullptr);
errors[error_entry->value] = error_entry;
}
bool cur_is_superset = true;
for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
ErrorTableEntry *contained_error_entry = err_set_type->data.error_set.errors[i];
ErrorTableEntry *error_entry = errors[contained_error_entry->value];
if (error_entry == nullptr) {
cur_is_superset = false;
break;
}
}
if (cur_is_superset) {
err_set_type = cur_err_set_type;
prev_inst = cur_inst;
assert(errors != nullptr);
continue;
}
// not a subset. invent new error set type, union of both of them
err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, err_set_type);
prev_inst = cur_inst;
assert(errors != nullptr);
continue;
} else {
prev_inst = cur_inst;
continue;
}
}
if (cur_type->id == ZigTypeIdErrorSet) {
if (prev_type->id == ZigTypeIdArray) {
convert_to_const_slice = true;
}
if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) {
return ira->codegen->builtin_types.entry_invalid;
}
if (type_is_global_error_set(cur_type)) {
err_set_type = ira->codegen->builtin_types.entry_global_error_set;
continue;
}
if (err_set_type != nullptr && type_is_global_error_set(err_set_type)) {
continue;
}
update_errors_helper(ira->codegen, &errors, &errors_count);
if (err_set_type == nullptr) {
if (prev_type->id == ZigTypeIdErrorUnion) {
err_set_type = prev_type->data.error_union.err_set_type;
} else {
err_set_type = cur_type;
}
for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
assert(errors[error_entry->value] == nullptr);
errors[error_entry->value] = error_entry;
}
if (err_set_type == cur_type) {
continue;
}
}
// check if the cur type error set is a subset
bool prev_is_superset = true;
for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) {
ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i];
ErrorTableEntry *error_entry = errors[contained_error_entry->value];
if (error_entry == nullptr) {
prev_is_superset = false;
break;
}
}
if (prev_is_superset) {
continue;
}
// not a subset. invent new error set type, union of both of them
err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_type);
assert(errors != nullptr);
continue;
}
if (prev_type->id == ZigTypeIdErrorUnion && cur_type->id == ZigTypeIdErrorUnion) {
ZigType *prev_payload_type = prev_type->data.error_union.payload_type;
ZigType *cur_payload_type = cur_type->data.error_union.payload_type;
bool const_cast_prev = types_match_const_cast_only(ira, prev_payload_type, cur_payload_type,
source_node, false).id == ConstCastResultIdOk;
bool const_cast_cur = types_match_const_cast_only(ira, cur_payload_type, prev_payload_type,
source_node, false).id == ConstCastResultIdOk;
if (const_cast_prev || const_cast_cur) {
if (const_cast_cur) {
prev_inst = cur_inst;
}
ZigType *prev_err_set_type = (err_set_type == nullptr) ? prev_type->data.error_union.err_set_type : err_set_type;
ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type;
if (!resolve_inferred_error_set(ira->codegen, prev_err_set_type, cur_inst->source_node)) {
return ira->codegen->builtin_types.entry_invalid;
}
if (!resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) {
return ira->codegen->builtin_types.entry_invalid;
}
if (type_is_global_error_set(prev_err_set_type) || type_is_global_error_set(cur_err_set_type)) {
err_set_type = ira->codegen->builtin_types.entry_global_error_set;
continue;
}
update_errors_helper(ira->codegen, &errors, &errors_count);
if (err_set_type == nullptr) {
err_set_type = prev_err_set_type;
for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) {
ErrorTableEntry *error_entry = prev_err_set_type->data.error_set.errors[i];
assert(errors[error_entry->value] == nullptr);
errors[error_entry->value] = error_entry;
}
}
bool prev_is_superset = true;
for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) {
ErrorTableEntry *contained_error_entry = cur_err_set_type->data.error_set.errors[i];
ErrorTableEntry *error_entry = errors[contained_error_entry->value];
if (error_entry == nullptr) {
prev_is_superset = false;
break;
}
}
if (prev_is_superset) {
continue;
}
// unset all the errors
for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
errors[error_entry->value] = nullptr;
}
for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) {
assert(errors[i] == nullptr);
}
for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) {
ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i];
assert(errors[error_entry->value] == nullptr);
errors[error_entry->value] = error_entry;
}
bool cur_is_superset = true;
for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) {
ErrorTableEntry *contained_error_entry = prev_err_set_type->data.error_set.errors[i];
ErrorTableEntry *error_entry = errors[contained_error_entry->value];
if (error_entry == nullptr) {
cur_is_superset = false;
break;
}
}
if (cur_is_superset) {
err_set_type = cur_err_set_type;
continue;
}
err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, prev_err_set_type);
continue;
}
}
if (prev_type->id == ZigTypeIdNull) {
prev_inst = cur_inst;
continue;
}
if (cur_type->id == ZigTypeIdNull) {
any_are_null = true;
continue;
}
if (prev_type->id == ZigTypeIdEnum && cur_type->id == ZigTypeIdEnumLiteral) {
TypeEnumField *field = find_enum_type_field(prev_type, cur_inst->value.data.x_enum_literal);
if (field != nullptr) {
continue;
}
}
if (cur_type->id == ZigTypeIdEnum && prev_type->id == ZigTypeIdEnumLiteral) {
TypeEnumField *field = find_enum_type_field(cur_type, prev_inst->value.data.x_enum_literal);
if (field != nullptr) {
prev_inst = cur_inst;
continue;
}
}
if (prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenC &&
(cur_type->id == ZigTypeIdComptimeInt || cur_type->id == ZigTypeIdInt))
{
continue;
}
if (cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenC &&
(prev_type->id == ZigTypeIdComptimeInt || prev_type->id == ZigTypeIdInt))
{
prev_inst = cur_inst;
continue;
}
if (prev_type->id == ZigTypeIdPointer && cur_type->id == ZigTypeIdPointer) {
if (prev_type->data.pointer.ptr_len == PtrLenC &&
types_match_const_cast_only(ira, prev_type->data.pointer.child_type,
cur_type->data.pointer.child_type, source_node,
!prev_type->data.pointer.is_const).id == ConstCastResultIdOk)
{
continue;
}
if (cur_type->data.pointer.ptr_len == PtrLenC &&
types_match_const_cast_only(ira, cur_type->data.pointer.child_type,
prev_type->data.pointer.child_type, source_node,
!cur_type->data.pointer.is_const).id == ConstCastResultIdOk)
{
prev_inst = cur_inst;
continue;
}
}
if (types_match_const_cast_only(ira, prev_type, cur_type, source_node, false).id == ConstCastResultIdOk) {
continue;
}
if (types_match_const_cast_only(ira, cur_type, prev_type, source_node, false).id == ConstCastResultIdOk) {
prev_inst = cur_inst;
continue;
}
if (prev_type->id == ZigTypeIdInt &&
cur_type->id == ZigTypeIdInt &&
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;
}
if (prev_type->id == ZigTypeIdFloat && cur_type->id == ZigTypeIdFloat) {
if (cur_type->data.floating.bit_count > prev_type->data.floating.bit_count) {
prev_inst = cur_inst;
}
continue;
}
if (prev_type->id == ZigTypeIdErrorUnion &&
types_match_const_cast_only(ira, prev_type->data.error_union.payload_type, cur_type,
source_node, false).id == ConstCastResultIdOk)
{
continue;
}
if (cur_type->id == ZigTypeIdErrorUnion &&
types_match_const_cast_only(ira, cur_type->data.error_union.payload_type, prev_type,
source_node, false).id == ConstCastResultIdOk)
{
if (err_set_type != nullptr) {
ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type;
if (!resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) {
return ira->codegen->builtin_types.entry_invalid;
}
if (type_is_global_error_set(cur_err_set_type) || type_is_global_error_set(err_set_type)) {
err_set_type = ira->codegen->builtin_types.entry_global_error_set;
prev_inst = cur_inst;
continue;
}
update_errors_helper(ira->codegen, &errors, &errors_count);
err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_err_set_type);
}
prev_inst = cur_inst;
continue;
}
if (prev_type->id == ZigTypeIdOptional &&
types_match_const_cast_only(ira, prev_type->data.maybe.child_type, cur_type,
source_node, false).id == ConstCastResultIdOk)
{
continue;
}
if (cur_type->id == ZigTypeIdOptional &&
types_match_const_cast_only(ira, cur_type->data.maybe.child_type, prev_type,
source_node, false).id == ConstCastResultIdOk)
{
prev_inst = cur_inst;
continue;
}
if (prev_type->id == ZigTypeIdOptional &&
types_match_const_cast_only(ira, cur_type, prev_type->data.maybe.child_type,
source_node, false).id == ConstCastResultIdOk)
{
prev_inst = cur_inst;
any_are_null = true;
continue;
}
if (cur_type->id == ZigTypeIdOptional &&
types_match_const_cast_only(ira, prev_type, cur_type->data.maybe.child_type,
source_node, false).id == ConstCastResultIdOk)
{
any_are_null = true;
continue;
}
if (cur_type->id == ZigTypeIdUndefined) {
continue;
}
if (prev_type->id == ZigTypeIdUndefined) {
prev_inst = cur_inst;
continue;
}
if (prev_type->id == ZigTypeIdComptimeInt ||
prev_type->id == ZigTypeIdComptimeFloat)
{
if (ir_num_lit_fits_in_other_type(ira, prev_inst, cur_type, false)) {
prev_inst = cur_inst;
continue;
} else {
return ira->codegen->builtin_types.entry_invalid;
}
}
if (cur_type->id == ZigTypeIdComptimeInt ||
cur_type->id == ZigTypeIdComptimeFloat)
{
if (ir_num_lit_fits_in_other_type(ira, cur_inst, prev_type, false)) {
continue;
} else {
return ira->codegen->builtin_types.entry_invalid;
}
}
if (cur_type->id == ZigTypeIdArray && prev_type->id == ZigTypeIdArray &&
cur_type->data.array.len != prev_type->data.array.len &&
types_match_const_cast_only(ira, cur_type->data.array.child_type, prev_type->data.array.child_type,
source_node, false).id == ConstCastResultIdOk)
{
convert_to_const_slice = true;
prev_inst = cur_inst;
continue;
}
if (cur_type->id == ZigTypeIdArray && prev_type->id == ZigTypeIdArray &&
cur_type->data.array.len != prev_type->data.array.len &&
types_match_const_cast_only(ira, prev_type->data.array.child_type, cur_type->data.array.child_type,
source_node, false).id == ConstCastResultIdOk)
{
convert_to_const_slice = true;
continue;
}
if (cur_type->id == ZigTypeIdArray && is_slice(prev_type) &&
(prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||
cur_type->data.array.len == 0) &&
types_match_const_cast_only(ira,
prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,
cur_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk)
{
convert_to_const_slice = false;
continue;
}
if (prev_type->id == ZigTypeIdArray && is_slice(cur_type) &&
(cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||
prev_type->data.array.len == 0) &&
types_match_const_cast_only(ira,
cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,
prev_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk)
{
prev_inst = cur_inst;
convert_to_const_slice = false;
continue;
}
if (prev_type->id == ZigTypeIdEnum && cur_type->id == ZigTypeIdUnion &&
(cur_type->data.unionation.decl_node->data.container_decl.auto_enum || cur_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
{
if ((err = type_resolve(ira->codegen, cur_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->builtin_types.entry_invalid;
if (cur_type->data.unionation.tag_type == prev_type) {
continue;
}
}
if (cur_type->id == ZigTypeIdEnum && prev_type->id == ZigTypeIdUnion &&
(prev_type->data.unionation.decl_node->data.container_decl.auto_enum || prev_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
{
if ((err = type_resolve(ira->codegen, prev_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->builtin_types.entry_invalid;
if (prev_type->data.unionation.tag_type == cur_type) {
prev_inst = cur_inst;
continue;
}
}
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;
}
free(errors);
if (convert_to_const_slice) {
assert(prev_inst->value.type->id == ZigTypeIdArray);
ZigType *ptr_type = get_pointer_to_type_extra(
ira->codegen, prev_inst->value.type->data.array.child_type,
true, false, PtrLenUnknown,
0, 0, 0, false);
ZigType *slice_type = get_slice_type(ira->codegen, ptr_type);
if (err_set_type != nullptr) {
return get_error_union_type(ira->codegen, err_set_type, slice_type);
} else {
return slice_type;
}
} else if (err_set_type != nullptr) {
if (prev_inst->value.type->id == ZigTypeIdErrorSet) {
return err_set_type;
} else if (prev_inst->value.type->id == ZigTypeIdErrorUnion) {
ZigType *payload_type = prev_inst->value.type->data.error_union.payload_type;
if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown)))
return ira->codegen->builtin_types.entry_invalid;
return get_error_union_type(ira->codegen, err_set_type, payload_type);
} else if (expected_type != nullptr && expected_type->id == ZigTypeIdErrorUnion) {
ZigType *payload_type = expected_type->data.error_union.payload_type;
if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown)))
return ira->codegen->builtin_types.entry_invalid;
return get_error_union_type(ira->codegen, err_set_type, payload_type);
} else {
if (prev_inst->value.type->id == ZigTypeIdComptimeInt ||
prev_inst->value.type->id == ZigTypeIdComptimeFloat)
{
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 == ZigTypeIdNull) {
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 ((err = type_resolve(ira->codegen, prev_inst->value.type, ResolveStatusSizeKnown)))
return ira->codegen->builtin_types.entry_invalid;
return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type);
}
}
} else if (any_are_null && prev_inst->value.type->id != ZigTypeIdNull) {
if (prev_inst->value.type->id == ZigTypeIdComptimeInt ||
prev_inst->value.type->id == ZigTypeIdComptimeFloat)
{
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 == ZigTypeIdOptional) {
return prev_inst->value.type;
} else {
return get_optional_type(ira->codegen, prev_inst->value.type);
}
} else {
return prev_inst->value.type;
}
}
static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, ZigType *type_entry) {
if (type_has_bits(type_entry) && handle_is_ptr(type_entry)) {
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
if (fn_entry != nullptr) {
fn_entry->alloca_list.append(instruction);
}
}
}
static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs) {
ConstGlobalRefs *global_refs = dest->global_refs;
assert(!same_global_refs || src->global_refs != nullptr);
*dest = *src;
if (!same_global_refs) {
dest->global_refs = global_refs;
if (dest->type->id == ZigTypeIdStruct) {
dest->data.x_struct.fields = allocate_nonzero<ConstExprValue>(dest->type->data.structure.src_field_count);
memcpy(dest->data.x_struct.fields, src->data.x_struct.fields, sizeof(ConstExprValue) * dest->type->data.structure.src_field_count);
}
}
}
static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_instr,
CastOp cast_op,
ConstExprValue *other_val, ZigType *other_type,
ConstExprValue *const_val, ZigType *new_type)
{
const_val->special = other_val->special;
assert(other_val != const_val);
switch (cast_op) {
case CastOpNoCast:
zig_unreachable();
case CastOpErrSet:
case CastOpBitCast:
case CastOpPtrOfArrayToSlice:
zig_panic("TODO");
case CastOpNoop:
{
bool same_global_refs = other_val->special == ConstValSpecialStatic;
copy_const_val(const_val, other_val, same_global_refs);
const_val->type = new_type;
break;
}
case CastOpNumLitToConcrete:
if (other_val->type->id == ZigTypeIdComptimeFloat) {
assert(new_type->id == ZigTypeIdFloat);
switch (new_type->data.floating.bit_count) {
case 16:
const_val->data.x_f16 = bigfloat_to_f16(&other_val->data.x_bigfloat);
break;
case 32:
const_val->data.x_f32 = bigfloat_to_f32(&other_val->data.x_bigfloat);
break;
case 64:
const_val->data.x_f64 = bigfloat_to_f64(&other_val->data.x_bigfloat);
break;
case 80:
zig_panic("TODO");
case 128:
const_val->data.x_f128 = bigfloat_to_f128(&other_val->data.x_bigfloat);
break;
default:
zig_unreachable();
}
} else if (other_val->type->id == ZigTypeIdComptimeInt) {
bigint_init_bigint(&const_val->data.x_bigint, &other_val->data.x_bigint);
} else {
zig_unreachable();
}
const_val->type = new_type;
break;
case CastOpIntToFloat:
{
assert(new_type->id == ZigTypeIdFloat);
BigFloat bigfloat;
bigfloat_init_bigint(&bigfloat, &other_val->data.x_bigint);
switch (new_type->data.floating.bit_count) {
case 16:
const_val->data.x_f16 = bigfloat_to_f16(&bigfloat);
break;
case 32:
const_val->data.x_f32 = bigfloat_to_f32(&bigfloat);
break;
case 64:
const_val->data.x_f64 = bigfloat_to_f64(&bigfloat);
break;
case 80:
zig_panic("TODO");
case 128:
const_val->data.x_f128 = bigfloat_to_f128(&bigfloat);
break;
default:
zig_unreachable();
}
const_val->special = ConstValSpecialStatic;
break;
}
case CastOpFloatToInt:
float_init_bigint(&const_val->data.x_bigint, other_val);
if (new_type->id == ZigTypeIdInt) {
if (!bigint_fits_in_bits(&const_val->data.x_bigint, new_type->data.integral.bit_count,
new_type->data.integral.is_signed))
{
Buf *int_buf = buf_alloc();
bigint_append_buf(int_buf, &const_val->data.x_bigint, 10);
ir_add_error(ira, source_instr,
buf_sprintf("integer value '%s' cannot be stored in type '%s'",
buf_ptr(int_buf), buf_ptr(&new_type->name)));
return false;
}
}
const_val->special = ConstValSpecialStatic;
break;
case CastOpBoolToInt:
bigint_init_unsigned(&const_val->data.x_bigint, other_val->data.x_bool ? 1 : 0);
const_val->special = ConstValSpecialStatic;
break;
}
return true;
}
static IrInstruction *ir_const(IrAnalyze *ira, IrInstruction *old_instruction, ZigType *ty) {
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
old_instruction->scope, old_instruction->source_node);
IrInstruction *new_instruction = &const_instruction->base;
new_instruction->value.type = ty;
new_instruction->value.special = ConstValSpecialStatic;
return new_instruction;
}
static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
ZigType *wanted_type, CastOp cast_op, bool need_alloca)
{
if (instr_is_comptime(value) || !type_has_bits(wanted_type)) {
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, &value->value, value->value.type,
&result->value, wanted_type))
{
return ira->codegen->invalid_instruction;
}
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) {
ir_add_alloca(ira, result, wanted_type);
}
return result;
}
}
static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *value, ZigType *wanted_type)
{
assert(value->value.type->id == ZigTypeIdPointer);
Error err;
if ((err = type_resolve(ira->codegen, value->value.type->data.pointer.child_type,
ResolveStatusAlignmentKnown)))
{
return ira->codegen->invalid_instruction;
}
wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value.type));
if (instr_is_comptime(value)) {
ConstExprValue *pointee = const_ptr_pointee(ira, ira->codegen, &value->value, source_instr->source_node);
if (pointee == nullptr)
return ira->codegen->invalid_instruction;
if (pointee->special != ConstValSpecialRuntime) {
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
result->value.data.x_ptr.special = ConstPtrSpecialBaseArray;
result->value.data.x_ptr.mut = value->value.data.x_ptr.mut;
result->value.data.x_ptr.data.base_array.array_val = pointee;
result->value.data.x_ptr.data.base_array.elem_index = 0;
result->value.data.x_ptr.data.base_array.is_cstr = false;
return result;
}
}
IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node,
wanted_type, value, CastOpBitCast);
result->value.type = wanted_type;
return result;
}
static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *value, ZigType *wanted_type)
{
Error err;
if ((err = type_resolve(ira->codegen, value->value.type->data.pointer.child_type,
ResolveStatusAlignmentKnown)))
{
return ira->codegen->invalid_instruction;
}
wanted_type = adjust_slice_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value.type));
if (instr_is_comptime(value)) {
ConstExprValue *pointee = const_ptr_pointee(ira, ira->codegen, &value->value, source_instr->source_node);
if (pointee == nullptr)
return ira->codegen->invalid_instruction;
if (pointee->special != ConstValSpecialRuntime) {
assert(value->value.type->id == ZigTypeIdPointer);
ZigType *array_type = value->value.type->data.pointer.child_type;
assert(is_slice(wanted_type));
bool is_const = wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const;
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
init_const_slice(ira->codegen, &result->value, pointee, 0, array_type->data.array.len, is_const);
result->value.data.x_struct.fields[slice_ptr_index].data.x_ptr.mut =
value->value.data.x_ptr.mut;
result->value.type = wanted_type;
return result;
}
}
IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node,
wanted_type, value, CastOpPtrOfArrayToSlice);
result->value.type = wanted_type;
ir_add_alloca(ira, result, wanted_type);
return result;
}
static bool is_container(ZigType *type) {
return type->id == ZigTypeIdStruct ||
type->id == ZigTypeIdEnum ||
type->id == ZigTypeIdUnion;
}
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;
return new_bb;
}
static IrBasicBlock *ir_get_new_bb_runtime(IrAnalyze *ira, IrBasicBlock *old_bb, IrInstruction *ref_old_instruction) {
assert(ref_old_instruction != nullptr);
IrBasicBlock *new_bb = ir_get_new_bb(ira, old_bb, ref_old_instruction);
if (new_bb->must_be_comptime_source_instr) {
ErrorMsg *msg = ir_add_error(ira, ref_old_instruction,
buf_sprintf("control flow attempts to use compile-time variable at runtime"));
add_error_note(ira->codegen, msg, new_bb->must_be_comptime_source_instr->source_node,
buf_sprintf("compile-time variable assigned here"));
return nullptr;
}
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;
}
static void ir_finish_bb(IrAnalyze *ira) {
ira->new_irb.exec->basic_block_list.append(ira->new_irb.current_basic_block);
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->old_bb_index += 1;
bool need_repeat = true;
for (;;) {
while (ira->old_bb_index < ira->old_irb.exec->basic_block_list.length) {
IrBasicBlock *old_bb = ira->old_irb.exec->basic_block_list.at(ira->old_bb_index);
if (old_bb->other == nullptr) {
ira->old_bb_index += 1;
continue;
}
if (old_bb->other->instruction_list.length != 0) {
ira->old_bb_index += 1;
continue;
}
ira->new_irb.current_basic_block = old_bb->other;
ir_start_bb(ira, old_bb, nullptr);
return;
}
if (!need_repeat)
return;
need_repeat = false;
ira->old_bb_index = 0;
continue;
}
}
static IrInstruction *ir_unreach_error(IrAnalyze *ira) {
ira->old_bb_index = SIZE_MAX;
ira->new_irb.exec->invalid = true;
return ira->codegen->unreach_instruction;
}
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) {
assert(ira->codegen->errors.length > 0);
return false;
}
*bbc += 1;
if (*bbc > *quota) {
ir_add_error(ira, source_instruction,
buf_sprintf("evaluation exceeded %" ZIG_PRI_usize " backwards branches", *quota));
return false;
}
return true;
}
static IrInstruction *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->unreach_instruction;
}
static IrInstruction *ir_finish_anal(IrAnalyze *ira, IrInstruction *instruction) {
if (instruction->value.type->id == ZigTypeIdUnreachable)
ir_finish_bb(ira);
return instruction;
}
static IrInstruction *ir_const_type(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *ty) {
IrInstruction *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_type);
result->value.data.x_type = ty;
return result;
}
static IrInstruction *ir_const_bool(IrAnalyze *ira, IrInstruction *source_instruction, bool value) {
IrInstruction *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_bool);
result->value.data.x_bool = value;
return result;
}
static IrInstruction *ir_const_undef(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *ty) {
IrInstruction *result = ir_const(ira, source_instruction, ty);
result->value.special = ConstValSpecialUndef;
return result;
}
static IrInstruction *ir_const_void(IrAnalyze *ira, IrInstruction *source_instruction) {
return ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_void);
}
static IrInstruction *ir_const_unsigned(IrAnalyze *ira, IrInstruction *source_instruction, uint64_t value) {
IrInstruction *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_num_lit_int);
bigint_init_unsigned(&result->value.data.x_bigint, value);
return result;
}
static IrInstruction *ir_get_const_ptr(IrAnalyze *ira, IrInstruction *instruction,
ConstExprValue *pointee, ZigType *pointee_type,
ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile, uint32_t ptr_align)
{
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, pointee_type,
ptr_is_const, ptr_is_volatile, PtrLenSingle, ptr_align, 0, 0, false);
IrInstruction *const_instr = ir_const(ira, instruction, ptr_type);
ConstExprValue *const_val = &const_instr->value;
const_val->data.x_ptr.special = ConstPtrSpecialRef;
const_val->data.x_ptr.mut = ptr_mut;
const_val->data.x_ptr.data.ref.pointee = pointee;
return const_instr;
}
static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed) {
switch (value->value.special) {
case ConstValSpecialStatic:
return &value->value;
case ConstValSpecialRuntime:
if (!type_has_bits(value->value.type)) {
return &value->value;
}
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 here causes undefined behavior"));
return nullptr;
}
}
zig_unreachable();
}
ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
ZigType *expected_type, size_t *backward_branch_count, size_t *backward_branch_quota,
ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name,
IrExecutable *parent_exec, AstNode *expected_type_source_node)
{
if (expected_type != nullptr && type_is_invalid(expected_type))
return &codegen->invalid_instruction->value;
IrExecutable *ir_executable = allocate<IrExecutable>(1);
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->value;
if (codegen->verbose_ir) {
fprintf(stderr, "\nSource: ");
ast_render(stderr, node, 4);
fprintf(stderr, "\n{ // (IR)\n");
ir_print(codegen, stderr, ir_executable, 2);
fprintf(stderr, "}\n");
}
IrExecutable *analyzed_executable = allocate<IrExecutable>(1);
analyzed_executable->source_node = source_node;
analyzed_executable->parent_exec = parent_exec;
analyzed_executable->source_exec = ir_executable;
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;
ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, expected_type_source_node);
if (type_is_invalid(result_type))
return &codegen->invalid_instruction->value;
if (codegen->verbose_ir) {
fprintf(stderr, "{ // (analyzed)\n");
ir_print(codegen, stderr, analyzed_executable, 2);
fprintf(stderr, "}\n");
}
return ir_exec_const_result(codegen, analyzed_executable);
}
static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value) {
if (type_is_invalid(type_value->value.type))
return ira->codegen->builtin_types.entry_invalid;
if (type_value->value.type->id != ZigTypeIdMetaType) {
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;
assert(const_val->data.x_type != nullptr);
return const_val->data.x_type;
}
static ZigType *ir_resolve_error_set_type(IrAnalyze *ira, IrInstruction *op_source, IrInstruction *type_value) {
if (type_is_invalid(type_value->value.type))
return ira->codegen->builtin_types.entry_invalid;
if (type_value->value.type->id != ZigTypeIdMetaType) {
ErrorMsg *msg = ir_add_error(ira, type_value,
buf_sprintf("expected error set type, found '%s'", buf_ptr(&type_value->value.type->name)));
add_error_note(ira->codegen, msg, op_source->source_node,
buf_sprintf("`||` merges error sets; `or` performs boolean OR"));
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;
assert(const_val->data.x_type != nullptr);
ZigType *result_type = const_val->data.x_type;
if (result_type->id != ZigTypeIdErrorSet) {
ErrorMsg *msg = ir_add_error(ira, type_value,
buf_sprintf("expected error set type, found type '%s'", buf_ptr(&result_type->name)));
add_error_note(ira->codegen, msg, op_source->source_node,
buf_sprintf("`||` merges error sets; `or` performs boolean OR"));
return ira->codegen->builtin_types.entry_invalid;
}
return result_type;
}
static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) {
if (fn_value == ira->codegen->invalid_instruction)
return nullptr;
if (type_is_invalid(fn_value->value.type))
return nullptr;
if (fn_value->value.type->id != ZigTypeIdFn) {
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;
assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction);
return const_val->data.x_ptr.data.fn.fn_entry;
}
static IrInstruction *ir_analyze_optional_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
ZigType *wanted_type)
{
assert(wanted_type->id == ZigTypeIdOptional);
if (instr_is_comptime(value)) {
ZigType *payload_type = wanted_type->data.maybe.child_type;
IrInstruction *casted_payload = ir_implicit_cast(ira, value, payload_type);
if (type_is_invalid(casted_payload->value.type))
return ira->codegen->invalid_instruction;
ConstExprValue *val = ir_resolve_const(ira, casted_payload, 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.special = ConstValSpecialStatic;
if (types_have_same_zig_comptime_repr(wanted_type, payload_type)) {
copy_const_val(&const_instruction->base.value, val, val->data.x_ptr.mut == ConstPtrMutComptimeConst);
} else {
const_instruction->base.value.data.x_optional = val;
}
const_instruction->base.value.type = wanted_type;
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 = RuntimeHintOptionalNonNull;
ir_add_alloca(ira, result, wanted_type);
return result;
}
static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *value, ZigType *wanted_type)
{
assert(wanted_type->id == ZigTypeIdErrorUnion);
if (instr_is_comptime(value)) {
ZigType *payload_type = wanted_type->data.error_union.payload_type;
IrInstruction *casted_payload = ir_implicit_cast(ira, value, payload_type);
if (type_is_invalid(casted_payload->value.type))
return ira->codegen->invalid_instruction;
ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
ConstExprValue *err_set_val = create_const_vals(1);
err_set_val->type = wanted_type->data.error_union.err_set_type;
err_set_val->special = ConstValSpecialStatic;
err_set_val->data.x_err_set = nullptr;
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.data.x_err_union.error_set = err_set_val;
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_set_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
ZigType *wanted_type)
{
assert(value->value.type->id == ZigTypeIdErrorSet);
assert(wanted_type->id == ZigTypeIdErrorSet);
if (instr_is_comptime(value)) {
ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) {
return ira->codegen->invalid_instruction;
}
if (!type_is_global_error_set(wanted_type)) {
bool subset = false;
for (uint32_t i = 0, count = wanted_type->data.error_set.err_count; i < count; i += 1) {
if (wanted_type->data.error_set.errors[i]->value == val->data.x_err_set->value) {
subset = true;
break;
}
}
if (!subset) {
ir_add_error(ira, source_instr,
buf_sprintf("error.%s not a member of error set '%s'",
buf_ptr(&val->data.x_err_set->name), buf_ptr(&wanted_type->name)));
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.data.x_err_set = val->data.x_err_set;
return &const_instruction->base;
}
IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, CastOpErrSet);
result->value.type = wanted_type;
return result;
}
static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) {
assert(wanted_type->id == ZigTypeIdErrorUnion);
IrInstruction *casted_value = ir_implicit_cast(ira, value, wanted_type->data.error_union.err_set_type);
if (instr_is_comptime(casted_value)) {
ConstExprValue *val = ir_resolve_const(ira, casted_value, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
ConstExprValue *err_set_val = create_const_vals(1);
err_set_val->special = ConstValSpecialStatic;
err_set_val->type = wanted_type->data.error_union.err_set_type;
err_set_val->data.x_err_set = val->data.x_err_set;
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.data.x_err_union.error_set = err_set_val;
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_null_to_maybe(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) {
assert(wanted_type->id == ZigTypeIdOptional);
assert(instr_is_comptime(value));
ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
assert(val != nullptr);
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
result->value.special = ConstValSpecialStatic;
if (get_codegen_ptr_type(wanted_type) != nullptr) {
result->value.data.x_ptr.special = ConstPtrSpecialNull;
} else if (is_opt_err_set(wanted_type)) {
result->value.data.x_err_set = nullptr;
} else {
result->value.data.x_optional = nullptr;
}
return result;
}
static IrInstruction *ir_analyze_null_to_c_pointer(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *value, ZigType *wanted_type)
{
assert(wanted_type->id == ZigTypeIdPointer);
assert(wanted_type->data.pointer.ptr_len == PtrLenC);
assert(instr_is_comptime(value));
ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
assert(val != nullptr);
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
result->value.data.x_ptr.special = ConstPtrSpecialNull;
result->value.data.x_ptr.mut = ConstPtrMutComptimeConst;
return result;
}
static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *value,
bool is_const, bool is_volatile)
{
Error err;
if (type_is_invalid(value->value.type))
return ira->codegen->invalid_instruction;
if ((err = type_resolve(ira->codegen, value->value.type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
if (instr_is_comptime(value)) {
ConstExprValue *val = ir_resolve_const(ira, value, UndefOk);
if (!val)
return ira->codegen->invalid_instruction;
return ir_get_const_ptr(ira, source_instruction, val, value->value.type,
ConstPtrMutComptimeConst, is_const, is_volatile, 0);
}
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value->value.type,
is_const, is_volatile, PtrLenSingle, 0, 0, 0, false);
IrInstruction *new_instruction = ir_build_ref(&ira->new_irb, source_instruction->scope,
source_instruction->source_node, value, is_const, is_volatile);
new_instruction->value.type = ptr_type;
new_instruction->value.data.rh_ptr = RuntimeHintPtrStack;
if (type_has_bits(ptr_type) && !handle_is_ptr(value->value.type)) {
ZigFn *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_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *array_arg, ZigType *wanted_type)
{
assert(is_slice(wanted_type));
// In this function we honor the const-ness of wanted_type, because
// we may be casting [0]T to []const T which is perfectly valid.
IrInstruction *array_ptr = nullptr;
IrInstruction *array;
if (array_arg->value.type->id == ZigTypeIdPointer) {
array = ir_get_deref(ira, source_instr, array_arg);
array_ptr = array_arg;
} else {
array = array_arg;
}
ZigType *array_type = array->value.type;
assert(array_type->id == ZigTypeIdArray);
if (instr_is_comptime(array) || array_type->data.array.len == 0) {
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
init_const_slice(ira->codegen, &result->value, &array->value, 0, array_type->data.array.len, true);
result->value.type = wanted_type;
return result;
}
IrInstruction *start = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_usize);
init_const_usize(ira->codegen, &start->value, 0);
IrInstruction *end = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_usize);
init_const_usize(ira->codegen, &end->value, array_type->data.array.len);
if (!array_ptr) array_ptr = ir_get_ref(ira, source_instr, array, true, false);
IrInstruction *result = ir_build_slice(&ira->new_irb, source_instr->scope,
source_instr->source_node, array_ptr, start, end, false);
result->value.type = wanted_type;
result->value.data.rh_slice.id = RuntimeHintSliceIdLen;
result->value.data.rh_slice.len = array_type->data.array.len;
ir_add_alloca(ira, result, result->value.type);
return result;
}
static ZigType *ir_resolve_union_tag_type(IrAnalyze *ira, IrInstruction *source_instr, ZigType *union_type) {
assert(union_type->id == ZigTypeIdUnion);
Error err;
if ((err = type_resolve(ira->codegen, union_type, ResolveStatusSizeKnown)))
return ira->codegen->builtin_types.entry_invalid;
AstNode *decl_node = union_type->data.unionation.decl_node;
if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) {
assert(union_type->data.unionation.tag_type != nullptr);
return union_type->data.unionation.tag_type;
} else {
ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("union '%s' has no tag",
buf_ptr(&union_type->name)));
add_error_note(ira->codegen, msg, decl_node, buf_sprintf("consider 'union(enum)' here"));
return ira->codegen->builtin_types.entry_invalid;
}
}
static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target) {
Error err;
IrInstruction *enum_target;
ZigType *enum_type;
if (target->value.type->id == ZigTypeIdUnion) {
enum_type = ir_resolve_union_tag_type(ira, target, target->value.type);
if (type_is_invalid(enum_type))
return ira->codegen->invalid_instruction;
enum_target = ir_implicit_cast(ira, target, enum_type);
if (type_is_invalid(enum_target->value.type))
return ira->codegen->invalid_instruction;
} else if (target->value.type->id == ZigTypeIdEnum) {
enum_target = target;
enum_type = target->value.type;
} else {
ir_add_error(ira, target,
buf_sprintf("expected enum, found type '%s'", buf_ptr(&target->value.type->name)));
return ira->codegen->invalid_instruction;
}
if ((err = type_resolve(ira->codegen, enum_type, ResolveStatusSizeKnown)))
return ira->codegen->invalid_instruction;
ZigType *tag_type = enum_type->data.enumeration.tag_int_type;
assert(tag_type->id == ZigTypeIdInt || tag_type->id == ZigTypeIdComptimeInt);
// If there is only one possible tag, then we know at comptime what it is.
if (enum_type->data.enumeration.layout == ContainerLayoutAuto &&
enum_type->data.enumeration.src_field_count == 1)
{
assert(tag_type == ira->codegen->builtin_types.entry_num_lit_int);
IrInstruction *result = ir_const(ira, source_instr, tag_type);
init_const_bigint(&result->value, tag_type,
&enum_type->data.enumeration.fields[0].value);
return result;
}
if (instr_is_comptime(enum_target)) {
ConstExprValue *val = ir_resolve_const(ira, enum_target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_const(ira, source_instr, tag_type);
init_const_bigint(&result->value, tag_type, &val->data.x_enum_tag);
return result;
}
IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope,
source_instr->source_node, enum_target);
result->value.type = tag_type;
return result;
}
static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *target, ZigType *wanted_type)
{
assert(target->value.type->id == ZigTypeIdUnion);
assert(wanted_type->id == ZigTypeIdEnum);
assert(wanted_type == target->value.type->data.unionation.tag_type);
if (instr_is_comptime(target)) {
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
result->value.special = ConstValSpecialStatic;
result->value.type = wanted_type;
bigint_init_bigint(&result->value.data.x_enum_tag, &val->data.x_union.tag);
return result;
}
// If there is only 1 possible tag, then we know at comptime what it is.
if (wanted_type->data.enumeration.layout == ContainerLayoutAuto &&
wanted_type->data.enumeration.src_field_count == 1)
{
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
result->value.special = ConstValSpecialStatic;
result->value.type = wanted_type;
TypeEnumField *enum_field = target->value.type->data.unionation.fields[0].enum_field;
bigint_init_bigint(&result->value.data.x_enum_tag, &enum_field->value);
return result;
}
IrInstruction *result = ir_build_union_tag(&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, ZigType *wanted_type)
{
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
init_const_undefined(ira->codegen, &result->value);
return result;
}
static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *target, ZigType *wanted_type)
{
Error err;
assert(wanted_type->id == ZigTypeIdUnion);
assert(target->value.type->id == ZigTypeIdEnum);
if (instr_is_comptime(target)) {
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag);
assert(union_field != nullptr);
if ((err = type_resolve(ira->codegen, union_field->type_entry, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
switch (type_has_one_possible_value(ira->codegen, union_field->type_entry)) {
case OnePossibleValueInvalid:
return ira->codegen->invalid_instruction;
case OnePossibleValueNo: {
AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(
union_field->enum_field->decl_index);
ErrorMsg *msg = ir_add_error(ira, source_instr,
buf_sprintf("cast to union '%s' must initialize '%s' field '%s'",
buf_ptr(&wanted_type->name),
buf_ptr(&union_field->type_entry->name),
buf_ptr(union_field->name)));
add_error_note(ira->codegen, msg, field_node,
buf_sprintf("field '%s' declared here", buf_ptr(union_field->name)));
return ira->codegen->invalid_instruction;
}
case OnePossibleValueYes:
break;
}
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
result->value.special = ConstValSpecialStatic;
result->value.type = wanted_type;
bigint_init_bigint(&result->value.data.x_union.tag, &val->data.x_enum_tag);
result->value.data.x_union.payload = create_const_vals(1);
result->value.data.x_union.payload->special = ConstValSpecialStatic;
result->value.data.x_union.payload->type = union_field->type_entry;
return result;
}
// if the union has all fields 0 bits, we can do it
// and in fact it's a noop cast because the union value is just the enum value
if (wanted_type->data.unionation.gen_field_count == 0) {
IrInstruction *result = ir_build_cast(&ira->new_irb, target->scope, target->source_node, wanted_type, target, CastOpNoop);
result->value.type = wanted_type;
return result;
}
ErrorMsg *msg = ir_add_error(ira, source_instr,
buf_sprintf("runtime cast to union '%s' which has non-void fields",
buf_ptr(&wanted_type->name)));
for (uint32_t i = 0; i < wanted_type->data.unionation.src_field_count; i += 1) {
TypeUnionField *union_field = &wanted_type->data.unionation.fields[i];
if (type_has_bits(union_field->type_entry)) {
AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(i);
add_error_note(ira->codegen, msg, field_node,
buf_sprintf("field '%s' has type '%s'",
buf_ptr(union_field->name),
buf_ptr(&union_field->type_entry->name)));
}
}
return ira->codegen->invalid_instruction;
}
static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *target, ZigType *wanted_type)
{
assert(wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdFloat);
if (instr_is_comptime(target)) {
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
if (wanted_type->id == ZigTypeIdInt) {
if (bigint_cmp_zero(&val->data.x_bigint) == CmpLT && !wanted_type->data.integral.is_signed) {
ir_add_error(ira, source_instr,
buf_sprintf("attempt to cast negative value to unsigned integer"));
return ira->codegen->invalid_instruction;
}
if (!bigint_fits_in_bits(&val->data.x_bigint, wanted_type->data.integral.bit_count,
wanted_type->data.integral.is_signed))
{
ir_add_error(ira, source_instr,
buf_sprintf("cast from '%s' to '%s' truncates bits",
buf_ptr(&target->value.type->name), buf_ptr(&wanted_type->name)));
return ira->codegen->invalid_instruction;
}
}
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
result->value.type = wanted_type;
if (wanted_type->id == ZigTypeIdInt) {
bigint_init_bigint(&result->value.data.x_bigint, &val->data.x_bigint);
} else {
float_init_float(&result->value, val);
}
return result;
}
// If the destination integer type has no bits, then we can emit a comptime
// zero. However, we still want to emit a runtime safety check to make sure
// the target is zero.
if (!type_has_bits(wanted_type)) {
assert(wanted_type->id == ZigTypeIdInt);
assert(type_has_bits(target->value.type));
ir_build_assert_zero(ira, source_instr, target);
IrInstruction *result = ir_const_unsigned(ira, source_instr, 0);
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_int_to_enum(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *target, ZigType *wanted_type)
{
Error err;
assert(wanted_type->id == ZigTypeIdEnum);
ZigType *actual_type = target->value.type;
if ((err = ensure_complete_type(ira->codegen, wanted_type)))
return ira->codegen->invalid_instruction;
if (actual_type != wanted_type->data.enumeration.tag_int_type) {
ir_add_error(ira, source_instr,
buf_sprintf("integer to enum cast from '%s' instead of its tag type, '%s'",
buf_ptr(&actual_type->name),
buf_ptr(&wanted_type->data.enumeration.tag_int_type->name)));
return ira->codegen->invalid_instruction;
}
assert(actual_type->id == ZigTypeIdInt || actual_type->id == ZigTypeIdComptimeInt);
if (instr_is_comptime(target)) {
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
TypeEnumField *field = find_enum_field_by_tag(wanted_type, &val->data.x_bigint);
if (field == nullptr) {
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &val->data.x_bigint, 10);
ErrorMsg *msg = ir_add_error(ira, source_instr,
buf_sprintf("enum '%s' has no tag matching integer value %s",
buf_ptr(&wanted_type->name), buf_ptr(val_buf)));
add_error_note(ira->codegen, msg, wanted_type->data.enumeration.decl_node,
buf_sprintf("'%s' declared here", buf_ptr(&wanted_type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
bigint_init_bigint(&result->value.data.x_enum_tag, &val->data.x_bigint);
return result;
}
IrInstruction *result = ir_build_int_to_enum(&ira->new_irb, source_instr->scope,
source_instr->source_node, nullptr, target);
result->value.type = wanted_type;
return result;
}
static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *target, ZigType *wanted_type)
{
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
if (wanted_type->id == ZigTypeIdComptimeFloat) {
float_init_float(&result->value, val);
} else if (wanted_type->id == ZigTypeIdComptimeInt) {
bigint_init_bigint(&result->value.data.x_bigint, &val->data.x_bigint);
} else {
zig_unreachable();
}
return result;
}
static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
ZigType *wanted_type)
{
assert(target->value.type->id == ZigTypeIdInt);
assert(!target->value.type->data.integral.is_signed);
assert(wanted_type->id == ZigTypeIdErrorSet);
if (instr_is_comptime(target)) {
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) {
return ira->codegen->invalid_instruction;
}
if (type_is_global_error_set(wanted_type)) {
BigInt err_count;
bigint_init_unsigned(&err_count, ira->codegen->errors_by_index.length);
if (bigint_cmp_zero(&val->data.x_bigint) == CmpEQ || bigint_cmp(&val->data.x_bigint, &err_count) != CmpLT) {
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &val->data.x_bigint, 10);
ir_add_error(ira, source_instr,
buf_sprintf("integer value %s represents no error", buf_ptr(val_buf)));
return ira->codegen->invalid_instruction;
}
size_t index = bigint_as_unsigned(&val->data.x_bigint);
result->value.data.x_err_set = ira->codegen->errors_by_index.at(index);
return result;
} else {
ErrorTableEntry *err = nullptr;
BigInt err_int;
for (uint32_t i = 0, count = wanted_type->data.error_set.err_count; i < count; i += 1) {
ErrorTableEntry *this_err = wanted_type->data.error_set.errors[i];
bigint_init_unsigned(&err_int, this_err->value);
if (bigint_cmp(&val->data.x_bigint, &err_int) == CmpEQ) {
err = this_err;
break;
}
}
if (err == nullptr) {
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &val->data.x_bigint, 10);
ir_add_error(ira, source_instr,
buf_sprintf("integer value %s represents no error in '%s'", buf_ptr(val_buf), buf_ptr(&wanted_type->name)));
return ira->codegen->invalid_instruction;
}
result->value.data.x_err_set = err;
return result;
}
}
IrInstruction *result = ir_build_int_to_err(&ira->new_irb, source_instr->scope, source_instr->source_node, target);
result->value.type = wanted_type;
return result;
}
static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
ZigType *wanted_type)
{
assert(wanted_type->id == ZigTypeIdInt);
ZigType *err_type = target->value.type;
if (instr_is_comptime(target)) {
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
ErrorTableEntry *err;
if (err_type->id == ZigTypeIdErrorUnion) {
err = val->data.x_err_union.error_set->data.x_err_set;
} else if (err_type->id == ZigTypeIdErrorSet) {
err = val->data.x_err_set;
} else {
zig_unreachable();
}
result->value.type = wanted_type;
uint64_t err_value = err ? err->value : 0;
bigint_init_unsigned(&result->value.data.x_bigint, err_value);
if (!bigint_fits_in_bits(&result->value.data.x_bigint,
wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed))
{
ir_add_error_node(ira, source_instr->source_node,
buf_sprintf("error code '%s' does not fit in '%s'",
buf_ptr(&err->name), buf_ptr(&wanted_type->name)));
return ira->codegen->invalid_instruction;
}
return result;
}
ZigType *err_set_type;
if (err_type->id == ZigTypeIdErrorUnion) {
err_set_type = err_type->data.error_union.err_set_type;
} else if (err_type->id == ZigTypeIdErrorSet) {
err_set_type = err_type;
} else {
zig_unreachable();
}
if (!type_is_global_error_set(err_set_type)) {
if (!resolve_inferred_error_set(ira->codegen, err_set_type, source_instr->source_node)) {
return ira->codegen->invalid_instruction;
}
if (err_set_type->data.error_set.err_count == 0) {
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
bigint_init_unsigned(&result->value.data.x_bigint, 0);
return result;
} else if (err_set_type->data.error_set.err_count == 1) {
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
ErrorTableEntry *err = err_set_type->data.error_set.errors[0];
bigint_init_unsigned(&result->value.data.x_bigint, err->value);
return result;
}
}
BigInt bn;
bigint_init_unsigned(&bn, ira->codegen->errors_by_index.length);
if (!bigint_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) {
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;
}
IrInstruction *result = ir_build_err_to_int(&ira->new_irb, source_instr->scope, source_instr->source_node, target);
result->value.type = wanted_type;
return result;
}
static IrInstruction *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
ZigType *wanted_type)
{
assert(wanted_type->id == ZigTypeIdPointer);
Error err;
if ((err = type_resolve(ira->codegen, target->value.type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
return ira->codegen->invalid_instruction;
assert((wanted_type->data.pointer.is_const && target->value.type->data.pointer.is_const) || !target->value.type->data.pointer.is_const);
wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, target->value.type));
ZigType *array_type = wanted_type->data.pointer.child_type;
assert(array_type->id == ZigTypeIdArray);
assert(array_type->data.array.len == 1);
if (instr_is_comptime(target)) {
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
assert(val->type->id == ZigTypeIdPointer);
ConstExprValue *pointee = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node);
if (pointee == nullptr)
return ira->codegen->invalid_instruction;
if (pointee->special != ConstValSpecialRuntime) {
ConstExprValue *array_val = create_const_vals(1);
array_val->special = ConstValSpecialStatic;
array_val->type = array_type;
array_val->data.x_array.special = ConstArraySpecialNone;
array_val->data.x_array.data.s_none.elements = pointee;
array_val->parent.id = ConstParentIdScalar;
array_val->parent.data.p_scalar.scalar_val = pointee;
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.data.x_ptr.special = ConstPtrSpecialRef;
const_instruction->base.value.data.x_ptr.data.ref.pointee = array_val;
const_instruction->base.value.data.x_ptr.mut = val->data.x_ptr.mut;
return &const_instruction->base;
}
}
// pointer to array and pointer to single item are represented the same way at runtime
IrInstruction *result = ir_build_cast(&ira->new_irb, target->scope, target->source_node,
wanted_type, target, CastOpBitCast);
result->value.type = wanted_type;
return result;
}
static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCastOnly *cast_result,
ErrorMsg *parent_msg)
{
switch (cast_result->id) {
case ConstCastResultIdOk:
zig_unreachable();
case ConstCastResultIdInvalid:
zig_unreachable();
case ConstCastResultIdOptionalChild: {
ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node,
buf_sprintf("optional type child '%s' cannot cast into optional type child '%s'",
buf_ptr(&cast_result->data.optional->actual_child->name),
buf_ptr(&cast_result->data.optional->wanted_child->name)));
report_recursive_error(ira, source_node, &cast_result->data.optional->child, msg);
break;
}
case ConstCastResultIdErrorUnionErrorSet: {
ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node,
buf_sprintf("error set '%s' cannot cast into error set '%s'",
buf_ptr(&cast_result->data.error_union_error_set->actual_err_set->name),
buf_ptr(&cast_result->data.error_union_error_set->wanted_err_set->name)));
report_recursive_error(ira, source_node, &cast_result->data.error_union_error_set->child, msg);
break;
}
case ConstCastResultIdErrSet: {
ZigList<ErrorTableEntry *> *missing_errors = &cast_result->data.error_set_mismatch->missing_errors;
for (size_t i = 0; i < missing_errors->length; i += 1) {
ErrorTableEntry *error_entry = missing_errors->at(i);
add_error_note(ira->codegen, parent_msg, error_entry->decl_node,
buf_sprintf("'error.%s' not a member of destination error set", buf_ptr(&error_entry->name)));
}
break;
}
case ConstCastResultIdErrSetGlobal: {
add_error_note(ira->codegen, parent_msg, source_node,
buf_sprintf("cannot cast global error set into smaller set"));
break;
}
case ConstCastResultIdPointerChild: {
ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node,
buf_sprintf("pointer type child '%s' cannot cast into pointer type child '%s'",
buf_ptr(&cast_result->data.pointer_mismatch->actual_child->name),
buf_ptr(&cast_result->data.pointer_mismatch->wanted_child->name)));
report_recursive_error(ira, source_node, &cast_result->data.pointer_mismatch->child, msg);
break;
}
case ConstCastResultIdSliceChild: {
ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node,
buf_sprintf("slice type child '%s' cannot cast into slice type child '%s'",
buf_ptr(&cast_result->data.slice_mismatch->actual_child->name),
buf_ptr(&cast_result->data.slice_mismatch->wanted_child->name)));
report_recursive_error(ira, source_node, &cast_result->data.slice_mismatch->child, msg);
break;
}
case ConstCastResultIdErrorUnionPayload: {
ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node,
buf_sprintf("error union payload '%s' cannot cast into error union payload '%s'",
buf_ptr(&cast_result->data.error_union_payload->actual_payload->name),
buf_ptr(&cast_result->data.error_union_payload->wanted_payload->name)));
report_recursive_error(ira, source_node, &cast_result->data.error_union_payload->child, msg);
break;
}
case ConstCastResultIdType: {
AstNode *wanted_decl_node = type_decl_node(cast_result->data.type_mismatch->wanted_type);
AstNode *actual_decl_node = type_decl_node(cast_result->data.type_mismatch->actual_type);
if (wanted_decl_node != nullptr) {
add_error_note(ira->codegen, parent_msg, wanted_decl_node,
buf_sprintf("%s declared here",
buf_ptr(&cast_result->data.type_mismatch->wanted_type->name)));
}
if (actual_decl_node != nullptr) {
add_error_note(ira->codegen, parent_msg, actual_decl_node,
buf_sprintf("%s declared here",
buf_ptr(&cast_result->data.type_mismatch->actual_type->name)));
}
break;
}
case ConstCastResultIdFnArg: {
ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node,
buf_sprintf("parameter %" ZIG_PRI_usize ": '%s' cannot cast into '%s'",
cast_result->data.fn_arg.arg_index,
buf_ptr(&cast_result->data.fn_arg.actual_param_type->name),
buf_ptr(&cast_result->data.fn_arg.expected_param_type->name)));
report_recursive_error(ira, source_node, cast_result->data.fn_arg.child, msg);
break;
}
case ConstCastResultIdBadAllowsZero: {
ZigType *wanted_type = cast_result->data.bad_allows_zero->wanted_type;
ZigType *actual_type = cast_result->data.bad_allows_zero->actual_type;
bool wanted_allows_zero = ptr_allows_addr_zero(wanted_type);
bool actual_allows_zero = ptr_allows_addr_zero(actual_type);
if (actual_allows_zero && !wanted_allows_zero) {
add_error_note(ira->codegen, parent_msg, source_node,
buf_sprintf("'%s' could have null values which are illegal in type '%s'",
buf_ptr(&actual_type->name),
buf_ptr(&wanted_type->name)));
} else {
add_error_note(ira->codegen, parent_msg, source_node,
buf_sprintf("mutable '%s' allows illegal null values stored to type '%s'",
buf_ptr(&wanted_type->name),
buf_ptr(&actual_type->name)));
}
break;
}
case ConstCastResultIdFnIsGeneric:
add_error_note(ira->codegen, parent_msg, source_node,
buf_sprintf("only one of the functions is generic"));
break;
case ConstCastResultIdFnAlign: // TODO
case ConstCastResultIdFnCC: // TODO
case ConstCastResultIdFnVarArgs: // TODO
case ConstCastResultIdFnReturnType: // TODO
case ConstCastResultIdFnArgCount: // TODO
case ConstCastResultIdFnGenericArgCount: // TODO
case ConstCastResultIdFnArgNoAlias: // TODO
case ConstCastResultIdUnresolvedInferredErrSet: // TODO
case ConstCastResultIdAsyncAllocatorType: // TODO
break;
}
}
static IrInstruction *ir_analyze_array_to_vector(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *array, ZigType *vector_type)
{
if (instr_is_comptime(array)) {
// arrays and vectors have the same ConstExprValue representation
IrInstruction *result = ir_const(ira, source_instr, vector_type);
copy_const_val(&result->value, &array->value, false);
result->value.type = vector_type;
return result;
}
return ir_build_array_to_vector(ira, source_instr, array, vector_type);
}
static IrInstruction *ir_analyze_vector_to_array(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *vector, ZigType *array_type)
{
if (instr_is_comptime(vector)) {
// arrays and vectors have the same ConstExprValue representation
IrInstruction *result = ir_const(ira, source_instr, array_type);
copy_const_val(&result->value, &vector->value, false);
result->value.type = array_type;
return result;
}
return ir_build_vector_to_array(ira, source_instr, vector, array_type);
}
static IrInstruction *ir_analyze_int_to_c_ptr(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *integer, ZigType *dest_type)
{
IrInstruction *unsigned_integer;
if (instr_is_comptime(integer)) {
unsigned_integer = integer;
} else {
assert(integer->value.type->id == ZigTypeIdInt);
if (integer->value.type->data.integral.bit_count >
ira->codegen->builtin_types.entry_usize->data.integral.bit_count)
{
ir_add_error(ira, source_instr,
buf_sprintf("integer type '%s' too big for implicit @intToPtr to type '%s'",
buf_ptr(&integer->value.type->name),
buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
if (integer->value.type->data.integral.is_signed) {
ZigType *unsigned_int_type = get_int_type(ira->codegen, false,
integer->value.type->data.integral.bit_count);
unsigned_integer = ir_analyze_bit_cast(ira, source_instr, integer, unsigned_int_type);
if (type_is_invalid(unsigned_integer->value.type))
return ira->codegen->invalid_instruction;
} else {
unsigned_integer = integer;
}
}
return ir_analyze_int_to_ptr(ira, source_instr, unsigned_integer, dest_type);
}
static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
ZigType *wanted_type, IrInstruction *value)
{
Error err;
ZigType *actual_type = value->value.type;
AstNode *source_node = source_instr->source_node;
if (type_is_invalid(wanted_type) || type_is_invalid(actual_type)) {
return ira->codegen->invalid_instruction;
}
// perfect match or non-const to const
ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type,
source_node, false);
if (const_cast_result.id == ConstCastResultIdInvalid)
return ira->codegen->invalid_instruction;
if (const_cast_result.id == ConstCastResultIdOk) {
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false);
}
// cast from T to ?T
// note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism
if (wanted_type->id == ZigTypeIdOptional) {
ZigType *wanted_child_type = wanted_type->data.maybe.child_type;
if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node,
false).id == ConstCastResultIdOk)
{
return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type);
} else if (actual_type->id == ZigTypeIdComptimeInt ||
actual_type->id == ZigTypeIdComptimeFloat)
{
if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) {
return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type);
} else {
return ira->codegen->invalid_instruction;
}
} else if (
wanted_child_type->id == ZigTypeIdPointer &&
wanted_child_type->data.pointer.ptr_len == PtrLenUnknown &&
actual_type->id == ZigTypeIdPointer &&
actual_type->data.pointer.ptr_len == PtrLenSingle &&
actual_type->data.pointer.child_type->id == ZigTypeIdArray)
{
if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
return ira->codegen->invalid_instruction;
if ((err = type_resolve(ira->codegen, wanted_child_type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
return ira->codegen->invalid_instruction;
if (get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_child_type) &&
types_match_const_cast_only(ira, wanted_child_type->data.pointer.child_type,
actual_type->data.pointer.child_type->data.array.child_type, source_node,
!wanted_child_type->data.pointer.is_const).id == ConstCastResultIdOk)
{
IrInstruction *cast1 = ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value,
wanted_child_type);
if (type_is_invalid(cast1->value.type))
return ira->codegen->invalid_instruction;
return ir_analyze_optional_wrap(ira, source_instr, cast1, wanted_type);
}
}
}
// T to E!T
if (wanted_type->id == ZigTypeIdErrorUnion) {
if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type,
source_node, false).id == ConstCastResultIdOk)
{
return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
} else if (actual_type->id == ZigTypeIdComptimeInt ||
actual_type->id == ZigTypeIdComptimeFloat)
{
if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error_union.payload_type, true)) {
return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
} else {
return ira->codegen->invalid_instruction;
}
}
}
// cast from T to E!?T
if (wanted_type->id == ZigTypeIdErrorUnion &&
wanted_type->data.error_union.payload_type->id == ZigTypeIdOptional &&
actual_type->id != ZigTypeIdOptional)
{
ZigType *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type;
if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, false).id == ConstCastResultIdOk ||
actual_type->id == ZigTypeIdNull ||
actual_type->id == ZigTypeIdComptimeInt ||
actual_type->id == ZigTypeIdComptimeFloat)
{
IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value);
if (type_is_invalid(cast1->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
if (type_is_invalid(cast2->value.type))
return ira->codegen->invalid_instruction;
return cast2;
}
}
// cast from comptime-known number to another number type
if (instr_is_comptime(value) &&
(actual_type->id == ZigTypeIdInt || actual_type->id == ZigTypeIdComptimeInt ||
actual_type->id == ZigTypeIdFloat || actual_type->id == ZigTypeIdComptimeFloat) &&
(wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdComptimeInt ||
wanted_type->id == ZigTypeIdFloat || wanted_type->id == ZigTypeIdComptimeFloat))
{
if (value->value.special == ConstValSpecialUndef) {
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
result->value.special = ConstValSpecialUndef;
return result;
}
if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) {
if (wanted_type->id == ZigTypeIdComptimeInt || wanted_type->id == ZigTypeIdInt) {
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdInt) {
bigint_init_bigint(&result->value.data.x_bigint, &value->value.data.x_bigint);
} else {
float_init_bigint(&result->value.data.x_bigint, &value->value);
}
return result;
} else if (wanted_type->id == ZigTypeIdComptimeFloat || wanted_type->id == ZigTypeIdFloat) {
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdInt) {
BigFloat bf;
bigfloat_init_bigint(&bf, &value->value.data.x_bigint);
float_init_bigfloat(&result->value, &bf);
} else {
float_init_float(&result->value, &value->value);
}
return result;
}
zig_unreachable();
} else {
return ira->codegen->invalid_instruction;
}
}
// widening conversion
if (wanted_type->id == ZigTypeIdInt &&
actual_type->id == ZigTypeIdInt &&
wanted_type->data.integral.is_signed == actual_type->data.integral.is_signed &&
wanted_type->data.integral.bit_count >= actual_type->data.integral.bit_count)
{
return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
}
// small enough unsigned ints can get casted to large enough signed ints
if (wanted_type->id == ZigTypeIdInt && wanted_type->data.integral.is_signed &&
actual_type->id == ZigTypeIdInt && !actual_type->data.integral.is_signed &&
wanted_type->data.integral.bit_count > actual_type->data.integral.bit_count)
{
return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
}
// float widening conversion
if (wanted_type->id == ZigTypeIdFloat &&
actual_type->id == ZigTypeIdFloat &&
wanted_type->data.floating.bit_count >= actual_type->data.floating.bit_count)
{
return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
}
// cast from [N]T to []const T
// TODO: once https://github.com/ziglang/zig/issues/265 lands, remove this
if (is_slice(wanted_type) && actual_type->id == ZigTypeIdArray) {
ZigType *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
assert(ptr_type->id == ZigTypeIdPointer);
if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
source_node, false).id == ConstCastResultIdOk)
{
return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);
}
}
// cast from [N]T to ?[]const T
// TODO: once https://github.com/ziglang/zig/issues/265 lands, remove this
if (wanted_type->id == ZigTypeIdOptional &&
is_slice(wanted_type->data.maybe.child_type) &&
actual_type->id == ZigTypeIdArray)
{
ZigType *ptr_type =
wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry;
assert(ptr_type->id == ZigTypeIdPointer);
if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
source_node, false).id == ConstCastResultIdOk)
{
IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value);
if (type_is_invalid(cast1->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
if (type_is_invalid(cast2->value.type))
return ira->codegen->invalid_instruction;
return cast2;
}
}
// *[N]T to [*]T
if (wanted_type->id == ZigTypeIdPointer &&
wanted_type->data.pointer.ptr_len == PtrLenUnknown &&
actual_type->id == ZigTypeIdPointer &&
actual_type->data.pointer.ptr_len == PtrLenSingle &&
actual_type->data.pointer.child_type->id == ZigTypeIdArray)
{
if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
return ira->codegen->invalid_instruction;
if ((err = type_resolve(ira->codegen, wanted_type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
return ira->codegen->invalid_instruction;
if (get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_type) &&
types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
actual_type->data.pointer.child_type->data.array.child_type, source_node,
!wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
{
return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type);
}
}
// *[N]T to []T
if (is_slice(wanted_type) &&
actual_type->id == ZigTypeIdPointer &&
actual_type->data.pointer.ptr_len == PtrLenSingle &&
actual_type->data.pointer.child_type->id == ZigTypeIdArray)
{
ZigType *slice_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
assert(slice_ptr_type->id == ZigTypeIdPointer);
ZigType *array_type = actual_type->data.pointer.child_type;
bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0
|| !actual_type->data.pointer.is_const);
if (const_ok && types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type,
array_type->data.array.child_type, source_node,
!slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk)
{
return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type);
}
}
// cast from null literal to maybe type
if (wanted_type->id == ZigTypeIdOptional &&
actual_type->id == ZigTypeIdNull)
{
return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type);
}
// cast from null literal to C pointer
if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC &&
actual_type->id == ZigTypeIdNull)
{
return ir_analyze_null_to_c_pointer(ira, source_instr, value, wanted_type);
}
// cast from [N]T to E![]const T
if (wanted_type->id == ZigTypeIdErrorUnion &&
is_slice(wanted_type->data.error_union.payload_type) &&
actual_type->id == ZigTypeIdArray)
{
ZigType *ptr_type =
wanted_type->data.error_union.payload_type->data.structure.fields[slice_ptr_index].type_entry;
assert(ptr_type->id == ZigTypeIdPointer);
if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
source_node, false).id == ConstCastResultIdOk)
{
IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value);
if (type_is_invalid(cast1->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
if (type_is_invalid(cast2->value.type))
return ira->codegen->invalid_instruction;
return cast2;
}
}
// cast from E to E!T
if (wanted_type->id == ZigTypeIdErrorUnion &&
actual_type->id == ZigTypeIdErrorSet)
{
return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type);
}
// cast from typed number to integer or float literal.
// works when the number is known at compile time
if (instr_is_comptime(value) &&
((actual_type->id == ZigTypeIdInt && wanted_type->id == ZigTypeIdComptimeInt) ||
(actual_type->id == ZigTypeIdFloat && wanted_type->id == ZigTypeIdComptimeFloat)))
{
return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type);
}
// cast from enum literal to enum with matching field name
if (actual_type->id == ZigTypeIdEnumLiteral && wanted_type->id == ZigTypeIdEnum) {
if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
TypeEnumField *field = find_enum_type_field(wanted_type, value->value.data.x_enum_literal);
if (field == nullptr) {
ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("enum '%s' has no field named '%s'",
buf_ptr(&wanted_type->name), buf_ptr(value->value.data.x_enum_literal)));
add_error_note(ira->codegen, msg, wanted_type->data.enumeration.decl_node,
buf_sprintf("'%s' declared here", buf_ptr(&wanted_type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
bigint_init_bigint(&result->value.data.x_enum_tag, &field->value);
return result;
}
// cast from union to the enum type of the union
if (actual_type->id == ZigTypeIdUnion && wanted_type->id == ZigTypeIdEnum) {
if ((err = type_resolve(ira->codegen, actual_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
if (actual_type->data.unionation.tag_type == wanted_type) {
return ir_analyze_union_to_tag(ira, source_instr, value, wanted_type);
}
}
// enum to union which has the enum as the tag type
if (wanted_type->id == ZigTypeIdUnion && actual_type->id == ZigTypeIdEnum &&
(wanted_type->data.unionation.decl_node->data.container_decl.auto_enum ||
wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
{
if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
if (wanted_type->data.unionation.tag_type == actual_type) {
return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type);
}
}
// cast from *T to *[1]T
if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle &&
actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle)
{
ZigType *array_type = wanted_type->data.pointer.child_type;
if (array_type->id == ZigTypeIdArray && array_type->data.array.len == 1 &&
types_match_const_cast_only(ira, array_type->data.array.child_type,
actual_type->data.pointer.child_type, source_node,
!wanted_type->data.pointer.is_const).id == ConstCastResultIdOk &&
// This should be the job of `types_match_const_cast_only`
// but `types_match_const_cast_only` only gets info for child_types
((wanted_type->data.pointer.is_const && actual_type->data.pointer.is_const) ||
!actual_type->data.pointer.is_const))
{
if ((err = type_resolve(ira->codegen, wanted_type->data.pointer.child_type,
ResolveStatusAlignmentKnown)))
{
return ira->codegen->invalid_instruction;
}
if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type,
ResolveStatusAlignmentKnown)))
{
return ira->codegen->invalid_instruction;
}
uint32_t wanted_align = get_ptr_align(ira->codegen, wanted_type);
uint32_t actual_align = get_ptr_align(ira->codegen, actual_type);
if (wanted_align > actual_align) {
ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment"));
add_error_note(ira->codegen, msg, value->source_node,
buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name), actual_align));
add_error_note(ira->codegen, msg, source_instr->source_node,
buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name), wanted_align));
return ira->codegen->invalid_instruction;
}
return ir_analyze_ptr_to_array(ira, source_instr, value, wanted_type);
}
}
// cast from *T and [*]T to *c_void and ?*c_void
// but don't do it if the actual type is a double pointer
if (actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.child_type->id != ZigTypeIdPointer) {
ZigType *dest_ptr_type = nullptr;
if (wanted_type->id == ZigTypeIdPointer &&
wanted_type->data.pointer.ptr_len == PtrLenSingle &&
wanted_type->data.pointer.child_type == ira->codegen->builtin_types.entry_c_void)
{
dest_ptr_type = wanted_type;
} else if (wanted_type->id == ZigTypeIdOptional &&
wanted_type->data.maybe.child_type->id == ZigTypeIdPointer &&
wanted_type->data.maybe.child_type->data.pointer.ptr_len == PtrLenSingle &&
wanted_type->data.maybe.child_type->data.pointer.child_type == ira->codegen->builtin_types.entry_c_void)
{
dest_ptr_type = wanted_type->data.maybe.child_type;
}
if (dest_ptr_type != nullptr &&
(!actual_type->data.pointer.is_const || dest_ptr_type->data.pointer.is_const) &&
(!actual_type->data.pointer.is_volatile || dest_ptr_type->data.pointer.is_volatile) &&
actual_type->data.pointer.bit_offset_in_host == dest_ptr_type->data.pointer.bit_offset_in_host &&
actual_type->data.pointer.host_int_bytes == dest_ptr_type->data.pointer.host_int_bytes &&
get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, dest_ptr_type))
{
return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr, true);
}
}
// cast from T to *T where T is zero bits
if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle &&
types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
{
if ((err = type_resolve(ira->codegen, actual_type, ResolveStatusZeroBitsKnown))) {
return ira->codegen->invalid_instruction;
}
if (!type_has_bits(actual_type)) {
return ir_get_ref(ira, source_instr, value, false, false);
}
}
// cast from @Vector(N, T) to [N]T
if (wanted_type->id == ZigTypeIdArray && actual_type->id == ZigTypeIdVector &&
wanted_type->data.array.len == actual_type->data.vector.len &&
types_match_const_cast_only(ira, wanted_type->data.array.child_type,
actual_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk)
{
return ir_analyze_vector_to_array(ira, source_instr, value, wanted_type);
}
// cast from [N]T to @Vector(N, T)
if (actual_type->id == ZigTypeIdArray && wanted_type->id == ZigTypeIdVector &&
actual_type->data.array.len == wanted_type->data.vector.len &&
types_match_const_cast_only(ira, actual_type->data.array.child_type,
wanted_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk)
{
return ir_analyze_array_to_vector(ira, source_instr, value, wanted_type);
}
// casting between C pointers and normal pointers
if (wanted_type->id == ZigTypeIdPointer && actual_type->id == ZigTypeIdPointer &&
(wanted_type->data.pointer.ptr_len == PtrLenC || actual_type->data.pointer.ptr_len == PtrLenC) &&
types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
actual_type->data.pointer.child_type, source_node,
!wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
{
return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr, true);
}
// cast from integer to C pointer
if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC &&
(actual_type->id == ZigTypeIdInt || actual_type->id == ZigTypeIdComptimeInt))
{
return ir_analyze_int_to_c_ptr(ira, source_instr, value, wanted_type);
}
// cast from undefined to anything
if (actual_type->id == ZigTypeIdUndefined) {
return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type);
}
ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node,
buf_sprintf("expected type '%s', found '%s'",
buf_ptr(&wanted_type->name),
buf_ptr(&actual_type->name)));
report_recursive_error(ira, source_instr->source_node, &const_cast_result, parent_msg);
return ira->codegen->invalid_instruction;
}
static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, ZigType *expected_type) {
assert(value);
assert(value != ira->codegen->invalid_instruction);
assert(!expected_type || !type_is_invalid(expected_type));
assert(value->value.type);
assert(!type_is_invalid(value->value.type));
if (expected_type == nullptr)
return value; // anything will do
if (expected_type == value->value.type)
return value; // match
if (value->value.type->id == ZigTypeIdUnreachable)
return value;
return ir_analyze_cast(ira, value, expected_type, value);
}
static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr) {
Error err;
ZigType *type_entry = ptr->value.type;
if (type_is_invalid(type_entry)) {
return ira->codegen->invalid_instruction;
} else if (type_entry->id == ZigTypeIdPointer) {
ZigType *child_type = type_entry->data.pointer.child_type;
// if the child type has one possible value, the deref is comptime
switch (type_has_one_possible_value(ira->codegen, child_type)) {
case OnePossibleValueInvalid:
return ira->codegen->invalid_instruction;
case OnePossibleValueYes:
return ir_const(ira, source_instruction, child_type);
case OnePossibleValueNo:
break;
}
if (instr_is_comptime(ptr)) {
if (ptr->value.special == ConstValSpecialUndef) {
ir_add_error(ira, ptr, buf_sprintf("attempt to dereference undefined value"));
return ira->codegen->invalid_instruction;
}
if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst ||
ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar)
{
ConstExprValue *pointee = const_ptr_pointee_unchecked(ira->codegen, &ptr->value);
if (pointee->special != ConstValSpecialRuntime) {
IrInstruction *result = ir_const(ira, source_instruction, child_type);
if ((err = ir_read_const_ptr(ira, ira->codegen, source_instruction->source_node, &result->value,
&ptr->value)))
{
return ira->codegen->invalid_instruction;
}
result->value.type = child_type;
return result;
}
}
}
// if the instruction is a const ref instruction we can skip it
if (ptr->id == IrInstructionIdRef) {
IrInstructionRef *ref_inst = reinterpret_cast<IrInstructionRef *>(ptr);
return ref_inst->value;
}
IrInstruction *result = ir_build_load_ptr_gen(ira, source_instruction, ptr, child_type);
if (type_entry->data.pointer.host_int_bytes != 0 && handle_is_ptr(child_type)) {
ir_add_alloca(ira, result, child_type);
}
return result;
} 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 bool ir_resolve_align(IrAnalyze *ira, IrInstruction *value, uint32_t *out) {
if (type_is_invalid(value->value.type))
return false;
IrInstruction *casted_value = ir_implicit_cast(ira, value, get_align_amt_type(ira->codegen));
if (type_is_invalid(casted_value->value.type))
return false;
ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
if (!const_val)
return false;
uint32_t align_bytes = bigint_as_unsigned(&const_val->data.x_bigint);
if (align_bytes == 0) {
ir_add_error(ira, value, buf_sprintf("alignment must be >= 1"));
return false;
}
if (!is_power_of_2(align_bytes)) {
ir_add_error(ira, value, buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes));
return false;
}
*out = align_bytes;
return true;
}
static bool ir_resolve_unsigned(IrAnalyze *ira, IrInstruction *value, ZigType *int_type, uint64_t *out) {
if (type_is_invalid(value->value.type))
return false;
IrInstruction *casted_value = ir_implicit_cast(ira, value, int_type);
if (type_is_invalid(casted_value->value.type))
return false;
ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
if (!const_val)
return false;
*out = bigint_as_unsigned(&const_val->data.x_bigint);
return true;
}
static bool ir_resolve_usize(IrAnalyze *ira, IrInstruction *value, uint64_t *out) {
return ir_resolve_unsigned(ira, value, ira->codegen->builtin_types.entry_usize, out);
}
static bool ir_resolve_bool(IrAnalyze *ira, IrInstruction *value, bool *out) {
if (type_is_invalid(value->value.type))
return false;
IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_bool);
if (type_is_invalid(casted_value->value.type))
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 (type_is_invalid(value->value.type))
return false;
ConstExprValue *atomic_order_val = get_builtin_value(ira->codegen, "AtomicOrder");
assert(atomic_order_val->type->id == ZigTypeIdMetaType);
ZigType *atomic_order_type = atomic_order_val->data.x_type;
IrInstruction *casted_value = ir_implicit_cast(ira, value, atomic_order_type);
if (type_is_invalid(casted_value->value.type))
return false;
ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
if (!const_val)
return false;
*out = (AtomicOrder)bigint_as_unsigned(&const_val->data.x_enum_tag);
return true;
}
static bool ir_resolve_atomic_rmw_op(IrAnalyze *ira, IrInstruction *value, AtomicRmwOp *out) {
if (type_is_invalid(value->value.type))
return false;
ConstExprValue *atomic_rmw_op_val = get_builtin_value(ira->codegen, "AtomicRmwOp");
assert(atomic_rmw_op_val->type->id == ZigTypeIdMetaType);
ZigType *atomic_rmw_op_type = atomic_rmw_op_val->data.x_type;
IrInstruction *casted_value = ir_implicit_cast(ira, value, atomic_rmw_op_type);
if (type_is_invalid(casted_value->value.type))
return false;
ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
if (!const_val)
return false;
*out = (AtomicRmwOp)bigint_as_unsigned(&const_val->data.x_enum_tag);
return true;
}
static bool ir_resolve_global_linkage(IrAnalyze *ira, IrInstruction *value, GlobalLinkageId *out) {
if (type_is_invalid(value->value.type))
return false;
ConstExprValue *global_linkage_val = get_builtin_value(ira->codegen, "GlobalLinkage");
assert(global_linkage_val->type->id == ZigTypeIdMetaType);
ZigType *global_linkage_type = global_linkage_val->data.x_type;
IrInstruction *casted_value = ir_implicit_cast(ira, value, global_linkage_type);
if (type_is_invalid(casted_value->value.type))
return false;
ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
if (!const_val)
return false;
*out = (GlobalLinkageId)bigint_as_unsigned(&const_val->data.x_enum_tag);
return true;
}
static bool ir_resolve_float_mode(IrAnalyze *ira, IrInstruction *value, FloatMode *out) {
if (type_is_invalid(value->value.type))
return false;
ConstExprValue *float_mode_val = get_builtin_value(ira->codegen, "FloatMode");
assert(float_mode_val->type->id == ZigTypeIdMetaType);
ZigType *float_mode_type = float_mode_val->data.x_type;
IrInstruction *casted_value = ir_implicit_cast(ira, value, float_mode_type);
if (type_is_invalid(casted_value->value.type))
return false;
ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
if (!const_val)
return false;
*out = (FloatMode)bigint_as_unsigned(&const_val->data.x_enum_tag);
return true;
}
static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) {
if (type_is_invalid(value->value.type))
return nullptr;
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
true, false, PtrLenUnknown, 0, 0, 0, false);
ZigType *str_type = get_slice_type(ira->codegen, ptr_type);
IrInstruction *casted_value = ir_implicit_cast(ira, value, str_type);
if (type_is_invalid(casted_value->value.type))
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];
assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray);
ConstExprValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val;
if (array_val->data.x_array.special == ConstArraySpecialBuf) {
return array_val->data.x_array.data.s_buf;
}
expand_undef_array(ira->codegen, array_val);
size_t len = bigint_as_unsigned(&len_field->data.x_bigint);
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.data.base_array.elem_index + i;
ConstExprValue *char_val = &array_val->data.x_array.data.s_none.elements[new_index];
if (char_val->special == ConstValSpecialUndef) {
ir_add_error(ira, casted_value, buf_sprintf("use of undefined value"));
return nullptr;
}
uint64_t big_c = bigint_as_unsigned(&char_val->data.x_bigint);
assert(big_c <= UINT8_MAX);
uint8_t c = (uint8_t)big_c;
buf_ptr(result)[i] = c;
}
return result;
}
static IrInstruction *ir_analyze_instruction_add_implicit_return_type(IrAnalyze *ira,
IrInstructionAddImplicitReturnType *instruction)
{
IrInstruction *value = instruction->value->child;
if (type_is_invalid(value->value.type))
return ir_unreach_error(ira);
ira->src_implicit_return_type_list.append(value);
return ir_const_void(ira, &instruction->base);
}
static IrInstruction *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructionReturn *instruction) {
IrInstruction *value = instruction->value->child;
if (type_is_invalid(value->value.type))
return ir_unreach_error(ira);
IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->explicit_return_type);
if (type_is_invalid(casted_value->value.type)) {
AstNode *source_node = ira->explicit_return_type_source_node;
if (source_node != nullptr) {
ErrorMsg *msg = ira->codegen->errors.last();
add_error_note(ira->codegen, msg, source_node,
buf_sprintf("return type declared here"));
}
return ir_unreach_error(ira);
}
if (casted_value->value.special == ConstValSpecialRuntime &&
casted_value->value.type->id == ZigTypeIdPointer &&
casted_value->value.data.rh_ptr == RuntimeHintPtrStack)
{
ir_add_error(ira, casted_value, buf_sprintf("function returns address of local variable"));
return ir_unreach_error(ira);
}
IrInstruction *result = ir_build_return(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, casted_value);
result->value.type = ira->codegen->builtin_types.entry_unreachable;
return ir_finish_anal(ira, result);
}
static IrInstruction *ir_analyze_instruction_const(IrAnalyze *ira, IrInstructionConst *instruction) {
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
copy_const_val(&result->value, &instruction->base.value, true);
return result;
}
static IrInstruction *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
IrInstruction *op1 = bin_op_instruction->op1->child;
if (type_is_invalid(op1->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *op2 = bin_op_instruction->op2->child;
if (type_is_invalid(op2->value.type))
return ira->codegen->invalid_instruction;
ZigType *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->invalid_instruction;
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, bool_type);
if (casted_op2 == ira->codegen->invalid_instruction)
return ira->codegen->invalid_instruction;
if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) {
ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad);
if (op1_val == nullptr)
return ira->codegen->invalid_instruction;
ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);
if (op2_val == nullptr)
return ira->codegen->invalid_instruction;
assert(casted_op1->value.type->id == ZigTypeIdBool);
assert(casted_op2->value.type->id == ZigTypeIdBool);
bool result_bool;
if (bin_op_instruction->op_id == IrBinOpBoolOr) {
result_bool = op1_val->data.x_bool || op2_val->data.x_bool;
} else if (bin_op_instruction->op_id == IrBinOpBoolAnd) {
result_bool = op1_val->data.x_bool && op2_val->data.x_bool;
} else {
zig_unreachable();
}
return ir_const_bool(ira, &bin_op_instruction->base, result_bool);
}
IrInstruction *result = ir_build_bin_op(&ira->new_irb,
bin_op_instruction->base.scope, bin_op_instruction->base.source_node,
bin_op_instruction->op_id, casted_op1, casted_op2, bin_op_instruction->safety_check_on);
result->value.type = bool_type;
return result;
}
static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) {
switch (op_id) {
case IrBinOpCmpEq:
return cmp == CmpEQ;
case IrBinOpCmpNotEq:
return cmp != CmpEQ;
case IrBinOpCmpLessThan:
return cmp == CmpLT;
case IrBinOpCmpGreaterThan:
return cmp == CmpGT;
case IrBinOpCmpLessOrEq:
return cmp != CmpGT;
case IrBinOpCmpGreaterOrEq:
return cmp != CmpLT;
default:
zig_unreachable();
}
}
static bool optional_value_is_null(ConstExprValue *val) {
assert(val->special == ConstValSpecialStatic);
if (get_codegen_ptr_type(val->type) != nullptr) {
if (val->data.x_ptr.special == ConstPtrSpecialNull) {
return true;
} else if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {
return val->data.x_ptr.data.hard_coded_addr.addr == 0;
} else {
return false;
}
} else if (is_opt_err_set(val->type)) {
return val->data.x_err_set == nullptr;
} else {
return val->data.x_optional == nullptr;
}
}
static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
IrInstruction *op1 = bin_op_instruction->op1->child;
if (type_is_invalid(op1->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *op2 = bin_op_instruction->op2->child;
if (type_is_invalid(op2->value.type))
return ira->codegen->invalid_instruction;
AstNode *source_node = bin_op_instruction->base.source_node;
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 == ZigTypeIdNull && op2->value.type->id == ZigTypeIdNull) {
return ir_const_bool(ira, &bin_op_instruction->base, (op_id == IrBinOpCmpEq));
} else if (is_equality_cmp &&
((op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdOptional) ||
(op2->value.type->id == ZigTypeIdNull && op1->value.type->id == ZigTypeIdOptional)))
{
IrInstruction *maybe_op;
if (op1->value.type->id == ZigTypeIdNull) {
maybe_op = op2;
} else if (op2->value.type->id == ZigTypeIdNull) {
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->invalid_instruction;
bool is_null = optional_value_is_null(maybe_val);
bool bool_result = (op_id == IrBinOpCmpEq) ? is_null : !is_null;
return ir_const_bool(ira, &bin_op_instruction->base, bool_result);
}
IrInstruction *is_non_null = ir_build_test_nonnull(&ira->new_irb, bin_op_instruction->base.scope,
source_node, maybe_op);
is_non_null->value.type = ira->codegen->builtin_types.entry_bool;
if (op_id == IrBinOpCmpEq) {
IrInstruction *result = ir_build_bool_not(&ira->new_irb, bin_op_instruction->base.scope,
bin_op_instruction->base.source_node, is_non_null);
result->value.type = ira->codegen->builtin_types.entry_bool;
return result;
} else {
return is_non_null;
}
} else if (is_equality_cmp &&
((op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdPointer &&
op2->value.type->data.pointer.ptr_len == PtrLenC) ||
(op2->value.type->id == ZigTypeIdNull && op1->value.type->id == ZigTypeIdPointer &&
op1->value.type->data.pointer.ptr_len == PtrLenC)))
{
IrInstruction *c_ptr_op;
if (op1->value.type->id == ZigTypeIdNull) {
c_ptr_op = op2;
} else if (op2->value.type->id == ZigTypeIdNull) {
c_ptr_op = op1;
} else {
zig_unreachable();
}
if (instr_is_comptime(c_ptr_op)) {
ConstExprValue *c_ptr_val = ir_resolve_const(ira, c_ptr_op, UndefOk);
if (!c_ptr_val)
return ira->codegen->invalid_instruction;
if (c_ptr_val->special == ConstValSpecialUndef)
return ir_const_undef(ira, &bin_op_instruction->base, ira->codegen->builtin_types.entry_bool);
bool is_null = c_ptr_val->data.x_ptr.special == ConstPtrSpecialNull ||
(c_ptr_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr &&
c_ptr_val->data.x_ptr.data.hard_coded_addr.addr == 0);
bool bool_result = (op_id == IrBinOpCmpEq) ? is_null : !is_null;
return ir_const_bool(ira, &bin_op_instruction->base, bool_result);
}
IrInstruction *is_non_null = ir_build_test_nonnull(&ira->new_irb, bin_op_instruction->base.scope,
source_node, c_ptr_op);
is_non_null->value.type = ira->codegen->builtin_types.entry_bool;
if (op_id == IrBinOpCmpEq) {
IrInstruction *result = ir_build_bool_not(&ira->new_irb, bin_op_instruction->base.scope,
bin_op_instruction->base.source_node, is_non_null);
result->value.type = ira->codegen->builtin_types.entry_bool;
return result;
} else {
return is_non_null;
}
} else if (op1->value.type->id == ZigTypeIdNull || op2->value.type->id == ZigTypeIdNull) {
ZigType *non_null_type = (op1->value.type->id == ZigTypeIdNull) ? op2->value.type : op1->value.type;
ir_add_error_node(ira, source_node, buf_sprintf("comparison of '%s' with null",
buf_ptr(&non_null_type->name)));
return ira->codegen->invalid_instruction;
}
if (op1->value.type->id == ZigTypeIdErrorSet && op2->value.type->id == ZigTypeIdErrorSet) {
if (!is_equality_cmp) {
ir_add_error_node(ira, source_node, buf_sprintf("operator not allowed for errors"));
return ira->codegen->invalid_instruction;
}
ZigType *intersect_type = get_error_set_intersection(ira, op1->value.type, op2->value.type, source_node);
if (type_is_invalid(intersect_type)) {
return ira->codegen->invalid_instruction;
}
if (!resolve_inferred_error_set(ira->codegen, intersect_type, source_node)) {
return ira->codegen->invalid_instruction;
}
// exception if one of the operators has the type of the empty error set, we allow the comparison
// (and make it comptime known)
// this is a function which is evaluated at comptime and returns an inferred error set will have an empty
// error set.
if (op1->value.type->data.error_set.err_count == 0 || op2->value.type->data.error_set.err_count == 0) {
bool are_equal = false;
bool answer;
if (op_id == IrBinOpCmpEq) {
answer = are_equal;
} else if (op_id == IrBinOpCmpNotEq) {
answer = !are_equal;
} else {
zig_unreachable();
}
return ir_const_bool(ira, &bin_op_instruction->base, answer);
}
if (!type_is_global_error_set(intersect_type)) {
if (intersect_type->data.error_set.err_count == 0) {
ir_add_error_node(ira, source_node,
buf_sprintf("error sets '%s' and '%s' have no common errors",
buf_ptr(&op1->value.type->name), buf_ptr(&op2->value.type->name)));
return ira->codegen->invalid_instruction;
}
if (op1->value.type->data.error_set.err_count == 1 && op2->value.type->data.error_set.err_count == 1) {
bool are_equal = true;
bool answer;
if (op_id == IrBinOpCmpEq) {
answer = are_equal;
} else if (op_id == IrBinOpCmpNotEq) {
answer = !are_equal;
} else {
zig_unreachable();
}
return ir_const_bool(ira, &bin_op_instruction->base, answer);
}
}
if (instr_is_comptime(op1) && instr_is_comptime(op2)) {
ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
if (op1_val == nullptr)
return ira->codegen->invalid_instruction;
ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad);
if (op2_val == nullptr)
return ira->codegen->invalid_instruction;
bool answer;
bool are_equal = op1_val->data.x_err_set->value == op2_val->data.x_err_set->value;
if (op_id == IrBinOpCmpEq) {
answer = are_equal;
} else if (op_id == IrBinOpCmpNotEq) {
answer = !are_equal;
} else {
zig_unreachable();
}
return ir_const_bool(ira, &bin_op_instruction->base, answer);
}
IrInstruction *result = ir_build_bin_op(&ira->new_irb,
bin_op_instruction->base.scope, bin_op_instruction->base.source_node,
op_id, op1, op2, bin_op_instruction->safety_check_on);
result->value.type = ira->codegen->builtin_types.entry_bool;
return result;
}
IrInstruction *instructions[] = {op1, op2};
ZigType *resolved_type = ir_resolve_peer_types(ira, source_node, nullptr, instructions, 2);
if (type_is_invalid(resolved_type))
return ira->codegen->invalid_instruction;
bool operator_allowed;
switch (resolved_type->id) {
case ZigTypeIdInvalid:
zig_unreachable(); // handled above
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdInt:
case ZigTypeIdFloat:
case ZigTypeIdVector:
operator_allowed = true;
break;
case ZigTypeIdBool:
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdErrorSet:
case ZigTypeIdFn:
case ZigTypeIdOpaque:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdPromise:
case ZigTypeIdEnum:
case ZigTypeIdEnumLiteral:
operator_allowed = is_equality_cmp;
break;
case ZigTypeIdPointer:
operator_allowed = is_equality_cmp || (resolved_type->data.pointer.ptr_len == PtrLenC);
break;
case ZigTypeIdUnreachable:
case ZigTypeIdArray:
case ZigTypeIdStruct:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdErrorUnion:
case ZigTypeIdUnion:
operator_allowed = false;
break;
case ZigTypeIdOptional:
operator_allowed = is_equality_cmp && get_codegen_ptr_type(resolved_type) != nullptr;
break;
}
if (!operator_allowed) {
ir_add_error_node(ira, source_node,
buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, resolved_type);
if (casted_op1 == ira->codegen->invalid_instruction)
return ira->codegen->invalid_instruction;
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, resolved_type);
if (casted_op2 == ira->codegen->invalid_instruction)
return ira->codegen->invalid_instruction;
bool one_possible_value;
switch (type_has_one_possible_value(ira->codegen, resolved_type)) {
case OnePossibleValueInvalid:
return ira->codegen->invalid_instruction;
case OnePossibleValueYes:
one_possible_value = true;
break;
case OnePossibleValueNo:
one_possible_value = false;
break;
}
if (one_possible_value || (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2))) {
ConstExprValue *op1_val = one_possible_value ? &casted_op1->value : ir_resolve_const(ira, casted_op1, UndefBad);
if (op1_val == nullptr)
return ira->codegen->invalid_instruction;
ConstExprValue *op2_val = one_possible_value ? &casted_op2->value : ir_resolve_const(ira, casted_op2, UndefBad);
if (op2_val == nullptr)
return ira->codegen->invalid_instruction;
if (resolved_type->id == ZigTypeIdComptimeFloat || resolved_type->id == ZigTypeIdFloat) {
if (float_is_nan(op1_val) || float_is_nan(op2_val)) {
return ir_const_bool(ira, &bin_op_instruction->base, op_id == IrBinOpCmpNotEq);
}
Cmp cmp_result = float_cmp(op1_val, op2_val);
bool answer = resolve_cmp_op_id(op_id, cmp_result);
return ir_const_bool(ira, &bin_op_instruction->base, answer);
} else if (resolved_type->id == ZigTypeIdComptimeInt || resolved_type->id == ZigTypeIdInt) {
Cmp cmp_result = bigint_cmp(&op1_val->data.x_bigint, &op2_val->data.x_bigint);
bool answer = resolve_cmp_op_id(op_id, cmp_result);
return ir_const_bool(ira, &bin_op_instruction->base, answer);
} else if (resolved_type->id == ZigTypeIdPointer && op_id != IrBinOpCmpEq && op_id != IrBinOpCmpNotEq) {
if ((op1_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr ||
op1_val->data.x_ptr.special == ConstPtrSpecialNull) &&
(op2_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr ||
op2_val->data.x_ptr.special == ConstPtrSpecialNull))
{
uint64_t op1_addr = op1_val->data.x_ptr.special == ConstPtrSpecialNull ?
0 : op1_val->data.x_ptr.data.hard_coded_addr.addr;
uint64_t op2_addr = op2_val->data.x_ptr.special == ConstPtrSpecialNull ?
0 : op2_val->data.x_ptr.data.hard_coded_addr.addr;
Cmp cmp_result;
if (op1_addr > op2_addr) {
cmp_result = CmpGT;
} else if (op1_addr < op2_addr) {
cmp_result = CmpLT;
} else {
cmp_result = CmpEQ;
}
bool answer = resolve_cmp_op_id(op_id, cmp_result);
return ir_const_bool(ira, &bin_op_instruction->base, answer);
}
} else {
bool are_equal = one_possible_value || const_values_equal(ira->codegen, op1_val, op2_val);
bool answer;
if (op_id == IrBinOpCmpEq) {
answer = are_equal;
} else if (op_id == IrBinOpCmpNotEq) {
answer = !are_equal;
} else {
zig_unreachable();
}
return ir_const_bool(ira, &bin_op_instruction->base, answer);
}
}
// some comparisons with unsigned numbers can be evaluated
if (resolved_type->id == ZigTypeIdInt && !resolved_type->data.integral.is_signed) {
ConstExprValue *known_left_val;
IrBinOp flipped_op_id;
if (instr_is_comptime(casted_op1)) {
known_left_val = ir_resolve_const(ira, casted_op1, UndefBad);
if (known_left_val == nullptr)
return ira->codegen->invalid_instruction;
flipped_op_id = op_id;
} else if (instr_is_comptime(casted_op2)) {
known_left_val = ir_resolve_const(ira, casted_op2, UndefBad);
if (known_left_val == nullptr)
return ira->codegen->invalid_instruction;
if (op_id == IrBinOpCmpLessThan) {
flipped_op_id = IrBinOpCmpGreaterThan;
} else if (op_id == IrBinOpCmpGreaterThan) {
flipped_op_id = IrBinOpCmpLessThan;
} else if (op_id == IrBinOpCmpLessOrEq) {
flipped_op_id = IrBinOpCmpGreaterOrEq;
} else if (op_id == IrBinOpCmpGreaterOrEq) {
flipped_op_id = IrBinOpCmpLessOrEq;
} else {
flipped_op_id = op_id;
}
} else {
known_left_val = nullptr;
}
if (known_left_val != nullptr && bigint_cmp_zero(&known_left_val->data.x_bigint) == CmpEQ &&
(flipped_op_id == IrBinOpCmpLessOrEq || flipped_op_id == IrBinOpCmpGreaterThan))
{
bool answer = (flipped_op_id == IrBinOpCmpLessOrEq);
return ir_const_bool(ira, &bin_op_instruction->base, answer);
}
}
IrInstruction *result = ir_build_bin_op(&ira->new_irb,
bin_op_instruction->base.scope, bin_op_instruction->base.source_node,
op_id, casted_op1, casted_op2, bin_op_instruction->safety_check_on);
result->value.type = ira->codegen->builtin_types.entry_bool;
return result;
}
static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, IrInstruction *source_instr, ZigType *type_entry,
ConstExprValue *op1_val, IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val)
{
bool is_int;
bool is_float;
Cmp op2_zcmp;
if (type_entry->id == ZigTypeIdInt || type_entry->id == ZigTypeIdComptimeInt) {
is_int = true;
is_float = false;
op2_zcmp = bigint_cmp_zero(&op2_val->data.x_bigint);
} else if (type_entry->id == ZigTypeIdFloat ||
type_entry->id == ZigTypeIdComptimeFloat)
{
is_int = false;
is_float = true;
op2_zcmp = float_cmp_zero(op2_val);
} else {
zig_unreachable();
}
if ((op_id == IrBinOpDivUnspecified || op_id == IrBinOpRemRem || op_id == IrBinOpRemMod ||
op_id == IrBinOpDivTrunc || op_id == IrBinOpDivFloor) && op2_zcmp == CmpEQ)
{
return ir_add_error(ira, source_instr, buf_sprintf("division by zero"));
}
if ((op_id == IrBinOpRemRem || op_id == IrBinOpRemMod) && op2_zcmp == CmpLT) {
return ir_add_error(ira, source_instr, buf_sprintf("negative denominator"));
}
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:
case IrBinOpRemUnspecified:
case IrBinOpMergeErrorSets:
zig_unreachable();
case IrBinOpBinOr:
assert(is_int);
bigint_or(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
break;
case IrBinOpBinXor:
assert(is_int);
bigint_xor(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
break;
case IrBinOpBinAnd:
assert(is_int);
bigint_and(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
break;
case IrBinOpBitShiftLeftExact:
assert(is_int);
bigint_shl(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
break;
case IrBinOpBitShiftLeftLossy:
assert(type_entry->id == ZigTypeIdInt);
bigint_shl_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint,
type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
break;
case IrBinOpBitShiftRightExact:
{
assert(is_int);
bigint_shr(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
BigInt orig_bigint;
bigint_shl(&orig_bigint, &out_val->data.x_bigint, &op2_val->data.x_bigint);
if (bigint_cmp(&op1_val->data.x_bigint, &orig_bigint) != CmpEQ) {
return ir_add_error(ira, source_instr, buf_sprintf("exact shift shifted out 1 bits"));
}
break;
}
case IrBinOpBitShiftRightLossy:
assert(is_int);
bigint_shr(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
break;
case IrBinOpAdd:
if (is_int) {
bigint_add(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
} else {
float_add(out_val, op1_val, op2_val);
}
break;
case IrBinOpAddWrap:
assert(type_entry->id == ZigTypeIdInt);
bigint_add_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint,
type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
break;
case IrBinOpSub:
if (is_int) {
bigint_sub(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
} else {
float_sub(out_val, op1_val, op2_val);
}
break;
case IrBinOpSubWrap:
assert(type_entry->id == ZigTypeIdInt);
bigint_sub_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint,
type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
break;
case IrBinOpMult:
if (is_int) {
bigint_mul(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
} else {
float_mul(out_val, op1_val, op2_val);
}
break;
case IrBinOpMultWrap:
assert(type_entry->id == ZigTypeIdInt);
bigint_mul_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint,
type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
break;
case IrBinOpDivUnspecified:
assert(is_float);
float_div(out_val, op1_val, op2_val);
break;
case IrBinOpDivTrunc:
if (is_int) {
bigint_div_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
} else {
float_div_trunc(out_val, op1_val, op2_val);
}
break;
case IrBinOpDivFloor:
if (is_int) {
bigint_div_floor(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
} else {
float_div_floor(out_val, op1_val, op2_val);
}
break;
case IrBinOpDivExact:
if (is_int) {
bigint_div_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
BigInt remainder;
bigint_rem(&remainder, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
if (bigint_cmp_zero(&remainder) != CmpEQ) {
return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder"));
}
} else {
float_div_trunc(out_val, op1_val, op2_val);
ConstExprValue remainder;
float_rem(&remainder, op1_val, op2_val);
if (float_cmp_zero(&remainder) != CmpEQ) {
return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder"));
}
}
break;
case IrBinOpRemRem:
if (is_int) {
bigint_rem(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
} else {
float_rem(out_val, op1_val, op2_val);
}
break;
case IrBinOpRemMod:
if (is_int) {
bigint_mod(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
} else {
float_mod(out_val, op1_val, op2_val);
}
break;
}
if (type_entry->id == ZigTypeIdInt) {
if (!bigint_fits_in_bits(&out_val->data.x_bigint, type_entry->data.integral.bit_count,
type_entry->data.integral.is_signed))
{
return ir_add_error(ira, source_instr, buf_sprintf("operation caused overflow"));
}
}
out_val->type = type_entry;
out_val->special = ConstValSpecialStatic;
return nullptr;
}
// This works on operands that have already been checked to be comptime known.
static IrInstruction *ir_analyze_math_op(IrAnalyze *ira, IrInstruction *source_instr,
ZigType *type_entry, ConstExprValue *op1_val, IrBinOp op_id, ConstExprValue *op2_val)
{
IrInstruction *result_instruction = ir_const(ira, source_instr, type_entry);
ConstExprValue *out_val = &result_instruction->value;
if (type_entry->id == ZigTypeIdVector) {
expand_undef_array(ira->codegen, op1_val);
expand_undef_array(ira->codegen, op2_val);
out_val->special = ConstValSpecialUndef;
expand_undef_array(ira->codegen, out_val);
size_t len = type_entry->data.vector.len;
ZigType *scalar_type = type_entry->data.vector.elem_type;
for (size_t i = 0; i < len; i += 1) {
ConstExprValue *scalar_op1_val = &op1_val->data.x_array.data.s_none.elements[i];
ConstExprValue *scalar_op2_val = &op2_val->data.x_array.data.s_none.elements[i];
ConstExprValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i];
assert(scalar_op1_val->type == scalar_type);
assert(scalar_op2_val->type == scalar_type);
assert(scalar_out_val->type == scalar_type);
ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type,
scalar_op1_val, op_id, scalar_op2_val, scalar_out_val);
if (msg != nullptr) {
add_error_note(ira->codegen, msg, source_instr->source_node,
buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i));
return ira->codegen->invalid_instruction;
}
}
out_val->type = type_entry;
out_val->special = ConstValSpecialStatic;
} else {
if (ir_eval_math_op_scalar(ira, source_instr, type_entry, op1_val, op_id, op2_val, out_val) != nullptr) {
return ira->codegen->invalid_instruction;
}
}
return ir_implicit_cast(ira, result_instruction, type_entry);
}
static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
IrInstruction *op1 = bin_op_instruction->op1->child;
if (type_is_invalid(op1->value.type))
return ira->codegen->invalid_instruction;
if (op1->value.type->id != ZigTypeIdInt && op1->value.type->id != ZigTypeIdComptimeInt) {
ir_add_error(ira, &bin_op_instruction->base,
buf_sprintf("bit shifting operation expected integer type, found '%s'",
buf_ptr(&op1->value.type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *op2 = bin_op_instruction->op2->child;
if (type_is_invalid(op2->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_op2;
IrBinOp op_id = bin_op_instruction->op_id;
if (op1->value.type->id == ZigTypeIdComptimeInt) {
casted_op2 = op2;
if (op_id == IrBinOpBitShiftLeftLossy) {
op_id = IrBinOpBitShiftLeftExact;
}
if (casted_op2->value.data.x_bigint.is_negative) {
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &casted_op2->value.data.x_bigint, 10);
ir_add_error(ira, casted_op2, buf_sprintf("shift by negative value %s", buf_ptr(val_buf)));
return ira->codegen->invalid_instruction;
}
} else {
ZigType *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen,
op1->value.type->data.integral.bit_count - 1);
if (bin_op_instruction->op_id == IrBinOpBitShiftLeftLossy &&
op2->value.type->id == ZigTypeIdComptimeInt) {
if (!bigint_fits_in_bits(&op2->value.data.x_bigint,
shift_amt_type->data.integral.bit_count,
op2->value.data.x_bigint.is_negative)) {
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &op2->value.data.x_bigint, 10);
ErrorMsg* msg = ir_add_error(ira,
&bin_op_instruction->base,
buf_sprintf("RHS of shift is too large for LHS type"));
add_error_note(
ira->codegen,
msg,
op2->source_node,
buf_sprintf("value %s cannot fit into type %s",
buf_ptr(val_buf),
buf_ptr(&shift_amt_type->name)));
return ira->codegen->invalid_instruction;
}
}
casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type);
if (casted_op2 == ira->codegen->invalid_instruction)
return ira->codegen->invalid_instruction;
}
if (instr_is_comptime(op1) && instr_is_comptime(casted_op2)) {
ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
if (op1_val == nullptr)
return ira->codegen->invalid_instruction;
ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);
if (op2_val == nullptr)
return ira->codegen->invalid_instruction;
return ir_analyze_math_op(ira, &bin_op_instruction->base, op1->value.type, op1_val, op_id, op2_val);
} else if (op1->value.type->id == ZigTypeIdComptimeInt) {
ir_add_error(ira, &bin_op_instruction->base,
buf_sprintf("LHS of shift must be an integer type, or RHS must be compile-time known"));
return ira->codegen->invalid_instruction;
} else if (instr_is_comptime(casted_op2) && bigint_cmp_zero(&casted_op2->value.data.x_bigint) == CmpEQ) {
IrInstruction *result = ir_build_cast(&ira->new_irb, bin_op_instruction->base.scope,
bin_op_instruction->base.source_node, op1->value.type, op1, CastOpNoop);
result->value.type = op1->value.type;
return result;
}
IrInstruction *result = ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.scope,
bin_op_instruction->base.source_node, op_id,
op1, casted_op2, bin_op_instruction->safety_check_on);
result->value.type = op1->value.type;
return result;
}
static bool ok_float_op(IrBinOp op) {
switch (op) {
case IrBinOpInvalid:
zig_unreachable();
case IrBinOpAdd:
case IrBinOpSub:
case IrBinOpMult:
case IrBinOpDivUnspecified:
case IrBinOpDivTrunc:
case IrBinOpDivFloor:
case IrBinOpDivExact:
case IrBinOpRemRem:
case IrBinOpRemMod:
return true;
case IrBinOpBoolOr:
case IrBinOpBoolAnd:
case IrBinOpCmpEq:
case IrBinOpCmpNotEq:
case IrBinOpCmpLessThan:
case IrBinOpCmpGreaterThan:
case IrBinOpCmpLessOrEq:
case IrBinOpCmpGreaterOrEq:
case IrBinOpBinOr:
case IrBinOpBinXor:
case IrBinOpBinAnd:
case IrBinOpBitShiftLeftLossy:
case IrBinOpBitShiftLeftExact:
case IrBinOpBitShiftRightLossy:
case IrBinOpBitShiftRightExact:
case IrBinOpAddWrap:
case IrBinOpSubWrap:
case IrBinOpMultWrap:
case IrBinOpRemUnspecified:
case IrBinOpArrayCat:
case IrBinOpArrayMult:
case IrBinOpMergeErrorSets:
return false;
}
zig_unreachable();
}
static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) {
if (lhs_type->id != ZigTypeIdPointer)
return false;
switch (op) {
case IrBinOpAdd:
case IrBinOpSub:
break;
default:
return false;
}
switch (lhs_type->data.pointer.ptr_len) {
case PtrLenSingle:
return false;
case PtrLenUnknown:
case PtrLenC:
break;
}
return true;
}
static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *instruction) {
Error err;
IrInstruction *op1 = instruction->op1->child;
if (type_is_invalid(op1->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *op2 = instruction->op2->child;
if (type_is_invalid(op2->value.type))
return ira->codegen->invalid_instruction;
IrBinOp op_id = instruction->op_id;
// look for pointer math
if (is_pointer_arithmetic_allowed(op1->value.type, op_id)) {
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, ira->codegen->builtin_types.entry_usize);
if (type_is_invalid(casted_op2->value.type))
return ira->codegen->invalid_instruction;
if (op1->value.special == ConstValSpecialUndef || casted_op2->value.special == ConstValSpecialUndef) {
IrInstruction *result = ir_const(ira, &instruction->base, op1->value.type);
result->value.special = ConstValSpecialUndef;
return result;
}
if (casted_op2->value.special == ConstValSpecialStatic && op1->value.special == ConstValSpecialStatic &&
(op1->value.data.x_ptr.special == ConstPtrSpecialHardCodedAddr ||
op1->value.data.x_ptr.special == ConstPtrSpecialNull))
{
uint64_t start_addr = (op1->value.data.x_ptr.special == ConstPtrSpecialNull) ?
0 : op1->value.data.x_ptr.data.hard_coded_addr.addr;
uint64_t elem_offset;
if (!ir_resolve_usize(ira, casted_op2, &elem_offset))
return ira->codegen->invalid_instruction;
ZigType *elem_type = op1->value.type->data.pointer.child_type;
if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown)))
return ira->codegen->invalid_instruction;
uint64_t byte_offset = type_size(ira->codegen, elem_type) * elem_offset;
uint64_t new_addr;
if (op_id == IrBinOpAdd) {
new_addr = start_addr + byte_offset;
} else if (op_id == IrBinOpSub) {
new_addr = start_addr - byte_offset;
} else {
zig_unreachable();
}
IrInstruction *result = ir_const(ira, &instruction->base, op1->value.type);
result->value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
result->value.data.x_ptr.mut = ConstPtrMutRuntimeVar;
result->value.data.x_ptr.data.hard_coded_addr.addr = new_addr;
return result;
}
IrInstruction *result = ir_build_bin_op(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, op_id, op1, casted_op2, true);
result->value.type = op1->value.type;
return result;
}
IrInstruction *instructions[] = {op1, op2};
ZigType *resolved_type = ir_resolve_peer_types(ira, instruction->base.source_node, nullptr, instructions, 2);
if (type_is_invalid(resolved_type))
return ira->codegen->invalid_instruction;
bool is_int = resolved_type->id == ZigTypeIdInt || resolved_type->id == ZigTypeIdComptimeInt;
bool is_float = resolved_type->id == ZigTypeIdFloat || resolved_type->id == ZigTypeIdComptimeFloat;
bool is_signed_div = (
(resolved_type->id == ZigTypeIdInt && resolved_type->data.integral.is_signed) ||
resolved_type->id == ZigTypeIdFloat ||
(resolved_type->id == ZigTypeIdComptimeFloat &&
((bigfloat_cmp_zero(&op1->value.data.x_bigfloat) != CmpGT) !=
(bigfloat_cmp_zero(&op2->value.data.x_bigfloat) != CmpGT))) ||
(resolved_type->id == ZigTypeIdComptimeInt &&
((bigint_cmp_zero(&op1->value.data.x_bigint) != CmpGT) !=
(bigint_cmp_zero(&op2->value.data.x_bigint) != CmpGT)))
);
if (op_id == IrBinOpDivUnspecified && is_int) {
if (is_signed_div) {
bool ok = false;
if (instr_is_comptime(op1) && instr_is_comptime(op2)) {
ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
if (op1_val == nullptr)
return ira->codegen->invalid_instruction;
ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad);
if (op2_val == nullptr)
return ira->codegen->invalid_instruction;
if (bigint_cmp_zero(&op2_val->data.x_bigint) == CmpEQ) {
// the division by zero error will be caught later, but we don't have a
// division function ambiguity problem.
op_id = IrBinOpDivTrunc;
ok = true;
} else {
BigInt trunc_result;
BigInt floor_result;
bigint_div_trunc(&trunc_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
bigint_div_floor(&floor_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
if (bigint_cmp(&trunc_result, &floor_result) == CmpEQ) {
ok = true;
op_id = IrBinOpDivTrunc;
}
}
}
if (!ok) {
ir_add_error(ira, &instruction->base,
buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact",
buf_ptr(&op1->value.type->name),
buf_ptr(&op2->value.type->name)));
return ira->codegen->invalid_instruction;
}
} else {
op_id = IrBinOpDivTrunc;
}
} else if (op_id == IrBinOpRemUnspecified) {
if (is_signed_div && (is_int || is_float)) {
bool ok = false;
if (instr_is_comptime(op1) && instr_is_comptime(op2)) {
ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
if (op1_val == nullptr)
return ira->codegen->invalid_instruction;
if (is_int) {
ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad);
if (op2_val == nullptr)
return ira->codegen->invalid_instruction;
if (bigint_cmp_zero(&op2->value.data.x_bigint) == CmpEQ) {
// the division by zero error will be caught later, but we don't
// have a remainder function ambiguity problem
ok = true;
} else {
BigInt rem_result;
BigInt mod_result;
bigint_rem(&rem_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
bigint_mod(&mod_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
ok = bigint_cmp(&rem_result, &mod_result) == CmpEQ;
}
} else {
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, resolved_type);
if (casted_op2 == ira->codegen->invalid_instruction)
return ira->codegen->invalid_instruction;
ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);
if (op2_val == nullptr)
return ira->codegen->invalid_instruction;
if (float_cmp_zero(&casted_op2->value) == CmpEQ) {
// the division by zero error will be caught later, but we don't
// have a remainder function ambiguity problem
ok = true;
} else {
ConstExprValue rem_result;
ConstExprValue mod_result;
float_rem(&rem_result, op1_val, op2_val);
float_mod(&mod_result, op1_val, op2_val);
ok = float_cmp(&rem_result, &mod_result) == CmpEQ;
}
}
}
if (!ok) {
ir_add_error(ira, &instruction->base,
buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod",
buf_ptr(&op1->value.type->name),
buf_ptr(&op2->value.type->name)));
return ira->codegen->invalid_instruction;
}
}
op_id = IrBinOpRemRem;
}
bool ok = false;
if (is_int) {
ok = true;
} else if (is_float && ok_float_op(op_id)) {
ok = true;
} else if (resolved_type->id == ZigTypeIdVector) {
ZigType *elem_type = resolved_type->data.vector.elem_type;
if (elem_type->id == ZigTypeIdInt || elem_type->id == ZigTypeIdComptimeInt) {
ok = true;
} else if ((elem_type->id == ZigTypeIdFloat || elem_type->id == ZigTypeIdComptimeFloat) && ok_float_op(op_id)) {
ok = true;
}
}
if (!ok) {
AstNode *source_node = 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->invalid_instruction;
}
if (resolved_type->id == ZigTypeIdComptimeInt) {
if (op_id == IrBinOpAddWrap) {
op_id = IrBinOpAdd;
} else if (op_id == IrBinOpSubWrap) {
op_id = IrBinOpSub;
} else if (op_id == IrBinOpMultWrap) {
op_id = IrBinOpMult;
}
}
IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, resolved_type);
if (casted_op1 == ira->codegen->invalid_instruction)
return ira->codegen->invalid_instruction;
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, resolved_type);
if (casted_op2 == ira->codegen->invalid_instruction)
return ira->codegen->invalid_instruction;
if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) {
ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad);
if (op1_val == nullptr)
return ira->codegen->invalid_instruction;
ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);
if (op2_val == nullptr)
return ira->codegen->invalid_instruction;
return ir_analyze_math_op(ira, &instruction->base, resolved_type, op1_val, op_id, op2_val);
}
IrInstruction *result = ir_build_bin_op(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, op_id, casted_op1, casted_op2, instruction->safety_check_on);
result->value.type = resolved_type;
return result;
}
static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *instruction) {
IrInstruction *op1 = instruction->op1->child;
ZigType *op1_type = op1->value.type;
if (type_is_invalid(op1_type))
return ira->codegen->invalid_instruction;
IrInstruction *op2 = instruction->op2->child;
ZigType *op2_type = op2->value.type;
if (type_is_invalid(op2_type))
return ira->codegen->invalid_instruction;
ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
if (!op1_val)
return ira->codegen->invalid_instruction;
ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad);
if (!op2_val)
return ira->codegen->invalid_instruction;
ConstExprValue *op1_array_val;
size_t op1_array_index;
size_t op1_array_end;
ZigType *child_type;
if (op1_type->id == ZigTypeIdArray) {
child_type = op1_type->data.array.child_type;
op1_array_val = op1_val;
op1_array_index = 0;
op1_array_end = op1_type->data.array.len;
} else if (op1_type->id == ZigTypeIdPointer &&
op1_type->data.pointer.child_type == ira->codegen->builtin_types.entry_u8 &&
op1_val->data.x_ptr.special == ConstPtrSpecialBaseArray &&
op1_val->data.x_ptr.data.base_array.is_cstr)
{
child_type = op1_type->data.pointer.child_type;
op1_array_val = op1_val->data.x_ptr.data.base_array.array_val;
op1_array_index = op1_val->data.x_ptr.data.base_array.elem_index;
op1_array_end = op1_array_val->type->data.array.len - 1;
} else if (is_slice(op1_type)) {
ZigType *ptr_type = op1_type->data.structure.fields[slice_ptr_index].type_entry;
child_type = ptr_type->data.pointer.child_type;
ConstExprValue *ptr_val = &op1_val->data.x_struct.fields[slice_ptr_index];
assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray);
op1_array_val = ptr_val->data.x_ptr.data.base_array.array_val;
op1_array_index = ptr_val->data.x_ptr.data.base_array.elem_index;
ConstExprValue *len_val = &op1_val->data.x_struct.fields[slice_len_index];
op1_array_end = op1_array_index + bigint_as_unsigned(&len_val->data.x_bigint);
} else {
ir_add_error(ira, op1,
buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op1->value.type->name)));
return ira->codegen->invalid_instruction;
}
ConstExprValue *op2_array_val;
size_t op2_array_index;
size_t op2_array_end;
bool op2_type_valid;
if (op2_type->id == ZigTypeIdArray) {
op2_type_valid = op2_type->data.array.child_type == child_type;
op2_array_val = op2_val;
op2_array_index = 0;
op2_array_end = op2_array_val->type->data.array.len;
} else if (op2_type->id == ZigTypeIdPointer &&
op2_type->data.pointer.child_type == ira->codegen->builtin_types.entry_u8 &&
op2_val->data.x_ptr.special == ConstPtrSpecialBaseArray &&
op2_val->data.x_ptr.data.base_array.is_cstr)
{
op2_type_valid = child_type == ira->codegen->builtin_types.entry_u8;
op2_array_val = op2_val->data.x_ptr.data.base_array.array_val;
op2_array_index = op2_val->data.x_ptr.data.base_array.elem_index;
op2_array_end = op2_array_val->type->data.array.len - 1;
} else if (is_slice(op2_type)) {
ZigType *ptr_type = op2_type->data.structure.fields[slice_ptr_index].type_entry;
op2_type_valid = ptr_type->data.pointer.child_type == child_type;
ConstExprValue *ptr_val = &op2_val->data.x_struct.fields[slice_ptr_index];
assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray);
op2_array_val = ptr_val->data.x_ptr.data.base_array.array_val;
op2_array_index = ptr_val->data.x_ptr.data.base_array.elem_index;
ConstExprValue *len_val = &op2_val->data.x_struct.fields[slice_len_index];
op2_array_end = op2_array_index + bigint_as_unsigned(&len_val->data.x_bigint);
} else {
ir_add_error(ira, op2,
buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op2->value.type->name)));
return ira->codegen->invalid_instruction;
}
if (!op2_type_valid) {
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->invalid_instruction;
}
// The type of result is populated in the following if blocks
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
ConstExprValue *out_val = &result->value;
ConstExprValue *out_array_val;
size_t new_len = (op1_array_end - op1_array_index) + (op2_array_end - op2_array_index);
if (op1_type->id == ZigTypeIdArray || op2_type->id == ZigTypeIdArray) {
result->value.type = get_array_type(ira->codegen, child_type, new_len);
out_array_val = out_val;
} else if (is_slice(op1_type) || is_slice(op2_type)) {
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, child_type,
true, false, PtrLenUnknown, 0, 0, 0, false);
result->value.type = get_slice_type(ira->codegen, ptr_type);
out_array_val = create_const_vals(1);
out_array_val->special = ConstValSpecialStatic;
out_array_val->type = get_array_type(ira->codegen, child_type, new_len);
out_val->data.x_struct.fields = create_const_vals(2);
out_val->data.x_struct.fields[slice_ptr_index].type = ptr_type;
out_val->data.x_struct.fields[slice_ptr_index].special = ConstValSpecialStatic;
out_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.special = ConstPtrSpecialBaseArray;
out_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.data.base_array.array_val = out_array_val;
out_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.data.base_array.elem_index = 0;
out_val->data.x_struct.fields[slice_len_index].type = ira->codegen->builtin_types.entry_usize;
out_val->data.x_struct.fields[slice_len_index].special = ConstValSpecialStatic;
bigint_init_unsigned(&out_val->data.x_struct.fields[slice_len_index].data.x_bigint, new_len);
} else {
new_len += 1; // null byte
// TODO make this `[*]null T` instead of `[*]T`
result->value.type = get_pointer_to_type_extra(ira->codegen, child_type, true, false, PtrLenUnknown, 0, 0, 0, false);
out_array_val = create_const_vals(1);
out_array_val->special = ConstValSpecialStatic;
out_array_val->type = get_array_type(ira->codegen, child_type, new_len);
out_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
out_val->data.x_ptr.data.base_array.is_cstr = true;
out_val->data.x_ptr.data.base_array.array_val = out_array_val;
out_val->data.x_ptr.data.base_array.elem_index = 0;
}
if (op1_array_val->data.x_array.special == ConstArraySpecialUndef &&
op2_array_val->data.x_array.special == ConstArraySpecialUndef) {
out_array_val->data.x_array.special = ConstArraySpecialUndef;
return result;
}
out_array_val->data.x_array.data.s_none.elements = create_const_vals(new_len);
// TODO handle the buf case here for an optimization
expand_undef_array(ira->codegen, op1_array_val);
expand_undef_array(ira->codegen, op2_array_val);
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.data.s_none.elements[next_index] = op1_array_val->data.x_array.data.s_none.elements[i];
}
for (size_t i = op2_array_index; i < op2_array_end; i += 1, next_index += 1) {
out_array_val->data.x_array.data.s_none.elements[next_index] = op2_array_val->data.x_array.data.s_none.elements[i];
}
if (next_index < new_len) {
ConstExprValue *null_byte = &out_array_val->data.x_array.data.s_none.elements[next_index];
init_const_unsigned_negative(null_byte, child_type, 0, false);
next_index += 1;
}
assert(next_index == new_len);
return result;
}
static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp *instruction) {
IrInstruction *op1 = instruction->op1->child;
if (type_is_invalid(op1->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *op2 = instruction->op2->child;
if (type_is_invalid(op2->value.type))
return ira->codegen->invalid_instruction;
ConstExprValue *array_val = ir_resolve_const(ira, op1, UndefBad);
if (!array_val)
return ira->codegen->invalid_instruction;
uint64_t mult_amt;
if (!ir_resolve_usize(ira, op2, &mult_amt))
return ira->codegen->invalid_instruction;
ZigType *array_type = op1->value.type;
if (array_type->id != ZigTypeIdArray) {
ir_add_error(ira, op1, buf_sprintf("expected array type, found '%s'", buf_ptr(&op1->value.type->name)));
return ira->codegen->invalid_instruction;
}
uint64_t old_array_len = array_type->data.array.len;
uint64_t new_array_len;
if (mul_u64_overflow(old_array_len, mult_amt, &new_array_len))
{
ir_add_error(ira, &instruction->base, buf_sprintf("operation results in overflow"));
return ira->codegen->invalid_instruction;
}
ZigType *child_type = array_type->data.array.child_type;
IrInstruction *result = ir_const(ira, &instruction->base,
get_array_type(ira->codegen, child_type, new_array_len));
ConstExprValue *out_val = &result->value;
if (array_val->data.x_array.special == ConstArraySpecialUndef) {
out_val->data.x_array.special = ConstArraySpecialUndef;
return result;
}
// TODO optimize the buf case
expand_undef_array(ira->codegen, array_val);
out_val->data.x_array.data.s_none.elements = create_const_vals(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.data.s_none.elements[i] = array_val->data.x_array.data.s_none.elements[y];
i += 1;
}
}
assert(i == new_array_len);
return result;
}
static IrInstruction *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstructionBinOp *instruction) {
ZigType *op1_type = ir_resolve_error_set_type(ira, &instruction->base, instruction->op1->child);
if (type_is_invalid(op1_type))
return ira->codegen->invalid_instruction;
ZigType *op2_type = ir_resolve_error_set_type(ira, &instruction->base, instruction->op2->child);
if (type_is_invalid(op2_type))
return ira->codegen->invalid_instruction;
if (type_is_global_error_set(op1_type) ||
type_is_global_error_set(op2_type))
{
return ir_const_type(ira, &instruction->base, ira->codegen->builtin_types.entry_global_error_set);
}
if (!resolve_inferred_error_set(ira->codegen, op1_type, instruction->op1->child->source_node)) {
return ira->codegen->invalid_instruction;
}
if (!resolve_inferred_error_set(ira->codegen, op2_type, instruction->op2->child->source_node)) {
return ira->codegen->invalid_instruction;
}
ErrorTableEntry **errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);
for (uint32_t i = 0, count = op1_type->data.error_set.err_count; i < count; i += 1) {
ErrorTableEntry *error_entry = op1_type->data.error_set.errors[i];
assert(errors[error_entry->value] == nullptr);
errors[error_entry->value] = error_entry;
}
ZigType *result_type = get_error_set_union(ira->codegen, errors, op1_type, op2_type);
free(errors);
return ir_const_type(ira, &instruction->base, result_type);
}
static IrInstruction *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 IrBinOpBitShiftLeftLossy:
case IrBinOpBitShiftLeftExact:
case IrBinOpBitShiftRightLossy:
case IrBinOpBitShiftRightExact:
return ir_analyze_bit_shift(ira, bin_op_instruction);
case IrBinOpBinOr:
case IrBinOpBinXor:
case IrBinOpBinAnd:
case IrBinOpAdd:
case IrBinOpAddWrap:
case IrBinOpSub:
case IrBinOpSubWrap:
case IrBinOpMult:
case IrBinOpMultWrap:
case IrBinOpDivUnspecified:
case IrBinOpDivTrunc:
case IrBinOpDivFloor:
case IrBinOpDivExact:
case IrBinOpRemUnspecified:
case IrBinOpRemRem:
case IrBinOpRemMod:
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);
case IrBinOpMergeErrorSets:
return ir_analyze_merge_error_sets(ira, bin_op_instruction);
}
zig_unreachable();
}
static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira,
IrInstructionDeclVarSrc *decl_var_instruction)
{
Error err;
ZigVar *var = decl_var_instruction->var;
IrInstruction *init_value = decl_var_instruction->init_value->child;
if (type_is_invalid(init_value->value.type)) {
var->var_type = ira->codegen->builtin_types.entry_invalid;
return ira->codegen->invalid_instruction;
}
ZigType *explicit_type = nullptr;
IrInstruction *var_type = nullptr;
if (decl_var_instruction->var_type != nullptr) {
var_type = decl_var_instruction->var_type->child;
ZigType *proposed_type = ir_resolve_type(ira, var_type);
explicit_type = validate_var_type(ira->codegen, var_type->source_node, proposed_type);
if (type_is_invalid(explicit_type)) {
var->var_type = ira->codegen->builtin_types.entry_invalid;
return ira->codegen->invalid_instruction;
}
}
AstNode *source_node = decl_var_instruction->base.source_node;
IrInstruction *casted_init_value = ir_implicit_cast(ira, init_value, explicit_type);
bool is_comptime_var = ir_get_var_is_comptime(var);
bool var_class_requires_const = false;
ZigType *result_type = casted_init_value->value.type;
if (type_is_invalid(result_type)) {
result_type = ira->codegen->builtin_types.entry_invalid;
} else if (result_type->id == ZigTypeIdUnreachable || result_type->id == ZigTypeIdOpaque) {
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;
}
switch (type_requires_comptime(ira->codegen, result_type)) {
case ReqCompTimeInvalid:
result_type = ira->codegen->builtin_types.entry_invalid;
break;
case ReqCompTimeYes:
var_class_requires_const = true;
if (!var->gen_is_const && !is_comptime_var) {
ir_add_error_node(ira, source_node,
buf_sprintf("variable of type '%s' must be const or comptime",
buf_ptr(&result_type->name)));
result_type = ira->codegen->builtin_types.entry_invalid;
}
break;
case ReqCompTimeNo:
if (casted_init_value->value.special == ConstValSpecialStatic &&
casted_init_value->value.type->id == ZigTypeIdFn &&
casted_init_value->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr &&
casted_init_value->value.data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways)
{
var_class_requires_const = true;
if (!var->src_is_const && !is_comptime_var) {
ErrorMsg *msg = ir_add_error_node(ira, source_node,
buf_sprintf("functions marked inline must be stored in const or comptime var"));
AstNode *proto_node = casted_init_value->value.data.x_ptr.data.fn.fn_entry->proto_node;
add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here"));
result_type = ira->codegen->builtin_types.entry_invalid;
}
}
break;
}
if (var->var_type != nullptr && !is_comptime_var) {
// This is at least the second time we've seen this variable declaration during analysis.
// This means that this is actually a different variable due to, e.g. an inline while loop.
// We make a new variable so that it can hold a different type, and so the debug info can
// be distinct.
ZigVar *new_var = create_local_var(ira->codegen, var->decl_node, var->child_scope,
&var->name, var->src_is_const, var->gen_is_const, var->shadowable, var->is_comptime, true);
new_var->owner_exec = var->owner_exec;
new_var->align_bytes = var->align_bytes;
if (var->mem_slot_index != SIZE_MAX) {
ConstExprValue *vals = create_const_vals(1);
new_var->mem_slot_index = ira->exec_context.mem_slot_list.length;
ira->exec_context.mem_slot_list.append(vals);
}
var->next_var = new_var;
var = new_var;
}
// This must be done after possibly creating a new variable above
var->ref_count = 0;
var->var_type = result_type;
assert(var->var_type);
if (type_is_invalid(result_type)) {
return ir_const_void(ira, &decl_var_instruction->base);
}
if (decl_var_instruction->align_value == nullptr) {
if ((err = type_resolve(ira->codegen, result_type, ResolveStatusAlignmentKnown))) {
var->var_type = ira->codegen->builtin_types.entry_invalid;
return ir_const_void(ira, &decl_var_instruction->base);
}
var->align_bytes = get_abi_alignment(ira->codegen, result_type);
} else {
if (!ir_resolve_align(ira, decl_var_instruction->align_value->child, &var->align_bytes)) {
var->var_type = ira->codegen->builtin_types.entry_invalid;
}
}
if (casted_init_value->value.special != ConstValSpecialRuntime) {
if (var->mem_slot_index != SIZE_MAX) {
assert(var->mem_slot_index < ira->exec_context.mem_slot_list.length);
ConstExprValue *mem_slot = ira->exec_context.mem_slot_list.at(var->mem_slot_index);
copy_const_val(mem_slot, &casted_init_value->value, !is_comptime_var || var->gen_is_const);
if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) {
return ir_const_void(ira, &decl_var_instruction->base);
}
}
} else if (is_comptime_var) {
ir_add_error(ira, &decl_var_instruction->base,
buf_sprintf("cannot store runtime value in compile time variable"));
var->var_type = ira->codegen->builtin_types.entry_invalid;
return ira->codegen->invalid_instruction;
}
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
if (fn_entry)
fn_entry->variable_list.append(var);
return ir_build_var_decl_gen(ira, &decl_var_instruction->base, var, casted_init_value);
}
static VarLinkage global_linkage_to_var_linkage(GlobalLinkageId id) {
switch (id) {
case GlobalLinkageIdStrong:
return VarLinkageExportStrong;
case GlobalLinkageIdWeak:
return VarLinkageExportWeak;
case GlobalLinkageIdLinkOnce:
return VarLinkageExportLinkOnce;
case GlobalLinkageIdInternal:
return VarLinkageInternal;
}
zig_unreachable();
}
static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructionExport *instruction) {
IrInstruction *name = instruction->name->child;
Buf *symbol_name = ir_resolve_str(ira, name);
if (symbol_name == nullptr) {
return ira->codegen->invalid_instruction;
}
IrInstruction *target = instruction->target->child;
if (type_is_invalid(target->value.type)) {
return ira->codegen->invalid_instruction;
}
GlobalLinkageId global_linkage_id = GlobalLinkageIdStrong;
if (instruction->linkage != nullptr) {
IrInstruction *linkage_value = instruction->linkage->child;
if (!ir_resolve_global_linkage(ira, linkage_value, &global_linkage_id)) {
return ira->codegen->invalid_instruction;
}
}
// TODO: This function needs to be audited.
// It's not clear how all the different types are supposed to be handled.
// Need comprehensive tests for exporting one thing in one file and declaring an extern var
// in another file.
TldFn *tld_fn = allocate<TldFn>(1);
tld_fn->base.id = TldIdFn;
tld_fn->base.source_node = instruction->base.source_node;
auto entry = ira->codegen->exported_symbol_names.put_unique(symbol_name, &tld_fn->base);
if (entry) {
AstNode *other_export_node = entry->value->source_node;
ErrorMsg *msg = ir_add_error(ira, &instruction->base,
buf_sprintf("exported symbol collision: '%s'", buf_ptr(symbol_name)));
add_error_note(ira->codegen, msg, other_export_node, buf_sprintf("other symbol is here"));
return ira->codegen->invalid_instruction;
}
bool want_var_export = false;
switch (target->value.type->id) {
case ZigTypeIdInvalid:
case ZigTypeIdUnreachable:
zig_unreachable();
case ZigTypeIdFn: {
assert(target->value.data.x_ptr.special == ConstPtrSpecialFunction);
ZigFn *fn_entry = target->value.data.x_ptr.data.fn.fn_entry;
tld_fn->fn_entry = fn_entry;
CallingConvention cc = fn_entry->type_entry->data.fn.fn_type_id.cc;
switch (cc) {
case CallingConventionUnspecified: {
ErrorMsg *msg = ir_add_error(ira, target,
buf_sprintf("exported function must specify calling convention"));
add_error_note(ira->codegen, msg, fn_entry->proto_node, buf_sprintf("declared here"));
} break;
case CallingConventionAsync: {
ErrorMsg *msg = ir_add_error(ira, target,
buf_sprintf("exported function cannot be async"));
add_error_note(ira->codegen, msg, fn_entry->proto_node, buf_sprintf("declared here"));
} break;
case CallingConventionC:
case CallingConventionNaked:
case CallingConventionCold:
case CallingConventionStdcall:
add_fn_export(ira->codegen, fn_entry, symbol_name, global_linkage_id, cc == CallingConventionC);
break;
}
} break;
case ZigTypeIdStruct:
if (is_slice(target->value.type)) {
ir_add_error(ira, target,
buf_sprintf("unable to export value of type '%s'", buf_ptr(&target->value.type->name)));
} else if (target->value.type->data.structure.layout != ContainerLayoutExtern) {
ErrorMsg *msg = ir_add_error(ira, target,
buf_sprintf("exported struct value must be declared extern"));
add_error_note(ira->codegen, msg, target->value.type->data.structure.decl_node, buf_sprintf("declared here"));
} else {
want_var_export = true;
}
break;
case ZigTypeIdUnion:
if (target->value.type->data.unionation.layout != ContainerLayoutExtern) {
ErrorMsg *msg = ir_add_error(ira, target,
buf_sprintf("exported union value must be declared extern"));
add_error_note(ira->codegen, msg, target->value.type->data.unionation.decl_node, buf_sprintf("declared here"));
} else {
want_var_export = true;
}
break;
case ZigTypeIdEnum:
if (target->value.type->data.enumeration.layout != ContainerLayoutExtern) {
ErrorMsg *msg = ir_add_error(ira, target,
buf_sprintf("exported enum value must be declared extern"));
add_error_note(ira->codegen, msg, target->value.type->data.enumeration.decl_node, buf_sprintf("declared here"));
} else {
want_var_export = true;
}
break;
case ZigTypeIdMetaType: {
ZigType *type_value = target->value.data.x_type;
switch (type_value->id) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdStruct:
if (is_slice(type_value)) {
ir_add_error(ira, target,
buf_sprintf("unable to export type '%s'", buf_ptr(&type_value->name)));
} else if (type_value->data.structure.layout != ContainerLayoutExtern) {
ErrorMsg *msg = ir_add_error(ira, target,
buf_sprintf("exported struct must be declared extern"));
add_error_note(ira->codegen, msg, type_value->data.structure.decl_node, buf_sprintf("declared here"));
}
break;
case ZigTypeIdUnion:
if (type_value->data.unionation.layout != ContainerLayoutExtern) {
ErrorMsg *msg = ir_add_error(ira, target,
buf_sprintf("exported union must be declared extern"));
add_error_note(ira->codegen, msg, type_value->data.unionation.decl_node, buf_sprintf("declared here"));
}
break;
case ZigTypeIdEnum:
if (type_value->data.enumeration.layout != ContainerLayoutExtern) {
ErrorMsg *msg = ir_add_error(ira, target,
buf_sprintf("exported enum must be declared extern"));
add_error_note(ira->codegen, msg, type_value->data.enumeration.decl_node, buf_sprintf("declared here"));
}
break;
case ZigTypeIdFn: {
if (type_value->data.fn.fn_type_id.cc == CallingConventionUnspecified) {
ir_add_error(ira, target,
buf_sprintf("exported function type must specify calling convention"));
}
} break;
case ZigTypeIdInt:
case ZigTypeIdFloat:
case ZigTypeIdPointer:
case ZigTypeIdArray:
case ZigTypeIdBool:
case ZigTypeIdVector:
break;
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdUnreachable:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
case ZigTypeIdPromise:
ir_add_error(ira, target,
buf_sprintf("invalid export target '%s'", buf_ptr(&type_value->name)));
break;
}
} break;
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdInt:
case ZigTypeIdFloat:
case ZigTypeIdPointer:
case ZigTypeIdArray:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdVector:
zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name));
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
case ZigTypeIdPromise:
case ZigTypeIdEnumLiteral:
ir_add_error(ira, target,
buf_sprintf("invalid export target type '%s'", buf_ptr(&target->value.type->name)));
break;
}
// TODO audit the various ways to use @export
if (want_var_export && target->id == IrInstructionIdLoadPtrGen) {
IrInstructionLoadPtrGen *load_ptr = reinterpret_cast<IrInstructionLoadPtrGen *>(target);
if (load_ptr->ptr->id == IrInstructionIdVarPtr) {
IrInstructionVarPtr *var_ptr = reinterpret_cast<IrInstructionVarPtr *>(load_ptr->ptr);
ZigVar *var = var_ptr->var;
var->linkage = global_linkage_to_var_linkage(global_linkage_id);
}
}
return ir_const_void(ira, &instruction->base);
}
static bool exec_has_err_ret_trace(CodeGen *g, IrExecutable *exec) {
ZigFn *fn_entry = exec_fn_entry(exec);
return fn_entry != nullptr && fn_entry->calls_or_awaits_errorable_fn && g->have_err_ret_tracing;
}
static IrInstruction *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,
IrInstructionErrorReturnTrace *instruction)
{
if (instruction->optional == IrInstructionErrorReturnTrace::Null) {
ZigType *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen);
ZigType *optional_type = get_optional_type(ira->codegen, ptr_to_stack_trace_type);
if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) {
IrInstruction *result = ir_const(ira, &instruction->base, optional_type);
ConstExprValue *out_val = &result->value;
assert(get_codegen_ptr_type(optional_type) != nullptr);
out_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
out_val->data.x_ptr.data.hard_coded_addr.addr = 0;
return result;
}
IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, instruction->optional);
new_instruction->value.type = optional_type;
return new_instruction;
} else {
assert(ira->codegen->have_err_ret_tracing);
IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, instruction->optional);
new_instruction->value.type = get_ptr_to_stack_trace_type(ira->codegen);
return new_instruction;
}
}
static IrInstruction *ir_analyze_instruction_error_union(IrAnalyze *ira,
IrInstructionErrorUnion *instruction)
{
Error err;
ZigType *err_set_type = ir_resolve_type(ira, instruction->err_set->child);
if (type_is_invalid(err_set_type))
return ira->codegen->invalid_instruction;
ZigType *payload_type = ir_resolve_type(ira, instruction->payload->child);
if (type_is_invalid(payload_type))
return ira->codegen->invalid_instruction;
if (err_set_type->id != ZigTypeIdErrorSet) {
ir_add_error(ira, instruction->err_set->child,
buf_sprintf("expected error set type, found type '%s'",
buf_ptr(&err_set_type->name)));
return ira->codegen->invalid_instruction;
}
if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown)))
return ira->codegen->invalid_instruction;
ZigType *result_type = get_error_union_type(ira->codegen, err_set_type, payload_type);
return ir_const_type(ira, &instruction->base, result_type);
}
IrInstruction *ir_get_implicit_allocator(IrAnalyze *ira, IrInstruction *source_instr, ImplicitAllocatorId id) {
ZigFn *parent_fn_entry = exec_fn_entry(ira->new_irb.exec);
if (parent_fn_entry == nullptr) {
ir_add_error(ira, source_instr, buf_sprintf("no implicit allocator available"));
return ira->codegen->invalid_instruction;
}
FnTypeId *parent_fn_type = &parent_fn_entry->type_entry->data.fn.fn_type_id;
if (parent_fn_type->cc != CallingConventionAsync) {
ir_add_error(ira, source_instr, buf_sprintf("async function call from non-async caller requires allocator parameter"));
return ira->codegen->invalid_instruction;
}
assert(parent_fn_type->async_allocator_type != nullptr);
switch (id) {
case ImplicitAllocatorIdArg:
{
IrInstruction *result = ir_build_get_implicit_allocator(&ira->new_irb, source_instr->scope,
source_instr->source_node, ImplicitAllocatorIdArg);
result->value.type = parent_fn_type->async_allocator_type;
return result;
}
case ImplicitAllocatorIdLocalVar:
{
ZigVar *coro_allocator_var = ira->old_irb.exec->coro_allocator_var;
assert(coro_allocator_var != nullptr);
IrInstruction *var_ptr_inst = ir_get_var_ptr(ira, source_instr, coro_allocator_var);
IrInstruction *result = ir_get_deref(ira, source_instr, var_ptr_inst);
assert(result->value.type != nullptr);
return result;
}
}
zig_unreachable();
}
static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type,
IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count, IrInstruction *async_allocator_inst)
{
Buf *realloc_field_name = buf_create_from_str(ASYNC_REALLOC_FIELD_NAME);
assert(async_allocator_inst->value.type->id == ZigTypeIdPointer);
ZigType *container_type = async_allocator_inst->value.type->data.pointer.child_type;
IrInstruction *field_ptr_inst = ir_analyze_container_field_ptr(ira, realloc_field_name, &call_instruction->base,
async_allocator_inst, container_type);
if (type_is_invalid(field_ptr_inst->value.type)) {
return ira->codegen->invalid_instruction;
}
ZigType *ptr_to_realloc_fn_type = field_ptr_inst->value.type;
assert(ptr_to_realloc_fn_type->id == ZigTypeIdPointer);
ZigType *realloc_fn_type = ptr_to_realloc_fn_type->data.pointer.child_type;
if (realloc_fn_type->id != ZigTypeIdFn) {
ir_add_error(ira, &call_instruction->base,
buf_sprintf("expected reallocation function, found '%s'", buf_ptr(&realloc_fn_type->name)));
return ira->codegen->invalid_instruction;
}
ZigType *realloc_fn_return_type = realloc_fn_type->data.fn.fn_type_id.return_type;
if (realloc_fn_return_type->id != ZigTypeIdErrorUnion) {
ir_add_error(ira, fn_ref,
buf_sprintf("expected allocation function to return error union, but it returns '%s'", buf_ptr(&realloc_fn_return_type->name)));
return ira->codegen->invalid_instruction;
}
ZigType *alloc_fn_error_set_type = realloc_fn_return_type->data.error_union.err_set_type;
ZigType *return_type = fn_type->data.fn.fn_type_id.return_type;
ZigType *promise_type = get_promise_type(ira->codegen, return_type);
ZigType *async_return_type = get_error_union_type(ira->codegen, alloc_fn_error_set_type, promise_type);
IrInstruction *result = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node,
fn_entry, fn_ref, arg_count, casted_args, false, FnInlineAuto, true, async_allocator_inst, nullptr);
result->value.type = async_return_type;
return result;
}
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);
IrInstruction *casted_arg;
if (param_decl_node->data.param_decl.var_token == nullptr) {
AstNode *param_type_node = param_decl_node->data.param_decl.type;
ZigType *param_type = ir_analyze_type_expr(ira, *exec_scope, param_type_node);
if (type_is_invalid(param_type))
return false;
casted_arg = ir_implicit_cast(ira, arg, param_type);
if (type_is_invalid(casted_arg->value.type))
return false;
} else {
casted_arg = arg;
}
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;
ZigVar *var = add_variable(ira->codegen, param_decl_node,
*exec_scope, param_name, true, arg_val, nullptr, arg_val->type);
*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,
ZigFn *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 {
if (param_decl_node->data.param_decl.var_token == nullptr) {
AstNode *param_type_node = param_decl_node->data.param_decl.type;
ZigType *param_type = ir_analyze_type_expr(ira, *child_scope, param_type_node);
if (type_is_invalid(param_type))
return false;
casted_arg = ir_implicit_cast(ira, arg, param_type);
if (type_is_invalid(casted_arg->value.type))
return false;
} else {
arg_part_of_generic_id = true;
casted_arg = arg;
}
}
bool comptime_arg = param_decl_node->data.param_decl.is_inline ||
casted_arg->value.type->id == ZigTypeIdComptimeInt || casted_arg->value.type->id == ZigTypeIdComptimeFloat;
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;
} 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 (!param_name) return false;
if (!is_var_args) {
ZigVar *var = add_variable(ira->codegen, param_decl_node,
*child_scope, param_name, true, arg_val, nullptr, arg_val->type);
*child_scope = var->child_scope;
var->shadowable = !comptime_arg;
*next_proto_i += 1;
} else if (casted_arg->value.type->id == ZigTypeIdComptimeInt ||
casted_arg->value.type->id == ZigTypeIdComptimeFloat)
{
ir_add_error(ira, casted_arg,
buf_sprintf("compiler bug: integer and float literals in var args function must be casted. https://github.com/ziglang/zig/issues/557"));
return false;
}
if (!comptime_arg) {
switch (type_requires_comptime(ira->codegen, casted_arg->value.type)) {
case ReqCompTimeYes:
ir_add_error(ira, casted_arg,
buf_sprintf("parameter of type '%s' requires comptime", buf_ptr(&casted_arg->value.type->name)));
return false;
case ReqCompTimeInvalid:
return false;
case ReqCompTimeNo:
break;
}
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 ZigVar *get_fn_var_by_index(ZigFn *fn_entry, size_t index) {
FnTypeParamInfo *src_param_info = &fn_entry->type_entry->data.fn.fn_type_id.param_info[index];
if (!type_has_bits(src_param_info->type))
return nullptr;
size_t next_var_i = 0;
for (size_t param_i = 0; param_i < index; param_i += 1) {
FnTypeParamInfo *src_param_info = &fn_entry->type_entry->data.fn.fn_type_id.param_info[param_i];
if (!type_has_bits(src_param_info->type)) {
continue;
}
next_var_i += 1;
}
return fn_entry->variable_list.at(next_var_i);
}
static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ZigVar *var) {
while (var->next_var != nullptr) {
var = var->next_var;
}
if (var->mem_slot_index != SIZE_MAX && var->owner_exec->analysis == nullptr) {
assert(ira->codegen->errors.length != 0);
return ira->codegen->invalid_instruction;
}
if (var->var_type == nullptr || type_is_invalid(var->var_type))
return ira->codegen->invalid_instruction;
ConstExprValue *mem_slot = nullptr;
bool comptime_var_mem = ir_get_var_is_comptime(var);
bool linkage_makes_it_runtime = var->linkage == VarLinkageExternal;
bool is_const = var->src_is_const;
bool is_volatile = false;
if (linkage_makes_it_runtime)
goto no_mem_slot;
if (var->const_value->special == ConstValSpecialStatic) {
mem_slot = var->const_value;
} else {
if (var->mem_slot_index != SIZE_MAX && (comptime_var_mem || var->gen_is_const)) {
// find the relevant exec_context
assert(var->owner_exec != nullptr);
assert(var->owner_exec->analysis != nullptr);
IrExecContext *exec_context = &var->owner_exec->analysis->exec_context;
assert(var->mem_slot_index < exec_context->mem_slot_list.length);
mem_slot = exec_context->mem_slot_list.at(var->mem_slot_index);
}
}
if (mem_slot != nullptr) {
switch (mem_slot->special) {
case ConstValSpecialRuntime:
goto no_mem_slot;
case ConstValSpecialStatic: // fallthrough
case ConstValSpecialUndef: {
ConstPtrMut ptr_mut;
if (comptime_var_mem) {
ptr_mut = ConstPtrMutComptimeVar;
} else if (var->gen_is_const) {
ptr_mut = ConstPtrMutComptimeConst;
} else {
assert(!comptime_var_mem);
ptr_mut = ConstPtrMutRuntimeVar;
}
return ir_get_const_ptr(ira, instruction, mem_slot, var->var_type,
ptr_mut, is_const, is_volatile, var->align_bytes);
}
}
zig_unreachable();
}
no_mem_slot:
IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb,
instruction->scope, instruction->source_node, var);
var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->var_type,
var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0, false);
bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr);
var_ptr_instruction->value.data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack;
return var_ptr_instruction;
}
static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *ptr, IrInstruction *uncasted_value)
{
assert(ptr->value.type->id == ZigTypeIdPointer);
if (ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) {
if (uncasted_value->value.type->id == ZigTypeIdErrorUnion ||
uncasted_value->value.type->id == ZigTypeIdErrorSet)
{
ir_add_error(ira, source_instr, buf_sprintf("error is discarded"));
return ira->codegen->invalid_instruction;
}
return ir_const_void(ira, source_instr);
}
ZigType *child_type = ptr->value.type->data.pointer.child_type;
if (ptr->value.type->data.pointer.is_const && !source_instr->is_gen) {
ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant"));
return ira->codegen->invalid_instruction;
}
IrInstruction *value = ir_implicit_cast(ira, uncasted_value, child_type);
if (value == ira->codegen->invalid_instruction)
return ira->codegen->invalid_instruction;
switch (type_has_one_possible_value(ira->codegen, child_type)) {
case OnePossibleValueInvalid:
return ira->codegen->invalid_instruction;
case OnePossibleValueYes:
return ir_const_void(ira, source_instr);
case OnePossibleValueNo:
break;
}
if (instr_is_comptime(ptr) && ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) {
if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst) {
ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant"));
return ira->codegen->invalid_instruction;
}
if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) {
if (instr_is_comptime(value)) {
ConstExprValue *dest_val = const_ptr_pointee(ira, ira->codegen, &ptr->value, source_instr->source_node);
if (dest_val == nullptr)
return ira->codegen->invalid_instruction;
if (dest_val->special != ConstValSpecialRuntime) {
// TODO this allows a value stored to have the original value modified and then
// have that affect what should be a copy. We need some kind of advanced copy-on-write
// system to make these two tests pass at the same time:
// * "string literal used as comptime slice is memoized"
// * "comptime modification of const struct field" - except modified to avoid
// ConstPtrMutComptimeVar, thus defeating the logic below.
bool same_global_refs = ptr->value.data.x_ptr.mut != ConstPtrMutComptimeVar;
copy_const_val(dest_val, &value->value, same_global_refs);
if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr) {
ira->new_irb.current_basic_block->must_be_comptime_source_instr = source_instr;
}
return ir_const_void(ira, source_instr);
}
}
ir_add_error(ira, source_instr,
buf_sprintf("cannot store runtime value in compile time variable"));
ConstExprValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, &ptr->value);
dest_val->type = ira->codegen->builtin_types.entry_invalid;
return ira->codegen->invalid_instruction;
}
}
switch (type_requires_comptime(ira->codegen, child_type)) {
case ReqCompTimeInvalid:
return ira->codegen->invalid_instruction;
case ReqCompTimeYes:
switch (type_has_one_possible_value(ira->codegen, ptr->value.type)) {
case OnePossibleValueInvalid:
return ira->codegen->invalid_instruction;
case OnePossibleValueNo:
ir_add_error(ira, source_instr,
buf_sprintf("cannot store runtime value in type '%s'", buf_ptr(&child_type->name)));
return ira->codegen->invalid_instruction;
case OnePossibleValueYes:
return ir_const_void(ira, source_instr);
}
zig_unreachable();
case ReqCompTimeNo:
break;
}
IrInstruction *result = ir_build_store_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node,
ptr, value);
result->value.type = ira->codegen->builtin_types.entry_void;
return result;
}
static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction,
ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref,
IrInstruction *first_arg_ptr, bool comptime_fn_call, FnInline fn_inline)
{
Error err;
FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
size_t first_arg_1_or_0 = first_arg_ptr ? 1 : 0;
// for extern functions, the var args argument is not counted.
// for zig functions, it is.
size_t var_args_1_or_0;
if (fn_type_id->cc == CallingConventionC) {
var_args_1_or_0 = 0;
} else {
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;
for (size_t i = 0; i < call_instruction->arg_count; i += 1) {
ConstExprValue *arg_tuple_value = &call_instruction->args[i]->child->value;
if (arg_tuple_value->type->id == ZigTypeIdArgTuple) {
call_param_count -= 1;
call_param_count += arg_tuple_value->data.x_arg_tuple.end_index -
arg_tuple_value->data.x_arg_tuple.start_index;
}
}
AstNode *source_node = call_instruction->base.source_node;
AstNode *fn_proto_node = fn_entry ? fn_entry->proto_node : nullptr;;
if (fn_type_id->cc == CallingConventionNaked) {
ErrorMsg *msg = ir_add_error(ira, fn_ref, buf_sprintf("unable to call function with naked calling convention"));
if (fn_proto_node) {
add_error_note(ira->codegen, msg, fn_proto_node, buf_sprintf("declared here"));
}
return ira->codegen->invalid_instruction;
}
if (fn_type_id->cc == CallingConventionAsync && !call_instruction->is_async) {
ErrorMsg *msg = ir_add_error(ira, fn_ref, buf_sprintf("must use async keyword to call async function"));
if (fn_proto_node) {
add_error_note(ira->codegen, msg, fn_proto_node, buf_sprintf("declared here"));
}
return ira->codegen->invalid_instruction;
}
if (fn_type_id->cc != CallingConventionAsync && call_instruction->is_async) {
ErrorMsg *msg = ir_add_error(ira, fn_ref, buf_sprintf("cannot use async keyword to call non-async function"));
if (fn_proto_node) {
add_error_note(ira->codegen, msg, fn_proto_node, buf_sprintf("declared here"));
}
return ira->codegen->invalid_instruction;
}
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 %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize "", 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->invalid_instruction;
}
} else if (src_param_count != call_param_count) {
ErrorMsg *msg = ir_add_error_node(ira, source_node,
buf_sprintf("expected %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize "", 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->invalid_instruction;
}
if (comptime_fn_call) {
// No special handling is needed for compile time evaluation of generic functions.
if (!fn_entry || fn_entry->body_node == nullptr) {
ir_add_error(ira, fn_ref, buf_sprintf("unable to evaluate constant expression"));
return ira->codegen->invalid_instruction;
}
if (!ir_emit_backward_branch(ira, &call_instruction->base))
return ira->codegen->invalid_instruction;
// 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) {
assert(first_arg_ptr->value.type->id == ZigTypeIdPointer);
bool first_arg_known_bare = false;
if (fn_type_id->next_param_index >= 1) {
ZigType *param_type = fn_type_id->param_info[next_proto_i].type;
if (type_is_invalid(param_type))
return ira->codegen->invalid_instruction;
first_arg_known_bare = param_type->id != ZigTypeIdPointer;
}
IrInstruction *first_arg;
if (!first_arg_known_bare && 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 (type_is_invalid(first_arg->value.type))
return ira->codegen->invalid_instruction;
}
if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, first_arg, &exec_scope, &next_proto_i))
return ira->codegen->invalid_instruction;
}
if (fn_proto_node->data.fn_proto.is_var_args) {
ir_add_error(ira, &call_instruction->base,
buf_sprintf("compiler bug: unable to call var args function at compile time. https://github.com/ziglang/zig/issues/313"));
return ira->codegen->invalid_instruction;
}
for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) {
IrInstruction *old_arg = call_instruction->args[call_i]->child;
if (type_is_invalid(old_arg->value.type))
return ira->codegen->invalid_instruction;
if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, old_arg, &exec_scope, &next_proto_i))
return ira->codegen->invalid_instruction;
}
AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
ZigType *specified_return_type = ir_analyze_type_expr(ira, exec_scope, return_type_node);
if (type_is_invalid(specified_return_type))
return ira->codegen->invalid_instruction;
ZigType *return_type;
ZigType *inferred_err_set_type = nullptr;
if (fn_proto_node->data.fn_proto.auto_err_set) {
inferred_err_set_type = get_auto_err_set_type(ira->codegen, fn_entry);
if ((err = type_resolve(ira->codegen, specified_return_type, ResolveStatusSizeKnown)))
return ira->codegen->invalid_instruction;
return_type = get_error_union_type(ira->codegen, inferred_err_set_type, specified_return_type);
} else {
return_type = specified_return_type;
}
bool cacheable = fn_eval_cacheable(exec_scope, return_type);
ConstExprValue *result = nullptr;
if (cacheable) {
auto entry = ira->codegen->memoized_fn_eval_table.maybe_get(exec_scope);
if (entry)
result = entry->value;
}
if (result == nullptr) {
// Analyze the fn body block like any other constant expression.
AstNode *body_node = fn_entry->body_node;
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, return_type_node);
if (inferred_err_set_type != nullptr) {
inferred_err_set_type->data.error_set.infer_fn = nullptr;
if (result->type->id == ZigTypeIdErrorUnion) {
ErrorTableEntry *err = result->data.x_err_union.error_set->data.x_err_set;
if (err != nullptr) {
inferred_err_set_type->data.error_set.err_count = 1;
inferred_err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(1);
inferred_err_set_type->data.error_set.errors[0] = err;
}
ZigType *fn_inferred_err_set_type = result->type->data.error_union.err_set_type;
inferred_err_set_type->data.error_set.err_count = fn_inferred_err_set_type->data.error_set.err_count;
inferred_err_set_type->data.error_set.errors = fn_inferred_err_set_type->data.error_set.errors;
} else if (result->type->id == ZigTypeIdErrorSet) {
inferred_err_set_type->data.error_set.err_count = result->type->data.error_set.err_count;
inferred_err_set_type->data.error_set.errors = result->type->data.error_set.errors;
}
}
if (cacheable) {
ira->codegen->memoized_fn_eval_table.put(exec_scope, result);
}
if (type_is_invalid(result->type))
return ira->codegen->invalid_instruction;
}
IrInstruction *new_instruction = ir_const(ira, &call_instruction->base, result->type);
copy_const_val(&new_instruction->value, result, true);
new_instruction->value.type = return_type;
return ir_finish_anal(ira, new_instruction);
}
IrInstruction *casted_new_stack = nullptr;
if (call_instruction->new_stack != nullptr) {
ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
false, false, PtrLenUnknown, 0, 0, 0, false);
ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
IrInstruction *new_stack = call_instruction->new_stack->child;
if (type_is_invalid(new_stack->value.type))
return ira->codegen->invalid_instruction;
casted_new_stack = ir_implicit_cast(ira, new_stack, u8_slice);
if (type_is_invalid(casted_new_stack->value.type))
return ira->codegen->invalid_instruction;
}
if (fn_type->data.fn.is_generic) {
if (!fn_entry) {
ir_add_error(ira, call_instruction->fn_ref,
buf_sprintf("calling a generic function requires compile-time known function value"));
return ira->codegen->invalid_instruction;
}
// Count the arguments of the function type id we are creating
size_t new_fn_arg_count = first_arg_1_or_0;
for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) {
IrInstruction *arg = call_instruction->args[call_i]->child;
if (type_is_invalid(arg->value.type))
return ira->codegen->invalid_instruction;
if (arg->value.type->id == ZigTypeIdArgTuple) {
new_fn_arg_count += arg->value.data.x_arg_tuple.end_index - arg->value.data.x_arg_tuple.start_index;
} else {
new_fn_arg_count += 1;
}
}
IrInstruction **casted_args = allocate<IrInstruction *>(new_fn_arg_count);
// Fork a scope of the function with known values for the parameters.
Scope *parent_scope = fn_entry->fndef_scope->base.parent;
ZigFn *impl_fn = create_fn(ira->codegen, fn_proto_node);
impl_fn->param_source_nodes = allocate<AstNode *>(new_fn_arg_count);
buf_init_from_buf(&impl_fn->symbol_name, &fn_entry->symbol_name);
impl_fn->fndef_scope = create_fndef_scope(ira->codegen, impl_fn->body_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, new_fn_arg_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 = create_const_vals(new_fn_arg_count);
size_t next_proto_i = 0;
if (first_arg_ptr) {
assert(first_arg_ptr->value.type->id == ZigTypeIdPointer);
bool first_arg_known_bare = false;
if (fn_type_id->next_param_index >= 1) {
ZigType *param_type = fn_type_id->param_info[next_proto_i].type;
if (type_is_invalid(param_type))
return ira->codegen->invalid_instruction;
first_arg_known_bare = param_type->id != ZigTypeIdPointer;
}
IrInstruction *first_arg;
if (!first_arg_known_bare && 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 (type_is_invalid(first_arg->value.type))
return ira->codegen->invalid_instruction;
}
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->invalid_instruction;
}
}
bool found_first_var_arg = false;
size_t first_var_arg;
ZigFn *parent_fn_entry = exec_fn_entry(ira->new_irb.exec);
assert(parent_fn_entry);
for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) {
IrInstruction *arg = call_instruction->args[call_i]->child;
if (type_is_invalid(arg->value.type))
return ira->codegen->invalid_instruction;
if (arg->value.type->id == ZigTypeIdArgTuple) {
for (size_t arg_tuple_i = arg->value.data.x_arg_tuple.start_index;
arg_tuple_i < arg->value.data.x_arg_tuple.end_index; arg_tuple_i += 1)
{
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;
}
ZigVar *arg_var = get_fn_var_by_index(parent_fn_entry, arg_tuple_i);
if (arg_var == nullptr) {
ir_add_error(ira, arg,
buf_sprintf("compiler bug: var args can't handle void. https://github.com/ziglang/zig/issues/557"));
return ira->codegen->invalid_instruction;
}
IrInstruction *arg_var_ptr_inst = ir_get_var_ptr(ira, arg, arg_var);
if (type_is_invalid(arg_var_ptr_inst->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *arg_tuple_arg = ir_get_deref(ira, arg, arg_var_ptr_inst);
if (type_is_invalid(arg_tuple_arg->value.type))
return ira->codegen->invalid_instruction;
if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg_tuple_arg, &impl_fn->child_scope,
&next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn))
{
return ira->codegen->invalid_instruction;
}
}
} else {
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->invalid_instruction;
}
}
}
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;
if (!found_first_var_arg) {
first_var_arg = inst_fn_type_id.param_count;
}
ConstExprValue *var_args_val = create_const_arg_tuple(ira->codegen,
first_var_arg, inst_fn_type_id.param_count);
ZigVar *var = add_variable(ira->codegen, param_decl_node,
impl_fn->child_scope, param_name, true, var_args_val, nullptr, var_args_val->type);
impl_fn->child_scope = var->child_scope;
}
if (fn_proto_node->data.fn_proto.align_expr != nullptr) {
ConstExprValue *align_result = ir_eval_const_value(ira->codegen, impl_fn->child_scope,
fn_proto_node->data.fn_proto.align_expr, get_align_amt_type(ira->codegen),
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota,
nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec, nullptr);
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr);
const_instruction->base.value = *align_result;
uint32_t align_bytes = 0;
ir_resolve_align(ira, &const_instruction->base, &align_bytes);
impl_fn->align_bytes = align_bytes;
inst_fn_type_id.alignment = align_bytes;
}
if (fn_proto_node->data.fn_proto.return_var_token == nullptr) {
AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
ZigType *specified_return_type = ir_analyze_type_expr(ira, impl_fn->child_scope, return_type_node);
if (type_is_invalid(specified_return_type))
return ira->codegen->invalid_instruction;
if (fn_proto_node->data.fn_proto.auto_err_set) {
ZigType *inferred_err_set_type = get_auto_err_set_type(ira->codegen, impl_fn);
if ((err = type_resolve(ira->codegen, specified_return_type, ResolveStatusSizeKnown)))
return ira->codegen->invalid_instruction;
inst_fn_type_id.return_type = get_error_union_type(ira->codegen, inferred_err_set_type, specified_return_type);
} else {
inst_fn_type_id.return_type = specified_return_type;
}
switch (type_requires_comptime(ira->codegen, specified_return_type)) {
case ReqCompTimeYes:
// Throw out our work and call the function as if it were comptime.
return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr,
true, FnInlineAuto);
case ReqCompTimeInvalid:
return ira->codegen->invalid_instruction;
case ReqCompTimeNo:
break;
}
}
IrInstruction *async_allocator_inst = nullptr;
if (call_instruction->is_async) {
AstNode *async_allocator_type_node = fn_proto_node->data.fn_proto.async_allocator_type;
if (async_allocator_type_node != nullptr) {
ZigType *async_allocator_type = ir_analyze_type_expr(ira, impl_fn->child_scope, async_allocator_type_node);
if (type_is_invalid(async_allocator_type))
return ira->codegen->invalid_instruction;
inst_fn_type_id.async_allocator_type = async_allocator_type;
}
IrInstruction *uncasted_async_allocator_inst;
if (call_instruction->async_allocator == nullptr) {
uncasted_async_allocator_inst = ir_get_implicit_allocator(ira, &call_instruction->base,
ImplicitAllocatorIdLocalVar);
if (type_is_invalid(uncasted_async_allocator_inst->value.type))
return ira->codegen->invalid_instruction;
} else {
uncasted_async_allocator_inst = call_instruction->async_allocator->child;
if (type_is_invalid(uncasted_async_allocator_inst->value.type))
return ira->codegen->invalid_instruction;
}
if (inst_fn_type_id.async_allocator_type == nullptr) {
inst_fn_type_id.async_allocator_type = uncasted_async_allocator_inst->value.type;
}
async_allocator_inst = ir_implicit_cast(ira, uncasted_async_allocator_inst, inst_fn_type_id.async_allocator_type);
if (type_is_invalid(async_allocator_inst->value.type))
return ira->codegen->invalid_instruction;
}
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 (type_is_invalid(impl_fn->type_entry))
return ira->codegen->invalid_instruction;
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;
impl_fn->analyzed_executable.backward_branch_quota = ira->new_irb.exec->backward_branch_quota;
impl_fn->analyzed_executable.is_generic_instantiation = true;
ira->codegen->fn_defs.append(impl_fn);
}
FnTypeId *impl_fn_type_id = &impl_fn->type_entry->data.fn.fn_type_id;
if (fn_type_can_fail(impl_fn_type_id)) {
parent_fn_entry->calls_or_awaits_errorable_fn = true;
}
size_t impl_param_count = impl_fn_type_id->param_count;
if (call_instruction->is_async) {
IrInstruction *result = ir_analyze_async_call(ira, call_instruction, impl_fn, impl_fn->type_entry,
fn_ref, casted_args, impl_param_count, async_allocator_inst);
ir_add_alloca(ira, result, result->value.type);
return ir_finish_anal(ira, result);
}
assert(async_allocator_inst == nullptr);
IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb,
call_instruction->base.scope, call_instruction->base.source_node,
impl_fn, nullptr, impl_param_count, casted_args, false, fn_inline,
call_instruction->is_async, nullptr, casted_new_stack);
new_call_instruction->value.type = impl_fn_type_id->return_type;
ir_add_alloca(ira, new_call_instruction, impl_fn_type_id->return_type);
return ir_finish_anal(ira, new_call_instruction);
}
ZigFn *parent_fn_entry = exec_fn_entry(ira->new_irb.exec);
assert(fn_type_id->return_type != nullptr);
assert(parent_fn_entry != nullptr);
if (fn_type_can_fail(fn_type_id)) {
parent_fn_entry->calls_or_awaits_errorable_fn = true;
}
IrInstruction **casted_args = allocate<IrInstruction *>(call_param_count);
size_t next_arg_index = 0;
if (first_arg_ptr) {
assert(first_arg_ptr->value.type->id == ZigTypeIdPointer);
ZigType *param_type = fn_type_id->param_info[next_arg_index].type;
if (type_is_invalid(param_type))
return ira->codegen->invalid_instruction;
IrInstruction *first_arg;
if (param_type->id == ZigTypeIdPointer &&
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 (type_is_invalid(first_arg->value.type))
return ira->codegen->invalid_instruction;
}
IrInstruction *casted_arg = ir_implicit_cast(ira, first_arg, param_type);
if (type_is_invalid(casted_arg->value.type))
return ira->codegen->invalid_instruction;
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]->child;
if (type_is_invalid(old_arg->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_arg;
if (next_arg_index < src_param_count) {
ZigType *param_type = fn_type_id->param_info[next_arg_index].type;
if (type_is_invalid(param_type))
return ira->codegen->invalid_instruction;
casted_arg = ir_implicit_cast(ira, old_arg, param_type);
if (type_is_invalid(casted_arg->value.type))
return ira->codegen->invalid_instruction;
} else {
casted_arg = old_arg;
}
casted_args[next_arg_index] = casted_arg;
next_arg_index += 1;
}
assert(next_arg_index == call_param_count);
ZigType *return_type = fn_type_id->return_type;
if (type_is_invalid(return_type))
return ira->codegen->invalid_instruction;
if (call_instruction->is_async) {
IrInstruction *uncasted_async_allocator_inst;
if (call_instruction->async_allocator == nullptr) {
uncasted_async_allocator_inst = ir_get_implicit_allocator(ira, &call_instruction->base,
ImplicitAllocatorIdLocalVar);
if (type_is_invalid(uncasted_async_allocator_inst->value.type))
return ira->codegen->invalid_instruction;
} else {
uncasted_async_allocator_inst = call_instruction->async_allocator->child;
if (type_is_invalid(uncasted_async_allocator_inst->value.type))
return ira->codegen->invalid_instruction;
}
IrInstruction *async_allocator_inst = ir_implicit_cast(ira, uncasted_async_allocator_inst, fn_type_id->async_allocator_type);
if (type_is_invalid(async_allocator_inst->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_analyze_async_call(ira, call_instruction, fn_entry, fn_type, fn_ref,
casted_args, call_param_count, async_allocator_inst);
ir_add_alloca(ira, result, result->value.type);
return ir_finish_anal(ira, result);
}
if (fn_entry != nullptr && fn_entry->fn_inline == FnInlineAlways && fn_inline == FnInlineNever) {
ir_add_error(ira, &call_instruction->base,
buf_sprintf("no-inline call of inline function"));
return ira->codegen->invalid_instruction;
}
IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb,
call_instruction->base.scope, call_instruction->base.source_node,
fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr, casted_new_stack);
new_call_instruction->value.type = return_type;
ir_add_alloca(ira, new_call_instruction, return_type);
return ir_finish_anal(ira, new_call_instruction);
}
static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionCall *call_instruction) {
IrInstruction *fn_ref = call_instruction->fn_ref->child;
if (type_is_invalid(fn_ref->value.type))
return ira->codegen->invalid_instruction;
bool is_comptime = call_instruction->is_comptime ||
ir_should_inline(ira->new_irb.exec, call_instruction->base.scope);
if (is_comptime || instr_is_comptime(fn_ref)) {
if (fn_ref->value.type->id == ZigTypeIdMetaType) {
ZigType *dest_type = ir_resolve_type(ira, fn_ref);
if (type_is_invalid(dest_type))
return ira->codegen->invalid_instruction;
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->invalid_instruction;
}
IrInstruction *arg = call_instruction->args[0]->child;
IrInstruction *cast_instruction = ir_analyze_cast(ira, &call_instruction->base, dest_type, arg);
if (type_is_invalid(cast_instruction->value.type))
return ira->codegen->invalid_instruction;
return ir_finish_anal(ira, cast_instruction);
} else if (fn_ref->value.type->id == ZigTypeIdFn) {
ZigFn *fn_table_entry = ir_resolve_fn(ira, fn_ref);
if (fn_table_entry == nullptr)
return ira->codegen->invalid_instruction;
return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,
fn_ref, nullptr, is_comptime, call_instruction->fn_inline);
} else if (fn_ref->value.type->id == ZigTypeIdBoundFn) {
assert(fn_ref->value.special == ConstValSpecialStatic);
ZigFn *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,
fn_ref, first_arg_ptr, is_comptime, call_instruction->fn_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->invalid_instruction;
}
}
if (fn_ref->value.type->id == ZigTypeIdFn) {
return ir_analyze_fn_call(ira, call_instruction, nullptr, fn_ref->value.type,
fn_ref, nullptr, false, FnInlineAuto);
} 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->invalid_instruction;
}
}
// out_val->type must be the type to read the pointer as
// if the type is different than the actual type then it does a comptime byte reinterpretation
static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node,
ConstExprValue *out_val, ConstExprValue *ptr_val)
{
Error err;
assert(out_val->type != nullptr);
ConstExprValue *pointee = const_ptr_pointee_unchecked(codegen, ptr_val);
if ((err = type_resolve(codegen, pointee->type, ResolveStatusSizeKnown)))
return ErrorSemanticAnalyzeFail;
if ((err = type_resolve(codegen, out_val->type, ResolveStatusSizeKnown)))
return ErrorSemanticAnalyzeFail;
size_t src_size = type_size(codegen, pointee->type);
size_t dst_size = type_size(codegen, out_val->type);
if (dst_size <= src_size) {
if (src_size == dst_size && types_have_same_zig_comptime_repr(pointee->type, out_val->type)) {
copy_const_val(out_val, pointee, ptr_val->data.x_ptr.mut == ConstPtrMutComptimeConst);
return ErrorNone;
}
Buf buf = BUF_INIT;
buf_resize(&buf, src_size);
buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf), pointee);
if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val)))
return err;
return ErrorNone;
}
switch (ptr_val->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
zig_unreachable();
case ConstPtrSpecialNull:
if (dst_size == 0)
return ErrorNone;
opt_ir_add_error_node(ira, codegen, source_node,
buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from null pointer",
dst_size));
return ErrorSemanticAnalyzeFail;
case ConstPtrSpecialRef: {
opt_ir_add_error_node(ira, codegen, source_node,
buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from pointer to %s which is %" ZIG_PRI_usize " bytes",
dst_size, buf_ptr(&pointee->type->name), src_size));
return ErrorSemanticAnalyzeFail;
}
case ConstPtrSpecialBaseArray: {
ConstExprValue *array_val = ptr_val->data.x_ptr.data.base_array.array_val;
assert(array_val->type->id == ZigTypeIdArray);
if (array_val->data.x_array.special != ConstArraySpecialNone)
zig_panic("TODO");
size_t elem_size = src_size;
size_t elem_index = ptr_val->data.x_ptr.data.base_array.elem_index;
src_size = elem_size * (array_val->type->data.array.len - elem_index);
if (dst_size > src_size) {
opt_ir_add_error_node(ira, codegen, source_node,
buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from %s at index %" ZIG_PRI_usize " which is %" ZIG_PRI_usize " bytes",
dst_size, buf_ptr(&array_val->type->name), elem_index, src_size));
return ErrorSemanticAnalyzeFail;
}
size_t elem_count = (dst_size % elem_size == 0) ? (dst_size / elem_size) : (dst_size / elem_size + 1);
Buf buf = BUF_INIT;
buf_resize(&buf, elem_count * elem_size);
for (size_t i = 0; i < elem_count; i += 1) {
ConstExprValue *elem_val = &array_val->data.x_array.data.s_none.elements[elem_index + i];
buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf) + (i * elem_size), elem_val);
}
if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val)))
return err;
return ErrorNone;
}
case ConstPtrSpecialBaseStruct:
case ConstPtrSpecialBaseErrorUnionCode:
case ConstPtrSpecialBaseErrorUnionPayload:
case ConstPtrSpecialBaseOptionalPayload:
case ConstPtrSpecialDiscard:
case ConstPtrSpecialHardCodedAddr:
case ConstPtrSpecialFunction:
zig_panic("TODO");
}
zig_unreachable();
}
static IrInstruction *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
Error err;
IrInstruction *value = un_op_instruction->value->child;
ZigType *type_entry = ir_resolve_type(ira, value);
if (type_is_invalid(type_entry))
return ira->codegen->invalid_instruction;
if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown)))
return ira->codegen->invalid_instruction;
switch (type_entry->id) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdInt:
case ZigTypeIdVector:
case ZigTypeIdFloat:
case ZigTypeIdPointer:
case ZigTypeIdArray:
case ZigTypeIdStruct:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdPromise:
return ir_const_type(ira, &un_op_instruction->base, get_optional_type(ira->codegen, type_entry));
case ZigTypeIdUnreachable:
case ZigTypeIdOpaque:
ir_add_error_node(ira, un_op_instruction->base.source_node,
buf_sprintf("type '%s' not optional", buf_ptr(&type_entry->name)));
return ira->codegen->invalid_instruction;
}
zig_unreachable();
}
static ErrorMsg *ir_eval_negation_scalar(IrAnalyze *ira, IrInstruction *source_instr, ZigType *scalar_type,
ConstExprValue *operand_val, ConstExprValue *scalar_out_val, bool is_wrap_op)
{
bool is_float = (scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat);
bool ok_type = ((scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) ||
scalar_type->id == ZigTypeIdComptimeInt || (is_float && !is_wrap_op));
if (!ok_type) {
const char *fmt = is_wrap_op ? "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'";
return ir_add_error(ira, source_instr, buf_sprintf(fmt, buf_ptr(&scalar_type->name)));
}
if (is_float) {
float_negate(scalar_out_val, operand_val);
} else if (is_wrap_op) {
bigint_negate_wrap(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint,
scalar_type->data.integral.bit_count);
} else {
bigint_negate(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint);
}
scalar_out_val->type = scalar_type;
scalar_out_val->special = ConstValSpecialStatic;
if (is_wrap_op || is_float || scalar_type->id == ZigTypeIdComptimeInt) {
return nullptr;
}
if (!bigint_fits_in_bits(&scalar_out_val->data.x_bigint, scalar_type->data.integral.bit_count, true)) {
return ir_add_error(ira, source_instr, buf_sprintf("negation caused overflow"));
}
return nullptr;
}
static IrInstruction *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *instruction) {
IrInstruction *value = instruction->value->child;
ZigType *expr_type = value->value.type;
if (type_is_invalid(expr_type))
return ira->codegen->invalid_instruction;
bool is_wrap_op = (instruction->op_id == IrUnOpNegationWrap);
ZigType *scalar_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type;
if (instr_is_comptime(value)) {
ConstExprValue *operand_val = ir_resolve_const(ira, value, UndefBad);
if (!operand_val)
return ira->codegen->invalid_instruction;
IrInstruction *result_instruction = ir_const(ira, &instruction->base, expr_type);
ConstExprValue *out_val = &result_instruction->value;
if (expr_type->id == ZigTypeIdVector) {
expand_undef_array(ira->codegen, operand_val);
out_val->special = ConstValSpecialUndef;
expand_undef_array(ira->codegen, out_val);
size_t len = expr_type->data.vector.len;
for (size_t i = 0; i < len; i += 1) {
ConstExprValue *scalar_operand_val = &operand_val->data.x_array.data.s_none.elements[i];
ConstExprValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i];
assert(scalar_operand_val->type == scalar_type);
assert(scalar_out_val->type == scalar_type);
ErrorMsg *msg = ir_eval_negation_scalar(ira, &instruction->base, scalar_type,
scalar_operand_val, scalar_out_val, is_wrap_op);
if (msg != nullptr) {
add_error_note(ira->codegen, msg, instruction->base.source_node,
buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i));
return ira->codegen->invalid_instruction;
}
}
out_val->type = expr_type;
out_val->special = ConstValSpecialStatic;
} else {
if (ir_eval_negation_scalar(ira, &instruction->base, scalar_type, operand_val, out_val,
is_wrap_op) != nullptr)
{
return ira->codegen->invalid_instruction;
}
}
return result_instruction;
}
IrInstruction *result = ir_build_un_op(&ira->new_irb,
instruction->base.scope, instruction->base.source_node,
instruction->op_id, value);
result->value.type = expr_type;
return result;
}
static IrInstruction *ir_analyze_bin_not(IrAnalyze *ira, IrInstructionUnOp *instruction) {
IrInstruction *value = instruction->value->child;
ZigType *expr_type = value->value.type;
if (type_is_invalid(expr_type))
return ira->codegen->invalid_instruction;
if (expr_type->id == ZigTypeIdInt) {
if (instr_is_comptime(value)) {
ConstExprValue *target_const_val = ir_resolve_const(ira, value, UndefBad);
if (target_const_val == nullptr)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_const(ira, &instruction->base, expr_type);
bigint_not(&result->value.data.x_bigint, &target_const_val->data.x_bigint,
expr_type->data.integral.bit_count, expr_type->data.integral.is_signed);
return result;
}
IrInstruction *result = ir_build_un_op(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, IrUnOpBinNot, value);
result->value.type = expr_type;
return result;
}
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->invalid_instruction;
}
static IrInstruction *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructionUnOp *instruction) {
IrUnOp op_id = instruction->op_id;
switch (op_id) {
case IrUnOpInvalid:
zig_unreachable();
case IrUnOpBinNot:
return ir_analyze_bin_not(ira, instruction);
case IrUnOpNegation:
case IrUnOpNegationWrap:
return ir_analyze_negation(ira, instruction);
case IrUnOpDereference: {
IrInstruction *ptr = instruction->value->child;
if (type_is_invalid(ptr->value.type))
return ira->codegen->invalid_instruction;
ZigType *ptr_type = ptr->value.type;
if (ptr_type->id == ZigTypeIdPointer && ptr_type->data.pointer.ptr_len == PtrLenUnknown) {
ir_add_error_node(ira, instruction->base.source_node,
buf_sprintf("index syntax required for unknown-length pointer type '%s'",
buf_ptr(&ptr_type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *result = ir_get_deref(ira, &instruction->base, ptr);
if (result == ira->codegen->invalid_instruction)
return ira->codegen->invalid_instruction;
// If the result needs to be an lvalue, type check it
if (instruction->lval == LValPtr && result->value.type->id != ZigTypeIdPointer) {
ir_add_error(ira, &instruction->base,
buf_sprintf("attempt to dereference non-pointer type '%s'", buf_ptr(&result->value.type->name)));
return ira->codegen->invalid_instruction;
}
return result;
}
case IrUnOpOptional:
return ir_analyze_maybe(ira, instruction);
}
zig_unreachable();
}
static IrInstruction *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->child, &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_runtime(ira, old_dest_block, &br_instruction->base);
if (new_bb == nullptr)
return ir_unreach_error(ira);
IrInstruction *result = ir_build_br(&ira->new_irb,
br_instruction->base.scope, br_instruction->base.source_node, new_bb, nullptr);
result->value.type = ira->codegen->builtin_types.entry_unreachable;
return ir_finish_anal(ira, result);
}
static IrInstruction *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructionCondBr *cond_br_instruction) {
IrInstruction *condition = cond_br_instruction->condition->child;
if (type_is_invalid(condition->value.type))
return ir_unreach_error(ira);
bool is_comptime;
if (!ir_resolve_comptime(ira, cond_br_instruction->is_comptime->child, &is_comptime))
return ir_unreach_error(ira);
ZigType *bool_type = ira->codegen->builtin_types.entry_bool;
IrInstruction *casted_condition = ir_implicit_cast(ira, condition, bool_type);
if (type_is_invalid(casted_condition->value.type))
return ir_unreach_error(ira);
if (is_comptime || instr_is_comptime(casted_condition)) {
bool cond_is_true;
if (!ir_resolve_bool(ira, casted_condition, &cond_is_true))
return ir_unreach_error(ira);
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_runtime(ira, old_dest_block, &cond_br_instruction->base);
if (new_dest_block == nullptr)
return ir_unreach_error(ira);
IrInstruction *result = ir_build_br(&ira->new_irb,
cond_br_instruction->base.scope, cond_br_instruction->base.source_node, new_dest_block, nullptr);
result->value.type = ira->codegen->builtin_types.entry_unreachable;
return ir_finish_anal(ira, result);
}
assert(cond_br_instruction->then_block != cond_br_instruction->else_block);
IrBasicBlock *new_then_block = ir_get_new_bb_runtime(ira, cond_br_instruction->then_block, &cond_br_instruction->base);
if (new_then_block == nullptr)
return ir_unreach_error(ira);
IrBasicBlock *new_else_block = ir_get_new_bb_runtime(ira, cond_br_instruction->else_block, &cond_br_instruction->base);
if (new_else_block == nullptr)
return ir_unreach_error(ira);
IrInstruction *result = ir_build_cond_br(&ira->new_irb,
cond_br_instruction->base.scope, cond_br_instruction->base.source_node,
casted_condition, new_then_block, new_else_block, nullptr);
result->value.type = ira->codegen->builtin_types.entry_unreachable;
return ir_finish_anal(ira, result);
}
static IrInstruction *ir_analyze_instruction_unreachable(IrAnalyze *ira,
IrInstructionUnreachable *unreachable_instruction)
{
IrInstruction *result = ir_build_unreachable(&ira->new_irb,
unreachable_instruction->base.scope, unreachable_instruction->base.source_node);
result->value.type = ira->codegen->builtin_types.entry_unreachable;
return ir_finish_anal(ira, result);
}
static IrInstruction *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]->child;
assert(value->value.type);
if (type_is_invalid(value->value.type))
return ira->codegen->invalid_instruction;
if (value->value.special != ConstValSpecialRuntime) {
IrInstruction *result = ir_const(ira, &phi_instruction->base, nullptr);
copy_const_val(&result->value, &value->value, true);
return result;
} else {
return value;
}
}
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->child;
if (!new_value || new_value->value.type->id == ZigTypeIdUnreachable || predecessor->other == nullptr)
continue;
if (type_is_invalid(new_value->value.type))
return ira->codegen->invalid_instruction;
assert(predecessor->other);
new_incoming_blocks.append(predecessor->other);
new_incoming_values.append(new_value);
}
if (new_incoming_blocks.length == 0) {
IrInstruction *result = ir_build_unreachable(&ira->new_irb,
phi_instruction->base.scope, phi_instruction->base.source_node);
result->value.type = ira->codegen->builtin_types.entry_unreachable;
return ir_finish_anal(ira, result);
}
if (new_incoming_blocks.length == 1) {
return new_incoming_values.at(0);
}
ZigType *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node, nullptr,
new_incoming_values.items, new_incoming_values.length);
if (type_is_invalid(resolved_type))
return ira->codegen->invalid_instruction;
switch (type_has_one_possible_value(ira->codegen, resolved_type)) {
case OnePossibleValueInvalid:
return ira->codegen->invalid_instruction;
case OnePossibleValueYes:
return ir_const(ira, &phi_instruction->base, resolved_type);
case OnePossibleValueNo:
break;
}
switch (type_requires_comptime(ira->codegen, resolved_type)) {
case ReqCompTimeInvalid:
return ira->codegen->invalid_instruction;
case ReqCompTimeYes:
ir_add_error_node(ira, phi_instruction->base.source_node,
buf_sprintf("values of type '%s' must be comptime known", buf_ptr(&resolved_type->name)));
return ira->codegen->invalid_instruction;
case ReqCompTimeNo:
break;
}
bool all_stack_ptrs = (resolved_type->id == ZigTypeIdPointer);
// 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);
if (casted_value == ira->codegen->invalid_instruction) {
return ira->codegen->invalid_instruction;
}
new_incoming_values.items[i] = casted_value;
predecessor->instruction_list.append(branch_instruction);
if (all_stack_ptrs && (casted_value->value.special != ConstValSpecialRuntime ||
casted_value->value.data.rh_ptr != RuntimeHintPtrStack))
{
all_stack_ptrs = false;
}
}
ir_set_cursor_at_end(&ira->new_irb, cur_bb);
IrInstruction *result = ir_build_phi(&ira->new_irb,
phi_instruction->base.scope, phi_instruction->base.source_node,
new_incoming_blocks.length, new_incoming_blocks.items, new_incoming_values.items);
result->value.type = resolved_type;
if (all_stack_ptrs) {
assert(result->value.special == ConstValSpecialRuntime);
result->value.data.rh_ptr = RuntimeHintPtrStack;
}
return result;
}
static IrInstruction *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructionVarPtr *instruction) {
ZigVar *var = instruction->var;
IrInstruction *result = ir_get_var_ptr(ira, &instruction->base, var);
if (instruction->crossed_fndef_scope != nullptr && !instr_is_comptime(result)) {
ErrorMsg *msg = ir_add_error(ira, &instruction->base,
buf_sprintf("'%s' not accessible from inner function", buf_ptr(&var->name)));
add_error_note(ira->codegen, msg, instruction->crossed_fndef_scope->base.source_node,
buf_sprintf("crossed function definition here"));
add_error_note(ira->codegen, msg, var->decl_node,
buf_sprintf("declared here"));
return ira->codegen->invalid_instruction;
}
return result;
}
static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align) {
assert(ptr_type->id == ZigTypeIdPointer);
return get_pointer_to_type_extra(g,
ptr_type->data.pointer.child_type,
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
ptr_type->data.pointer.ptr_len,
new_align,
ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes,
ptr_type->data.pointer.allow_zero);
}
static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align) {
assert(is_slice(slice_type));
ZigType *ptr_type = adjust_ptr_align(g, slice_type->data.structure.fields[slice_ptr_index].type_entry,
new_align);
return get_slice_type(g, ptr_type);
}
static ZigType *adjust_ptr_len(CodeGen *g, ZigType *ptr_type, PtrLen ptr_len) {
assert(ptr_type->id == ZigTypeIdPointer);
return get_pointer_to_type_extra(g,
ptr_type->data.pointer.child_type,
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
ptr_len,
ptr_type->data.pointer.explicit_alignment,
ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes,
ptr_type->data.pointer.allow_zero);
}
static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) {
Error err;
IrInstruction *array_ptr = elem_ptr_instruction->array_ptr->child;
if (type_is_invalid(array_ptr->value.type))
return ira->codegen->invalid_instruction;
ConstExprValue *orig_array_ptr_val = &array_ptr->value;
IrInstruction *elem_index = elem_ptr_instruction->elem_index->child;
if (type_is_invalid(elem_index->value.type))
return ira->codegen->invalid_instruction;
ZigType *ptr_type = orig_array_ptr_val->type;
assert(ptr_type->id == ZigTypeIdPointer);
ZigType *array_type = ptr_type->data.pointer.child_type;
// At first return_type will be the pointer type we want to return, except with an optimistic alignment.
// We will adjust return_type's alignment before returning it.
ZigType *return_type;
if (type_is_invalid(array_type)) {
return ira->codegen->invalid_instruction;
} else if (array_type->id == ZigTypeIdArray ||
(array_type->id == ZigTypeIdPointer &&
array_type->data.pointer.ptr_len == PtrLenSingle &&
array_type->data.pointer.child_type->id == ZigTypeIdArray))
{
if (array_type->id == ZigTypeIdPointer) {
array_type = array_type->data.pointer.child_type;
ptr_type = ptr_type->data.pointer.child_type;
if (orig_array_ptr_val->special != ConstValSpecialRuntime) {
orig_array_ptr_val = const_ptr_pointee(ira, ira->codegen, orig_array_ptr_val,
elem_ptr_instruction->base.source_node);
if (orig_array_ptr_val == nullptr)
return ira->codegen->invalid_instruction;
}
}
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"));
return ira->codegen->invalid_instruction;
}
ZigType *child_type = array_type->data.array.child_type;
if (ptr_type->data.pointer.host_int_bytes == 0) {
return_type = get_pointer_to_type_extra(ira->codegen, child_type,
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
elem_ptr_instruction->ptr_len,
ptr_type->data.pointer.explicit_alignment, 0, 0, false);
} else {
uint64_t elem_val_scalar;
if (!ir_resolve_usize(ira, elem_index, &elem_val_scalar))
return ira->codegen->invalid_instruction;
size_t bit_width = type_size_bits(ira->codegen, child_type);
size_t bit_offset = bit_width * elem_val_scalar;
return_type = get_pointer_to_type_extra(ira->codegen, child_type,
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
elem_ptr_instruction->ptr_len,
1, (uint32_t)bit_offset, ptr_type->data.pointer.host_int_bytes, false);
}
} else if (array_type->id == ZigTypeIdPointer) {
if (array_type->data.pointer.ptr_len == PtrLenSingle) {
ir_add_error_node(ira, elem_ptr_instruction->base.source_node,
buf_sprintf("index of single-item pointer"));
return ira->codegen->invalid_instruction;
}
return_type = adjust_ptr_len(ira->codegen, array_type, elem_ptr_instruction->ptr_len);
} else if (is_slice(array_type)) {
return_type = adjust_ptr_len(ira->codegen, array_type->data.structure.fields[slice_ptr_index].type_entry,
elem_ptr_instruction->ptr_len);
} else if (array_type->id == ZigTypeIdArgTuple) {
ConstExprValue *ptr_val = ir_resolve_const(ira, array_ptr, UndefBad);
if (!ptr_val)
return ira->codegen->invalid_instruction;
ConstExprValue *args_val = const_ptr_pointee(ira, ira->codegen, ptr_val, elem_ptr_instruction->base.source_node);
if (args_val == nullptr)
return ira->codegen->invalid_instruction;
size_t start = args_val->data.x_arg_tuple.start_index;
size_t end = args_val->data.x_arg_tuple.end_index;
uint64_t elem_index_val;
if (!ir_resolve_usize(ira, elem_index, &elem_index_val))
return ira->codegen->invalid_instruction;
size_t index = elem_index_val;
size_t len = end - start;
if (index >= len) {
ir_add_error(ira, &elem_ptr_instruction->base,
buf_sprintf("index %" ZIG_PRI_usize " outside argument list of size %" ZIG_PRI_usize "", index, len));
return ira->codegen->invalid_instruction;
}
size_t abs_index = start + index;
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
assert(fn_entry);
ZigVar *var = get_fn_var_by_index(fn_entry, abs_index);
bool is_const = true;
bool is_volatile = false;
if (var) {
return ir_get_var_ptr(ira, &elem_ptr_instruction->base, var);
} else {
return ir_get_const_ptr(ira, &elem_ptr_instruction->base, &ira->codegen->const_void_val,
ira->codegen->builtin_types.entry_void, ConstPtrMutComptimeConst, is_const, is_volatile, 0);
}
} 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->invalid_instruction;
}
ZigType *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->invalid_instruction;
bool safety_check_on = elem_ptr_instruction->safety_check_on;
if ((err = ensure_complete_type(ira->codegen, return_type->data.pointer.child_type)))
return ira->codegen->invalid_instruction;
uint64_t elem_size = type_size(ira->codegen, return_type->data.pointer.child_type);
uint64_t abi_align = get_abi_alignment(ira->codegen, return_type->data.pointer.child_type);
uint64_t ptr_align = get_ptr_align(ira->codegen, return_type);
if (instr_is_comptime(casted_elem_index)) {
uint64_t index = bigint_as_unsigned(&casted_elem_index->value.data.x_bigint);
if (array_type->id == ZigTypeIdArray) {
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 %" ZIG_PRI_u64 " outside array of size %" ZIG_PRI_u64,
index, array_len));
return ira->codegen->invalid_instruction;
}
safety_check_on = false;
}
{
// figure out the largest alignment possible
uint64_t chosen_align = abi_align;
if (ptr_align >= abi_align) {
while (ptr_align > abi_align) {
if ((index * elem_size) % ptr_align == 0) {
chosen_align = ptr_align;
break;
}
ptr_align >>= 1;
}
} else if (elem_size >= ptr_align && elem_size % ptr_align == 0) {
chosen_align = ptr_align;
} else {
// can't get here because guaranteed elem_size >= abi_align
zig_unreachable();
}
return_type = adjust_ptr_align(ira->codegen, return_type, chosen_align);
}
if (orig_array_ptr_val->special != ConstValSpecialRuntime &&
(orig_array_ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar ||
array_type->id == ZigTypeIdArray))
{
ConstExprValue *array_ptr_val = const_ptr_pointee(ira, ira->codegen, orig_array_ptr_val,
elem_ptr_instruction->base.source_node);
if (array_ptr_val == nullptr)
return ira->codegen->invalid_instruction;
if (array_ptr_val->special != ConstValSpecialRuntime &&
(array_type->id != ZigTypeIdPointer ||
array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr))
{
if (array_type->id == ZigTypeIdPointer) {
IrInstruction *result = ir_const(ira, &elem_ptr_instruction->base, return_type);
ConstExprValue *out_val = &result->value;
out_val->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut;
size_t new_index;
size_t mem_size;
size_t old_size;
switch (array_ptr_val->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
case ConstPtrSpecialDiscard:
zig_unreachable();
case ConstPtrSpecialRef:
mem_size = 1;
old_size = 1;
new_index = index;
out_val->data.x_ptr.special = ConstPtrSpecialRef;
out_val->data.x_ptr.data.ref.pointee = array_ptr_val->data.x_ptr.data.ref.pointee;
break;
case ConstPtrSpecialBaseArray:
{
size_t offset = array_ptr_val->data.x_ptr.data.base_array.elem_index;
new_index = offset + index;
mem_size = array_ptr_val->data.x_ptr.data.base_array.array_val->type->data.array.len;
old_size = mem_size - offset;
assert(array_ptr_val->data.x_ptr.data.base_array.array_val);
out_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
out_val->data.x_ptr.data.base_array.array_val =
array_ptr_val->data.x_ptr.data.base_array.array_val;
out_val->data.x_ptr.data.base_array.elem_index = new_index;
out_val->data.x_ptr.data.base_array.is_cstr =
array_ptr_val->data.x_ptr.data.base_array.is_cstr;
break;
}
case ConstPtrSpecialBaseStruct:
zig_panic("TODO elem ptr on a const inner struct");
case ConstPtrSpecialBaseErrorUnionCode:
zig_panic("TODO elem ptr on a const inner error union code");
case ConstPtrSpecialBaseErrorUnionPayload:
zig_panic("TODO elem ptr on a const inner error union payload");
case ConstPtrSpecialBaseOptionalPayload:
zig_panic("TODO elem ptr on a const inner optional payload");
case ConstPtrSpecialHardCodedAddr:
zig_unreachable();
case ConstPtrSpecialFunction:
zig_panic("TODO element ptr of a function casted to a ptr");
case ConstPtrSpecialNull:
zig_panic("TODO elem ptr on a null pointer");
}
if (new_index >= mem_size) {
ir_add_error_node(ira, elem_ptr_instruction->base.source_node,
buf_sprintf("index %" ZIG_PRI_u64 " outside pointer of size %" ZIG_PRI_usize "", index, old_size));
return ira->codegen->invalid_instruction;
}
return result;
} else if (is_slice(array_type)) {
ConstExprValue *ptr_field = &array_ptr_val->data.x_struct.fields[slice_ptr_index];
if (ptr_field->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {
IrInstruction *result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope, elem_ptr_instruction->base.source_node,
array_ptr, casted_elem_index, false, elem_ptr_instruction->ptr_len);
result->value.type = return_type;
return result;
}
ConstExprValue *len_field = &array_ptr_val->data.x_struct.fields[slice_len_index];
IrInstruction *result = ir_const(ira, &elem_ptr_instruction->base, return_type);
ConstExprValue *out_val = &result->value;
uint64_t slice_len = bigint_as_unsigned(&len_field->data.x_bigint);
if (index >= slice_len) {
ir_add_error_node(ira, elem_ptr_instruction->base.source_node,
buf_sprintf("index %" ZIG_PRI_u64 " outside slice of size %" ZIG_PRI_u64,
index, slice_len));
return ira->codegen->invalid_instruction;
}
out_val->data.x_ptr.mut = ptr_field->data.x_ptr.mut;
switch (ptr_field->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
case ConstPtrSpecialDiscard:
zig_unreachable();
case ConstPtrSpecialRef:
out_val->data.x_ptr.special = ConstPtrSpecialRef;
out_val->data.x_ptr.data.ref.pointee = ptr_field->data.x_ptr.data.ref.pointee;
break;
case ConstPtrSpecialBaseArray:
{
size_t offset = ptr_field->data.x_ptr.data.base_array.elem_index;
uint64_t new_index = offset + index;
assert(new_index < ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len);
out_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
out_val->data.x_ptr.data.base_array.array_val =
ptr_field->data.x_ptr.data.base_array.array_val;
out_val->data.x_ptr.data.base_array.elem_index = new_index;
out_val->data.x_ptr.data.base_array.is_cstr =
ptr_field->data.x_ptr.data.base_array.is_cstr;
break;
}
case ConstPtrSpecialBaseStruct:
zig_panic("TODO elem ptr on a slice backed by const inner struct");
case ConstPtrSpecialBaseErrorUnionCode:
zig_panic("TODO elem ptr on a slice backed by const inner error union code");
case ConstPtrSpecialBaseErrorUnionPayload:
zig_panic("TODO elem ptr on a slice backed by const inner error union payload");
case ConstPtrSpecialBaseOptionalPayload:
zig_panic("TODO elem ptr on a slice backed by const optional payload");
case ConstPtrSpecialHardCodedAddr:
zig_unreachable();
case ConstPtrSpecialFunction:
zig_panic("TODO elem ptr on a slice that was ptrcast from a function");
case ConstPtrSpecialNull:
zig_panic("TODO elem ptr on a slice has a null pointer");
}
return result;
} else if (array_type->id == ZigTypeIdArray) {
IrInstruction *result = ir_const(ira, &elem_ptr_instruction->base, return_type);
ConstExprValue *out_val = &result->value;
out_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
out_val->data.x_ptr.mut = orig_array_ptr_val->data.x_ptr.mut;
out_val->data.x_ptr.data.base_array.array_val = array_ptr_val;
out_val->data.x_ptr.data.base_array.elem_index = index;
return result;
} else {
zig_unreachable();
}
}
}
} else {
// runtime known element index
switch (type_requires_comptime(ira->codegen, return_type)) {
case ReqCompTimeYes:
ir_add_error(ira, elem_index,
buf_sprintf("values of type '%s' must be comptime known, but index value is runtime known",
buf_ptr(&return_type->data.pointer.child_type->name)));
return ira->codegen->invalid_instruction;
case ReqCompTimeInvalid:
return ira->codegen->invalid_instruction;
case ReqCompTimeNo:
break;
}
if (ptr_align < abi_align) {
if (elem_size >= ptr_align && elem_size % ptr_align == 0) {
return_type = adjust_ptr_align(ira->codegen, return_type, ptr_align);
} else {
// can't get here because guaranteed elem_size >= abi_align
zig_unreachable();
}
} else {
return_type = adjust_ptr_align(ira->codegen, return_type, abi_align);
}
}
IrInstruction *result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope, elem_ptr_instruction->base.source_node,
array_ptr, casted_elem_index, safety_check_on, elem_ptr_instruction->ptr_len);
result->value.type = return_type;
return result;
}
static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira,
ZigType *bare_struct_type, Buf *field_name, IrInstruction *source_instr,
IrInstruction *container_ptr, ZigType *container_type)
{
if (!is_slice(bare_struct_type)) {
ScopeDecls *container_scope = get_container_scope(bare_struct_type);
assert(container_scope != nullptr);
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, source_instr->source_node);
if (tld->resolution == TldResolutionInvalid)
return ira->codegen->invalid_instruction;
TldFn *tld_fn = (TldFn *)tld;
ZigFn *fn_entry = tld_fn->fn_entry;
if (type_is_invalid(fn_entry->type_entry))
return ira->codegen->invalid_instruction;
IrInstruction *bound_fn_value = ir_build_const_bound_fn(&ira->new_irb, source_instr->scope,
source_instr->source_node, fn_entry, container_ptr);
return ir_get_ref(ira, source_instr, bound_fn_value, true, false);
}
}
const char *prefix_name;
if (is_slice(bare_struct_type)) {
prefix_name = "";
} else if (bare_struct_type->id == ZigTypeIdStruct) {
prefix_name = "struct ";
} else if (bare_struct_type->id == ZigTypeIdEnum) {
prefix_name = "enum ";
} else if (bare_struct_type->id == ZigTypeIdUnion) {
prefix_name = "union ";
} else {
prefix_name = "";
}
ir_add_error_node(ira, source_instr->source_node,
buf_sprintf("no member named '%s' in %s'%s'", buf_ptr(field_name), prefix_name, buf_ptr(&bare_struct_type->name)));
return ira->codegen->invalid_instruction;
}
static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name,
IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type)
{
Error err;
ZigType *bare_type = container_ref_type(container_type);
if ((err = ensure_complete_type(ira->codegen, bare_type)))
return ira->codegen->invalid_instruction;
assert(container_ptr->value.type->id == ZigTypeIdPointer);
bool is_const = container_ptr->value.type->data.pointer.is_const;
bool is_volatile = container_ptr->value.type->data.pointer.is_volatile;
if (bare_type->id == ZigTypeIdStruct) {
TypeStructField *field = find_struct_type_field(bare_type, field_name);
if (field) {
switch (type_has_one_possible_value(ira->codegen, field->type_entry)) {
case OnePossibleValueInvalid:
return ira->codegen->invalid_instruction;
case OnePossibleValueYes: {
IrInstruction *elem = ir_const(ira, source_instr, field->type_entry);
return ir_get_ref(ira, source_instr, elem, false, false);
}
case OnePossibleValueNo:
break;
}
bool is_packed = (bare_type->data.structure.layout == ContainerLayoutPacked);
uint32_t align_bytes = is_packed ? 1 : get_abi_alignment(ira->codegen, field->type_entry);
uint32_t ptr_bit_offset = container_ptr->value.type->data.pointer.bit_offset_in_host;
uint32_t ptr_host_int_bytes = container_ptr->value.type->data.pointer.host_int_bytes;
uint32_t host_int_bytes_for_result_type = (ptr_host_int_bytes == 0) ?
get_host_int_bytes(ira->codegen, bare_type, field) : ptr_host_int_bytes;
if (instr_is_comptime(container_ptr)) {
ConstExprValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad);
if (!ptr_val)
return ira->codegen->invalid_instruction;
if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) {
ConstExprValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node);
if (struct_val == nullptr)
return ira->codegen->invalid_instruction;
if (type_is_invalid(struct_val->type))
return ira->codegen->invalid_instruction;
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry,
is_const, is_volatile, PtrLenSingle, align_bytes,
(uint32_t)(ptr_bit_offset + field->bit_offset_in_host),
(uint32_t)host_int_bytes_for_result_type, false);
IrInstruction *result = ir_const(ira, source_instr, ptr_type);
ConstExprValue *const_val = &result->value;
const_val->data.x_ptr.special = ConstPtrSpecialBaseStruct;
const_val->data.x_ptr.mut = container_ptr->value.data.x_ptr.mut;
const_val->data.x_ptr.data.base_struct.struct_val = struct_val;
const_val->data.x_ptr.data.base_struct.field_index = field->src_index;
return result;
}
}
IrInstruction *result = ir_build_struct_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node,
container_ptr, field);
result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile,
PtrLenSingle,
align_bytes,
(uint32_t)(ptr_bit_offset + field->bit_offset_in_host),
host_int_bytes_for_result_type, false);
return result;
} else {
return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
source_instr, container_ptr, container_type);
}
} else if (bare_type->id == ZigTypeIdEnum) {
return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
source_instr, container_ptr, container_type);
} else if (bare_type->id == ZigTypeIdUnion) {
TypeUnionField *field = find_union_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->invalid_instruction;
if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) {
ConstExprValue *union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node);
if (union_val == nullptr)
return ira->codegen->invalid_instruction;
if (type_is_invalid(union_val->type))
return ira->codegen->invalid_instruction;
TypeUnionField *actual_field = find_union_field_by_tag(bare_type, &union_val->data.x_union.tag);
if (actual_field == nullptr)
zig_unreachable();
if (field != actual_field) {
ir_add_error_node(ira, source_instr->source_node,
buf_sprintf("accessing union field '%s' while field '%s' is set", buf_ptr(field_name),
buf_ptr(actual_field->name)));
return ira->codegen->invalid_instruction;
}
ConstExprValue *payload_val = union_val->data.x_union.payload;
ZigType *field_type = field->type_entry;
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type,
is_const, is_volatile, PtrLenSingle, 0, 0, 0, false);
IrInstruction *result = ir_const(ira, source_instr, ptr_type);
ConstExprValue *const_val = &result->value;
const_val->data.x_ptr.special = ConstPtrSpecialRef;
const_val->data.x_ptr.mut = container_ptr->value.data.x_ptr.mut;
const_val->data.x_ptr.data.ref.pointee = payload_val;
return result;
}
}
IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, container_ptr, field);
result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile,
PtrLenSingle, 0, 0, 0, false);
return result;
} else {
return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
source_instr, container_ptr, container_type);
}
} else {
zig_unreachable();
}
}
static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name, AstNode *source_node) {
bool is_libc = target_is_libc_lib_name(ira->codegen->zig_target, buf_ptr(lib_name));
if (is_libc && ira->codegen->libc_link_lib == nullptr && !ira->codegen->reported_bad_link_libc_error) {
ir_add_error_node(ira, source_node,
buf_sprintf("dependency on library c must be explicitly specified in the build command"));
ira->codegen->reported_bad_link_libc_error = true;
}
LinkLib *link_lib = add_link_lib(ira->codegen, lib_name);
for (size_t i = 0; i < link_lib->symbols.length; i += 1) {
Buf *existing_symbol_name = link_lib->symbols.at(i);
if (buf_eql_buf(existing_symbol_name, symbol_name)) {
return;
}
}
if (!is_libc && !target_is_wasm(ira->codegen->zig_target) && !ira->codegen->have_pic && !ira->codegen->reported_bad_link_libc_error) {
ErrorMsg *msg = ir_add_error_node(ira, source_node,
buf_sprintf("dependency on dynamic library '%s' requires enabling Position Independent Code",
buf_ptr(lib_name)));
add_error_note(ira->codegen, msg, source_node,
buf_sprintf("fixed by `--library %s` or `--enable-pic`", buf_ptr(lib_name)));
ira->codegen->reported_bad_link_libc_error = true;
}
for (size_t i = 0; i < ira->codegen->forbidden_libs.length; i += 1) {
Buf *forbidden_lib_name = ira->codegen->forbidden_libs.at(i);
if (buf_eql_buf(lib_name, forbidden_lib_name)) {
ir_add_error_node(ira, source_node,
buf_sprintf("linking against forbidden library '%s'", buf_ptr(symbol_name)));
}
}
link_lib->symbols.append(symbol_name);
}
static IrInstruction *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source_instruction, Tld *tld) {
resolve_top_level_decl(ira->codegen, tld, source_instruction->source_node);
if (tld->resolution == TldResolutionInvalid)
return ira->codegen->invalid_instruction;
switch (tld->id) {
case TldIdContainer:
case TldIdCompTime:
zig_unreachable();
case TldIdVar:
{
TldVar *tld_var = (TldVar *)tld;
ZigVar *var = tld_var->var;
if (tld_var->extern_lib_name != nullptr) {
add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, source_instruction->source_node);
}
return ir_get_var_ptr(ira, source_instruction, var);
}
case TldIdFn:
{
TldFn *tld_fn = (TldFn *)tld;
ZigFn *fn_entry = tld_fn->fn_entry;
assert(fn_entry->type_entry);
if (type_is_invalid(fn_entry->type_entry))
return ira->codegen->invalid_instruction;
// 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 = create_const_vals(1);
const_val->special = ConstValSpecialStatic;
const_val->type = fn_entry->type_entry;
const_val->data.x_ptr.data.fn.fn_entry = fn_entry;
const_val->data.x_ptr.special = ConstPtrSpecialFunction;
const_val->data.x_ptr.mut = ConstPtrMutComptimeConst;
if (tld_fn->extern_lib_name != nullptr) {
add_link_lib_symbol(ira, tld_fn->extern_lib_name, &fn_entry->symbol_name, source_instruction->source_node);
}
bool ptr_is_const = true;
bool ptr_is_volatile = false;
return ir_get_const_ptr(ira, source_instruction, const_val, fn_entry->type_entry,
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
}
}
zig_unreachable();
}
static ErrorTableEntry *find_err_table_entry(ZigType *err_set_type, Buf *field_name) {
assert(err_set_type->id == ZigTypeIdErrorSet);
for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
ErrorTableEntry *err_table_entry = err_set_type->data.error_set.errors[i];
if (buf_eql_buf(&err_table_entry->name, field_name)) {
return err_table_entry;
}
}
return nullptr;
}
static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) {
Error err;
IrInstruction *container_ptr = field_ptr_instruction->container_ptr->child;
if (type_is_invalid(container_ptr->value.type))
return ira->codegen->invalid_instruction;
ZigType *container_type = container_ptr->value.type->data.pointer.child_type;
Buf *field_name = field_ptr_instruction->field_name_buffer;
if (!field_name) {
IrInstruction *field_name_expr = field_ptr_instruction->field_name_expr->child;
field_name = ir_resolve_str(ira, field_name_expr);
if (!field_name)
return ira->codegen->invalid_instruction;
}
AstNode *source_node = field_ptr_instruction->base.source_node;
if (type_is_invalid(container_type)) {
return ira->codegen->invalid_instruction;
} else if (is_container_ref(container_type)) {
assert(container_ptr->value.type->id == ZigTypeIdPointer);
if (container_type->id == ZigTypeIdPointer) {
ZigType *bare_type = container_ref_type(container_type);
IrInstruction *container_child = ir_get_deref(ira, &field_ptr_instruction->base, container_ptr);
IrInstruction *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base, container_child, bare_type);
return result;
} else {
IrInstruction *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base, container_ptr, container_type);
return result;
}
} else if (is_array_ref(container_type)) {
if (buf_eql_str(field_name, "len")) {
ConstExprValue *len_val = create_const_vals(1);
if (container_type->id == ZigTypeIdPointer) {
init_const_usize(ira->codegen, len_val, container_type->data.pointer.child_type->data.array.len);
} else {
init_const_usize(ira->codegen, len_val, container_type->data.array.len);
}
ZigType *usize = ira->codegen->builtin_types.entry_usize;
bool ptr_is_const = true;
bool ptr_is_volatile = false;
return ir_get_const_ptr(ira, &field_ptr_instruction->base, len_val,
usize, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
} 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->invalid_instruction;
}
} else if (container_type->id == ZigTypeIdArgTuple) {
ConstExprValue *container_ptr_val = ir_resolve_const(ira, container_ptr, UndefBad);
if (!container_ptr_val)
return ira->codegen->invalid_instruction;
assert(container_ptr->value.type->id == ZigTypeIdPointer);
ConstExprValue *child_val = const_ptr_pointee(ira, ira->codegen, container_ptr_val, source_node);
if (child_val == nullptr)
return ira->codegen->invalid_instruction;
if (buf_eql_str(field_name, "len")) {
ConstExprValue *len_val = create_const_vals(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);
ZigType *usize = ira->codegen->builtin_types.entry_usize;
bool ptr_is_const = true;
bool ptr_is_volatile = false;
return ir_get_const_ptr(ira, &field_ptr_instruction->base, len_val,
usize, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
} 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->invalid_instruction;
}
} else if (container_type->id == ZigTypeIdMetaType) {
ConstExprValue *container_ptr_val = ir_resolve_const(ira, container_ptr, UndefBad);
if (!container_ptr_val)
return ira->codegen->invalid_instruction;
assert(container_ptr->value.type->id == ZigTypeIdPointer);
ConstExprValue *child_val = const_ptr_pointee(ira, ira->codegen, container_ptr_val, source_node);
if (child_val == nullptr)
return ira->codegen->invalid_instruction;
ZigType *child_type = child_val->data.x_type;
if (type_is_invalid(child_type)) {
return ira->codegen->invalid_instruction;
} else if (is_container(child_type)) {
if (is_slice(child_type) && buf_eql_str(field_name, "Child")) {
bool ptr_is_const = true;
bool ptr_is_volatile = false;
TypeStructField *ptr_field = &child_type->data.structure.fields[slice_ptr_index];
assert(ptr_field->type_entry->id == ZigTypeIdPointer);
ZigType *child_type = ptr_field->type_entry->data.pointer.child_type;
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
create_const_type(ira->codegen, child_type),
ira->codegen->builtin_types.entry_type,
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
}
if (child_type->id == ZigTypeIdEnum) {
if ((err = ensure_complete_type(ira->codegen, child_type)))
return ira->codegen->invalid_instruction;
TypeEnumField *field = find_enum_type_field(child_type, field_name);
if (field) {
bool ptr_is_const = true;
bool ptr_is_volatile = false;
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
create_const_enum(child_type, &field->value), child_type,
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
}
}
ScopeDecls *container_scope = get_container_scope(child_type);
Tld *tld = find_container_decl(ira->codegen, container_scope, field_name);
if (tld) {
if (tld->visib_mod == VisibModPrivate &&
tld->import != get_scope_import(field_ptr_instruction->base.scope))
{
ErrorMsg *msg = ir_add_error(ira, &field_ptr_instruction->base,
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->invalid_instruction;
}
return ir_analyze_decl_ref(ira, &field_ptr_instruction->base, tld);
}
if (child_type->id == ZigTypeIdUnion &&
(child_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr ||
child_type->data.unionation.decl_node->data.container_decl.auto_enum))
{
if ((err = ensure_complete_type(ira->codegen, child_type)))
return ira->codegen->invalid_instruction;
TypeUnionField *field = find_union_type_field(child_type, field_name);
if (field) {
ZigType *enum_type = child_type->data.unionation.tag_type;
bool ptr_is_const = true;
bool ptr_is_volatile = false;
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
create_const_enum(enum_type, &field->enum_field->value), enum_type,
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
}
}
const char *container_name = (child_type == ira->codegen->root_import) ?
"root source file" : buf_ptr(buf_sprintf("container '%s'", buf_ptr(&child_type->name)));
ir_add_error(ira, &field_ptr_instruction->base,
buf_sprintf("%s has no member called '%s'",
container_name, buf_ptr(field_name)));
return ira->codegen->invalid_instruction;
} else if (child_type->id == ZigTypeIdErrorSet) {
ErrorTableEntry *err_entry;
ZigType *err_set_type;
if (type_is_global_error_set(child_type)) {
auto existing_entry = ira->codegen->error_table.maybe_get(field_name);
if (existing_entry) {
err_entry = existing_entry->value;
} else {
err_entry = allocate<ErrorTableEntry>(1);
err_entry->decl_node = field_ptr_instruction->base.source_node;
buf_init_from_buf(&err_entry->name, field_name);
size_t error_value_count = ira->codegen->errors_by_index.length;
assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)ira->codegen->err_tag_type->data.integral.bit_count));
err_entry->value = error_value_count;
ira->codegen->errors_by_index.append(err_entry);
ira->codegen->error_table.put(field_name, err_entry);
}
if (err_entry->set_with_only_this_in_it == nullptr) {
err_entry->set_with_only_this_in_it = make_err_set_with_one_item(ira->codegen,
field_ptr_instruction->base.scope, field_ptr_instruction->base.source_node,
err_entry);
}
err_set_type = err_entry->set_with_only_this_in_it;
} else {
if (!resolve_inferred_error_set(ira->codegen, child_type, field_ptr_instruction->base.source_node)) {
return ira->codegen->invalid_instruction;
}
err_entry = find_err_table_entry(child_type, field_name);
if (err_entry == nullptr) {
ir_add_error(ira, &field_ptr_instruction->base,
buf_sprintf("no error named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&child_type->name)));
return ira->codegen->invalid_instruction;
}
err_set_type = child_type;
}
ConstExprValue *const_val = create_const_vals(1);
const_val->special = ConstValSpecialStatic;
const_val->type = err_set_type;
const_val->data.x_err_set = err_entry;
bool ptr_is_const = true;
bool ptr_is_volatile = false;
return ir_get_const_ptr(ira, &field_ptr_instruction->base, const_val,
err_set_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
} else if (child_type->id == ZigTypeIdInt) {
if (buf_eql_str(field_name, "bit_count")) {
bool ptr_is_const = true;
bool ptr_is_volatile = false;
return ir_get_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,
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
} else if (buf_eql_str(field_name, "is_signed")) {
bool ptr_is_const = true;
bool ptr_is_volatile = false;
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
create_const_bool(ira->codegen, child_type->data.integral.is_signed),
ira->codegen->builtin_types.entry_bool,
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
} 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->invalid_instruction;
}
} else if (child_type->id == ZigTypeIdFloat) {
if (buf_eql_str(field_name, "bit_count")) {
bool ptr_is_const = true;
bool ptr_is_volatile = false;
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
create_const_unsigned_negative(ira->codegen->builtin_types.entry_num_lit_int,
child_type->data.floating.bit_count, false),
ira->codegen->builtin_types.entry_num_lit_int,
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
} 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->invalid_instruction;
}
} else if (child_type->id == ZigTypeIdPointer) {
if (buf_eql_str(field_name, "Child")) {
bool ptr_is_const = true;
bool ptr_is_volatile = false;
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
create_const_type(ira->codegen, child_type->data.pointer.child_type),
ira->codegen->builtin_types.entry_type,
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
} else if (buf_eql_str(field_name, "alignment")) {
bool ptr_is_const = true;
bool ptr_is_volatile = false;
if ((err = type_resolve(ira->codegen, child_type->data.pointer.child_type,
ResolveStatusAlignmentKnown)))
{
return ira->codegen->invalid_instruction;
}
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
create_const_unsigned_negative(ira->codegen->builtin_types.entry_num_lit_int,
get_ptr_align(ira->codegen, child_type), false),
ira->codegen->builtin_types.entry_num_lit_int,
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
} 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->invalid_instruction;
}
} else if (child_type->id == ZigTypeIdArray) {
if (buf_eql_str(field_name, "Child")) {
bool ptr_is_const = true;
bool ptr_is_volatile = false;
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
create_const_type(ira->codegen, child_type->data.array.child_type),
ira->codegen->builtin_types.entry_type,
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
} else if (buf_eql_str(field_name, "len")) {
bool ptr_is_const = true;
bool ptr_is_volatile = false;
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
create_const_unsigned_negative(ira->codegen->builtin_types.entry_num_lit_int,
child_type->data.array.len, false),
ira->codegen->builtin_types.entry_num_lit_int,
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
} 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->invalid_instruction;
}
} else if (child_type->id == ZigTypeIdErrorUnion) {
if (buf_eql_str(field_name, "Payload")) {
bool ptr_is_const = true;
bool ptr_is_volatile = false;
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
create_const_type(ira->codegen, child_type->data.error_union.payload_type),
ira->codegen->builtin_types.entry_type,
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
} else if (buf_eql_str(field_name, "ErrorSet")) {
bool ptr_is_const = true;
bool ptr_is_volatile = false;
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
create_const_type(ira->codegen, child_type->data.error_union.err_set_type),
ira->codegen->builtin_types.entry_type,
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
} 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->invalid_instruction;
}
} else if (child_type->id == ZigTypeIdOptional) {
if (buf_eql_str(field_name, "Child")) {
bool ptr_is_const = true;
bool ptr_is_volatile = false;
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
create_const_type(ira->codegen, child_type->data.maybe.child_type),
ira->codegen->builtin_types.entry_type,
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
} 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->invalid_instruction;
}
} else if (child_type->id == ZigTypeIdFn) {
if (buf_eql_str(field_name, "ReturnType")) {
if (child_type->data.fn.fn_type_id.return_type == nullptr) {
// Return type can only ever be null, if the function is generic
assert(child_type->data.fn.is_generic);
ir_add_error(ira, &field_ptr_instruction->base,
buf_sprintf("ReturnType has not been resolved because '%s' is generic", buf_ptr(&child_type->name)));
return ira->codegen->invalid_instruction;
}
bool ptr_is_const = true;
bool ptr_is_volatile = false;
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
create_const_type(ira->codegen, child_type->data.fn.fn_type_id.return_type),
ira->codegen->builtin_types.entry_type,
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
} else if (buf_eql_str(field_name, "is_var_args")) {
bool ptr_is_const = true;
bool ptr_is_volatile = false;
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
create_const_bool(ira->codegen, child_type->data.fn.fn_type_id.is_var_args),
ira->codegen->builtin_types.entry_bool,
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
} else if (buf_eql_str(field_name, "arg_count")) {
bool ptr_is_const = true;
bool ptr_is_volatile = false;
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
create_const_usize(ira->codegen, child_type->data.fn.fn_type_id.param_count),
ira->codegen->builtin_types.entry_usize,
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
} 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->invalid_instruction;
}
} else {
ir_add_error(ira, &field_ptr_instruction->base,
buf_sprintf("type '%s' does not support field access", buf_ptr(&child_type->name)));
return ira->codegen->invalid_instruction;
}
} 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->invalid_instruction;
}
}
static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *instruction) {
IrInstruction *ptr = instruction->ptr->child;
if (type_is_invalid(ptr->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *value = instruction->value->child;
if (type_is_invalid(value->value.type))
return ira->codegen->invalid_instruction;
return ir_analyze_store_ptr(ira, &instruction->base, ptr, value);
}
static IrInstruction *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *instruction) {
IrInstruction *ptr = instruction->ptr->child;
if (type_is_invalid(ptr->value.type))
return ira->codegen->invalid_instruction;
return ir_get_deref(ira, &instruction->base, ptr);
}
static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) {
IrInstruction *expr_value = typeof_instruction->value->child;
ZigType *type_entry = expr_value->value.type;
if (type_is_invalid(type_entry))
return ira->codegen->invalid_instruction;
return ir_const_type(ira, &typeof_instruction->base, type_entry);
}
static IrInstruction *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira,
IrInstructionToPtrType *to_ptr_type_instruction)
{
Error err;
IrInstruction *ptr_ptr = to_ptr_type_instruction->ptr->child;
if (type_is_invalid(ptr_ptr->value.type))
return ira->codegen->invalid_instruction;
ZigType *ptr_ptr_type = ptr_ptr->value.type;
assert(ptr_ptr_type->id == ZigTypeIdPointer);
ZigType *type_entry = ptr_ptr_type->data.pointer.child_type;
ZigType *ptr_type;
if (type_entry->id == ZigTypeIdArray) {
ptr_type = get_pointer_to_type(ira->codegen, type_entry->data.array.child_type, ptr_ptr_type->data.pointer.is_const);
} else if (is_array_ref(type_entry)) {
ptr_type = get_pointer_to_type(ira->codegen,
type_entry->data.pointer.child_type->data.array.child_type, type_entry->data.pointer.is_const);
} else if (is_slice(type_entry)) {
ZigType *slice_ptr_type = type_entry->data.structure.fields[0].type_entry;
ptr_type = adjust_ptr_len(ira->codegen, slice_ptr_type, PtrLenSingle);
// If the pointer is over-aligned, we may have to reduce it based on the alignment of the element type.
if (slice_ptr_type->data.pointer.explicit_alignment != 0) {
ZigType *elem_type = slice_ptr_type->data.pointer.child_type;
if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusAlignmentKnown)))
return ira->codegen->invalid_instruction;
uint32_t elem_align = get_abi_alignment(ira->codegen, elem_type);
uint32_t reduced_align = min(elem_align, slice_ptr_type->data.pointer.explicit_alignment);
ptr_type = adjust_ptr_align(ira->codegen, ptr_type, reduced_align);
}
} else if (type_entry->id == ZigTypeIdArgTuple) {
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->invalid_instruction;
}
return ir_const_type(ira, &to_ptr_type_instruction->base, ptr_type);
}
static IrInstruction *ir_analyze_instruction_ptr_type_child(IrAnalyze *ira,
IrInstructionPtrTypeChild *ptr_type_child_instruction)
{
IrInstruction *type_value = ptr_type_child_instruction->value->child;
ZigType *type_entry = ir_resolve_type(ira, type_value);
if (type_is_invalid(type_entry))
return ira->codegen->invalid_instruction;
if (type_entry->id != ZigTypeIdPointer) {
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->invalid_instruction;
}
return ir_const_type(ira, &ptr_type_child_instruction->base, type_entry->data.pointer.child_type);
}
static IrInstruction *ir_analyze_instruction_set_cold(IrAnalyze *ira, IrInstructionSetCold *instruction) {
if (ira->new_irb.exec->is_inline) {
// ignore setCold when running functions at compile time
return ir_const_void(ira, &instruction->base);
}
IrInstruction *is_cold_value = instruction->is_cold->child;
bool want_cold;
if (!ir_resolve_bool(ira, is_cold_value, &want_cold))
return ira->codegen->invalid_instruction;
ZigFn *fn_entry = scope_fn_entry(instruction->base.scope);
if (fn_entry == nullptr) {
ir_add_error(ira, &instruction->base, buf_sprintf("@setCold outside function"));
return ira->codegen->invalid_instruction;
}
if (fn_entry->set_cold_node != nullptr) {
ErrorMsg *msg = ir_add_error(ira, &instruction->base, buf_sprintf("cold set twice in same function"));
add_error_note(ira->codegen, msg, fn_entry->set_cold_node, buf_sprintf("first set here"));
return ira->codegen->invalid_instruction;
}
fn_entry->set_cold_node = instruction->base.source_node;
fn_entry->is_cold = want_cold;
return ir_const_void(ira, &instruction->base);
}
static IrInstruction *ir_analyze_instruction_set_runtime_safety(IrAnalyze *ira,
IrInstructionSetRuntimeSafety *set_runtime_safety_instruction)
{
if (ira->new_irb.exec->is_inline) {
// ignore setRuntimeSafety when running functions at compile time
return ir_const_void(ira, &set_runtime_safety_instruction->base);
}
bool *safety_off_ptr;
AstNode **safety_set_node_ptr;
Scope *scope = set_runtime_safety_instruction->base.scope;
while (scope != nullptr) {
if (scope->id == ScopeIdBlock) {
ScopeBlock *block_scope = (ScopeBlock *)scope;
safety_off_ptr = &block_scope->safety_off;
safety_set_node_ptr = &block_scope->safety_set_node;
break;
} else if (scope->id == ScopeIdFnDef) {
ScopeFnDef *def_scope = (ScopeFnDef *)scope;
ZigFn *target_fn = def_scope->fn_entry;
assert(target_fn->def_scope != nullptr);
safety_off_ptr = &target_fn->def_scope->safety_off;
safety_set_node_ptr = &target_fn->def_scope->safety_set_node;
break;
} else if (scope->id == ScopeIdDecls) {
ScopeDecls *decls_scope = (ScopeDecls *)scope;
safety_off_ptr = &decls_scope->safety_off;
safety_set_node_ptr = &decls_scope->safety_set_node;
break;
} else {
scope = scope->parent;
continue;
}
}
assert(scope != nullptr);
IrInstruction *safety_on_value = set_runtime_safety_instruction->safety_on->child;
bool want_runtime_safety;
if (!ir_resolve_bool(ira, safety_on_value, &want_runtime_safety))
return ira->codegen->invalid_instruction;
AstNode *source_node = set_runtime_safety_instruction->base.source_node;
if (*safety_set_node_ptr) {
ErrorMsg *msg = ir_add_error_node(ira, source_node,
buf_sprintf("runtime safety set twice for same scope"));
add_error_note(ira->codegen, msg, *safety_set_node_ptr, buf_sprintf("first set here"));
return ira->codegen->invalid_instruction;
}
*safety_set_node_ptr = source_node;
*safety_off_ptr = !want_runtime_safety;
return ir_const_void(ira, &set_runtime_safety_instruction->base);
}
static IrInstruction *ir_analyze_instruction_set_float_mode(IrAnalyze *ira,
IrInstructionSetFloatMode *instruction)
{
if (ira->new_irb.exec->is_inline) {
// ignore setFloatMode when running functions at compile time
return ir_const_void(ira, &instruction->base);
}
bool *fast_math_on_ptr;
AstNode **fast_math_set_node_ptr;
Scope *scope = instruction->base.scope;
while (scope != nullptr) {
if (scope->id == ScopeIdBlock) {
ScopeBlock *block_scope = (ScopeBlock *)scope;
fast_math_on_ptr = &block_scope->fast_math_on;
fast_math_set_node_ptr = &block_scope->fast_math_set_node;
break;
} else if (scope->id == ScopeIdFnDef) {
ScopeFnDef *def_scope = (ScopeFnDef *)scope;
ZigFn *target_fn = def_scope->fn_entry;
assert(target_fn->def_scope != nullptr);
fast_math_on_ptr = &target_fn->def_scope->fast_math_on;
fast_math_set_node_ptr = &target_fn->def_scope->fast_math_set_node;
break;
} else if (scope->id == ScopeIdDecls) {
ScopeDecls *decls_scope = (ScopeDecls *)scope;
fast_math_on_ptr = &decls_scope->fast_math_on;
fast_math_set_node_ptr = &decls_scope->fast_math_set_node;
break;
} else {
scope = scope->parent;
continue;
}
}
assert(scope != nullptr);
IrInstruction *float_mode_value = instruction->mode_value->child;
FloatMode float_mode_scalar;
if (!ir_resolve_float_mode(ira, float_mode_value, &float_mode_scalar))
return ira->codegen->invalid_instruction;
AstNode *source_node = instruction->base.source_node;
if (*fast_math_set_node_ptr) {
ErrorMsg *msg = ir_add_error_node(ira, source_node,
buf_sprintf("float mode set twice for same scope"));
add_error_note(ira->codegen, msg, *fast_math_set_node_ptr, buf_sprintf("first set here"));
return ira->codegen->invalid_instruction;
}
*fast_math_set_node_ptr = source_node;
*fast_math_on_ptr = (float_mode_scalar == FloatModeOptimized);
return ir_const_void(ira, &instruction->base);
}
static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira,
IrInstructionSliceType *slice_type_instruction)
{
Error err;
uint32_t align_bytes = 0;
if (slice_type_instruction->align_value != nullptr) {
if (!ir_resolve_align(ira, slice_type_instruction->align_value->child, &align_bytes))
return ira->codegen->invalid_instruction;
}
ZigType *child_type = ir_resolve_type(ira, slice_type_instruction->child_type->child);
if (type_is_invalid(child_type))
return ira->codegen->invalid_instruction;
bool is_const = slice_type_instruction->is_const;
bool is_volatile = slice_type_instruction->is_volatile;
bool is_allow_zero = slice_type_instruction->is_allow_zero;
switch (child_type->id) {
case ZigTypeIdInvalid: // handled above
zig_unreachable();
case ZigTypeIdUnreachable:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
ir_add_error_node(ira, slice_type_instruction->base.source_node,
buf_sprintf("slice of type '%s' not allowed", buf_ptr(&child_type->name)));
return ira->codegen->invalid_instruction;
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdInt:
case ZigTypeIdFloat:
case ZigTypeIdPointer:
case ZigTypeIdArray:
case ZigTypeIdStruct:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdBoundFn:
case ZigTypeIdPromise:
case ZigTypeIdVector:
{
if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type,
is_const, is_volatile, PtrLenUnknown, align_bytes, 0, 0, is_allow_zero);
ZigType *result_type = get_slice_type(ira->codegen, slice_ptr_type);
return ir_const_type(ira, &slice_type_instruction->base, result_type);
}
}
zig_unreachable();
}
static IrInstruction *ir_analyze_instruction_global_asm(IrAnalyze *ira, IrInstructionGlobalAsm *instruction) {
buf_append_char(&ira->codegen->global_asm, '\n');
buf_append_buf(&ira->codegen->global_asm, instruction->asm_code);
return ir_const_void(ira, &instruction->base);
}
static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAsm *asm_instruction) {
assert(asm_instruction->base.source_node->type == NodeTypeAsmExpr);
AstNodeAsmExpr *asm_expr = &asm_instruction->base.source_node->data.asm_expr;
if (!ir_emit_global_runtime_side_effect(ira, &asm_instruction->base))
return ira->codegen->invalid_instruction;
// TODO validate the output types and variable types
IrInstruction **input_list = allocate<IrInstruction *>(asm_expr->input_list.length);
IrInstruction **output_types = allocate<IrInstruction *>(asm_expr->output_list.length);
ZigType *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]->child;
return_type = ir_resolve_type(ira, output_types[i]);
if (type_is_invalid(return_type))
return ira->codegen->invalid_instruction;
}
}
for (size_t i = 0; i < asm_expr->input_list.length; i += 1) {
IrInstruction *const input_value = asm_instruction->input_list[i]->child;
if (type_is_invalid(input_value->value.type))
return ira->codegen->invalid_instruction;
if (instr_is_comptime(input_value) &&
(input_value->value.type->id == ZigTypeIdComptimeInt ||
input_value->value.type->id == ZigTypeIdComptimeFloat)) {
ir_add_error_node(ira, input_value->source_node,
buf_sprintf("expected sized integer or sized float, found %s", buf_ptr(&input_value->value.type->name)));
return ira->codegen->invalid_instruction;
}
input_list[i] = input_value;
}
IrInstruction *result = ir_build_asm(&ira->new_irb,
asm_instruction->base.scope, asm_instruction->base.source_node,
asm_instruction->asm_template, asm_instruction->token_list, asm_instruction->token_list_len,
input_list, output_types, asm_instruction->output_vars, asm_instruction->return_count,
asm_instruction->has_side_effects);
result->value.type = return_type;
return result;
}
static IrInstruction *ir_analyze_instruction_array_type(IrAnalyze *ira,
IrInstructionArrayType *array_type_instruction)
{
Error err;
IrInstruction *size_value = array_type_instruction->size->child;
uint64_t size;
if (!ir_resolve_usize(ira, size_value, &size))
return ira->codegen->invalid_instruction;
IrInstruction *child_type_value = array_type_instruction->child_type->child;
ZigType *child_type = ir_resolve_type(ira, child_type_value);
if (type_is_invalid(child_type))
return ira->codegen->invalid_instruction;
switch (child_type->id) {
case ZigTypeIdInvalid: // handled above
zig_unreachable();
case ZigTypeIdUnreachable:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
ir_add_error_node(ira, array_type_instruction->base.source_node,
buf_sprintf("array of type '%s' not allowed", buf_ptr(&child_type->name)));
return ira->codegen->invalid_instruction;
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdInt:
case ZigTypeIdFloat:
case ZigTypeIdPointer:
case ZigTypeIdArray:
case ZigTypeIdStruct:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdBoundFn:
case ZigTypeIdPromise:
case ZigTypeIdVector:
{
if ((err = ensure_complete_type(ira->codegen, child_type)))
return ira->codegen->invalid_instruction;
ZigType *result_type = get_array_type(ira->codegen, child_type, size);
return ir_const_type(ira, &array_type_instruction->base, result_type);
}
}
zig_unreachable();
}
static IrInstruction *ir_analyze_instruction_promise_type(IrAnalyze *ira, IrInstructionPromiseType *instruction) {
ZigType *promise_type;
if (instruction->payload_type == nullptr) {
promise_type = ira->codegen->builtin_types.entry_promise;
} else {
ZigType *payload_type = ir_resolve_type(ira, instruction->payload_type->child);
if (type_is_invalid(payload_type))
return ira->codegen->invalid_instruction;
promise_type = get_promise_type(ira->codegen, payload_type);
}
return ir_const_type(ira, &instruction->base, promise_type);
}
static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira,
IrInstructionSizeOf *size_of_instruction)
{
Error err;
IrInstruction *type_value = size_of_instruction->type_value->child;
ZigType *type_entry = ir_resolve_type(ira, type_value);
if ((err = ensure_complete_type(ira->codegen, type_entry)))
return ira->codegen->invalid_instruction;
switch (type_entry->id) {
case ZigTypeIdInvalid: // handled above
zig_unreachable();
case ZigTypeIdUnreachable:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
ir_add_error_node(ira, type_value->source_node,
buf_sprintf("no size available for type '%s'", buf_ptr(&type_entry->name)));
return ira->codegen->invalid_instruction;
case ZigTypeIdMetaType:
case ZigTypeIdEnumLiteral:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdInt:
case ZigTypeIdFloat:
case ZigTypeIdPointer:
case ZigTypeIdArray:
case ZigTypeIdStruct:
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdPromise:
case ZigTypeIdVector:
{
uint64_t size_in_bytes = type_size(ira->codegen, type_entry);
return ir_const_unsigned(ira, &size_of_instruction->base, size_in_bytes);
}
}
zig_unreachable();
}
static IrInstruction *ir_analyze_test_non_null(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *value) {
ZigType *type_entry = value->value.type;
if (type_entry->id == ZigTypeIdOptional) {
if (instr_is_comptime(value)) {
ConstExprValue *maybe_val = ir_resolve_const(ira, value, UndefBad);
if (!maybe_val)
return ira->codegen->invalid_instruction;
return ir_const_bool(ira, source_inst, !optional_value_is_null(maybe_val));
}
IrInstruction *result = ir_build_test_nonnull(&ira->new_irb,
source_inst->scope, source_inst->source_node, value);
result->value.type = ira->codegen->builtin_types.entry_bool;
return result;
} else if (type_entry->id == ZigTypeIdNull) {
return ir_const_bool(ira, source_inst, false);
} else {
return ir_const_bool(ira, source_inst, true);
}
}
static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrInstructionTestNonNull *instruction) {
IrInstruction *value = instruction->value->child;
if (type_is_invalid(value->value.type))
return ira->codegen->invalid_instruction;
return ir_analyze_test_non_null(ira, &instruction->base, value);
}
static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *base_ptr, bool safety_check_on)
{
ZigType *ptr_type = base_ptr->value.type;
assert(ptr_type->id == ZigTypeIdPointer);
ZigType *type_entry = ptr_type->data.pointer.child_type;
if (type_is_invalid(type_entry))
return ira->codegen->invalid_instruction;
if (type_entry->id != ZigTypeIdOptional) {
ir_add_error_node(ira, base_ptr->source_node,
buf_sprintf("expected optional type, found '%s'", buf_ptr(&type_entry->name)));
return ira->codegen->invalid_instruction;
}
ZigType *child_type = type_entry->data.maybe.child_type;
ZigType *result_type = get_pointer_to_type_extra(ira->codegen, child_type,
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0, false);
if (instr_is_comptime(base_ptr)) {
ConstExprValue *val = ir_resolve_const(ira, base_ptr, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
ConstExprValue *maybe_val = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node);
if (maybe_val == nullptr)
return ira->codegen->invalid_instruction;
if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) {
if (optional_value_is_null(maybe_val)) {
ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null"));
return ira->codegen->invalid_instruction;
}
IrInstruction *result = ir_const(ira, source_instr, result_type);
ConstExprValue *out_val = &result->value;
out_val->data.x_ptr.special = ConstPtrSpecialRef;
out_val->data.x_ptr.mut = val->data.x_ptr.mut;
if (types_have_same_zig_comptime_repr(type_entry, child_type)) {
out_val->data.x_ptr.data.ref.pointee = maybe_val;
} else {
out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_optional;
}
return result;
}
}
IrInstruction *result = ir_build_optional_unwrap_ptr(&ira->new_irb, source_instr->scope,
source_instr->source_node, base_ptr, safety_check_on);
result->value.type = result_type;
return result;
}
static IrInstruction *ir_analyze_instruction_optional_unwrap_ptr(IrAnalyze *ira,
IrInstructionOptionalUnwrapPtr *instruction)
{
IrInstruction *base_ptr = instruction->base_ptr->child;
if (type_is_invalid(base_ptr->value.type))
return ira->codegen->invalid_instruction;
return ir_analyze_unwrap_optional_payload(ira, &instruction->base, base_ptr, instruction->safety_check_on);
}
static IrInstruction *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstructionCtz *ctz_instruction) {
IrInstruction *value = ctz_instruction->value->child;
if (type_is_invalid(value->value.type)) {
return ira->codegen->invalid_instruction;
} else if (value->value.type->id == ZigTypeIdInt) {
ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen,
value->value.type->data.integral.bit_count);
if (value->value.special != ConstValSpecialRuntime) {
size_t result_usize = bigint_ctz(&value->value.data.x_bigint,
value->value.type->data.integral.bit_count);
IrInstruction *result = ir_const(ira, &ctz_instruction->base, return_type);
bigint_init_unsigned(&result->value.data.x_bigint, result_usize);
return result;
}
IrInstruction *result = ir_build_ctz(&ira->new_irb,
ctz_instruction->base.scope, ctz_instruction->base.source_node, value);
result->value.type = return_type;
return result;
} 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->invalid_instruction;
}
}
static IrInstruction *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstructionClz *clz_instruction) {
IrInstruction *value = clz_instruction->value->child;
if (type_is_invalid(value->value.type)) {
return ira->codegen->invalid_instruction;
} else if (value->value.type->id == ZigTypeIdInt) {
ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen,
value->value.type->data.integral.bit_count);
if (value->value.special != ConstValSpecialRuntime) {
size_t result_usize = bigint_clz(&value->value.data.x_bigint,
value->value.type->data.integral.bit_count);
IrInstruction *result = ir_const(ira, &clz_instruction->base, return_type);
bigint_init_unsigned(&result->value.data.x_bigint, result_usize);
return result;
}
IrInstruction *result = ir_build_clz(&ira->new_irb,
clz_instruction->base.scope, clz_instruction->base.source_node, value);
result->value.type = return_type;
return result;
} 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->invalid_instruction;
}
}
static IrInstruction *ir_analyze_instruction_pop_count(IrAnalyze *ira, IrInstructionPopCount *instruction) {
IrInstruction *value = instruction->value->child;
if (type_is_invalid(value->value.type))
return ira->codegen->invalid_instruction;
if (value->value.type->id != ZigTypeIdInt && value->value.type->id != ZigTypeIdComptimeInt) {
ir_add_error(ira, value,
buf_sprintf("expected integer 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;
if (bigint_cmp_zero(&val->data.x_bigint) != CmpLT) {
size_t result = bigint_popcount_unsigned(&val->data.x_bigint);
return ir_const_unsigned(ira, &instruction->base, result);
}
if (value->value.type->id == ZigTypeIdComptimeInt) {
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &val->data.x_bigint, 10);
ir_add_error(ira, &instruction->base,
buf_sprintf("@popCount on negative %s value %s",
buf_ptr(&value->value.type->name), buf_ptr(val_buf)));
return ira->codegen->invalid_instruction;
}
size_t result = bigint_popcount_signed(&val->data.x_bigint, value->value.type->data.integral.bit_count);
return ir_const_unsigned(ira, &instruction->base, result);
}
IrInstruction *result = ir_build_pop_count(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, value);
result->value.type = get_smallest_unsigned_int_type(ira->codegen, value->value.type->data.integral.bit_count);
return result;
}
static IrInstruction *ir_analyze_union_tag(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value) {
if (type_is_invalid(value->value.type))
return ira->codegen->invalid_instruction;
if (value->value.type->id == ZigTypeIdEnum) {
return value;
}
if (value->value.type->id != ZigTypeIdUnion) {
ir_add_error(ira, value,
buf_sprintf("expected enum or union type, found '%s'", buf_ptr(&value->value.type->name)));
return ira->codegen->invalid_instruction;
}
if (!value->value.type->data.unionation.have_explicit_tag_type && !source_instr->is_gen) {
ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("union has no associated enum"));
if (value->value.type->data.unionation.decl_node != nullptr) {
add_error_note(ira->codegen, msg, value->value.type->data.unionation.decl_node,
buf_sprintf("declared here"));
}
return ira->codegen->invalid_instruction;
}
ZigType *tag_type = value->value.type->data.unionation.tag_type;
assert(tag_type->id == ZigTypeIdEnum);
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 = tag_type;
const_instruction->base.value.special = ConstValSpecialStatic;
bigint_init_bigint(&const_instruction->base.value.data.x_enum_tag, &val->data.x_union.tag);
return &const_instruction->base;
}
IrInstruction *result = ir_build_union_tag(&ira->new_irb, source_instr->scope, source_instr->source_node, value);
result->value.type = tag_type;
return result;
}
static IrInstruction *ir_analyze_instruction_switch_br(IrAnalyze *ira,
IrInstructionSwitchBr *switch_br_instruction)
{
IrInstruction *target_value = switch_br_instruction->target_value->child;
if (type_is_invalid(target_value->value.type))
return ir_unreach_error(ira);
if (switch_br_instruction->switch_prongs_void != nullptr) {
if (type_is_invalid(switch_br_instruction->switch_prongs_void->child->value.type)) {
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->child, &is_comptime))
return ira->codegen->invalid_instruction;
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->child;
if (type_is_invalid(case_value->value.type))
return ir_unreach_error(ira);
if (case_value->value.type->id == ZigTypeIdEnum) {
case_value = ir_analyze_union_tag(ira, &switch_br_instruction->base, case_value);
if (type_is_invalid(case_value->value.type))
return ir_unreach_error(ira);
}
IrInstruction *casted_case_value = ir_implicit_cast(ira, case_value, target_value->value.type);
if (type_is_invalid(casted_case_value->value.type))
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(ira->codegen, 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);
IrInstruction *result = ir_build_br(&ira->new_irb,
switch_br_instruction->base.scope, switch_br_instruction->base.source_node,
new_dest_block, nullptr);
result->value.type = ira->codegen->builtin_types.entry_unreachable;
return ir_finish_anal(ira, result);
}
}
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->child;
if (type_is_invalid(new_value->value.type))
continue;
if (new_value->value.type->id == ZigTypeIdEnum) {
new_value = ir_analyze_union_tag(ira, &switch_br_instruction->base, new_value);
if (type_is_invalid(new_value->value.type))
continue;
}
IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, target_value->value.type);
if (type_is_invalid(casted_new_value->value.type))
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];
if (new_case->value == ira->codegen->invalid_instruction)
return ir_unreach_error(ira);
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);
IrInstruction *result = ir_build_switch_br(&ira->new_irb,
switch_br_instruction->base.scope, switch_br_instruction->base.source_node,
target_value, new_else_block, case_count, cases, nullptr, nullptr);
result->value.type = ira->codegen->builtin_types.entry_unreachable;
return ir_finish_anal(ira, result);
}
static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira,
IrInstructionSwitchTarget *switch_target_instruction)
{
Error err;
IrInstruction *target_value_ptr = switch_target_instruction->target_value_ptr->child;
if (type_is_invalid(target_value_ptr->value.type))
return ira->codegen->invalid_instruction;
if (target_value_ptr->value.type->id == ZigTypeIdMetaType) {
assert(instr_is_comptime(target_value_ptr));
ZigType *ptr_type = target_value_ptr->value.data.x_type;
assert(ptr_type->id == ZigTypeIdPointer);
return ir_const_type(ira, &switch_target_instruction->base, ptr_type->data.pointer.child_type);
}
ZigType *target_type = target_value_ptr->value.type->data.pointer.child_type;
ConstExprValue *pointee_val = nullptr;
if (instr_is_comptime(target_value_ptr)) {
pointee_val = const_ptr_pointee(ira, ira->codegen, &target_value_ptr->value, target_value_ptr->source_node);
if (pointee_val == nullptr)
return ira->codegen->invalid_instruction;
if (pointee_val->special == ConstValSpecialRuntime)
pointee_val = nullptr;
}
if ((err = ensure_complete_type(ira->codegen, target_type)))
return ira->codegen->invalid_instruction;
switch (target_type->id) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdInt:
case ZigTypeIdFloat:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdPointer:
case ZigTypeIdPromise:
case ZigTypeIdFn:
case ZigTypeIdErrorSet: {
if (pointee_val) {
IrInstruction *result = ir_const(ira, &switch_target_instruction->base, nullptr);
copy_const_val(&result->value, pointee_val, true);
result->value.type = target_type;
return result;
}
IrInstruction *result = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr);
result->value.type = target_type;
return result;
}
case ZigTypeIdUnion: {
AstNode *decl_node = target_type->data.unionation.decl_node;
if (!decl_node->data.container_decl.auto_enum &&
decl_node->data.container_decl.init_arg_expr == nullptr)
{
ErrorMsg *msg = ir_add_error(ira, target_value_ptr,
buf_sprintf("switch on union which has no attached enum"));
add_error_note(ira->codegen, msg, decl_node,
buf_sprintf("consider 'union(enum)' here"));
return ira->codegen->invalid_instruction;
}
ZigType *tag_type = target_type->data.unionation.tag_type;
assert(tag_type != nullptr);
assert(tag_type->id == ZigTypeIdEnum);
if (pointee_val) {
IrInstruction *result = ir_const(ira, &switch_target_instruction->base, tag_type);
bigint_init_bigint(&result->value.data.x_enum_tag, &pointee_val->data.x_union.tag);
return result;
}
if (tag_type->data.enumeration.src_field_count == 1) {
IrInstruction *result = ir_const(ira, &switch_target_instruction->base, tag_type);
TypeEnumField *only_field = &tag_type->data.enumeration.fields[0];
bigint_init_bigint(&result->value.data.x_enum_tag, &only_field->value);
return result;
}
IrInstruction *union_value = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr);
union_value->value.type = target_type;
IrInstruction *union_tag_inst = ir_build_union_tag(&ira->new_irb, switch_target_instruction->base.scope,
switch_target_instruction->base.source_node, union_value);
union_tag_inst->value.type = tag_type;
return union_tag_inst;
}
case ZigTypeIdEnum: {
if ((err = type_resolve(ira->codegen, target_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
if (target_type->data.enumeration.src_field_count < 2) {
TypeEnumField *only_field = &target_type->data.enumeration.fields[0];
IrInstruction *result = ir_const(ira, &switch_target_instruction->base, target_type);
bigint_init_bigint(&result->value.data.x_enum_tag, &only_field->value);
return result;
}
if (pointee_val) {
IrInstruction *result = ir_const(ira, &switch_target_instruction->base, target_type);
bigint_init_bigint(&result->value.data.x_enum_tag, &pointee_val->data.x_enum_tag);
return result;
}
IrInstruction *enum_value = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr);
enum_value->value.type = target_type;
return enum_value;
}
case ZigTypeIdErrorUnion:
case ZigTypeIdUnreachable:
case ZigTypeIdArray:
case ZigTypeIdStruct:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdOptional:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
case ZigTypeIdVector:
ir_add_error(ira, &switch_target_instruction->base,
buf_sprintf("invalid switch target type '%s'", buf_ptr(&target_type->name)));
return ira->codegen->invalid_instruction;
}
zig_unreachable();
}
static IrInstruction *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstructionSwitchVar *instruction) {
IrInstruction *target_value_ptr = instruction->target_value_ptr->child;
if (type_is_invalid(target_value_ptr->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *prong_value = instruction->prong_value->child;
if (type_is_invalid(prong_value->value.type))
return ira->codegen->invalid_instruction;
assert(target_value_ptr->value.type->id == ZigTypeIdPointer);
ZigType *target_type = target_value_ptr->value.type->data.pointer.child_type;
if (target_type->id == ZigTypeIdUnion) {
ZigType *enum_type = target_type->data.unionation.tag_type;
assert(enum_type != nullptr);
assert(enum_type->id == ZigTypeIdEnum);
IrInstruction *casted_prong_value = ir_implicit_cast(ira, prong_value, enum_type);
if (type_is_invalid(casted_prong_value->value.type))
return ira->codegen->invalid_instruction;
ConstExprValue *prong_val = ir_resolve_const(ira, casted_prong_value, UndefBad);
if (!prong_val)
return ira->codegen->invalid_instruction;
TypeUnionField *field = find_union_field_by_tag(target_type, &prong_val->data.x_enum_tag);
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->invalid_instruction;
ConstExprValue *pointee_val = const_ptr_pointee(ira, ira->codegen, target_val_ptr, instruction->base.source_node);
if (pointee_val == nullptr)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_const(ira, &instruction->base,
get_pointer_to_type(ira->codegen, field->type_entry,
target_val_ptr->type->data.pointer.is_const));
ConstExprValue *out_val = &result->value;
out_val->data.x_ptr.special = ConstPtrSpecialRef;
out_val->data.x_ptr.mut = target_val_ptr->data.x_ptr.mut;
out_val->data.x_ptr.data.ref.pointee = pointee_val->data.x_union.payload;
return result;
}
IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb,
instruction->base.scope, instruction->base.source_node, target_value_ptr, field);
result->value.type = get_pointer_to_type(ira->codegen, field->type_entry,
target_value_ptr->value.type->data.pointer.is_const);
return result;
} else {
ir_add_error(ira, &instruction->base,
buf_sprintf("switch on type '%s' provides no expression parameter", buf_ptr(&target_type->name)));
return ira->codegen->invalid_instruction;
}
}
static IrInstruction *ir_analyze_instruction_union_tag(IrAnalyze *ira, IrInstructionUnionTag *instruction) {
IrInstruction *value = instruction->value->child;
return ir_analyze_union_tag(ira, &instruction->base, value);
}
static IrInstruction *ir_analyze_instruction_import(IrAnalyze *ira, IrInstructionImport *import_instruction) {
Error err;
IrInstruction *name_value = import_instruction->name->child;
Buf *import_target_str = ir_resolve_str(ira, name_value);
if (!import_target_str)
return ira->codegen->invalid_instruction;
AstNode *source_node = import_instruction->base.source_node;
ZigType *import = source_node->owner;
Buf *import_target_path;
Buf *search_dir;
assert(import->data.structure.root_struct->package);
ZigPackage *target_package;
auto package_entry = import->data.structure.root_struct->package->package_table.maybe_get(import_target_str);
SourceKind source_kind;
if (package_entry) {
target_package = package_entry->value;
import_target_path = &target_package->root_src_path;
search_dir = &target_package->root_src_dir;
source_kind = SourceKindPkgMain;
} else {
// try it as a filename
target_package = import->data.structure.root_struct->package;
import_target_path = import_target_str;
// search relative to importing file
search_dir = buf_alloc();
os_path_dirname(import->data.structure.root_struct->path, search_dir);
source_kind = SourceKindNonRoot;
}
Buf full_path = BUF_INIT;
os_path_join(search_dir, import_target_path, &full_path);
Buf *import_code = buf_alloc();
Buf *resolved_path = buf_alloc();
Buf *resolve_paths[] = { &full_path, };
*resolved_path = os_path_resolve(resolve_paths, 1);
auto import_entry = ira->codegen->import_table.maybe_get(resolved_path);
if (import_entry) {
return ir_const_type(ira, &import_instruction->base, import_entry->value);
}
if (source_kind == SourceKindNonRoot) {
ZigPackage *cur_scope_pkg = scope_package(import_instruction->base.scope);
Buf *pkg_root_src_dir = &cur_scope_pkg->root_src_dir;
Buf resolved_root_src_dir = os_path_resolve(&pkg_root_src_dir, 1);
if (!buf_starts_with_buf(resolved_path, &resolved_root_src_dir)) {
ir_add_error_node(ira, source_node,
buf_sprintf("import of file outside package path: '%s'",
buf_ptr(import_target_path)));
return ira->codegen->invalid_instruction;
}
}
if ((err = file_fetch(ira->codegen, resolved_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->invalid_instruction;
} 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->invalid_instruction;
}
}
ZigType *target_import = add_source_file(ira->codegen, target_package, resolved_path, import_code, source_kind);
return ir_const_type(ira, &import_instruction->base, target_import);
}
static IrInstruction *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionRef *ref_instruction) {
IrInstruction *value = ref_instruction->value->child;
if (type_is_invalid(value->value.type))
return ira->codegen->invalid_instruction;
return ir_get_ref(ira, &ref_instruction->base, value, ref_instruction->is_const, ref_instruction->is_volatile);
}
static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrInstruction *instruction,
ZigType *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields)
{
Error err;
assert(container_type->id == ZigTypeIdUnion);
if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown)))
return ira->codegen->invalid_instruction;
if (instr_field_count != 1) {
ir_add_error(ira, instruction,
buf_sprintf("union initialization expects exactly one field"));
return ira->codegen->invalid_instruction;
}
IrInstructionContainerInitFieldsField *field = &fields[0];
IrInstruction *field_value = field->value->child;
if (type_is_invalid(field_value->value.type))
return ira->codegen->invalid_instruction;
TypeUnionField *type_field = find_union_type_field(container_type, field->name);
if (!type_field) {
ir_add_error_node(ira, field->source_node,
buf_sprintf("no member named '%s' in union '%s'",
buf_ptr(field->name), buf_ptr(&container_type->name)));
return ira->codegen->invalid_instruction;
}
if (type_is_invalid(type_field->type_entry))
return ira->codegen->invalid_instruction;
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->invalid_instruction;
if ((err = type_resolve(ira->codegen, casted_field_value->value.type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope)
|| type_requires_comptime(ira->codegen, container_type) == ReqCompTimeYes;
if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime ||
!type_has_bits(casted_field_value->value.type))
{
ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk);
if (!field_val)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_const(ira, instruction, container_type);
ConstExprValue *out_val = &result->value;
out_val->data.x_union.payload = field_val;
out_val->data.x_union.tag = type_field->enum_field->value;
out_val->parent.id = ConstParentIdUnion;
out_val->parent.data.p_union.union_val = out_val;
return result;
}
IrInstruction *new_instruction = ir_build_union_init(&ira->new_irb,
instruction->scope, instruction->source_node,
container_type, type_field, casted_field_value);
new_instruction->value.type = container_type;
ir_add_alloca(ira, new_instruction, container_type);
return new_instruction;
}
static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruction *instruction,
ZigType *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields)
{
Error err;
if (container_type->id == ZigTypeIdUnion) {
return ir_analyze_container_init_fields_union(ira, instruction, container_type, instr_field_count, fields);
}
if (container_type->id != ZigTypeIdStruct || 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->invalid_instruction;
}
if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown)))
return ira->codegen->invalid_instruction;
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)
|| type_requires_comptime(ira->codegen, container_type) == ReqCompTimeYes;
ConstExprValue const_val = {};
const_val.special = ConstValSpecialStatic;
const_val.type = container_type;
// const_val.global_refs = allocate<ConstGlobalRefs>(1);
const_val.data.x_struct.fields = create_const_vals(actual_field_count);
for (size_t i = 0; i < instr_field_count; i += 1) {
IrInstructionContainerInitFieldsField *field = &fields[i];
IrInstruction *field_value = field->value->child;
if (type_is_invalid(field_value->value.type))
return ira->codegen->invalid_instruction;
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 struct '%s'",
buf_ptr(field->name), buf_ptr(&container_type->name)));
return ira->codegen->invalid_instruction;
}
if (type_is_invalid(type_field->type_entry))
return ira->codegen->invalid_instruction;
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->invalid_instruction;
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"));
return ira->codegen->invalid_instruction;
}
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->invalid_instruction;
copy_const_val(&const_val.data.x_struct.fields[field_index], field_val, true);
} 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->invalid_instruction;
if (const_val.special == ConstValSpecialStatic) {
IrInstruction *result = ir_const(ira, instruction, nullptr);
ConstExprValue *out_val = &result->value;
copy_const_val(out_val, &const_val, false);
out_val->type = container_type;
for (size_t i = 0; i < instr_field_count; i += 1) {
ConstExprValue *field_val = &out_val->data.x_struct.fields[i];
ConstParent *parent = get_const_val_parent(ira->codegen, field_val);
if (parent != nullptr) {
parent->id = ConstParentIdStruct;
parent->data.p_struct.field_index = i;
parent->data.p_struct.struct_val = out_val;
}
}
return result;
}
if (is_comptime) {
ir_add_error_node(ira, first_non_const_instruction->source_node,
buf_sprintf("unable to evaluate constant expression"));
return ira->codegen->invalid_instruction;
}
IrInstruction *new_instruction = ir_build_struct_init(&ira->new_irb,
instruction->scope, instruction->source_node,
container_type, actual_field_count, new_fields);
new_instruction->value.type = container_type;
ir_add_alloca(ira, new_instruction, container_type);
return new_instruction;
}
static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira,
IrInstructionContainerInitList *instruction)
{
Error err;
ZigType *container_type = ir_resolve_type(ira, instruction->container_type->child);
if (type_is_invalid(container_type))
return ira->codegen->invalid_instruction;
size_t elem_count = instruction->item_count;
if (container_type->id == ZigTypeIdStruct && !is_slice(container_type) && elem_count == 0) {
return ir_analyze_container_init_fields(ira, &instruction->base, container_type,
0, nullptr);
} else if (is_slice(container_type) || container_type->id == ZigTypeIdArray) {
// array is same as slice init but we make a compile error if the length is wrong
ZigType *child_type;
if (container_type->id == ZigTypeIdArray) {
child_type = container_type->data.array.child_type;
if (container_type->data.array.len != elem_count) {
ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count);
ir_add_error(ira, &instruction->base,
buf_sprintf("expected %s literal, found %s literal",
buf_ptr(&container_type->name), buf_ptr(&literal_type->name)));
return ira->codegen->invalid_instruction;
}
} else {
ZigType *pointer_type = container_type->data.structure.fields[slice_ptr_index].type_entry;
assert(pointer_type->id == ZigTypeIdPointer);
child_type = pointer_type->data.pointer.child_type;
}
if ((err = type_resolve(ira->codegen, child_type, ResolveStatusSizeKnown))) {
return ira->codegen->invalid_instruction;
}
ZigType *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.global_refs = allocate<ConstGlobalRefs>(1);
const_val.data.x_array.data.s_none.elements = create_const_vals(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]->child;
if (type_is_invalid(arg_value->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_arg = ir_implicit_cast(ira, arg_value, child_type);
if (casted_arg == ira->codegen->invalid_instruction)
return ira->codegen->invalid_instruction;
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->invalid_instruction;
copy_const_val(&const_val.data.x_array.data.s_none.elements[i], elem_val, true);
} else {
first_non_const_instruction = casted_arg;
const_val.special = ConstValSpecialRuntime;
}
}
}
if (const_val.special == ConstValSpecialStatic) {
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
ConstExprValue *out_val = &result->value;
copy_const_val(out_val, &const_val, false);
result->value.type = fixed_size_array_type;
for (size_t i = 0; i < elem_count; i += 1) {
ConstExprValue *elem_val = &out_val->data.x_array.data.s_none.elements[i];
ConstParent *parent = get_const_val_parent(ira->codegen, elem_val);
if (parent != nullptr) {
parent->id = ConstParentIdArray;
parent->data.p_array.array_val = out_val;
parent->data.p_array.elem_index = i;
}
}
return result;
}
if (is_comptime) {
ir_add_error_node(ira, first_non_const_instruction->source_node,
buf_sprintf("unable to evaluate constant expression"));
return ira->codegen->invalid_instruction;
}
IrInstruction *new_instruction = ir_build_container_init_list(&ira->new_irb,
instruction->base.scope, instruction->base.source_node,
nullptr, elem_count, new_items);
new_instruction->value.type = fixed_size_array_type;
ir_add_alloca(ira, new_instruction, fixed_size_array_type);
return new_instruction;
} else if (container_type->id == ZigTypeIdVoid) {
if (elem_count != 0) {
ir_add_error_node(ira, instruction->base.source_node,
buf_sprintf("void expression expects no arguments"));
return ira->codegen->invalid_instruction;
}
return ir_const_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->invalid_instruction;
}
}
static IrInstruction *ir_analyze_instruction_container_init_fields(IrAnalyze *ira,
IrInstructionContainerInitFields *instruction)
{
IrInstruction *container_type_value = instruction->container_type->child;
ZigType *container_type = ir_resolve_type(ira, container_type_value);
if (type_is_invalid(container_type))
return ira->codegen->invalid_instruction;
return ir_analyze_container_init_fields(ira, &instruction->base, container_type,
instruction->field_count, instruction->fields);
}
static IrInstruction *ir_analyze_instruction_compile_err(IrAnalyze *ira,
IrInstructionCompileErr *instruction)
{
IrInstruction *msg_value = instruction->msg->child;
Buf *msg_buf = ir_resolve_str(ira, msg_value);
if (!msg_buf)
return ira->codegen->invalid_instruction;
ErrorMsg *msg = ir_add_error(ira, &instruction->base, msg_buf);
emit_error_notes_for_ref_stack(ira->codegen, msg);
return ira->codegen->invalid_instruction;
}
static IrInstruction *ir_analyze_instruction_compile_log(IrAnalyze *ira, IrInstructionCompileLog *instruction) {
Buf buf = BUF_INIT;
fprintf(stderr, "| ");
for (size_t i = 0; i < instruction->msg_count; i += 1) {
IrInstruction *msg = instruction->msg_list[i]->child;
if (type_is_invalid(msg->value.type))
return ira->codegen->invalid_instruction;
buf_resize(&buf, 0);
render_const_value(ira->codegen, &buf, &msg->value);
const char *comma_str = (i != 0) ? ", " : "";
fprintf(stderr, "%s%s", comma_str, buf_ptr(&buf));
}
fprintf(stderr, "\n");
auto *expr = &instruction->base.source_node->data.fn_call_expr;
if (!expr->seen) {
// Here we bypass higher level functions such as ir_add_error because we do not want
// invalidate_exec to be called.
add_node_error(ira->codegen, instruction->base.source_node, buf_sprintf("found compile log statement"));
}
expr->seen = true;
return ir_const_void(ira, &instruction->base);
}
static IrInstruction *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstructionErrName *instruction) {
IrInstruction *value = instruction->value->child;
if (type_is_invalid(value->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_global_error_set);
if (type_is_invalid(casted_value->value.type))
return ira->codegen->invalid_instruction;
ZigType *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
true, false, PtrLenUnknown, 0, 0, 0, false);
ZigType *str_type = get_slice_type(ira->codegen, u8_ptr_type);
if (casted_value->value.special == ConstValSpecialStatic) {
ErrorTableEntry *err = casted_value->value.data.x_err_set;
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);
}
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
copy_const_val(&result->value, err->cached_error_name_val, true);
result->value.type = str_type;
return result;
}
ira->codegen->generate_error_name_table = true;
IrInstruction *result = ir_build_err_name(&ira->new_irb,
instruction->base.scope, instruction->base.source_node, value);
result->value.type = str_type;
return result;
}
static IrInstruction *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstructionTagName *instruction) {
Error err;
IrInstruction *target = instruction->target->child;
if (type_is_invalid(target->value.type))
return ira->codegen->invalid_instruction;
assert(target->value.type->id == ZigTypeIdEnum);
if (instr_is_comptime(target)) {
if ((err = type_resolve(ira->codegen, target->value.type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
TypeEnumField *field = find_enum_field_by_tag(target->value.type, &target->value.data.x_bigint);
ConstExprValue *array_val = create_const_str_lit(ira->codegen, field->name);
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
init_const_slice(ira->codegen, &result->value, array_val, 0, buf_len(field->name), true);
return result;
}
IrInstruction *result = ir_build_tag_name(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, target);
ZigType *u8_ptr_type = get_pointer_to_type_extra(
ira->codegen, ira->codegen->builtin_types.entry_u8,
true, false, PtrLenUnknown,
0, 0, 0, false);
result->value.type = get_slice_type(ira->codegen, u8_ptr_type);
return result;
}
static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira,
IrInstructionFieldParentPtr *instruction)
{
Error err;
IrInstruction *type_value = instruction->type_value->child;
ZigType *container_type = ir_resolve_type(ira, type_value);
if (type_is_invalid(container_type))
return ira->codegen->invalid_instruction;
IrInstruction *field_name_value = instruction->field_name->child;
Buf *field_name = ir_resolve_str(ira, field_name_value);
if (!field_name)
return ira->codegen->invalid_instruction;
IrInstruction *field_ptr = instruction->field_ptr->child;
if (type_is_invalid(field_ptr->value.type))
return ira->codegen->invalid_instruction;
if (container_type->id != ZigTypeIdStruct) {
ir_add_error(ira, type_value,
buf_sprintf("expected struct type, found '%s'", buf_ptr(&container_type->name)));
return ira->codegen->invalid_instruction;
}
if ((err = ensure_complete_type(ira->codegen, container_type)))
return ira->codegen->invalid_instruction;
TypeStructField *field = find_struct_type_field(container_type, field_name);
if (field == nullptr) {
ir_add_error(ira, field_name_value,
buf_sprintf("struct '%s' has no field '%s'",
buf_ptr(&container_type->name), buf_ptr(field_name)));
return ira->codegen->invalid_instruction;
}
if (field_ptr->value.type->id != ZigTypeIdPointer) {
ir_add_error(ira, field_ptr,
buf_sprintf("expected pointer, found '%s'", buf_ptr(&field_ptr->value.type->name)));
return ira->codegen->invalid_instruction;
}
bool is_packed = (container_type->data.structure.layout == ContainerLayoutPacked);
uint32_t field_ptr_align = is_packed ? 1 : get_abi_alignment(ira->codegen, field->type_entry);
uint32_t parent_ptr_align = is_packed ? 1 : get_abi_alignment(ira->codegen, container_type);
ZigType *field_ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry,
field_ptr->value.type->data.pointer.is_const,
field_ptr->value.type->data.pointer.is_volatile,
PtrLenSingle,
field_ptr_align, 0, 0, false);
IrInstruction *casted_field_ptr = ir_implicit_cast(ira, field_ptr, field_ptr_type);
if (type_is_invalid(casted_field_ptr->value.type))
return ira->codegen->invalid_instruction;
ZigType *result_type = get_pointer_to_type_extra(ira->codegen, container_type,
casted_field_ptr->value.type->data.pointer.is_const,
casted_field_ptr->value.type->data.pointer.is_volatile,
PtrLenSingle,
parent_ptr_align, 0, 0, false);
if (instr_is_comptime(casted_field_ptr)) {
ConstExprValue *field_ptr_val = ir_resolve_const(ira, casted_field_ptr, UndefBad);
if (!field_ptr_val)
return ira->codegen->invalid_instruction;
if (field_ptr_val->data.x_ptr.special != ConstPtrSpecialBaseStruct) {
ir_add_error(ira, field_ptr, buf_sprintf("pointer value not based on parent struct"));
return ira->codegen->invalid_instruction;
}
size_t ptr_field_index = field_ptr_val->data.x_ptr.data.base_struct.field_index;
if (ptr_field_index != field->src_index) {
ir_add_error(ira, &instruction->base,
buf_sprintf("field '%s' has index %" ZIG_PRI_usize " but pointer value is index %" ZIG_PRI_usize " of struct '%s'",
buf_ptr(field->name), field->src_index,
ptr_field_index, buf_ptr(&container_type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *result = ir_const(ira, &instruction->base, result_type);
ConstExprValue *out_val = &result->value;
out_val->data.x_ptr.special = ConstPtrSpecialRef;
out_val->data.x_ptr.data.ref.pointee = field_ptr_val->data.x_ptr.data.base_struct.struct_val;
out_val->data.x_ptr.mut = field_ptr_val->data.x_ptr.mut;
return result;
}
IrInstruction *result = ir_build_field_parent_ptr(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, type_value, field_name_value, casted_field_ptr, field);
result->value.type = result_type;
return result;
}
static TypeStructField *validate_byte_offset(IrAnalyze *ira,
IrInstruction *type_value,
IrInstruction *field_name_value,
size_t *byte_offset)
{
ZigType *container_type = ir_resolve_type(ira, type_value);
if (type_is_invalid(container_type))
return nullptr;
Error err;
if ((err = ensure_complete_type(ira->codegen, container_type)))
return nullptr;
Buf *field_name = ir_resolve_str(ira, field_name_value);
if (!field_name)
return nullptr;
if (container_type->id != ZigTypeIdStruct) {
ir_add_error(ira, type_value,
buf_sprintf("expected struct type, found '%s'", buf_ptr(&container_type->name)));
return nullptr;
}
TypeStructField *field = find_struct_type_field(container_type, field_name);
if (field == nullptr) {
ir_add_error(ira, field_name_value,
buf_sprintf("struct '%s' has no field '%s'",
buf_ptr(&container_type->name), buf_ptr(field_name)));
return nullptr;
}
if (!type_has_bits(field->type_entry)) {
ir_add_error(ira, field_name_value,
buf_sprintf("zero-bit field '%s' in struct '%s' has no offset",
buf_ptr(field_name), buf_ptr(&container_type->name)));
return nullptr;
}
*byte_offset = field->offset;
return field;
}
static IrInstruction *ir_analyze_instruction_byte_offset_of(IrAnalyze *ira,
IrInstructionByteOffsetOf *instruction)
{
IrInstruction *type_value = instruction->type_value->child;
if (type_is_invalid(type_value->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *field_name_value = instruction->field_name->child;
size_t byte_offset = 0;
if (!validate_byte_offset(ira, type_value, field_name_value, &byte_offset))
return ira->codegen->invalid_instruction;
return ir_const_unsigned(ira, &instruction->base, byte_offset);
}
static IrInstruction *ir_analyze_instruction_bit_offset_of(IrAnalyze *ira,
IrInstructionBitOffsetOf *instruction)
{
IrInstruction *type_value = instruction->type_value->child;
if (type_is_invalid(type_value->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *field_name_value = instruction->field_name->child;
size_t byte_offset = 0;
TypeStructField *field = nullptr;
if (!(field = validate_byte_offset(ira, type_value, field_name_value, &byte_offset)))
return ira->codegen->invalid_instruction;
size_t bit_offset = byte_offset * 8 + field->bit_offset_in_host;
return ir_const_unsigned(ira, &instruction->base, bit_offset);
}
static IrInstruction *ir_error_dependency_loop(IrAnalyze *ira, IrInstruction *source_instr) {
ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("dependency loop detected"));
emit_error_notes_for_ref_stack(ira->codegen, msg);
return ira->codegen->invalid_instruction;
}
static void ensure_field_index(ZigType *type, const char *field_name, size_t index) {
Buf *field_name_buf;
assert(type != nullptr && !type_is_invalid(type));
// Check for our field by creating a buffer in place then using the comma operator to free it so that we don't
// leak memory in debug mode.
assert(find_struct_type_field(type, field_name_buf = buf_create_from_str(field_name))->src_index == index &&
(buf_deinit(field_name_buf), true));
}
static ZigType *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, ZigType *root) {
Error err;
ConstExprValue *type_info_var = get_builtin_value(ira->codegen, "TypeInfo");
assert(type_info_var->type->id == ZigTypeIdMetaType);
ZigType *type_info_type = type_info_var->data.x_type;
assert(type_info_type->id == ZigTypeIdUnion);
if ((err = type_resolve(ira->codegen, type_info_type, ResolveStatusSizeKnown))) {
zig_unreachable();
}
if (type_name == nullptr && root == nullptr)
return type_info_type;
else if (type_name == nullptr)
return root;
ZigType *root_type = (root == nullptr) ? type_info_type : root;
ScopeDecls *type_info_scope = get_container_scope(root_type);
assert(type_info_scope != nullptr);
Buf field_name = BUF_INIT;
buf_init_from_str(&field_name, type_name);
auto entry = type_info_scope->decl_table.get(&field_name);
buf_deinit(&field_name);
TldVar *tld = (TldVar *)entry;
assert(tld->base.id == TldIdVar);
ZigVar *var = tld->var;
if ((err = ensure_complete_type(ira->codegen, var->const_value->type)))
return ira->codegen->builtin_types.entry_invalid;
assert(var->const_value->type->id == ZigTypeIdMetaType);
return var->const_value->data.x_type;
}
static Error ir_make_type_info_defs(IrAnalyze *ira, IrInstruction *source_instr, ConstExprValue *out_val,
ScopeDecls *decls_scope)
{
Error err;
ZigType *type_info_definition_type = ir_type_info_get_type(ira, "Definition", nullptr);
if ((err = type_resolve(ira->codegen, type_info_definition_type, ResolveStatusSizeKnown)))
return err;
ensure_field_index(type_info_definition_type, "name", 0);
ensure_field_index(type_info_definition_type, "is_pub", 1);
ensure_field_index(type_info_definition_type, "data", 2);
ZigType *type_info_definition_data_type = ir_type_info_get_type(ira, "Data", type_info_definition_type);
if ((err = ensure_complete_type(ira->codegen, type_info_definition_data_type)))
return err;
ZigType *type_info_fn_def_type = ir_type_info_get_type(ira, "FnDef", type_info_definition_data_type);
if ((err = ensure_complete_type(ira->codegen, type_info_fn_def_type)))
return err;
ZigType *type_info_fn_def_inline_type = ir_type_info_get_type(ira, "Inline", type_info_fn_def_type);
if ((err = ensure_complete_type(ira->codegen, type_info_fn_def_inline_type)))
return err;
// Loop through our definitions once to figure out how many definitions we will generate info for.
auto decl_it = decls_scope->decl_table.entry_iterator();
decltype(decls_scope->decl_table)::Entry *curr_entry = nullptr;
int definition_count = 0;
while ((curr_entry = decl_it.next()) != nullptr) {
// If the definition is unresolved, force it to be resolved again.
if (curr_entry->value->resolution == TldResolutionUnresolved) {
resolve_top_level_decl(ira->codegen, curr_entry->value, curr_entry->value->source_node);
if (curr_entry->value->resolution != TldResolutionOk) {
return ErrorSemanticAnalyzeFail;
}
}
// Skip comptime blocks and test functions.
if (curr_entry->value->id != TldIdCompTime) {
if (curr_entry->value->id == TldIdFn) {
ZigFn *fn_entry = ((TldFn *)curr_entry->value)->fn_entry;
if (fn_entry->is_test)
continue;
}
definition_count += 1;
}
}
ConstExprValue *definition_array = create_const_vals(1);
definition_array->special = ConstValSpecialStatic;
definition_array->type = get_array_type(ira->codegen, type_info_definition_type, definition_count);
definition_array->data.x_array.special = ConstArraySpecialNone;
definition_array->data.x_array.data.s_none.elements = create_const_vals(definition_count);
init_const_slice(ira->codegen, out_val, definition_array, 0, definition_count, false);
// Loop through the definitions and generate info.
decl_it = decls_scope->decl_table.entry_iterator();
curr_entry = nullptr;
int definition_index = 0;
while ((curr_entry = decl_it.next()) != nullptr) {
// Skip comptime blocks and test functions.
if (curr_entry->value->id == TldIdCompTime) {
continue;
} else if (curr_entry->value->id == TldIdFn) {
ZigFn *fn_entry = ((TldFn *)curr_entry->value)->fn_entry;
if (fn_entry->is_test)
continue;
}
ConstExprValue *definition_val = &definition_array->data.x_array.data.s_none.elements[definition_index];
definition_val->special = ConstValSpecialStatic;
definition_val->type = type_info_definition_type;
ConstExprValue *inner_fields = create_const_vals(3);
ConstExprValue *name = create_const_str_lit(ira->codegen, curr_entry->key);
init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(curr_entry->key), true);
inner_fields[1].special = ConstValSpecialStatic;
inner_fields[1].type = ira->codegen->builtin_types.entry_bool;
inner_fields[1].data.x_bool = curr_entry->value->visib_mod == VisibModPub;
inner_fields[2].special = ConstValSpecialStatic;
inner_fields[2].type = type_info_definition_data_type;
inner_fields[2].parent.id = ConstParentIdStruct;
inner_fields[2].parent.data.p_struct.struct_val = definition_val;
inner_fields[2].parent.data.p_struct.field_index = 1;
switch (curr_entry->value->id) {
case TldIdVar:
{
ZigVar *var = ((TldVar *)curr_entry->value)->var;
if ((err = ensure_complete_type(ira->codegen, var->const_value->type)))
return ErrorSemanticAnalyzeFail;
if (var->const_value->type->id == ZigTypeIdMetaType) {
// We have a variable of type 'type', so it's actually a type definition.
// 0: Data.Type: type
bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0);
inner_fields[2].data.x_union.payload = var->const_value;
} else {
// We have a variable of another type, so we store the type of the variable.
// 1: Data.Var: type
bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 1);
ConstExprValue *payload = create_const_vals(1);
payload->type = ira->codegen->builtin_types.entry_type;
payload->data.x_type = var->const_value->type;
inner_fields[2].data.x_union.payload = payload;
}
break;
}
case TldIdFn:
{
// 2: Data.Fn: Data.FnDef
bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 2);
ZigFn *fn_entry = ((TldFn *)curr_entry->value)->fn_entry;
assert(!fn_entry->is_test);
if (fn_entry->type_entry == nullptr) {
ir_error_dependency_loop(ira, source_instr);
return ErrorSemanticAnalyzeFail;
}
AstNodeFnProto *fn_node = (AstNodeFnProto *)(fn_entry->proto_node);
ConstExprValue *fn_def_val = create_const_vals(1);
fn_def_val->special = ConstValSpecialStatic;
fn_def_val->type = type_info_fn_def_type;
fn_def_val->parent.id = ConstParentIdUnion;
fn_def_val->parent.data.p_union.union_val = &inner_fields[2];
ConstExprValue *fn_def_fields = create_const_vals(9);
fn_def_val->data.x_struct.fields = fn_def_fields;
// fn_type: type
ensure_field_index(fn_def_val->type, "fn_type", 0);
fn_def_fields[0].special = ConstValSpecialStatic;
fn_def_fields[0].type = ira->codegen->builtin_types.entry_type;
fn_def_fields[0].data.x_type = fn_entry->type_entry;
// inline_type: Data.FnDef.Inline
ensure_field_index(fn_def_val->type, "inline_type", 1);
fn_def_fields[1].special = ConstValSpecialStatic;
fn_def_fields[1].type = type_info_fn_def_inline_type;
bigint_init_unsigned(&fn_def_fields[1].data.x_enum_tag, fn_entry->fn_inline);
// calling_convention: TypeInfo.CallingConvention
ensure_field_index(fn_def_val->type, "calling_convention", 2);
fn_def_fields[2].special = ConstValSpecialStatic;
fn_def_fields[2].type = ir_type_info_get_type(ira, "CallingConvention", nullptr);
bigint_init_unsigned(&fn_def_fields[2].data.x_enum_tag, fn_node->cc);
// is_var_args: bool
ensure_field_index(fn_def_val->type, "is_var_args", 3);
bool is_varargs = fn_node->is_var_args;
fn_def_fields[3].special = ConstValSpecialStatic;
fn_def_fields[3].type = ira->codegen->builtin_types.entry_bool;
fn_def_fields[3].data.x_bool = is_varargs;
// is_extern: bool
ensure_field_index(fn_def_val->type, "is_extern", 4);
fn_def_fields[4].special = ConstValSpecialStatic;
fn_def_fields[4].type = ira->codegen->builtin_types.entry_bool;
fn_def_fields[4].data.x_bool = fn_node->is_extern;
// is_export: bool
ensure_field_index(fn_def_val->type, "is_export", 5);
fn_def_fields[5].special = ConstValSpecialStatic;
fn_def_fields[5].type = ira->codegen->builtin_types.entry_bool;
fn_def_fields[5].data.x_bool = fn_node->is_export;
// lib_name: ?[]const u8
ensure_field_index(fn_def_val->type, "lib_name", 6);
fn_def_fields[6].special = ConstValSpecialStatic;
ZigType *u8_ptr = get_pointer_to_type_extra(
ira->codegen, ira->codegen->builtin_types.entry_u8,
true, false, PtrLenUnknown,
0, 0, 0, false);
fn_def_fields[6].type = get_optional_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr));
if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) {
fn_def_fields[6].data.x_optional = create_const_vals(1);
ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name);
init_const_slice(ira->codegen, fn_def_fields[6].data.x_optional, lib_name, 0, buf_len(fn_node->lib_name), true);
} else {
fn_def_fields[6].data.x_optional = nullptr;
}
// return_type: type
ensure_field_index(fn_def_val->type, "return_type", 7);
fn_def_fields[7].special = ConstValSpecialStatic;
fn_def_fields[7].type = ira->codegen->builtin_types.entry_type;
fn_def_fields[7].data.x_type = fn_entry->type_entry->data.fn.fn_type_id.return_type;
// arg_names: [][] const u8
ensure_field_index(fn_def_val->type, "arg_names", 8);
size_t fn_arg_count = fn_entry->variable_list.length;
ConstExprValue *fn_arg_name_array = create_const_vals(1);
fn_arg_name_array->special = ConstValSpecialStatic;
fn_arg_name_array->type = get_array_type(ira->codegen,
get_slice_type(ira->codegen, u8_ptr), fn_arg_count);
fn_arg_name_array->data.x_array.special = ConstArraySpecialNone;
fn_arg_name_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count);
init_const_slice(ira->codegen, &fn_def_fields[8], fn_arg_name_array, 0, fn_arg_count, false);
for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) {
ZigVar *arg_var = fn_entry->variable_list.at(fn_arg_index);
ConstExprValue *fn_arg_name_val = &fn_arg_name_array->data.x_array.data.s_none.elements[fn_arg_index];
ConstExprValue *arg_name = create_const_str_lit(ira->codegen, &arg_var->name);
init_const_slice(ira->codegen, fn_arg_name_val, arg_name, 0, buf_len(&arg_var->name), true);
fn_arg_name_val->parent.id = ConstParentIdArray;
fn_arg_name_val->parent.data.p_array.array_val = fn_arg_name_array;
fn_arg_name_val->parent.data.p_array.elem_index = fn_arg_index;
}
inner_fields[2].data.x_union.payload = fn_def_val;
break;
}
case TldIdContainer:
{
ZigType *type_entry = ((TldContainer *)curr_entry->value)->type_entry;
if ((err = ensure_complete_type(ira->codegen, type_entry)))
return ErrorSemanticAnalyzeFail;
// This is a type.
bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0);
ConstExprValue *payload = create_const_vals(1);
payload->type = ira->codegen->builtin_types.entry_type;
payload->data.x_type = type_entry;
inner_fields[2].data.x_union.payload = payload;
break;
}
default:
zig_unreachable();
}
definition_val->data.x_struct.fields = inner_fields;
definition_index++;
}
assert(definition_index == definition_count);
return ErrorNone;
}
static uint32_t ptr_len_to_size_enum_index(PtrLen ptr_len) {
switch (ptr_len) {
case PtrLenSingle:
return 0;
case PtrLenUnknown:
return 1;
case PtrLenC:
return 3;
}
zig_unreachable();
}
static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_entry) {
Error err;
ZigType *attrs_type;
uint32_t size_enum_index;
if (is_slice(ptr_type_entry)) {
attrs_type = ptr_type_entry->data.structure.fields[slice_ptr_index].type_entry;
size_enum_index = 2;
} else if (ptr_type_entry->id == ZigTypeIdPointer) {
attrs_type = ptr_type_entry;
size_enum_index = ptr_len_to_size_enum_index(ptr_type_entry->data.pointer.ptr_len);
} else {
zig_unreachable();
}
if ((err = type_resolve(ira->codegen, attrs_type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
return nullptr;
ZigType *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer", nullptr);
assertNoError(ensure_complete_type(ira->codegen, type_info_pointer_type));
ConstExprValue *result = create_const_vals(1);
result->special = ConstValSpecialStatic;
result->type = type_info_pointer_type;
ConstExprValue *fields = create_const_vals(6);
result->data.x_struct.fields = fields;
// size: Size
ensure_field_index(result->type, "size", 0);
ZigType *type_info_pointer_size_type = ir_type_info_get_type(ira, "Size", type_info_pointer_type);
assertNoError(ensure_complete_type(ira->codegen, type_info_pointer_size_type));
fields[0].special = ConstValSpecialStatic;
fields[0].type = type_info_pointer_size_type;
bigint_init_unsigned(&fields[0].data.x_enum_tag, size_enum_index);
// is_const: bool
ensure_field_index(result->type, "is_const", 1);
fields[1].special = ConstValSpecialStatic;
fields[1].type = ira->codegen->builtin_types.entry_bool;
fields[1].data.x_bool = attrs_type->data.pointer.is_const;
// is_volatile: bool
ensure_field_index(result->type, "is_volatile", 2);
fields[2].special = ConstValSpecialStatic;
fields[2].type = ira->codegen->builtin_types.entry_bool;
fields[2].data.x_bool = attrs_type->data.pointer.is_volatile;
// alignment: u32
ensure_field_index(result->type, "alignment", 3);
fields[3].special = ConstValSpecialStatic;
fields[3].type = ira->codegen->builtin_types.entry_num_lit_int;
bigint_init_unsigned(&fields[3].data.x_bigint, get_ptr_align(ira->codegen, attrs_type));
// child: type
ensure_field_index(result->type, "child", 4);
fields[4].special = ConstValSpecialStatic;
fields[4].type = ira->codegen->builtin_types.entry_type;
fields[4].data.x_type = attrs_type->data.pointer.child_type;
// is_allowzero: bool
ensure_field_index(result->type, "is_allowzero", 5);
fields[5].special = ConstValSpecialStatic;
fields[5].type = ira->codegen->builtin_types.entry_bool;
fields[5].data.x_bool = attrs_type->data.pointer.allow_zero;
return result;
};
static void make_enum_field_val(IrAnalyze *ira, ConstExprValue *enum_field_val, TypeEnumField *enum_field,
ZigType *type_info_enum_field_type)
{
enum_field_val->special = ConstValSpecialStatic;
enum_field_val->type = type_info_enum_field_type;
ConstExprValue *inner_fields = create_const_vals(2);
inner_fields[1].special = ConstValSpecialStatic;
inner_fields[1].type = ira->codegen->builtin_types.entry_num_lit_int;
ConstExprValue *name = create_const_str_lit(ira->codegen, enum_field->name);
init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(enum_field->name), true);
bigint_init_bigint(&inner_fields[1].data.x_bigint, &enum_field->value);
enum_field_val->data.x_struct.fields = inner_fields;
}
static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr, ZigType *type_entry,
ConstExprValue **out)
{
Error err;
assert(type_entry != nullptr);
assert(!type_is_invalid(type_entry));
if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown)))
return err;
auto entry = ira->codegen->type_info_cache.maybe_get(type_entry);
if (entry != nullptr) {
*out = entry->value;
return ErrorNone;
}
ConstExprValue *result = nullptr;
switch (type_entry->id) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdUnreachable:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
result = &ira->codegen->const_void_val;
break;
case ZigTypeIdInt:
{
result = create_const_vals(1);
result->special = ConstValSpecialStatic;
result->type = ir_type_info_get_type(ira, "Int", nullptr);
ConstExprValue *fields = create_const_vals(2);
result->data.x_struct.fields = fields;
// is_signed: bool
ensure_field_index(result->type, "is_signed", 0);
fields[0].special = ConstValSpecialStatic;
fields[0].type = ira->codegen->builtin_types.entry_bool;
fields[0].data.x_bool = type_entry->data.integral.is_signed;
// bits: u8
ensure_field_index(result->type, "bits", 1);
fields[1].special = ConstValSpecialStatic;
fields[1].type = ira->codegen->builtin_types.entry_num_lit_int;
bigint_init_unsigned(&fields[1].data.x_bigint, type_entry->data.integral.bit_count);
break;
}
case ZigTypeIdFloat:
{
result = create_const_vals(1);
result->special = ConstValSpecialStatic;
result->type = ir_type_info_get_type(ira, "Float", nullptr);
ConstExprValue *fields = create_const_vals(1);
result->data.x_struct.fields = fields;
// bits: u8
ensure_field_index(result->type, "bits", 0);
fields[0].special = ConstValSpecialStatic;
fields[0].type = ira->codegen->builtin_types.entry_num_lit_int;
bigint_init_unsigned(&fields->data.x_bigint, type_entry->data.floating.bit_count);
break;
}
case ZigTypeIdPointer:
{
result = create_ptr_like_type_info(ira, type_entry);
if (result == nullptr)
return ErrorSemanticAnalyzeFail;
break;
}
case ZigTypeIdArray:
{
result = create_const_vals(1);
result->special = ConstValSpecialStatic;
result->type = ir_type_info_get_type(ira, "Array", nullptr);
ConstExprValue *fields = create_const_vals(2);
result->data.x_struct.fields = fields;
// len: usize
ensure_field_index(result->type, "len", 0);
fields[0].special = ConstValSpecialStatic;
fields[0].type = ira->codegen->builtin_types.entry_num_lit_int;
bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.array.len);
// child: type
ensure_field_index(result->type, "child", 1);
fields[1].special = ConstValSpecialStatic;
fields[1].type = ira->codegen->builtin_types.entry_type;
fields[1].data.x_type = type_entry->data.array.child_type;
break;
}
case ZigTypeIdVector: {
result = create_const_vals(1);
result->special = ConstValSpecialStatic;
result->type = ir_type_info_get_type(ira, "Vector", nullptr);
ConstExprValue *fields = create_const_vals(2);
result->data.x_struct.fields = fields;
// len: usize
ensure_field_index(result->type, "len", 0);
fields[0].special = ConstValSpecialStatic;
fields[0].type = ira->codegen->builtin_types.entry_num_lit_int;
bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.vector.len);
// child: type
ensure_field_index(result->type, "child", 1);
fields[1].special = ConstValSpecialStatic;
fields[1].type = ira->codegen->builtin_types.entry_type;
fields[1].data.x_type = type_entry->data.vector.elem_type;
break;
}
case ZigTypeIdOptional:
{
result = create_const_vals(1);
result->special = ConstValSpecialStatic;
result->type = ir_type_info_get_type(ira, "Optional", nullptr);
ConstExprValue *fields = create_const_vals(1);
result->data.x_struct.fields = fields;
// child: type
ensure_field_index(result->type, "child", 0);
fields[0].special = ConstValSpecialStatic;
fields[0].type = ira->codegen->builtin_types.entry_type;
fields[0].data.x_type = type_entry->data.maybe.child_type;
break;
}
case ZigTypeIdPromise:
{
result = create_const_vals(1);
result->special = ConstValSpecialStatic;
result->type = ir_type_info_get_type(ira, "Promise", nullptr);
ConstExprValue *fields = create_const_vals(1);
result->data.x_struct.fields = fields;
// child: ?type
ensure_field_index(result->type, "child", 0);
fields[0].special = ConstValSpecialStatic;
fields[0].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type);
if (type_entry->data.promise.result_type == nullptr)
fields[0].data.x_optional = nullptr;
else {
ConstExprValue *child_type = create_const_vals(1);
child_type->special = ConstValSpecialStatic;
child_type->type = ira->codegen->builtin_types.entry_type;
child_type->data.x_type = type_entry->data.promise.result_type;
fields[0].data.x_optional = child_type;
}
break;
}
case ZigTypeIdEnum:
{
result = create_const_vals(1);
result->special = ConstValSpecialStatic;
result->type = ir_type_info_get_type(ira, "Enum", nullptr);
ConstExprValue *fields = create_const_vals(4);
result->data.x_struct.fields = fields;
// layout: ContainerLayout
ensure_field_index(result->type, "layout", 0);
fields[0].special = ConstValSpecialStatic;
fields[0].type = ir_type_info_get_type(ira, "ContainerLayout", nullptr);
bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.enumeration.layout);
// tag_type: type
ensure_field_index(result->type, "tag_type", 1);
fields[1].special = ConstValSpecialStatic;
fields[1].type = ira->codegen->builtin_types.entry_type;
fields[1].data.x_type = type_entry->data.enumeration.tag_int_type;
// fields: []TypeInfo.EnumField
ensure_field_index(result->type, "fields", 2);
ZigType *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField", nullptr);
if ((err = type_resolve(ira->codegen, type_info_enum_field_type, ResolveStatusSizeKnown))) {
zig_unreachable();
}
uint32_t enum_field_count = type_entry->data.enumeration.src_field_count;
ConstExprValue *enum_field_array = create_const_vals(1);
enum_field_array->special = ConstValSpecialStatic;
enum_field_array->type = get_array_type(ira->codegen, type_info_enum_field_type, enum_field_count);
enum_field_array->data.x_array.special = ConstArraySpecialNone;
enum_field_array->data.x_array.data.s_none.elements = create_const_vals(enum_field_count);
init_const_slice(ira->codegen, &fields[2], enum_field_array, 0, enum_field_count, false);
for (uint32_t enum_field_index = 0; enum_field_index < enum_field_count; enum_field_index++)
{
TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index];
ConstExprValue *enum_field_val = &enum_field_array->data.x_array.data.s_none.elements[enum_field_index];
make_enum_field_val(ira, enum_field_val, enum_field, type_info_enum_field_type);
enum_field_val->parent.id = ConstParentIdArray;
enum_field_val->parent.data.p_array.array_val = enum_field_array;
enum_field_val->parent.data.p_array.elem_index = enum_field_index;
}
// defs: []TypeInfo.Definition
ensure_field_index(result->type, "defs", 3);
if ((err = ir_make_type_info_defs(ira, source_instr, &fields[3],
type_entry->data.enumeration.decls_scope)))
{
return err;
}
break;
}
case ZigTypeIdErrorSet:
{
result = create_const_vals(1);
result->special = ConstValSpecialStatic;
result->type = ir_type_info_get_type(ira, "ErrorSet", nullptr);
ZigType *type_info_error_type = ir_type_info_get_type(ira, "Error", nullptr);
if (!resolve_inferred_error_set(ira->codegen, type_entry, source_instr->source_node)) {
return ErrorSemanticAnalyzeFail;
}
if (type_is_global_error_set(type_entry)) {
result->data.x_optional = nullptr;
break;
}
if ((err = type_resolve(ira->codegen, type_info_error_type, ResolveStatusSizeKnown))) {
zig_unreachable();
}
ConstExprValue *slice_val = create_const_vals(1);
result->data.x_optional = slice_val;
uint32_t error_count = type_entry->data.error_set.err_count;
ConstExprValue *error_array = create_const_vals(1);
error_array->special = ConstValSpecialStatic;
error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count);
error_array->data.x_array.special = ConstArraySpecialNone;
error_array->data.x_array.data.s_none.elements = create_const_vals(error_count);
init_const_slice(ira->codegen, slice_val, error_array, 0, error_count, false);
for (uint32_t error_index = 0; error_index < error_count; error_index++) {
ErrorTableEntry *error = type_entry->data.error_set.errors[error_index];
ConstExprValue *error_val = &error_array->data.x_array.data.s_none.elements[error_index];
error_val->special = ConstValSpecialStatic;
error_val->type = type_info_error_type;
ConstExprValue *inner_fields = create_const_vals(2);
inner_fields[1].special = ConstValSpecialStatic;
inner_fields[1].type = ira->codegen->builtin_types.entry_num_lit_int;
ConstExprValue *name = nullptr;
if (error->cached_error_name_val != nullptr)
name = error->cached_error_name_val;
if (name == nullptr)
name = create_const_str_lit(ira->codegen, &error->name);
init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(&error->name), true);
bigint_init_unsigned(&inner_fields[1].data.x_bigint, error->value);
error_val->data.x_struct.fields = inner_fields;
error_val->parent.id = ConstParentIdArray;
error_val->parent.data.p_array.array_val = error_array;
error_val->parent.data.p_array.elem_index = error_index;
}
break;
}
case ZigTypeIdErrorUnion:
{
result = create_const_vals(1);
result->special = ConstValSpecialStatic;
result->type = ir_type_info_get_type(ira, "ErrorUnion", nullptr);
ConstExprValue *fields = create_const_vals(2);
result->data.x_struct.fields = fields;
// error_set: type
ensure_field_index(result->type, "error_set", 0);
fields[0].special = ConstValSpecialStatic;
fields[0].type = ira->codegen->builtin_types.entry_type;
fields[0].data.x_type = type_entry->data.error_union.err_set_type;
// payload: type
ensure_field_index(result->type, "payload", 1);
fields[1].special = ConstValSpecialStatic;
fields[1].type = ira->codegen->builtin_types.entry_type;
fields[1].data.x_type = type_entry->data.error_union.payload_type;
break;
}
case ZigTypeIdUnion:
{
result = create_const_vals(1);
result->special = ConstValSpecialStatic;
result->type = ir_type_info_get_type(ira, "Union", nullptr);
ConstExprValue *fields = create_const_vals(4);
result->data.x_struct.fields = fields;
// layout: ContainerLayout
ensure_field_index(result->type, "layout", 0);
fields[0].special = ConstValSpecialStatic;
fields[0].type = ir_type_info_get_type(ira, "ContainerLayout", nullptr);
bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.unionation.layout);
// tag_type: ?type
ensure_field_index(result->type, "tag_type", 1);
fields[1].special = ConstValSpecialStatic;
fields[1].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type);
AstNode *union_decl_node = type_entry->data.unionation.decl_node;
if (union_decl_node->data.container_decl.auto_enum ||
union_decl_node->data.container_decl.init_arg_expr != nullptr)
{
ConstExprValue *tag_type = create_const_vals(1);
tag_type->special = ConstValSpecialStatic;
tag_type->type = ira->codegen->builtin_types.entry_type;
tag_type->data.x_type = type_entry->data.unionation.tag_type;
fields[1].data.x_optional = tag_type;
} else {
fields[1].data.x_optional = nullptr;
}
// fields: []TypeInfo.UnionField
ensure_field_index(result->type, "fields", 2);
ZigType *type_info_union_field_type = ir_type_info_get_type(ira, "UnionField", nullptr);
if ((err = type_resolve(ira->codegen, type_info_union_field_type, ResolveStatusSizeKnown)))
zig_unreachable();
uint32_t union_field_count = type_entry->data.unionation.src_field_count;
ConstExprValue *union_field_array = create_const_vals(1);
union_field_array->special = ConstValSpecialStatic;
union_field_array->type = get_array_type(ira->codegen, type_info_union_field_type, union_field_count);
union_field_array->data.x_array.special = ConstArraySpecialNone;
union_field_array->data.x_array.data.s_none.elements = create_const_vals(union_field_count);
init_const_slice(ira->codegen, &fields[2], union_field_array, 0, union_field_count, false);
ZigType *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField", nullptr);
for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) {
TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index];
ConstExprValue *union_field_val = &union_field_array->data.x_array.data.s_none.elements[union_field_index];
union_field_val->special = ConstValSpecialStatic;
union_field_val->type = type_info_union_field_type;
ConstExprValue *inner_fields = create_const_vals(3);
inner_fields[1].special = ConstValSpecialStatic;
inner_fields[1].type = get_optional_type(ira->codegen, type_info_enum_field_type);
if (fields[1].data.x_optional == nullptr) {
inner_fields[1].data.x_optional = nullptr;
} else {
inner_fields[1].data.x_optional = create_const_vals(1);
make_enum_field_val(ira, inner_fields[1].data.x_optional, union_field->enum_field, type_info_enum_field_type);
}
inner_fields[2].special = ConstValSpecialStatic;
inner_fields[2].type = ira->codegen->builtin_types.entry_type;
inner_fields[2].data.x_type = union_field->type_entry;
ConstExprValue *name = create_const_str_lit(ira->codegen, union_field->name);
init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(union_field->name), true);
union_field_val->data.x_struct.fields = inner_fields;
union_field_val->parent.id = ConstParentIdArray;
union_field_val->parent.data.p_array.array_val = union_field_array;
union_field_val->parent.data.p_array.elem_index = union_field_index;
}
// defs: []TypeInfo.Definition
ensure_field_index(result->type, "defs", 3);
if ((err = ir_make_type_info_defs(ira, source_instr, &fields[3],
type_entry->data.unionation.decls_scope)))
{
return err;
}
break;
}
case ZigTypeIdStruct:
{
if (type_entry->data.structure.is_slice) {
result = create_ptr_like_type_info(ira, type_entry);
if (result == nullptr)
return ErrorSemanticAnalyzeFail;
break;
}
result = create_const_vals(1);
result->special = ConstValSpecialStatic;
result->type = ir_type_info_get_type(ira, "Struct", nullptr);
ConstExprValue *fields = create_const_vals(3);
result->data.x_struct.fields = fields;
// layout: ContainerLayout
ensure_field_index(result->type, "layout", 0);
fields[0].special = ConstValSpecialStatic;
fields[0].type = ir_type_info_get_type(ira, "ContainerLayout", nullptr);
bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.structure.layout);
// fields: []TypeInfo.StructField
ensure_field_index(result->type, "fields", 1);
ZigType *type_info_struct_field_type = ir_type_info_get_type(ira, "StructField", nullptr);
if ((err = type_resolve(ira->codegen, type_info_struct_field_type, ResolveStatusSizeKnown))) {
zig_unreachable();
}
uint32_t struct_field_count = type_entry->data.structure.src_field_count;
ConstExprValue *struct_field_array = create_const_vals(1);
struct_field_array->special = ConstValSpecialStatic;
struct_field_array->type = get_array_type(ira->codegen, type_info_struct_field_type, struct_field_count);
struct_field_array->data.x_array.special = ConstArraySpecialNone;
struct_field_array->data.x_array.data.s_none.elements = create_const_vals(struct_field_count);
init_const_slice(ira->codegen, &fields[1], struct_field_array, 0, struct_field_count, false);
for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) {
TypeStructField *struct_field = &type_entry->data.structure.fields[struct_field_index];
ConstExprValue *struct_field_val = &struct_field_array->data.x_array.data.s_none.elements[struct_field_index];
struct_field_val->special = ConstValSpecialStatic;
struct_field_val->type = type_info_struct_field_type;
ConstExprValue *inner_fields = create_const_vals(3);
inner_fields[1].special = ConstValSpecialStatic;
inner_fields[1].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int);
if (!type_has_bits(struct_field->type_entry)) {
inner_fields[1].data.x_optional = nullptr;
} else {
size_t byte_offset = struct_field->offset;
inner_fields[1].data.x_optional = create_const_vals(1);
inner_fields[1].data.x_optional->special = ConstValSpecialStatic;
inner_fields[1].data.x_optional->type = ira->codegen->builtin_types.entry_num_lit_int;
bigint_init_unsigned(&inner_fields[1].data.x_optional->data.x_bigint, byte_offset);
}
inner_fields[2].special = ConstValSpecialStatic;
inner_fields[2].type = ira->codegen->builtin_types.entry_type;
inner_fields[2].data.x_type = struct_field->type_entry;
ConstExprValue *name = create_const_str_lit(ira->codegen, struct_field->name);
init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(struct_field->name), true);
struct_field_val->data.x_struct.fields = inner_fields;
struct_field_val->parent.id = ConstParentIdArray;
struct_field_val->parent.data.p_array.array_val = struct_field_array;
struct_field_val->parent.data.p_array.elem_index = struct_field_index;
}
// defs: []TypeInfo.Definition
ensure_field_index(result->type, "defs", 2);
if ((err = ir_make_type_info_defs(ira, source_instr, &fields[2],
type_entry->data.structure.decls_scope)))
{
return err;
}
break;
}
case ZigTypeIdFn:
{
result = create_const_vals(1);
result->special = ConstValSpecialStatic;
result->type = ir_type_info_get_type(ira, "Fn", nullptr);
ConstExprValue *fields = create_const_vals(6);
result->data.x_struct.fields = fields;
// calling_convention: TypeInfo.CallingConvention
ensure_field_index(result->type, "calling_convention", 0);
fields[0].special = ConstValSpecialStatic;
fields[0].type = ir_type_info_get_type(ira, "CallingConvention", nullptr);
bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.fn.fn_type_id.cc);
// is_generic: bool
ensure_field_index(result->type, "is_generic", 1);
bool is_generic = type_entry->data.fn.is_generic;
fields[1].special = ConstValSpecialStatic;
fields[1].type = ira->codegen->builtin_types.entry_bool;
fields[1].data.x_bool = is_generic;
// is_varargs: bool
ensure_field_index(result->type, "is_var_args", 2);
bool is_varargs = type_entry->data.fn.fn_type_id.is_var_args;
fields[2].special = ConstValSpecialStatic;
fields[2].type = ira->codegen->builtin_types.entry_bool;
fields[2].data.x_bool = type_entry->data.fn.fn_type_id.is_var_args;
// return_type: ?type
ensure_field_index(result->type, "return_type", 3);
fields[3].special = ConstValSpecialStatic;
fields[3].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type);
if (type_entry->data.fn.fn_type_id.return_type == nullptr)
fields[3].data.x_optional = nullptr;
else {
ConstExprValue *return_type = create_const_vals(1);
return_type->special = ConstValSpecialStatic;
return_type->type = ira->codegen->builtin_types.entry_type;
return_type->data.x_type = type_entry->data.fn.fn_type_id.return_type;
fields[3].data.x_optional = return_type;
}
// async_allocator_type: type
ensure_field_index(result->type, "async_allocator_type", 4);
fields[4].special = ConstValSpecialStatic;
fields[4].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type);
if (type_entry->data.fn.fn_type_id.async_allocator_type == nullptr)
fields[4].data.x_optional = nullptr;
else {
ConstExprValue *async_alloc_type = create_const_vals(1);
async_alloc_type->special = ConstValSpecialStatic;
async_alloc_type->type = ira->codegen->builtin_types.entry_type;
async_alloc_type->data.x_type = type_entry->data.fn.fn_type_id.async_allocator_type;
fields[4].data.x_optional = async_alloc_type;
}
// args: []TypeInfo.FnArg
ZigType *type_info_fn_arg_type = ir_type_info_get_type(ira, "FnArg", nullptr);
if ((err = type_resolve(ira->codegen, type_info_fn_arg_type, ResolveStatusSizeKnown))) {
zig_unreachable();
}
size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count -
(is_varargs && type_entry->data.fn.fn_type_id.cc != CallingConventionC);
ConstExprValue *fn_arg_array = create_const_vals(1);
fn_arg_array->special = ConstValSpecialStatic;
fn_arg_array->type = get_array_type(ira->codegen, type_info_fn_arg_type, fn_arg_count);
fn_arg_array->data.x_array.special = ConstArraySpecialNone;
fn_arg_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count);
init_const_slice(ira->codegen, &fields[5], fn_arg_array, 0, fn_arg_count, false);
for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++)
{
FnTypeParamInfo *fn_param_info = &type_entry->data.fn.fn_type_id.param_info[fn_arg_index];
ConstExprValue *fn_arg_val = &fn_arg_array->data.x_array.data.s_none.elements[fn_arg_index];
fn_arg_val->special = ConstValSpecialStatic;
fn_arg_val->type = type_info_fn_arg_type;
bool arg_is_generic = fn_param_info->type == nullptr;
if (arg_is_generic) assert(is_generic);
ConstExprValue *inner_fields = create_const_vals(3);
inner_fields[0].special = ConstValSpecialStatic;
inner_fields[0].type = ira->codegen->builtin_types.entry_bool;
inner_fields[0].data.x_bool = arg_is_generic;
inner_fields[1].special = ConstValSpecialStatic;
inner_fields[1].type = ira->codegen->builtin_types.entry_bool;
inner_fields[1].data.x_bool = fn_param_info->is_noalias;
inner_fields[2].special = ConstValSpecialStatic;
inner_fields[2].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type);
if (arg_is_generic)
inner_fields[2].data.x_optional = nullptr;
else {
ConstExprValue *arg_type = create_const_vals(1);
arg_type->special = ConstValSpecialStatic;
arg_type->type = ira->codegen->builtin_types.entry_type;
arg_type->data.x_type = fn_param_info->type;
inner_fields[2].data.x_optional = arg_type;
}
fn_arg_val->data.x_struct.fields = inner_fields;
fn_arg_val->parent.id = ConstParentIdArray;
fn_arg_val->parent.data.p_array.array_val = fn_arg_array;
fn_arg_val->parent.data.p_array.elem_index = fn_arg_index;
}
break;
}
case ZigTypeIdBoundFn:
{
ZigType *fn_type = type_entry->data.bound_fn.fn_type;
assert(fn_type->id == ZigTypeIdFn);
if ((err = ir_make_type_info_value(ira, source_instr, fn_type, &result)))
return err;
break;
}
}
assert(result != nullptr);
ira->codegen->type_info_cache.put(type_entry, result);
*out = result;
return ErrorNone;
}
static IrInstruction *ir_analyze_instruction_type_info(IrAnalyze *ira,
IrInstructionTypeInfo *instruction)
{
Error err;
IrInstruction *type_value = instruction->type_value->child;
ZigType *type_entry = ir_resolve_type(ira, type_value);
if (type_is_invalid(type_entry))
return ira->codegen->invalid_instruction;
ZigType *result_type = ir_type_info_get_type(ira, nullptr, nullptr);
ConstExprValue *payload;
if ((err = ir_make_type_info_value(ira, &instruction->base, type_entry, &payload)))
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_const(ira, &instruction->base, result_type);
ConstExprValue *out_val = &result->value;
bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry));
out_val->data.x_union.payload = payload;
if (payload != nullptr) {
payload->parent.id = ConstParentIdUnion;
payload->parent.data.p_union.union_val = out_val;
}
return result;
}
static IrInstruction *ir_analyze_instruction_type_id(IrAnalyze *ira,
IrInstructionTypeId *instruction)
{
IrInstruction *type_value = instruction->type_value->child;
ZigType *type_entry = ir_resolve_type(ira, type_value);
if (type_is_invalid(type_entry))
return ira->codegen->invalid_instruction;
ConstExprValue *var_value = get_builtin_value(ira->codegen, "TypeId");
assert(var_value->type->id == ZigTypeIdMetaType);
ZigType *result_type = var_value->data.x_type;
IrInstruction *result = ir_const(ira, &instruction->base, result_type);
bigint_init_unsigned(&result->value.data.x_enum_tag, type_id_index(type_entry));
return result;
}
static IrInstruction *ir_analyze_instruction_set_eval_branch_quota(IrAnalyze *ira,
IrInstructionSetEvalBranchQuota *instruction)
{
uint64_t new_quota;
if (!ir_resolve_usize(ira, instruction->new_quota->child, &new_quota))
return ira->codegen->invalid_instruction;
if (new_quota > *ira->new_irb.exec->backward_branch_quota) {
*ira->new_irb.exec->backward_branch_quota = new_quota;
}
return ir_const_void(ira, &instruction->base);
}
static IrInstruction *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstructionTypeName *instruction) {
IrInstruction *type_value = instruction->type_value->child;
ZigType *type_entry = ir_resolve_type(ira, type_value);
if (type_is_invalid(type_entry))
return ira->codegen->invalid_instruction;
if (!type_entry->cached_const_name_val) {
type_entry->cached_const_name_val = create_const_str_lit(ira->codegen, type_bare_name(type_entry));
}
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
copy_const_val(&result->value, type_entry->cached_const_name_val, true);
return result;
}
static void ir_cimport_cache_paths(Buf *cache_dir, Buf *tmp_c_file_digest, Buf *out_zig_dir, Buf *out_zig_path) {
buf_resize(out_zig_dir, 0);
buf_resize(out_zig_path, 0);
buf_appendf(out_zig_dir, "%s" OS_SEP "o" OS_SEP "%s",
buf_ptr(cache_dir), buf_ptr(tmp_c_file_digest));
buf_appendf(out_zig_path, "%s" OS_SEP "cimport.zig", buf_ptr(out_zig_dir));
}
static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstructionCImport *instruction) {
Error err;
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(ira->codegen, node, instruction->base.scope);
// Execute the C import block like an inline function
ZigType *void_type = ira->codegen->builtin_types.entry_void;
ConstExprValue *cimport_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, nullptr);
if (type_is_invalid(cimport_result->type))
return ira->codegen->invalid_instruction;
ZigPackage *cur_scope_pkg = scope_package(instruction->base.scope);
Buf *namespace_name = buf_sprintf("%s.cimport:%" ZIG_PRI_usize ":%" ZIG_PRI_usize,
buf_ptr(&cur_scope_pkg->pkg_path), node->line + 1, node->column + 1);
ZigPackage *cimport_pkg = new_anonymous_package();
cimport_pkg->package_table.put(buf_create_from_str("builtin"), ira->codegen->compile_var_package);
cimport_pkg->package_table.put(buf_create_from_str("std"), ira->codegen->std_package);
buf_init_from_buf(&cimport_pkg->pkg_path, namespace_name);
CacheHash *cache_hash;
if ((err = create_c_object_cache(ira->codegen, &cache_hash, false))) {
ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to create cache: %s", err_str(err)));
return ira->codegen->invalid_instruction;
}
cache_buf(cache_hash, &cimport_scope->buf);
// Set this because we're not adding any files before checking for a hit.
cache_hash->force_check_manifest = true;
Buf tmp_c_file_digest = BUF_INIT;
buf_resize(&tmp_c_file_digest, 0);
if ((err = cache_hit(cache_hash, &tmp_c_file_digest))) {
if (err != ErrorInvalidFormat) {
ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to check cache: %s", err_str(err)));
return ira->codegen->invalid_instruction;
}
}
ira->codegen->caches_to_release.append(cache_hash);
Buf *out_zig_dir = buf_alloc();
Buf *out_zig_path = buf_alloc();
if (buf_len(&tmp_c_file_digest) == 0 || cache_hash->files.length == 0) {
// Cache Miss
Buf *tmp_c_file_dir = buf_sprintf("%s" OS_SEP "o" OS_SEP "%s",
buf_ptr(ira->codegen->cache_dir), buf_ptr(&cache_hash->b64_digest));
Buf *resolve_paths[] = {
tmp_c_file_dir,
buf_create_from_str("cimport.h"),
};
Buf tmp_c_file_path = os_path_resolve(resolve_paths, 2);
if ((err = os_make_path(tmp_c_file_dir))) {
ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to make dir: %s", err_str(err)));
return ira->codegen->invalid_instruction;
}
if ((err = os_write_file(&tmp_c_file_path, &cimport_scope->buf))) {
ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to write .h file: %s", err_str(err)));
return ira->codegen->invalid_instruction;
}
if (ira->codegen->verbose_cimport) {
fprintf(stderr, "@cImport source: %s\n", buf_ptr(&tmp_c_file_path));
}
Buf *tmp_dep_file = buf_sprintf("%s.d", buf_ptr(&tmp_c_file_path));
ZigList<const char *> clang_argv = {0};
add_cc_args(ira->codegen, clang_argv, buf_ptr(tmp_dep_file), true);
clang_argv.append(buf_ptr(&tmp_c_file_path));
if (ira->codegen->verbose_cc) {
fprintf(stderr, "clang");
for (size_t i = 0; i < clang_argv.length; i += 1) {
fprintf(stderr, " %s", clang_argv.at(i));
}
fprintf(stderr, "\n");
}
clang_argv.append(nullptr); // to make the [start...end] argument work
AstNode *root_node;
Stage2ErrorMsg *errors_ptr;
size_t errors_len;
const char *resources_path = buf_ptr(ira->codegen->zig_c_headers_dir);
if ((err = parse_h_file(ira->codegen, &root_node, &errors_ptr, &errors_len,
&clang_argv.at(0), &clang_argv.last(), Stage2TranslateModeImport, resources_path)))
{
if (err != ErrorCCompileErrors) {
ir_add_error_node(ira, node, buf_sprintf("C import failed: %s", err_str(err)));
return ira->codegen->invalid_instruction;
}
ErrorMsg *parent_err_msg = ir_add_error_node(ira, node, buf_sprintf("C import failed"));
if (ira->codegen->libc_link_lib == nullptr) {
add_error_note(ira->codegen, parent_err_msg, node,
buf_sprintf("libc headers not available; compilation does not link against libc"));
}
for (size_t i = 0; i < errors_len; i += 1) {
Stage2ErrorMsg *clang_err = &errors_ptr[i];
ErrorMsg *err_msg = err_msg_create_with_offset(
clang_err->filename_ptr ?
buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) : buf_alloc(),
clang_err->line, clang_err->column, clang_err->offset, clang_err->source,
buf_create_from_mem(clang_err->msg_ptr, clang_err->msg_len));
err_msg_add_note(parent_err_msg, err_msg);
}
return ira->codegen->invalid_instruction;
}
if (ira->codegen->verbose_cimport) {
fprintf(stderr, "@cImport .d file: %s\n", buf_ptr(tmp_dep_file));
}
if ((err = cache_add_dep_file(cache_hash, tmp_dep_file, false))) {
ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to parse .d file: %s", err_str(err)));
return ira->codegen->invalid_instruction;
}
if ((err = cache_final(cache_hash, &tmp_c_file_digest))) {
ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to finalize cache: %s", err_str(err)));
return ira->codegen->invalid_instruction;
}
ir_cimport_cache_paths(ira->codegen->cache_dir, &tmp_c_file_digest, out_zig_dir, out_zig_path);
if ((err = os_make_path(out_zig_dir))) {
ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to make output dir: %s", err_str(err)));
return ira->codegen->invalid_instruction;
}
FILE *out_file = fopen(buf_ptr(out_zig_path), "wb");
if (out_file == nullptr) {
ir_add_error_node(ira, node,
buf_sprintf("C import failed: unable to open output file: %s", strerror(errno)));
return ira->codegen->invalid_instruction;
}
ast_render(out_file, root_node, 4);
if (fclose(out_file) != 0) {
ir_add_error_node(ira, node,
buf_sprintf("C import failed: unable to write to output file: %s", strerror(errno)));
return ira->codegen->invalid_instruction;
}
if (ira->codegen->verbose_cimport) {
fprintf(stderr, "@cImport output: %s\n", buf_ptr(out_zig_path));
}
} else {
// Cache Hit
ir_cimport_cache_paths(ira->codegen->cache_dir, &tmp_c_file_digest, out_zig_dir, out_zig_path);
if (ira->codegen->verbose_cimport) {
fprintf(stderr, "@cImport cache hit: %s\n", buf_ptr(out_zig_path));
}
}
Buf *import_code = buf_alloc();
if ((err = file_fetch(ira->codegen, out_zig_path, import_code))) {
ir_add_error_node(ira, node,
buf_sprintf("unable to open '%s': %s", buf_ptr(out_zig_path), err_str(err)));
return ira->codegen->invalid_instruction;
}
ZigType *child_import = add_source_file(ira->codegen, cimport_pkg, out_zig_path,
import_code, SourceKindCImport);
return ir_const_type(ira, &instruction->base, child_import);
}
static IrInstruction *ir_analyze_instruction_c_include(IrAnalyze *ira, IrInstructionCInclude *instruction) {
IrInstruction *name_value = instruction->name->child;
if (type_is_invalid(name_value->value.type))
return ira->codegen->invalid_instruction;
Buf *include_name = ir_resolve_str(ira, name_value);
if (!include_name)
return ira->codegen->invalid_instruction;
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));
return ir_const_void(ira, &instruction->base);
}
static IrInstruction *ir_analyze_instruction_c_define(IrAnalyze *ira, IrInstructionCDefine *instruction) {
IrInstruction *name = instruction->name->child;
if (type_is_invalid(name->value.type))
return ira->codegen->invalid_instruction;
Buf *define_name = ir_resolve_str(ira, name);
if (!define_name)
return ira->codegen->invalid_instruction;
IrInstruction *value = instruction->value->child;
if (type_is_invalid(value->value.type))
return ira->codegen->invalid_instruction;
Buf *define_value = ir_resolve_str(ira, value);
if (!define_value)
return ira->codegen->invalid_instruction;
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));
return ir_const_void(ira, &instruction->base);
}
static IrInstruction *ir_analyze_instruction_c_undef(IrAnalyze *ira, IrInstructionCUndef *instruction) {
IrInstruction *name = instruction->name->child;
if (type_is_invalid(name->value.type))
return ira->codegen->invalid_instruction;
Buf *undef_name = ir_resolve_str(ira, name);
if (!undef_name)
return ira->codegen->invalid_instruction;
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));
return ir_const_void(ira, &instruction->base);
}
static IrInstruction *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstructionEmbedFile *instruction) {
IrInstruction *name = instruction->name->child;
if (type_is_invalid(name->value.type))
return ira->codegen->invalid_instruction;
Buf *rel_file_path = ir_resolve_str(ira, name);
if (!rel_file_path)
return ira->codegen->invalid_instruction;
ZigType *import = get_scope_import(instruction->base.scope);
// figure out absolute path to resource
Buf source_dir_path = BUF_INIT;
os_path_dirname(import->data.structure.root_struct->path, &source_dir_path);
Buf *resolve_paths[] = {
&source_dir_path,
rel_file_path,
};
Buf *file_path = buf_alloc();
*file_path = os_path_resolve(resolve_paths, 2);
// load from file system into const expr
Buf *file_contents = buf_alloc();
Error err;
if ((err = file_fetch(ira->codegen, 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->invalid_instruction;
} else {
ir_add_error(ira, instruction->name, buf_sprintf("unable to open '%s': %s", buf_ptr(file_path), err_str(err)));
return ira->codegen->invalid_instruction;
}
}
ZigType *result_type = get_array_type(ira->codegen,
ira->codegen->builtin_types.entry_u8, buf_len(file_contents));
IrInstruction *result = ir_const(ira, &instruction->base, result_type);
init_const_str_lit(ira->codegen, &result->value, file_contents);
return result;
}
static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchgSrc *instruction) {
ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->type_value->child);
if (type_is_invalid(operand_type))
return ira->codegen->invalid_instruction;
IrInstruction *ptr = instruction->ptr->child;
if (type_is_invalid(ptr->value.type))
return ira->codegen->invalid_instruction;
// TODO let this be volatile
ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false);
IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr, ptr_type);
if (type_is_invalid(casted_ptr->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *cmp_value = instruction->cmp_value->child;
if (type_is_invalid(cmp_value->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *new_value = instruction->new_value->child;
if (type_is_invalid(new_value->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *success_order_value = instruction->success_order_value->child;
if (type_is_invalid(success_order_value->value.type))
return ira->codegen->invalid_instruction;
AtomicOrder success_order;
if (!ir_resolve_atomic_order(ira, success_order_value, &success_order))
return ira->codegen->invalid_instruction;
IrInstruction *failure_order_value = instruction->failure_order_value->child;
if (type_is_invalid(failure_order_value->value.type))
return ira->codegen->invalid_instruction;
AtomicOrder failure_order;
if (!ir_resolve_atomic_order(ira, failure_order_value, &failure_order))
return ira->codegen->invalid_instruction;
IrInstruction *casted_cmp_value = ir_implicit_cast(ira, cmp_value, operand_type);
if (type_is_invalid(casted_cmp_value->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, operand_type);
if (type_is_invalid(casted_new_value->value.type))
return ira->codegen->invalid_instruction;
if (success_order < AtomicOrderMonotonic) {
ir_add_error(ira, success_order_value,
buf_sprintf("success atomic ordering must be Monotonic or stricter"));
return ira->codegen->invalid_instruction;
}
if (failure_order < AtomicOrderMonotonic) {
ir_add_error(ira, failure_order_value,
buf_sprintf("failure atomic ordering must be Monotonic or stricter"));
return ira->codegen->invalid_instruction;
}
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->invalid_instruction;
}
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->invalid_instruction;
}
if (instr_is_comptime(casted_ptr) && instr_is_comptime(casted_cmp_value) && instr_is_comptime(casted_new_value)) {
zig_panic("TODO compile-time execution of cmpxchg");
}
IrInstruction *result = ir_build_cmpxchg_gen(ira, &instruction->base,
casted_ptr, casted_cmp_value, casted_new_value,
success_order, failure_order, instruction->is_weak);
result->value.type = get_optional_type(ira->codegen, operand_type);
ir_add_alloca(ira, result, result->value.type);
return result;
}
static IrInstruction *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstructionFence *instruction) {
IrInstruction *order_value = instruction->order_value->child;
if (type_is_invalid(order_value->value.type))
return ira->codegen->invalid_instruction;
AtomicOrder order;
if (!ir_resolve_atomic_order(ira, order_value, &order))
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_build_fence(&ira->new_irb,
instruction->base.scope, instruction->base.source_node, order_value, order);
result->value.type = ira->codegen->builtin_types.entry_void;
return result;
}
static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstructionTruncate *instruction) {
IrInstruction *dest_type_value = instruction->dest_type->child;
ZigType *dest_type = ir_resolve_type(ira, dest_type_value);
if (type_is_invalid(dest_type))
return ira->codegen->invalid_instruction;
if (dest_type->id != ZigTypeIdInt &&
dest_type->id != ZigTypeIdComptimeInt)
{
ir_add_error(ira, dest_type_value, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *target = instruction->target->child;
ZigType *src_type = target->value.type;
if (type_is_invalid(src_type))
return ira->codegen->invalid_instruction;
if (src_type->id != ZigTypeIdInt &&
src_type->id != ZigTypeIdComptimeInt)
{
ir_add_error(ira, target, buf_sprintf("expected integer type, found '%s'", buf_ptr(&src_type->name)));
return ira->codegen->invalid_instruction;
}
if (dest_type->id == ZigTypeIdComptimeInt) {
return ir_implicit_cast(ira, target, dest_type);
}
if (instr_is_comptime(target)) {
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (val == nullptr)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_const(ira, &instruction->base, dest_type);
bigint_truncate(&result->value.data.x_bigint, &val->data.x_bigint,
dest_type->data.integral.bit_count, dest_type->data.integral.is_signed);
return result;
}
if (src_type->data.integral.bit_count == 0 || dest_type->data.integral.bit_count == 0) {
IrInstruction *result = ir_const(ira, &instruction->base, dest_type);
bigint_init_unsigned(&result->value.data.x_bigint, 0);
return result;
}
if (src_type->data.integral.is_signed != dest_type->data.integral.is_signed) {
const char *sign_str = 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)));
return ira->codegen->invalid_instruction;
} else if (src_type->data.integral.bit_count < dest_type->data.integral.bit_count) {
ir_add_error(ira, target, buf_sprintf("type '%s' has fewer bits than destination type '%s'",
buf_ptr(&src_type->name), buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *new_instruction = ir_build_truncate(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, dest_type_value, target);
new_instruction->value.type = dest_type;
return new_instruction;
}
static IrInstruction *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstructionIntCast *instruction) {
ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child);
if (type_is_invalid(dest_type))
return ira->codegen->invalid_instruction;
if (dest_type->id != ZigTypeIdInt && dest_type->id != ZigTypeIdComptimeInt) {
ir_add_error(ira, instruction->dest_type, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *target = instruction->target->child;
if (type_is_invalid(target->value.type))
return ira->codegen->invalid_instruction;
if (target->value.type->id != ZigTypeIdInt && target->value.type->id != ZigTypeIdComptimeInt) {
ir_add_error(ira, instruction->target, buf_sprintf("expected integer type, found '%s'",
buf_ptr(&target->value.type->name)));
return ira->codegen->invalid_instruction;
}
if (instr_is_comptime(target)) {
return ir_implicit_cast(ira, target, dest_type);
}
if (dest_type->id == ZigTypeIdComptimeInt) {
ir_add_error(ira, instruction->target, buf_sprintf("attempt to cast runtime value to '%s'",
buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
return ir_analyze_widen_or_shorten(ira, &instruction->base, target, dest_type);
}
static IrInstruction *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstructionFloatCast *instruction) {
ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child);
if (type_is_invalid(dest_type))
return ira->codegen->invalid_instruction;
if (dest_type->id != ZigTypeIdFloat) {
ir_add_error(ira, instruction->dest_type,
buf_sprintf("expected float type, found '%s'", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *target = instruction->target->child;
if (type_is_invalid(target->value.type))
return ira->codegen->invalid_instruction;
if (target->value.type->id == ZigTypeIdComptimeInt ||
target->value.type->id == ZigTypeIdComptimeFloat)
{
if (ir_num_lit_fits_in_other_type(ira, target, dest_type, true)) {
CastOp op;
if (target->value.type->id == ZigTypeIdComptimeInt) {
op = CastOpIntToFloat;
} else {
op = CastOpNumLitToConcrete;
}
return ir_resolve_cast(ira, &instruction->base, target, dest_type, op, false);
} else {
return ira->codegen->invalid_instruction;
}
}
if (target->value.type->id != ZigTypeIdFloat) {
ir_add_error(ira, instruction->target, buf_sprintf("expected float type, found '%s'",
buf_ptr(&target->value.type->name)));
return ira->codegen->invalid_instruction;
}
return ir_analyze_widen_or_shorten(ira, &instruction->base, target, dest_type);
}
static IrInstruction *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInstructionErrSetCast *instruction) {
ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child);
if (type_is_invalid(dest_type))
return ira->codegen->invalid_instruction;
if (dest_type->id != ZigTypeIdErrorSet) {
ir_add_error(ira, instruction->dest_type,
buf_sprintf("expected error set type, found '%s'", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *target = instruction->target->child;
if (type_is_invalid(target->value.type))
return ira->codegen->invalid_instruction;
if (target->value.type->id != ZigTypeIdErrorSet) {
ir_add_error(ira, instruction->target,
buf_sprintf("expected error set type, found '%s'", buf_ptr(&target->value.type->name)));
return ira->codegen->invalid_instruction;
}
return ir_analyze_err_set_cast(ira, &instruction->base, target, dest_type);
}
static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstructionFromBytes *instruction) {
Error err;
ZigType *dest_child_type = ir_resolve_type(ira, instruction->dest_child_type->child);
if (type_is_invalid(dest_child_type))
return ira->codegen->invalid_instruction;
IrInstruction *target = instruction->target->child;
if (type_is_invalid(target->value.type))
return ira->codegen->invalid_instruction;
bool src_ptr_const;
bool src_ptr_volatile;
uint32_t src_ptr_align;
if (target->value.type->id == ZigTypeIdPointer) {
src_ptr_const = target->value.type->data.pointer.is_const;
src_ptr_volatile = target->value.type->data.pointer.is_volatile;
if ((err = resolve_ptr_align(ira, target->value.type, &src_ptr_align)))
return ira->codegen->invalid_instruction;
} else if (is_slice(target->value.type)) {
ZigType *src_ptr_type = target->value.type->data.structure.fields[slice_ptr_index].type_entry;
src_ptr_const = src_ptr_type->data.pointer.is_const;
src_ptr_volatile = src_ptr_type->data.pointer.is_volatile;
if ((err = resolve_ptr_align(ira, src_ptr_type, &src_ptr_align)))
return ira->codegen->invalid_instruction;
} else {
src_ptr_const = true;
src_ptr_volatile = false;
if ((err = type_resolve(ira->codegen, target->value.type, ResolveStatusAlignmentKnown)))
return ira->codegen->invalid_instruction;
src_ptr_align = get_abi_alignment(ira->codegen, target->value.type);
}
if ((err = type_resolve(ira->codegen, dest_child_type, ResolveStatusSizeKnown)))
return ira->codegen->invalid_instruction;
ZigType *dest_ptr_type = get_pointer_to_type_extra(ira->codegen, dest_child_type,
src_ptr_const, src_ptr_volatile, PtrLenUnknown,
src_ptr_align, 0, 0, false);
ZigType *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type);
ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
src_ptr_const, src_ptr_volatile, PtrLenUnknown,
src_ptr_align, 0, 0, false);
ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
IrInstruction *casted_value = ir_implicit_cast(ira, target, u8_slice);
if (type_is_invalid(casted_value->value.type))
return ira->codegen->invalid_instruction;
bool have_known_len = false;
uint64_t known_len;
if (instr_is_comptime(casted_value)) {
ConstExprValue *val = ir_resolve_const(ira, casted_value, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
ConstExprValue *len_val = &val->data.x_struct.fields[slice_len_index];
if (value_is_comptime(len_val)) {
known_len = bigint_as_unsigned(&len_val->data.x_bigint);
have_known_len = true;
}
}
if (casted_value->value.data.rh_slice.id == RuntimeHintSliceIdLen) {
known_len = casted_value->value.data.rh_slice.len;
have_known_len = true;
}
if (have_known_len) {
uint64_t child_type_size = type_size(ira->codegen, dest_child_type);
uint64_t remainder = known_len % child_type_size;
if (remainder != 0) {
ErrorMsg *msg = ir_add_error(ira, &instruction->base,
buf_sprintf("unable to convert [%" ZIG_PRI_u64 "]u8 to %s: size mismatch",
known_len, buf_ptr(&dest_slice_type->name)));
add_error_note(ira->codegen, msg, instruction->dest_child_type->source_node,
buf_sprintf("%s has size %" ZIG_PRI_u64 "; remaining bytes: %" ZIG_PRI_u64,
buf_ptr(&dest_child_type->name), child_type_size, remainder));
return ira->codegen->invalid_instruction;
}
}
IrInstruction *result = ir_build_resize_slice(ira, &instruction->base, casted_value, dest_slice_type);
ir_add_alloca(ira, result, dest_slice_type);
return result;
}
static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstructionToBytes *instruction) {
Error err;
IrInstruction *target = instruction->target->child;
if (type_is_invalid(target->value.type))
return ira->codegen->invalid_instruction;
if (!is_slice(target->value.type)) {
ir_add_error(ira, instruction->target,
buf_sprintf("expected slice, found '%s'", buf_ptr(&target->value.type->name)));
return ira->codegen->invalid_instruction;
}
ZigType *src_ptr_type = target->value.type->data.structure.fields[slice_ptr_index].type_entry;
uint32_t alignment;
if ((err = resolve_ptr_align(ira, src_ptr_type, &alignment)))
return ira->codegen->invalid_instruction;
ZigType *dest_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
src_ptr_type->data.pointer.is_const, src_ptr_type->data.pointer.is_volatile, PtrLenUnknown,
alignment, 0, 0, false);
ZigType *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type);
if (instr_is_comptime(target)) {
ConstExprValue *target_val = ir_resolve_const(ira, target, UndefBad);
if (target_val == nullptr)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_const(ira, &instruction->base, dest_slice_type);
result->value.data.x_struct.fields = create_const_vals(2);
ConstExprValue *ptr_val = &result->value.data.x_struct.fields[slice_ptr_index];
ConstExprValue *target_ptr_val = &target_val->data.x_struct.fields[slice_ptr_index];
copy_const_val(ptr_val, target_ptr_val, false);
ptr_val->type = dest_ptr_type;
ConstExprValue *len_val = &result->value.data.x_struct.fields[slice_len_index];
len_val->special = ConstValSpecialStatic;
len_val->type = ira->codegen->builtin_types.entry_usize;
ConstExprValue *target_len_val = &target_val->data.x_struct.fields[slice_len_index];
ZigType *elem_type = src_ptr_type->data.pointer.child_type;
BigInt elem_size_bigint;
bigint_init_unsigned(&elem_size_bigint, type_size(ira->codegen, elem_type));
bigint_mul(&len_val->data.x_bigint, &target_len_val->data.x_bigint, &elem_size_bigint);
return result;
}
IrInstruction *result = ir_build_resize_slice(ira, &instruction->base, target, dest_slice_type);
ir_add_alloca(ira, result, dest_slice_type);
return result;
}
static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) {
Error err;
ZigType *ptr_type = get_src_ptr_type(ty);
assert(ptr_type != nullptr);
if (ptr_type->id == ZigTypeIdPointer) {
if ((err = type_resolve(ira->codegen, ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
return err;
}
*result_align = get_ptr_align(ira->codegen, ty);
return ErrorNone;
}
static IrInstruction *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstructionIntToFloat *instruction) {
ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child);
if (type_is_invalid(dest_type))
return ira->codegen->invalid_instruction;
IrInstruction *target = instruction->target->child;
if (type_is_invalid(target->value.type))
return ira->codegen->invalid_instruction;
if (target->value.type->id != ZigTypeIdInt && target->value.type->id != ZigTypeIdComptimeInt) {
ir_add_error(ira, instruction->target, buf_sprintf("expected int type, found '%s'",
buf_ptr(&target->value.type->name)));
return ira->codegen->invalid_instruction;
}
return ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpIntToFloat, false);
}
static IrInstruction *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrInstructionFloatToInt *instruction) {
ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child);
if (type_is_invalid(dest_type))
return ira->codegen->invalid_instruction;
IrInstruction *target = instruction->target->child;
if (type_is_invalid(target->value.type))
return ira->codegen->invalid_instruction;
if (target->value.type->id == ZigTypeIdComptimeInt) {
return ir_implicit_cast(ira, target, dest_type);
}
if (target->value.type->id != ZigTypeIdFloat && target->value.type->id != ZigTypeIdComptimeFloat) {
ir_add_error(ira, instruction->target, buf_sprintf("expected float type, found '%s'",
buf_ptr(&target->value.type->name)));
return ira->codegen->invalid_instruction;
}
return ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpFloatToInt, false);
}
static IrInstruction *ir_analyze_instruction_err_to_int(IrAnalyze *ira, IrInstructionErrToInt *instruction) {
IrInstruction *target = instruction->target->child;
if (type_is_invalid(target->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_target;
if (target->value.type->id == ZigTypeIdErrorSet) {
casted_target = target;
} else {
casted_target = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_global_error_set);
if (type_is_invalid(casted_target->value.type))
return ira->codegen->invalid_instruction;
}
return ir_analyze_err_to_int(ira, &instruction->base, casted_target, ira->codegen->err_tag_type);
}
static IrInstruction *ir_analyze_instruction_int_to_err(IrAnalyze *ira, IrInstructionIntToErr *instruction) {
IrInstruction *target = instruction->target->child;
if (type_is_invalid(target->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_target = ir_implicit_cast(ira, target, ira->codegen->err_tag_type);
if (type_is_invalid(casted_target->value.type))
return ira->codegen->invalid_instruction;
return ir_analyze_int_to_err(ira, &instruction->base, casted_target, ira->codegen->builtin_types.entry_global_error_set);
}
static IrInstruction *ir_analyze_instruction_bool_to_int(IrAnalyze *ira, IrInstructionBoolToInt *instruction) {
IrInstruction *target = instruction->target->child;
if (type_is_invalid(target->value.type))
return ira->codegen->invalid_instruction;
if (target->value.type->id != ZigTypeIdBool) {
ir_add_error(ira, instruction->target, buf_sprintf("expected bool, found '%s'",
buf_ptr(&target->value.type->name)));
return ira->codegen->invalid_instruction;
}
if (instr_is_comptime(target)) {
bool is_true;
if (!ir_resolve_bool(ira, target, &is_true))
return ira->codegen->invalid_instruction;
return ir_const_unsigned(ira, &instruction->base, is_true ? 1 : 0);
}
ZigType *u1_type = get_int_type(ira->codegen, false, 1);
return ir_resolve_cast(ira, &instruction->base, target, u1_type, CastOpBoolToInt, false);
}
static IrInstruction *ir_analyze_instruction_int_type(IrAnalyze *ira, IrInstructionIntType *instruction) {
IrInstruction *is_signed_value = instruction->is_signed->child;
bool is_signed;
if (!ir_resolve_bool(ira, is_signed_value, &is_signed))
return ira->codegen->invalid_instruction;
IrInstruction *bit_count_value = instruction->bit_count->child;
uint64_t bit_count;
if (!ir_resolve_unsigned(ira, bit_count_value, ira->codegen->builtin_types.entry_u16, &bit_count))
return ira->codegen->invalid_instruction;
return ir_const_type(ira, &instruction->base, get_int_type(ira->codegen, is_signed, (uint32_t)bit_count));
}
static IrInstruction *ir_analyze_instruction_vector_type(IrAnalyze *ira, IrInstructionVectorType *instruction) {
uint64_t len;
if (!ir_resolve_unsigned(ira, instruction->len->child, ira->codegen->builtin_types.entry_u32, &len))
return ira->codegen->invalid_instruction;
ZigType *elem_type = ir_resolve_type(ira, instruction->elem_type->child);
if (type_is_invalid(elem_type))
return ira->codegen->invalid_instruction;
if (!is_valid_vector_elem_type(elem_type)) {
ir_add_error(ira, instruction->elem_type,
buf_sprintf("vector element type must be integer, float, or pointer; '%s' is invalid",
buf_ptr(&elem_type->name)));
return ira->codegen->invalid_instruction;
}
ZigType *vector_type = get_vector_type(ira->codegen, len, elem_type);
return ir_const_type(ira, &instruction->base, vector_type);
}
static IrInstruction *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstructionBoolNot *instruction) {
IrInstruction *value = instruction->value->child;
if (type_is_invalid(value->value.type))
return ira->codegen->invalid_instruction;
ZigType *bool_type = ira->codegen->builtin_types.entry_bool;
IrInstruction *casted_value = ir_implicit_cast(ira, value, bool_type);
if (type_is_invalid(casted_value->value.type))
return ira->codegen->invalid_instruction;
if (instr_is_comptime(casted_value)) {
ConstExprValue *value = ir_resolve_const(ira, casted_value, UndefBad);
if (value == nullptr)
return ira->codegen->invalid_instruction;
return ir_const_bool(ira, &instruction->base, !value->data.x_bool);
}
IrInstruction *result = ir_build_bool_not(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, casted_value);
result->value.type = bool_type;
return result;
}
static IrInstruction *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructionMemset *instruction) {
Error err;
IrInstruction *dest_ptr = instruction->dest_ptr->child;
if (type_is_invalid(dest_ptr->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *byte_value = instruction->byte->child;
if (type_is_invalid(byte_value->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *count_value = instruction->count->child;
if (type_is_invalid(count_value->value.type))
return ira->codegen->invalid_instruction;
ZigType *dest_uncasted_type = dest_ptr->value.type;
bool dest_is_volatile = (dest_uncasted_type->id == ZigTypeIdPointer) &&
dest_uncasted_type->data.pointer.is_volatile;
ZigType *usize = ira->codegen->builtin_types.entry_usize;
ZigType *u8 = ira->codegen->builtin_types.entry_u8;
uint32_t dest_align;
if (dest_uncasted_type->id == ZigTypeIdPointer) {
if ((err = resolve_ptr_align(ira, dest_uncasted_type, &dest_align)))
return ira->codegen->invalid_instruction;
} else {
dest_align = get_abi_alignment(ira->codegen, u8);
}
ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile,
PtrLenUnknown, dest_align, 0, 0, false);
IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr);
if (type_is_invalid(casted_dest_ptr->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_byte = ir_implicit_cast(ira, byte_value, u8);
if (type_is_invalid(casted_byte->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_count = ir_implicit_cast(ira, count_value, usize);
if (type_is_invalid(casted_count->value.type))
return ira->codegen->invalid_instruction;
// TODO test this at comptime with u8 and non-u8 types
if (casted_dest_ptr->value.special == ConstValSpecialStatic &&
casted_byte->value.special == ConstValSpecialStatic &&
casted_count->value.special == ConstValSpecialStatic &&
casted_dest_ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr &&
casted_dest_ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar)
{
ConstExprValue *dest_ptr_val = &casted_dest_ptr->value;
ConstExprValue *dest_elements;
size_t start;
size_t bound_end;
switch (dest_ptr_val->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
case ConstPtrSpecialDiscard:
zig_unreachable();
case ConstPtrSpecialRef:
dest_elements = dest_ptr_val->data.x_ptr.data.ref.pointee;
start = 0;
bound_end = 1;
break;
case ConstPtrSpecialBaseArray:
{
ConstExprValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val;
expand_undef_array(ira->codegen, array_val);
dest_elements = array_val->data.x_array.data.s_none.elements;
start = dest_ptr_val->data.x_ptr.data.base_array.elem_index;
bound_end = array_val->type->data.array.len;
break;
}
case ConstPtrSpecialBaseStruct:
zig_panic("TODO memset on const inner struct");
case ConstPtrSpecialBaseErrorUnionCode:
zig_panic("TODO memset on const inner error union code");
case ConstPtrSpecialBaseErrorUnionPayload:
zig_panic("TODO memset on const inner error union payload");
case ConstPtrSpecialBaseOptionalPayload:
zig_panic("TODO memset on const inner optional payload");
case ConstPtrSpecialHardCodedAddr:
zig_unreachable();
case ConstPtrSpecialFunction:
zig_panic("TODO memset on ptr cast from function");
case ConstPtrSpecialNull:
zig_panic("TODO memset on null ptr");
}
size_t count = bigint_as_unsigned(&casted_count->value.data.x_bigint);
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->invalid_instruction;
}
ConstExprValue *byte_val = &casted_byte->value;
for (size_t i = start; i < end; i += 1) {
dest_elements[i] = *byte_val;
}
return ir_const_void(ira, &instruction->base);
}
IrInstruction *result = ir_build_memset(&ira->new_irb, instruction->base.scope, instruction->base.source_node,
casted_dest_ptr, casted_byte, casted_count);
result->value.type = ira->codegen->builtin_types.entry_void;
return result;
}
static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructionMemcpy *instruction) {
Error err;
IrInstruction *dest_ptr = instruction->dest_ptr->child;
if (type_is_invalid(dest_ptr->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *src_ptr = instruction->src_ptr->child;
if (type_is_invalid(src_ptr->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *count_value = instruction->count->child;
if (type_is_invalid(count_value->value.type))
return ira->codegen->invalid_instruction;
ZigType *u8 = ira->codegen->builtin_types.entry_u8;
ZigType *dest_uncasted_type = dest_ptr->value.type;
ZigType *src_uncasted_type = src_ptr->value.type;
bool dest_is_volatile = (dest_uncasted_type->id == ZigTypeIdPointer) &&
dest_uncasted_type->data.pointer.is_volatile;
bool src_is_volatile = (src_uncasted_type->id == ZigTypeIdPointer) &&
src_uncasted_type->data.pointer.is_volatile;
uint32_t dest_align;
if (dest_uncasted_type->id == ZigTypeIdPointer) {
if ((err = resolve_ptr_align(ira, dest_uncasted_type, &dest_align)))
return ira->codegen->invalid_instruction;
} else {
dest_align = get_abi_alignment(ira->codegen, u8);
}
uint32_t src_align;
if (src_uncasted_type->id == ZigTypeIdPointer) {
if ((err = resolve_ptr_align(ira, src_uncasted_type, &src_align)))
return ira->codegen->invalid_instruction;
} else {
src_align = get_abi_alignment(ira->codegen, u8);
}
ZigType *usize = ira->codegen->builtin_types.entry_usize;
ZigType *u8_ptr_mut = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile,
PtrLenUnknown, dest_align, 0, 0, false);
ZigType *u8_ptr_const = get_pointer_to_type_extra(ira->codegen, u8, true, src_is_volatile,
PtrLenUnknown, src_align, 0, 0, false);
IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr_mut);
if (type_is_invalid(casted_dest_ptr->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_src_ptr = ir_implicit_cast(ira, src_ptr, u8_ptr_const);
if (type_is_invalid(casted_src_ptr->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_count = ir_implicit_cast(ira, count_value, usize);
if (type_is_invalid(casted_count->value.type))
return ira->codegen->invalid_instruction;
// TODO test this at comptime with u8 and non-u8 types
// TODO test with dest ptr being a global runtime variable
if (casted_dest_ptr->value.special == ConstValSpecialStatic &&
casted_src_ptr->value.special == ConstValSpecialStatic &&
casted_count->value.special == ConstValSpecialStatic &&
casted_dest_ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr)
{
size_t count = bigint_as_unsigned(&casted_count->value.data.x_bigint);
ConstExprValue *dest_ptr_val = &casted_dest_ptr->value;
ConstExprValue *dest_elements;
size_t dest_start;
size_t dest_end;
switch (dest_ptr_val->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
case ConstPtrSpecialDiscard:
zig_unreachable();
case ConstPtrSpecialRef:
dest_elements = dest_ptr_val->data.x_ptr.data.ref.pointee;
dest_start = 0;
dest_end = 1;
break;
case ConstPtrSpecialBaseArray:
{
ConstExprValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val;
expand_undef_array(ira->codegen, array_val);
dest_elements = array_val->data.x_array.data.s_none.elements;
dest_start = dest_ptr_val->data.x_ptr.data.base_array.elem_index;
dest_end = array_val->type->data.array.len;
break;
}
case ConstPtrSpecialBaseStruct:
zig_panic("TODO memcpy on const inner struct");
case ConstPtrSpecialBaseErrorUnionCode:
zig_panic("TODO memcpy on const inner error union code");
case ConstPtrSpecialBaseErrorUnionPayload:
zig_panic("TODO memcpy on const inner error union payload");
case ConstPtrSpecialBaseOptionalPayload:
zig_panic("TODO memcpy on const inner optional payload");
case ConstPtrSpecialHardCodedAddr:
zig_unreachable();
case ConstPtrSpecialFunction:
zig_panic("TODO memcpy on ptr cast from function");
case ConstPtrSpecialNull:
zig_panic("TODO memcpy on null ptr");
}
if (dest_start + count > dest_end) {
ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds pointer access"));
return ira->codegen->invalid_instruction;
}
ConstExprValue *src_ptr_val = &casted_src_ptr->value;
ConstExprValue *src_elements;
size_t src_start;
size_t src_end;
switch (src_ptr_val->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
case ConstPtrSpecialDiscard:
zig_unreachable();
case ConstPtrSpecialRef:
src_elements = src_ptr_val->data.x_ptr.data.ref.pointee;
src_start = 0;
src_end = 1;
break;
case ConstPtrSpecialBaseArray:
{
ConstExprValue *array_val = src_ptr_val->data.x_ptr.data.base_array.array_val;
expand_undef_array(ira->codegen, array_val);
src_elements = array_val->data.x_array.data.s_none.elements;
src_start = src_ptr_val->data.x_ptr.data.base_array.elem_index;
src_end = array_val->type->data.array.len;
break;
}
case ConstPtrSpecialBaseStruct:
zig_panic("TODO memcpy on const inner struct");
case ConstPtrSpecialBaseErrorUnionCode:
zig_panic("TODO memcpy on const inner error union code");
case ConstPtrSpecialBaseErrorUnionPayload:
zig_panic("TODO memcpy on const inner error union payload");
case ConstPtrSpecialBaseOptionalPayload:
zig_panic("TODO memcpy on const inner optional payload");
case ConstPtrSpecialHardCodedAddr:
zig_unreachable();
case ConstPtrSpecialFunction:
zig_panic("TODO memcpy on ptr cast from function");
case ConstPtrSpecialNull:
zig_panic("TODO memcpy on null ptr");
}
if (src_start + count > src_end) {
ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds pointer access"));
return ira->codegen->invalid_instruction;
}
// 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];
}
return ir_const_void(ira, &instruction->base);
}
IrInstruction *result = ir_build_memcpy(&ira->new_irb, instruction->base.scope, instruction->base.source_node,
casted_dest_ptr, casted_src_ptr, casted_count);
result->value.type = ira->codegen->builtin_types.entry_void;
return result;
}
static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructionSlice *instruction) {
IrInstruction *ptr_ptr = instruction->ptr->child;
if (type_is_invalid(ptr_ptr->value.type))
return ira->codegen->invalid_instruction;
ZigType *ptr_ptr_type = ptr_ptr->value.type;
assert(ptr_ptr_type->id == ZigTypeIdPointer);
ZigType *array_type = ptr_ptr_type->data.pointer.child_type;
IrInstruction *start = instruction->start->child;
if (type_is_invalid(start->value.type))
return ira->codegen->invalid_instruction;
ZigType *usize = ira->codegen->builtin_types.entry_usize;
IrInstruction *casted_start = ir_implicit_cast(ira, start, usize);
if (type_is_invalid(casted_start->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *end;
if (instruction->end) {
end = instruction->end->child;
if (type_is_invalid(end->value.type))
return ira->codegen->invalid_instruction;
end = ir_implicit_cast(ira, end, usize);
if (type_is_invalid(end->value.type))
return ira->codegen->invalid_instruction;
} else {
end = nullptr;
}
ZigType *return_type;
if (array_type->id == ZigTypeIdArray) {
bool is_comptime_const = ptr_ptr->value.special == ConstValSpecialStatic &&
ptr_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst;
ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type,
ptr_ptr_type->data.pointer.is_const || is_comptime_const,
ptr_ptr_type->data.pointer.is_volatile,
PtrLenUnknown,
ptr_ptr_type->data.pointer.explicit_alignment, 0, 0, false);
return_type = get_slice_type(ira->codegen, slice_ptr_type);
} else if (array_type->id == ZigTypeIdPointer) {
if (array_type->data.pointer.ptr_len == PtrLenSingle) {
ZigType *main_type = array_type->data.pointer.child_type;
if (main_type->id == ZigTypeIdArray) {
ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen,
main_type->data.pointer.child_type,
array_type->data.pointer.is_const, array_type->data.pointer.is_volatile,
PtrLenUnknown,
array_type->data.pointer.explicit_alignment, 0, 0, false);
return_type = get_slice_type(ira->codegen, slice_ptr_type);
} else {
ir_add_error(ira, &instruction->base, buf_sprintf("slice of single-item pointer"));
return ira->codegen->invalid_instruction;
}
} else {
if (array_type->data.pointer.ptr_len == PtrLenC) {
array_type = adjust_ptr_len(ira->codegen, array_type, PtrLenUnknown);
}
return_type = get_slice_type(ira->codegen, array_type);
if (!end) {
ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value"));
return ira->codegen->invalid_instruction;
}
}
} else if (is_slice(array_type)) {
ZigType *ptr_type = array_type->data.structure.fields[slice_ptr_index].type_entry;
return_type = get_slice_type(ira->codegen, ptr_type);
} else {
ir_add_error(ira, &instruction->base,
buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name)));
return ira->codegen->invalid_instruction;
}
if (instr_is_comptime(ptr_ptr) &&
value_is_comptime(&casted_start->value) &&
(!end || value_is_comptime(&end->value)))
{
ConstExprValue *array_val;
ConstExprValue *parent_ptr;
size_t abs_offset;
size_t rel_end;
bool ptr_is_undef = false;
if (array_type->id == ZigTypeIdArray ||
(array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle))
{
if (array_type->id == ZigTypeIdPointer) {
ZigType *child_array_type = array_type->data.pointer.child_type;
assert(child_array_type->id == ZigTypeIdArray);
parent_ptr = const_ptr_pointee(ira, ira->codegen, &ptr_ptr->value, instruction->base.source_node);
if (parent_ptr == nullptr)
return ira->codegen->invalid_instruction;
array_val = const_ptr_pointee(ira, ira->codegen, parent_ptr, instruction->base.source_node);
if (array_val == nullptr)
return ira->codegen->invalid_instruction;
rel_end = child_array_type->data.array.len;
abs_offset = 0;
} else {
array_val = const_ptr_pointee(ira, ira->codegen, &ptr_ptr->value, instruction->base.source_node);
if (array_val == nullptr)
return ira->codegen->invalid_instruction;
rel_end = array_type->data.array.len;
parent_ptr = nullptr;
abs_offset = 0;
}
} else if (array_type->id == ZigTypeIdPointer) {
assert(array_type->data.pointer.ptr_len == PtrLenUnknown);
parent_ptr = const_ptr_pointee(ira, ira->codegen, &ptr_ptr->value, instruction->base.source_node);
if (parent_ptr == nullptr)
return ira->codegen->invalid_instruction;
if (parent_ptr->special == ConstValSpecialUndef) {
array_val = nullptr;
abs_offset = 0;
rel_end = SIZE_MAX;
ptr_is_undef = true;
} else switch (parent_ptr->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
case ConstPtrSpecialDiscard:
zig_unreachable();
case ConstPtrSpecialRef:
if (parent_ptr->data.x_ptr.data.ref.pointee->type->id == ZigTypeIdArray) {
array_val = parent_ptr->data.x_ptr.data.ref.pointee;
abs_offset = 0;
rel_end = array_val->type->data.array.len;
} else {
array_val = nullptr;
abs_offset = SIZE_MAX;
rel_end = 1;
}
break;
case ConstPtrSpecialBaseArray:
array_val = parent_ptr->data.x_ptr.data.base_array.array_val;
abs_offset = parent_ptr->data.x_ptr.data.base_array.elem_index;
rel_end = array_val->type->data.array.len - abs_offset;
break;
case ConstPtrSpecialBaseStruct:
zig_panic("TODO slice const inner struct");
case ConstPtrSpecialBaseErrorUnionCode:
zig_panic("TODO slice const inner error union code");
case ConstPtrSpecialBaseErrorUnionPayload:
zig_panic("TODO slice const inner error union payload");
case ConstPtrSpecialBaseOptionalPayload:
zig_panic("TODO slice const inner optional payload");
case ConstPtrSpecialHardCodedAddr:
array_val = nullptr;
abs_offset = 0;
rel_end = SIZE_MAX;
break;
case ConstPtrSpecialFunction:
zig_panic("TODO slice of ptr cast from function");
case ConstPtrSpecialNull:
zig_panic("TODO slice of null ptr");
}
} else if (is_slice(array_type)) {
ConstExprValue *slice_ptr = const_ptr_pointee(ira, ira->codegen, &ptr_ptr->value, instruction->base.source_node);
if (slice_ptr == nullptr)
return ira->codegen->invalid_instruction;
parent_ptr = &slice_ptr->data.x_struct.fields[slice_ptr_index];
if (parent_ptr->special == ConstValSpecialUndef) {
ir_add_error(ira, &instruction->base, buf_sprintf("slice of undefined"));
return ira->codegen->invalid_instruction;
}
ConstExprValue *len_val = &slice_ptr->data.x_struct.fields[slice_len_index];
switch (parent_ptr->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
case ConstPtrSpecialDiscard:
zig_unreachable();
case ConstPtrSpecialRef:
array_val = nullptr;
abs_offset = SIZE_MAX;
rel_end = 1;
break;
case ConstPtrSpecialBaseArray:
array_val = parent_ptr->data.x_ptr.data.base_array.array_val;
abs_offset = parent_ptr->data.x_ptr.data.base_array.elem_index;
rel_end = bigint_as_unsigned(&len_val->data.x_bigint);
break;
case ConstPtrSpecialBaseStruct:
zig_panic("TODO slice const inner struct");
case ConstPtrSpecialBaseErrorUnionCode:
zig_panic("TODO slice const inner error union code");
case ConstPtrSpecialBaseErrorUnionPayload:
zig_panic("TODO slice const inner error union payload");
case ConstPtrSpecialBaseOptionalPayload:
zig_panic("TODO slice const inner optional payload");
case ConstPtrSpecialHardCodedAddr:
array_val = nullptr;
abs_offset = 0;
rel_end = bigint_as_unsigned(&len_val->data.x_bigint);
break;
case ConstPtrSpecialFunction:
zig_panic("TODO slice of slice cast from function");
case ConstPtrSpecialNull:
zig_panic("TODO slice of null");
}
} else {
zig_unreachable();
}
uint64_t start_scalar = bigint_as_unsigned(&casted_start->value.data.x_bigint);
if (!ptr_is_undef && start_scalar > rel_end) {
ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));
return ira->codegen->invalid_instruction;
}
uint64_t end_scalar;
if (end) {
end_scalar = bigint_as_unsigned(&end->value.data.x_bigint);
} else {
end_scalar = rel_end;
}
if (!ptr_is_undef) {
if (end_scalar > rel_end) {
ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));
return ira->codegen->invalid_instruction;
}
if (start_scalar > end_scalar) {
ir_add_error(ira, &instruction->base, buf_sprintf("slice start is greater than end"));
return ira->codegen->invalid_instruction;
}
}
if (ptr_is_undef && start_scalar != end_scalar) {
ir_add_error(ira, &instruction->base, buf_sprintf("non-zero length slice of undefined pointer"));
return ira->codegen->invalid_instruction;
}
IrInstruction *result = ir_const(ira, &instruction->base, return_type);
ConstExprValue *out_val = &result->value;
out_val->data.x_struct.fields = create_const_vals(2);
ConstExprValue *ptr_val = &out_val->data.x_struct.fields[slice_ptr_index];
if (array_val) {
size_t index = abs_offset + start_scalar;
bool is_const = slice_is_const(return_type);
init_const_ptr_array(ira->codegen, ptr_val, array_val, index, is_const, PtrLenUnknown);
if (array_type->id == ZigTypeIdArray) {
ptr_val->data.x_ptr.mut = ptr_ptr->value.data.x_ptr.mut;
} else if (is_slice(array_type)) {
ptr_val->data.x_ptr.mut = parent_ptr->data.x_ptr.mut;
} else if (array_type->id == ZigTypeIdPointer) {
ptr_val->data.x_ptr.mut = parent_ptr->data.x_ptr.mut;
}
} else if (ptr_is_undef) {
ptr_val->type = get_pointer_to_type(ira->codegen, parent_ptr->type->data.pointer.child_type,
slice_is_const(return_type));
ptr_val->special = ConstValSpecialUndef;
} else switch (parent_ptr->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
case ConstPtrSpecialDiscard:
zig_unreachable();
case ConstPtrSpecialRef:
init_const_ptr_ref(ira->codegen, ptr_val,
parent_ptr->data.x_ptr.data.ref.pointee, slice_is_const(return_type));
break;
case ConstPtrSpecialBaseArray:
zig_unreachable();
case ConstPtrSpecialBaseStruct:
zig_panic("TODO");
case ConstPtrSpecialBaseErrorUnionCode:
zig_panic("TODO");
case ConstPtrSpecialBaseErrorUnionPayload:
zig_panic("TODO");
case ConstPtrSpecialBaseOptionalPayload:
zig_panic("TODO");
case ConstPtrSpecialHardCodedAddr:
init_const_ptr_hard_coded_addr(ira->codegen, ptr_val,
parent_ptr->type->data.pointer.child_type,
parent_ptr->data.x_ptr.data.hard_coded_addr.addr + start_scalar,
slice_is_const(return_type));
break;
case ConstPtrSpecialFunction:
zig_panic("TODO");
case ConstPtrSpecialNull:
zig_panic("TODO");
}
ConstExprValue *len_val = &out_val->data.x_struct.fields[slice_len_index];
init_const_usize(ira->codegen, len_val, end_scalar - start_scalar);
return result;
}
IrInstruction *new_instruction = ir_build_slice(&ira->new_irb,
instruction->base.scope, instruction->base.source_node,
ptr_ptr, casted_start, end, instruction->safety_check_on);
new_instruction->value.type = return_type;
ir_add_alloca(ira, new_instruction, return_type);
return new_instruction;
}
static IrInstruction *ir_analyze_instruction_member_count(IrAnalyze *ira, IrInstructionMemberCount *instruction) {
Error err;
IrInstruction *container = instruction->container->child;
if (type_is_invalid(container->value.type))
return ira->codegen->invalid_instruction;
ZigType *container_type = ir_resolve_type(ira, container);
if ((err = ensure_complete_type(ira->codegen, container_type)))
return ira->codegen->invalid_instruction;
uint64_t result;
if (type_is_invalid(container_type)) {
return ira->codegen->invalid_instruction;
} else if (container_type->id == ZigTypeIdEnum) {
result = container_type->data.enumeration.src_field_count;
} else if (container_type->id == ZigTypeIdStruct) {
result = container_type->data.structure.src_field_count;
} else if (container_type->id == ZigTypeIdUnion) {
result = container_type->data.unionation.src_field_count;
} else if (container_type->id == ZigTypeIdErrorSet) {
if (!resolve_inferred_error_set(ira->codegen, container_type, instruction->base.source_node)) {
return ira->codegen->invalid_instruction;
}
if (type_is_global_error_set(container_type)) {
ir_add_error(ira, &instruction->base, buf_sprintf("global error set member count not available at comptime"));
return ira->codegen->invalid_instruction;
}
result = container_type->data.error_set.err_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->invalid_instruction;
}
return ir_const_unsigned(ira, &instruction->base, result);
}
static IrInstruction *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInstructionMemberType *instruction) {
Error err;
IrInstruction *container_type_value = instruction->container_type->child;
ZigType *container_type = ir_resolve_type(ira, container_type_value);
if (type_is_invalid(container_type))
return ira->codegen->invalid_instruction;
if ((err = ensure_complete_type(ira->codegen, container_type)))
return ira->codegen->invalid_instruction;
uint64_t member_index;
IrInstruction *index_value = instruction->member_index->child;
if (!ir_resolve_usize(ira, index_value, &member_index))
return ira->codegen->invalid_instruction;
if (container_type->id == ZigTypeIdStruct) {
if (member_index >= container_type->data.structure.src_field_count) {
ir_add_error(ira, index_value,
buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members",
member_index, buf_ptr(&container_type->name), container_type->data.structure.src_field_count));
return ira->codegen->invalid_instruction;
}
TypeStructField *field = &container_type->data.structure.fields[member_index];
return ir_const_type(ira, &instruction->base, field->type_entry);
} else if (container_type->id == ZigTypeIdUnion) {
if (member_index >= container_type->data.unionation.src_field_count) {
ir_add_error(ira, index_value,
buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members",
member_index, buf_ptr(&container_type->name), container_type->data.unionation.src_field_count));
return ira->codegen->invalid_instruction;
}
TypeUnionField *field = &container_type->data.unionation.fields[member_index];
return ir_const_type(ira, &instruction->base, field->type_entry);
} else {
ir_add_error(ira, container_type_value,
buf_sprintf("type '%s' does not support @memberType", buf_ptr(&container_type->name)));
return ira->codegen->invalid_instruction;
}
}
static IrInstruction *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInstructionMemberName *instruction) {
Error err;
IrInstruction *container_type_value = instruction->container_type->child;
ZigType *container_type = ir_resolve_type(ira, container_type_value);
if (type_is_invalid(container_type))
return ira->codegen->invalid_instruction;
if ((err = ensure_complete_type(ira->codegen, container_type)))
return ira->codegen->invalid_instruction;
uint64_t member_index;
IrInstruction *index_value = instruction->member_index->child;
if (!ir_resolve_usize(ira, index_value, &member_index))
return ira->codegen->invalid_instruction;
if (container_type->id == ZigTypeIdStruct) {
if (member_index >= container_type->data.structure.src_field_count) {
ir_add_error(ira, index_value,
buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members",
member_index, buf_ptr(&container_type->name), container_type->data.structure.src_field_count));
return ira->codegen->invalid_instruction;
}
TypeStructField *field = &container_type->data.structure.fields[member_index];
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
init_const_str_lit(ira->codegen, &result->value, field->name);
return result;
} else if (container_type->id == ZigTypeIdEnum) {
if (member_index >= container_type->data.enumeration.src_field_count) {
ir_add_error(ira, index_value,
buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members",
member_index, buf_ptr(&container_type->name), container_type->data.enumeration.src_field_count));
return ira->codegen->invalid_instruction;
}
TypeEnumField *field = &container_type->data.enumeration.fields[member_index];
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
init_const_str_lit(ira->codegen, &result->value, field->name);
return result;
} else if (container_type->id == ZigTypeIdUnion) {
if (member_index >= container_type->data.unionation.src_field_count) {
ir_add_error(ira, index_value,
buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members",
member_index, buf_ptr(&container_type->name), container_type->data.unionation.src_field_count));
return ira->codegen->invalid_instruction;
}
TypeUnionField *field = &container_type->data.unionation.fields[member_index];
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
init_const_str_lit(ira->codegen, &result->value, field->name);
return result;
} else {
ir_add_error(ira, container_type_value,
buf_sprintf("type '%s' does not support @memberName", buf_ptr(&container_type->name)));
return ira->codegen->invalid_instruction;
}
}
static IrInstruction *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstructionBreakpoint *instruction) {
IrInstruction *result = ir_build_breakpoint(&ira->new_irb,
instruction->base.scope, instruction->base.source_node);
result->value.type = ira->codegen->builtin_types.entry_void;
return result;
}
static IrInstruction *ir_analyze_instruction_return_address(IrAnalyze *ira, IrInstructionReturnAddress *instruction) {
IrInstruction *result = ir_build_return_address(&ira->new_irb,
instruction->base.scope, instruction->base.source_node);
result->value.type = ira->codegen->builtin_types.entry_usize;
return result;
}
static IrInstruction *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrInstructionFrameAddress *instruction) {
IrInstruction *result = ir_build_frame_address(&ira->new_irb,
instruction->base.scope, instruction->base.source_node);
result->value.type = ira->codegen->builtin_types.entry_usize;
return result;
}
static IrInstruction *ir_analyze_instruction_handle(IrAnalyze *ira, IrInstructionHandle *instruction) {
IrInstruction *result = ir_build_handle(&ira->new_irb, instruction->base.scope, instruction->base.source_node);
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
assert(fn_entry != nullptr);
result->value.type = get_promise_type(ira->codegen, fn_entry->type_entry->data.fn.fn_type_id.return_type);
return result;
}
static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) {
Error err;
IrInstruction *type_value = instruction->type_value->child;
if (type_is_invalid(type_value->value.type))
return ira->codegen->invalid_instruction;
ZigType *type_entry = ir_resolve_type(ira, type_value);
if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusAlignmentKnown)))
return ira->codegen->invalid_instruction;
switch (type_entry->id) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdMetaType:
case ZigTypeIdUnreachable:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdVoid:
case ZigTypeIdOpaque:
ir_add_error(ira, instruction->type_value,
buf_sprintf("no align available for type '%s'", buf_ptr(&type_entry->name)));
return ira->codegen->invalid_instruction;
case ZigTypeIdBool:
case ZigTypeIdInt:
case ZigTypeIdFloat:
case ZigTypeIdPointer:
case ZigTypeIdPromise:
case ZigTypeIdArray:
case ZigTypeIdStruct:
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdVector:
{
uint64_t align_in_bytes = get_abi_alignment(ira->codegen, type_entry);
return ir_const_unsigned(ira, &instruction->base, align_in_bytes);
}
}
zig_unreachable();
}
static IrInstruction *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstructionOverflowOp *instruction) {
Error err;
IrInstruction *type_value = instruction->type_value->child;
if (type_is_invalid(type_value->value.type))
return ira->codegen->invalid_instruction;
ZigType *dest_type = ir_resolve_type(ira, type_value);
if (type_is_invalid(dest_type))
return ira->codegen->invalid_instruction;
if (dest_type->id != ZigTypeIdInt) {
ir_add_error(ira, type_value,
buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *op1 = instruction->op1->child;
if (type_is_invalid(op1->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, dest_type);
if (type_is_invalid(casted_op1->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *op2 = instruction->op2->child;
if (type_is_invalid(op2->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_op2;
if (instruction->op == IrOverflowOpShl) {
ZigType *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen,
dest_type->data.integral.bit_count - 1);
casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type);
} else {
casted_op2 = ir_implicit_cast(ira, op2, dest_type);
}
if (type_is_invalid(casted_op2->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *result_ptr = instruction->result_ptr->child;
if (type_is_invalid(result_ptr->value.type))
return ira->codegen->invalid_instruction;
ZigType *expected_ptr_type;
if (result_ptr->value.type->id == ZigTypeIdPointer) {
uint32_t alignment;
if ((err = resolve_ptr_align(ira, result_ptr->value.type, &alignment)))
return ira->codegen->invalid_instruction;
expected_ptr_type = get_pointer_to_type_extra(ira->codegen, dest_type,
false, result_ptr->value.type->data.pointer.is_volatile,
PtrLenSingle,
alignment, 0, 0, false);
} else {
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 (type_is_invalid(casted_result_ptr->value.type))
return ira->codegen->invalid_instruction;
if (casted_op1->value.special == ConstValSpecialStatic &&
casted_op2->value.special == ConstValSpecialStatic &&
casted_result_ptr->value.special == ConstValSpecialStatic)
{
BigInt *op1_bigint = &casted_op1->value.data.x_bigint;
BigInt *op2_bigint = &casted_op2->value.data.x_bigint;
ConstExprValue *pointee_val = const_ptr_pointee(ira, ira->codegen, &casted_result_ptr->value, casted_result_ptr->source_node);
if (pointee_val == nullptr)
return ira->codegen->invalid_instruction;
BigInt *dest_bigint = &pointee_val->data.x_bigint;
switch (instruction->op) {
case IrOverflowOpAdd:
bigint_add(dest_bigint, op1_bigint, op2_bigint);
break;
case IrOverflowOpSub:
bigint_sub(dest_bigint, op1_bigint, op2_bigint);
break;
case IrOverflowOpMul:
bigint_mul(dest_bigint, op1_bigint, op2_bigint);
break;
case IrOverflowOpShl:
bigint_shl(dest_bigint, op1_bigint, op2_bigint);
break;
}
bool result_bool = false;
if (!bigint_fits_in_bits(dest_bigint, dest_type->data.integral.bit_count,
dest_type->data.integral.is_signed))
{
result_bool = true;
BigInt tmp_bigint;
bigint_init_bigint(&tmp_bigint, dest_bigint);
bigint_truncate(dest_bigint, &tmp_bigint, dest_type->data.integral.bit_count,
dest_type->data.integral.is_signed);
}
pointee_val->special = ConstValSpecialStatic;
return ir_const_bool(ira, &instruction->base, result_bool);
}
IrInstruction *result = ir_build_overflow_op(&ira->new_irb,
instruction->base.scope, instruction->base.source_node,
instruction->op, type_value, casted_op1, casted_op2, casted_result_ptr, dest_type);
result->value.type = ira->codegen->builtin_types.entry_bool;
return result;
}
static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstructionTestErr *instruction) {
IrInstruction *value = instruction->value->child;
if (type_is_invalid(value->value.type))
return ira->codegen->invalid_instruction;
ZigType *type_entry = value->value.type;
if (type_is_invalid(type_entry)) {
return ira->codegen->invalid_instruction;
} else if (type_entry->id == ZigTypeIdErrorUnion) {
if (instr_is_comptime(value)) {
ConstExprValue *err_union_val = ir_resolve_const(ira, value, UndefBad);
if (!err_union_val)
return ira->codegen->invalid_instruction;
if (err_union_val->special != ConstValSpecialRuntime) {
ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set;
return ir_const_bool(ira, &instruction->base, (err != nullptr));
}
}
ZigType *err_set_type = type_entry->data.error_union.err_set_type;
if (!resolve_inferred_error_set(ira->codegen, err_set_type, instruction->base.source_node)) {
return ira->codegen->invalid_instruction;
}
if (!type_is_global_error_set(err_set_type) &&
err_set_type->data.error_set.err_count == 0)
{
assert(err_set_type->data.error_set.infer_fn == nullptr);
return ir_const_bool(ira, &instruction->base, false);
}
IrInstruction *result = ir_build_test_err(&ira->new_irb,
instruction->base.scope, instruction->base.source_node, value);
result->value.type = ira->codegen->builtin_types.entry_bool;
return result;
} else if (type_entry->id == ZigTypeIdErrorSet) {
return ir_const_bool(ira, &instruction->base, true);
} else {
return ir_const_bool(ira, &instruction->base, false);
}
}
static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, IrInstructionUnwrapErrCode *instruction) {
IrInstruction *base_ptr = instruction->err_union->child;
if (type_is_invalid(base_ptr->value.type))
return ira->codegen->invalid_instruction;
ZigType *ptr_type = base_ptr->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 == ZigTypeIdPointer);
ZigType *type_entry = ptr_type->data.pointer.child_type;
if (type_is_invalid(type_entry))
return ira->codegen->invalid_instruction;
if (type_entry->id != ZigTypeIdErrorUnion) {
ir_add_error(ira, base_ptr,
buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name)));
return ira->codegen->invalid_instruction;
}
if (instr_is_comptime(base_ptr)) {
ConstExprValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad);
if (!ptr_val)
return ira->codegen->invalid_instruction;
ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.source_node);
if (err_union_val == nullptr)
return ira->codegen->invalid_instruction;
if (err_union_val->special != ConstValSpecialRuntime) {
ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set;
assert(err);
IrInstruction *result = ir_const(ira, &instruction->base,
type_entry->data.error_union.err_set_type);
result->value.data.x_err_set = err;
return result;
}
}
IrInstruction *result = ir_build_unwrap_err_code(&ira->new_irb,
instruction->base.scope, instruction->base.source_node, base_ptr);
result->value.type = type_entry->data.error_union.err_set_type;
return result;
}
static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira,
IrInstructionUnwrapErrPayload *instruction)
{
assert(instruction->value->child);
IrInstruction *value = instruction->value->child;
if (type_is_invalid(value->value.type))
return ira->codegen->invalid_instruction;
ZigType *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 == ZigTypeIdPointer);
ZigType *type_entry = ptr_type->data.pointer.child_type;
if (type_is_invalid(type_entry))
return ira->codegen->invalid_instruction;
if (type_entry->id != ZigTypeIdErrorUnion) {
ir_add_error(ira, value,
buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name)));
return ira->codegen->invalid_instruction;
}
ZigType *payload_type = type_entry->data.error_union.payload_type;
if (type_is_invalid(payload_type))
return ira->codegen->invalid_instruction;
ZigType *result_type = get_pointer_to_type_extra(ira->codegen, payload_type,
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
PtrLenSingle, 0, 0, 0, false);
if (instr_is_comptime(value)) {
ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad);
if (!ptr_val)
return ira->codegen->invalid_instruction;
ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.source_node);
if (err_union_val == nullptr)
return ira->codegen->invalid_instruction;
if (err_union_val->special != ConstValSpecialRuntime) {
ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set;
if (err != nullptr) {
ir_add_error(ira, &instruction->base,
buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *result = ir_const(ira, &instruction->base, result_type);
result->value.data.x_ptr.special = ConstPtrSpecialRef;
result->value.data.x_ptr.data.ref.pointee = err_union_val->data.x_err_union.payload;
return result;
}
}
IrInstruction *result = ir_build_unwrap_err_payload(&ira->new_irb,
instruction->base.scope, instruction->base.source_node, value, instruction->safety_check_on);
result->value.type = result_type;
return result;
}
static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnProto *instruction) {
AstNode *proto_node = instruction->base.source_node;
assert(proto_node->type == NodeTypeFnProto);
if (proto_node->data.fn_proto.auto_err_set) {
ir_add_error(ira, &instruction->base,
buf_sprintf("inferring error set of return type valid only for function definitions"));
return ira->codegen->invalid_instruction;
}
FnTypeId fn_type_id = {0};
init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length);
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);
bool param_is_var_args = param_node->data.param_decl.is_var_args;
if (param_is_var_args) {
if (fn_type_id.cc == CallingConventionC) {
fn_type_id.param_count = fn_type_id.next_param_index;
continue;
} else if (fn_type_id.cc == CallingConventionUnspecified) {
return ir_const_type(ira, &instruction->base, get_generic_fn_type(ira->codegen, &fn_type_id));
} else {
zig_unreachable();
}
}
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;
if (instruction->param_types[fn_type_id.next_param_index] == nullptr) {
param_info->type = nullptr;
return ir_const_type(ira, &instruction->base, get_generic_fn_type(ira->codegen, &fn_type_id));
} else {
IrInstruction *param_type_value = instruction->param_types[fn_type_id.next_param_index]->child;
if (type_is_invalid(param_type_value->value.type))
return ira->codegen->invalid_instruction;
ZigType *param_type = ir_resolve_type(ira, param_type_value);
switch (type_requires_comptime(ira->codegen, param_type)) {
case ReqCompTimeYes:
if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
ir_add_error(ira, param_type_value,
buf_sprintf("parameter of type '%s' not allowed in function with calling convention '%s'",
buf_ptr(&param_type->name), calling_convention_name(fn_type_id.cc)));
return ira->codegen->invalid_instruction;
}
param_info->type = param_type;
fn_type_id.next_param_index += 1;
return ir_const_type(ira, &instruction->base, get_generic_fn_type(ira->codegen, &fn_type_id));
case ReqCompTimeInvalid:
return ira->codegen->invalid_instruction;
case ReqCompTimeNo:
break;
}
if (!type_has_bits(param_type) && !calling_convention_allows_zig_types(fn_type_id.cc)) {
ir_add_error(ira, param_type_value,
buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'",
buf_ptr(&param_type->name), calling_convention_name(fn_type_id.cc)));
return ira->codegen->invalid_instruction;
}
param_info->type = param_type;
}
}
if (instruction->align_value != nullptr) {
if (!ir_resolve_align(ira, instruction->align_value->child, &fn_type_id.alignment))
return ira->codegen->invalid_instruction;
}
IrInstruction *return_type_value = instruction->return_type->child;
fn_type_id.return_type = ir_resolve_type(ira, return_type_value);
if (type_is_invalid(fn_type_id.return_type))
return ira->codegen->invalid_instruction;
if (fn_type_id.return_type->id == ZigTypeIdOpaque) {
ir_add_error(ira, instruction->return_type,
buf_sprintf("return type cannot be opaque"));
return ira->codegen->invalid_instruction;
}
if (fn_type_id.cc == CallingConventionAsync) {
if (instruction->async_allocator_type_value == nullptr) {
ir_add_error(ira, &instruction->base,
buf_sprintf("async fn proto missing allocator type"));
return ira->codegen->invalid_instruction;
}
IrInstruction *async_allocator_type_value = instruction->async_allocator_type_value->child;
fn_type_id.async_allocator_type = ir_resolve_type(ira, async_allocator_type_value);
if (type_is_invalid(fn_type_id.async_allocator_type))
return ira->codegen->invalid_instruction;
}
return ir_const_type(ira, &instruction->base, get_fn_type(ira->codegen, &fn_type_id));
}
static IrInstruction *ir_analyze_instruction_test_comptime(IrAnalyze *ira, IrInstructionTestComptime *instruction) {
IrInstruction *value = instruction->value->child;
if (type_is_invalid(value->value.type))
return ira->codegen->invalid_instruction;
return ir_const_bool(ira, &instruction->base, instr_is_comptime(value));
}
static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
IrInstructionCheckSwitchProngs *instruction)
{
IrInstruction *target_value = instruction->target_value->child;
ZigType *switch_type = target_value->value.type;
if (type_is_invalid(switch_type))
return ira->codegen->invalid_instruction;
if (switch_type->id == ZigTypeIdEnum) {
HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> field_prev_uses = {};
field_prev_uses.init(switch_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_uncasted = range->start->child;
if (type_is_invalid(start_value_uncasted->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *start_value = ir_implicit_cast(ira, start_value_uncasted, switch_type);
if (type_is_invalid(start_value->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *end_value_uncasted = range->end->child;
if (type_is_invalid(end_value_uncasted->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *end_value = ir_implicit_cast(ira, end_value_uncasted, switch_type);
if (type_is_invalid(end_value->value.type))
return ira->codegen->invalid_instruction;
BigInt start_index;
bigint_init_bigint(&start_index, &start_value->value.data.x_enum_tag);
assert(end_value->value.type->id == ZigTypeIdEnum);
BigInt end_index;
bigint_init_bigint(&end_index, &end_value->value.data.x_enum_tag);
BigInt field_index;
bigint_init_bigint(&field_index, &start_index);
for (;;) {
Cmp cmp = bigint_cmp(&field_index, &end_index);
if (cmp == CmpGT) {
break;
}
auto entry = field_prev_uses.put_unique(field_index, start_value->source_node);
if (entry) {
AstNode *prev_node = entry->value;
TypeEnumField *enum_field = find_enum_field_by_tag(switch_type, &field_index);
assert(enum_field != nullptr);
ErrorMsg *msg = ir_add_error(ira, start_value,
buf_sprintf("duplicate switch value: '%s.%s'", buf_ptr(&switch_type->name),
buf_ptr(enum_field->name)));
add_error_note(ira->codegen, msg, prev_node, buf_sprintf("other value is here"));
}
bigint_incr(&field_index);
}
}
if (!instruction->have_else_prong) {
for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) {
TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i];
auto entry = field_prev_uses.maybe_get(enum_field->value);
if (!entry) {
ir_add_error(ira, &instruction->base,
buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&switch_type->name),
buf_ptr(enum_field->name)));
}
}
}
} else if (switch_type->id == ZigTypeIdErrorSet) {
if (!resolve_inferred_error_set(ira->codegen, switch_type, target_value->source_node)) {
return ira->codegen->invalid_instruction;
}
AstNode **field_prev_uses = allocate<AstNode *>(ira->codegen->errors_by_index.length);
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->child;
if (type_is_invalid(start_value->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *end_value = range->end->child;
if (type_is_invalid(end_value->value.type))
return ira->codegen->invalid_instruction;
assert(start_value->value.type->id == ZigTypeIdErrorSet);
uint32_t start_index = start_value->value.data.x_err_set->value;
assert(end_value->value.type->id == ZigTypeIdErrorSet);
uint32_t end_index = end_value->value.data.x_err_set->value;
if (start_index != end_index) {
ir_add_error(ira, end_value, buf_sprintf("ranges not allowed when switching on errors"));
return ira->codegen->invalid_instruction;
}
AstNode *prev_node = field_prev_uses[start_index];
if (prev_node != nullptr) {
Buf *err_name = &ira->codegen->errors_by_index.at(start_index)->name;
ErrorMsg *msg = ir_add_error(ira, start_value,
buf_sprintf("duplicate switch value: '%s.%s'", buf_ptr(&switch_type->name), buf_ptr(err_name)));
add_error_note(ira->codegen, msg, prev_node, buf_sprintf("other value is here"));
}
field_prev_uses[start_index] = start_value->source_node;
}
if (!instruction->have_else_prong) {
if (type_is_global_error_set(switch_type)) {
ir_add_error(ira, &instruction->base,
buf_sprintf("else prong required when switching on type 'anyerror'"));
return ira->codegen->invalid_instruction;
} else {
for (uint32_t i = 0; i < switch_type->data.error_set.err_count; i += 1) {
ErrorTableEntry *err_entry = switch_type->data.error_set.errors[i];
AstNode *prev_node = field_prev_uses[err_entry->value];
if (prev_node == nullptr) {
ir_add_error(ira, &instruction->base,
buf_sprintf("error.%s not handled in switch", buf_ptr(&err_entry->name)));
}
}
}
}
free(field_prev_uses);
} else if (switch_type->id == ZigTypeIdInt) {
RangeSet rs = {0};
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->child;
if (type_is_invalid(start_value->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_start_value = ir_implicit_cast(ira, start_value, switch_type);
if (type_is_invalid(casted_start_value->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *end_value = range->end->child;
if (type_is_invalid(end_value->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_end_value = ir_implicit_cast(ira, end_value, switch_type);
if (type_is_invalid(casted_end_value->value.type))
return ira->codegen->invalid_instruction;
ConstExprValue *start_val = ir_resolve_const(ira, casted_start_value, UndefBad);
if (!start_val)
return ira->codegen->invalid_instruction;
ConstExprValue *end_val = ir_resolve_const(ira, casted_end_value, UndefBad);
if (!end_val)
return ira->codegen->invalid_instruction;
assert(start_val->type->id == ZigTypeIdInt || start_val->type->id == ZigTypeIdComptimeInt);
assert(end_val->type->id == ZigTypeIdInt || end_val->type->id == ZigTypeIdComptimeInt);
AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bigint, &end_val->data.x_bigint,
start_value->source_node);
if (prev_node != nullptr) {
ErrorMsg *msg = ir_add_error(ira, start_value, buf_sprintf("duplicate switch value"));
add_error_note(ira->codegen, msg, prev_node, buf_sprintf("previous value is here"));
return ira->codegen->invalid_instruction;
}
}
if (!instruction->have_else_prong) {
BigInt min_val;
eval_min_max_value_int(ira->codegen, switch_type, &min_val, false);
BigInt max_val;
eval_min_max_value_int(ira->codegen, switch_type, &max_val, true);
if (!rangeset_spans(&rs, &min_val, &max_val)) {
ir_add_error(ira, &instruction->base, buf_sprintf("switch must handle all possibilities"));
return ira->codegen->invalid_instruction;
}
}
} else if (switch_type->id == ZigTypeIdBool) {
int seenTrue = 0;
int seenFalse = 0;
for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i];
IrInstruction *value = range->start->child;
IrInstruction *casted_value = ir_implicit_cast(ira, value, switch_type);
if (type_is_invalid(casted_value->value.type))
return ira->codegen->invalid_instruction;
ConstExprValue *const_expr_val = ir_resolve_const(ira, casted_value, UndefBad);
if (!const_expr_val)
return ira->codegen->invalid_instruction;
assert(const_expr_val->type->id == ZigTypeIdBool);
if (const_expr_val->data.x_bool == true) {
seenTrue += 1;
} else {
seenFalse += 1;
}
if ((seenTrue > 1) || (seenFalse > 1)) {
ir_add_error(ira, value, buf_sprintf("duplicate switch value"));
return ira->codegen->invalid_instruction;
}
}
if (((seenTrue < 1) || (seenFalse < 1)) && !instruction->have_else_prong) {
ir_add_error(ira, &instruction->base, buf_sprintf("switch must handle all possibilities"));
return ira->codegen->invalid_instruction;
}
} else if (!instruction->have_else_prong) {
ir_add_error(ira, &instruction->base,
buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name)));
return ira->codegen->invalid_instruction;
}
return ir_const_void(ira, &instruction->base);
}
static IrInstruction *ir_analyze_instruction_check_statement_is_void(IrAnalyze *ira,
IrInstructionCheckStatementIsVoid *instruction)
{
IrInstruction *statement_value = instruction->statement_value->child;
ZigType *statement_type = statement_value->value.type;
if (type_is_invalid(statement_type))
return ira->codegen->invalid_instruction;
if (statement_type->id != ZigTypeIdVoid) {
ir_add_error(ira, &instruction->base, buf_sprintf("expression value is ignored"));
}
return ir_const_void(ira, &instruction->base);
}
static IrInstruction *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstructionPanic *instruction) {
IrInstruction *msg = instruction->msg->child;
if (type_is_invalid(msg->value.type))
return ir_unreach_error(ira);
if (ir_should_inline(ira->new_irb.exec, instruction->base.scope)) {
ir_add_error(ira, &instruction->base, buf_sprintf("encountered @panic at compile-time"));
return ir_unreach_error(ira);
}
ZigType *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
true, false, PtrLenUnknown, 0, 0, 0, false);
ZigType *str_type = get_slice_type(ira->codegen, u8_ptr_type);
IrInstruction *casted_msg = ir_implicit_cast(ira, msg, str_type);
if (type_is_invalid(casted_msg->value.type))
return ir_unreach_error(ira);
IrInstruction *new_instruction = ir_build_panic(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, casted_msg);
return ir_finish_anal(ira, new_instruction);
}
static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint32_t align_bytes, bool safety_check_on) {
Error err;
ZigType *target_type = target->value.type;
assert(!type_is_invalid(target_type));
ZigType *result_type;
uint32_t old_align_bytes;
if (target_type->id == ZigTypeIdPointer) {
result_type = adjust_ptr_align(ira->codegen, target_type, align_bytes);
if ((err = resolve_ptr_align(ira, target_type, &old_align_bytes)))
return ira->codegen->invalid_instruction;
} else if (target_type->id == ZigTypeIdFn) {
FnTypeId fn_type_id = target_type->data.fn.fn_type_id;
old_align_bytes = fn_type_id.alignment;
fn_type_id.alignment = align_bytes;
result_type = get_fn_type(ira->codegen, &fn_type_id);
} else if (target_type->id == ZigTypeIdOptional &&
target_type->data.maybe.child_type->id == ZigTypeIdPointer)
{
ZigType *ptr_type = target_type->data.maybe.child_type;
if ((err = resolve_ptr_align(ira, ptr_type, &old_align_bytes)))
return ira->codegen->invalid_instruction;
ZigType *better_ptr_type = adjust_ptr_align(ira->codegen, ptr_type, align_bytes);
result_type = get_optional_type(ira->codegen, better_ptr_type);
} else if (target_type->id == ZigTypeIdOptional &&
target_type->data.maybe.child_type->id == ZigTypeIdFn)
{
FnTypeId fn_type_id = target_type->data.maybe.child_type->data.fn.fn_type_id;
old_align_bytes = fn_type_id.alignment;
fn_type_id.alignment = align_bytes;
ZigType *fn_type = get_fn_type(ira->codegen, &fn_type_id);
result_type = get_optional_type(ira->codegen, fn_type);
} else if (is_slice(target_type)) {
ZigType *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index].type_entry;
if ((err = resolve_ptr_align(ira, slice_ptr_type, &old_align_bytes)))
return ira->codegen->invalid_instruction;
ZigType *result_ptr_type = adjust_ptr_align(ira->codegen, slice_ptr_type, align_bytes);
result_type = get_slice_type(ira->codegen, result_ptr_type);
} else {
ir_add_error(ira, target,
buf_sprintf("expected pointer or slice, found '%s'", buf_ptr(&target_type->name)));
return ira->codegen->invalid_instruction;
}
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 == ConstPtrSpecialHardCodedAddr &&
val->data.x_ptr.data.hard_coded_addr.addr % align_bytes != 0)
{
ir_add_error(ira, target,
buf_sprintf("pointer address 0x%" ZIG_PRI_x64 " is not aligned to %" PRIu32 " bytes",
val->data.x_ptr.data.hard_coded_addr.addr, align_bytes));
return ira->codegen->invalid_instruction;
}
IrInstruction *result = ir_const(ira, target, result_type);
copy_const_val(&result->value, val, true);
result->value.type = result_type;
return result;
}
IrInstruction *result;
if (safety_check_on && align_bytes > old_align_bytes && align_bytes != 1) {
result = ir_build_align_cast(&ira->new_irb, target->scope, target->source_node, nullptr, target);
} else {
result = ir_build_cast(&ira->new_irb, target->scope, target->source_node, result_type, target, CastOpNoop);
}
result->value.type = result_type;
return result;
}
static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr,
ZigType *dest_type, IrInstruction *dest_type_src, bool safety_check_on)
{
Error err;
ZigType *src_type = ptr->value.type;
assert(!type_is_invalid(src_type));
// We have a check for zero bits later so we use get_src_ptr_type to
// validate src_type and dest_type.
ZigType *src_ptr_type = get_src_ptr_type(src_type);
if (src_ptr_type == nullptr) {
ir_add_error(ira, ptr, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name)));
return ira->codegen->invalid_instruction;
}
ZigType *dest_ptr_type = get_src_ptr_type(dest_type);
if (dest_ptr_type == nullptr) {
ir_add_error(ira, dest_type_src,
buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
if (get_ptr_const(src_type) && !get_ptr_const(dest_type)) {
ir_add_error(ira, source_instr, buf_sprintf("cast discards const qualifier"));
return ira->codegen->invalid_instruction;
}
uint32_t src_align_bytes;
if ((err = resolve_ptr_align(ira, src_type, &src_align_bytes)))
return ira->codegen->invalid_instruction;
uint32_t dest_align_bytes;
if ((err = resolve_ptr_align(ira, dest_type, &dest_align_bytes)))
return ira->codegen->invalid_instruction;
if (instr_is_comptime(ptr)) {
bool dest_allows_addr_zero = ptr_allows_addr_zero(dest_type);
UndefAllowed is_undef_allowed = dest_allows_addr_zero ? UndefOk : UndefBad;
ConstExprValue *val = ir_resolve_const(ira, ptr, is_undef_allowed);
if (!val)
return ira->codegen->invalid_instruction;
if (val->special == ConstValSpecialStatic) {
bool is_addr_zero = val->data.x_ptr.special == ConstPtrSpecialNull ||
(val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr &&
val->data.x_ptr.data.hard_coded_addr.addr == 0);
if (is_addr_zero && !dest_allows_addr_zero) {
ir_add_error(ira, source_instr,
buf_sprintf("null pointer casted to type '%s'", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
}
IrInstruction *result = ir_const(ira, source_instr, dest_type);
copy_const_val(&result->value, val, true);
result->value.type = dest_type;
// Keep the bigger alignment, it can only help-
// unless the target is zero bits.
if (src_align_bytes > dest_align_bytes && type_has_bits(dest_type)) {
result = ir_align_cast(ira, result, src_align_bytes, false);
}
return result;
}
if (dest_align_bytes > src_align_bytes) {
ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment"));
add_error_note(ira->codegen, msg, ptr->source_node,
buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&src_type->name), src_align_bytes));
add_error_note(ira->codegen, msg, dest_type_src->source_node,
buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&dest_type->name), dest_align_bytes));
return ira->codegen->invalid_instruction;
}
IrInstruction *casted_ptr = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr, safety_check_on);
if (type_has_bits(dest_type) && !type_has_bits(src_type)) {
ErrorMsg *msg = ir_add_error(ira, source_instr,
buf_sprintf("'%s' and '%s' do not have the same in-memory representation",
buf_ptr(&src_type->name), buf_ptr(&dest_type->name)));
add_error_note(ira->codegen, msg, ptr->source_node,
buf_sprintf("'%s' has no in-memory bits", buf_ptr(&src_type->name)));
add_error_note(ira->codegen, msg, dest_type_src->source_node,
buf_sprintf("'%s' has in-memory bits", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
// Keep the bigger alignment, it can only help-
// unless the target is zero bits.
IrInstruction *result;
if (src_align_bytes > dest_align_bytes && type_has_bits(dest_type)) {
result = ir_align_cast(ira, casted_ptr, src_align_bytes, false);
if (type_is_invalid(result->value.type))
return ira->codegen->invalid_instruction;
} else {
result = casted_ptr;
}
return result;
}
static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCastSrc *instruction) {
IrInstruction *dest_type_value = instruction->dest_type->child;
ZigType *dest_type = ir_resolve_type(ira, dest_type_value);
if (type_is_invalid(dest_type))
return ira->codegen->invalid_instruction;
IrInstruction *ptr = instruction->ptr->child;
ZigType *src_type = ptr->value.type;
if (type_is_invalid(src_type))
return ira->codegen->invalid_instruction;
return ir_analyze_ptr_cast(ira, &instruction->base, ptr, dest_type, dest_type_value,
instruction->safety_check_on);
}
static void buf_write_value_bytes_array(CodeGen *codegen, uint8_t *buf, ConstExprValue *val, size_t len) {
size_t buf_i = 0;
// TODO optimize the buf case
expand_undef_array(codegen, val);
for (size_t elem_i = 0; elem_i < val->type->data.array.len; elem_i += 1) {
ConstExprValue *elem = &val->data.x_array.data.s_none.elements[elem_i];
buf_write_value_bytes(codegen, &buf[buf_i], elem);
buf_i += type_size(codegen, elem->type);
}
}
static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val) {
if (val->special == ConstValSpecialUndef)
val->special = ConstValSpecialStatic;
assert(val->special == ConstValSpecialStatic);
switch (val->type->id) {
case ZigTypeIdInvalid:
case ZigTypeIdMetaType:
case ZigTypeIdOpaque:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdUnreachable:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdPromise:
zig_unreachable();
case ZigTypeIdVoid:
return;
case ZigTypeIdBool:
buf[0] = val->data.x_bool ? 1 : 0;
return;
case ZigTypeIdInt:
bigint_write_twos_complement(&val->data.x_bigint, buf, val->type->data.integral.bit_count,
codegen->is_big_endian);
return;
case ZigTypeIdEnum:
bigint_write_twos_complement(&val->data.x_enum_tag, buf,
val->type->data.enumeration.tag_int_type->data.integral.bit_count,
codegen->is_big_endian);
return;
case ZigTypeIdFloat:
float_write_ieee597(val, buf, codegen->is_big_endian);
return;
case ZigTypeIdPointer:
if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {
BigInt bn;
bigint_init_unsigned(&bn, val->data.x_ptr.data.hard_coded_addr.addr);
bigint_write_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count, codegen->is_big_endian);
return;
} else {
zig_unreachable();
}
case ZigTypeIdArray:
return buf_write_value_bytes_array(codegen, buf, val, val->type->data.array.len);
case ZigTypeIdVector:
return buf_write_value_bytes_array(codegen, buf, val, val->type->data.vector.len);
case ZigTypeIdStruct:
switch (val->type->data.structure.layout) {
case ContainerLayoutAuto:
zig_unreachable();
case ContainerLayoutExtern: {
size_t src_field_count = val->type->data.structure.src_field_count;
for (size_t field_i = 0; field_i < src_field_count; field_i += 1) {
TypeStructField *struct_field = &val->type->data.structure.fields[field_i];
if (struct_field->gen_index == SIZE_MAX)
continue;
ConstExprValue *field_val = &val->data.x_struct.fields[field_i];
size_t offset = struct_field->offset;
buf_write_value_bytes(codegen, buf + offset, field_val);
}
return;
}
case ContainerLayoutPacked: {
size_t src_field_count = val->type->data.structure.src_field_count;
size_t gen_field_count = val->type->data.structure.gen_field_count;
size_t gen_i = 0;
size_t src_i = 0;
size_t offset = 0;
bool is_big_endian = codegen->is_big_endian;
uint8_t child_buf_prealloc[16];
size_t child_buf_len = 16;
uint8_t *child_buf = child_buf_prealloc;
while (gen_i < gen_field_count) {
size_t big_int_byte_count = val->type->data.structure.host_int_bytes[gen_i];
if (big_int_byte_count > child_buf_len) {
child_buf = allocate_nonzero<uint8_t>(big_int_byte_count);
child_buf_len = big_int_byte_count;
}
BigInt big_int;
bigint_init_unsigned(&big_int, 0);
size_t used_bits = 0;
while (src_i < src_field_count) {
TypeStructField *field = &val->type->data.structure.fields[src_i];
assert(field->gen_index != SIZE_MAX);
if (field->gen_index != gen_i)
break;
uint32_t packed_bits_size = type_size_bits(codegen, field->type_entry);
buf_write_value_bytes(codegen, child_buf, &val->data.x_struct.fields[src_i]);
BigInt child_val;
bigint_read_twos_complement(&child_val, child_buf, packed_bits_size, is_big_endian,
false);
if (is_big_endian) {
BigInt shift_amt;
bigint_init_unsigned(&shift_amt, packed_bits_size);
BigInt shifted;
bigint_shl(&shifted, &big_int, &shift_amt);
bigint_or(&big_int, &shifted, &child_val);
} else {
BigInt shift_amt;
bigint_init_unsigned(&shift_amt, used_bits);
BigInt child_val_shifted;
bigint_shl(&child_val_shifted, &child_val, &shift_amt);
BigInt tmp;
bigint_or(&tmp, &big_int, &child_val_shifted);
big_int = tmp;
used_bits += packed_bits_size;
}
src_i += 1;
}
bigint_write_twos_complement(&big_int, buf + offset, big_int_byte_count * 8, is_big_endian);
offset += big_int_byte_count;
gen_i += 1;
}
return;
}
}
case ZigTypeIdOptional:
zig_panic("TODO buf_write_value_bytes maybe type");
case ZigTypeIdErrorUnion:
zig_panic("TODO buf_write_value_bytes error union");
case ZigTypeIdErrorSet:
zig_panic("TODO buf_write_value_bytes pure error type");
case ZigTypeIdFn:
zig_panic("TODO buf_write_value_bytes fn type");
case ZigTypeIdUnion:
zig_panic("TODO buf_write_value_bytes union type");
}
zig_unreachable();
}
static Error buf_read_value_bytes_array(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf,
ConstExprValue *val, ZigType *elem_type, size_t len)
{
Error err;
uint64_t elem_size = type_size(codegen, elem_type);
switch (val->data.x_array.special) {
case ConstArraySpecialNone:
val->data.x_array.data.s_none.elements = create_const_vals(len);
for (size_t i = 0; i < len; i++) {
ConstExprValue *elem = &val->data.x_array.data.s_none.elements[i];
elem->special = ConstValSpecialStatic;
elem->type = elem_type;
if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem)))
return err;
}
return ErrorNone;
case ConstArraySpecialUndef:
zig_panic("TODO buf_read_value_bytes ConstArraySpecialUndef array type");
case ConstArraySpecialBuf:
zig_panic("TODO buf_read_value_bytes ConstArraySpecialBuf array type");
}
zig_unreachable();
}
static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ConstExprValue *val) {
Error err;
assert(val->special == ConstValSpecialStatic);
switch (val->type->id) {
case ZigTypeIdInvalid:
case ZigTypeIdMetaType:
case ZigTypeIdOpaque:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdUnreachable:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdPromise:
zig_unreachable();
case ZigTypeIdVoid:
return ErrorNone;
case ZigTypeIdBool:
val->data.x_bool = (buf[0] != 0);
return ErrorNone;
case ZigTypeIdInt:
bigint_read_twos_complement(&val->data.x_bigint, buf, val->type->data.integral.bit_count,
codegen->is_big_endian, val->type->data.integral.is_signed);
return ErrorNone;
case ZigTypeIdFloat:
float_read_ieee597(val, buf, codegen->is_big_endian);
return ErrorNone;
case ZigTypeIdPointer:
{
val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
BigInt bn;
bigint_read_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count,
codegen->is_big_endian, false);
val->data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&bn);
return ErrorNone;
}
case ZigTypeIdArray:
return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.array.child_type,
val->type->data.array.len);
case ZigTypeIdVector:
return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.vector.elem_type,
val->type->data.vector.len);
case ZigTypeIdEnum:
switch (val->type->data.enumeration.layout) {
case ContainerLayoutAuto:
zig_panic("TODO buf_read_value_bytes enum auto");
case ContainerLayoutPacked:
zig_panic("TODO buf_read_value_bytes enum packed");
case ContainerLayoutExtern: {
ZigType *tag_int_type = val->type->data.enumeration.tag_int_type;
assert(tag_int_type->id == ZigTypeIdInt);
bigint_read_twos_complement(&val->data.x_enum_tag, buf, tag_int_type->data.integral.bit_count,
codegen->is_big_endian, tag_int_type->data.integral.is_signed);
return ErrorNone;
}
}
zig_unreachable();
case ZigTypeIdStruct:
switch (val->type->data.structure.layout) {
case ContainerLayoutAuto: {
ErrorMsg *msg = opt_ir_add_error_node(ira, codegen, source_node,
buf_sprintf("non-extern, non-packed struct '%s' cannot have its bytes reinterpreted",
buf_ptr(&val->type->name)));
add_error_note(codegen, msg, val->type->data.structure.decl_node,
buf_sprintf("declared here"));
return ErrorSemanticAnalyzeFail;
}
case ContainerLayoutExtern: {
size_t src_field_count = val->type->data.structure.src_field_count;
val->data.x_struct.fields = create_const_vals(src_field_count);
for (size_t field_i = 0; field_i < src_field_count; field_i += 1) {
ConstExprValue *field_val = &val->data.x_struct.fields[field_i];
field_val->special = ConstValSpecialStatic;
TypeStructField *struct_field = &val->type->data.structure.fields[field_i];
field_val->type = struct_field->type_entry;
if (struct_field->gen_index == SIZE_MAX)
continue;
size_t offset = struct_field->offset;
uint8_t *new_buf = buf + offset;
if ((err = buf_read_value_bytes(ira, codegen, source_node, new_buf, field_val)))
return err;
}
return ErrorNone;
}
case ContainerLayoutPacked: {
size_t src_field_count = val->type->data.structure.src_field_count;
val->data.x_struct.fields = create_const_vals(src_field_count);
size_t gen_field_count = val->type->data.structure.gen_field_count;
size_t gen_i = 0;
size_t src_i = 0;
size_t offset = 0;
bool is_big_endian = codegen->is_big_endian;
uint8_t child_buf_prealloc[16];
size_t child_buf_len = 16;
uint8_t *child_buf = child_buf_prealloc;
while (gen_i < gen_field_count) {
size_t big_int_byte_count = val->type->data.structure.host_int_bytes[gen_i];
if (big_int_byte_count > child_buf_len) {
child_buf = allocate_nonzero<uint8_t>(big_int_byte_count);
child_buf_len = big_int_byte_count;
}
BigInt big_int;
bigint_read_twos_complement(&big_int, buf + offset, big_int_byte_count * 8, is_big_endian, false);
while (src_i < src_field_count) {
TypeStructField *field = &val->type->data.structure.fields[src_i];
assert(field->gen_index != SIZE_MAX);
if (field->gen_index != gen_i)
break;
ConstExprValue *field_val = &val->data.x_struct.fields[src_i];
field_val->special = ConstValSpecialStatic;
field_val->type = field->type_entry;
uint32_t packed_bits_size = type_size_bits(codegen, field->type_entry);
BigInt child_val;
if (is_big_endian) {
zig_panic("TODO buf_read_value_bytes packed struct big endian");
} else {
BigInt packed_bits_size_bi;
bigint_init_unsigned(&packed_bits_size_bi, packed_bits_size);
bigint_truncate(&child_val, &big_int, packed_bits_size, false);
BigInt tmp;
bigint_shr(&tmp, &big_int, &packed_bits_size_bi);
big_int = tmp;
}
bigint_write_twos_complement(&child_val, child_buf, big_int_byte_count * 8, is_big_endian);
if ((err = buf_read_value_bytes(ira, codegen, source_node, child_buf, field_val))) {
return err;
}
src_i += 1;
}
offset += big_int_byte_count;
gen_i += 1;
}
return ErrorNone;
}
}
zig_unreachable();
case ZigTypeIdOptional:
zig_panic("TODO buf_read_value_bytes maybe type");
case ZigTypeIdErrorUnion:
zig_panic("TODO buf_read_value_bytes error union");
case ZigTypeIdErrorSet:
zig_panic("TODO buf_read_value_bytes pure error type");
case ZigTypeIdFn:
zig_panic("TODO buf_read_value_bytes fn type");
case ZigTypeIdUnion:
zig_panic("TODO buf_read_value_bytes union type");
}
zig_unreachable();
}
static bool type_can_bit_cast(ZigType *t) {
switch (t->id) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdMetaType:
case ZigTypeIdOpaque:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdUnreachable:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdPointer:
return false;
default:
// TODO list these types out explicitly, there are probably some other invalid ones here
return true;
}
}
static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
ZigType *dest_type)
{
Error err;
ZigType *src_type = value->value.type;
assert(get_codegen_ptr_type(src_type) == nullptr);
assert(type_can_bit_cast(src_type));
assert(get_codegen_ptr_type(dest_type) == nullptr);
assert(type_can_bit_cast(dest_type));
if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusSizeKnown)))
return ira->codegen->invalid_instruction;
if ((err = type_resolve(ira->codegen, src_type, ResolveStatusSizeKnown)))
return ira->codegen->invalid_instruction;
uint64_t dest_size_bytes = type_size(ira->codegen, dest_type);
uint64_t src_size_bytes = type_size(ira->codegen, src_type);
if (dest_size_bytes != src_size_bytes) {
ir_add_error(ira, source_instr,
buf_sprintf("destination type '%s' has size %" ZIG_PRI_u64 " but source type '%s' has size %" ZIG_PRI_u64,
buf_ptr(&dest_type->name), dest_size_bytes,
buf_ptr(&src_type->name), src_size_bytes));
return ira->codegen->invalid_instruction;
}
uint64_t dest_size_bits = type_size_bits(ira->codegen, dest_type);
uint64_t src_size_bits = type_size_bits(ira->codegen, src_type);
if (dest_size_bits != src_size_bits) {
ir_add_error(ira, source_instr,
buf_sprintf("destination type '%s' has %" ZIG_PRI_u64 " bits but source type '%s' has %" ZIG_PRI_u64 " bits",
buf_ptr(&dest_type->name), dest_size_bits,
buf_ptr(&src_type->name), src_size_bits));
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;
IrInstruction *result = ir_const(ira, source_instr, dest_type);
uint8_t *buf = allocate_nonzero<uint8_t>(src_size_bytes);
buf_write_value_bytes(ira->codegen, buf, val);
if ((err = buf_read_value_bytes(ira, ira->codegen, source_instr->source_node, buf, &result->value)))
return ira->codegen->invalid_instruction;
return result;
}
IrInstruction *result = ir_build_bit_cast_gen(ira, source_instr, value, dest_type);
if (handle_is_ptr(dest_type) && !handle_is_ptr(src_type)) {
ir_add_alloca(ira, result, dest_type);
}
return result;
}
static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBitCast *instruction) {
IrInstruction *dest_type_value = instruction->dest_type->child;
ZigType *dest_type = ir_resolve_type(ira, dest_type_value);
if (type_is_invalid(dest_type))
return ira->codegen->invalid_instruction;
IrInstruction *value = instruction->value->child;
ZigType *src_type = value->value.type;
if (type_is_invalid(src_type))
return ira->codegen->invalid_instruction;
if (get_codegen_ptr_type(src_type) != nullptr) {
ir_add_error(ira, value,
buf_sprintf("unable to @bitCast from pointer type '%s'", buf_ptr(&src_type->name)));
return ira->codegen->invalid_instruction;
}
if (!type_can_bit_cast(src_type)) {
ir_add_error(ira, dest_type_value,
buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&src_type->name)));
return ira->codegen->invalid_instruction;
}
if (get_codegen_ptr_type(dest_type) != nullptr) {
ir_add_error(ira, dest_type_value,
buf_sprintf("unable to @bitCast to pointer type '%s'", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
if (!type_can_bit_cast(dest_type)) {
ir_add_error(ira, dest_type_value,
buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
return ir_analyze_bit_cast(ira, &instruction->base, value, dest_type);
}
static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
ZigType *ptr_type)
{
assert(get_src_ptr_type(ptr_type) != nullptr);
assert(type_has_bits(ptr_type));
IrInstruction *casted_int = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_usize);
if (type_is_invalid(casted_int->value.type))
return ira->codegen->invalid_instruction;
if (instr_is_comptime(casted_int)) {
ConstExprValue *val = ir_resolve_const(ira, casted_int, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
uint64_t addr = bigint_as_unsigned(&val->data.x_bigint);
if (!ptr_allows_addr_zero(ptr_type) && addr == 0) {
ir_add_error(ira, source_instr,
buf_sprintf("pointer type '%s' does not allow address zero", buf_ptr(&ptr_type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *result = ir_const(ira, source_instr, ptr_type);
result->value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
result->value.data.x_ptr.mut = ConstPtrMutRuntimeVar;
result->value.data.x_ptr.data.hard_coded_addr.addr = addr;
return result;
}
IrInstruction *result = ir_build_int_to_ptr(&ira->new_irb, source_instr->scope,
source_instr->source_node, nullptr, casted_int);
result->value.type = ptr_type;
return result;
}
static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionIntToPtr *instruction) {
Error err;
IrInstruction *dest_type_value = instruction->dest_type->child;
ZigType *dest_type = ir_resolve_type(ira, dest_type_value);
if (type_is_invalid(dest_type))
return ira->codegen->invalid_instruction;
// We explicitly check for the size, so we can use get_src_ptr_type
if (get_src_ptr_type(dest_type) == nullptr) {
ir_add_error(ira, dest_type_value, buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
if (!type_has_bits(dest_type)) {
ir_add_error(ira, dest_type_value,
buf_sprintf("type '%s' has 0 bits and cannot store information", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *target = instruction->target->child;
if (type_is_invalid(target->value.type))
return ira->codegen->invalid_instruction;
return ir_analyze_int_to_ptr(ira, &instruction->base, target, dest_type);
}
static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira,
IrInstructionDeclRef *instruction)
{
Tld *tld = instruction->tld;
LVal lval = instruction->lval;
resolve_top_level_decl(ira->codegen, tld, instruction->base.source_node);
if (tld->resolution == TldResolutionInvalid)
return ira->codegen->invalid_instruction;
switch (tld->id) {
case TldIdContainer:
case TldIdCompTime:
zig_unreachable();
case TldIdVar: {
TldVar *tld_var = (TldVar *)tld;
ZigVar *var = tld_var->var;
if (var == nullptr) {
return ir_error_dependency_loop(ira, &instruction->base);
}
IrInstruction *var_ptr = ir_get_var_ptr(ira, &instruction->base, var);
if (type_is_invalid(var_ptr->value.type))
return ira->codegen->invalid_instruction;
if (tld_var->extern_lib_name != nullptr) {
add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, instruction->base.source_node);
}
if (lval == LValPtr) {
return var_ptr;
} else {
return ir_get_deref(ira, &instruction->base, var_ptr);
}
}
case TldIdFn: {
TldFn *tld_fn = (TldFn *)tld;
ZigFn *fn_entry = tld_fn->fn_entry;
assert(fn_entry->type_entry);
if (tld_fn->extern_lib_name != nullptr) {
add_link_lib_symbol(ira, tld_fn->extern_lib_name, &fn_entry->symbol_name, instruction->base.source_node);
}
IrInstruction *ref_instruction = ir_create_const_fn(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, fn_entry);
if (lval == LValPtr) {
return ir_get_ref(ira, &instruction->base, ref_instruction, true, false);
} else {
return ref_instruction;
}
}
}
zig_unreachable();
}
static IrInstruction *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstructionPtrToInt *instruction) {
IrInstruction *target = instruction->target->child;
if (type_is_invalid(target->value.type))
return ira->codegen->invalid_instruction;
ZigType *usize = ira->codegen->builtin_types.entry_usize;
// We check size explicitly so we can use get_src_ptr_type here.
if (get_src_ptr_type(target->value.type) == nullptr) {
ir_add_error(ira, target,
buf_sprintf("expected pointer, found '%s'", buf_ptr(&target->value.type->name)));
return ira->codegen->invalid_instruction;
}
if (!type_has_bits(target->value.type)) {
ir_add_error(ira, target,
buf_sprintf("pointer to size 0 type has no address"));
return ira->codegen->invalid_instruction;
}
if (instr_is_comptime(target)) {
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
if (val->type->id == ZigTypeIdPointer && val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {
IrInstruction *result = ir_const(ira, &instruction->base, usize);
bigint_init_unsigned(&result->value.data.x_bigint, val->data.x_ptr.data.hard_coded_addr.addr);
result->value.type = usize;
return result;
}
}
IrInstruction *result = ir_build_ptr_to_int(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, target);
result->value.type = usize;
return result;
}
static IrInstruction *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstructionPtrType *instruction) {
Error err;
ZigType *child_type = ir_resolve_type(ira, instruction->child_type->child);
if (type_is_invalid(child_type))
return ira->codegen->invalid_instruction;
if (child_type->id == ZigTypeIdUnreachable) {
ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed"));
return ira->codegen->invalid_instruction;
} else if (child_type->id == ZigTypeIdOpaque && instruction->ptr_len == PtrLenUnknown) {
ir_add_error(ira, &instruction->base, buf_sprintf("unknown-length pointer to opaque"));
return ira->codegen->invalid_instruction;
} else if (instruction->ptr_len == PtrLenC) {
if (!type_allowed_in_extern(ira->codegen, child_type)) {
ir_add_error(ira, &instruction->base,
buf_sprintf("C pointers cannot point to non-C-ABI-compatible type '%s'", buf_ptr(&child_type->name)));
return ira->codegen->invalid_instruction;
} else if (child_type->id == ZigTypeIdOpaque) {
ir_add_error(ira, &instruction->base, buf_sprintf("C pointers cannot point opaque types"));
return ira->codegen->invalid_instruction;
} else if (instruction->is_allow_zero) {
ir_add_error(ira, &instruction->base, buf_sprintf("C pointers always allow address zero"));
return ira->codegen->invalid_instruction;
}
}
uint32_t align_bytes;
if (instruction->align_value != nullptr) {
if (!ir_resolve_align(ira, instruction->align_value->child, &align_bytes))
return ira->codegen->invalid_instruction;
if ((err = type_resolve(ira->codegen, child_type, ResolveStatusAlignmentKnown)))
return ira->codegen->invalid_instruction;
if (!type_has_bits(child_type)) {
align_bytes = 0;
}
} else {
if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
align_bytes = 0;
}
bool allow_zero = instruction->is_allow_zero || instruction->ptr_len == PtrLenC;
ZigType *result_type = get_pointer_to_type_extra(ira->codegen, child_type,
instruction->is_const, instruction->is_volatile,
instruction->ptr_len, align_bytes,
instruction->bit_offset_start, instruction->host_int_bytes, allow_zero);
return ir_const_type(ira, &instruction->base, result_type);
}
static IrInstruction *ir_analyze_instruction_align_cast(IrAnalyze *ira, IrInstructionAlignCast *instruction) {
uint32_t align_bytes;
IrInstruction *align_bytes_inst = instruction->align_bytes->child;
if (!ir_resolve_align(ira, align_bytes_inst, &align_bytes))
return ira->codegen->invalid_instruction;
IrInstruction *target = instruction->target->child;
if (type_is_invalid(target->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_align_cast(ira, target, align_bytes, true);
if (type_is_invalid(result->value.type))
return ira->codegen->invalid_instruction;
return result;
}
static IrInstruction *ir_analyze_instruction_opaque_type(IrAnalyze *ira, IrInstructionOpaqueType *instruction) {
Buf *bare_name = buf_alloc();
Buf *full_name = get_anon_type_name(ira->codegen, ira->new_irb.exec, "opaque",
instruction->base.scope, instruction->base.source_node, bare_name);
ZigType *result_type = get_opaque_type(ira->codegen, instruction->base.scope, instruction->base.source_node,
buf_ptr(full_name), bare_name);
return ir_const_type(ira, &instruction->base, result_type);
}
static IrInstruction *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, IrInstructionSetAlignStack *instruction) {
uint32_t align_bytes;
IrInstruction *align_bytes_inst = instruction->align_bytes->child;
if (!ir_resolve_align(ira, align_bytes_inst, &align_bytes))
return ira->codegen->invalid_instruction;
if (align_bytes > 256) {
ir_add_error(ira, &instruction->base, buf_sprintf("attempt to @setAlignStack(%" PRIu32 "); maximum is 256", align_bytes));
return ira->codegen->invalid_instruction;
}
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
if (fn_entry == nullptr) {
ir_add_error(ira, &instruction->base, buf_sprintf("@setAlignStack outside function"));
return ira->codegen->invalid_instruction;
}
if (fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionNaked) {
ir_add_error(ira, &instruction->base, buf_sprintf("@setAlignStack in naked function"));
return ira->codegen->invalid_instruction;
}
if (fn_entry->fn_inline == FnInlineAlways) {
ir_add_error(ira, &instruction->base, buf_sprintf("@setAlignStack in inline function"));
return ira->codegen->invalid_instruction;
}
if (fn_entry->set_alignstack_node != nullptr) {
ErrorMsg *msg = ir_add_error_node(ira, instruction->base.source_node,
buf_sprintf("alignstack set twice"));
add_error_note(ira->codegen, msg, fn_entry->set_alignstack_node, buf_sprintf("first set here"));
return ira->codegen->invalid_instruction;
}
fn_entry->set_alignstack_node = instruction->base.source_node;
fn_entry->alignstack_value = align_bytes;
return ir_const_void(ira, &instruction->base);
}
static IrInstruction *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstructionArgType *instruction) {
IrInstruction *fn_type_inst = instruction->fn_type->child;
ZigType *fn_type = ir_resolve_type(ira, fn_type_inst);
if (type_is_invalid(fn_type))
return ira->codegen->invalid_instruction;
IrInstruction *arg_index_inst = instruction->arg_index->child;
uint64_t arg_index;
if (!ir_resolve_usize(ira, arg_index_inst, &arg_index))
return ira->codegen->invalid_instruction;
if (fn_type->id != ZigTypeIdFn) {
ir_add_error(ira, fn_type_inst, buf_sprintf("expected function, found '%s'", buf_ptr(&fn_type->name)));
return ira->codegen->invalid_instruction;
}
FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
if (arg_index >= fn_type_id->param_count) {
ir_add_error(ira, arg_index_inst,
buf_sprintf("arg index %" ZIG_PRI_u64 " out of bounds; '%s' has %" ZIG_PRI_usize " arguments",
arg_index, buf_ptr(&fn_type->name), fn_type_id->param_count));
return ira->codegen->invalid_instruction;
}
ZigType *result_type = fn_type_id->param_info[arg_index].type;
if (result_type == nullptr) {
// Args are only unresolved if our function is generic.
assert(fn_type->data.fn.is_generic);
ir_add_error(ira, arg_index_inst,
buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_u64 " because '%s' is generic",
arg_index, buf_ptr(&fn_type->name)));
return ira->codegen->invalid_instruction;
}
return ir_const_type(ira, &instruction->base, result_type);
}
static IrInstruction *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstructionTagType *instruction) {
Error err;
IrInstruction *target_inst = instruction->target->child;
ZigType *enum_type = ir_resolve_type(ira, target_inst);
if (type_is_invalid(enum_type))
return ira->codegen->invalid_instruction;
if (enum_type->id == ZigTypeIdEnum) {
if ((err = ensure_complete_type(ira->codegen, enum_type)))
return ira->codegen->invalid_instruction;
return ir_const_type(ira, &instruction->base, enum_type->data.enumeration.tag_int_type);
} else if (enum_type->id == ZigTypeIdUnion) {
ZigType *tag_type = ir_resolve_union_tag_type(ira, instruction->target, enum_type);
if (type_is_invalid(tag_type))
return ira->codegen->invalid_instruction;
return ir_const_type(ira, &instruction->base, tag_type);
} else {
ir_add_error(ira, target_inst, buf_sprintf("expected enum or union, found '%s'",
buf_ptr(&enum_type->name)));
return ira->codegen->invalid_instruction;
}
}
static IrInstruction *ir_analyze_instruction_cancel(IrAnalyze *ira, IrInstructionCancel *instruction) {
IrInstruction *target_inst = instruction->target->child;
if (type_is_invalid(target_inst->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_target = ir_implicit_cast(ira, target_inst, ira->codegen->builtin_types.entry_promise);
if (type_is_invalid(casted_target->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_build_cancel(&ira->new_irb, instruction->base.scope, instruction->base.source_node, casted_target);
result->value.type = ira->codegen->builtin_types.entry_void;
result->value.special = ConstValSpecialStatic;
return result;
}
static IrInstruction *ir_analyze_instruction_coro_id(IrAnalyze *ira, IrInstructionCoroId *instruction) {
IrInstruction *promise_ptr = instruction->promise_ptr->child;
if (type_is_invalid(promise_ptr->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_build_coro_id(&ira->new_irb, instruction->base.scope, instruction->base.source_node,
promise_ptr);
result->value.type = ira->codegen->builtin_types.entry_usize;
return result;
}
static IrInstruction *ir_analyze_instruction_coro_alloc(IrAnalyze *ira, IrInstructionCoroAlloc *instruction) {
IrInstruction *coro_id = instruction->coro_id->child;
if (type_is_invalid(coro_id->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_build_coro_alloc(&ira->new_irb, instruction->base.scope, instruction->base.source_node,
coro_id);
result->value.type = ira->codegen->builtin_types.entry_bool;
return result;
}
static IrInstruction *ir_analyze_instruction_coro_size(IrAnalyze *ira, IrInstructionCoroSize *instruction) {
IrInstruction *result = ir_build_coro_size(&ira->new_irb, instruction->base.scope, instruction->base.source_node);
result->value.type = ira->codegen->builtin_types.entry_usize;
return result;
}
static IrInstruction *ir_analyze_instruction_coro_begin(IrAnalyze *ira, IrInstructionCoroBegin *instruction) {
IrInstruction *coro_id = instruction->coro_id->child;
if (type_is_invalid(coro_id->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *coro_mem_ptr = instruction->coro_mem_ptr->child;
if (type_is_invalid(coro_mem_ptr->value.type))
return ira->codegen->invalid_instruction;
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
assert(fn_entry != nullptr);
IrInstruction *result = ir_build_coro_begin(&ira->new_irb, instruction->base.scope, instruction->base.source_node,
coro_id, coro_mem_ptr);
result->value.type = get_promise_type(ira->codegen, fn_entry->type_entry->data.fn.fn_type_id.return_type);
return result;
}
static IrInstruction *ir_analyze_instruction_get_implicit_allocator(IrAnalyze *ira, IrInstructionGetImplicitAllocator *instruction) {
return ir_get_implicit_allocator(ira, &instruction->base, instruction->id);
}
static IrInstruction *ir_analyze_instruction_coro_alloc_fail(IrAnalyze *ira, IrInstructionCoroAllocFail *instruction) {
IrInstruction *err_val = instruction->err_val->child;
if (type_is_invalid(err_val->value.type))
return ir_unreach_error(ira);
IrInstruction *result = ir_build_coro_alloc_fail(&ira->new_irb, instruction->base.scope, instruction->base.source_node, err_val);
result->value.type = ira->codegen->builtin_types.entry_unreachable;
return ir_finish_anal(ira, result);
}
static IrInstruction *ir_analyze_instruction_coro_suspend(IrAnalyze *ira, IrInstructionCoroSuspend *instruction) {
IrInstruction *save_point = nullptr;
if (instruction->save_point != nullptr) {
save_point = instruction->save_point->child;
if (type_is_invalid(save_point->value.type))
return ira->codegen->invalid_instruction;
}
IrInstruction *is_final = instruction->is_final->child;
if (type_is_invalid(is_final->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_build_coro_suspend(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, save_point, is_final);
result->value.type = ira->codegen->builtin_types.entry_u8;
return result;
}
static IrInstruction *ir_analyze_instruction_coro_end(IrAnalyze *ira, IrInstructionCoroEnd *instruction) {
IrInstruction *result = ir_build_coro_end(&ira->new_irb, instruction->base.scope,
instruction->base.source_node);
result->value.type = ira->codegen->builtin_types.entry_void;
return result;
}
static IrInstruction *ir_analyze_instruction_coro_free(IrAnalyze *ira, IrInstructionCoroFree *instruction) {
IrInstruction *coro_id = instruction->coro_id->child;
if (type_is_invalid(coro_id->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *coro_handle = instruction->coro_handle->child;
if (type_is_invalid(coro_handle->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_build_coro_free(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, coro_id, coro_handle);
ZigType *ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, false);
result->value.type = get_optional_type(ira->codegen, ptr_type);
return result;
}
static IrInstruction *ir_analyze_instruction_coro_resume(IrAnalyze *ira, IrInstructionCoroResume *instruction) {
IrInstruction *awaiter_handle = instruction->awaiter_handle->child;
if (type_is_invalid(awaiter_handle->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_target = ir_implicit_cast(ira, awaiter_handle, ira->codegen->builtin_types.entry_promise);
if (type_is_invalid(casted_target->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_build_coro_resume(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, casted_target);
result->value.type = ira->codegen->builtin_types.entry_void;
return result;
}
static IrInstruction *ir_analyze_instruction_coro_save(IrAnalyze *ira, IrInstructionCoroSave *instruction) {
IrInstruction *coro_handle = instruction->coro_handle->child;
if (type_is_invalid(coro_handle->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_build_coro_save(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, coro_handle);
result->value.type = ira->codegen->builtin_types.entry_usize;
return result;
}
static IrInstruction *ir_analyze_instruction_coro_promise(IrAnalyze *ira, IrInstructionCoroPromise *instruction) {
IrInstruction *coro_handle = instruction->coro_handle->child;
if (type_is_invalid(coro_handle->value.type))
return ira->codegen->invalid_instruction;
if (coro_handle->value.type->id != ZigTypeIdPromise ||
coro_handle->value.type->data.promise.result_type == nullptr)
{
ir_add_error(ira, &instruction->base, buf_sprintf("expected promise->T, found '%s'",
buf_ptr(&coro_handle->value.type->name)));
return ira->codegen->invalid_instruction;
}
ZigType *coro_frame_type = get_promise_frame_type(ira->codegen,
coro_handle->value.type->data.promise.result_type);
IrInstruction *result = ir_build_coro_promise(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, coro_handle);
result->value.type = get_pointer_to_type(ira->codegen, coro_frame_type, false);
return result;
}
static IrInstruction *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira, IrInstructionCoroAllocHelper *instruction) {
IrInstruction *realloc_fn = instruction->realloc_fn->child;
if (type_is_invalid(realloc_fn->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *coro_size = instruction->coro_size->child;
if (type_is_invalid(coro_size->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_build_coro_alloc_helper(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, realloc_fn, coro_size);
ZigType *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, false);
result->value.type = get_optional_type(ira->codegen, u8_ptr_type);
return result;
}
static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op) {
ZigType *operand_type = ir_resolve_type(ira, op);
if (type_is_invalid(operand_type))
return ira->codegen->builtin_types.entry_invalid;
if (operand_type->id == ZigTypeIdInt) {
if (operand_type->data.integral.bit_count < 8) {
ir_add_error(ira, op,
buf_sprintf("expected integer type 8 bits or larger, found %" PRIu32 "-bit integer type",
operand_type->data.integral.bit_count));
return ira->codegen->builtin_types.entry_invalid;
}
if (operand_type->data.integral.bit_count > ira->codegen->pointer_size_bytes * 8) {
ir_add_error(ira, op,
buf_sprintf("expected integer type pointer size or smaller, found %" PRIu32 "-bit integer type",
operand_type->data.integral.bit_count));
return ira->codegen->builtin_types.entry_invalid;
}
if (!is_power_of_2(operand_type->data.integral.bit_count)) {
ir_add_error(ira, op,
buf_sprintf("%" PRIu32 "-bit integer type is not a power of 2", operand_type->data.integral.bit_count));
return ira->codegen->builtin_types.entry_invalid;
}
} else if (get_codegen_ptr_type(operand_type) == nullptr) {
ir_add_error(ira, op,
buf_sprintf("expected integer or pointer type, found '%s'", buf_ptr(&operand_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
return operand_type;
}
static IrInstruction *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstructionAtomicRmw *instruction) {
ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->child);
if (type_is_invalid(operand_type))
return ira->codegen->invalid_instruction;
IrInstruction *ptr_inst = instruction->ptr->child;
if (type_is_invalid(ptr_inst->value.type))
return ira->codegen->invalid_instruction;
// TODO let this be volatile
ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false);
IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type);
if (type_is_invalid(casted_ptr->value.type))
return ira->codegen->invalid_instruction;
AtomicRmwOp op;
if (instruction->op == nullptr) {
op = instruction->resolved_op;
} else {
if (!ir_resolve_atomic_rmw_op(ira, instruction->op->child, &op)) {
return ira->codegen->invalid_instruction;
}
}
IrInstruction *operand = instruction->operand->child;
if (type_is_invalid(operand->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_operand = ir_implicit_cast(ira, operand, operand_type);
if (type_is_invalid(casted_operand->value.type))
return ira->codegen->invalid_instruction;
AtomicOrder ordering;
if (instruction->ordering == nullptr) {
ordering = instruction->resolved_ordering;
} else {
if (!ir_resolve_atomic_order(ira, instruction->ordering->child, &ordering))
return ira->codegen->invalid_instruction;
if (ordering == AtomicOrderUnordered) {
ir_add_error(ira, instruction->ordering,
buf_sprintf("@atomicRmw atomic ordering must not be Unordered"));
return ira->codegen->invalid_instruction;
}
}
if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar)
{
zig_panic("TODO compile-time execution of atomicRmw");
}
IrInstruction *result = ir_build_atomic_rmw(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, nullptr, casted_ptr, nullptr, casted_operand, nullptr,
op, ordering);
result->value.type = operand_type;
return result;
}
static IrInstruction *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstructionAtomicLoad *instruction) {
ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->child);
if (type_is_invalid(operand_type))
return ira->codegen->invalid_instruction;
IrInstruction *ptr_inst = instruction->ptr->child;
if (type_is_invalid(ptr_inst->value.type))
return ira->codegen->invalid_instruction;
ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, true);
IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type);
if (type_is_invalid(casted_ptr->value.type))
return ira->codegen->invalid_instruction;
AtomicOrder ordering;
if (instruction->ordering == nullptr) {
ordering = instruction->resolved_ordering;
} else {
if (!ir_resolve_atomic_order(ira, instruction->ordering->child, &ordering))
return ira->codegen->invalid_instruction;
}
if (ordering == AtomicOrderRelease || ordering == AtomicOrderAcqRel) {
assert(instruction->ordering != nullptr);
ir_add_error(ira, instruction->ordering,
buf_sprintf("@atomicLoad atomic ordering must not be Release or AcqRel"));
return ira->codegen->invalid_instruction;
}
if (instr_is_comptime(casted_ptr)) {
IrInstruction *result = ir_get_deref(ira, &instruction->base, casted_ptr);
assert(result->value.type != nullptr);
return result;
}
IrInstruction *result = ir_build_atomic_load(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, nullptr, casted_ptr, nullptr, ordering);
result->value.type = operand_type;
return result;
}
static IrInstruction *ir_analyze_instruction_promise_result_type(IrAnalyze *ira, IrInstructionPromiseResultType *instruction) {
ZigType *promise_type = ir_resolve_type(ira, instruction->promise_type->child);
if (type_is_invalid(promise_type))
return ira->codegen->invalid_instruction;
if (promise_type->id != ZigTypeIdPromise || promise_type->data.promise.result_type == nullptr) {
ir_add_error(ira, &instruction->base, buf_sprintf("expected promise->T, found '%s'",
buf_ptr(&promise_type->name)));
return ira->codegen->invalid_instruction;
}
return ir_const_type(ira, &instruction->base, promise_type->data.promise.result_type);
}
static IrInstruction *ir_analyze_instruction_await_bookkeeping(IrAnalyze *ira, IrInstructionAwaitBookkeeping *instruction) {
ZigType *promise_result_type = ir_resolve_type(ira, instruction->promise_result_type->child);
if (type_is_invalid(promise_result_type))
return ira->codegen->invalid_instruction;
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
assert(fn_entry != nullptr);
if (type_can_fail(promise_result_type)) {
fn_entry->calls_or_awaits_errorable_fn = true;
}
return ir_const_void(ira, &instruction->base);
}
static IrInstruction *ir_analyze_instruction_merge_err_ret_traces(IrAnalyze *ira,
IrInstructionMergeErrRetTraces *instruction)
{
IrInstruction *coro_promise_ptr = instruction->coro_promise_ptr->child;
if (type_is_invalid(coro_promise_ptr->value.type))
return ira->codegen->invalid_instruction;
assert(coro_promise_ptr->value.type->id == ZigTypeIdPointer);
ZigType *promise_frame_type = coro_promise_ptr->value.type->data.pointer.child_type;
assert(promise_frame_type->id == ZigTypeIdStruct);
ZigType *promise_result_type = promise_frame_type->data.structure.fields[1].type_entry;
if (!type_can_fail(promise_result_type)) {
return ir_const_void(ira, &instruction->base);
}
IrInstruction *src_err_ret_trace_ptr = instruction->src_err_ret_trace_ptr->child;
if (type_is_invalid(src_err_ret_trace_ptr->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *dest_err_ret_trace_ptr = instruction->dest_err_ret_trace_ptr->child;
if (type_is_invalid(dest_err_ret_trace_ptr->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_build_merge_err_ret_traces(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr);
result->value.type = ira->codegen->builtin_types.entry_void;
return result;
}
static IrInstruction *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira, IrInstructionSaveErrRetAddr *instruction) {
IrInstruction *result = ir_build_save_err_ret_addr(&ira->new_irb, instruction->base.scope,
instruction->base.source_node);
result->value.type = ira->codegen->builtin_types.entry_void;
return result;
}
static IrInstruction *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze *ira, IrInstructionMarkErrRetTracePtr *instruction) {
IrInstruction *err_ret_trace_ptr = instruction->err_ret_trace_ptr->child;
if (type_is_invalid(err_ret_trace_ptr->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_build_mark_err_ret_trace_ptr(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, err_ret_trace_ptr);
result->value.type = ira->codegen->builtin_types.entry_void;
return result;
}
static IrInstruction *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionSqrt *instruction) {
ZigType *float_type = ir_resolve_type(ira, instruction->type->child);
if (type_is_invalid(float_type))
return ira->codegen->invalid_instruction;
IrInstruction *op = instruction->op->child;
if (type_is_invalid(op->value.type))
return ira->codegen->invalid_instruction;
bool ok_type = float_type->id == ZigTypeIdComptimeFloat || float_type->id == ZigTypeIdFloat;
if (!ok_type) {
ir_add_error(ira, instruction->type, buf_sprintf("@sqrt does not support type '%s'", buf_ptr(&float_type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *casted_op = ir_implicit_cast(ira, op, float_type);
if (type_is_invalid(casted_op->value.type))
return ira->codegen->invalid_instruction;
if (instr_is_comptime(casted_op)) {
ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_const(ira, &instruction->base, float_type);
ConstExprValue *out_val = &result->value;
if (float_type->id == ZigTypeIdComptimeFloat) {
bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat);
} else if (float_type->id == ZigTypeIdFloat) {
switch (float_type->data.floating.bit_count) {
case 16:
out_val->data.x_f16 = f16_sqrt(val->data.x_f16);
break;
case 32:
out_val->data.x_f32 = sqrtf(val->data.x_f32);
break;
case 64:
out_val->data.x_f64 = sqrt(val->data.x_f64);
break;
case 128:
f128M_sqrt(&val->data.x_f128, &out_val->data.x_f128);
break;
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
return result;
}
assert(float_type->id == ZigTypeIdFloat);
if (float_type->data.floating.bit_count != 16 &&
float_type->data.floating.bit_count != 32 &&
float_type->data.floating.bit_count != 64) {
ir_add_error(ira, instruction->type, buf_sprintf("compiler TODO: add implementation of sqrt for '%s'", buf_ptr(&float_type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *result = ir_build_sqrt(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, nullptr, casted_op);
result->value.type = float_type;
return result;
}
static IrInstruction *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstructionBswap *instruction) {
ZigType *int_type = ir_resolve_type(ira, instruction->type->child);
if (type_is_invalid(int_type))
return ira->codegen->invalid_instruction;
IrInstruction *op = instruction->op->child;
if (type_is_invalid(op->value.type))
return ira->codegen->invalid_instruction;
if (int_type->id != ZigTypeIdInt) {
ir_add_error(ira, instruction->type,
buf_sprintf("expected integer type, found '%s'", buf_ptr(&int_type->name)));
return ira->codegen->invalid_instruction;
}
if (int_type->data.integral.bit_count % 8 != 0) {
ir_add_error(ira, instruction->type,
buf_sprintf("@bswap integer type '%s' has %" PRIu32 " bits which is not evenly divisible by 8",
buf_ptr(&int_type->name), int_type->data.integral.bit_count));
return ira->codegen->invalid_instruction;
}
IrInstruction *casted_op = ir_implicit_cast(ira, op, int_type);
if (type_is_invalid(casted_op->value.type))
return ira->codegen->invalid_instruction;
if (int_type->data.integral.bit_count == 0) {
IrInstruction *result = ir_const(ira, &instruction->base, int_type);
bigint_init_unsigned(&result->value.data.x_bigint, 0);
return result;
}
if (int_type->data.integral.bit_count == 8) {
return casted_op;
}
if (instr_is_comptime(casted_op)) {
ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_const(ira, &instruction->base, int_type);
size_t buf_size = int_type->data.integral.bit_count / 8;
uint8_t *buf = allocate_nonzero<uint8_t>(buf_size);
bigint_write_twos_complement(&val->data.x_bigint, buf, int_type->data.integral.bit_count, true);
bigint_read_twos_complement(&result->value.data.x_bigint, buf, int_type->data.integral.bit_count, false,
int_type->data.integral.is_signed);
return result;
}
IrInstruction *result = ir_build_bswap(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, nullptr, casted_op);
result->value.type = int_type;
return result;
}
static IrInstruction *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstructionBitReverse *instruction) {
ZigType *int_type = ir_resolve_type(ira, instruction->type->child);
if (type_is_invalid(int_type))
return ira->codegen->invalid_instruction;
IrInstruction *op = instruction->op->child;
if (type_is_invalid(op->value.type))
return ira->codegen->invalid_instruction;
if (int_type->id != ZigTypeIdInt) {
ir_add_error(ira, instruction->type,
buf_sprintf("expected integer type, found '%s'", buf_ptr(&int_type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *casted_op = ir_implicit_cast(ira, op, int_type);
if (type_is_invalid(casted_op->value.type))
return ira->codegen->invalid_instruction;
if (int_type->data.integral.bit_count == 0) {
IrInstruction *result = ir_const(ira, &instruction->base, int_type);
bigint_init_unsigned(&result->value.data.x_bigint, 0);
return result;
}
if (instr_is_comptime(casted_op)) {
ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_const(ira, &instruction->base, int_type);
size_t num_bits = int_type->data.integral.bit_count;
size_t buf_size = (num_bits + 7) / 8;
uint8_t *comptime_buf = allocate_nonzero<uint8_t>(buf_size);
uint8_t *result_buf = allocate_nonzero<uint8_t>(buf_size);
memset(comptime_buf,0,buf_size);
memset(result_buf,0,buf_size);
bigint_write_twos_complement(&val->data.x_bigint,comptime_buf,num_bits,ira->codegen->is_big_endian);
size_t bit_i = 0;
size_t bit_rev_i = num_bits - 1;
for (; bit_i < num_bits; bit_i++, bit_rev_i--) {
if (comptime_buf[bit_i / 8] & (1 << (bit_i % 8))) {
result_buf[bit_rev_i / 8] |= (1 << (bit_rev_i % 8));
}
}
bigint_read_twos_complement(&result->value.data.x_bigint,
result_buf,
int_type->data.integral.bit_count,
ira->codegen->is_big_endian,
int_type->data.integral.is_signed);
return result;
}
IrInstruction *result = ir_build_bit_reverse(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, nullptr, casted_op);
result->value.type = int_type;
return result;
}
static IrInstruction *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstructionEnumToInt *instruction) {
IrInstruction *target = instruction->target->child;
if (type_is_invalid(target->value.type))
return ira->codegen->invalid_instruction;
return ir_analyze_enum_to_int(ira, &instruction->base, target);
}
static IrInstruction *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstructionIntToEnum *instruction) {
Error err;
IrInstruction *dest_type_value = instruction->dest_type->child;
ZigType *dest_type = ir_resolve_type(ira, dest_type_value);
if (type_is_invalid(dest_type))
return ira->codegen->invalid_instruction;
if (dest_type->id != ZigTypeIdEnum) {
ir_add_error(ira, instruction->dest_type,
buf_sprintf("expected enum, found type '%s'", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
ZigType *tag_type = dest_type->data.enumeration.tag_int_type;
IrInstruction *target = instruction->target->child;
if (type_is_invalid(target->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_target = ir_implicit_cast(ira, target, tag_type);
if (type_is_invalid(casted_target->value.type))
return ira->codegen->invalid_instruction;
return ir_analyze_int_to_enum(ira, &instruction->base, casted_target, dest_type);
}
static IrInstruction *ir_analyze_instruction_check_runtime_scope(IrAnalyze *ira, IrInstructionCheckRuntimeScope *instruction) {
IrInstruction *block_comptime_inst = instruction->scope_is_comptime->child;
bool scope_is_comptime;
if (!ir_resolve_bool(ira, block_comptime_inst, &scope_is_comptime))
return ira->codegen->invalid_instruction;
IrInstruction *is_comptime_inst = instruction->is_comptime->child;
bool is_comptime;
if (!ir_resolve_bool(ira, is_comptime_inst, &is_comptime))
return ira->codegen->invalid_instruction;
if (!scope_is_comptime && is_comptime) {
ErrorMsg *msg = ir_add_error(ira, &instruction->base,
buf_sprintf("comptime control flow inside runtime block"));
add_error_note(ira->codegen, msg, block_comptime_inst->source_node,
buf_sprintf("runtime block created here"));
return ira->codegen->invalid_instruction;
}
return ir_const_void(ira, &instruction->base);
}
static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
switch (instruction->id) {
case IrInstructionIdInvalid:
case IrInstructionIdWidenOrShorten:
case IrInstructionIdStructInit:
case IrInstructionIdUnionInit:
case IrInstructionIdStructFieldPtr:
case IrInstructionIdUnionFieldPtr:
case IrInstructionIdOptionalWrap:
case IrInstructionIdErrWrapCode:
case IrInstructionIdErrWrapPayload:
case IrInstructionIdCast:
case IrInstructionIdDeclVarGen:
case IrInstructionIdPtrCastGen:
case IrInstructionIdCmpxchgGen:
case IrInstructionIdArrayToVector:
case IrInstructionIdVectorToArray:
case IrInstructionIdAssertZero:
case IrInstructionIdResizeSlice:
case IrInstructionIdLoadPtrGen:
case IrInstructionIdBitCastGen:
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 IrInstructionIdDeclVarSrc:
return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVarSrc *)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 IrInstructionIdSetCold:
return ir_analyze_instruction_set_cold(ira, (IrInstructionSetCold *)instruction);
case IrInstructionIdSetRuntimeSafety:
return ir_analyze_instruction_set_runtime_safety(ira, (IrInstructionSetRuntimeSafety *)instruction);
case IrInstructionIdSetFloatMode:
return ir_analyze_instruction_set_float_mode(ira, (IrInstructionSetFloatMode *)instruction);
case IrInstructionIdSliceType:
return ir_analyze_instruction_slice_type(ira, (IrInstructionSliceType *)instruction);
case IrInstructionIdGlobalAsm:
return ir_analyze_instruction_global_asm(ira, (IrInstructionGlobalAsm *)instruction);
case IrInstructionIdAsm:
return ir_analyze_instruction_asm(ira, (IrInstructionAsm *)instruction);
case IrInstructionIdArrayType:
return ir_analyze_instruction_array_type(ira, (IrInstructionArrayType *)instruction);
case IrInstructionIdPromiseType:
return ir_analyze_instruction_promise_type(ira, (IrInstructionPromiseType *)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 IrInstructionIdOptionalUnwrapPtr:
return ir_analyze_instruction_optional_unwrap_ptr(ira, (IrInstructionOptionalUnwrapPtr *)instruction);
case IrInstructionIdClz:
return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction);
case IrInstructionIdCtz:
return ir_analyze_instruction_ctz(ira, (IrInstructionCtz *)instruction);
case IrInstructionIdPopCount:
return ir_analyze_instruction_pop_count(ira, (IrInstructionPopCount *)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 IrInstructionIdUnionTag:
return ir_analyze_instruction_union_tag(ira, (IrInstructionUnionTag *)instruction);
case IrInstructionIdImport:
return ir_analyze_instruction_import(ira, (IrInstructionImport *)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 IrInstructionIdCompileErr:
return ir_analyze_instruction_compile_err(ira, (IrInstructionCompileErr *)instruction);
case IrInstructionIdCompileLog:
return ir_analyze_instruction_compile_log(ira, (IrInstructionCompileLog *)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 IrInstructionIdCmpxchgSrc:
return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchgSrc *)instruction);
case IrInstructionIdFence:
return ir_analyze_instruction_fence(ira, (IrInstructionFence *)instruction);
case IrInstructionIdTruncate:
return ir_analyze_instruction_truncate(ira, (IrInstructionTruncate *)instruction);
case IrInstructionIdIntCast:
return ir_analyze_instruction_int_cast(ira, (IrInstructionIntCast *)instruction);
case IrInstructionIdFloatCast:
return ir_analyze_instruction_float_cast(ira, (IrInstructionFloatCast *)instruction);
case IrInstructionIdErrSetCast:
return ir_analyze_instruction_err_set_cast(ira, (IrInstructionErrSetCast *)instruction);
case IrInstructionIdFromBytes:
return ir_analyze_instruction_from_bytes(ira, (IrInstructionFromBytes *)instruction);
case IrInstructionIdToBytes:
return ir_analyze_instruction_to_bytes(ira, (IrInstructionToBytes *)instruction);
case IrInstructionIdIntToFloat:
return ir_analyze_instruction_int_to_float(ira, (IrInstructionIntToFloat *)instruction);
case IrInstructionIdFloatToInt:
return ir_analyze_instruction_float_to_int(ira, (IrInstructionFloatToInt *)instruction);
case IrInstructionIdBoolToInt:
return ir_analyze_instruction_bool_to_int(ira, (IrInstructionBoolToInt *)instruction);
case IrInstructionIdIntType:
return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction);
case IrInstructionIdVectorType:
return ir_analyze_instruction_vector_type(ira, (IrInstructionVectorType *)instruction);
case IrInstructionIdBoolNot:
return ir_analyze_instruction_bool_not(ira, (IrInstructionBoolNot *)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 IrInstructionIdMemberType:
return ir_analyze_instruction_member_type(ira, (IrInstructionMemberType *)instruction);
case IrInstructionIdMemberName:
return ir_analyze_instruction_member_name(ira, (IrInstructionMemberName *)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 IrInstructionIdHandle:
return ir_analyze_instruction_handle(ira, (IrInstructionHandle *)instruction);
case IrInstructionIdAlignOf:
return ir_analyze_instruction_align_of(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 IrInstructionIdCheckStatementIsVoid:
return ir_analyze_instruction_check_statement_is_void(ira, (IrInstructionCheckStatementIsVoid *)instruction);
case IrInstructionIdDeclRef:
return ir_analyze_instruction_decl_ref(ira, (IrInstructionDeclRef *)instruction);
case IrInstructionIdPanic:
return ir_analyze_instruction_panic(ira, (IrInstructionPanic *)instruction);
case IrInstructionIdPtrCastSrc:
return ir_analyze_instruction_ptr_cast(ira, (IrInstructionPtrCastSrc *)instruction);
case IrInstructionIdBitCast:
return ir_analyze_instruction_bit_cast(ira, (IrInstructionBitCast *)instruction);
case IrInstructionIdIntToPtr:
return ir_analyze_instruction_int_to_ptr(ira, (IrInstructionIntToPtr *)instruction);
case IrInstructionIdPtrToInt:
return ir_analyze_instruction_ptr_to_int(ira, (IrInstructionPtrToInt *)instruction);
case IrInstructionIdTagName:
return ir_analyze_instruction_enum_tag_name(ira, (IrInstructionTagName *)instruction);
case IrInstructionIdFieldParentPtr:
return ir_analyze_instruction_field_parent_ptr(ira, (IrInstructionFieldParentPtr *)instruction);
case IrInstructionIdByteOffsetOf:
return ir_analyze_instruction_byte_offset_of(ira, (IrInstructionByteOffsetOf *)instruction);
case IrInstructionIdBitOffsetOf:
return ir_analyze_instruction_bit_offset_of(ira, (IrInstructionBitOffsetOf *)instruction);
case IrInstructionIdTypeInfo:
return ir_analyze_instruction_type_info(ira, (IrInstructionTypeInfo *) instruction);
case IrInstructionIdTypeId:
return ir_analyze_instruction_type_id(ira, (IrInstructionTypeId *)instruction);
case IrInstructionIdSetEvalBranchQuota:
return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstructionSetEvalBranchQuota *)instruction);
case IrInstructionIdPtrType:
return ir_analyze_instruction_ptr_type(ira, (IrInstructionPtrType *)instruction);
case IrInstructionIdAlignCast:
return ir_analyze_instruction_align_cast(ira, (IrInstructionAlignCast *)instruction);
case IrInstructionIdOpaqueType:
return ir_analyze_instruction_opaque_type(ira, (IrInstructionOpaqueType *)instruction);
case IrInstructionIdSetAlignStack:
return ir_analyze_instruction_set_align_stack(ira, (IrInstructionSetAlignStack *)instruction);
case IrInstructionIdArgType:
return ir_analyze_instruction_arg_type(ira, (IrInstructionArgType *)instruction);
case IrInstructionIdTagType:
return ir_analyze_instruction_tag_type(ira, (IrInstructionTagType *)instruction);
case IrInstructionIdExport:
return ir_analyze_instruction_export(ira, (IrInstructionExport *)instruction);
case IrInstructionIdErrorReturnTrace:
return ir_analyze_instruction_error_return_trace(ira, (IrInstructionErrorReturnTrace *)instruction);
case IrInstructionIdErrorUnion:
return ir_analyze_instruction_error_union(ira, (IrInstructionErrorUnion *)instruction);
case IrInstructionIdCancel:
return ir_analyze_instruction_cancel(ira, (IrInstructionCancel *)instruction);
case IrInstructionIdCoroId:
return ir_analyze_instruction_coro_id(ira, (IrInstructionCoroId *)instruction);
case IrInstructionIdCoroAlloc:
return ir_analyze_instruction_coro_alloc(ira, (IrInstructionCoroAlloc *)instruction);
case IrInstructionIdCoroSize:
return ir_analyze_instruction_coro_size(ira, (IrInstructionCoroSize *)instruction);
case IrInstructionIdCoroBegin:
return ir_analyze_instruction_coro_begin(ira, (IrInstructionCoroBegin *)instruction);
case IrInstructionIdGetImplicitAllocator:
return ir_analyze_instruction_get_implicit_allocator(ira, (IrInstructionGetImplicitAllocator *)instruction);
case IrInstructionIdCoroAllocFail:
return ir_analyze_instruction_coro_alloc_fail(ira, (IrInstructionCoroAllocFail *)instruction);
case IrInstructionIdCoroSuspend:
return ir_analyze_instruction_coro_suspend(ira, (IrInstructionCoroSuspend *)instruction);
case IrInstructionIdCoroEnd:
return ir_analyze_instruction_coro_end(ira, (IrInstructionCoroEnd *)instruction);
case IrInstructionIdCoroFree:
return ir_analyze_instruction_coro_free(ira, (IrInstructionCoroFree *)instruction);
case IrInstructionIdCoroResume:
return ir_analyze_instruction_coro_resume(ira, (IrInstructionCoroResume *)instruction);
case IrInstructionIdCoroSave:
return ir_analyze_instruction_coro_save(ira, (IrInstructionCoroSave *)instruction);
case IrInstructionIdCoroPromise:
return ir_analyze_instruction_coro_promise(ira, (IrInstructionCoroPromise *)instruction);
case IrInstructionIdCoroAllocHelper:
return ir_analyze_instruction_coro_alloc_helper(ira, (IrInstructionCoroAllocHelper *)instruction);
case IrInstructionIdAtomicRmw:
return ir_analyze_instruction_atomic_rmw(ira, (IrInstructionAtomicRmw *)instruction);
case IrInstructionIdAtomicLoad:
return ir_analyze_instruction_atomic_load(ira, (IrInstructionAtomicLoad *)instruction);
case IrInstructionIdPromiseResultType:
return ir_analyze_instruction_promise_result_type(ira, (IrInstructionPromiseResultType *)instruction);
case IrInstructionIdAwaitBookkeeping:
return ir_analyze_instruction_await_bookkeeping(ira, (IrInstructionAwaitBookkeeping *)instruction);
case IrInstructionIdSaveErrRetAddr:
return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstructionSaveErrRetAddr *)instruction);
case IrInstructionIdAddImplicitReturnType:
return ir_analyze_instruction_add_implicit_return_type(ira, (IrInstructionAddImplicitReturnType *)instruction);
case IrInstructionIdMergeErrRetTraces:
return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction);
case IrInstructionIdMarkErrRetTracePtr:
return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction);
case IrInstructionIdSqrt:
return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction);
case IrInstructionIdBswap:
return ir_analyze_instruction_bswap(ira, (IrInstructionBswap *)instruction);
case IrInstructionIdBitReverse:
return ir_analyze_instruction_bit_reverse(ira, (IrInstructionBitReverse *)instruction);
case IrInstructionIdIntToErr:
return ir_analyze_instruction_int_to_err(ira, (IrInstructionIntToErr *)instruction);
case IrInstructionIdErrToInt:
return ir_analyze_instruction_err_to_int(ira, (IrInstructionErrToInt *)instruction);
case IrInstructionIdIntToEnum:
return ir_analyze_instruction_int_to_enum(ira, (IrInstructionIntToEnum *)instruction);
case IrInstructionIdEnumToInt:
return ir_analyze_instruction_enum_to_int(ira, (IrInstructionEnumToInt *)instruction);
case IrInstructionIdCheckRuntimeScope:
return ir_analyze_instruction_check_runtime_scope(ira, (IrInstructionCheckRuntimeScope *)instruction);
}
zig_unreachable();
}
static IrInstruction *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *old_instruction) {
IrInstruction *new_instruction = ir_analyze_instruction_nocast(ira, old_instruction);
assert(new_instruction->value.type != nullptr);
old_instruction->child = new_instruction;
return new_instruction;
}
// 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.
ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_exec,
ZigType *expected_type, AstNode *expected_type_source_node)
{
assert(!old_exec->invalid);
assert(expected_type == nullptr || !type_is_invalid(expected_type));
IrAnalyze *ira = allocate<IrAnalyze>(1);
old_exec->analysis = ira;
ira->codegen = codegen;
ZigFn *fn_entry = exec_fn_entry(old_exec);
bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync;
ira->explicit_return_type = is_async ? get_promise_type(codegen, expected_type) : expected_type;
ira->explicit_return_type_source_node = expected_type_source_node;
ira->old_irb.codegen = codegen;
ira->old_irb.exec = old_exec;
ira->new_irb.codegen = codegen;
ira->new_irb.exec = new_exec;
ConstExprValue *vals = create_const_vals(ira->old_irb.exec->mem_slot_count);
ira->exec_context.mem_slot_list.resize(ira->old_irb.exec->mem_slot_count);
for (size_t i = 0; i < ira->exec_context.mem_slot_list.length; i += 1) {
ira->exec_context.mem_slot_list.items[i] = &vals[i];
}
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->old_bb_index = 0;
ir_start_bb(ira, old_entry_bb, nullptr);
while (ira->old_bb_index < ira->old_irb.exec->basic_block_list.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;
}
IrInstruction *new_instruction = ir_analyze_instruction(ira, old_instruction);
if (type_is_invalid(new_instruction->value.type) && ir_should_inline(new_exec, old_instruction->scope)) {
return ira->codegen->builtin_types.entry_invalid;
}
// unreachable instructions do their own control flow.
if (new_instruction->value.type->id == ZigTypeIdUnreachable)
continue;
ira->instruction_index += 1;
}
if (new_exec->invalid) {
return ira->codegen->builtin_types.entry_invalid;
} else if (ira->src_implicit_return_type_list.length == 0) {
return codegen->builtin_types.entry_unreachable;
} else {
return ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items,
ira->src_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 IrInstructionIdDeclVarSrc:
case IrInstructionIdDeclVarGen:
case IrInstructionIdStorePtr:
case IrInstructionIdCall:
case IrInstructionIdReturn:
case IrInstructionIdUnreachable:
case IrInstructionIdSetCold:
case IrInstructionIdSetRuntimeSafety:
case IrInstructionIdSetFloatMode:
case IrInstructionIdImport:
case IrInstructionIdCompileErr:
case IrInstructionIdCompileLog:
case IrInstructionIdCImport:
case IrInstructionIdCInclude:
case IrInstructionIdCDefine:
case IrInstructionIdCUndef:
case IrInstructionIdFence:
case IrInstructionIdMemset:
case IrInstructionIdMemcpy:
case IrInstructionIdBreakpoint:
case IrInstructionIdOverflowOp: // TODO when we support multiple returns this can be side effect free
case IrInstructionIdCheckSwitchProngs:
case IrInstructionIdCheckStatementIsVoid:
case IrInstructionIdCheckRuntimeScope:
case IrInstructionIdPanic:
case IrInstructionIdSetEvalBranchQuota:
case IrInstructionIdPtrType:
case IrInstructionIdSetAlignStack:
case IrInstructionIdExport:
case IrInstructionIdCancel:
case IrInstructionIdCoroId:
case IrInstructionIdCoroBegin:
case IrInstructionIdCoroAllocFail:
case IrInstructionIdCoroEnd:
case IrInstructionIdCoroResume:
case IrInstructionIdCoroSave:
case IrInstructionIdCoroAllocHelper:
case IrInstructionIdAwaitBookkeeping:
case IrInstructionIdSaveErrRetAddr:
case IrInstructionIdAddImplicitReturnType:
case IrInstructionIdMergeErrRetTraces:
case IrInstructionIdMarkErrRetTracePtr:
case IrInstructionIdAtomicRmw:
case IrInstructionIdCmpxchgGen:
case IrInstructionIdCmpxchgSrc:
case IrInstructionIdAssertZero:
case IrInstructionIdResizeSlice:
case IrInstructionIdGlobalAsm:
return true;
case IrInstructionIdPhi:
case IrInstructionIdUnOp:
case IrInstructionIdBinOp:
case IrInstructionIdLoadPtr:
case IrInstructionIdLoadPtrGen:
case IrInstructionIdConst:
case IrInstructionIdCast:
case IrInstructionIdContainerInitList:
case IrInstructionIdContainerInitFields:
case IrInstructionIdStructInit:
case IrInstructionIdUnionInit:
case IrInstructionIdFieldPtr:
case IrInstructionIdElemPtr:
case IrInstructionIdVarPtr:
case IrInstructionIdTypeOf:
case IrInstructionIdToPtrType:
case IrInstructionIdPtrTypeChild:
case IrInstructionIdStructFieldPtr:
case IrInstructionIdUnionFieldPtr:
case IrInstructionIdArrayType:
case IrInstructionIdPromiseType:
case IrInstructionIdSliceType:
case IrInstructionIdSizeOf:
case IrInstructionIdTestNonNull:
case IrInstructionIdOptionalUnwrapPtr:
case IrInstructionIdClz:
case IrInstructionIdCtz:
case IrInstructionIdPopCount:
case IrInstructionIdSwitchVar:
case IrInstructionIdSwitchTarget:
case IrInstructionIdUnionTag:
case IrInstructionIdRef:
case IrInstructionIdEmbedFile:
case IrInstructionIdTruncate:
case IrInstructionIdIntType:
case IrInstructionIdVectorType:
case IrInstructionIdBoolNot:
case IrInstructionIdSlice:
case IrInstructionIdMemberCount:
case IrInstructionIdMemberType:
case IrInstructionIdMemberName:
case IrInstructionIdAlignOf:
case IrInstructionIdReturnAddress:
case IrInstructionIdFrameAddress:
case IrInstructionIdHandle:
case IrInstructionIdTestErr:
case IrInstructionIdUnwrapErrCode:
case IrInstructionIdOptionalWrap:
case IrInstructionIdErrWrapCode:
case IrInstructionIdErrWrapPayload:
case IrInstructionIdFnProto:
case IrInstructionIdTestComptime:
case IrInstructionIdPtrCastSrc:
case IrInstructionIdPtrCastGen:
case IrInstructionIdBitCast:
case IrInstructionIdBitCastGen:
case IrInstructionIdWidenOrShorten:
case IrInstructionIdPtrToInt:
case IrInstructionIdIntToPtr:
case IrInstructionIdIntToEnum:
case IrInstructionIdIntToErr:
case IrInstructionIdErrToInt:
case IrInstructionIdDeclRef:
case IrInstructionIdErrName:
case IrInstructionIdTypeName:
case IrInstructionIdTagName:
case IrInstructionIdFieldParentPtr:
case IrInstructionIdByteOffsetOf:
case IrInstructionIdBitOffsetOf:
case IrInstructionIdTypeInfo:
case IrInstructionIdTypeId:
case IrInstructionIdAlignCast:
case IrInstructionIdOpaqueType:
case IrInstructionIdArgType:
case IrInstructionIdTagType:
case IrInstructionIdErrorReturnTrace:
case IrInstructionIdErrorUnion:
case IrInstructionIdGetImplicitAllocator:
case IrInstructionIdCoroAlloc:
case IrInstructionIdCoroSize:
case IrInstructionIdCoroSuspend:
case IrInstructionIdCoroFree:
case IrInstructionIdCoroPromise:
case IrInstructionIdPromiseResultType:
case IrInstructionIdSqrt:
case IrInstructionIdBswap:
case IrInstructionIdBitReverse:
case IrInstructionIdAtomicLoad:
case IrInstructionIdIntCast:
case IrInstructionIdFloatCast:
case IrInstructionIdErrSetCast:
case IrInstructionIdIntToFloat:
case IrInstructionIdFloatToInt:
case IrInstructionIdBoolToInt:
case IrInstructionIdFromBytes:
case IrInstructionIdToBytes:
case IrInstructionIdEnumToInt:
case IrInstructionIdVectorToArray:
case IrInstructionIdArrayToVector:
return false;
case IrInstructionIdAsm:
{
IrInstructionAsm *asm_instruction = (IrInstructionAsm *)instruction;
return asm_instruction->has_side_effects;
}
case IrInstructionIdUnwrapErrPayload:
{
IrInstructionUnwrapErrPayload *unwrap_err_payload_instruction =
(IrInstructionUnwrapErrPayload *)instruction;
return unwrap_err_payload_instruction->safety_check_on;
}
}
zig_unreachable();
}