Files
zig/src/ir.cpp
Andrew Kelley 8f336b3970 revert one part of ir get_elem_ptr analysis
this reverts one part of 4c3bfeca. it solves some behavior regressions
but introduces new ones. This change was incorrect to make however,
and this commit takes the code in a better direction.
2020-01-16 21:58:52 -05:00

29789 lines
1.3 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 "util.hpp"
#include <errno.h>
struct IrBuilder {
CodeGen *codegen;
IrExecutable *exec;
IrBasicBlock *current_basic_block;
AstNode *main_block_node;
};
struct IrAnalyze {
CodeGen *codegen;
IrBuilder old_irb;
IrBuilder new_irb;
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;
ZigList<IrSuspendPosition> resume_stack;
IrBasicBlock *const_predecessor_bb;
size_t ref_count;
size_t break_debug_id; // for debugging purposes
IrInstruction *return_ptr;
// For the purpose of using in a debugger
void dump();
};
enum ConstCastResultId {
ConstCastResultIdOk,
ConstCastResultIdInvalid,
ConstCastResultIdErrSet,
ConstCastResultIdErrSetGlobal,
ConstCastResultIdPointerChild,
ConstCastResultIdSliceChild,
ConstCastResultIdOptionalChild,
ConstCastResultIdErrorUnionPayload,
ConstCastResultIdErrorUnionErrorSet,
ConstCastResultIdFnAlign,
ConstCastResultIdFnCC,
ConstCastResultIdFnVarArgs,
ConstCastResultIdFnIsGeneric,
ConstCastResultIdFnReturnType,
ConstCastResultIdFnArgCount,
ConstCastResultIdFnGenericArgCount,
ConstCastResultIdFnArg,
ConstCastResultIdFnArgNoAlias,
ConstCastResultIdType,
ConstCastResultIdUnresolvedInferredErrSet,
ConstCastResultIdAsyncAllocatorType,
ConstCastResultIdBadAllowsZero,
ConstCastResultIdArrayChild,
ConstCastResultIdSentinelArrays,
ConstCastResultIdPtrLens,
ConstCastResultIdCV,
ConstCastResultIdPtrSentinel,
ConstCastResultIdIntShorten,
};
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 ConstCastArrayMismatch;
struct ConstCastBadAllowsZero;
struct ConstCastBadNullTermArrays;
struct ConstCastBadCV;
struct ConstCastPtrSentinel;
struct ConstCastIntShorten;
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;
ConstCastArrayMismatch *array_mismatch;
ConstCastOnly *return_type;
ConstCastOnly *null_wrap_ptr_child;
ConstCastArg fn_arg;
ConstCastArgNoAlias arg_no_alias;
ConstCastBadAllowsZero *bad_allows_zero;
ConstCastBadNullTermArrays *sentinel_arrays;
ConstCastBadCV *bad_cv;
ConstCastPtrSentinel *bad_ptr_sentinel;
ConstCastIntShorten *int_shorten;
} 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 ConstCastArrayMismatch {
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;
};
struct ConstCastBadNullTermArrays {
ConstCastOnly child;
ZigType *wanted_type;
ZigType *actual_type;
};
struct ConstCastBadCV {
ZigType *wanted_type;
ZigType *actual_type;
};
struct ConstCastPtrSentinel {
ZigType *wanted_type;
ZigType *actual_type;
};
struct ConstCastIntShorten {
ZigType *wanted_type;
ZigType *actual_type;
};
// for debugging purposes
struct DbgIrBreakPoint {
const char *src_file;
uint32_t line;
};
DbgIrBreakPoint dbg_ir_breakpoints_buf[20];
size_t dbg_ir_breakpoints_count = 0;
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,
ResultLoc *result_loc);
static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, ZigType *expected_type);
static IrInstruction *ir_implicit_cast2(IrAnalyze *ira, IrInstruction *value_source_instr,
IrInstruction *value, ZigType *expected_type);
static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr,
ResultLoc *result_loc);
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, bool initializing);
static void ir_assert(bool ok, IrInstruction *source_instruction);
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, ResultLoc *result_loc);
static IrInstruction *ir_expr_wrap(IrBuilder *irb, Scope *scope, IrInstruction *inst, ResultLoc *result_loc);
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, ZigValue *val);
static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ZigValue *val);
static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node,
ZigValue *out_val, ZigValue *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 ZigValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed);
static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align);
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 IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspend_source_instr,
ResultLoc *result_loc, ZigType *value_type, IrInstruction *value, bool force_runtime,
bool allow_discard);
static IrInstruction *ir_resolve_result(IrAnalyze *ira, IrInstruction *suspend_source_instr,
ResultLoc *result_loc, ZigType *value_type, IrInstruction *value, bool force_runtime,
bool allow_discard);
static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *base_ptr, bool safety_check_on, bool initializing);
static IrInstruction *ir_analyze_unwrap_error_payload(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *base_ptr, bool safety_check_on, bool initializing);
static IrInstruction *ir_analyze_unwrap_err_code(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *base_ptr, bool initializing);
static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *ptr, IrInstruction *uncasted_value, bool allow_write_through_const);
static IrInstruction *ir_gen_union_init_expr(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *union_type, IrInstruction *field_name, AstNode *expr_node,
LVal lval, ResultLoc *parent_result_loc);
static void ir_reset_result(ResultLoc *result_loc);
static Buf *get_anon_type_name(CodeGen *codegen, IrExecutable *exec, const char *kind_name,
Scope *scope, AstNode *source_node, Buf *out_bare_name);
static ResultLocCast *ir_build_cast_result_loc(IrBuilder *irb, IrInstruction *dest_type,
ResultLoc *parent_result_loc);
static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction *source_instr,
TypeStructField *field, IrInstruction *struct_ptr, ZigType *struct_type, bool initializing);
static IrInstruction *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_name,
IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type);
static ResultLoc *no_result_loc(void);
static IrInstruction *ir_analyze_test_non_null(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *value);
static void destroy_instruction(IrInstruction *inst) {
#ifdef ZIG_ENABLE_MEM_PROFILE
const char *name = ir_instruction_type_str(inst->id);
#else
const char *name = nullptr;
#endif
switch (inst->id) {
case IrInstructionIdInvalid:
zig_unreachable();
case IrInstructionIdReturn:
return destroy(reinterpret_cast<IrInstructionReturn *>(inst), name);
case IrInstructionIdConst:
return destroy(reinterpret_cast<IrInstructionConst *>(inst), name);
case IrInstructionIdBinOp:
return destroy(reinterpret_cast<IrInstructionBinOp *>(inst), name);
case IrInstructionIdMergeErrSets:
return destroy(reinterpret_cast<IrInstructionMergeErrSets *>(inst), name);
case IrInstructionIdDeclVarSrc:
return destroy(reinterpret_cast<IrInstructionDeclVarSrc *>(inst), name);
case IrInstructionIdCast:
return destroy(reinterpret_cast<IrInstructionCast *>(inst), name);
case IrInstructionIdCallSrc:
return destroy(reinterpret_cast<IrInstructionCallSrc *>(inst), name);
case IrInstructionIdCallSrcArgs:
return destroy(reinterpret_cast<IrInstructionCallSrcArgs *>(inst), name);
case IrInstructionIdCallExtra:
return destroy(reinterpret_cast<IrInstructionCallExtra *>(inst), name);
case IrInstructionIdCallGen:
return destroy(reinterpret_cast<IrInstructionCallGen *>(inst), name);
case IrInstructionIdUnOp:
return destroy(reinterpret_cast<IrInstructionUnOp *>(inst), name);
case IrInstructionIdCondBr:
return destroy(reinterpret_cast<IrInstructionCondBr *>(inst), name);
case IrInstructionIdBr:
return destroy(reinterpret_cast<IrInstructionBr *>(inst), name);
case IrInstructionIdPhi:
return destroy(reinterpret_cast<IrInstructionPhi *>(inst), name);
case IrInstructionIdContainerInitList:
return destroy(reinterpret_cast<IrInstructionContainerInitList *>(inst), name);
case IrInstructionIdContainerInitFields:
return destroy(reinterpret_cast<IrInstructionContainerInitFields *>(inst), name);
case IrInstructionIdUnreachable:
return destroy(reinterpret_cast<IrInstructionUnreachable *>(inst), name);
case IrInstructionIdElemPtr:
return destroy(reinterpret_cast<IrInstructionElemPtr *>(inst), name);
case IrInstructionIdVarPtr:
return destroy(reinterpret_cast<IrInstructionVarPtr *>(inst), name);
case IrInstructionIdReturnPtr:
return destroy(reinterpret_cast<IrInstructionReturnPtr *>(inst), name);
case IrInstructionIdLoadPtr:
return destroy(reinterpret_cast<IrInstructionLoadPtr *>(inst), name);
case IrInstructionIdLoadPtrGen:
return destroy(reinterpret_cast<IrInstructionLoadPtrGen *>(inst), name);
case IrInstructionIdStorePtr:
return destroy(reinterpret_cast<IrInstructionStorePtr *>(inst), name);
case IrInstructionIdVectorStoreElem:
return destroy(reinterpret_cast<IrInstructionVectorStoreElem *>(inst), name);
case IrInstructionIdTypeOf:
return destroy(reinterpret_cast<IrInstructionTypeOf *>(inst), name);
case IrInstructionIdFieldPtr:
return destroy(reinterpret_cast<IrInstructionFieldPtr *>(inst), name);
case IrInstructionIdStructFieldPtr:
return destroy(reinterpret_cast<IrInstructionStructFieldPtr *>(inst), name);
case IrInstructionIdUnionFieldPtr:
return destroy(reinterpret_cast<IrInstructionUnionFieldPtr *>(inst), name);
case IrInstructionIdSetCold:
return destroy(reinterpret_cast<IrInstructionSetCold *>(inst), name);
case IrInstructionIdSetRuntimeSafety:
return destroy(reinterpret_cast<IrInstructionSetRuntimeSafety *>(inst), name);
case IrInstructionIdSetFloatMode:
return destroy(reinterpret_cast<IrInstructionSetFloatMode *>(inst), name);
case IrInstructionIdArrayType:
return destroy(reinterpret_cast<IrInstructionArrayType *>(inst), name);
case IrInstructionIdSliceType:
return destroy(reinterpret_cast<IrInstructionSliceType *>(inst), name);
case IrInstructionIdAnyFrameType:
return destroy(reinterpret_cast<IrInstructionAnyFrameType *>(inst), name);
case IrInstructionIdAsmSrc:
return destroy(reinterpret_cast<IrInstructionAsmSrc *>(inst), name);
case IrInstructionIdAsmGen:
return destroy(reinterpret_cast<IrInstructionAsmGen *>(inst), name);
case IrInstructionIdSizeOf:
return destroy(reinterpret_cast<IrInstructionSizeOf *>(inst), name);
case IrInstructionIdTestNonNull:
return destroy(reinterpret_cast<IrInstructionTestNonNull *>(inst), name);
case IrInstructionIdOptionalUnwrapPtr:
return destroy(reinterpret_cast<IrInstructionOptionalUnwrapPtr *>(inst), name);
case IrInstructionIdPopCount:
return destroy(reinterpret_cast<IrInstructionPopCount *>(inst), name);
case IrInstructionIdClz:
return destroy(reinterpret_cast<IrInstructionClz *>(inst), name);
case IrInstructionIdCtz:
return destroy(reinterpret_cast<IrInstructionCtz *>(inst), name);
case IrInstructionIdBswap:
return destroy(reinterpret_cast<IrInstructionBswap *>(inst), name);
case IrInstructionIdBitReverse:
return destroy(reinterpret_cast<IrInstructionBitReverse *>(inst), name);
case IrInstructionIdSwitchBr:
return destroy(reinterpret_cast<IrInstructionSwitchBr *>(inst), name);
case IrInstructionIdSwitchVar:
return destroy(reinterpret_cast<IrInstructionSwitchVar *>(inst), name);
case IrInstructionIdSwitchElseVar:
return destroy(reinterpret_cast<IrInstructionSwitchElseVar *>(inst), name);
case IrInstructionIdSwitchTarget:
return destroy(reinterpret_cast<IrInstructionSwitchTarget *>(inst), name);
case IrInstructionIdUnionTag:
return destroy(reinterpret_cast<IrInstructionUnionTag *>(inst), name);
case IrInstructionIdImport:
return destroy(reinterpret_cast<IrInstructionImport *>(inst), name);
case IrInstructionIdRef:
return destroy(reinterpret_cast<IrInstructionRef *>(inst), name);
case IrInstructionIdRefGen:
return destroy(reinterpret_cast<IrInstructionRefGen *>(inst), name);
case IrInstructionIdCompileErr:
return destroy(reinterpret_cast<IrInstructionCompileErr *>(inst), name);
case IrInstructionIdCompileLog:
return destroy(reinterpret_cast<IrInstructionCompileLog *>(inst), name);
case IrInstructionIdErrName:
return destroy(reinterpret_cast<IrInstructionErrName *>(inst), name);
case IrInstructionIdCImport:
return destroy(reinterpret_cast<IrInstructionCImport *>(inst), name);
case IrInstructionIdCInclude:
return destroy(reinterpret_cast<IrInstructionCInclude *>(inst), name);
case IrInstructionIdCDefine:
return destroy(reinterpret_cast<IrInstructionCDefine *>(inst), name);
case IrInstructionIdCUndef:
return destroy(reinterpret_cast<IrInstructionCUndef *>(inst), name);
case IrInstructionIdEmbedFile:
return destroy(reinterpret_cast<IrInstructionEmbedFile *>(inst), name);
case IrInstructionIdCmpxchgSrc:
return destroy(reinterpret_cast<IrInstructionCmpxchgSrc *>(inst), name);
case IrInstructionIdCmpxchgGen:
return destroy(reinterpret_cast<IrInstructionCmpxchgGen *>(inst), name);
case IrInstructionIdFence:
return destroy(reinterpret_cast<IrInstructionFence *>(inst), name);
case IrInstructionIdTruncate:
return destroy(reinterpret_cast<IrInstructionTruncate *>(inst), name);
case IrInstructionIdIntCast:
return destroy(reinterpret_cast<IrInstructionIntCast *>(inst), name);
case IrInstructionIdFloatCast:
return destroy(reinterpret_cast<IrInstructionFloatCast *>(inst), name);
case IrInstructionIdErrSetCast:
return destroy(reinterpret_cast<IrInstructionErrSetCast *>(inst), name);
case IrInstructionIdFromBytes:
return destroy(reinterpret_cast<IrInstructionFromBytes *>(inst), name);
case IrInstructionIdToBytes:
return destroy(reinterpret_cast<IrInstructionToBytes *>(inst), name);
case IrInstructionIdIntToFloat:
return destroy(reinterpret_cast<IrInstructionIntToFloat *>(inst), name);
case IrInstructionIdFloatToInt:
return destroy(reinterpret_cast<IrInstructionFloatToInt *>(inst), name);
case IrInstructionIdBoolToInt:
return destroy(reinterpret_cast<IrInstructionBoolToInt *>(inst), name);
case IrInstructionIdIntType:
return destroy(reinterpret_cast<IrInstructionIntType *>(inst), name);
case IrInstructionIdVectorType:
return destroy(reinterpret_cast<IrInstructionVectorType *>(inst), name);
case IrInstructionIdShuffleVector:
return destroy(reinterpret_cast<IrInstructionShuffleVector *>(inst), name);
case IrInstructionIdSplatSrc:
return destroy(reinterpret_cast<IrInstructionSplatSrc *>(inst), name);
case IrInstructionIdSplatGen:
return destroy(reinterpret_cast<IrInstructionSplatGen *>(inst), name);
case IrInstructionIdBoolNot:
return destroy(reinterpret_cast<IrInstructionBoolNot *>(inst), name);
case IrInstructionIdMemset:
return destroy(reinterpret_cast<IrInstructionMemset *>(inst), name);
case IrInstructionIdMemcpy:
return destroy(reinterpret_cast<IrInstructionMemcpy *>(inst), name);
case IrInstructionIdSliceSrc:
return destroy(reinterpret_cast<IrInstructionSliceSrc *>(inst), name);
case IrInstructionIdSliceGen:
return destroy(reinterpret_cast<IrInstructionSliceGen *>(inst), name);
case IrInstructionIdMemberCount:
return destroy(reinterpret_cast<IrInstructionMemberCount *>(inst), name);
case IrInstructionIdMemberType:
return destroy(reinterpret_cast<IrInstructionMemberType *>(inst), name);
case IrInstructionIdMemberName:
return destroy(reinterpret_cast<IrInstructionMemberName *>(inst), name);
case IrInstructionIdBreakpoint:
return destroy(reinterpret_cast<IrInstructionBreakpoint *>(inst), name);
case IrInstructionIdReturnAddress:
return destroy(reinterpret_cast<IrInstructionReturnAddress *>(inst), name);
case IrInstructionIdFrameAddress:
return destroy(reinterpret_cast<IrInstructionFrameAddress *>(inst), name);
case IrInstructionIdFrameHandle:
return destroy(reinterpret_cast<IrInstructionFrameHandle *>(inst), name);
case IrInstructionIdFrameType:
return destroy(reinterpret_cast<IrInstructionFrameType *>(inst), name);
case IrInstructionIdFrameSizeSrc:
return destroy(reinterpret_cast<IrInstructionFrameSizeSrc *>(inst), name);
case IrInstructionIdFrameSizeGen:
return destroy(reinterpret_cast<IrInstructionFrameSizeGen *>(inst), name);
case IrInstructionIdAlignOf:
return destroy(reinterpret_cast<IrInstructionAlignOf *>(inst), name);
case IrInstructionIdOverflowOp:
return destroy(reinterpret_cast<IrInstructionOverflowOp *>(inst), name);
case IrInstructionIdTestErrSrc:
return destroy(reinterpret_cast<IrInstructionTestErrSrc *>(inst), name);
case IrInstructionIdTestErrGen:
return destroy(reinterpret_cast<IrInstructionTestErrGen *>(inst), name);
case IrInstructionIdUnwrapErrCode:
return destroy(reinterpret_cast<IrInstructionUnwrapErrCode *>(inst), name);
case IrInstructionIdUnwrapErrPayload:
return destroy(reinterpret_cast<IrInstructionUnwrapErrPayload *>(inst), name);
case IrInstructionIdOptionalWrap:
return destroy(reinterpret_cast<IrInstructionOptionalWrap *>(inst), name);
case IrInstructionIdErrWrapCode:
return destroy(reinterpret_cast<IrInstructionErrWrapCode *>(inst), name);
case IrInstructionIdErrWrapPayload:
return destroy(reinterpret_cast<IrInstructionErrWrapPayload *>(inst), name);
case IrInstructionIdFnProto:
return destroy(reinterpret_cast<IrInstructionFnProto *>(inst), name);
case IrInstructionIdTestComptime:
return destroy(reinterpret_cast<IrInstructionTestComptime *>(inst), name);
case IrInstructionIdPtrCastSrc:
return destroy(reinterpret_cast<IrInstructionPtrCastSrc *>(inst), name);
case IrInstructionIdPtrCastGen:
return destroy(reinterpret_cast<IrInstructionPtrCastGen *>(inst), name);
case IrInstructionIdBitCastSrc:
return destroy(reinterpret_cast<IrInstructionBitCastSrc *>(inst), name);
case IrInstructionIdBitCastGen:
return destroy(reinterpret_cast<IrInstructionBitCastGen *>(inst), name);
case IrInstructionIdWidenOrShorten:
return destroy(reinterpret_cast<IrInstructionWidenOrShorten *>(inst), name);
case IrInstructionIdPtrToInt:
return destroy(reinterpret_cast<IrInstructionPtrToInt *>(inst), name);
case IrInstructionIdIntToPtr:
return destroy(reinterpret_cast<IrInstructionIntToPtr *>(inst), name);
case IrInstructionIdIntToEnum:
return destroy(reinterpret_cast<IrInstructionIntToEnum *>(inst), name);
case IrInstructionIdIntToErr:
return destroy(reinterpret_cast<IrInstructionIntToErr *>(inst), name);
case IrInstructionIdErrToInt:
return destroy(reinterpret_cast<IrInstructionErrToInt *>(inst), name);
case IrInstructionIdCheckSwitchProngs:
return destroy(reinterpret_cast<IrInstructionCheckSwitchProngs *>(inst), name);
case IrInstructionIdCheckStatementIsVoid:
return destroy(reinterpret_cast<IrInstructionCheckStatementIsVoid *>(inst), name);
case IrInstructionIdTypeName:
return destroy(reinterpret_cast<IrInstructionTypeName *>(inst), name);
case IrInstructionIdTagName:
return destroy(reinterpret_cast<IrInstructionTagName *>(inst), name);
case IrInstructionIdPtrType:
return destroy(reinterpret_cast<IrInstructionPtrType *>(inst), name);
case IrInstructionIdDeclRef:
return destroy(reinterpret_cast<IrInstructionDeclRef *>(inst), name);
case IrInstructionIdPanic:
return destroy(reinterpret_cast<IrInstructionPanic *>(inst), name);
case IrInstructionIdFieldParentPtr:
return destroy(reinterpret_cast<IrInstructionFieldParentPtr *>(inst), name);
case IrInstructionIdByteOffsetOf:
return destroy(reinterpret_cast<IrInstructionByteOffsetOf *>(inst), name);
case IrInstructionIdBitOffsetOf:
return destroy(reinterpret_cast<IrInstructionBitOffsetOf *>(inst), name);
case IrInstructionIdTypeInfo:
return destroy(reinterpret_cast<IrInstructionTypeInfo *>(inst), name);
case IrInstructionIdType:
return destroy(reinterpret_cast<IrInstructionType *>(inst), name);
case IrInstructionIdHasField:
return destroy(reinterpret_cast<IrInstructionHasField *>(inst), name);
case IrInstructionIdTypeId:
return destroy(reinterpret_cast<IrInstructionTypeId *>(inst), name);
case IrInstructionIdSetEvalBranchQuota:
return destroy(reinterpret_cast<IrInstructionSetEvalBranchQuota *>(inst), name);
case IrInstructionIdAlignCast:
return destroy(reinterpret_cast<IrInstructionAlignCast *>(inst), name);
case IrInstructionIdImplicitCast:
return destroy(reinterpret_cast<IrInstructionImplicitCast *>(inst), name);
case IrInstructionIdResolveResult:
return destroy(reinterpret_cast<IrInstructionResolveResult *>(inst), name);
case IrInstructionIdResetResult:
return destroy(reinterpret_cast<IrInstructionResetResult *>(inst), name);
case IrInstructionIdOpaqueType:
return destroy(reinterpret_cast<IrInstructionOpaqueType *>(inst), name);
case IrInstructionIdSetAlignStack:
return destroy(reinterpret_cast<IrInstructionSetAlignStack *>(inst), name);
case IrInstructionIdArgType:
return destroy(reinterpret_cast<IrInstructionArgType *>(inst), name);
case IrInstructionIdTagType:
return destroy(reinterpret_cast<IrInstructionTagType *>(inst), name);
case IrInstructionIdExport:
return destroy(reinterpret_cast<IrInstructionExport *>(inst), name);
case IrInstructionIdErrorReturnTrace:
return destroy(reinterpret_cast<IrInstructionErrorReturnTrace *>(inst), name);
case IrInstructionIdErrorUnion:
return destroy(reinterpret_cast<IrInstructionErrorUnion *>(inst), name);
case IrInstructionIdAtomicRmw:
return destroy(reinterpret_cast<IrInstructionAtomicRmw *>(inst), name);
case IrInstructionIdSaveErrRetAddr:
return destroy(reinterpret_cast<IrInstructionSaveErrRetAddr *>(inst), name);
case IrInstructionIdAddImplicitReturnType:
return destroy(reinterpret_cast<IrInstructionAddImplicitReturnType *>(inst), name);
case IrInstructionIdFloatOp:
return destroy(reinterpret_cast<IrInstructionFloatOp *>(inst), name);
case IrInstructionIdMulAdd:
return destroy(reinterpret_cast<IrInstructionMulAdd *>(inst), name);
case IrInstructionIdAtomicLoad:
return destroy(reinterpret_cast<IrInstructionAtomicLoad *>(inst), name);
case IrInstructionIdAtomicStore:
return destroy(reinterpret_cast<IrInstructionAtomicStore *>(inst), name);
case IrInstructionIdEnumToInt:
return destroy(reinterpret_cast<IrInstructionEnumToInt *>(inst), name);
case IrInstructionIdCheckRuntimeScope:
return destroy(reinterpret_cast<IrInstructionCheckRuntimeScope *>(inst), name);
case IrInstructionIdDeclVarGen:
return destroy(reinterpret_cast<IrInstructionDeclVarGen *>(inst), name);
case IrInstructionIdArrayToVector:
return destroy(reinterpret_cast<IrInstructionArrayToVector *>(inst), name);
case IrInstructionIdVectorToArray:
return destroy(reinterpret_cast<IrInstructionVectorToArray *>(inst), name);
case IrInstructionIdPtrOfArrayToSlice:
return destroy(reinterpret_cast<IrInstructionPtrOfArrayToSlice *>(inst), name);
case IrInstructionIdAssertZero:
return destroy(reinterpret_cast<IrInstructionAssertZero *>(inst), name);
case IrInstructionIdAssertNonNull:
return destroy(reinterpret_cast<IrInstructionAssertNonNull *>(inst), name);
case IrInstructionIdResizeSlice:
return destroy(reinterpret_cast<IrInstructionResizeSlice *>(inst), name);
case IrInstructionIdHasDecl:
return destroy(reinterpret_cast<IrInstructionHasDecl *>(inst), name);
case IrInstructionIdUndeclaredIdent:
return destroy(reinterpret_cast<IrInstructionUndeclaredIdent *>(inst), name);
case IrInstructionIdAllocaSrc:
return destroy(reinterpret_cast<IrInstructionAllocaSrc *>(inst), name);
case IrInstructionIdAllocaGen:
return destroy(reinterpret_cast<IrInstructionAllocaGen *>(inst), name);
case IrInstructionIdEndExpr:
return destroy(reinterpret_cast<IrInstructionEndExpr *>(inst), name);
case IrInstructionIdUnionInitNamedField:
return destroy(reinterpret_cast<IrInstructionUnionInitNamedField *>(inst), name);
case IrInstructionIdSuspendBegin:
return destroy(reinterpret_cast<IrInstructionSuspendBegin *>(inst), name);
case IrInstructionIdSuspendFinish:
return destroy(reinterpret_cast<IrInstructionSuspendFinish *>(inst), name);
case IrInstructionIdResume:
return destroy(reinterpret_cast<IrInstructionResume *>(inst), name);
case IrInstructionIdAwaitSrc:
return destroy(reinterpret_cast<IrInstructionAwaitSrc *>(inst), name);
case IrInstructionIdAwaitGen:
return destroy(reinterpret_cast<IrInstructionAwaitGen *>(inst), name);
case IrInstructionIdSpillBegin:
return destroy(reinterpret_cast<IrInstructionSpillBegin *>(inst), name);
case IrInstructionIdSpillEnd:
return destroy(reinterpret_cast<IrInstructionSpillEnd *>(inst), name);
case IrInstructionIdVectorExtractElem:
return destroy(reinterpret_cast<IrInstructionVectorExtractElem *>(inst), name);
}
zig_unreachable();
}
static void ira_ref(IrAnalyze *ira) {
ira->ref_count += 1;
}
static void ira_deref(IrAnalyze *ira) {
if (ira->ref_count > 1) {
ira->ref_count -= 1;
return;
}
assert(ira->ref_count != 0);
for (size_t bb_i = 0; bb_i < ira->old_irb.exec->basic_block_list.length; bb_i += 1) {
IrBasicBlock *pass1_bb = ira->old_irb.exec->basic_block_list.items[bb_i];
for (size_t inst_i = 0; inst_i < pass1_bb->instruction_list.length; inst_i += 1) {
IrInstruction *pass1_inst = pass1_bb->instruction_list.items[inst_i];
destroy_instruction(pass1_inst);
}
destroy(pass1_bb, "IrBasicBlock");
}
ira->old_irb.exec->basic_block_list.deinit();
ira->old_irb.exec->tld_list.deinit();
// cannot destroy here because of var->owner_exec
//destroy(ira->old_irb.exec, "IrExecutablePass1");
ira->src_implicit_return_type_list.deinit();
ira->resume_stack.deinit();
destroy(ira, "IrAnalyze");
}
static ZigValue *const_ptr_pointee_unchecked(CodeGen *g, ZigValue *const_val) {
assert(get_src_ptr_type(const_val->type) != nullptr);
assert(const_val->special == ConstValSpecialStatic);
ZigValue *result;
InferredStructField *isf = const_val->type->data.pointer.inferred_struct_field;
if (isf != nullptr) {
TypeStructField *field = find_struct_type_field(isf->inferred_struct_type, isf->field_name);
assert(field != nullptr);
if (field->is_comptime) {
assert(field->init_val != nullptr);
return field->init_val;
}
assert(const_val->data.x_ptr.special == ConstPtrSpecialRef);
ZigValue *struct_val = const_val->data.x_ptr.data.ref.pointee;
return struct_val->data.x_struct.fields[field->src_index];
}
switch (type_has_one_possible_value(g, const_val->type->data.pointer.child_type)) {
case OnePossibleValueInvalid:
return nullptr;
case OnePossibleValueYes:
return get_the_one_possible_value(g, const_val->type->data.pointer.child_type);
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: {
ZigValue *array_val = const_val->data.x_ptr.data.base_array.array_val;
if (const_val->data.x_ptr.data.base_array.elem_index == array_val->type->data.array.len) {
result = array_val->type->data.array.sentinel;
} else {
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: {
ZigValue *struct_val = const_val->data.x_ptr.data.base_struct.struct_val;
expand_undef_struct(g, 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_tuple(ZigType *type) {
return type->id == ZigTypeIdStruct && type->data.structure.special == StructSpecialInferredTuple;
}
static bool is_slice(ZigType *type) {
return type->id == ZigTypeIdStruct && type->data.structure.special == StructSpecialSlice;
}
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;
}
// This function returns true when you can change the type of a ZigValue and the
// value remains meaningful.
static bool types_have_same_zig_comptime_repr(CodeGen *codegen, ZigType *expected, ZigType *actual) {
if (expected == actual)
return true;
if (get_codegen_ptr_type(expected) != nullptr && get_codegen_ptr_type(actual) != nullptr)
return true;
if (is_opt_err_set(expected) && is_opt_err_set(actual))
return true;
if (expected->id != actual->id)
return false;
switch (expected->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:
case ZigTypeIdAnyFrame:
case ZigTypeIdFn:
return true;
case ZigTypeIdFloat:
return expected->data.floating.bit_count == actual->data.floating.bit_count;
case ZigTypeIdInt:
return expected->data.integral.is_signed == actual->data.integral.is_signed;
case ZigTypeIdStruct:
return is_slice(expected) && is_slice(actual);
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdEnum:
case ZigTypeIdUnion:
case ZigTypeIdVector:
case ZigTypeIdFnFrame:
return false;
case ZigTypeIdArray:
return expected->data.array.len == actual->data.array.len &&
expected->data.array.child_type == actual->data.array.child_type &&
(expected->data.array.sentinel == nullptr || (actual->data.array.sentinel != nullptr &&
const_values_equal(codegen, expected->data.array.sentinel, actual->data.array.sentinel)));
}
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 == ScopeIdTypeOf)
return false;
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 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(ZigValue *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;
}
static void create_result_ptr(CodeGen *codegen, ZigType *expected_type,
ZigValue **out_result, ZigValue **out_result_ptr)
{
ZigValue *result = create_const_vals(1);
ZigValue *result_ptr = create_const_vals(1);
result->special = ConstValSpecialUndef;
result->type = expected_type;
result_ptr->special = ConstValSpecialStatic;
result_ptr->type = get_pointer_to_type(codegen, result->type, false);
result_ptr->data.x_ptr.mut = ConstPtrMutComptimeVar;
result_ptr->data.x_ptr.special = ConstPtrSpecialRef;
result_ptr->data.x_ptr.data.ref.pointee = result;
*out_result = result;
*out_result_ptr = result_ptr;
}
ZigType *ir_analyze_type_expr(IrAnalyze *ira, Scope *scope, AstNode *node) {
Error err;
ZigValue *result;
ZigValue *result_ptr;
create_result_ptr(ira->codegen, ira->codegen->builtin_types.entry_type, &result, &result_ptr);
if ((err = ir_eval_const_value(ira->codegen, scope, node, result_ptr,
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota,
nullptr, nullptr, node, nullptr, ira->new_irb.exec, nullptr, UndefBad)))
{
return ira->codegen->builtin_types.entry_invalid;
}
if (type_is_invalid(result->type))
return ira->codegen->builtin_types.entry_invalid;
assert(result->special != ConstValSpecialRuntime);
ZigType *res_type = result->data.x_type;
destroy(result_ptr, "ZigValue");
destroy(result, "ZigValue");
return res_type;
}
static IrBasicBlock *ir_create_basic_block(IrBuilder *irb, Scope *scope, const char *name_hint) {
IrBasicBlock *result = allocate<IrBasicBlock>(1, "IrBasicBlock");
result->scope = scope;
result->name_hint = name_hint;
result->debug_id = exec_next_debug_id(irb->exec);
result->index = SIZE_MAX; // set later
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(IrInstructionSwitchElseVar *) {
return IrInstructionIdSwitchElseVar;
}
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(IrInstructionMergeErrSets *) {
return IrInstructionIdMergeErrSets;
}
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(IrInstructionVectorStoreElem *) {
return IrInstructionIdVectorStoreElem;
}
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(IrInstructionReturnPtr *) {
return IrInstructionIdReturnPtr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCallSrc *) {
return IrInstructionIdCallSrc;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCallSrcArgs *) {
return IrInstructionIdCallSrcArgs;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCallExtra *) {
return IrInstructionIdCallExtra;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCallGen *) {
return IrInstructionIdCallGen;
}
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(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(IrInstructionAnyFrameType *) {
return IrInstructionIdAnyFrameType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSliceType *) {
return IrInstructionIdSliceType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAsmSrc *) {
return IrInstructionIdAsmSrc;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAsmGen *) {
return IrInstructionIdAsmGen;
}
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(IrInstructionBswap *) {
return IrInstructionIdBswap;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionBitReverse *) {
return IrInstructionIdBitReverse;
}
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(IrInstructionRefGen *) {
return IrInstructionIdRefGen;
}
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(IrInstructionShuffleVector *) {
return IrInstructionIdShuffleVector;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSplatSrc *) {
return IrInstructionIdSplatSrc;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSplatGen *) {
return IrInstructionIdSplatGen;
}
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(IrInstructionSliceSrc *) {
return IrInstructionIdSliceSrc;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSliceGen *) {
return IrInstructionIdSliceGen;
}
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(IrInstructionFrameHandle *) {
return IrInstructionIdFrameHandle;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameType *) {
return IrInstructionIdFrameType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameSizeSrc *) {
return IrInstructionIdFrameSizeSrc;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameSizeGen *) {
return IrInstructionIdFrameSizeGen;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) {
return IrInstructionIdAlignOf;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionOverflowOp *) {
return IrInstructionIdOverflowOp;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionTestErrSrc *) {
return IrInstructionIdTestErrSrc;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionTestErrGen *) {
return IrInstructionIdTestErrGen;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionMulAdd *) {
return IrInstructionIdMulAdd;
}
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(IrInstructionBitCastSrc *) {
return IrInstructionIdBitCastSrc;
}
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(IrInstructionType *) {
return IrInstructionIdType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionHasField *) {
return IrInstructionIdHasField;
}
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(IrInstructionImplicitCast *) {
return IrInstructionIdImplicitCast;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionResolveResult *) {
return IrInstructionIdResolveResult;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionResetResult *) {
return IrInstructionIdResetResult;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrOfArrayToSlice *) {
return IrInstructionIdPtrOfArrayToSlice;
}
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(IrInstructionAtomicRmw *) {
return IrInstructionIdAtomicRmw;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicLoad *) {
return IrInstructionIdAtomicLoad;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicStore *) {
return IrInstructionIdAtomicStore;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSaveErrRetAddr *) {
return IrInstructionIdSaveErrRetAddr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAddImplicitReturnType *) {
return IrInstructionIdAddImplicitReturnType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatOp *) {
return IrInstructionIdFloatOp;
}
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;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertNonNull *) {
return IrInstructionIdAssertNonNull;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionHasDecl *) {
return IrInstructionIdHasDecl;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionUndeclaredIdent *) {
return IrInstructionIdUndeclaredIdent;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAllocaSrc *) {
return IrInstructionIdAllocaSrc;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAllocaGen *) {
return IrInstructionIdAllocaGen;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionEndExpr *) {
return IrInstructionIdEndExpr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionInitNamedField *) {
return IrInstructionIdUnionInitNamedField;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSuspendBegin *) {
return IrInstructionIdSuspendBegin;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSuspendFinish *) {
return IrInstructionIdSuspendFinish;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAwaitSrc *) {
return IrInstructionIdAwaitSrc;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAwaitGen *) {
return IrInstructionIdAwaitGen;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionResume *) {
return IrInstructionIdResume;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSpillBegin *) {
return IrInstructionIdSpillBegin;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSpillEnd *) {
return IrInstructionIdSpillEnd;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorExtractElem *) {
return IrInstructionIdVectorExtractElem;
}
template<typename T>
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
const char *name = nullptr;
#ifdef ZIG_ENABLE_MEM_PROFILE
T *dummy = nullptr;
name = ir_instruction_type_str(ir_instruction_id(dummy));
#endif
T *special_instruction = allocate<T>(1, name);
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 = allocate<ZigValue>(1, "ZigValue");
return special_instruction;
}
template<typename T>
static T *ir_create_instruction_noval(IrBuilder *irb, Scope *scope, AstNode *source_node) {
const char *name = nullptr;
#ifdef ZIG_ENABLE_MEM_PROFILE
T *dummy = nullptr;
name = ir_instruction_type_str(ir_instruction_id(dummy));
#endif
T *special_instruction = allocate<T>(1, name);
special_instruction->base.id = ir_instruction_id(special_instruction);
special_instruction->base.scope = scope;
special_instruction->base.source_node = source_node;
special_instruction->base.debug_id = exec_next_debug_id(irb->exec);
special_instruction->base.owner_bb = irb->current_basic_block;
return special_instruction;
}
template<typename T>
static T *ir_build_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
T *special_instruction = ir_create_instruction<T>(irb, scope, source_node);
ir_instruction_append(irb->current_basic_block, &special_instruction->base);
return special_instruction;
}
static IrInstruction *ir_build_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, 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 *operand)
{
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->operand = operand;
if (operand != nullptr) ir_ref_instruction(operand, 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_create_instruction_noval<IrInstructionConst>(irb, scope, source_node);
ir_instruction_append(irb->current_basic_block, &const_instruction->base);
const_instruction->base.value = irb->codegen->intern.for_void();
return &const_instruction->base;
}
static IrInstruction *ir_build_const_undefined(IrBuilder *irb, Scope *scope, AstNode *source_node) {
IrInstructionConst *const_instruction = ir_create_instruction_noval<IrInstructionConst>(irb, scope, source_node);
ir_instruction_append(irb->current_basic_block, &const_instruction->base);
const_instruction->base.value = irb->codegen->intern.for_undefined();
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_create_instruction_noval<IrInstructionConst>(irb, scope, source_node);
ir_instruction_append(irb->current_basic_block, &const_instruction->base);
const_instruction->base.value = irb->codegen->intern.for_null();
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_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_bin_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrBinOp op_id,
IrInstruction *op1, IrInstruction *op2, bool safety_check_on)
{
IrInstructionBinOp *bin_op_instruction = ir_build_instruction<IrInstructionBinOp>(irb, scope, source_node);
bin_op_instruction->op_id = op_id;
bin_op_instruction->op1 = op1;
bin_op_instruction->op2 = op2;
bin_op_instruction->safety_check_on = safety_check_on;
ir_ref_instruction(op1, irb->current_basic_block);
ir_ref_instruction(op2, irb->current_basic_block);
return &bin_op_instruction->base;
}
static IrInstruction *ir_build_bin_op_gen(IrAnalyze *ira, IrInstruction *source_instr, ZigType *res_type,
IrBinOp op_id, IrInstruction *op1, IrInstruction *op2, bool safety_check_on)
{
IrInstructionBinOp *bin_op_instruction = ir_build_instruction<IrInstructionBinOp>(&ira->new_irb,
source_instr->scope, source_instr->source_node);
bin_op_instruction->base.value->type = res_type;
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, ira->new_irb.current_basic_block);
ir_ref_instruction(op2, ira->new_irb.current_basic_block);
return &bin_op_instruction->base;
}
static IrInstruction *ir_build_merge_err_sets(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *op1, IrInstruction *op2, Buf *type_name)
{
IrInstructionMergeErrSets *merge_err_sets_instruction = ir_build_instruction<IrInstructionMergeErrSets>(irb, scope, source_node);
merge_err_sets_instruction->op1 = op1;
merge_err_sets_instruction->op2 = op2;
merge_err_sets_instruction->type_name = type_name;
ir_ref_instruction(op1, irb->current_basic_block);
ir_ref_instruction(op2, irb->current_basic_block);
return &merge_err_sets_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_return_ptr(IrAnalyze *ira, Scope *scope, AstNode *source_node,
ZigType *ty)
{
IrInstructionReturnPtr *instruction = ir_build_instruction<IrInstructionReturnPtr>(&ira->new_irb,
scope, source_node);
instruction->base.value->type = ty;
return &instruction->base;
}
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,
AstNode *init_array_type_source_node)
{
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;
instruction->init_array_type_source_node = init_array_type_source_node;
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, bool initializing)
{
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;
instruction->initializing = initializing;
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, bool initializing)
{
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;
instruction->initializing = initializing;
ir_ref_instruction(container_ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_has_field(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *container_type, IrInstruction *field_name)
{
IrInstructionHasField *instruction = ir_build_instruction<IrInstructionHasField>(irb, scope, source_node);
instruction->container_type = container_type;
instruction->field_name = field_name;
ir_ref_instruction(container_type, irb->current_basic_block);
ir_ref_instruction(field_name, 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, bool safety_check_on, bool initializing)
{
IrInstructionUnionFieldPtr *instruction = ir_build_instruction<IrInstructionUnionFieldPtr>(irb, scope, source_node);
instruction->initializing = initializing;
instruction->safety_check_on = safety_check_on;
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_extra(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *options, IrInstruction *fn_ref, IrInstruction *args, ResultLoc *result_loc)
{
IrInstructionCallExtra *call_instruction = ir_build_instruction<IrInstructionCallExtra>(irb, scope, source_node);
call_instruction->options = options;
call_instruction->fn_ref = fn_ref;
call_instruction->args = args;
call_instruction->result_loc = result_loc;
ir_ref_instruction(options, irb->current_basic_block);
ir_ref_instruction(fn_ref, irb->current_basic_block);
ir_ref_instruction(args, irb->current_basic_block);
return &call_instruction->base;
}
static IrInstruction *ir_build_call_src_args(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *options, IrInstruction *fn_ref, IrInstruction **args_ptr, size_t args_len,
ResultLoc *result_loc)
{
IrInstructionCallSrcArgs *call_instruction = ir_build_instruction<IrInstructionCallSrcArgs>(irb, scope, source_node);
call_instruction->options = options;
call_instruction->fn_ref = fn_ref;
call_instruction->args_ptr = args_ptr;
call_instruction->args_len = args_len;
call_instruction->result_loc = result_loc;
ir_ref_instruction(options, irb->current_basic_block);
ir_ref_instruction(fn_ref, irb->current_basic_block);
for (size_t i = 0; i < args_len; i += 1)
ir_ref_instruction(args_ptr[i], irb->current_basic_block);
return &call_instruction->base;
}
static IrInstruction *ir_build_call_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
ZigFn *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,
IrInstruction *ret_ptr, CallModifier modifier, bool is_async_call_builtin,
IrInstruction *new_stack, ResultLoc *result_loc)
{
IrInstructionCallSrc *call_instruction = ir_build_instruction<IrInstructionCallSrc>(irb, scope, source_node);
call_instruction->fn_entry = fn_entry;
call_instruction->fn_ref = fn_ref;
call_instruction->args = args;
call_instruction->arg_count = arg_count;
call_instruction->modifier = modifier;
call_instruction->is_async_call_builtin = is_async_call_builtin;
call_instruction->new_stack = new_stack;
call_instruction->result_loc = result_loc;
call_instruction->ret_ptr = ret_ptr;
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 (ret_ptr != nullptr) ir_ref_instruction(ret_ptr, irb->current_basic_block);
if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block);
return &call_instruction->base;
}
static IrInstructionCallGen *ir_build_call_gen(IrAnalyze *ira, IrInstruction *source_instruction,
ZigFn *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,
CallModifier modifier, IrInstruction *new_stack, bool is_async_call_builtin,
IrInstruction *result_loc, ZigType *return_type)
{
IrInstructionCallGen *call_instruction = ir_build_instruction<IrInstructionCallGen>(&ira->new_irb,
source_instruction->scope, source_instruction->source_node);
call_instruction->base.value->type = return_type;
call_instruction->fn_entry = fn_entry;
call_instruction->fn_ref = fn_ref;
call_instruction->args = args;
call_instruction->arg_count = arg_count;
call_instruction->modifier = modifier;
call_instruction->is_async_call_builtin = is_async_call_builtin;
call_instruction->new_stack = new_stack;
call_instruction->result_loc = result_loc;
if (fn_ref != nullptr) ir_ref_instruction(fn_ref, ira->new_irb.current_basic_block);
for (size_t i = 0; i < arg_count; i += 1)
ir_ref_instruction(args[i], ira->new_irb.current_basic_block);
if (new_stack != nullptr) ir_ref_instruction(new_stack, ira->new_irb.current_basic_block);
if (result_loc != nullptr) ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
return call_instruction;
}
static IrInstruction *ir_build_phi(IrBuilder *irb, Scope *scope, AstNode *source_node,
size_t incoming_count, IrBasicBlock **incoming_blocks, IrInstruction **incoming_values,
ResultLocPeerParent *peer_parent)
{
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;
phi_instruction->peer_parent = peer_parent;
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 *sentinel, 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->sentinel = sentinel;
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 (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block);
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, ResultLoc *result_loc)
{
IrInstructionUnOp *instruction = ir_build_instruction<IrInstructionUnOp>(irb, scope, source_node);
instruction->op_id = op_id;
instruction->value = value;
instruction->lval = lval;
instruction->result_loc = result_loc;
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, nullptr);
}
static IrInstruction *ir_build_container_init_list(IrBuilder *irb, Scope *scope, AstNode *source_node,
size_t item_count, IrInstruction **elem_result_loc_list, IrInstruction *result_loc,
AstNode *init_array_type_source_node)
{
IrInstructionContainerInitList *container_init_list_instruction =
ir_build_instruction<IrInstructionContainerInitList>(irb, scope, source_node);
container_init_list_instruction->item_count = item_count;
container_init_list_instruction->elem_result_loc_list = elem_result_loc_list;
container_init_list_instruction->result_loc = result_loc;
container_init_list_instruction->init_array_type_source_node = init_array_type_source_node;
for (size_t i = 0; i < item_count; i += 1) {
ir_ref_instruction(elem_result_loc_list[i], irb->current_basic_block);
}
if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block);
return &container_init_list_instruction->base;
}
static IrInstruction *ir_build_container_init_fields(IrBuilder *irb, Scope *scope, AstNode *source_node,
size_t field_count, IrInstructionContainerInitFieldsField *fields, IrInstruction *result_loc)
{
IrInstructionContainerInitFields *container_init_fields_instruction =
ir_build_instruction<IrInstructionContainerInitFields>(irb, scope, source_node);
container_init_fields_instruction->field_count = field_count;
container_init_fields_instruction->fields = fields;
container_init_fields_instruction->result_loc = result_loc;
for (size_t i = 0; i < field_count; i += 1) {
ir_ref_instruction(fields[i].result_loc, irb->current_basic_block);
}
if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block);
return &container_init_fields_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 IrInstructionStorePtr *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;
}
static IrInstruction *ir_build_vector_store_elem(IrAnalyze *ira, IrInstruction *source_instruction,
IrInstruction *vector_ptr, IrInstruction *index, IrInstruction *value)
{
IrInstructionVectorStoreElem *inst = ir_build_instruction<IrInstructionVectorStoreElem>(
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
inst->base.value->type = ira->codegen->builtin_types.entry_void;
inst->vector_ptr = vector_ptr;
inst->index = index;
inst->value = value;
ir_ref_instruction(vector_ptr, ira->new_irb.current_basic_block);
ir_ref_instruction(index, ira->new_irb.current_basic_block);
ir_ref_instruction(value, ira->new_irb.current_basic_block);
return &inst->base;
}
static IrInstruction *ir_build_var_decl_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
ZigVar *var, IrInstruction *align_value, IrInstruction *ptr)
{
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->align_value = align_value;
decl_var_instruction->ptr = ptr;
if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
ir_ref_instruction(ptr, irb->current_basic_block);
return &decl_var_instruction->base;
}
static IrInstruction *ir_build_var_decl_gen(IrAnalyze *ira, IrInstruction *source_instruction,
ZigVar *var, IrInstruction *var_ptr)
{
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->var_ptr = var_ptr;
ir_ref_instruction(var_ptr, 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, IrInstruction *result_loc)
{
IrInstructionResizeSlice *instruction = ir_build_instruction<IrInstructionResizeSlice>(&ira->new_irb,
source_instruction->scope, source_instruction->source_node);
instruction->base.value->type = ty;
instruction->operand = operand;
instruction->result_loc = result_loc;
ir_ref_instruction(operand, ira->new_irb.current_basic_block);
if (result_loc != nullptr) ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_export(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *target, IrInstruction *options)
{
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->target = target;
export_instruction->options = options;
ir_ref_instruction(target, irb->current_basic_block);
ir_ref_instruction(options, 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_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 *sentinel, IrInstruction *child_type)
{
IrInstructionArrayType *instruction = ir_build_instruction<IrInstructionArrayType>(irb, scope, source_node);
instruction->size = size;
instruction->sentinel = sentinel;
instruction->child_type = child_type;
ir_ref_instruction(size, irb->current_basic_block);
if (sentinel != nullptr) ir_ref_instruction(sentinel, irb->current_basic_block);
ir_ref_instruction(child_type, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_anyframe_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *payload_type)
{
IrInstructionAnyFrameType *instruction = ir_build_instruction<IrInstructionAnyFrameType>(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 *sentinel, 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->sentinel = sentinel;
instruction->align_value = align_value;
instruction->is_allow_zero = is_allow_zero;
if (sentinel != nullptr) ir_ref_instruction(sentinel, irb->current_basic_block);
if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
ir_ref_instruction(child_type, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_asm_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *asm_template, IrInstruction **input_list, IrInstruction **output_types,
ZigVar **output_vars, size_t return_count, bool has_side_effects, bool is_global)
{
IrInstructionAsmSrc *instruction = ir_build_instruction<IrInstructionAsmSrc>(irb, scope, source_node);
instruction->asm_template = asm_template;
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;
instruction->is_global = is_global;
assert(source_node->type == NodeTypeAsmExpr);
for (size_t i = 0; i < source_node->data.asm_expr.output_list.length; i += 1) {
IrInstruction *output_type = output_types[i];
if (output_type) ir_ref_instruction(output_type, irb->current_basic_block);
}
for (size_t i = 0; i < source_node->data.asm_expr.input_list.length; i += 1) {
IrInstruction *input_value = input_list[i];
ir_ref_instruction(input_value, irb->current_basic_block);
}
return &instruction->base;
}
static IrInstruction *ir_build_asm_gen(IrAnalyze *ira, 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)
{
IrInstructionAsmGen *instruction = ir_build_instruction<IrInstructionAsmGen>(&ira->new_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, ira->new_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, ira->new_irb.current_basic_block);
}
return &instruction->base;
}
static IrInstruction *ir_build_size_of(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value, bool bit_size) {
IrInstructionSizeOf *instruction = ir_build_instruction<IrInstructionSizeOf>(irb, scope, source_node);
instruction->type_value = type_value;
instruction->bit_size = bit_size;
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, bool initializing)
{
IrInstructionOptionalUnwrapPtr *instruction = ir_build_instruction<IrInstructionOptionalUnwrapPtr>(irb, scope, source_node);
instruction->base_ptr = base_ptr;
instruction->safety_check_on = safety_check_on;
instruction->initializing = initializing;
ir_ref_instruction(base_ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_optional_wrap(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *result_ty,
IrInstruction *operand, IrInstruction *result_loc)
{
IrInstructionOptionalWrap *instruction = ir_build_instruction<IrInstructionOptionalWrap>(
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
instruction->base.value->type = result_ty;
instruction->operand = operand;
instruction->result_loc = result_loc;
ir_ref_instruction(operand, ira->new_irb.current_basic_block);
if (result_loc != nullptr) ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_err_wrap_payload(IrAnalyze *ira, IrInstruction *source_instruction,
ZigType *result_type, IrInstruction *operand, IrInstruction *result_loc)
{
IrInstructionErrWrapPayload *instruction = ir_build_instruction<IrInstructionErrWrapPayload>(
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
instruction->base.value->type = result_type;
instruction->operand = operand;
instruction->result_loc = result_loc;
ir_ref_instruction(operand, ira->new_irb.current_basic_block);
if (result_loc != nullptr) ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_err_wrap_code(IrAnalyze *ira, IrInstruction *source_instruction,
ZigType *result_type, IrInstruction *operand, IrInstruction *result_loc)
{
IrInstructionErrWrapCode *instruction = ir_build_instruction<IrInstructionErrWrapCode>(
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
instruction->base.value->type = result_type;
instruction->operand = operand;
instruction->result_loc = result_loc;
ir_ref_instruction(operand, ira->new_irb.current_basic_block);
if (result_loc != nullptr) ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_clz(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) {
IrInstructionClz *instruction = ir_build_instruction<IrInstructionClz>(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_ctz(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) {
IrInstructionCtz *instruction = ir_build_instruction<IrInstructionCtz>(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_pop_count(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) {
IrInstructionPopCount *instruction = ir_build_instruction<IrInstructionPopCount>(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 IrInstructionSwitchBr *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;
}
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 **prongs_ptr, size_t prongs_len)
{
IrInstructionSwitchVar *instruction = ir_build_instruction<IrInstructionSwitchVar>(irb, scope, source_node);
instruction->target_value_ptr = target_value_ptr;
instruction->prongs_ptr = prongs_ptr;
instruction->prongs_len = prongs_len;
ir_ref_instruction(target_value_ptr, irb->current_basic_block);
for (size_t i = 0; i < prongs_len; i += 1) {
ir_ref_instruction(prongs_ptr[i], irb->current_basic_block);
}
return &instruction->base;
}
// For this instruction the switch_br must be set later.
static IrInstructionSwitchElseVar *ir_build_switch_else_var(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *target_value_ptr)
{
IrInstructionSwitchElseVar *instruction = ir_build_instruction<IrInstructionSwitchElseVar>(irb, scope, source_node);
instruction->target_value_ptr = target_value_ptr;
ir_ref_instruction(target_value_ptr, irb->current_basic_block);
return instruction;
}
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_ref_gen(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *result_type,
IrInstruction *operand, IrInstruction *result_loc)
{
IrInstructionRefGen *instruction = ir_build_instruction<IrInstructionRefGen>(&ira->new_irb,
source_instruction->scope, source_instruction->source_node);
instruction->base.value->type = result_type;
instruction->operand = operand;
instruction->result_loc = result_loc;
ir_ref_instruction(operand, ira->new_irb.current_basic_block);
if (result_loc != nullptr) ir_ref_instruction(result_loc, ira->new_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, ResultLoc *result_loc)
{
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;
instruction->result_loc = result_loc;
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, ZigType *result_type,
IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value,
AtomicOrder success_order, AtomicOrder failure_order, bool is_weak, IrInstruction *result_loc)
{
IrInstructionCmpxchgGen *instruction = ir_build_instruction<IrInstructionCmpxchgGen>(&ira->new_irb,
source_instruction->scope, source_instruction->source_node);
instruction->base.value->type = result_type;
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;
instruction->result_loc = result_loc;
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);
if (result_loc != nullptr) ir_ref_instruction(result_loc, 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,
ResultLoc *result_loc)
{
IrInstructionToBytes *instruction = ir_build_instruction<IrInstructionToBytes>(irb, scope, source_node);
instruction->target = target;
instruction->result_loc = result_loc;
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, ResultLoc *result_loc)
{
IrInstructionFromBytes *instruction = ir_build_instruction<IrInstructionFromBytes>(irb, scope, source_node);
instruction->dest_child_type = dest_child_type;
instruction->target = target;
instruction->result_loc = result_loc;
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_shuffle_vector(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *scalar_type, IrInstruction *a, IrInstruction *b, IrInstruction *mask)
{
IrInstructionShuffleVector *instruction = ir_build_instruction<IrInstructionShuffleVector>(irb, scope, source_node);
instruction->scalar_type = scalar_type;
instruction->a = a;
instruction->b = b;
instruction->mask = mask;
if (scalar_type != nullptr) {
ir_ref_instruction(scalar_type, irb->current_basic_block);
}
ir_ref_instruction(a, irb->current_basic_block);
ir_ref_instruction(b, irb->current_basic_block);
ir_ref_instruction(mask, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_splat_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *len, IrInstruction *scalar)
{
IrInstructionSplatSrc *instruction = ir_build_instruction<IrInstructionSplatSrc>(irb, scope, source_node);
instruction->len = len;
instruction->scalar = scalar;
ir_ref_instruction(len, irb->current_basic_block);
ir_ref_instruction(scalar, 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_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *ptr, IrInstruction *start, IrInstruction *end, IrInstruction *sentinel,
bool safety_check_on, ResultLoc *result_loc)
{
IrInstructionSliceSrc *instruction = ir_build_instruction<IrInstructionSliceSrc>(irb, scope, source_node);
instruction->ptr = ptr;
instruction->start = start;
instruction->end = end;
instruction->sentinel = sentinel;
instruction->safety_check_on = safety_check_on;
instruction->result_loc = result_loc;
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);
if (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_splat_gen(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *result_type,
IrInstruction *scalar)
{
IrInstructionSplatGen *instruction = ir_build_instruction<IrInstructionSplatGen>(
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
instruction->base.value->type = result_type;
instruction->scalar = scalar;
ir_ref_instruction(scalar, ira->new_irb.current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_slice_gen(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *slice_type,
IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool safety_check_on, IrInstruction *result_loc)
{
IrInstructionSliceGen *instruction = ir_build_instruction<IrInstructionSliceGen>(
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
instruction->base.value->type = slice_type;
instruction->ptr = ptr;
instruction->start = start;
instruction->end = end;
instruction->safety_check_on = safety_check_on;
instruction->result_loc = result_loc;
ir_ref_instruction(ptr, ira->new_irb.current_basic_block);
ir_ref_instruction(start, ira->new_irb.current_basic_block);
if (end) ir_ref_instruction(end, ira->new_irb.current_basic_block);
ir_ref_instruction(result_loc, ira->new_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) {
IrInstructionFrameHandle *instruction = ir_build_instruction<IrInstructionFrameHandle>(irb, scope, source_node);
return &instruction->base;
}
static IrInstruction *ir_build_frame_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *fn) {
IrInstructionFrameType *instruction = ir_build_instruction<IrInstructionFrameType>(irb, scope, source_node);
instruction->fn = fn;
ir_ref_instruction(fn, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_frame_size_src(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *fn) {
IrInstructionFrameSizeSrc *instruction = ir_build_instruction<IrInstructionFrameSizeSrc>(irb, scope, source_node);
instruction->fn = fn;
ir_ref_instruction(fn, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_frame_size_gen(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *fn)
{
IrInstructionFrameSizeGen *instruction = ir_build_instruction<IrInstructionFrameSizeGen>(irb, scope, source_node);
instruction->fn = fn;
ir_ref_instruction(fn, irb->current_basic_block);
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;
}
//TODO Powi, Pow, minnum, maxnum, maximum, minimum, copysign,
// lround, llround, lrint, llrint
// So far this is only non-complicated type functions.
const char *float_op_to_name(BuiltinFnId op) {
switch (op) {
case BuiltinFnIdSqrt:
return "sqrt";
case BuiltinFnIdSin:
return "sin";
case BuiltinFnIdCos:
return "cos";
case BuiltinFnIdExp:
return "exp";
case BuiltinFnIdExp2:
return "exp2";
case BuiltinFnIdLog:
return "log";
case BuiltinFnIdLog10:
return "log10";
case BuiltinFnIdLog2:
return "log2";
case BuiltinFnIdFabs:
return "fabs";
case BuiltinFnIdFloor:
return "floor";
case BuiltinFnIdCeil:
return "ceil";
case BuiltinFnIdTrunc:
return "trunc";
case BuiltinFnIdNearbyInt:
return "nearbyint";
case BuiltinFnIdRound:
return "round";
default:
zig_unreachable();
}
}
static IrInstruction *ir_build_float_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *operand,
BuiltinFnId fn_id)
{
IrInstructionFloatOp *instruction = ir_build_instruction<IrInstructionFloatOp>(irb, scope, source_node);
instruction->operand = operand;
instruction->fn_id = fn_id;
ir_ref_instruction(operand, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_mul_add(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2, IrInstruction *op3) {
IrInstructionMulAdd *instruction = ir_build_instruction<IrInstructionMulAdd>(irb, scope, source_node);
instruction->type_value = type_value;
instruction->op1 = op1;
instruction->op2 = op2;
instruction->op3 = op3;
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(op3, 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_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *base_ptr, bool resolve_err_set, bool base_ptr_is_payload)
{
IrInstructionTestErrSrc *instruction = ir_build_instruction<IrInstructionTestErrSrc>(irb, scope, source_node);
instruction->base_ptr = base_ptr;
instruction->resolve_err_set = resolve_err_set;
instruction->base_ptr_is_payload = base_ptr_is_payload;
ir_ref_instruction(base_ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_test_err_gen(IrAnalyze *ira, IrInstruction *source_instruction,
IrInstruction *err_union)
{
IrInstructionTestErrGen *instruction = ir_build_instruction<IrInstructionTestErrGen>(
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
instruction->base.value->type = ira->codegen->builtin_types.entry_bool;
instruction->err_union = err_union;
ir_ref_instruction(err_union, ira->new_irb.current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_unwrap_err_code(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *err_union_ptr)
{
IrInstructionUnwrapErrCode *instruction = ir_build_instruction<IrInstructionUnwrapErrCode>(irb, scope, source_node);
instruction->err_union_ptr = err_union_ptr;
ir_ref_instruction(err_union_ptr, 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, bool initializing)
{
IrInstructionUnwrapErrPayload *instruction = ir_build_instruction<IrInstructionUnwrapErrPayload>(irb, scope, source_node);
instruction->value = value;
instruction->safety_check_on = safety_check_on;
instruction->initializing = initializing;
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 *callconv_value,
IrInstruction *return_type, 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->callconv_value = callconv_value;
instruction->return_type = return_type;
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 (callconv_value != nullptr) ir_ref_instruction(callconv_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, IrInstruction *result_loc)
{
IrInstructionLoadPtrGen *instruction = ir_build_instruction<IrInstructionLoadPtrGen>(
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
instruction->base.value->type = ty;
instruction->ptr = ptr;
instruction->result_loc = result_loc;
ir_ref_instruction(ptr, ira->new_irb.current_basic_block);
if (result_loc != nullptr) ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_implicit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *operand, ResultLocCast *result_loc_cast)
{
IrInstructionImplicitCast *instruction = ir_build_instruction<IrInstructionImplicitCast>(irb, scope, source_node);
instruction->operand = operand;
instruction->result_loc_cast = result_loc_cast;
ir_ref_instruction(operand, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_bit_cast_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *operand, ResultLocBitCast *result_loc_bit_cast)
{
IrInstructionBitCastSrc *instruction = ir_build_instruction<IrInstructionBitCastSrc>(irb, scope, source_node);
instruction->operand = operand;
instruction->result_loc_bit_cast = result_loc_bit_cast;
ir_ref_instruction(operand, 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(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_info) {
IrInstructionType *instruction = ir_build_instruction<IrInstructionType>(irb, scope, source_node);
instruction->type_info = type_info;
ir_ref_instruction(type_info, 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_resolve_result(IrBuilder *irb, Scope *scope, AstNode *source_node,
ResultLoc *result_loc, IrInstruction *ty)
{
IrInstructionResolveResult *instruction = ir_build_instruction<IrInstructionResolveResult>(irb, scope, source_node);
instruction->result_loc = result_loc;
instruction->ty = ty;
if (ty != nullptr) ir_ref_instruction(ty, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_reset_result(IrBuilder *irb, Scope *scope, AstNode *source_node,
ResultLoc *result_loc)
{
IrInstructionResetResult *instruction = ir_build_instruction<IrInstructionResetResult>(irb, scope, source_node);
instruction->result_loc = result_loc;
instruction->base.is_gen = true;
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, bool allow_var)
{
IrInstructionArgType *instruction = ir_build_instruction<IrInstructionArgType>(irb, scope, source_node);
instruction->fn_type = fn_type;
instruction->arg_index = arg_index;
instruction->allow_var = allow_var;
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_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_atomic_store(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *operand_type, IrInstruction *ptr, IrInstruction *value,
IrInstruction *ordering, AtomicOrder resolved_ordering)
{
IrInstructionAtomicStore *instruction = ir_build_instruction<IrInstructionAtomicStore>(irb, scope, source_node);
instruction->operand_type = operand_type;
instruction->ptr = ptr;
instruction->value = value;
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);
ir_ref_instruction(value, irb->current_basic_block);
if (ordering != nullptr) ir_ref_instruction(ordering, 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, ResultLocReturn *result_loc_ret)
{
IrInstructionAddImplicitReturnType *instruction = ir_build_instruction<IrInstructionAddImplicitReturnType>(irb, scope, source_node);
instruction->value = value;
instruction->result_loc_ret = result_loc_ret;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_has_decl(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *container, IrInstruction *name)
{
IrInstructionHasDecl *instruction = ir_build_instruction<IrInstructionHasDecl>(irb, scope, source_node);
instruction->container = container;
instruction->name = name;
ir_ref_instruction(container, irb->current_basic_block);
ir_ref_instruction(name, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_undeclared_identifier(IrBuilder *irb, Scope *scope, AstNode *source_node,
Buf *name)
{
IrInstructionUndeclaredIdent *instruction = ir_build_instruction<IrInstructionUndeclaredIdent>(irb, scope, source_node);
instruction->name = name;
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_union_init_named_field(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *union_type, IrInstruction *field_name, IrInstruction *field_result_loc, IrInstruction *result_loc)
{
IrInstructionUnionInitNamedField *instruction = ir_build_instruction<IrInstructionUnionInitNamedField>(irb, scope, source_node);
instruction->union_type = union_type;
instruction->field_name = field_name;
instruction->field_result_loc = field_result_loc;
instruction->result_loc = result_loc;
ir_ref_instruction(union_type, irb->current_basic_block);
ir_ref_instruction(field_name, irb->current_basic_block);
ir_ref_instruction(field_result_loc, irb->current_basic_block);
if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_vector_to_array(IrAnalyze *ira, IrInstruction *source_instruction,
ZigType *result_type, IrInstruction *vector, IrInstruction *result_loc)
{
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;
instruction->result_loc = result_loc;
ir_ref_instruction(vector, ira->new_irb.current_basic_block);
ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruction *source_instruction,
ZigType *result_type, IrInstruction *operand, IrInstruction *result_loc)
{
IrInstructionPtrOfArrayToSlice *instruction = ir_build_instruction<IrInstructionPtrOfArrayToSlice>(&ira->new_irb,
source_instruction->scope, source_instruction->source_node);
instruction->base.value->type = result_type;
instruction->operand = operand;
instruction->result_loc = result_loc;
ir_ref_instruction(operand, ira->new_irb.current_basic_block);
ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
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 IrInstruction *ir_build_assert_non_null(IrAnalyze *ira, IrInstruction *source_instruction,
IrInstruction *target)
{
IrInstructionAssertNonNull *instruction = ir_build_instruction<IrInstructionAssertNonNull>(&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 IrInstruction *ir_build_alloca_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *align, const char *name_hint, IrInstruction *is_comptime)
{
IrInstructionAllocaSrc *instruction = ir_build_instruction<IrInstructionAllocaSrc>(irb, scope, source_node);
instruction->base.is_gen = true;
instruction->align = align;
instruction->name_hint = name_hint;
instruction->is_comptime = is_comptime;
if (align != nullptr) ir_ref_instruction(align, irb->current_basic_block);
if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block);
return &instruction->base;
}
static IrInstructionAllocaGen *ir_build_alloca_gen(IrAnalyze *ira, IrInstruction *source_instruction,
uint32_t align, const char *name_hint)
{
IrInstructionAllocaGen *instruction = ir_create_instruction<IrInstructionAllocaGen>(&ira->new_irb,
source_instruction->scope, source_instruction->source_node);
instruction->align = align;
instruction->name_hint = name_hint;
return instruction;
}
static IrInstruction *ir_build_end_expr(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *value, ResultLoc *result_loc)
{
IrInstructionEndExpr *instruction = ir_build_instruction<IrInstructionEndExpr>(irb, scope, source_node);
instruction->base.is_gen = true;
instruction->value = value;
instruction->result_loc = result_loc;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstructionSuspendBegin *ir_build_suspend_begin(IrBuilder *irb, Scope *scope, AstNode *source_node) {
IrInstructionSuspendBegin *instruction = ir_build_instruction<IrInstructionSuspendBegin>(irb, scope, source_node);
instruction->base.value->type = irb->codegen->builtin_types.entry_void;
return instruction;
}
static IrInstruction *ir_build_suspend_finish(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstructionSuspendBegin *begin)
{
IrInstructionSuspendFinish *instruction = ir_build_instruction<IrInstructionSuspendFinish>(irb, scope, source_node);
instruction->base.value->type = irb->codegen->builtin_types.entry_void;
instruction->begin = begin;
ir_ref_instruction(&begin->base, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_await_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *frame, ResultLoc *result_loc)
{
IrInstructionAwaitSrc *instruction = ir_build_instruction<IrInstructionAwaitSrc>(irb, scope, source_node);
instruction->frame = frame;
instruction->result_loc = result_loc;
ir_ref_instruction(frame, irb->current_basic_block);
return &instruction->base;
}
static IrInstructionAwaitGen *ir_build_await_gen(IrAnalyze *ira, IrInstruction *source_instruction,
IrInstruction *frame, ZigType *result_type, IrInstruction *result_loc)
{
IrInstructionAwaitGen *instruction = ir_build_instruction<IrInstructionAwaitGen>(&ira->new_irb,
source_instruction->scope, source_instruction->source_node);
instruction->base.value->type = result_type;
instruction->frame = frame;
instruction->result_loc = result_loc;
ir_ref_instruction(frame, ira->new_irb.current_basic_block);
if (result_loc != nullptr) ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
return instruction;
}
static IrInstruction *ir_build_resume(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *frame) {
IrInstructionResume *instruction = ir_build_instruction<IrInstructionResume>(irb, scope, source_node);
instruction->base.value->type = irb->codegen->builtin_types.entry_void;
instruction->frame = frame;
ir_ref_instruction(frame, irb->current_basic_block);
return &instruction->base;
}
static IrInstructionSpillBegin *ir_build_spill_begin(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *operand, SpillId spill_id)
{
IrInstructionSpillBegin *instruction = ir_build_instruction<IrInstructionSpillBegin>(irb, scope, source_node);
instruction->base.value->special = ConstValSpecialStatic;
instruction->base.value->type = irb->codegen->builtin_types.entry_void;
instruction->operand = operand;
instruction->spill_id = spill_id;
ir_ref_instruction(operand, irb->current_basic_block);
return instruction;
}
static IrInstruction *ir_build_spill_end(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstructionSpillBegin *begin)
{
IrInstructionSpillEnd *instruction = ir_build_instruction<IrInstructionSpillEnd>(irb, scope, source_node);
instruction->begin = begin;
ir_ref_instruction(&begin->base, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_vector_extract_elem(IrAnalyze *ira, IrInstruction *source_instruction,
IrInstruction *vector, IrInstruction *index)
{
IrInstructionVectorExtractElem *instruction = ir_build_instruction<IrInstructionVectorExtractElem>(
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
instruction->base.value->type = vector->value->type->data.vector.elem_type;
instruction->vector = vector;
instruction->index = index;
ir_ref_instruction(vector, ira->new_irb.current_basic_block);
ir_ref_instruction(index, 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:
case ScopeIdTypeOf:
case ScopeIdExpr:
scope = scope->parent;
continue;
case ScopeIdDeferExpr:
case ScopeIdCImport:
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:
case ScopeIdTypeOf:
case ScopeIdExpr:
scope = scope->parent;
continue;
case ScopeIdDeferExpr:
case ScopeIdCImport:
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) {
basic_block->index = irb->exec->basic_block_list.length;
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 IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) {
assert(node->type == NodeTypeReturnExpr);
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:
{
ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1, "ResultLocReturn");
result_loc_ret->base.id = ResultLocIdReturn;
ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
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_extra(irb, expr_node, scope, LValNone, &result_loc_ret->base);
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);
}
ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, return_value, result_loc_ret));
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) {
// only generate unconditional defers
ir_gen_defers_for_block(irb, scope, outer_scope, false);
IrInstruction *result = ir_build_return(irb, scope, node, return_value);
result_loc_ret->base.source_instruction = result;
return result;
}
bool should_inline = ir_should_inline(irb->exec, scope);
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_src(irb, scope, node, return_value, false, true);
IrInstruction *is_comptime;
if (should_inline) {
is_comptime = ir_build_const_bool(irb, scope, node, should_inline);
} 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);
IrInstruction *result = ir_build_return(irb, scope, node, return_value);
result_loc_ret->base.source_instruction = result;
return result;
}
case ReturnKindError:
{
assert(expr_node);
IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
if (err_union_ptr == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *is_err_val = ir_build_test_err_src(irb, scope, node, err_union_ptr, true, false);
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);
IrInstruction *err_val_ptr = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr);
IrInstruction *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr);
ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, err_val, nullptr));
IrInstructionSpillBegin *spill_begin = ir_build_spill_begin(irb, scope, node, err_val,
SpillIdRetErrCode);
ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1, "ResultLocReturn");
result_loc_ret->base.id = ResultLocIdReturn;
ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
ir_build_end_expr(irb, scope, node, err_val, &result_loc_ret->base);
if (!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);
}
err_val = ir_build_spill_end(irb, scope, node, spill_begin);
IrInstruction *ret_inst = ir_build_return(irb, scope, node, err_val);
result_loc_ret->base.source_instruction = ret_inst;
}
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, false);
if (lval == LValPtr)
return unwrapped_ptr;
else
return ir_expr_wrap(irb, scope, ir_build_load_ptr(irb, scope, node, unwrapped_ptr), result_loc);
}
}
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, "ZigVar");
variable_entry->parent_scope = parent_scope;
variable_entry->shadowable = is_shadowable;
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) {
variable_entry->name = strdup(buf_ptr(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
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->owner_exec = irb->exec;
}
assert(var->child_scope);
return var;
}
static ResultLocPeer *create_peer_result(ResultLocPeerParent *peer_parent) {
ResultLocPeer *result = allocate<ResultLocPeer>(1, "ResultLocPeer");
result->base.id = ResultLocIdPeer;
result->base.source_instruction = peer_parent->base.source_instruction;
result->parent = peer_parent;
result->base.allow_write_through_const = peer_parent->parent->allow_write_through_const;
return result;
}
static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node, LVal lval,
ResultLoc *result_loc)
{
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_lval_wrap(irb, parent_scope, ir_build_const_void(irb, child_scope, block_node), lval, result_loc);
}
if (block_node->data.block.name != nullptr) {
scope_block->lval = lval;
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));
scope_block->peer_parent = allocate<ResultLocPeerParent>(1, "ResultLocPeerParent");
scope_block->peer_parent->base.id = ResultLocIdPeerParent;
scope_block->peer_parent->base.source_instruction = scope_block->is_comptime;
scope_block->peer_parent->base.allow_write_through_const = result_loc->allow_write_through_const;
scope_block->peer_parent->end_bb = scope_block->end_block;
scope_block->peer_parent->is_comptime = scope_block->is_comptime;
scope_block->peer_parent->parent = result_loc;
ir_build_reset_result(irb, parent_scope, block_node, &scope_block->peer_parent->base);
}
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;
}
// This logic must be kept in sync with
// [STMT_EXPR_TEST_THING] <--- (search this token)
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;
}
if (scope_block->peer_parent != nullptr && scope_block->peer_parent->peers.length != 0) {
scope_block->peer_parent->peers.last()->next_bb = scope_block->end_block;
}
ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block);
IrInstruction *phi = ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length,
incoming_blocks.items, incoming_values.items, scope_block->peer_parent);
return ir_expr_wrap(irb, parent_scope, phi, result_loc);
} else {
incoming_blocks.append(irb->current_basic_block);
IrInstruction *else_expr_result = ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node));
if (scope_block->peer_parent != nullptr) {
ResultLocPeer *peer_result = create_peer_result(scope_block->peer_parent);
scope_block->peer_parent->peers.append(peer_result);
ir_build_end_expr(irb, parent_scope, block_node, else_expr_result, &peer_result->base);
if (scope_block->peer_parent->peers.length != 0) {
scope_block->peer_parent->peers.last()->next_bb = scope_block->end_block;
}
}
incoming_values.append(else_expr_result);
}
bool is_return_from_fn = block_node == irb->main_block_node;
if (!is_return_from_fn) {
ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false);
}
IrInstruction *result;
if (block_node->data.block.name != nullptr) {
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);
IrInstruction *phi = ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length,
incoming_blocks.items, incoming_values.items, scope_block->peer_parent);
result = ir_expr_wrap(irb, parent_scope, phi, result_loc);
} else {
IrInstruction *void_inst = ir_mark_gen(ir_build_const_void(irb, child_scope, block_node));
result = ir_lval_wrap(irb, parent_scope, void_inst, lval, result_loc);
}
if (!is_return_from_fn)
return result;
// no need for save_err_ret_addr because this cannot return error
// only generate unconditional defers
ir_mark_gen(ir_build_add_implicit_return_type(irb, child_scope, block_node, result, nullptr));
ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1, "ResultLocReturn");
result_loc_ret->base.id = ResultLocIdReturn;
ir_build_reset_result(irb, parent_scope, block_node, &result_loc_ret->base);
ir_mark_gen(ir_build_end_expr(irb, parent_scope, block_node, result, &result_loc_ret->base));
ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false);
return ir_mark_gen(ir_build_return(irb, child_scope, result->source_node, result));
}
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_merge_err_sets(IrBuilder *irb, Scope *scope, AstNode *node) {
IrInstruction *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope);
IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
if (op1 == irb->codegen->invalid_instruction || op2 == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
// TODO only pass type_name when the || operator is the top level AST node in the var decl expr
Buf bare_name = BUF_INIT;
Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error", scope, node, &bare_name);
return ir_build_merge_err_sets(irb, scope, node, op1, op2, type_name);
}
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, nullptr);
if (lvalue == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1, "ResultLocInstruction");
result_loc_inst->base.id = ResultLocIdInstruction;
result_loc_inst->base.source_instruction = lvalue;
ir_ref_instruction(lvalue, irb->current_basic_block);
ir_build_reset_result(irb, scope, node, &result_loc_inst->base);
IrInstruction *rvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op2, scope, LValNone,
&result_loc_inst->base);
if (rvalue == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
return ir_build_const_void(irb, scope, node);
}
static IrInstruction *ir_gen_assign_merge_err_sets(IrBuilder *irb, Scope *scope, AstNode *node) {
IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr, nullptr);
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_merge_err_sets(irb, scope, node, op1, op2, nullptr);
ir_build_store_ptr(irb, scope, node, lvalue, result);
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, nullptr);
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, "IrInstruction *");
incoming_values[0] = val1;
incoming_values[1] = val2;
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2, "IrBasicBlock *");
incoming_blocks[0] = post_val1_block;
incoming_blocks[1] = post_val2_block;
return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, nullptr);
}
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, "IrBasicBlock *");
incoming_blocks[0] = post_val1_block;
incoming_blocks[1] = post_val2_block;
return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, nullptr);
}
static ResultLocPeerParent *ir_build_result_peers(IrBuilder *irb, IrInstruction *cond_br_inst,
IrBasicBlock *end_block, ResultLoc *parent, IrInstruction *is_comptime)
{
ResultLocPeerParent *peer_parent = allocate<ResultLocPeerParent>(1);
peer_parent->base.id = ResultLocIdPeerParent;
peer_parent->base.source_instruction = cond_br_inst;
peer_parent->base.allow_write_through_const = parent->allow_write_through_const;
peer_parent->end_bb = end_block;
peer_parent->is_comptime = is_comptime;
peer_parent->parent = parent;
IrInstruction *popped_inst = irb->current_basic_block->instruction_list.pop();
ir_assert(popped_inst == cond_br_inst, cond_br_inst);
ir_build_reset_result(irb, cond_br_inst->scope, cond_br_inst->source_node, &peer_parent->base);
irb->current_basic_block->instruction_list.append(popped_inst);
return peer_parent;
}
static ResultLocPeerParent *ir_build_binary_result_peers(IrBuilder *irb, IrInstruction *cond_br_inst,
IrBasicBlock *else_block, IrBasicBlock *end_block, ResultLoc *parent, IrInstruction *is_comptime)
{
ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, parent, is_comptime);
peer_parent->peers.append(create_peer_result(peer_parent));
peer_parent->peers.last()->next_bb = else_block;
peer_parent->peers.append(create_peer_result(peer_parent));
peer_parent->peers.last()->next_bb = end_block;
return peer_parent;
}
static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
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, nullptr);
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");
IrInstruction *cond_br_inst = ir_build_cond_br(irb, parent_scope, node, is_non_null, ok_block, null_block, is_comptime);
ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, ok_block, end_block,
result_loc, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, null_block);
IrInstruction *null_result = ir_gen_node_extra(irb, op2_node, parent_scope, LValNone,
&peer_parent->peers.at(0)->base);
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, false);
IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr);
ir_build_end_expr(irb, parent_scope, node, unwrapped_payload, &peer_parent->peers.at(1)->base);
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, "IrBasicBlock *");
incoming_blocks[0] = after_null_block;
incoming_blocks[1] = after_ok_block;
IrInstruction *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent);
return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc);
}
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, LVal lval, ResultLoc *result_loc) {
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_lval_wrap(irb, scope, ir_gen_assign(irb, scope, node), lval, result_loc);
case BinOpTypeAssignTimes:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMult), lval, result_loc);
case BinOpTypeAssignTimesWrap:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap), lval, result_loc);
case BinOpTypeAssignDiv:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpDivUnspecified), lval, result_loc);
case BinOpTypeAssignMod:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpRemUnspecified), lval, result_loc);
case BinOpTypeAssignPlus:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpAdd), lval, result_loc);
case BinOpTypeAssignPlusWrap:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpAddWrap), lval, result_loc);
case BinOpTypeAssignMinus:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpSub), lval, result_loc);
case BinOpTypeAssignMinusWrap:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap), lval, result_loc);
case BinOpTypeAssignBitShiftLeft:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc);
case BinOpTypeAssignBitShiftRight:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
case BinOpTypeAssignBitAnd:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd), lval, result_loc);
case BinOpTypeAssignBitXor:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinXor), lval, result_loc);
case BinOpTypeAssignBitOr:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinOr), lval, result_loc);
case BinOpTypeAssignMergeErrorSets:
return ir_lval_wrap(irb, scope, ir_gen_assign_merge_err_sets(irb, scope, node), lval, result_loc);
case BinOpTypeBoolOr:
return ir_lval_wrap(irb, scope, ir_gen_bool_or(irb, scope, node), lval, result_loc);
case BinOpTypeBoolAnd:
return ir_lval_wrap(irb, scope, ir_gen_bool_and(irb, scope, node), lval, result_loc);
case BinOpTypeCmpEq:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpEq), lval, result_loc);
case BinOpTypeCmpNotEq:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpNotEq), lval, result_loc);
case BinOpTypeCmpLessThan:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessThan), lval, result_loc);
case BinOpTypeCmpGreaterThan:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterThan), lval, result_loc);
case BinOpTypeCmpLessOrEq:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessOrEq), lval, result_loc);
case BinOpTypeCmpGreaterOrEq:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterOrEq), lval, result_loc);
case BinOpTypeBinOr:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinOr), lval, result_loc);
case BinOpTypeBinXor:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinXor), lval, result_loc);
case BinOpTypeBinAnd:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd), lval, result_loc);
case BinOpTypeBitShiftLeft:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc);
case BinOpTypeBitShiftRight:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
case BinOpTypeAdd:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd), lval, result_loc);
case BinOpTypeAddWrap:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpAddWrap), lval, result_loc);
case BinOpTypeSub:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpSub), lval, result_loc);
case BinOpTypeSubWrap:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpSubWrap), lval, result_loc);
case BinOpTypeMult:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMult), lval, result_loc);
case BinOpTypeMultWrap:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap), lval, result_loc);
case BinOpTypeDiv:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpDivUnspecified), lval, result_loc);
case BinOpTypeMod:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpRemUnspecified), lval, result_loc);
case BinOpTypeArrayCat:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat), lval, result_loc);
case BinOpTypeArrayMult:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult), lval, result_loc);
case BinOpTypeMergeErrorSets:
return ir_lval_wrap(irb, scope, ir_gen_merge_err_sets(irb, scope, node), lval, result_loc);
case BinOpTypeUnwrapOptional:
return ir_gen_orelse(irb, scope, node, lval, result_loc);
case BinOpTypeErrorUnion:
return ir_lval_wrap(irb, scope, ir_gen_error_union(irb, scope, node), lval, result_loc);
}
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, ResultLoc *result_loc) {
Error err;
assert(node->type == NodeTypeSymbol);
Buf *variable_name = node->data.symbol_expr.symbol;
if (buf_eql_str(variable_name, "_")) {
if (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;
} else {
add_node_error(irb->codegen, node, buf_sprintf("`_` may only be used to assign things to"));
return irb->codegen->invalid_instruction;
}
}
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 ir_expr_wrap(irb, scope, value, result_loc);
}
}
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_expr_wrap(irb, scope, ir_build_load_ptr(irb, scope, node, var_ptr), result_loc);
}
}
Tld *tld = find_decl(irb->codegen, scope, variable_name);
if (tld) {
IrInstruction *decl_ref = ir_build_decl_ref(irb, scope, node, tld, lval);
if (lval == LValPtr) {
return decl_ref;
} else {
return ir_expr_wrap(irb, scope, decl_ref, result_loc);
}
}
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;
}
return ir_build_undeclared_identifier(irb, scope, node, variable_name);
}
static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
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, nullptr);
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, nullptr);
if (lval == LValPtr)
return ptr_instruction;
IrInstruction *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction);
return ir_expr_wrap(irb, scope, load_ptr, result_loc);
}
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, nullptr);
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, false);
}
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_mul_add(IrBuilder *irb, Scope *scope, AstNode *node) {
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 *op3_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 *op3 = ir_gen_node(irb, op3_node, scope);
if (op3 == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
return ir_build_mul_add(irb, scope, node, type_value, op1, op2, op3);
}
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_async_call(IrBuilder *irb, Scope *scope, AstNode *await_node, AstNode *call_node,
LVal lval, ResultLoc *result_loc)
{
size_t arg_offset = 3;
if (call_node->data.fn_call_expr.params.length < arg_offset) {
add_node_error(irb->codegen, call_node,
buf_sprintf("expected at least %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize,
arg_offset, call_node->data.fn_call_expr.params.length));
return irb->codegen->invalid_instruction;
}
AstNode *bytes_node = call_node->data.fn_call_expr.params.at(0);
IrInstruction *bytes = ir_gen_node(irb, bytes_node, scope);
if (bytes == irb->codegen->invalid_instruction)
return bytes;
AstNode *ret_ptr_node = call_node->data.fn_call_expr.params.at(1);
IrInstruction *ret_ptr = ir_gen_node(irb, ret_ptr_node, scope);
if (ret_ptr == irb->codegen->invalid_instruction)
return ret_ptr;
AstNode *fn_ref_node = call_node->data.fn_call_expr.params.at(2);
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 = call_node->data.fn_call_expr.params.length - arg_offset;
IrInstruction **args = allocate<IrInstruction*>(arg_count);
for (size_t i = 0; i < arg_count; i += 1) {
AstNode *arg_node = call_node->data.fn_call_expr.params.at(i + arg_offset);
IrInstruction *arg = ir_gen_node(irb, arg_node, scope);
if (arg == irb->codegen->invalid_instruction)
return arg;
args[i] = arg;
}
CallModifier modifier = (await_node == nullptr) ? CallModifierAsync : CallModifierNone;
bool is_async_call_builtin = true;
IrInstruction *call = ir_build_call_src(irb, scope, call_node, nullptr, fn_ref, arg_count, args,
ret_ptr, modifier, is_async_call_builtin, bytes, result_loc);
return ir_lval_wrap(irb, scope, call, lval, result_loc);
}
static IrInstruction *ir_gen_fn_call_with_args(IrBuilder *irb, Scope *scope, AstNode *source_node,
AstNode *fn_ref_node, CallModifier modifier, IrInstruction *options,
AstNode **args_ptr, size_t args_len, LVal lval, ResultLoc *result_loc)
{
IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
if (fn_ref == irb->codegen->invalid_instruction)
return fn_ref;
IrInstruction *fn_type = ir_build_typeof(irb, scope, source_node, fn_ref);
IrInstruction **args = allocate<IrInstruction*>(args_len);
for (size_t i = 0; i < args_len; i += 1) {
AstNode *arg_node = args_ptr[i];
IrInstruction *arg_index = ir_build_const_usize(irb, scope, arg_node, i);
IrInstruction *arg_type = ir_build_arg_type(irb, scope, source_node, fn_type, arg_index, true);
ResultLoc *no_result = no_result_loc();
ir_build_reset_result(irb, scope, source_node, no_result);
ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, arg_type, no_result);
IrInstruction *arg = ir_gen_node_extra(irb, arg_node, scope, LValNone, &result_loc_cast->base);
if (arg == irb->codegen->invalid_instruction)
return arg;
args[i] = ir_build_implicit_cast(irb, scope, arg_node, arg, result_loc_cast);
}
IrInstruction *fn_call;
if (options != nullptr) {
fn_call = ir_build_call_src_args(irb, scope, source_node, options, fn_ref, args, args_len, result_loc);
} else {
fn_call = ir_build_call_src(irb, scope, source_node, nullptr, fn_ref, args_len, args, nullptr,
modifier, false, nullptr, result_loc);
}
return ir_lval_wrap(irb, scope, fn_call, lval, result_loc);
}
static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
assert(node->type == NodeTypeFnCallExpr);
AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
Buf *name = fn_ref_expr->data.symbol_expr.symbol;
auto entry = irb->codegen->builtin_fn_table.maybe_get(name);
if (!entry) {
add_node_error(irb->codegen, node,
buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));
return irb->codegen->invalid_instruction;
}
BuiltinFnEntry *builtin_fn = entry->value;
size_t actual_param_count = node->data.fn_call_expr.params.length;
if (builtin_fn->param_count != 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;
}
switch (builtin_fn->id) {
case BuiltinFnIdInvalid:
zig_unreachable();
case BuiltinFnIdTypeof:
{
Scope *sub_scope = create_typeof_scope(irb->codegen, node, scope);
AstNode *arg_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg = ir_gen_node(irb, arg_node, sub_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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
case BuiltinFnIdSizeof:
case BuiltinFnIdBitSizeof:
{
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, builtin_fn->id == BuiltinFnIdBitSizeof);
return ir_lval_wrap(irb, scope, size_of, lval, result_loc);
}
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, result_loc);
}
case BuiltinFnIdCImport:
{
IrInstruction *c_import = ir_build_c_import(irb, scope, node);
return ir_lval_wrap(irb, scope, c_import, lval, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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),
result_loc);
return ir_lval_wrap(irb, scope, cmpxchg, lval, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
case BuiltinFnIdSqrt:
case BuiltinFnIdSin:
case BuiltinFnIdCos:
case BuiltinFnIdExp:
case BuiltinFnIdExp2:
case BuiltinFnIdLog:
case BuiltinFnIdLog2:
case BuiltinFnIdLog10:
case BuiltinFnIdFabs:
case BuiltinFnIdFloor:
case BuiltinFnIdCeil:
case BuiltinFnIdTrunc:
case BuiltinFnIdNearbyInt:
case BuiltinFnIdRound:
{
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 *inst = ir_build_float_op(irb, scope, node, arg0_value, builtin_fn->id);
return ir_lval_wrap(irb, scope, inst, lval, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
return ir_lval_wrap(irb, scope, result, lval, result_loc);
}
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, result_loc);
return ir_lval_wrap(irb, scope, result, lval, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
case BuiltinFnIdShuffle:
{
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;
IrInstruction *shuffle_vector = ir_build_shuffle_vector(irb, scope, node,
arg0_value, arg1_value, arg2_value, arg3_value);
return ir_lval_wrap(irb, scope, shuffle_vector, lval, result_loc);
}
case BuiltinFnIdSplat:
{
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 *splat = ir_build_splat_src(irb, scope, node,
arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, splat, lval, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, nullptr);
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, false);
if (lval == LValPtr)
return ptr_instruction;
IrInstruction *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction);
return ir_expr_wrap(irb, scope, load_ptr, result_loc);
}
case BuiltinFnIdHasField:
{
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 *type_info = ir_build_has_field(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, type_info, lval, result_loc);
}
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, result_loc);
}
case BuiltinFnIdType:
{
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 = ir_build_type(irb, scope, node, arg);
return ir_lval_wrap(irb, scope, type, lval, result_loc);
}
case BuiltinFnIdBreakpoint:
return ir_lval_wrap(irb, scope, ir_build_breakpoint(irb, scope, node), lval, result_loc);
case BuiltinFnIdReturnAddress:
return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval, result_loc);
case BuiltinFnIdFrameAddress:
return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval, result_loc);
case BuiltinFnIdFrameHandle:
if (!irb->exec->fn_entry) {
add_node_error(irb->codegen, node, buf_sprintf("@frame() called outside of function definition"));
return irb->codegen->invalid_instruction;
}
return ir_lval_wrap(irb, scope, ir_build_handle(irb, scope, node), lval, result_loc);
case BuiltinFnIdFrameType: {
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 *frame_type = ir_build_frame_type(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, frame_type, lval, result_loc);
}
case BuiltinFnIdFrameSize: {
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 *frame_size = ir_build_frame_size_src(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, frame_size, lval, result_loc);
}
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, result_loc);
}
case BuiltinFnIdAddWithOverflow:
return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd), lval, result_loc);
case BuiltinFnIdSubWithOverflow:
return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub), lval, result_loc);
case BuiltinFnIdMulWithOverflow:
return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval, result_loc);
case BuiltinFnIdShlWithOverflow:
return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval, result_loc);
case BuiltinFnIdMulAdd:
return ir_lval_wrap(irb, scope, ir_gen_mul_add(irb, scope, node), lval, result_loc);
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
case BuiltinFnIdBitCast:
{
AstNode *dest_type_node = node->data.fn_call_expr.params.at(0);
IrInstruction *dest_type = ir_gen_node(irb, dest_type_node, scope);
if (dest_type == irb->codegen->invalid_instruction)
return dest_type;
ResultLocBitCast *result_loc_bit_cast = allocate<ResultLocBitCast>(1);
result_loc_bit_cast->base.id = ResultLocIdBitCast;
result_loc_bit_cast->base.source_instruction = dest_type;
ir_ref_instruction(dest_type, irb->current_basic_block);
result_loc_bit_cast->parent = result_loc;
ir_build_reset_result(irb, scope, node, &result_loc_bit_cast->base);
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstruction *arg1_value = ir_gen_node_extra(irb, arg1_node, scope, LValNone,
&result_loc_bit_cast->base);
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;
IrInstruction *bitcast = ir_build_bit_cast_src(irb, scope, arg1_node, arg1_value, result_loc_bit_cast);
return ir_lval_wrap(irb, scope, bitcast, lval, result_loc);
}
case BuiltinFnIdAs:
{
AstNode *dest_type_node = node->data.fn_call_expr.params.at(0);
IrInstruction *dest_type = ir_gen_node(irb, dest_type_node, scope);
if (dest_type == irb->codegen->invalid_instruction)
return dest_type;
ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, dest_type, result_loc);
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstruction *arg1_value = ir_gen_node_extra(irb, arg1_node, scope, LValNone,
&result_loc_cast->base);
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;
IrInstruction *result = ir_build_implicit_cast(irb, scope, node, arg1_value, result_loc_cast);
return ir_lval_wrap(irb, scope, result, lval, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
case BuiltinFnIdNewStackCall:
{
if (node->data.fn_call_expr.params.length < 2) {
add_node_error(irb->codegen, node,
buf_sprintf("expected at least 2 arguments, found %" ZIG_PRI_usize,
node->data.fn_call_expr.params.length));
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_src(irb, scope, node, nullptr, fn_ref, arg_count, args,
nullptr, CallModifierNone, false, new_stack, result_loc);
return ir_lval_wrap(irb, scope, call, lval, result_loc);
}
case BuiltinFnIdCall: {
// Cast the options parameter to the options type
ZigType *options_type = get_builtin_type(irb->codegen, "CallOptions");
IrInstruction *options_type_inst = ir_build_const_type(irb, scope, node, options_type);
ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc());
AstNode *options_node = node->data.fn_call_expr.params.at(0);
IrInstruction *options_inner = ir_gen_node_extra(irb, options_node, scope,
LValNone, &result_loc_cast->base);
if (options_inner == irb->codegen->invalid_instruction)
return options_inner;
IrInstruction *options = ir_build_implicit_cast(irb, scope, options_node, options_inner, result_loc_cast);
AstNode *fn_ref_node = node->data.fn_call_expr.params.at(1);
AstNode *args_node = node->data.fn_call_expr.params.at(2);
if (args_node->type == NodeTypeContainerInitExpr) {
if (args_node->data.container_init_expr.kind == ContainerInitKindArray ||
args_node->data.container_init_expr.entries.length == 0)
{
return ir_gen_fn_call_with_args(irb, scope, node,
fn_ref_node, CallModifierNone, options,
args_node->data.container_init_expr.entries.items,
args_node->data.container_init_expr.entries.length,
lval, result_loc);
} else {
exec_add_error_node(irb->codegen, irb->exec, args_node,
buf_sprintf("TODO: @call with anon struct literal"));
return irb->codegen->invalid_instruction;
}
} else {
IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
if (fn_ref == irb->codegen->invalid_instruction)
return fn_ref;
IrInstruction *args = ir_gen_node(irb, args_node, scope);
if (args == irb->codegen->invalid_instruction)
return args;
IrInstruction *call = ir_build_call_extra(irb, scope, node, options, fn_ref, args, result_loc);
return ir_lval_wrap(irb, scope, call, lval, result_loc);
}
}
case BuiltinFnIdAsyncCall:
return ir_gen_async_call(irb, scope, nullptr, node, lval, result_loc);
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
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, result_loc);
}
case BuiltinFnIdOpaqueType:
{
IrInstruction *opaque_type = ir_build_opaque_type(irb, scope, node);
return ir_lval_wrap(irb, scope, opaque_type, lval, result_loc);
}
case BuiltinFnIdThis:
{
IrInstruction *this_inst = ir_gen_this(irb, scope, node);
return ir_lval_wrap(irb, scope, this_inst, lval, result_loc);
}
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, result_loc);
}
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, false);
return ir_lval_wrap(irb, scope, arg_type, lval, result_loc);
}
case BuiltinFnIdExport:
{
// Cast the options parameter to the options type
ZigType *options_type = get_builtin_type(irb->codegen, "ExportOptions");
IrInstruction *options_type_inst = ir_build_const_type(irb, scope, node, options_type);
ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc());
AstNode *target_node = node->data.fn_call_expr.params.at(0);
IrInstruction *target_value = ir_gen_node(irb, target_node, scope);
if (target_value == irb->codegen->invalid_instruction)
return target_value;
AstNode *options_node = node->data.fn_call_expr.params.at(1);
IrInstruction *options_value = ir_gen_node_extra(irb, options_node,
scope, LValNone, &result_loc_cast->base);
if (options_value == irb->codegen->invalid_instruction)
return options_value;
IrInstruction *casted_options_value = ir_build_implicit_cast(
irb, scope, options_node, options_value, result_loc_cast);
IrInstruction *ir_export = ir_build_export(irb, scope, node, target_value, casted_options_value);
return ir_lval_wrap(irb, scope, ir_export, lval, result_loc);
}
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, result_loc);
}
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;
IrInstruction *inst = 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);
return ir_lval_wrap(irb, scope, inst, lval, result_loc);
}
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;
IrInstruction *inst = 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);
return ir_lval_wrap(irb, scope, inst, lval, result_loc);
}
case BuiltinFnIdAtomicStore:
{
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;
IrInstruction *inst = ir_build_atomic_store(irb, scope, node, arg0_value, arg1_value, arg2_value, arg3_value,
// this value does not mean anything since we passed non-null values for other arg
AtomicOrderMonotonic);
return ir_lval_wrap(irb, scope, inst, lval, result_loc);
}
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, result_loc);
}
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, result_loc);
}
case BuiltinFnIdCtz:
case BuiltinFnIdPopCount:
case BuiltinFnIdClz:
case BuiltinFnIdBswap:
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;
switch (builtin_fn->id) {
case BuiltinFnIdCtz:
result = ir_build_ctz(irb, scope, node, arg0_value, arg1_value);
break;
case BuiltinFnIdPopCount:
result = ir_build_pop_count(irb, scope, node, arg0_value, arg1_value);
break;
case BuiltinFnIdClz:
result = ir_build_clz(irb, scope, node, arg0_value, arg1_value);
break;
case BuiltinFnIdBswap:
result = ir_build_bswap(irb, scope, node, arg0_value, arg1_value);
break;
case BuiltinFnIdBitReverse:
result = ir_build_bit_reverse(irb, scope, node, arg0_value, arg1_value);
break;
default:
zig_unreachable();
}
return ir_lval_wrap(irb, scope, result, lval, result_loc);
}
case BuiltinFnIdHasDecl:
{
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 *has_decl = ir_build_has_decl(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, has_decl, lval, result_loc);
}
case BuiltinFnIdUnionInit:
{
AstNode *union_type_node = node->data.fn_call_expr.params.at(0);
IrInstruction *union_type_inst = ir_gen_node(irb, union_type_node, scope);
if (union_type_inst == irb->codegen->invalid_instruction)
return union_type_inst;
AstNode *name_node = node->data.fn_call_expr.params.at(1);
IrInstruction *name_inst = ir_gen_node(irb, name_node, scope);
if (name_inst == irb->codegen->invalid_instruction)
return name_inst;
AstNode *init_node = node->data.fn_call_expr.params.at(2);
return ir_gen_union_init_expr(irb, scope, node, union_type_inst, name_inst, init_node,
lval, result_loc);
}
}
zig_unreachable();
}
static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
assert(node->type == NodeTypeFnCallExpr);
if (node->data.fn_call_expr.modifier == CallModifierBuiltin)
return ir_gen_builtin_fn_call(irb, scope, node, lval, result_loc);
AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
return ir_gen_fn_call_with_args(irb, scope, node, fn_ref_node, node->data.fn_call_expr.modifier,
nullptr, node->data.fn_call_expr.params.items, node->data.fn_call_expr.params.length, lval, result_loc);
}
static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
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");
IrInstruction *cond_br_inst = ir_build_cond_br(irb, scope, node, condition,
then_block, else_block, is_comptime);
ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block,
result_loc, 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_extra(irb, then_node, subexpr_scope, lval,
&peer_parent->peers.at(0)->base);
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_extra(irb, else_node, subexpr_scope, lval, &peer_parent->peers.at(1)->base);
if (else_expr_result == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
} else {
else_expr_result = ir_build_const_void(irb, scope, node);
ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base);
}
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, "IrBasicBlock *");
incoming_blocks[0] = after_then_block;
incoming_blocks[1] = after_else_block;
IrInstruction *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent);
return ir_expr_wrap(irb, scope, phi, result_loc);
}
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, nullptr);
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_expr_wrap(IrBuilder *irb, Scope *scope, IrInstruction *inst, ResultLoc *result_loc) {
if (inst == irb->codegen->invalid_instruction) return inst;
ir_build_end_expr(irb, scope, inst->source_node, inst, result_loc);
return inst;
}
static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval,
ResultLoc *result_loc)
{
// This logic must be kept in sync with
// [STMT_EXPR_TEST_THING] <--- (search this token)
if (value == irb->codegen->invalid_instruction ||
instr_is_unreachable(value) ||
value->source_node->type == NodeTypeDefer ||
value->id == IrInstructionIdDeclVarSrc)
{
return value;
}
if (lval == LValPtr) {
// We needed a pointer to a value, but we got a value. So we create
// an instruction which just makes a pointer of it.
return ir_build_ref(irb, scope, value->source_node, value, false, false);
} else if (result_loc != nullptr) {
return ir_expr_wrap(irb, scope, value, result_loc);
} else {
return value;
}
}
static PtrLen star_token_to_ptr_len(TokenId token_id) {
switch (token_id) {
case TokenIdStar:
case TokenIdStarStar:
return PtrLenSingle;
case TokenIdLBracket:
return PtrLenUnknown;
case TokenIdSymbol:
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 *sentinel_expr = node->data.pointer_type.sentinel;
AstNode *expr_node = node->data.pointer_type.op_expr;
AstNode *align_expr = node->data.pointer_type.align_expr;
IrInstruction *sentinel;
if (sentinel_expr != nullptr) {
sentinel = ir_gen_node(irb, sentinel_expr, scope);
if (sentinel == irb->codegen->invalid_instruction)
return sentinel;
} else {
sentinel = nullptr;
}
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_u32(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_u32(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, sentinel, 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, ResultLoc *result_loc)
{
IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
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, false);
if (payload_ptr == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
if (lval == LValPtr)
return payload_ptr;
IrInstruction *load_ptr = ir_build_load_ptr(irb, scope, source_node, payload_ptr);
return ir_expr_wrap(irb, scope, load_ptr, result_loc);
}
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,
ResultLoc *result_loc)
{
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, result_loc);
case PrefixOpBinNot:
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpBinNot), lval, result_loc);
case PrefixOpNegation:
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation), lval, result_loc);
case PrefixOpNegationWrap:
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval, result_loc);
case PrefixOpOptional:
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval, result_loc);
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, nullptr), lval, result_loc);
}
}
zig_unreachable();
}
static IrInstruction *ir_gen_union_init_expr(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *union_type, IrInstruction *field_name, AstNode *expr_node,
LVal lval, ResultLoc *parent_result_loc)
{
IrInstruction *container_ptr = ir_build_resolve_result(irb, scope, source_node, parent_result_loc, union_type);
IrInstruction *field_ptr = ir_build_field_ptr_instruction(irb, scope, source_node, container_ptr,
field_name, true);
ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1);
result_loc_inst->base.id = ResultLocIdInstruction;
result_loc_inst->base.source_instruction = field_ptr;
ir_ref_instruction(field_ptr, irb->current_basic_block);
ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base);
IrInstruction *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone,
&result_loc_inst->base);
if (expr_value == irb->codegen->invalid_instruction)
return expr_value;
IrInstruction *init_union = ir_build_union_init_named_field(irb, scope, source_node, union_type,
field_name, field_ptr, container_ptr);
return ir_lval_wrap(irb, scope, init_union, lval, parent_result_loc);
}
static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
ResultLoc *parent_result_loc)
{
assert(node->type == NodeTypeContainerInitExpr);
AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr;
ContainerInitKind kind = container_init_expr->kind;
ResultLocCast *result_loc_cast = nullptr;
ResultLoc *child_result_loc;
AstNode *init_array_type_source_node;
if (container_init_expr->type != nullptr) {
IrInstruction *container_type;
if (container_init_expr->type->type == NodeTypeInferredArrayType) {
if (kind == ContainerInitKindStruct) {
add_node_error(irb->codegen, container_init_expr->type,
buf_sprintf("initializing array with struct syntax"));
return irb->codegen->invalid_instruction;
}
IrInstruction *sentinel;
if (container_init_expr->type->data.inferred_array_type.sentinel != nullptr) {
sentinel = ir_gen_node(irb, container_init_expr->type->data.inferred_array_type.sentinel, scope);
if (sentinel == irb->codegen->invalid_instruction)
return sentinel;
} else {
sentinel = nullptr;
}
IrInstruction *elem_type = ir_gen_node(irb,
container_init_expr->type->data.inferred_array_type.child_type, scope);
if (elem_type == irb->codegen->invalid_instruction)
return elem_type;
size_t item_count = container_init_expr->entries.length;
IrInstruction *item_count_inst = ir_build_const_usize(irb, scope, node, item_count);
container_type = ir_build_array_type(irb, scope, node, item_count_inst, sentinel, elem_type);
} else {
container_type = ir_gen_node(irb, container_init_expr->type, scope);
if (container_type == irb->codegen->invalid_instruction)
return container_type;
}
result_loc_cast = ir_build_cast_result_loc(irb, container_type, parent_result_loc);
child_result_loc = &result_loc_cast->base;
init_array_type_source_node = container_type->source_node;
} else {
child_result_loc = parent_result_loc;
if (parent_result_loc->source_instruction != nullptr) {
init_array_type_source_node = parent_result_loc->source_instruction->source_node;
} else {
init_array_type_source_node = node;
}
}
switch (kind) {
case ContainerInitKindStruct: {
IrInstruction *container_ptr = ir_build_resolve_result(irb, scope, node, child_result_loc,
nullptr);
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 *field_ptr = ir_build_field_ptr(irb, scope, entry_node, container_ptr, name, true);
ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1);
result_loc_inst->base.id = ResultLocIdInstruction;
result_loc_inst->base.source_instruction = field_ptr;
result_loc_inst->base.allow_write_through_const = true;
ir_ref_instruction(field_ptr, irb->current_basic_block);
ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base);
IrInstruction *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone,
&result_loc_inst->base);
if (expr_value == irb->codegen->invalid_instruction)
return expr_value;
fields[i].name = name;
fields[i].source_node = entry_node;
fields[i].result_loc = field_ptr;
}
IrInstruction *result = ir_build_container_init_fields(irb, scope, node, field_count,
fields, container_ptr);
if (result_loc_cast != nullptr) {
result = ir_build_implicit_cast(irb, scope, node, result, result_loc_cast);
}
return ir_lval_wrap(irb, scope, result, lval, parent_result_loc);
}
case ContainerInitKindArray: {
size_t item_count = container_init_expr->entries.length;
IrInstruction *container_ptr = ir_build_resolve_result(irb, scope, node, child_result_loc,
nullptr);
IrInstruction **result_locs = allocate<IrInstruction *>(item_count);
for (size_t i = 0; i < item_count; i += 1) {
AstNode *expr_node = container_init_expr->entries.at(i);
IrInstruction *elem_index = ir_build_const_usize(irb, scope, expr_node, i);
IrInstruction *elem_ptr = ir_build_elem_ptr(irb, scope, expr_node, container_ptr,
elem_index, false, PtrLenSingle, init_array_type_source_node);
ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1);
result_loc_inst->base.id = ResultLocIdInstruction;
result_loc_inst->base.source_instruction = elem_ptr;
result_loc_inst->base.allow_write_through_const = true;
ir_ref_instruction(elem_ptr, irb->current_basic_block);
ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base);
IrInstruction *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone,
&result_loc_inst->base);
if (expr_value == irb->codegen->invalid_instruction)
return expr_value;
result_locs[i] = elem_ptr;
}
IrInstruction *result = ir_build_container_init_list(irb, scope, node, item_count,
result_locs, container_ptr, init_array_type_source_node);
if (result_loc_cast != nullptr) {
result = ir_build_implicit_cast(irb, scope, node, result, result_loc_cast);
}
return ir_lval_wrap(irb, scope, result, lval, parent_result_loc);
}
}
zig_unreachable();
}
static ResultLocVar *ir_build_var_result_loc(IrBuilder *irb, IrInstruction *alloca, ZigVar *var) {
ResultLocVar *result_loc_var = allocate<ResultLocVar>(1);
result_loc_var->base.id = ResultLocIdVar;
result_loc_var->base.source_instruction = alloca;
result_loc_var->base.allow_write_through_const = true;
result_loc_var->var = var;
ir_build_reset_result(irb, alloca->scope, alloca->source_node, &result_loc_var->base);
return result_loc_var;
}
static ResultLocCast *ir_build_cast_result_loc(IrBuilder *irb, IrInstruction *dest_type,
ResultLoc *parent_result_loc)
{
ResultLocCast *result_loc_cast = allocate<ResultLocCast>(1);
result_loc_cast->base.id = ResultLocIdCast;
result_loc_cast->base.source_instruction = dest_type;
result_loc_cast->base.allow_write_through_const = parent_result_loc->allow_write_through_const;
ir_ref_instruction(dest_type, irb->current_basic_block);
result_loc_cast->parent = parent_result_loc;
ir_build_reset_result(irb, dest_type->scope, dest_type->source_node, &result_loc_cast->base);
return result_loc_cast;
}
static void build_decl_var_and_init(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var,
IrInstruction *init, const char *name_hint, IrInstruction *is_comptime)
{
IrInstruction *alloca = ir_build_alloca_src(irb, scope, source_node, nullptr, name_hint, is_comptime);
ResultLocVar *var_result_loc = ir_build_var_result_loc(irb, alloca, var);
ir_build_end_expr(irb, scope, source_node, init, &var_result_loc->base);
ir_build_var_decl_src(irb, scope, source_node, var, nullptr, alloca);
}
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;
}
// Used for the type expr and the align expr
Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope);
IrInstruction *type_instruction;
if (variable_declaration->type != nullptr) {
type_instruction = ir_gen_node(irb, variable_declaration->type, comptime_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;
bool is_comptime_scalar = ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime;
IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, is_comptime_scalar);
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, comptime_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)));
}
// Parser should ensure that this never happens
assert(variable_declaration->threadlocal_tok == nullptr);
IrInstruction *alloca = ir_build_alloca_src(irb, scope, node, align_value,
buf_ptr(variable_declaration->symbol), is_comptime);
// Create a result location for the initialization expression.
ResultLocVar *result_loc_var = ir_build_var_result_loc(irb, alloca, var);
ResultLoc *init_result_loc;
ResultLocCast *result_loc_cast;
if (type_instruction != nullptr) {
result_loc_cast = ir_build_cast_result_loc(irb, type_instruction, &result_loc_var->base);
init_result_loc = &result_loc_cast->base;
} else {
result_loc_cast = nullptr;
init_result_loc = &result_loc_var->base;
}
Scope *init_scope = is_comptime_scalar ?
create_comptime_scope(irb->codegen, variable_declaration->expr, scope) : scope;
// 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_extra(irb, variable_declaration->expr, init_scope,
LValNone, init_result_loc);
irb->exec->name = old_exec_name;
if (init_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
if (result_loc_cast != nullptr) {
IrInstruction *implicit_cast = ir_build_implicit_cast(irb, scope, init_value->source_node,
init_value, result_loc_cast);
ir_build_end_expr(irb, scope, node, implicit_cast, &result_loc_var->base);
}
return ir_build_var_decl_src(irb, scope, node, var, align_value, alloca);
}
static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
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, nullptr);
if (err_val_ptr == irb->codegen->invalid_instruction)
return err_val_ptr;
IrInstruction *is_err = ir_build_test_err_src(irb, scope, node->data.while_expr.condition, err_val_ptr,
true, false);
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));
IrInstruction *cond_br_inst;
if (!instr_is_unreachable(is_err)) {
cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_err,
else_block, body_block, is_comptime);
cond_br_inst->is_gen = true;
} else {
// for the purposes of the source instruction to ir_build_result_peers
cond_br_inst = irb->current_basic_block->instruction_list.last();
}
ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc,
is_comptime);
ir_set_cursor_at_end_and_append_block(irb, body_block);
if (var_symbol) {
IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, payload_scope, symbol_node,
err_val_ptr, false, false);
IrInstruction *var_ptr = node->data.while_expr.var_is_ptr ?
ir_build_ref(irb, payload_scope, symbol_node, payload_ptr, true, false) : payload_ptr;
ir_build_var_decl_src(irb, payload_scope, symbol_node, payload_var, nullptr, var_ptr);
}
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;
loop_scope->lval = lval;
loop_scope->peer_parent = peer_parent;
// Note the body block of the loop is not the place that lval and result_loc are used -
// it's actually in break statements, handled similarly to return statements.
// That is why we set those values in loop_scope above and not in this ir_gen_node call.
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_ptr = 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, err_ptr);
if (peer_parent->peers.length != 0) {
peer_parent->peers.last()->next_bb = else_block;
}
ResultLocPeer *peer_result = create_peer_result(peer_parent);
peer_parent->peers.append(peer_result);
IrInstruction *else_result = ir_gen_node_extra(irb, else_node, err_scope, lval, &peer_result->base);
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);
}
if (peer_parent->peers.length != 0) {
peer_parent->peers.last()->next_bb = end_block;
}
IrInstruction *phi = ir_build_phi(irb, scope, node, incoming_blocks.length,
incoming_blocks.items, incoming_values.items, peer_parent);
return ir_expr_wrap(irb, scope, phi, result_loc);
} 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, nullptr);
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));
IrInstruction *cond_br_inst;
if (!instr_is_unreachable(is_non_null)) {
cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_non_null,
body_block, else_block, is_comptime);
cond_br_inst->is_gen = true;
} else {
// for the purposes of the source instruction to ir_build_result_peers
cond_br_inst = irb->current_basic_block->instruction_list.last();
}
ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc,
is_comptime);
ir_set_cursor_at_end_and_append_block(irb, body_block);
IrInstruction *payload_ptr = ir_build_optional_unwrap_ptr(irb, child_scope, symbol_node, maybe_val_ptr, false, false);
IrInstruction *var_ptr = node->data.while_expr.var_is_ptr ?
ir_build_ref(irb, child_scope, symbol_node, payload_ptr, true, false) : payload_ptr;
ir_build_var_decl_src(irb, child_scope, symbol_node, payload_var, nullptr, var_ptr);
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;
loop_scope->lval = lval;
loop_scope->peer_parent = peer_parent;
// Note the body block of the loop is not the place that lval and result_loc are used -
// it's actually in break statements, handled similarly to return statements.
// That is why we set those values in loop_scope above and not in this ir_gen_node call.
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);
if (peer_parent->peers.length != 0) {
peer_parent->peers.last()->next_bb = else_block;
}
ResultLocPeer *peer_result = create_peer_result(peer_parent);
peer_parent->peers.append(peer_result);
else_result = ir_gen_node_extra(irb, else_node, scope, lval, &peer_result->base);
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);
}
if (peer_parent->peers.length != 0) {
peer_parent->peers.last()->next_bb = end_block;
}
IrInstruction *phi = ir_build_phi(irb, scope, node, incoming_blocks.length,
incoming_blocks.items, incoming_values.items, peer_parent);
return ir_expr_wrap(irb, scope, phi, result_loc);
} 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));
IrInstruction *cond_br_inst;
if (!instr_is_unreachable(cond_val)) {
cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val,
body_block, else_block, is_comptime);
cond_br_inst->is_gen = true;
} else {
// for the purposes of the source instruction to ir_build_result_peers
cond_br_inst = irb->current_basic_block->instruction_list.last();
}
ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc,
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;
loop_scope->lval = lval;
loop_scope->peer_parent = peer_parent;
// Note the body block of the loop is not the place that lval and result_loc are used -
// it's actually in break statements, handled similarly to return statements.
// That is why we set those values in loop_scope above and not in this ir_gen_node call.
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);
if (peer_parent->peers.length != 0) {
peer_parent->peers.last()->next_bb = else_block;
}
ResultLocPeer *peer_result = create_peer_result(peer_parent);
peer_parent->peers.append(peer_result);
else_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_result->base);
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);
}
if (peer_parent->peers.length != 0) {
peer_parent->peers.last()->next_bb = end_block;
}
IrInstruction *phi = ir_build_phi(irb, scope, node, incoming_blocks.length,
incoming_blocks.items, incoming_values.items, peer_parent);
return ir_expr_wrap(irb, scope, phi, result_loc);
}
}
static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
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);
ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, parent_scope);
IrInstruction *array_val_ptr = ir_gen_node_extra(irb, array_node, &spill_scope->base, LValPtr, nullptr);
if (array_val_ptr == irb->codegen->invalid_instruction)
return array_val_ptr;
IrInstruction *is_comptime = ir_build_const_bool(irb, parent_scope, node,
ir_should_inline(irb->exec, parent_scope) || node->data.for_expr.is_inline);
AstNode *index_var_source_node;
ZigVar *index_var;
const char *index_var_name;
if (index_node) {
index_var_source_node = index_node;
Buf *index_var_name_buf = index_node->data.symbol_expr.symbol;
index_var = ir_create_var(irb, index_node, parent_scope, index_var_name_buf, true, false, false, is_comptime);
index_var_name = buf_ptr(index_var_name_buf);
} else {
index_var_source_node = node;
index_var = ir_create_var(irb, node, parent_scope, nullptr, true, false, true, is_comptime);
index_var_name = "i";
}
IrInstruction *zero = ir_build_const_usize(irb, parent_scope, node, 0);
build_decl_var_and_init(irb, parent_scope, index_var_source_node, index_var, zero, index_var_name, is_comptime);
parent_scope = index_var->child_scope;
IrInstruction *one = ir_build_const_usize(irb, parent_scope, node, 1);
IrInstruction *index_ptr = ir_build_var_ptr(irb, parent_scope, node, index_var);
IrBasicBlock *cond_block = ir_create_basic_block(irb, parent_scope, "ForCond");
IrBasicBlock *body_block = ir_create_basic_block(irb, parent_scope, "ForBody");
IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "ForEnd");
IrBasicBlock *else_block = else_node ? ir_create_basic_block(irb, parent_scope, "ForElse") : end_block;
IrBasicBlock *continue_block = ir_create_basic_block(irb, parent_scope, "ForContinue");
Buf *len_field_name = buf_create_from_str("len");
IrInstruction *len_ref = ir_build_field_ptr(irb, parent_scope, node, array_val_ptr, len_field_name, false);
IrInstruction *len_val = ir_build_load_ptr(irb, &spill_scope->base, node, len_ref);
ir_build_br(irb, parent_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, &spill_scope->base, node, index_ptr);
IrInstruction *cond = ir_build_bin_op(irb, parent_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));
IrInstruction *cond_br_inst = ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, cond,
body_block, else_block, is_comptime));
ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, body_block);
Scope *elem_ptr_scope = node->data.for_expr.elem_is_ptr ? parent_scope : &spill_scope->base;
IrInstruction *elem_ptr = ir_build_elem_ptr(irb, elem_ptr_scope, node, array_val_ptr, index_val, false,
PtrLenSingle, nullptr);
// 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 *var_ptr = node->data.for_expr.elem_is_ptr ?
ir_build_ref(irb, &spill_scope->base, elem_node, elem_ptr, true, false) : elem_ptr;
ir_build_var_decl_src(irb, parent_scope, elem_node, elem_var, nullptr, var_ptr);
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;
loop_scope->lval = LValNone;
loop_scope->peer_parent = peer_parent;
loop_scope->spill_scope = spill_scope;
// Note the body block of the loop is not the place that lval and result_loc are used -
// it's actually in break statements, handled similarly to return statements.
// That is why we set those values in loop_scope above and not in this ir_gen_node call.
IrInstruction *body_result = ir_gen_node(irb, body_node, &loop_scope->base);
if (body_result == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
if (!instr_is_unreachable(body_result)) {
ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.for_expr.body, 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_build_store_ptr(irb, child_scope, node, index_ptr, new_index_val)->allow_write_through_const = true;
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);
if (peer_parent->peers.length != 0) {
peer_parent->peers.last()->next_bb = else_block;
}
ResultLocPeer *peer_result = create_peer_result(peer_parent);
peer_parent->peers.append(peer_result);
else_result = ir_gen_node_extra(irb, else_node, parent_scope, LValNone, &peer_result->base);
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);
}
if (peer_parent->peers.length != 0) {
peer_parent->peers.last()->next_bb = end_block;
}
IrInstruction *phi = ir_build_phi(irb, parent_scope, node, incoming_blocks.length,
incoming_blocks.items, incoming_values.items, peer_parent);
return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc);
}
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);
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 *sentinel_expr = node->data.array_type.sentinel;
AstNode *align_expr = node->data.array_type.align_expr;
Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope);
IrInstruction *sentinel;
if (sentinel_expr != nullptr) {
sentinel = ir_gen_node(irb, sentinel_expr, comptime_scope);
if (sentinel == irb->codegen->invalid_instruction)
return sentinel;
} else {
sentinel = nullptr;
}
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, sentinel, 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, sentinel,
align_value, is_allow_zero);
}
}
static IrInstruction *ir_gen_anyframe_type(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeAnyFrameType);
AstNode *payload_type_node = node->data.anyframe_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_anyframe_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(IrAnalyze *ira, 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(ira->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(ira->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(ira->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 size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok, Buf *src_template) {
const char *ptr = buf_ptr(src_template) + tok->start + 2;
size_t len = tok->end - tok->start - 2;
size_t result = 0;
for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1, result += 1) {
AsmOutput *asm_output = node->data.asm_expr.output_list.at(i);
if (buf_eql_mem(asm_output->asm_symbolic_name, ptr, len)) {
return result;
}
}
for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1, result += 1) {
AsmInput *asm_input = node->data.asm_expr.input_list.at(i);
if (buf_eql_mem(asm_input->asm_symbolic_name, ptr, len)) {
return result;
}
}
return SIZE_MAX;
}
static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeAsmExpr);
AstNodeAsmExpr *asm_expr = &node->data.asm_expr;
IrInstruction *asm_template = ir_gen_node(irb, asm_expr->asm_template, scope);
if (asm_template == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
bool is_volatile = asm_expr->volatile_token != nullptr;
bool in_fn_scope = (scope_fn_entry(scope) != nullptr);
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_asm_src(irb, scope, node, asm_template, nullptr, nullptr,
nullptr, 0, is_volatile, true);
}
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_src(irb, scope, node, asm_template, input_list, output_types,
output_vars, return_count, is_volatile, false);
}
static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
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, nullptr);
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);
}
IrInstruction *cond_br_inst = ir_build_cond_br(irb, scope, node, is_non_null,
then_block, else_block, is_comptime);
ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block,
result_loc, 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) {
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 *payload_ptr = ir_build_optional_unwrap_ptr(irb, subexpr_scope, node, maybe_val_ptr, false, false);
IrInstruction *var_ptr = var_is_ptr ? ir_build_ref(irb, subexpr_scope, node, payload_ptr, true, false) : payload_ptr;
ir_build_var_decl_src(irb, subexpr_scope, node, var, nullptr, var_ptr);
var_scope = var->child_scope;
} else {
var_scope = subexpr_scope;
}
IrInstruction *then_expr_result = ir_gen_node_extra(irb, then_node, var_scope, lval,
&peer_parent->peers.at(0)->base);
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_extra(irb, else_node, subexpr_scope, lval, &peer_parent->peers.at(1)->base);
if (else_expr_result == irb->codegen->invalid_instruction)
return else_expr_result;
} else {
else_expr_result = ir_build_const_void(irb, scope, node);
ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base);
}
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, "IrBasicBlock *");
incoming_blocks[0] = after_then_block;
incoming_blocks[1] = after_else_block;
IrInstruction *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent);
return ir_expr_wrap(irb, scope, phi, result_loc);
}
static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
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, nullptr);
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_src(irb, scope, node, err_val_ptr, true, false);
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);
IrInstruction *cond_br_inst = ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime);
ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block,
result_loc, 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) {
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 *payload_ptr = ir_build_unwrap_err_payload(irb, subexpr_scope, node, err_val_ptr, false, false);
IrInstruction *var_ptr = var_is_ptr ?
ir_build_ref(irb, subexpr_scope, node, payload_ptr, true, false) : payload_ptr;
ir_build_var_decl_src(irb, subexpr_scope, node, var, nullptr, var_ptr);
var_scope = var->child_scope;
} else {
var_scope = subexpr_scope;
}
IrInstruction *then_expr_result = ir_gen_node_extra(irb, then_node, var_scope, lval,
&peer_parent->peers.at(0)->base);
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) {
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 *err_ptr = ir_build_unwrap_err_code(irb, subexpr_scope, node, err_val_ptr);
ir_build_var_decl_src(irb, subexpr_scope, node, var, nullptr, err_ptr);
err_var_scope = var->child_scope;
} else {
err_var_scope = subexpr_scope;
}
else_expr_result = ir_gen_node_extra(irb, else_node, err_var_scope, lval, &peer_parent->peers.at(1)->base);
if (else_expr_result == irb->codegen->invalid_instruction)
return else_expr_result;
} else {
else_expr_result = ir_build_const_void(irb, scope, node);
ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base);
}
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, "IrBasicBlock *");
incoming_blocks[0] = after_then_block;
incoming_blocks[1] = after_else_block;
IrInstruction *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent);
return ir_expr_wrap(irb, scope, phi, result_loc);
}
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_values, size_t prong_values_len,
ZigList<IrBasicBlock *> *incoming_blocks, ZigList<IrInstruction *> *incoming_values,
IrInstructionSwitchElseVar **out_switch_else_var, LVal lval, ResultLoc *result_loc)
{
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_ptr;
if (out_switch_else_var != nullptr) {
IrInstructionSwitchElseVar *switch_else_var = ir_build_switch_else_var(irb, scope, var_symbol_node,
target_value_ptr);
*out_switch_else_var = switch_else_var;
IrInstruction *payload_ptr = &switch_else_var->base;
var_ptr = var_is_ptr ? ir_build_ref(irb, scope, var_symbol_node, payload_ptr, true, false) : payload_ptr;
} else if (prong_values != nullptr) {
IrInstruction *payload_ptr = ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr,
prong_values, prong_values_len);
var_ptr = var_is_ptr ? ir_build_ref(irb, scope, var_symbol_node, payload_ptr, true, false) : payload_ptr;
} else {
var_ptr = var_is_ptr ?
ir_build_ref(irb, scope, var_symbol_node, target_value_ptr, true, false) : target_value_ptr;
}
ir_build_var_decl_src(irb, scope, var_symbol_node, var, nullptr, var_ptr);
} else {
child_scope = scope;
}
IrInstruction *expr_result = ir_gen_node_extra(irb, expr_node, child_scope, lval, result_loc);
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, LVal lval,
ResultLoc *result_loc)
{
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, nullptr);
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};
IrInstructionSwitchElseVar *switch_else_var = nullptr;
ResultLocPeerParent *peer_parent = allocate<ResultLocPeerParent>(1);
peer_parent->base.id = ResultLocIdPeerParent;
peer_parent->base.allow_write_through_const = result_loc->allow_write_through_const;
peer_parent->end_bb = end_block;
peer_parent->is_comptime = is_comptime;
peer_parent->parent = result_loc;
ir_build_reset_result(irb, scope, node, &peer_parent->base);
// 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) {
ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
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;
if (peer_parent->peers.length > 0) {
peer_parent->peers.last()->next_bb = else_block;
}
peer_parent->peers.append(this_peer_result_loc);
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, 0, &incoming_blocks, &incoming_values,
&switch_else_var, LValNone, &this_peer_result_loc->base))
{
return irb->codegen->invalid_instruction;
}
ir_set_cursor_at_end(irb, prev_block);
} else if (prong_node->data.switch_prong.any_items_are_range) {
ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
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);
IrInstruction *br_inst = ir_mark_gen(ir_build_cond_br(irb, scope, last_item_node, ok_bit,
range_block_yes, range_block_no, is_comptime));
if (peer_parent->base.source_instruction == nullptr) {
peer_parent->base.source_instruction = br_inst;
}
if (peer_parent->peers.length > 0) {
peer_parent->peers.last()->next_bb = range_block_yes;
}
peer_parent->peers.append(this_peer_result_loc);
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, 0,
&incoming_blocks, &incoming_values, nullptr, LValNone, &this_peer_result_loc->base))
{
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;
ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
IrBasicBlock *prong_block = ir_create_basic_block(irb, scope, "SwitchProng");
IrInstruction **items = allocate<IrInstruction *>(prong_item_count);
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;
items[item_i] = item_value;
}
IrBasicBlock *prev_block = irb->current_basic_block;
if (peer_parent->peers.length > 0) {
peer_parent->peers.last()->next_bb = prong_block;
}
peer_parent->peers.append(this_peer_result_loc);
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, items, prong_item_count,
&incoming_blocks, &incoming_values, nullptr, LValNone, &this_peer_result_loc->base))
{
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);
IrInstruction *br_instruction;
if (cases.length == 0) {
br_instruction = ir_build_br(irb, scope, node, else_block, is_comptime);
} else {
IrInstructionSwitchBr *switch_br = ir_build_switch_br(irb, scope, node, target_value, else_block,
cases.length, cases.items, is_comptime, switch_prongs_void);
if (switch_else_var != nullptr) {
switch_else_var->switch_br = switch_br;
}
br_instruction = &switch_br->base;
}
if (peer_parent->base.source_instruction == nullptr) {
peer_parent->base.source_instruction = br_instruction;
}
for (size_t i = 0; i < peer_parent->peers.length; i += 1) {
peer_parent->peers.at(i)->base.source_instruction = peer_parent->base.source_instruction;
}
if (!else_prong) {
if (peer_parent->peers.length != 0) {
peer_parent->peers.last()->next_bb = else_block;
}
ir_set_cursor_at_end_and_append_block(irb, else_block);
ir_build_unreachable(irb, scope, node);
} else {
if (peer_parent->peers.length != 0) {
peer_parent->peers.last()->next_bb = end_block;
}
}
ir_set_cursor_at_end_and_append_block(irb, end_block);
assert(incoming_blocks.length == incoming_values.length);
IrInstruction *result_instruction;
if (incoming_blocks.length == 0) {
result_instruction = ir_build_const_void(irb, scope, node);
} else {
result_instruction = ir_build_phi(irb, scope, node, incoming_blocks.length,
incoming_blocks.items, incoming_values.items, peer_parent);
}
return ir_lval_wrap(irb, scope, result_instruction, lval, result_loc);
}
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);
// purposefully pass null for result_loc and let EndExpr handle it
return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval, nullptr);
}
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) {
ResultLocPeer *peer_result = create_peer_result(block_scope->peer_parent);
block_scope->peer_parent->peers.append(peer_result);
result_value = ir_gen_node_extra(irb, node->data.break_expr.expr, break_scope, block_scope->lval,
&peer_result->base);
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) {
ResultLocPeer *peer_result = create_peer_result(loop_scope->peer_parent);
loop_scope->peer_parent->peers.append(peer_result);
result_value = ir_gen_node_extra(irb, node->data.break_expr.expr, break_scope,
loop_scope->lval, &peer_result->base);
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, LVal lval, ResultLoc *result_loc) {
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;
AstNode *sentinel_node = slice_expr->sentinel;
IrInstruction *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr, nullptr);
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;
}
IrInstruction *sentinel_value;
if (sentinel_node) {
sentinel_value = ir_gen_node(irb, sentinel_node, scope);
if (sentinel_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
} else {
sentinel_value = nullptr;
}
IrInstruction *slice = ir_build_slice_src(irb, scope, node, ptr_value, start_value, end_value,
sentinel_value, true, result_loc);
return ir_lval_wrap(irb, scope, slice, lval, result_loc);
}
static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
assert(node->type == NodeTypeCatchExpr);
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, lval, result_loc);
}
IrInstruction *err_union_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr, nullptr);
if (err_union_ptr == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *is_err = ir_build_test_err_src(irb, parent_scope, node, err_union_ptr, true, false);
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");
IrInstruction *cond_br_inst = ir_build_cond_br(irb, parent_scope, node, is_err, err_block, ok_block, is_comptime);
ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, ok_block, end_block, result_loc,
is_comptime);
ir_set_cursor_at_end_and_append_block(irb, err_block);
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, parent_scope, is_comptime);
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, subexpr_scope, var_name,
is_const, is_const, is_shadowable, is_comptime);
err_scope = var->child_scope;
IrInstruction *err_ptr = ir_build_unwrap_err_code(irb, err_scope, node, err_union_ptr);
ir_build_var_decl_src(irb, err_scope, var_node, var, nullptr, err_ptr);
} else {
err_scope = subexpr_scope;
}
IrInstruction *err_result = ir_gen_node_extra(irb, op2_node, err_scope, LValNone, &peer_parent->peers.at(0)->base);
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, parent_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, false);
IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr);
ir_build_end_expr(irb, parent_scope, node, unwrapped_payload, &peer_parent->peers.at(1)->base);
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, "IrBasicBlock *");
incoming_blocks[0] = after_err_block;
incoming_blocks[1] = after_ok_block;
IrInstruction *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent);
return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc);
}
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 != nullptr && exec->name) {
ZigType *import = get_scope_import(scope);
Buf *namespace_name = buf_alloc();
append_namespace_qualification(codegen, namespace_name, import);
buf_append_buf(namespace_name, exec->name);
buf_init_from_buf(out_bare_name, exec->name);
return namespace_name;
} else if (exec != nullptr && 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_alloc();
append_namespace_qualification(codegen, namespace_name, import);
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,
Buf *type_name)
{
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;
if (type_name == nullptr) {
buf_resize(&err_set_type->name, 0);
buf_appendf(&err_set_type->name, "error{");
} else {
buf_init_from_buf(&err_set_type->name, type_name);
}
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);
bool need_comma = false;
for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) {
ErrorTableEntry *error_entry = set1->data.error_set.errors[i];
if (type_name == nullptr) {
const char *comma = need_comma ? "," : "";
need_comma = true;
buf_appendf(&err_set_type->name, "%s%s", comma, 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;
if (type_name == nullptr) {
const char *comma = need_comma ? "," : "";
need_comma = true;
buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&error_entry->name));
}
err_set_type->data.error_set.errors[index] = error_entry;
index += 1;
}
}
assert(index == count);
if (type_name == nullptr) {
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 AstNode *ast_field_to_symbol_node(AstNode *err_set_field_node) {
if (err_set_field_node->type == NodeTypeSymbol) {
return err_set_field_node;
} else if (err_set_field_node->type == NodeTypeErrorSetField) {
assert(err_set_field_node->data.err_set_field.field_name->type == NodeTypeSymbol);
return err_set_field_node->data.err_set_field.field_name;
} else {
return err_set_field_node;
}
}
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);
size_t errors_count = irb->codegen->errors_by_index.length + err_count;
ErrorTableEntry **errors = allocate<ErrorTableEntry *>(errors_count, "ErrorTableEntry *");
for (uint32_t i = 0; i < err_count; i += 1) {
AstNode *field_node = node->data.err_set_decl.decls.at(i);
AstNode *symbol_node = ast_field_to_symbol_node(field_node);
Buf *err_name = symbol_node->data.symbol_expr.symbol;
ErrorTableEntry *err = allocate<ErrorTableEntry>(1);
err->decl_node = field_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, ast_field_to_symbol_node(err->decl_node),
buf_sprintf("duplicate error: '%s'", buf_ptr(&err->name)));
add_error_note(irb->codegen, msg, ast_field_to_symbol_node(prev_err->decl_node),
buf_sprintf("other error here"));
return irb->codegen->invalid_instruction;
}
errors[err->value] = err;
}
deallocate(errors, errors_count, "ErrorTableEntry *");
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 *callconv_value = nullptr;
if (node->data.fn_proto.callconv_expr != nullptr) {
callconv_value = ir_gen_node(irb, node->data.fn_proto.callconv_expr, parent_scope);
if (callconv_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;
}
return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, callconv_value, return_type, is_var_args);
}
static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeResume);
IrInstruction *target_inst = ir_gen_node_extra(irb, node->data.resume_expr.expr, scope, LValPtr, nullptr);
if (target_inst == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
return ir_build_resume(irb, scope, node, target_inst);
}
static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
assert(node->type == NodeTypeAwaitExpr);
AstNode *expr_node = node->data.await_expr.expr;
if (expr_node->type == NodeTypeFnCallExpr && expr_node->data.fn_call_expr.modifier == CallModifierBuiltin) {
AstNode *fn_ref_expr = 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 != nullptr) {
BuiltinFnEntry *builtin_fn = entry->value;
if (builtin_fn->id == BuiltinFnIdAsyncCall) {
return ir_gen_async_call(irb, scope, node, expr_node, lval, result_loc);
}
}
}
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;
}
ScopeSuspend *existing_suspend_scope = get_scope_suspend(scope);
if (existing_suspend_scope) {
if (!existing_suspend_scope->reported_err) {
ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot await inside suspend block"));
add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("suspend block here"));
existing_suspend_scope->reported_err = true;
}
return irb->codegen->invalid_instruction;
}
IrInstruction *target_inst = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
if (target_inst == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *await_inst = ir_build_await_src(irb, scope, node, target_inst, result_loc);
return ir_lval_wrap(irb, scope, await_inst, lval, result_loc);
}
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;
}
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;
}
IrInstructionSuspendBegin *begin = ir_build_suspend_begin(irb, parent_scope, node);
if (node->data.suspend.block != nullptr) {
ScopeSuspend *suspend_scope = create_suspend_scope(irb->codegen, node, parent_scope);
Scope *child_scope = &suspend_scope->base;
IrInstruction *susp_res = ir_gen_node(irb, node->data.suspend.block, child_scope);
ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.suspend.block, susp_res));
}
return ir_mark_gen(ir_build_suspend_finish(irb, parent_scope, node, begin));
}
static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope,
LVal lval, ResultLoc *result_loc)
{
assert(scope);
switch (node->type) {
case NodeTypeStructValueField:
case NodeTypeParamDecl:
case NodeTypeUsingNamespace:
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeStructField:
case NodeTypeErrorSetField:
case NodeTypeFnDef:
case NodeTypeTestDecl:
zig_unreachable();
case NodeTypeBlock:
return ir_gen_block(irb, scope, node, lval, result_loc);
case NodeTypeGroupedExpr:
return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval, result_loc);
case NodeTypeBinOpExpr:
return ir_gen_bin_op(irb, scope, node, lval, result_loc);
case NodeTypeIntLiteral:
return ir_lval_wrap(irb, scope, ir_gen_int_lit(irb, scope, node), lval, result_loc);
case NodeTypeFloatLiteral:
return ir_lval_wrap(irb, scope, ir_gen_float_lit(irb, scope, node), lval, result_loc);
case NodeTypeCharLiteral:
return ir_lval_wrap(irb, scope, ir_gen_char_lit(irb, scope, node), lval, result_loc);
case NodeTypeSymbol:
return ir_gen_symbol(irb, scope, node, lval, result_loc);
case NodeTypeFnCallExpr:
return ir_gen_fn_call(irb, scope, node, lval, result_loc);
case NodeTypeIfBoolExpr:
return ir_gen_if_bool_expr(irb, scope, node, lval, result_loc);
case NodeTypePrefixOpExpr:
return ir_gen_prefix_op_expr(irb, scope, node, lval, result_loc);
case NodeTypeContainerInitExpr:
return ir_gen_container_init_expr(irb, scope, node, lval, result_loc);
case NodeTypeVariableDeclaration:
return ir_gen_var_decl(irb, scope, node);
case NodeTypeWhileExpr:
return ir_gen_while_expr(irb, scope, node, lval, result_loc);
case NodeTypeForExpr:
return ir_gen_for_expr(irb, scope, node, lval, result_loc);
case NodeTypeArrayAccessExpr:
return ir_gen_array_access(irb, scope, node, lval, result_loc);
case NodeTypeReturnExpr:
return ir_gen_return(irb, scope, node, lval, result_loc);
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;
IrInstruction *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction);
return ir_expr_wrap(irb, scope, load_ptr, result_loc);
}
case NodeTypePtrDeref: {
AstNode *expr_node = node->data.ptr_deref_expr.target;
IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval, nullptr);
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
IrInstruction *un_op = ir_build_un_op_lval(irb, scope, node, IrUnOpDereference, value, lval, result_loc);
return ir_expr_wrap(irb, scope, un_op, result_loc);
}
case NodeTypeUnwrapOptional: {
AstNode *expr_node = node->data.unwrap_optional.expr;
IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
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, false);
if (lval == LValPtr)
return unwrapped_ptr;
IrInstruction *load_ptr = ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
return ir_expr_wrap(irb, scope, load_ptr, result_loc);
}
case NodeTypeBoolLiteral:
return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval, result_loc);
case NodeTypeArrayType:
return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval, result_loc);
case NodeTypePointerType:
return ir_lval_wrap(irb, scope, ir_gen_pointer_type(irb, scope, node), lval, result_loc);
case NodeTypeAnyFrameType:
return ir_lval_wrap(irb, scope, ir_gen_anyframe_type(irb, scope, node), lval, result_loc);
case NodeTypeStringLiteral:
return ir_lval_wrap(irb, scope, ir_gen_string_literal(irb, scope, node), lval, result_loc);
case NodeTypeUndefinedLiteral:
return ir_lval_wrap(irb, scope, ir_gen_undefined_literal(irb, scope, node), lval, result_loc);
case NodeTypeAsmExpr:
return ir_lval_wrap(irb, scope, ir_gen_asm_expr(irb, scope, node), lval, result_loc);
case NodeTypeNullLiteral:
return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval, result_loc);
case NodeTypeIfErrorExpr:
return ir_gen_if_err_expr(irb, scope, node, lval, result_loc);
case NodeTypeIfOptional:
return ir_gen_if_optional_expr(irb, scope, node, lval, result_loc);
case NodeTypeSwitchExpr:
return ir_gen_switch_expr(irb, scope, node, lval, result_loc);
case NodeTypeCompTime:
return ir_expr_wrap(irb, scope, ir_gen_comptime(irb, scope, node, lval), result_loc);
case NodeTypeErrorType:
return ir_lval_wrap(irb, scope, ir_gen_error_type(irb, scope, node), lval, result_loc);
case NodeTypeBreak:
return ir_lval_wrap(irb, scope, ir_gen_break(irb, scope, node), lval, result_loc);
case NodeTypeContinue:
return ir_lval_wrap(irb, scope, ir_gen_continue(irb, scope, node), lval, result_loc);
case NodeTypeUnreachable:
return ir_build_unreachable(irb, scope, node);
case NodeTypeDefer:
return ir_lval_wrap(irb, scope, ir_gen_defer(irb, scope, node), lval, result_loc);
case NodeTypeSliceExpr:
return ir_gen_slice(irb, scope, node, lval, result_loc);
case NodeTypeCatchExpr:
return ir_gen_catch(irb, scope, node, lval, result_loc);
case NodeTypeContainerDecl:
return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval, result_loc);
case NodeTypeFnProto:
return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval, result_loc);
case NodeTypeErrorSetDecl:
return ir_lval_wrap(irb, scope, ir_gen_err_set_decl(irb, scope, node), lval, result_loc);
case NodeTypeResume:
return ir_lval_wrap(irb, scope, ir_gen_resume(irb, scope, node), lval, result_loc);
case NodeTypeAwaitExpr:
return ir_gen_await_expr(irb, scope, node, lval, result_loc);
case NodeTypeSuspend:
return ir_lval_wrap(irb, scope, ir_gen_suspend(irb, scope, node), lval, result_loc);
case NodeTypeEnumLiteral:
return ir_lval_wrap(irb, scope, ir_gen_enum_literal(irb, scope, node), lval, result_loc);
case NodeTypeInferredArrayType:
add_node_error(irb->codegen, node,
buf_sprintf("inferred array size invalid here"));
return irb->codegen->invalid_instruction;
case NodeTypeVarFieldType:
return ir_lval_wrap(irb, scope,
ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_var), lval, result_loc);
}
zig_unreachable();
}
static ResultLoc *no_result_loc(void) {
ResultLocNone *result_loc_none = allocate<ResultLocNone>(1);
result_loc_none->base.id = ResultLocIdNone;
return &result_loc_none->base;
}
static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval,
ResultLoc *result_loc)
{
if (result_loc == nullptr) {
// Create a result location indicating there is none - but if one gets created
// it will be properly distributed.
result_loc = no_result_loc();
ir_build_reset_result(irb, scope, node, result_loc);
}
Scope *child_scope;
if (irb->exec->is_inline ||
(irb->exec->fn_entry != nullptr && irb->exec->fn_entry->child_scope == scope))
{
child_scope = scope;
} else {
child_scope = &create_expr_scope(irb->codegen, node, scope)->base;
}
IrInstruction *result = ir_gen_node_raw(irb, node, child_scope, lval, result_loc);
if (result == irb->codegen->invalid_instruction) {
if (irb->exec->first_err_trace_msg == nullptr) {
irb->exec->first_err_trace_msg = irb->codegen->trace_err;
}
}
return result;
}
static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope) {
return ir_gen_node_extra(irb, node, scope, LValNone, nullptr);
}
static void invalidate_exec(IrExecutable *exec, ErrorMsg *msg) {
if (exec->first_err_trace_msg != nullptr)
return;
exec->first_err_trace_msg = msg;
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, msg);
}
bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_executable) {
assert(node->owner);
IrBuilder ir_builder = {0};
IrBuilder *irb = &ir_builder;
irb->codegen = codegen;
irb->exec = ir_executable;
irb->main_block_node = node;
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);
IrInstruction *result = ir_gen_node_extra(irb, node, scope, LValNone, nullptr);
if (result == irb->codegen->invalid_instruction)
return false;
if (irb->exec->first_err_trace_msg != nullptr) {
codegen->trace_err = irb->exec->first_err_trace_msg;
return false;
}
if (!instr_is_unreachable(result)) {
ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, result->source_node, result, nullptr));
// no need for save_err_ret_addr because this cannot return error
ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1, "ResultLocReturn");
result_loc_ret->base.id = ResultLocIdReturn;
ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
ir_mark_gen(ir_build_end_expr(irb, scope, node, result, &result_loc_ret->base));
ir_mark_gen(ir_build_return(irb, scope, result->source_node, result));
}
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 ir_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"));
ir_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) {
ErrorMsg *err_msg = add_node_error(codegen, source_node, msg);
invalidate_exec(exec, err_msg);
if (exec->parent_exec) {
ir_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);
}
static void ir_assert(bool ok, IrInstruction *source_instruction) {
if (ok) return;
src_assert(ok, source_instruction->source_node);
}
// 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,
ZigValue *ptr_val)
{
Error err;
assert(ptr_val->type->id == ZigTypeIdPointer);
assert(ptr_val->special == ConstValSpecialStatic);
ZigValue 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;
ZigValue *child_val = const_ptr_pointee_unchecked(codegen, ptr_val);
copy_const_val(child_val, &tmp);
return ErrorNone;
}
ZigValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ZigValue *const_val,
AstNode *source_node)
{
Error err;
ZigValue *val = const_ptr_pointee_unchecked(codegen, const_val);
if (val == nullptr) return nullptr;
assert(const_val->type->id == ZigTypeIdPointer);
ZigType *expected_type = const_val->type->data.pointer.child_type;
if (expected_type == codegen->builtin_types.entry_var) {
return val;
}
switch (type_has_one_possible_value(codegen, expected_type)) {
case OnePossibleValueInvalid:
return nullptr;
case OnePossibleValueNo:
break;
case OnePossibleValueYes:
return get_the_one_possible_value(codegen, expected_type);
}
if (!types_have_same_zig_comptime_repr(codegen, expected_type, val->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 Error ir_exec_scan_for_side_effects(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) {
return ErrorNone;
} else if (ir_has_side_effects(instruction)) {
if (instr_is_comptime(instruction)) {
switch (instruction->id) {
case IrInstructionIdUnwrapErrPayload:
case IrInstructionIdUnionFieldPtr:
continue;
default:
break;
}
}
if (get_scope_typeof(instruction->scope) != nullptr) {
// doesn't count, it's inside a @TypeOf()
continue;
}
exec_add_error_node(codegen, exec, instruction->source_node,
buf_sprintf("unable to evaluate constant expression"));
return ErrorSemanticAnalyzeFail;
}
}
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(ZigValue *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(ZigValue *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, ZigValue *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, ZigValue *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(ZigValue *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(ZigValue *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(ZigValue *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(ZigValue *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(ZigValue *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(ZigValue *dest_val, ZigValue *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(ZigValue *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(ZigValue *op1, ZigValue *op2) {
if (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();
}
}
BigFloat op1_big;
BigFloat op2_big;
float_init_bigfloat(op1, &op1_big);
float_init_bigfloat(op2, &op2_big);
return bigfloat_cmp(&op1_big, &op2_big);
}
// This function cannot handle NaN
static Cmp float_cmp_zero(ZigValue *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(ZigValue *out_val, ZigValue *op1, ZigValue *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(ZigValue *out_val, ZigValue *op1, ZigValue *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(ZigValue *out_val, ZigValue *op1, ZigValue *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(ZigValue *out_val, ZigValue *op1, ZigValue *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(ZigValue *out_val, ZigValue *op1, ZigValue *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(ZigValue *out_val, ZigValue *op1, ZigValue *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(ZigValue *out_val, ZigValue *op1, ZigValue *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(ZigValue *out_val, ZigValue *op1, ZigValue *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(ZigValue *out_val, ZigValue *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(ZigValue *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(ZigValue *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;
}
ZigValue *const_val = ir_resolve_const(ira, instruction, LazyOkNoUndef);
if (const_val == nullptr)
return false;
if (const_val->special == ConstValSpecialLazy) {
switch (const_val->data.x_lazy->id) {
case LazyValueIdAlignOf: {
// This is guaranteed to fit into a u29
if (other_type->id == ZigTypeIdComptimeInt)
return true;
size_t align_bits = get_align_amt_type(ira->codegen)->data.integral.bit_count;
if (other_type->id == ZigTypeIdInt && !other_type->data.integral.is_signed &&
other_type->data.integral.bit_count >= align_bits)
{
return true;
}
break;
}
case LazyValueIdSizeOf: {
// This is guaranteed to fit into a usize
if (other_type->id == ZigTypeIdComptimeInt)
return true;
size_t usize_bits = ira->codegen->builtin_types.entry_usize->data.integral.bit_count;
if (other_type->id == ZigTypeIdInt && !other_type->data.integral.is_signed &&
other_type->data.integral.bit_count >= usize_bits)
{
return true;
}
break;
}
default:
break;
}
}
const_val = ir_resolve_const(ira, instruction, UndefBad);
if (const_val == nullptr)
return false;
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 (const_val_is_int && other_type->id == ZigTypeIdComptimeFloat) {
return true;
}
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 coerced to type '%s'",
num_lit_str,
buf_ptr(val_buf),
buf_ptr(&other_type->name)));
return false;
}
static bool is_tagged_union(ZigType *type) {
if (type->id != ZigTypeIdUnion)
return false;
return (type->data.unionation.decl_node->data.container_decl.auto_enum ||
type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr);
}
static void populate_error_set_table(ErrorTableEntry **errors, ZigType *set) {
assert(set->id == ZigTypeIdErrorSet);
for (uint32_t i = 0; i < set->data.error_set.err_count; i += 1) {
ErrorTableEntry *error_entry = set->data.error_set.errors[i];
assert(errors[error_entry->value] == nullptr);
errors[error_entry->value] = error_entry;
}
}
static ErrorTableEntry *better_documented_error(ErrorTableEntry *preferred, ErrorTableEntry *other) {
if (preferred->decl_node->type == NodeTypeErrorSetField)
return preferred;
if (other->decl_node->type == NodeTypeErrorSetField)
return other;
return preferred;
}
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;
}
size_t errors_count = ira->codegen->errors_by_index.length;
ErrorTableEntry **errors = allocate<ErrorTableEntry *>(errors_count, "ErrorTableEntry *");
populate_error_set_table(errors, set1);
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{");
bool need_comma = false;
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) {
// prefer the one with docs
const char *comma = need_comma ? "," : "";
need_comma = true;
ErrorTableEntry *existing_entry_with_docs = better_documented_error(existing_entry, error_entry);
intersection_list.append(existing_entry_with_docs);
buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&existing_entry_with_docs->name));
}
}
deallocate(errors, errors_count, "ErrorTableEntry *");
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
// sentinel-terminated pointers can coerce into PtrLenUnknown
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) {
bool ok_null_term_ptrs =
wanted_ptr_type->data.pointer.sentinel == nullptr ||
(actual_ptr_type->data.pointer.sentinel != nullptr &&
const_values_equal(ira->codegen, wanted_ptr_type->data.pointer.sentinel,
actual_ptr_type->data.pointer.sentinel));
if (!ok_null_term_ptrs) {
result.id = ConstCastResultIdPtrSentinel;
result.data.bad_ptr_sentinel = allocate_nonzero<ConstCastPtrSentinel>(1);
result.data.bad_ptr_sentinel->wanted_type = wanted_ptr_type;
result.data.bad_ptr_sentinel->actual_type = actual_ptr_type;
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)) {
result.id = ConstCastResultIdPtrLens;
return result;
}
bool ok_cv_qualifiers =
(!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);
if (!ok_cv_qualifiers) {
result.id = ConstCastResultIdCV;
result.data.bad_cv = allocate_nonzero<ConstCastBadCV>(1);
result.data.bad_cv->wanted_type = wanted_ptr_type;
result.data.bad_cv->actual_type = actual_ptr_type;
return result;
}
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;
}
if ((err = type_resolve(g, wanted_type, ResolveStatusZeroBitsKnown))) {
result.id = ConstCastResultIdInvalid;
return result;
}
if ((err = type_resolve(g, actual_type, ResolveStatusZeroBitsKnown))) {
result.id = ConstCastResultIdInvalid;
return result;
}
if (type_has_bits(wanted_type) == type_has_bits(actual_type) &&
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;
}
}
// arrays
if (wanted_type->id == ZigTypeIdArray && actual_type->id == ZigTypeIdArray &&
wanted_type->data.array.len == actual_type->data.array.len)
{
ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.array.child_type,
actual_type->data.array.child_type, source_node, wanted_is_mutable);
if (child.id == ConstCastResultIdInvalid)
return child;
if (child.id != ConstCastResultIdOk) {
result.id = ConstCastResultIdArrayChild;
result.data.array_mismatch = allocate_nonzero<ConstCastArrayMismatch>(1);
result.data.array_mismatch->child = child;
result.data.array_mismatch->wanted_child = wanted_type->data.array.child_type;
result.data.array_mismatch->actual_child = actual_type->data.array.child_type;
return result;
}
bool ok_null_terminated = (wanted_type->data.array.sentinel == nullptr) ||
(actual_type->data.array.sentinel != nullptr &&
const_values_equal(ira->codegen, wanted_type->data.array.sentinel, actual_type->data.array.sentinel));
if (!ok_null_terminated) {
result.id = ConstCastResultIdSentinelArrays;
result.data.sentinel_arrays = allocate_nonzero<ConstCastBadNullTermArrays>(1);
result.data.sentinel_arrays->child = child;
result.data.sentinel_arrays->wanted_type = wanted_type;
result.data.sentinel_arrays->actual_type = actual_type;
return result;
}
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;
}
bool ok_sentinels =
wanted_ptr_type->data.pointer.sentinel == nullptr ||
(actual_ptr_type->data.pointer.sentinel != nullptr &&
const_values_equal(ira->codegen, wanted_ptr_type->data.pointer.sentinel,
actual_ptr_type->data.pointer.sentinel));
if (!ok_sentinels) {
result.id = ConstCastResultIdPtrSentinel;
result.data.bad_ptr_sentinel = allocate_nonzero<ConstCastPtrSentinel>(1);
result.data.bad_ptr_sentinel->wanted_type = wanted_ptr_type;
result.data.bad_ptr_sentinel->actual_type = actual_ptr_type;
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 && container_set->data.error_set.incomplete) {
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;
}
size_t errors_count = g->errors_by_index.length;
ErrorTableEntry **errors = allocate<ErrorTableEntry *>(errors_count, "ErrorTableEntry *");
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);
}
}
deallocate(errors, errors_count, "ErrorTableEntry *");
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.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.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;
}
}
if (wanted_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) {
// ConstCastResultIdFnCC is guaranteed to be the last one reported, meaning everything else is ok.
result.id = ConstCastResultIdFnCC;
return result;
}
return result;
}
if (wanted_type->id == ZigTypeIdInt && actual_type->id == ZigTypeIdInt) {
result.id = ConstCastResultIdIntShorten;
result.data.int_shorten = allocate_nonzero<ConstCastIntShorten>(1);
result.data.int_shorten->wanted_type = wanted_type;
result.data.int_shorten->actual_type = actual_type;
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;
size_t i = 0;
for (;;) {
prev_inst = instructions[i];
if (type_is_invalid(prev_inst->value->type)) {
return ira->codegen->builtin_types.entry_invalid;
}
if (prev_inst->value->type->id == ZigTypeIdUnreachable) {
i += 1;
if (i == instruction_count) {
return prev_inst->value->type;
}
continue;
}
break;
}
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 (; 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 == cur_type) {
continue;
}
if (prev_type->id == ZigTypeIdUnreachable) {
prev_inst = cur_inst;
continue;
}
if (cur_type->id == ZigTypeIdUnreachable) {
continue;
}
if (prev_type->id == ZigTypeIdErrorSet) {
ir_assert(err_set_type != nullptr, prev_inst);
if (cur_type->id == ZigTypeIdErrorSet) {
if (type_is_global_error_set(err_set_type)) {
continue;
}
bool allow_infer = cur_type->data.error_set.infer_fn != nullptr &&
cur_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry;
if (!allow_infer && !resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) {
return ira->codegen->builtin_types.entry_invalid;
}
if (!allow_infer && 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, nullptr);
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;
bool allow_infer = cur_err_set_type->data.error_set.infer_fn != nullptr &&
cur_err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry;
if (!allow_infer && !resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) {
return ira->codegen->builtin_types.entry_invalid;
}
if (!allow_infer && 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, nullptr);
prev_inst = cur_inst;
assert(errors != nullptr);
continue;
} else {
prev_inst = cur_inst;
continue;
}
}
if (cur_type->id == ZigTypeIdErrorSet) {
bool allow_infer = cur_type->data.error_set.infer_fn != nullptr &&
cur_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry;
if (!allow_infer && !resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) {
return ira->codegen->builtin_types.entry_invalid;
}
if (!allow_infer && 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) {
bool allow_infer = false;
if (prev_type->id == ZigTypeIdErrorUnion) {
err_set_type = prev_type->data.error_union.err_set_type;
allow_infer = err_set_type->data.error_set.infer_fn != nullptr &&
err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry;
} else {
err_set_type = cur_type;
}
if (!allow_infer && !resolve_inferred_error_set(ira->codegen, err_set_type, cur_inst->source_node)) {
return ira->codegen->builtin_types.entry_invalid;
}
if (!allow_infer && type_is_global_error_set(err_set_type)) {
err_set_type = ira->codegen->builtin_types.entry_global_error_set;
continue;
}
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;
}
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, nullptr);
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 (prev_err_set_type == cur_err_set_type)
continue;
bool allow_infer_prev = prev_err_set_type->data.error_set.infer_fn != nullptr &&
prev_err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry;
bool allow_infer_cur = cur_err_set_type->data.error_set.infer_fn != nullptr &&
cur_err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry;
if (!allow_infer_prev && !resolve_inferred_error_set(ira->codegen, prev_err_set_type, cur_inst->source_node)) {
return ira->codegen->builtin_types.entry_invalid;
}
if (!allow_infer_cur && !resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) {
return ira->codegen->builtin_types.entry_invalid;
}
if ((!allow_infer_prev && type_is_global_error_set(prev_err_set_type)) ||
(!allow_infer_cur && 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, nullptr);
continue;
}
}
if (prev_type->id == ZigTypeIdNull) {
prev_inst = cur_inst;
any_are_null = true;
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 (is_tagged_union(prev_type) && cur_type->id == ZigTypeIdEnumLiteral) {
TypeUnionField *field = find_union_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 (is_tagged_union(cur_type) && prev_type->id == ZigTypeIdEnumLiteral) {
TypeUnionField *field = find_union_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;
bool allow_infer = cur_err_set_type->data.error_set.infer_fn != nullptr &&
cur_err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry;
if (!allow_infer && !resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) {
return ira->codegen->builtin_types.entry_invalid;
}
if ((!allow_infer && 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, nullptr);
}
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;
}
}
// *[N]T to []T
// *[N]T to E![]T
if (cur_type->id == ZigTypeIdPointer &&
cur_type->data.pointer.ptr_len == PtrLenSingle &&
cur_type->data.pointer.child_type->id == ZigTypeIdArray &&
((prev_type->id == ZigTypeIdErrorUnion && is_slice(prev_type->data.error_union.payload_type)) ||
is_slice(prev_type)))
{
ZigType *array_type = cur_type->data.pointer.child_type;
ZigType *slice_type = (prev_type->id == ZigTypeIdErrorUnion) ?
prev_type->data.error_union.payload_type : prev_type;
ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry;
if ((slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 ||
!cur_type->data.pointer.is_const) &&
types_match_const_cast_only(ira,
slice_ptr_type->data.pointer.child_type,
array_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk)
{
convert_to_const_slice = false;
continue;
}
}
// *[N]T to []T
// *[N]T to E![]T
if (prev_type->id == ZigTypeIdPointer &&
prev_type->data.pointer.child_type->id == ZigTypeIdArray &&
prev_type->data.pointer.ptr_len == PtrLenSingle &&
((cur_type->id == ZigTypeIdErrorUnion && is_slice(cur_type->data.error_union.payload_type)) ||
is_slice(cur_type)))
{
ZigType *array_type = prev_type->data.pointer.child_type;
ZigType *slice_type = (cur_type->id == ZigTypeIdErrorUnion) ?
cur_type->data.error_union.payload_type : cur_type;
ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry;
if ((slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 ||
!prev_type->data.pointer.is_const) &&
types_match_const_cast_only(ira,
slice_ptr_type->data.pointer.child_type,
array_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk)
{
prev_inst = cur_inst;
convert_to_const_slice = false;
continue;
}
}
// *[N]T and *[M]T
if (cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenSingle &&
cur_type->data.pointer.child_type->id == ZigTypeIdArray &&
prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenSingle &&
prev_type->data.pointer.child_type->id == ZigTypeIdArray &&
(cur_type->data.pointer.is_const || !prev_type->data.pointer.is_const ||
prev_type->data.pointer.child_type->data.array.len == 0) &&
(
prev_type->data.pointer.child_type->data.array.sentinel == nullptr ||
(cur_type->data.pointer.child_type->data.array.sentinel != nullptr &&
const_values_equal(ira->codegen, prev_type->data.pointer.child_type->data.array.sentinel,
cur_type->data.pointer.child_type->data.array.sentinel))
) &&
types_match_const_cast_only(ira,
cur_type->data.pointer.child_type->data.array.child_type,
prev_type->data.pointer.child_type->data.array.child_type,
source_node, !cur_type->data.pointer.is_const).id == ConstCastResultIdOk)
{
prev_inst = cur_inst;
convert_to_const_slice = true;
continue;
}
if (prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenSingle &&
prev_type->data.pointer.child_type->id == ZigTypeIdArray &&
cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenSingle &&
cur_type->data.pointer.child_type->id == ZigTypeIdArray &&
(prev_type->data.pointer.is_const || !cur_type->data.pointer.is_const ||
cur_type->data.pointer.child_type->data.array.len == 0) &&
(
cur_type->data.pointer.child_type->data.array.sentinel == nullptr ||
(prev_type->data.pointer.child_type->data.array.sentinel != nullptr &&
const_values_equal(ira->codegen, cur_type->data.pointer.child_type->data.array.sentinel,
prev_type->data.pointer.child_type->data.array.sentinel))
) &&
types_match_const_cast_only(ira,
prev_type->data.pointer.child_type->data.array.child_type,
cur_type->data.pointer.child_type->data.array.child_type,
source_node, !prev_type->data.pointer.is_const).id == ConstCastResultIdOk)
{
convert_to_const_slice = true;
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) {
if (prev_inst->value->type->id == ZigTypeIdPointer) {
ZigType *array_type = prev_inst->value->type->data.pointer.child_type;
src_assert(array_type->id == ZigTypeIdArray, source_node);
ZigType *ptr_type = get_pointer_to_type_extra2(
ira->codegen, array_type->data.array.child_type,
prev_inst->value->type->data.pointer.is_const, false,
PtrLenUnknown,
0, 0, 0, false,
VECTOR_INDEX_NONE, nullptr, array_type->data.array.sentinel);
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 {
zig_unreachable();
}
} 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 == ZigTypeIdOptional) {
return prev_inst->value->type;
} else {
if ((err = type_resolve(ira->codegen, prev_inst->value->type, ResolveStatusSizeKnown)))
return ira->codegen->builtin_types.entry_invalid;
return get_optional_type(ira->codegen, prev_inst->value->type);
}
} else {
return prev_inst->value->type;
}
}
static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_instr,
CastOp cast_op,
ZigValue *other_val, ZigType *other_type,
ZigValue *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:
zig_panic("TODO");
case CastOpNoop: {
copy_const_val(const_val, other_val);
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_const_noval(IrAnalyze *ira, IrInstruction *old_instruction) {
IrInstructionConst *const_instruction = ir_create_instruction_noval<IrInstructionConst>(&ira->new_irb,
old_instruction->scope, old_instruction->source_node);
return &const_instruction->base;
}
// This function initializes the new IrInstruction with the provided ZigValue,
// rather than creating a new one.
static IrInstruction *ir_const_move(IrAnalyze *ira, IrInstruction *old_instruction, ZigValue *val) {
IrInstruction *result = ir_const_noval(ira, old_instruction);
result->value = val;
return result;
}
static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
ZigType *wanted_type, CastOp cast_op)
{
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;
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)) {
ZigValue *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;
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 *array_ptr, ZigType *wanted_type, ResultLoc *result_loc)
{
Error err;
if ((err = type_resolve(ira->codegen, array_ptr->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, array_ptr->value->type));
if (instr_is_comptime(array_ptr)) {
ZigValue *array_ptr_val = ir_resolve_const(ira, array_ptr, UndefBad);
if (array_ptr_val == nullptr)
return ira->codegen->invalid_instruction;
ZigValue *pointee = const_ptr_pointee(ira, ira->codegen, array_ptr_val, source_instr->source_node);
if (pointee == nullptr)
return ira->codegen->invalid_instruction;
if (pointee->special != ConstValSpecialRuntime) {
assert(array_ptr_val->type->id == ZigTypeIdPointer);
ZigType *array_type = array_ptr_val->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 = array_ptr_val->data.x_ptr.mut;
result->value->type = wanted_type;
return result;
}
}
if (result_loc == nullptr) result_loc = no_result_loc();
IrInstruction *result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type,
nullptr, true, true);
if (type_is_invalid(result_loc_inst->value->type) || instr_is_unreachable(result_loc_inst)) {
return result_loc_inst;
}
return ir_build_ptr_of_array_to_slice(ira, source_instr, wanted_type, array_ptr, result_loc_inst);
}
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) {
ir_assert(!old_bb->suspended, (old_bb->instruction_list.length != 0) ? old_bb->instruction_list.at(0) : nullptr);
ira->instruction_index = 0;
ira->old_irb.current_basic_block = old_bb;
ira->const_predecessor_bb = const_predecessor_bb;
ira->old_bb_index = old_bb->index;
}
static IrInstruction *ira_suspend(IrAnalyze *ira, IrInstruction *old_instruction, IrBasicBlock *next_bb,
IrSuspendPosition *suspend_pos)
{
if (ira->codegen->verbose_ir) {
fprintf(stderr, "suspend %s_%zu %s_%zu #%" PRIu32 " (%zu,%zu)\n",
ira->old_irb.current_basic_block->name_hint,
ira->old_irb.current_basic_block->debug_id,
ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->name_hint,
ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->debug_id,
ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index)->debug_id,
ira->old_bb_index, ira->instruction_index);
}
suspend_pos->basic_block_index = ira->old_bb_index;
suspend_pos->instruction_index = ira->instruction_index;
ira->old_irb.current_basic_block->suspended = true;
// null next_bb means that the caller plans to call ira_resume before returning
if (next_bb != nullptr) {
ira->old_bb_index = next_bb->index;
ira->old_irb.current_basic_block = ira->old_irb.exec->basic_block_list.at(ira->old_bb_index);
assert(ira->old_irb.current_basic_block == next_bb);
ira->instruction_index = 0;
ira->const_predecessor_bb = nullptr;
next_bb->other = ir_get_new_bb_runtime(ira, next_bb, old_instruction);
ira->new_irb.current_basic_block = next_bb->other;
}
return ira->codegen->unreach_instruction;
}
static IrInstruction *ira_resume(IrAnalyze *ira) {
IrSuspendPosition pos = ira->resume_stack.pop();
if (ira->codegen->verbose_ir) {
fprintf(stderr, "resume (%zu,%zu) ", pos.basic_block_index, pos.instruction_index);
}
ira->old_bb_index = pos.basic_block_index;
ira->old_irb.current_basic_block = ira->old_irb.exec->basic_block_list.at(ira->old_bb_index);
assert(ira->old_irb.current_basic_block->in_resume_stack);
ira->old_irb.current_basic_block->in_resume_stack = false;
ira->old_irb.current_basic_block->suspended = false;
ira->instruction_index = pos.instruction_index;
assert(pos.instruction_index < ira->old_irb.current_basic_block->instruction_list.length);
if (ira->codegen->verbose_ir) {
fprintf(stderr, "%s_%zu #%" PRIu32 "\n", ira->old_irb.current_basic_block->name_hint,
ira->old_irb.current_basic_block->debug_id,
ira->old_irb.current_basic_block->instruction_list.at(pos.instruction_index)->debug_id);
}
ira->const_predecessor_bb = nullptr;
ira->new_irb.current_basic_block = ira->old_irb.current_basic_block->other;
assert(ira->new_irb.current_basic_block != nullptr);
return ira->codegen->unreach_instruction;
}
static void ir_start_next_bb(IrAnalyze *ira) {
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 && old_bb->suspend_instruction_ref == nullptr) {
ira->old_bb_index += 1;
continue;
}
// if it's already started, or
// if it's a suspended block,
// then skip it
if (old_bb->suspended ||
(old_bb->other != nullptr && old_bb->other->instruction_list.length != 0) ||
(old_bb->other != nullptr && old_bb->other->already_appended))
{
ira->old_bb_index += 1;
continue;
}
// if there is a resume_stack, pop one from there rather than moving on.
// the last item of the resume stack will be a basic block that will
// move on to the next one below
if (ira->resume_stack.length != 0) {
ira_resume(ira);
return;
}
if (old_bb->other == nullptr) {
old_bb->other = ir_get_new_bb_runtime(ira, old_bb, old_bb->suspend_instruction_ref);
}
ira->new_irb.current_basic_block = old_bb->other;
ir_start_bb(ira, old_bb, nullptr);
return;
}
if (!need_repeat) {
if (ira->resume_stack.length != 0) {
ira_resume(ira);
}
return;
}
need_repeat = false;
ira->old_bb_index = 0;
continue;
}
}
static void ir_finish_bb(IrAnalyze *ira) {
if (!ira->new_irb.current_basic_block->already_appended) {
ira->new_irb.current_basic_block->already_appended = true;
if (ira->codegen->verbose_ir) {
fprintf(stderr, "append new bb %s_%zu\n", ira->new_irb.current_basic_block->name_hint,
ira->new_irb.current_basic_block->debug_id);
}
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;
}
ir_start_next_bb(ira);
}
static IrInstruction *ir_unreach_error(IrAnalyze *ira) {
ira->old_bb_index = SIZE_MAX;
if (ira->new_irb.exec->first_err_trace_msg == nullptr) {
ira->new_irb.exec->first_err_trace_msg = ira->codegen->trace_err;
}
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_unreachable(IrAnalyze *ira, IrInstruction *source_instruction) {
IrInstruction *result = ir_const_noval(ira, source_instruction);
result->value = ira->codegen->intern.for_unreachable();
return result;
}
static IrInstruction *ir_const_void(IrAnalyze *ira, IrInstruction *source_instruction) {
IrInstruction *result = ir_const_noval(ira, source_instruction);
result->value = ira->codegen->intern.for_void();
return result;
}
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,
ZigValue *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);
ZigValue *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 Error ir_resolve_const_val(CodeGen *codegen, IrExecutable *exec, AstNode *source_node,
ZigValue *val, UndefAllowed undef_allowed)
{
Error err;
for (;;) {
switch (val->special) {
case ConstValSpecialStatic:
return ErrorNone;
case ConstValSpecialRuntime:
if (!type_has_bits(val->type))
return ErrorNone;
exec_add_error_node(codegen, exec, source_node,
buf_sprintf("unable to evaluate constant expression"));
return ErrorSemanticAnalyzeFail;
case ConstValSpecialUndef:
if (undef_allowed == UndefOk || undef_allowed == LazyOk)
return ErrorNone;
exec_add_error_node(codegen, exec, source_node,
buf_sprintf("use of undefined value here causes undefined behavior"));
return ErrorSemanticAnalyzeFail;
case ConstValSpecialLazy:
if (undef_allowed == LazyOk || undef_allowed == LazyOkNoUndef)
return ErrorNone;
if ((err = ir_resolve_lazy(codegen, source_node, val)))
return err;
continue;
}
}
}
static ZigValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed) {
Error err;
if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, value->source_node,
value->value, undef_allowed)))
{
return nullptr;
}
return value->value;
}
Error ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
ZigValue *return_ptr, 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, UndefAllowed undef_allowed)
{
Error err;
src_assert(return_ptr->type->id == ZigTypeIdPointer, source_node);
if (type_is_invalid(return_ptr->type))
return ErrorSemanticAnalyzeFail;
IrExecutable *ir_executable = allocate<IrExecutable>(1, "IrExecutablePass1");
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;
if (!ir_gen(codegen, node, scope, ir_executable))
return ErrorSemanticAnalyzeFail;
if (ir_executable->first_err_trace_msg != nullptr) {
codegen->trace_err = ir_executable->first_err_trace_msg;
return ErrorSemanticAnalyzeFail;
}
if (codegen->verbose_ir) {
fprintf(stderr, "\nSource: ");
ast_render(stderr, node, 4);
fprintf(stderr, "\n{ // (IR)\n");
ir_print(codegen, stderr, ir_executable, 2, IrPassSrc);
fprintf(stderr, "}\n");
}
IrExecutable *analyzed_executable = allocate<IrExecutable>(1, "IrExecutablePass2");
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,
return_ptr->type->data.pointer.child_type, expected_type_source_node, return_ptr);
if (type_is_invalid(result_type)) {
return ErrorSemanticAnalyzeFail;
}
if (codegen->verbose_ir) {
fprintf(stderr, "{ // (analyzed)\n");
ir_print(codegen, stderr, analyzed_executable, 2, IrPassGen);
fprintf(stderr, "}\n");
}
if ((err = ir_exec_scan_for_side_effects(codegen, analyzed_executable)))
return err;
ZigValue *result = const_ptr_pointee(nullptr, codegen, return_ptr, source_node);
if (result == nullptr)
return ErrorSemanticAnalyzeFail;
if ((err = ir_resolve_const_val(codegen, analyzed_executable, node, result, undef_allowed)))
return err;
return ErrorNone;
}
static ErrorTableEntry *ir_resolve_error(IrAnalyze *ira, IrInstruction *err_value) {
if (type_is_invalid(err_value->value->type))
return nullptr;
if (err_value->value->type->id != ZigTypeIdErrorSet) {
ir_add_error(ira, err_value,
buf_sprintf("expected error, found '%s'", buf_ptr(&err_value->value->type->name)));
return nullptr;
}
ZigValue *const_val = ir_resolve_const(ira, err_value, UndefBad);
if (!const_val)
return nullptr;
assert(const_val->data.x_err_set != nullptr);
return const_val->data.x_err_set;
}
static ZigType *ir_resolve_const_type(CodeGen *codegen, IrExecutable *exec, AstNode *source_node,
ZigValue *val)
{
Error err;
if ((err = ir_resolve_const_val(codegen, exec, source_node, val, UndefBad)))
return codegen->builtin_types.entry_invalid;
assert(val->data.x_type != nullptr);
return val->data.x_type;
}
static ZigValue *ir_resolve_type_lazy(IrAnalyze *ira, IrInstruction *type_value) {
if (type_is_invalid(type_value->value->type))
return nullptr;
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 nullptr;
}
Error err;
if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, type_value->source_node,
type_value->value, LazyOk)))
{
return nullptr;
}
return type_value->value;
}
static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value) {
ZigValue *val = ir_resolve_type_lazy(ira, type_value);
if (val == nullptr)
return ira->codegen->builtin_types.entry_invalid;
return ir_resolve_const_type(ira->codegen, ira->new_irb.exec, type_value->source_node, val);
}
static Error ir_validate_vector_elem_type(IrAnalyze *ira, IrInstruction *source_instr, ZigType *elem_type) {
if (!is_valid_vector_elem_type(elem_type)) {
ir_add_error(ira, source_instr,
buf_sprintf("vector element type must be integer, float, bool, or pointer; '%s' is invalid",
buf_ptr(&elem_type->name)));
return ErrorSemanticAnalyzeFail;
}
return ErrorNone;
}
static ZigType *ir_resolve_vector_elem_type(IrAnalyze *ira, IrInstruction *elem_type_value) {
Error err;
ZigType *elem_type = ir_resolve_type(ira, elem_type_value);
if (type_is_invalid(elem_type))
return ira->codegen->builtin_types.entry_invalid;
if ((err = ir_validate_vector_elem_type(ira, elem_type_value, elem_type)))
return ira->codegen->builtin_types.entry_invalid;
return elem_type;
}
static ZigType *ir_resolve_int_type(IrAnalyze *ira, IrInstruction *type_value) {
ZigType *ty = ir_resolve_type(ira, type_value);
if (type_is_invalid(ty))
return ira->codegen->builtin_types.entry_invalid;
if (ty->id != ZigTypeIdInt) {
ErrorMsg *msg = ir_add_error(ira, type_value,
buf_sprintf("expected integer type, found '%s'", buf_ptr(&ty->name)));
if (ty->id == ZigTypeIdVector &&
ty->data.vector.elem_type->id == ZigTypeIdInt)
{
add_error_note(ira->codegen, msg, type_value->source_node,
buf_sprintf("represent vectors with their element types, i.e. '%s'",
buf_ptr(&ty->data.vector.elem_type->name)));
}
return ira->codegen->builtin_types.entry_invalid;
}
return ty;
}
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;
}
ZigValue *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;
}
ZigValue *const_val = ir_resolve_const(ira, fn_value, UndefBad);
if (!const_val)
return nullptr;
// May be a ConstPtrSpecialHardCodedAddr
if (const_val->data.x_ptr.special != ConstPtrSpecialFunction)
return nullptr;
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, ResultLoc *result_loc)
{
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;
ZigValue *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(ira->codegen, wanted_type, payload_type)) {
copy_const_val(const_instruction->base.value, val);
} else {
const_instruction->base.value->data.x_optional = val;
}
const_instruction->base.value->type = wanted_type;
return &const_instruction->base;
}
if (result_loc == nullptr && handle_is_ptr(wanted_type)) {
result_loc = no_result_loc();
}
IrInstruction *result_loc_inst = nullptr;
if (result_loc != nullptr) {
result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, true);
if (type_is_invalid(result_loc_inst->value->type) || instr_is_unreachable(result_loc_inst)) {
return result_loc_inst;
}
}
IrInstruction *result = ir_build_optional_wrap(ira, source_instr, wanted_type, value, result_loc_inst);
result->value->data.rh_maybe = RuntimeHintOptionalNonNull;
return result;
}
static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *value, ZigType *wanted_type, ResultLoc *result_loc)
{
assert(wanted_type->id == ZigTypeIdErrorUnion);
ZigType *payload_type = wanted_type->data.error_union.payload_type;
ZigType *err_set_type = wanted_type->data.error_union.err_set_type;
if (instr_is_comptime(value)) {
IrInstruction *casted_payload = ir_implicit_cast(ira, value, payload_type);
if (type_is_invalid(casted_payload->value->type))
return ira->codegen->invalid_instruction;
ZigValue *val = ir_resolve_const(ira, casted_payload, UndefOk);
if (val == nullptr)
return ira->codegen->invalid_instruction;
ZigValue *err_set_val = create_const_vals(1);
err_set_val->type = 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_loc_inst;
if (handle_is_ptr(wanted_type)) {
if (result_loc == nullptr) result_loc = no_result_loc();
result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, true);
if (type_is_invalid(result_loc_inst->value->type) || instr_is_unreachable(result_loc_inst)) {
return result_loc_inst;
}
} else {
result_loc_inst = nullptr;
}
IrInstruction *result = ir_build_err_wrap_payload(ira, source_instr, wanted_type, value, result_loc_inst);
result->value->data.rh_error_union = RuntimeHintErrorUnionNonError;
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)) {
ZigValue *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_frame_ptr_to_anyframe(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *frame_ptr, ZigType *wanted_type)
{
if (instr_is_comptime(frame_ptr)) {
ZigValue *ptr_val = ir_resolve_const(ira, frame_ptr, UndefBad);
if (ptr_val == nullptr)
return ira->codegen->invalid_instruction;
ir_assert(ptr_val->type->id == ZigTypeIdPointer, source_instr);
if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) {
zig_panic("TODO comptime frame pointer");
}
}
IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node,
wanted_type, frame_ptr, CastOpBitCast);
result->value->type = wanted_type;
return result;
}
static IrInstruction *ir_analyze_anyframe_to_anyframe(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *value, ZigType *wanted_type)
{
if (instr_is_comptime(value)) {
zig_panic("TODO comptime anyframe->T to anyframe");
}
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_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
ZigType *wanted_type, ResultLoc *result_loc)
{
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)) {
ZigValue *val = ir_resolve_const(ira, casted_value, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
ZigValue *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_loc_inst;
if (handle_is_ptr(wanted_type)) {
if (result_loc == nullptr) result_loc = no_result_loc();
result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, true);
if (type_is_invalid(result_loc_inst->value->type) || instr_is_unreachable(result_loc_inst)) {
return result_loc_inst;
}
} else {
result_loc_inst = nullptr;
}
IrInstruction *result = ir_build_err_wrap_code(ira, source_instr, wanted_type, value, result_loc_inst);
result->value->data.rh_error_union = RuntimeHintErrorUnionError;
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));
ZigValue *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));
ZigValue *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 (instr_is_comptime(value)) {
ZigValue *val = ir_resolve_const(ira, value, LazyOk);
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);
if ((err = type_resolve(ira->codegen, ptr_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
IrInstruction *result_loc;
if (type_has_bits(ptr_type) && !handle_is_ptr(value->value->type)) {
result_loc = ir_resolve_result(ira, source_instruction, no_result_loc(), value->value->type,
nullptr, true, true);
} else {
result_loc = nullptr;
}
IrInstruction *new_instruction = ir_build_ref_gen(ira, source_instruction, ptr_type, value, result_loc);
new_instruction->value->data.rh_ptr = RuntimeHintPtrStack;
return new_instruction;
}
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)
{
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)) {
ZigValue *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)) {
ZigValue *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);
result->value->special = ConstValSpecialUndef;
return result;
}
static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *uncasted_target, ZigType *wanted_type)
{
Error err;
assert(wanted_type->id == ZigTypeIdUnion);
if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
IrInstruction *target = ir_implicit_cast(ira, uncasted_target, wanted_type->data.unionation.tag_type);
if (type_is_invalid(target->value->type))
return ira->codegen->invalid_instruction;
if (instr_is_comptime(target)) {
ZigValue *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);
ZigType *field_type = resolve_union_field_type(ira->codegen, union_field);
if (field_type == nullptr)
return ira->codegen->invalid_instruction;
if ((err = type_resolve(ira->codegen, field_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
switch (type_has_one_possible_value(ira->codegen, field_type)) {
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(&field_type->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 = field_type;
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];
ZigType *field_type = resolve_union_field_type(ira->codegen, union_field);
if (field_type == nullptr)
return ira->codegen->invalid_instruction;
bool has_bits;
if ((err = type_has_bits2(ira->codegen, field_type, &has_bits)))
return ira->codegen->invalid_instruction;
if (has_bits) {
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(&field_type->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)) {
ZigValue *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 = type_resolve(ira->codegen, wanted_type, ResolveStatusSizeKnown)))
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)) {
ZigValue *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 && wanted_type->data.enumeration.layout != ContainerLayoutExtern) {
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)
{
ZigValue *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)) {
ZigValue *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_usize(&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)) {
ZigValue *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)) {
ZigValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
assert(val->type->id == ZigTypeIdPointer);
ZigValue *pointee = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node);
if (pointee == nullptr)
return ira->codegen->invalid_instruction;
if (pointee->special != ConstValSpecialRuntime) {
ZigValue *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, ast_field_to_symbol_node(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 ConstCastResultIdPtrLens: {
add_error_note(ira->codegen, parent_msg, source_node,
buf_sprintf("pointer length mismatch"));
break;
}
case ConstCastResultIdPtrSentinel: {
ZigType *actual_type = cast_result->data.bad_ptr_sentinel->actual_type;
ZigType *wanted_type = cast_result->data.bad_ptr_sentinel->wanted_type;
{
Buf *txt_msg = buf_sprintf("destination pointer requires a terminating '");
render_const_value(ira->codegen, txt_msg, wanted_type->data.pointer.sentinel);
buf_appendf(txt_msg, "' sentinel");
if (actual_type->data.pointer.sentinel != nullptr) {
buf_appendf(txt_msg, ", but source pointer has a terminating '");
render_const_value(ira->codegen, txt_msg, actual_type->data.pointer.sentinel);
buf_appendf(txt_msg, "' sentinel");
}
add_error_note(ira->codegen, parent_msg, source_node, txt_msg);
}
break;
}
case ConstCastResultIdSentinelArrays: {
ZigType *actual_type = cast_result->data.sentinel_arrays->actual_type;
ZigType *wanted_type = cast_result->data.sentinel_arrays->wanted_type;
Buf *txt_msg = buf_sprintf("destination array requires a terminating '");
render_const_value(ira->codegen, txt_msg, wanted_type->data.array.sentinel);
buf_appendf(txt_msg, "' sentinel");
if (actual_type->data.array.sentinel != nullptr) {
buf_appendf(txt_msg, ", but source array has a terminating '");
render_const_value(ira->codegen, txt_msg, actual_type->data.array.sentinel);
buf_appendf(txt_msg, "' sentinel");
}
add_error_note(ira->codegen, parent_msg, source_node, txt_msg);
break;
}
case ConstCastResultIdCV: {
ZigType *wanted_type = cast_result->data.bad_cv->wanted_type;
ZigType *actual_type = cast_result->data.bad_cv->actual_type;
bool ok_const = !actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const;
bool ok_volatile = !actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile;
if (!ok_const) {
add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("cast discards const qualifier"));
} else if (!ok_volatile) {
add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("cast discards volatile qualifier"));
} else {
zig_unreachable();
}
break;
}
case ConstCastResultIdFnIsGeneric:
add_error_note(ira->codegen, parent_msg, source_node,
buf_sprintf("only one of the functions is generic"));
break;
case ConstCastResultIdFnCC:
add_error_note(ira->codegen, parent_msg, source_node,
buf_sprintf("calling convention mismatch"));
break;
case ConstCastResultIdIntShorten: {
ZigType *wanted_type = cast_result->data.int_shorten->wanted_type;
ZigType *actual_type = cast_result->data.int_shorten->actual_type;
const char *wanted_signed = wanted_type->data.integral.is_signed ? "signed" : "unsigned";
const char *actual_signed = actual_type->data.integral.is_signed ? "signed" : "unsigned";
add_error_note(ira->codegen, parent_msg, source_node,
buf_sprintf("%s %" PRIu32 "-bit int cannot represent all possible %s %" PRIu32 "-bit values",
wanted_signed, wanted_type->data.integral.bit_count,
actual_signed, actual_type->data.integral.bit_count));
break;
}
case ConstCastResultIdFnAlign: // TODO
case ConstCastResultIdFnVarArgs: // TODO
case ConstCastResultIdFnReturnType: // TODO
case ConstCastResultIdFnArgCount: // TODO
case ConstCastResultIdFnGenericArgCount: // TODO
case ConstCastResultIdFnArgNoAlias: // TODO
case ConstCastResultIdUnresolvedInferredErrSet: // TODO
case ConstCastResultIdAsyncAllocatorType: // TODO
case ConstCastResultIdArrayChild: // 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 ZigValue representation
IrInstruction *result = ir_const(ira, source_instr, vector_type);
copy_const_val(result->value, array->value);
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, ResultLoc *result_loc)
{
if (instr_is_comptime(vector)) {
// arrays and vectors have the same ZigValue representation
IrInstruction *result = ir_const(ira, source_instr, array_type);
copy_const_val(result->value, vector->value);
result->value->type = array_type;
return result;
}
if (result_loc == nullptr) {
result_loc = no_result_loc();
}
IrInstruction *result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, array_type, nullptr,
true, true);
if (type_is_invalid(result_loc_inst->value->type) || instr_is_unreachable(result_loc_inst)) {
return result_loc_inst;
}
return ir_build_vector_to_array(ira, source_instr, array_type, vector, result_loc_inst);
}
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 bool is_pointery_and_elem_is_not_pointery(ZigType *ty) {
if (ty->id == ZigTypeIdPointer) return ty->data.pointer.child_type->id != ZigTypeIdPointer;
if (ty->id == ZigTypeIdFn) return true;
if (ty->id == ZigTypeIdOptional) {
ZigType *ptr_ty = ty->data.maybe.child_type;
if (ptr_ty->id == ZigTypeIdPointer) return ptr_ty->data.pointer.child_type->id != ZigTypeIdPointer;
if (ptr_ty->id == ZigTypeIdFn) return true;
}
return false;
}
static IrInstruction *ir_analyze_enum_literal(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
ZigType *enum_type)
{
assert(enum_type->id == ZigTypeIdEnum);
Error err;
if ((err = type_resolve(ira->codegen, enum_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
TypeEnumField *field = find_enum_type_field(enum_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(&enum_type->name), buf_ptr(value->value->data.x_enum_literal)));
add_error_note(ira->codegen, msg, enum_type->data.enumeration.decl_node,
buf_sprintf("'%s' declared here", buf_ptr(&enum_type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *result = ir_const(ira, source_instr, enum_type);
bigint_init_bigint(&result->value->data.x_enum_tag, &field->value);
return result;
}
static IrInstruction *ir_analyze_struct_literal_to_array(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *value, ZigType *wanted_type)
{
ir_add_error(ira, source_instr, buf_sprintf("TODO: type coercion of anon list literal to array"));
return ira->codegen->invalid_instruction;
}
static IrInstruction *ir_analyze_struct_literal_to_struct(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *value, ZigType *wanted_type)
{
ir_add_error(ira, source_instr, buf_sprintf("TODO: type coercion of anon struct literal to struct"));
return ira->codegen->invalid_instruction;
}
static IrInstruction *ir_analyze_struct_literal_to_union(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *value, ZigType *wanted_type)
{
ir_add_error(ira, source_instr, buf_sprintf("TODO: type coercion of anon struct literal to union"));
return ira->codegen->invalid_instruction;
}
// Add a compile error and return ErrorSemanticAnalyzeFail if the pointer alignment does not work,
// otherwise return ErrorNone. Does not emit any instructions.
// Assumes that the pointer types have element types with the same ABI alignment. Avoids resolving the
// pointer types' alignments if both of the pointer types are ABI aligned.
static Error ir_cast_ptr_align(IrAnalyze *ira, IrInstruction *source_instr, ZigType *dest_ptr_type,
ZigType *src_ptr_type, AstNode *src_source_node)
{
Error err;
ir_assert(dest_ptr_type->id == ZigTypeIdPointer, source_instr);
ir_assert(src_ptr_type->id == ZigTypeIdPointer, source_instr);
if (dest_ptr_type->data.pointer.explicit_alignment == 0 &&
src_ptr_type->data.pointer.explicit_alignment == 0)
{
return ErrorNone;
}
if ((err = type_resolve(ira->codegen, dest_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
return ErrorSemanticAnalyzeFail;
if ((err = type_resolve(ira->codegen, src_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
return ErrorSemanticAnalyzeFail;
uint32_t wanted_align = get_ptr_align(ira->codegen, dest_ptr_type);
uint32_t actual_align = get_ptr_align(ira->codegen, src_ptr_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, src_source_node,
buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&src_ptr_type->name), actual_align));
add_error_note(ira->codegen, msg, source_instr->source_node,
buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&dest_ptr_type->name), wanted_align));
return ErrorSemanticAnalyzeFail;
}
return ErrorNone;
}
static IrInstruction *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *struct_operand, TypeStructField *field)
{
IrInstruction *struct_ptr = ir_get_ref(ira, source_instr, struct_operand, true, false);
if (type_is_invalid(struct_ptr->value->type))
return ira->codegen->invalid_instruction;
IrInstruction *field_ptr = ir_analyze_struct_field_ptr(ira, source_instr, field, struct_ptr,
struct_operand->value->type, false);
if (type_is_invalid(field_ptr->value->type))
return ira->codegen->invalid_instruction;
return ir_get_deref(ira, source_instr, field_ptr, nullptr);
}
static IrInstruction *ir_analyze_optional_value_payload_value(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *optional_operand, bool safety_check_on)
{
IrInstruction *opt_ptr = ir_get_ref(ira, source_instr, optional_operand, true, false);
IrInstruction *payload_ptr = ir_analyze_unwrap_optional_payload(ira, source_instr, opt_ptr,
safety_check_on, false);
return ir_get_deref(ira, source_instr, payload_ptr, nullptr);
}
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;
}
// This means the wanted type is anything.
if (wanted_type == ira->codegen->builtin_types.entry_var) {
return value;
}
// 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);
}
if (const_cast_result.id == ConstCastResultIdFnCC) {
ir_assert(value->value->type->id == ZigTypeIdFn, source_instr);
// ConstCastResultIdFnCC is guaranteed to be the last one reported, meaning everything else is ok.
if (wanted_type->data.fn.fn_type_id.cc == CallingConventionAsync &&
actual_type->data.fn.fn_type_id.cc == CallingConventionUnspecified)
{
ir_assert(value->value->data.x_ptr.special == ConstPtrSpecialFunction, source_instr);
ZigFn *fn = value->value->data.x_ptr.data.fn.fn_entry;
if (fn->inferred_async_node == nullptr) {
fn->inferred_async_node = source_instr->source_node;
}
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop);
}
}
// 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, nullptr);
} 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, nullptr);
} 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, nullptr);
}
}
}
// 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, nullptr);
} 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, nullptr);
} 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) {
copy_const_val(result->value, value->value);
result->value->type = wanted_type;
} 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);
}
// *[N]T to ?[]const T
if (wanted_type->id == ZigTypeIdOptional &&
is_slice(wanted_type->data.maybe.child_type) &&
actual_type->id == ZigTypeIdPointer &&
actual_type->data.pointer.ptr_len == PtrLenSingle &&
actual_type->data.pointer.child_type->id == ZigTypeIdArray)
{
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 and [*c]T
if (wanted_type->id == ZigTypeIdPointer &&
(wanted_type->data.pointer.ptr_len == PtrLenUnknown || wanted_type->data.pointer.ptr_len == PtrLenC) &&
actual_type->id == ZigTypeIdPointer &&
actual_type->data.pointer.ptr_len == PtrLenSingle &&
actual_type->data.pointer.child_type->id == ZigTypeIdArray &&
(!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) &&
(!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile))
{
ZigType *actual_array_type = actual_type->data.pointer.child_type;
if (wanted_type->data.pointer.sentinel == nullptr ||
(actual_array_type->data.array.sentinel != nullptr &&
const_values_equal(ira->codegen, wanted_type->data.pointer.sentinel,
actual_array_type->data.array.sentinel)))
{
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
// *[N]T to E![]T
if ((is_slice(wanted_type) ||
(wanted_type->id == ZigTypeIdErrorUnion &&
is_slice(wanted_type->data.error_union.payload_type))) &&
actual_type->id == ZigTypeIdPointer &&
actual_type->data.pointer.ptr_len == PtrLenSingle &&
actual_type->data.pointer.child_type->id == ZigTypeIdArray)
{
ZigType *slice_type = (wanted_type->id == ZigTypeIdErrorUnion) ?
wanted_type->data.error_union.payload_type : wanted_type;
ZigType *slice_ptr_type = slice_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)
{
// If the pointers both have ABI align, it works.
// Or if the array length is 0, alignment doesn't matter.
bool ok_align = array_type->data.array.len == 0 ||
(slice_ptr_type->data.pointer.explicit_alignment == 0 &&
actual_type->data.pointer.explicit_alignment == 0);
if (!ok_align) {
// If either one has non ABI align, we have to resolve them both
if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type,
ResolveStatusAlignmentKnown)))
{
return ira->codegen->invalid_instruction;
}
if ((err = type_resolve(ira->codegen, slice_ptr_type->data.pointer.child_type,
ResolveStatusAlignmentKnown)))
{
return ira->codegen->invalid_instruction;
}
ok_align = get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, slice_ptr_type);
}
if (ok_align) {
if (wanted_type->id == ZigTypeIdErrorUnion) {
IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, slice_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;
} else {
return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, slice_type, nullptr);
}
}
}
}
// *[N]T to E![]T
if (wanted_type->id == ZigTypeIdErrorUnion &&
is_slice(wanted_type->data.error_union.payload_type) &&
actual_type->id == ZigTypeIdPointer &&
actual_type->data.pointer.ptr_len == PtrLenSingle &&
actual_type->data.pointer.child_type->id == ZigTypeIdArray)
{
ZigType *slice_type = wanted_type->data.error_union.payload_type;
ZigType *slice_ptr_type = slice_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)
{
// If the pointers both have ABI align, it works.
bool ok_align = slice_ptr_type->data.pointer.explicit_alignment == 0 &&
actual_type->data.pointer.explicit_alignment == 0;
if (!ok_align) {
// If either one has non ABI align, we have to resolve them both
if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type,
ResolveStatusAlignmentKnown)))
{
return ira->codegen->invalid_instruction;
}
if ((err = type_resolve(ira->codegen, slice_ptr_type->data.pointer.child_type,
ResolveStatusAlignmentKnown)))
{
return ira->codegen->invalid_instruction;
}
ok_align = get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, slice_ptr_type);
}
if (ok_align) {
return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, slice_type, nullptr);
}
}
}
// *@Frame(func) to anyframe->T or anyframe
// *@Frame(func) to ?anyframe->T or ?anyframe
// *@Frame(func) to E!anyframe->T or E!anyframe
if (actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle &&
!actual_type->data.pointer.is_const &&
actual_type->data.pointer.child_type->id == ZigTypeIdFnFrame)
{
ZigType *anyframe_type;
if (wanted_type->id == ZigTypeIdAnyFrame) {
anyframe_type = wanted_type;
} else if (wanted_type->id == ZigTypeIdOptional &&
wanted_type->data.maybe.child_type->id == ZigTypeIdAnyFrame)
{
anyframe_type = wanted_type->data.maybe.child_type;
} else if (wanted_type->id == ZigTypeIdErrorUnion &&
wanted_type->data.error_union.payload_type->id == ZigTypeIdAnyFrame)
{
anyframe_type = wanted_type->data.error_union.payload_type;
} else {
anyframe_type = nullptr;
}
if (anyframe_type != nullptr) {
bool ok = true;
if (anyframe_type->data.any_frame.result_type != nullptr) {
ZigFn *fn = actual_type->data.pointer.child_type->data.frame.fn;
ZigType *fn_return_type = fn->type_entry->data.fn.fn_type_id.return_type;
if (anyframe_type->data.any_frame.result_type != fn_return_type) {
ok = false;
}
}
if (ok) {
IrInstruction *cast1 = ir_analyze_frame_ptr_to_anyframe(ira, source_instr, value, anyframe_type);
if (anyframe_type == wanted_type)
return cast1;
return ir_analyze_cast(ira, source_instr, wanted_type, cast1);
}
}
}
// anyframe->T to anyframe
if (actual_type->id == ZigTypeIdAnyFrame && actual_type->data.any_frame.result_type != nullptr &&
wanted_type->id == ZigTypeIdAnyFrame && wanted_type->data.any_frame.result_type == nullptr)
{
return ir_analyze_anyframe_to_anyframe(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 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, nullptr);
}
// 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)
{
return ir_analyze_enum_literal(ira, source_instr, value, wanted_type);
}
// cast from enum literal to optional enum
if (actual_type->id == ZigTypeIdEnumLiteral &&
(wanted_type->id == ZigTypeIdOptional && wanted_type->data.maybe.child_type->id == ZigTypeIdEnum))
{
IrInstruction *result = ir_analyze_enum_literal(ira, source_instr, value, wanted_type->data.maybe.child_type);
if (result == ira->codegen->invalid_instruction)
return result;
return ir_analyze_optional_wrap(ira, result, value, wanted_type, nullptr);
}
// cast from enum literal to error union when payload is an enum
if (actual_type->id == ZigTypeIdEnumLiteral &&
(wanted_type->id == ZigTypeIdErrorUnion && wanted_type->data.error_union.payload_type->id == ZigTypeIdEnum))
{
IrInstruction *result = ir_analyze_enum_literal(ira, source_instr, value, wanted_type->data.error_union.payload_type);
if (result == ira->codegen->invalid_instruction)
return result;
return ir_analyze_err_wrap_payload(ira, result, value, wanted_type, nullptr);
}
// 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, or
// enum literal to union which has a matching enum as the tag type
if (is_tagged_union(wanted_type) && (actual_type->id == ZigTypeIdEnum ||
actual_type->id == ZigTypeIdEnumLiteral))
{
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 &&
// `types_match_const_cast_only` only gets info for child_types
(!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) &&
(!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile))
{
if ((err = ir_cast_ptr_align(ira, source_instr, wanted_type, actual_type, value->source_node)))
return ira->codegen->invalid_instruction;
return ir_analyze_ptr_to_array(ira, source_instr, value, wanted_type);
}
}
// [:x]T to [*:x]T
// [:x]T to [*c]T
if (wanted_type->id == ZigTypeIdPointer && is_slice(actual_type) &&
((wanted_type->data.pointer.ptr_len == PtrLenUnknown && wanted_type->data.pointer.sentinel != nullptr) ||
wanted_type->data.pointer.ptr_len == PtrLenC))
{
ZigType *slice_ptr_type = resolve_struct_field_type(ira->codegen,
actual_type->data.structure.fields[slice_ptr_index]);
if (types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
slice_ptr_type->data.pointer.child_type, source_node,
!wanted_type->data.pointer.is_const).id == ConstCastResultIdOk &&
(slice_ptr_type->data.pointer.sentinel != nullptr &&
(wanted_type->data.pointer.ptr_len == PtrLenC ||
const_values_equal(ira->codegen, wanted_type->data.pointer.sentinel,
slice_ptr_type->data.pointer.sentinel))))
{
TypeStructField *ptr_field = actual_type->data.structure.fields[slice_ptr_index];
IrInstruction *slice_ptr = ir_analyze_struct_value_field_value(ira, source_instr, value, ptr_field);
return ir_implicit_cast2(ira, source_instr, slice_ptr, 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 (is_pointery_and_elem_is_not_pointery(actual_type)) {
ZigType *dest_ptr_type = nullptr;
if (wanted_type->id == ZigTypeIdPointer &&
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.child_type == ira->codegen->builtin_types.entry_c_void)
{
dest_ptr_type = wanted_type->data.maybe.child_type;
}
if (dest_ptr_type != nullptr) {
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)
{
bool has_bits;
if ((err = type_has_bits2(ira->codegen, actual_type, &has_bits)))
return ira->codegen->invalid_instruction;
if (!has_bits) {
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, nullptr);
}
// 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 inferred struct type to array, union, or struct
if (is_anon_container(actual_type)) {
AstNode *decl_node = actual_type->data.structure.decl_node;
ir_assert(decl_node->type == NodeTypeContainerInitExpr, source_instr);
ContainerInitKind init_kind = decl_node->data.container_init_expr.kind;
uint32_t field_count = actual_type->data.structure.src_field_count;
if (wanted_type->id == ZigTypeIdArray && (init_kind == ContainerInitKindArray || field_count == 0) &&
wanted_type->data.array.len == field_count)
{
return ir_analyze_struct_literal_to_array(ira, source_instr, value, wanted_type);
} else if (wanted_type->id == ZigTypeIdStruct &&
(init_kind == ContainerInitKindStruct || field_count == 0))
{
return ir_analyze_struct_literal_to_struct(ira, source_instr, value, wanted_type);
} else if (wanted_type->id == ZigTypeIdUnion && init_kind == ContainerInitKindStruct && field_count == 1) {
return ir_analyze_struct_literal_to_union(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);
}
// T to ?U, where T implicitly casts to U
if (wanted_type->id == ZigTypeIdOptional && actual_type->id != ZigTypeIdOptional) {
IrInstruction *cast1 = ir_implicit_cast2(ira, source_instr, value, wanted_type->data.maybe.child_type);
if (type_is_invalid(cast1->value->type))
return ira->codegen->invalid_instruction;
return ir_implicit_cast2(ira, source_instr, cast1, wanted_type);
}
// T to E!U, where T implicitly casts to U
if (wanted_type->id == ZigTypeIdErrorUnion && actual_type->id != ZigTypeIdErrorUnion &&
actual_type->id != ZigTypeIdErrorSet)
{
IrInstruction *cast1 = ir_implicit_cast2(ira, source_instr, value, wanted_type->data.error_union.payload_type);
if (type_is_invalid(cast1->value->type))
return ira->codegen->invalid_instruction;
return ir_implicit_cast2(ira, source_instr, cast1, 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_cast2(IrAnalyze *ira, IrInstruction *value_source_instr,
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_source_instr, expected_type, value);
}
static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, ZigType *expected_type) {
return ir_implicit_cast2(ira, value, value, expected_type);
}
static ZigType *get_ptr_elem_type(CodeGen *g, IrInstruction *ptr) {
ir_assert(ptr->value->type->id == ZigTypeIdPointer, ptr);
ZigType *elem_type = ptr->value->type->data.pointer.child_type;
if (elem_type != g->builtin_types.entry_var)
return elem_type;
if (ir_resolve_lazy(g, ptr->source_node, ptr->value))
return g->builtin_types.entry_invalid;
assert(value_is_comptime(ptr->value));
ZigValue *pointee = const_ptr_pointee_unchecked(g, ptr->value);
return pointee->type;
}
static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr,
ResultLoc *result_loc)
{
Error err;
ZigType *ptr_type = ptr->value->type;
if (type_is_invalid(ptr_type))
return ira->codegen->invalid_instruction;
if (ptr_type->id != ZigTypeIdPointer) {
ir_add_error_node(ira, source_instruction->source_node,
buf_sprintf("attempt to dereference non-pointer type '%s'",
buf_ptr(&ptr_type->name)));
return ira->codegen->invalid_instruction;
}
ZigType *child_type = ptr_type->data.pointer.child_type;
if (type_is_invalid(child_type))
return ira->codegen->invalid_instruction;
// 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_move(ira, source_instruction,
get_the_one_possible_value(ira->codegen, 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 != ConstPtrMutRuntimeVar) {
ZigValue *pointee = const_ptr_pointee_unchecked(ira->codegen, ptr->value);
if (child_type == ira->codegen->builtin_types.entry_var) {
child_type = pointee->type;
}
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;
}
// If the instruction is a element pointer instruction to a vector, we emit
// vector element extract instruction rather than load pointer. If the
// pointer type has non-VECTOR_INDEX_RUNTIME value, it would have been
// possible to implement this in the codegen for IrInstructionLoadPtrGen.
// However if it has VECTOR_INDEX_RUNTIME then we must emit a compile error
// if the vector index cannot be determined right here, right now, because
// the type information does not contain enough information to actually
// perform a dereference.
if (ptr_type->data.pointer.vector_index == VECTOR_INDEX_RUNTIME) {
if (ptr->id == IrInstructionIdElemPtr) {
IrInstructionElemPtr *elem_ptr = (IrInstructionElemPtr *)ptr;
IrInstruction *vector_loaded = ir_get_deref(ira, elem_ptr->array_ptr,
elem_ptr->array_ptr, nullptr);
IrInstruction *elem_index = elem_ptr->elem_index;
return ir_build_vector_extract_elem(ira, source_instruction, vector_loaded, elem_index);
}
ir_add_error(ira, ptr,
buf_sprintf("unable to determine vector element index of type '%s'", buf_ptr(&ptr_type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *result_loc_inst;
if (ptr_type->data.pointer.host_int_bytes != 0 && handle_is_ptr(child_type)) {
if (result_loc == nullptr) result_loc = no_result_loc();
result_loc_inst = ir_resolve_result(ira, source_instruction, result_loc, child_type, nullptr,
true, true);
if (type_is_invalid(result_loc_inst->value->type) || instr_is_unreachable(result_loc_inst)) {
return result_loc_inst;
}
} else {
result_loc_inst = nullptr;
}
return ir_build_load_ptr_gen(ira, source_instruction, ptr, child_type, result_loc_inst);
}
static bool ir_resolve_const_align(CodeGen *codegen, IrExecutable *exec, AstNode *source_node,
ZigValue *const_val, uint32_t *out)
{
Error err;
if ((err = ir_resolve_const_val(codegen, exec, source_node, const_val, UndefBad)))
return false;
uint32_t align_bytes = bigint_as_u32(&const_val->data.x_bigint);
if (align_bytes == 0) {
exec_add_error_node(codegen, exec, source_node, buf_sprintf("alignment must be >= 1"));
return false;
}
if (!is_power_of_2(align_bytes)) {
exec_add_error_node(codegen, exec, source_node,
buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes));
return false;
}
*out = align_bytes;
return true;
}
static bool ir_resolve_align(IrAnalyze *ira, IrInstruction *value, ZigType *elem_type, uint32_t *out) {
if (type_is_invalid(value->value->type))
return false;
// Look for this pattern: `*align(@alignOf(T)) T`.
// This can be resolved to be `*out = 0` without resolving any alignment.
if (elem_type != nullptr && value->value->special == ConstValSpecialLazy &&
value->value->data.x_lazy->id == LazyValueIdAlignOf)
{
LazyValueAlignOf *lazy_align_of = reinterpret_cast<LazyValueAlignOf *>(value->value->data.x_lazy);
ZigType *lazy_elem_type = ir_resolve_type(lazy_align_of->ira, lazy_align_of->target_type);
if (type_is_invalid(lazy_elem_type))
return false;
if (elem_type == lazy_elem_type) {
*out = 0;
return true;
}
}
IrInstruction *casted_value = ir_implicit_cast(ira, value, get_align_amt_type(ira->codegen));
if (type_is_invalid(casted_value->value->type))
return false;
return ir_resolve_const_align(ira->codegen, ira->new_irb.exec, value->source_node,
casted_value->value, out);
}
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;
ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
if (!const_val)
return false;
*out = bigint_as_u64(&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;
ZigValue *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;
ZigType *atomic_order_type = get_builtin_type(ira->codegen, "AtomicOrder");
IrInstruction *casted_value = ir_implicit_cast(ira, value, atomic_order_type);
if (type_is_invalid(casted_value->value->type))
return false;
ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
if (!const_val)
return false;
*out = (AtomicOrder)bigint_as_u32(&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;
ZigType *atomic_rmw_op_type = get_builtin_type(ira->codegen, "AtomicRmwOp");
IrInstruction *casted_value = ir_implicit_cast(ira, value, atomic_rmw_op_type);
if (type_is_invalid(casted_value->value->type))
return false;
ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
if (!const_val)
return false;
*out = (AtomicRmwOp)bigint_as_u32(&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;
ZigType *global_linkage_type = get_builtin_type(ira->codegen, "GlobalLinkage");
IrInstruction *casted_value = ir_implicit_cast(ira, value, global_linkage_type);
if (type_is_invalid(casted_value->value->type))
return false;
ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
if (!const_val)
return false;
*out = (GlobalLinkageId)bigint_as_u32(&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;
ZigType *float_mode_type = get_builtin_type(ira->codegen, "FloatMode");
IrInstruction *casted_value = ir_implicit_cast(ira, value, float_mode_type);
if (type_is_invalid(casted_value->value->type))
return false;
ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
if (!const_val)
return false;
*out = (FloatMode)bigint_as_u32(&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;
ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
if (!const_val)
return nullptr;
ZigValue *ptr_field = const_val->data.x_struct.fields[slice_ptr_index];
ZigValue *len_field = const_val->data.x_struct.fields[slice_len_index];
assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray);
ZigValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val;
expand_undef_array(ira->codegen, array_val);
size_t len = bigint_as_usize(&len_field->data.x_bigint);
if (array_val->data.x_array.special == ConstArraySpecialBuf && len == buf_len(array_val->data.x_array.data.s_buf)) {
return array_val->data.x_array.data.s_buf;
}
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;
ZigValue *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_u64(&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);
if (instruction->result_loc_ret == nullptr || !instruction->result_loc_ret->implicit_return_type_done) {
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 *operand = instruction->operand->child;
if (type_is_invalid(operand->value->type))
return ir_unreach_error(ira);
IrInstruction *casted_operand = ir_implicit_cast(ira, operand, ira->explicit_return_type);
if (type_is_invalid(casted_operand->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 (!instr_is_comptime(operand) && ira->explicit_return_type != nullptr &&
handle_is_ptr(ira->explicit_return_type))
{
// result location mechanism took care of it.
IrInstruction *result = ir_build_return(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, nullptr);
result->value->type = ira->codegen->builtin_types.entry_unreachable;
return ir_finish_anal(ira, result);
}
if (casted_operand->value->special == ConstValSpecialRuntime &&
casted_operand->value->type->id == ZigTypeIdPointer &&
casted_operand->value->data.rh_ptr == RuntimeHintPtrStack)
{
ir_add_error(ira, casted_operand, 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_operand);
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) {
return ir_const_move(ira, &instruction->base, instruction->base.value);
}
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)) {
ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad);
if (op1_val == nullptr)
return ira->codegen->invalid_instruction;
ZigValue *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(ZigValue *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 void set_optional_value_to_null(ZigValue *val) {
assert(val->special == ConstValSpecialStatic);
if (val->type->id == ZigTypeIdNull) return; // nothing to do
assert(val->type->id == ZigTypeIdOptional);
if (get_codegen_ptr_type(val->type) != nullptr) {
val->data.x_ptr.special = ConstPtrSpecialNull;
} else if (is_opt_err_set(val->type)) {
val->data.x_err_set = nullptr;
} else {
val->data.x_optional = nullptr;
}
}
static void set_optional_payload(ZigValue *opt_val, ZigValue *payload) {
assert(opt_val->special == ConstValSpecialStatic);
assert(opt_val->type->id == ZigTypeIdOptional);
if (payload == nullptr) {
set_optional_value_to_null(opt_val);
} else if (is_opt_err_set(opt_val->type)) {
assert(payload->type->id == ZigTypeIdErrorSet);
opt_val->data.x_err_set = payload->data.x_err_set;
} else {
opt_val->data.x_optional = payload;
}
}
static IrInstruction *ir_evaluate_bin_op_cmp(IrAnalyze *ira, ZigType *resolved_type,
ZigValue *op1_val, ZigValue *op2_val, IrInstructionBinOp *bin_op_instruction, IrBinOp op_id,
bool one_possible_value) {
if (op1_val->special == ConstValSpecialUndef ||
op2_val->special == ConstValSpecialUndef)
return ir_const_undef(ira, &bin_op_instruction->base, resolved_type);
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);
}
zig_unreachable();
}
// Returns ErrorNotLazy when the value cannot be determined
static Error lazy_cmp_zero(AstNode *source_node, ZigValue *val, Cmp *result) {
Error err;
switch (val->special) {
case ConstValSpecialRuntime:
case ConstValSpecialUndef:
return ErrorNotLazy;
case ConstValSpecialStatic:
switch (val->type->id) {
case ZigTypeIdComptimeInt:
case ZigTypeIdInt:
*result = bigint_cmp_zero(&val->data.x_bigint);
return ErrorNone;
case ZigTypeIdComptimeFloat:
case ZigTypeIdFloat:
if (float_is_nan(val))
return ErrorNotLazy;
*result = float_cmp_zero(val);
return ErrorNone;
default:
return ErrorNotLazy;
}
case ConstValSpecialLazy:
switch (val->data.x_lazy->id) {
case LazyValueIdInvalid:
zig_unreachable();
case LazyValueIdAlignOf:
*result = CmpGT;
return ErrorNone;
case LazyValueIdSizeOf: {
LazyValueSizeOf *lazy_size_of = reinterpret_cast<LazyValueSizeOf *>(val->data.x_lazy);
IrAnalyze *ira = lazy_size_of->ira;
bool is_zero_bits;
if ((err = type_val_resolve_zero_bits(ira->codegen, lazy_size_of->target_type->value,
nullptr, nullptr, &is_zero_bits)))
{
return err;
}
*result = is_zero_bits ? CmpEQ : CmpGT;
return ErrorNone;
}
default:
return ErrorNotLazy;
}
}
zig_unreachable();
}
static ErrorMsg *ir_eval_bin_op_cmp_scalar(IrAnalyze *ira, IrInstruction *source_instr,
ZigValue *op1_val, IrBinOp op_id, ZigValue *op2_val, ZigValue *out_val)
{
Error err;
{
// Before resolving the values, we special case comparisons against zero. These can often
// be done without resolving lazy values, preventing potential dependency loops.
Cmp op1_cmp_zero;
if ((err = lazy_cmp_zero(source_instr->source_node, op1_val, &op1_cmp_zero))) {
if (err == ErrorNotLazy) goto never_mind_just_calculate_it_normally;
return ira->codegen->trace_err;
}
Cmp op2_cmp_zero;
if ((err = lazy_cmp_zero(source_instr->source_node, op2_val, &op2_cmp_zero))) {
if (err == ErrorNotLazy) goto never_mind_just_calculate_it_normally;
return ira->codegen->trace_err;
}
bool can_cmp_zero = false;
Cmp cmp_result;
if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpEQ) {
can_cmp_zero = true;
cmp_result = CmpEQ;
} else if (op1_cmp_zero == CmpGT && op2_cmp_zero == CmpEQ) {
can_cmp_zero = true;
cmp_result = CmpGT;
} else if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpGT) {
can_cmp_zero = true;
cmp_result = CmpLT;
} else if (op1_cmp_zero == CmpLT && op2_cmp_zero == CmpEQ) {
can_cmp_zero = true;
cmp_result = CmpLT;
} else if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpLT) {
can_cmp_zero = true;
cmp_result = CmpGT;
} else if (op1_cmp_zero == CmpLT && op2_cmp_zero == CmpGT) {
can_cmp_zero = true;
cmp_result = CmpLT;
} else if (op1_cmp_zero == CmpGT && op2_cmp_zero == CmpLT) {
can_cmp_zero = true;
cmp_result = CmpGT;
}
if (can_cmp_zero) {
bool answer = resolve_cmp_op_id(op_id, cmp_result);
out_val->special = ConstValSpecialStatic;
out_val->data.x_bool = answer;
return nullptr;
}
}
never_mind_just_calculate_it_normally:
if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, source_instr->source_node,
op1_val, UndefOk)))
{
return ira->codegen->trace_err;
}
if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, source_instr->source_node,
op2_val, UndefOk)))
{
return ira->codegen->trace_err;
}
if (op1_val->special == ConstValSpecialUndef || op2_val->special == ConstValSpecialUndef ||
op1_val->type->id == ZigTypeIdUndefined || op2_val->type->id == ZigTypeIdUndefined)
{
out_val->special = ConstValSpecialUndef;
return nullptr;
}
bool op1_is_float = op1_val->type->id == ZigTypeIdFloat || op1_val->type->id == ZigTypeIdComptimeFloat;
bool op2_is_float = op2_val->type->id == ZigTypeIdFloat || op2_val->type->id == ZigTypeIdComptimeFloat;
if (op1_is_float && op2_is_float) {
if (float_is_nan(op1_val) || float_is_nan(op2_val)) {
out_val->special = ConstValSpecialStatic;
out_val->data.x_bool = op_id == IrBinOpCmpNotEq;
return nullptr;
}
if (op1_val->type->id == ZigTypeIdComptimeFloat) {
IrInstruction *tmp = ir_const_noval(ira, source_instr);
tmp->value = op1_val;
IrInstruction *casted = ir_implicit_cast(ira, tmp, op2_val->type);
op1_val = casted->value;
} else if (op2_val->type->id == ZigTypeIdComptimeFloat) {
IrInstruction *tmp = ir_const_noval(ira, source_instr);
tmp->value = op2_val;
IrInstruction *casted = ir_implicit_cast(ira, tmp, op1_val->type);
op2_val = casted->value;
}
Cmp cmp_result = float_cmp(op1_val, op2_val);
out_val->special = ConstValSpecialStatic;
out_val->data.x_bool = resolve_cmp_op_id(op_id, cmp_result);
return nullptr;
}
bool op1_is_int = op1_val->type->id == ZigTypeIdInt || op1_val->type->id == ZigTypeIdComptimeInt;
bool op2_is_int = op2_val->type->id == ZigTypeIdInt || op2_val->type->id == ZigTypeIdComptimeInt;
BigInt *op1_bigint;
BigInt *op2_bigint;
bool need_to_free_op1_bigint = false;
bool need_to_free_op2_bigint = false;
if (op1_is_float) {
op1_bigint = allocate<BigInt>(1, "BigInt");
need_to_free_op1_bigint = true;
float_init_bigint(op1_bigint, op1_val);
} else {
assert(op1_is_int);
op1_bigint = &op1_val->data.x_bigint;
}
if (op2_is_float) {
op2_bigint = allocate<BigInt>(1, "BigInt");
need_to_free_op2_bigint = true;
float_init_bigint(op2_bigint, op2_val);
} else {
assert(op2_is_int);
op2_bigint = &op2_val->data.x_bigint;
}
Cmp cmp_result = bigint_cmp(op1_bigint, op2_bigint);
out_val->special = ConstValSpecialStatic;
out_val->data.x_bool = resolve_cmp_op_id(op_id, cmp_result);
if (need_to_free_op1_bigint) destroy(op1_bigint, "BigInt");
if (need_to_free_op2_bigint) destroy(op2_bigint, "BigInt");
return nullptr;
}
static IrInstruction *ir_analyze_bin_op_cmp_numeric(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *op1, IrInstruction *op2, IrBinOp op_id)
{
Error err;
ZigType *scalar_result_type = ira->codegen->builtin_types.entry_bool;
ZigType *result_type = scalar_result_type;
ZigType *op1_scalar_type = op1->value->type;
ZigType *op2_scalar_type = op2->value->type;
if (op1->value->type->id == ZigTypeIdVector && op2->value->type->id == ZigTypeIdVector) {
if (op1->value->type->data.vector.len != op2->value->type->data.vector.len) {
ir_add_error(ira, source_instr,
buf_sprintf("vector length mismatch: %" PRIu32 " and %" PRIu32,
op1->value->type->data.vector.len, op2->value->type->data.vector.len));
return ira->codegen->invalid_instruction;
}
result_type = get_vector_type(ira->codegen, op1->value->type->data.vector.len, scalar_result_type);
op1_scalar_type = op1->value->type->data.vector.elem_type;
op2_scalar_type = op2->value->type->data.vector.elem_type;
} else if (op1->value->type->id == ZigTypeIdVector || op2->value->type->id == ZigTypeIdVector) {
ir_add_error(ira, source_instr,
buf_sprintf("mixed scalar and vector operands to comparison operator: '%s' and '%s'",
buf_ptr(&op1->value->type->name), buf_ptr(&op2->value->type->name)));
return ira->codegen->invalid_instruction;
}
bool opv_op1;
switch (type_has_one_possible_value(ira->codegen, op1->value->type)) {
case OnePossibleValueInvalid:
return ira->codegen->invalid_instruction;
case OnePossibleValueYes:
opv_op1 = true;
break;
case OnePossibleValueNo:
opv_op1 = false;
break;
}
bool opv_op2;
switch (type_has_one_possible_value(ira->codegen, op2->value->type)) {
case OnePossibleValueInvalid:
return ira->codegen->invalid_instruction;
case OnePossibleValueYes:
opv_op2 = true;
break;
case OnePossibleValueNo:
opv_op2 = false;
break;
}
Cmp op1_cmp_zero;
bool have_op1_cmp_zero = false;
if ((err = lazy_cmp_zero(source_instr->source_node, op1->value, &op1_cmp_zero))) {
if (err != ErrorNotLazy) return ira->codegen->invalid_instruction;
} else {
have_op1_cmp_zero = true;
}
Cmp op2_cmp_zero;
bool have_op2_cmp_zero = false;
if ((err = lazy_cmp_zero(source_instr->source_node, op2->value, &op2_cmp_zero))) {
if (err != ErrorNotLazy) return ira->codegen->invalid_instruction;
} else {
have_op2_cmp_zero = true;
}
if (((opv_op1 || instr_is_comptime(op1)) && (opv_op2 || instr_is_comptime(op2))) ||
(have_op1_cmp_zero && have_op2_cmp_zero))
{
IrInstruction *result_instruction = ir_const(ira, source_instr, result_type);
ZigValue *out_val = result_instruction->value;
if (result_type->id == ZigTypeIdVector) {
size_t len = result_type->data.vector.len;
expand_undef_array(ira->codegen, op1->value);
expand_undef_array(ira->codegen, op2->value);
out_val->special = ConstValSpecialUndef;
expand_undef_array(ira->codegen, out_val);
for (size_t i = 0; i < len; i += 1) {
ZigValue *scalar_op1_val = &op1->value->data.x_array.data.s_none.elements[i];
ZigValue *scalar_op2_val = &op2->value->data.x_array.data.s_none.elements[i];
ZigValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i];
assert(scalar_out_val->type == scalar_result_type);
ErrorMsg *msg = ir_eval_bin_op_cmp_scalar(ira, source_instr,
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 = result_type;
out_val->special = ConstValSpecialStatic;
} else {
if (ir_eval_bin_op_cmp_scalar(ira, source_instr, op1->value, op_id,
op2->value, out_val) != nullptr)
{
return ira->codegen->invalid_instruction;
}
}
return result_instruction;
}
// If one operand has a comptime-known comparison with 0, and the other operand is unsigned, we might
// know the answer, depending on the operator.
// TODO make this work with vectors
if (have_op1_cmp_zero && op2_scalar_type->id == ZigTypeIdInt && !op2_scalar_type->data.integral.is_signed) {
if (op1_cmp_zero == CmpEQ) {
// 0 <= unsigned_x // true
// 0 > unsigned_x // false
switch (op_id) {
case IrBinOpCmpLessOrEq:
return ir_const_bool(ira, source_instr, true);
case IrBinOpCmpGreaterThan:
return ir_const_bool(ira, source_instr, false);
default:
break;
}
} else if (op1_cmp_zero == CmpLT) {
// -1 != unsigned_x // true
// -1 <= unsigned_x // true
// -1 < unsigned_x // true
// -1 == unsigned_x // false
// -1 >= unsigned_x // false
// -1 > unsigned_x // false
switch (op_id) {
case IrBinOpCmpNotEq:
case IrBinOpCmpLessOrEq:
case IrBinOpCmpLessThan:
return ir_const_bool(ira, source_instr, true);
case IrBinOpCmpEq:
case IrBinOpCmpGreaterOrEq:
case IrBinOpCmpGreaterThan:
return ir_const_bool(ira, source_instr, false);
default:
break;
}
}
}
if (have_op2_cmp_zero && op1_scalar_type->id == ZigTypeIdInt && !op1_scalar_type->data.integral.is_signed) {
if (op2_cmp_zero == CmpEQ) {
// unsigned_x < 0 // false
// unsigned_x >= 0 // true
switch (op_id) {
case IrBinOpCmpLessThan:
return ir_const_bool(ira, source_instr, false);
case IrBinOpCmpGreaterOrEq:
return ir_const_bool(ira, source_instr, true);
default:
break;
}
} else if (op2_cmp_zero == CmpLT) {
// unsigned_x != -1 // true
// unsigned_x >= -1 // true
// unsigned_x > -1 // true
// unsigned_x == -1 // false
// unsigned_x < -1 // false
// unsigned_x <= -1 // false
switch (op_id) {
case IrBinOpCmpNotEq:
case IrBinOpCmpGreaterOrEq:
case IrBinOpCmpGreaterThan:
return ir_const_bool(ira, source_instr, true);
case IrBinOpCmpEq:
case IrBinOpCmpLessThan:
case IrBinOpCmpLessOrEq:
return ir_const_bool(ira, source_instr, false);
default:
break;
}
}
}
// It must be a runtime comparison.
// For floats, emit a float comparison instruction.
bool op1_is_float = op1_scalar_type->id == ZigTypeIdFloat || op1_scalar_type->id == ZigTypeIdComptimeFloat;
bool op2_is_float = op2_scalar_type->id == ZigTypeIdFloat || op2_scalar_type->id == ZigTypeIdComptimeFloat;
if (op1_is_float && op2_is_float) {
// Implicit cast the smaller one to the larger one.
ZigType *dest_scalar_type;
if (op1_scalar_type->id == ZigTypeIdComptimeFloat) {
dest_scalar_type = op2_scalar_type;
} else if (op2_scalar_type->id == ZigTypeIdComptimeFloat) {
dest_scalar_type = op1_scalar_type;
} else if (op1_scalar_type->data.floating.bit_count >= op2_scalar_type->data.floating.bit_count) {
dest_scalar_type = op1_scalar_type;
} else {
dest_scalar_type = op2_scalar_type;
}
ZigType *dest_type = (result_type->id == ZigTypeIdVector) ?
get_vector_type(ira->codegen, result_type->data.vector.len, dest_scalar_type) : dest_scalar_type;
IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, dest_type);
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, dest_type);
if (type_is_invalid(casted_op1->value->type) || type_is_invalid(casted_op2->value->type))
return ira->codegen->invalid_instruction;
return ir_build_bin_op_gen(ira, source_instr, result_type, op_id, casted_op1, casted_op2, true);
}
// For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
// For mixed signed and unsigned integers, implicit cast both operands to a signed
// integer with + 1 bit.
// For mixed floats and integers, extract the integer part from the float, cast that to
// a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
// add/subtract 1.
bool dest_int_is_signed = false;
if (have_op1_cmp_zero) {
if (op1_cmp_zero == CmpLT) dest_int_is_signed = true;
} else if (op1_is_float) {
dest_int_is_signed = true;
} else if (op1_scalar_type->id == ZigTypeIdInt && op1_scalar_type->data.integral.is_signed) {
dest_int_is_signed = true;
}
if (have_op2_cmp_zero) {
if (op2_cmp_zero == CmpLT) dest_int_is_signed = true;
} else if (op2_is_float) {
dest_int_is_signed = true;
} else if (op2->value->type->id == ZigTypeIdInt && op2->value->type->data.integral.is_signed) {
dest_int_is_signed = true;
}
ZigType *dest_float_type = nullptr;
uint32_t op1_bits;
if (instr_is_comptime(op1)) {
ZigValue *op1_val = ir_resolve_const(ira, op1, UndefOk);
if (op1_val == nullptr)
return ira->codegen->invalid_instruction;
if (op1_val->special == ConstValSpecialUndef)
return ir_const_undef(ira, source_instr, ira->codegen->builtin_types.entry_bool);
if (result_type->id == ZigTypeIdVector) {
ir_add_error(ira, op1, buf_sprintf("compiler bug: TODO: support comptime vector here"));
return ira->codegen->invalid_instruction;
}
bool is_unsigned;
if (op1_is_float) {
BigInt bigint = {};
float_init_bigint(&bigint, op1_val);
Cmp zcmp = float_cmp_zero(op1_val);
if (float_has_fraction(op1_val)) {
if (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq) {
return ir_const_bool(ira, source_instr, op_id == IrBinOpCmpNotEq);
}
if (zcmp == CmpLT) {
bigint_decr(&bigint);
} else {
bigint_incr(&bigint);
}
}
op1_bits = bigint_bits_needed(&bigint);
is_unsigned = zcmp != CmpLT;
} else {
op1_bits = bigint_bits_needed(&op1_val->data.x_bigint);
is_unsigned = bigint_cmp_zero(&op1_val->data.x_bigint) != CmpLT;
}
if (is_unsigned && dest_int_is_signed) {
op1_bits += 1;
}
} else if (op1_is_float) {
dest_float_type = op1_scalar_type;
} else {
ir_assert(op1_scalar_type->id == ZigTypeIdInt, source_instr);
op1_bits = op1_scalar_type->data.integral.bit_count;
if (!op1_scalar_type->data.integral.is_signed && dest_int_is_signed) {
op1_bits += 1;
}
}
uint32_t op2_bits;
if (instr_is_comptime(op2)) {
ZigValue *op2_val = ir_resolve_const(ira, op2, UndefOk);
if (op2_val == nullptr)
return ira->codegen->invalid_instruction;
if (op2_val->special == ConstValSpecialUndef)
return ir_const_undef(ira, source_instr, ira->codegen->builtin_types.entry_bool);
if (result_type->id == ZigTypeIdVector) {
ir_add_error(ira, op2, buf_sprintf("compiler bug: TODO: support comptime vector here"));
return ira->codegen->invalid_instruction;
}
bool is_unsigned;
if (op2_is_float) {
BigInt bigint = {};
float_init_bigint(&bigint, op2_val);
Cmp zcmp = float_cmp_zero(op2_val);
if (float_has_fraction(op2_val)) {
if (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq) {
return ir_const_bool(ira, source_instr, op_id == IrBinOpCmpNotEq);
}
if (zcmp == CmpLT) {
bigint_decr(&bigint);
} else {
bigint_incr(&bigint);
}
}
op2_bits = bigint_bits_needed(&bigint);
is_unsigned = zcmp != CmpLT;
} else {
op2_bits = bigint_bits_needed(&op2_val->data.x_bigint);
is_unsigned = bigint_cmp_zero(&op2_val->data.x_bigint) != CmpLT;
}
if (is_unsigned && dest_int_is_signed) {
op2_bits += 1;
}
} else if (op2_is_float) {
dest_float_type = op2_scalar_type;
} else {
ir_assert(op2_scalar_type->id == ZigTypeIdInt, source_instr);
op2_bits = op2_scalar_type->data.integral.bit_count;
if (!op2_scalar_type->data.integral.is_signed && dest_int_is_signed) {
op2_bits += 1;
}
}
ZigType *dest_scalar_type = (dest_float_type == nullptr) ?
get_int_type(ira->codegen, dest_int_is_signed, (op1_bits > op2_bits) ? op1_bits : op2_bits) :
dest_float_type;
ZigType *dest_type = (result_type->id == ZigTypeIdVector) ?
get_vector_type(ira->codegen, result_type->data.vector.len, dest_scalar_type) : dest_scalar_type;
IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, dest_type);
if (type_is_invalid(casted_op1->value->type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, dest_type);
if (type_is_invalid(casted_op2->value->type))
return ira->codegen->invalid_instruction;
return ir_build_bin_op_gen(ira, source_instr, result_type, op_id, casted_op1, casted_op2, true);
}
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)) {
ZigValue *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)) {
ZigValue *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;
} else if (is_equality_cmp && (
(op1->value->type->id == ZigTypeIdEnumLiteral && op2->value->type->id == ZigTypeIdUnion) ||
(op2->value->type->id == ZigTypeIdEnumLiteral && op1->value->type->id == ZigTypeIdUnion)))
{
// Support equality comparison between a union's tag value and a enum literal
IrInstruction *union_val = op1->value->type->id == ZigTypeIdUnion ? op1 : op2;
IrInstruction *enum_val = op1->value->type->id == ZigTypeIdUnion ? op2 : op1;
ZigType *tag_type = union_val->value->type->data.unionation.tag_type;
assert(tag_type != nullptr);
IrInstruction *casted_union = ir_implicit_cast(ira, union_val, tag_type);
if (type_is_invalid(casted_union->value->type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_val = ir_implicit_cast(ira, enum_val, tag_type);
if (type_is_invalid(casted_val->value->type))
return ira->codegen->invalid_instruction;
if (instr_is_comptime(casted_union)) {
ZigValue *const_union_val = ir_resolve_const(ira, casted_union, UndefBad);
if (!const_union_val)
return ira->codegen->invalid_instruction;
ZigValue *const_enum_val = ir_resolve_const(ira, casted_val, UndefBad);
if (!const_enum_val)
return ira->codegen->invalid_instruction;
Cmp cmp_result = bigint_cmp(&const_union_val->data.x_union.tag, &const_enum_val->data.x_enum_tag);
bool bool_result = (op_id == IrBinOpCmpEq) ? cmp_result == CmpEQ : cmp_result != CmpEQ;
return ir_const_bool(ira, &bin_op_instruction->base, bool_result);
}
IrInstruction *result = ir_build_bin_op(&ira->new_irb,
bin_op_instruction->base.scope, bin_op_instruction->base.source_node,
op_id, casted_union, casted_val, bin_op_instruction->safety_check_on);
result->value->type = ira->codegen->builtin_types.entry_bool;
return result;
}
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)) {
ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
if (op1_val == nullptr)
return ira->codegen->invalid_instruction;
ZigValue *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;
}
if (type_is_numeric(op1->value->type) && type_is_numeric(op2->value->type)) {
// This operation allows any combination of integer and float types, regardless of the
// signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
// numeric types.
return ir_analyze_bin_op_cmp_numeric(ira, &bin_op_instruction->base, op1, op2, op_id);
}
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:
zig_unreachable(); // handled with the type_is_numeric checks above
case ZigTypeIdBool:
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdErrorSet:
case ZigTypeIdFn:
case ZigTypeIdOpaque:
case ZigTypeIdBoundFn:
case ZigTypeIdEnum:
case ZigTypeIdEnumLiteral:
case ZigTypeIdAnyFrame:
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:
case ZigTypeIdFnFrame:
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))) {
ZigValue *op1_val = one_possible_value ? casted_op1->value : ir_resolve_const(ira, casted_op1, UndefBad);
if (op1_val == nullptr)
return ira->codegen->invalid_instruction;
ZigValue *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 != ZigTypeIdVector)
return ir_evaluate_bin_op_cmp(ira, resolved_type, op1_val, op2_val, bin_op_instruction, op_id, one_possible_value);
IrInstruction *result = ir_const(ira, &bin_op_instruction->base,
get_vector_type(ira->codegen, resolved_type->data.vector.len, ira->codegen->builtin_types.entry_bool));
result->value->data.x_array.data.s_none.elements =
create_const_vals(resolved_type->data.vector.len);
expand_undef_array(ira->codegen, result->value);
for (size_t i = 0;i < resolved_type->data.vector.len;i++) {
IrInstruction *cur_res = ir_evaluate_bin_op_cmp(ira, resolved_type->data.vector.elem_type,
&op1_val->data.x_array.data.s_none.elements[i],
&op2_val->data.x_array.data.s_none.elements[i],
bin_op_instruction, op_id, one_possible_value);
copy_const_val(&result->value->data.x_array.data.s_none.elements[i], cur_res->value);
}
return result;
}
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);
if (resolved_type->id == ZigTypeIdVector) {
result->value->type = get_vector_type(ira->codegen, resolved_type->data.vector.len,
ira->codegen->builtin_types.entry_bool);
} else {
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,
ZigValue *op1_val, IrBinOp op_id, ZigValue *op2_val, ZigValue *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:
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);
ZigValue 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, ZigValue *op1_val, IrBinOp op_id, ZigValue *op2_val)
{
IrInstruction *result_instruction = ir_const(ira, source_instr, type_entry);
ZigValue *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) {
ZigValue *scalar_op1_val = &op1_val->data.x_array.data.s_none.elements[i];
ZigValue *scalar_op2_val = &op2_val->data.x_array.data.s_none.elements[i];
ZigValue *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->op1,
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;
if (op2->value->type->id != ZigTypeIdInt && op2->value->type->id != ZigTypeIdComptimeInt) {
ir_add_error(ira, bin_op_instruction->op2,
buf_sprintf("shift amount has to be an integer type, but found '%s'",
buf_ptr(&op2->value->type->name)));
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) {
ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad);
if (op2_val == nullptr)
return ira->codegen->invalid_instruction;
if (!bigint_fits_in_bits(&op2_val->data.x_bigint,
shift_amt_type->data.integral.bit_count,
op2_val->data.x_bigint.is_negative)) {
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &op2_val->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)) {
ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
if (op1_val == nullptr)
return ira->codegen->invalid_instruction;
ZigValue *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:
return false;
}
zig_unreachable();
}
static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) {
switch (op) {
case IrBinOpAdd:
case IrBinOpSub:
break;
default:
return false;
}
if (lhs_type->id != ZigTypeIdPointer)
return false;
switch (lhs_type->data.pointer.ptr_len) {
case PtrLenSingle:
return lhs_type->data.pointer.child_type->id == ZigTypeIdArray;
case PtrLenUnknown:
case PtrLenC:
return true;
}
zig_unreachable();
}
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 either operand is undef, result is undef.
ZigValue *op1_val = nullptr;
ZigValue *op2_val = nullptr;
if (instr_is_comptime(op1)) {
op1_val = ir_resolve_const(ira, op1, UndefOk);
if (op1_val == nullptr)
return ira->codegen->invalid_instruction;
if (op1_val->special == ConstValSpecialUndef)
return ir_const_undef(ira, &instruction->base, op1->value->type);
}
if (instr_is_comptime(casted_op2)) {
op2_val = ir_resolve_const(ira, casted_op2, UndefOk);
if (op2_val == nullptr)
return ira->codegen->invalid_instruction;
if (op2_val->special == ConstValSpecialUndef)
return ir_const_undef(ira, &instruction->base, op1->value->type);
}
ZigType *elem_type = op1->value->type->data.pointer.child_type;
if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown)))
return ira->codegen->invalid_instruction;
// NOTE: this variable is meaningful iff op2_val is not null!
uint64_t byte_offset;
if (op2_val != nullptr) {
uint64_t elem_offset;
if (!ir_resolve_usize(ira, casted_op2, &elem_offset))
return ira->codegen->invalid_instruction;
byte_offset = type_size(ira->codegen, elem_type) * elem_offset;
}
// Fast path for cases where the RHS is zero
if (op2_val != nullptr && byte_offset == 0) {
return op1;
}
ZigType *result_type = op1->value->type;
// Calculate the new alignment of the pointer
{
uint32_t align_bytes;
if ((err = resolve_ptr_align(ira, op1->value->type, &align_bytes)))
return ira->codegen->invalid_instruction;
// If the addend is not a comptime-known value we can still count on
// it being a multiple of the type size
uint32_t addend = op2_val ? byte_offset : type_size(ira->codegen, elem_type);
// The resulting pointer is aligned to the lcd between the
// offset (an arbitrary number) and the alignment factor (always
// a power of two, non zero)
uint32_t new_align = 1 << ctzll(addend | align_bytes);
// Rough guard to prevent overflows
assert(new_align);
result_type = adjust_ptr_align(ira->codegen, result_type, new_align);
}
if (op2_val != nullptr && op1_val != nullptr &&
(op1->value->data.x_ptr.special == ConstPtrSpecialHardCodedAddr ||
op1->value->data.x_ptr.special == ConstPtrSpecialNull))
{
uint64_t start_addr = (op1_val->data.x_ptr.special == ConstPtrSpecialNull) ?
0 : op1_val->data.x_ptr.data.hard_coded_addr.addr;
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, result_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 = result_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)) {
ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
if (op1_val == nullptr)
return ira->codegen->invalid_instruction;
ZigValue *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)) {
ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
if (op1_val == nullptr)
return ira->codegen->invalid_instruction;
if (is_int) {
ZigValue *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;
ZigValue *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 {
ZigValue rem_result = {};
ZigValue 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)) {
ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad);
if (op1_val == nullptr)
return ira->codegen->invalid_instruction;
ZigValue *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_tuple_cat(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *op1, IrInstruction *op2)
{
Error err;
ZigType *op1_type = op1->value->type;
ZigType *op2_type = op2->value->type;
uint32_t op1_field_count = op1_type->data.structure.src_field_count;
uint32_t op2_field_count = op2_type->data.structure.src_field_count;
Buf *bare_name = buf_alloc();
Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct),
source_instr->scope, source_instr->source_node, bare_name);
ZigType *new_type = get_partial_container_type(ira->codegen, source_instr->scope,
ContainerKindStruct, source_instr->source_node, buf_ptr(name), bare_name, ContainerLayoutAuto);
new_type->data.structure.special = StructSpecialInferredTuple;
new_type->data.structure.resolve_status = ResolveStatusBeingInferred;
bool is_comptime = ir_should_inline(ira->new_irb.exec, source_instr->scope);
IrInstruction *new_struct_ptr = ir_resolve_result(ira, source_instr, no_result_loc(),
new_type, nullptr, false, true);
uint32_t new_field_count = op1_field_count + op2_field_count;
new_type->data.structure.src_field_count = new_field_count;
new_type->data.structure.fields = realloc_type_struct_fields(new_type->data.structure.fields,
0, new_field_count);
for (uint32_t i = 0; i < new_field_count; i += 1) {
TypeStructField *src_field;
if (i < op1_field_count) {
src_field = op1_type->data.structure.fields[i];
} else {
src_field = op2_type->data.structure.fields[i - op1_field_count];
}
TypeStructField *new_field = new_type->data.structure.fields[i];
new_field->name = buf_sprintf("%" PRIu32, i);
new_field->type_entry = src_field->type_entry;
new_field->type_val = src_field->type_val;
new_field->src_index = i;
new_field->decl_node = src_field->decl_node;
new_field->init_val = src_field->init_val;
new_field->is_comptime = src_field->is_comptime;
}
if ((err = type_resolve(ira->codegen, new_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
ZigList<IrInstruction *> const_ptrs = {};
IrInstruction *first_non_const_instruction = nullptr;
for (uint32_t i = 0; i < new_field_count; i += 1) {
TypeStructField *dst_field = new_type->data.structure.fields[i];
IrInstruction *src_struct_op;
TypeStructField *src_field;
if (i < op1_field_count) {
src_field = op1_type->data.structure.fields[i];
src_struct_op = op1;
} else {
src_field = op2_type->data.structure.fields[i - op1_field_count];
src_struct_op = op2;
}
IrInstruction *field_value = ir_analyze_struct_value_field_value(ira, source_instr,
src_struct_op, src_field);
if (type_is_invalid(field_value->value->type))
return ira->codegen->invalid_instruction;
IrInstruction *dest_ptr = ir_analyze_struct_field_ptr(ira, source_instr, dst_field,
new_struct_ptr, new_type, true);
if (type_is_invalid(dest_ptr->value->type))
return ira->codegen->invalid_instruction;
if (instr_is_comptime(field_value)) {
const_ptrs.append(dest_ptr);
} else {
first_non_const_instruction = field_value;
}
IrInstruction *store_ptr_inst = ir_analyze_store_ptr(ira, source_instr, dest_ptr, field_value,
true);
if (type_is_invalid(store_ptr_inst->value->type))
return ira->codegen->invalid_instruction;
}
if (const_ptrs.length != new_field_count) {
new_struct_ptr->value->special = ConstValSpecialRuntime;
for (size_t i = 0; i < const_ptrs.length; i += 1) {
IrInstruction *elem_result_loc = const_ptrs.at(i);
assert(elem_result_loc->value->special == ConstValSpecialStatic);
if (elem_result_loc->value->type->data.pointer.inferred_struct_field != nullptr) {
// This field will be generated comptime; no need to do this.
continue;
}
IrInstruction *deref = ir_get_deref(ira, elem_result_loc, elem_result_loc, nullptr);
elem_result_loc->value->special = ConstValSpecialRuntime;
ir_analyze_store_ptr(ira, elem_result_loc, elem_result_loc, deref, false);
}
}
IrInstruction *result = ir_get_deref(ira, source_instr, new_struct_ptr, nullptr);
if (instr_is_comptime(result))
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;
}
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;
if (is_tuple(op1_type) && is_tuple(op2_type)) {
return ir_analyze_tuple_cat(ira, &instruction->base, op1, op2);
}
ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
if (!op1_val)
return ira->codegen->invalid_instruction;
ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad);
if (!op2_val)
return ira->codegen->invalid_instruction;
ZigValue *sentinel1 = nullptr;
ZigValue *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;
sentinel1 = op1_type->data.array.sentinel;
} else if (op1_type->id == ZigTypeIdPointer &&
op1_type->data.pointer.child_type == ira->codegen->builtin_types.entry_u8 &&
op1_type->data.pointer.sentinel != nullptr &&
op1_val->data.x_ptr.special == ConstPtrSpecialBaseArray)
{
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;
sentinel1 = op1_type->data.pointer.sentinel;
} 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;
ZigValue *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;
ZigValue *len_val = op1_val->data.x_struct.fields[slice_len_index];
op1_array_end = op1_array_index + bigint_as_usize(&len_val->data.x_bigint);
sentinel1 = ptr_type->data.pointer.sentinel;
} else if (op1_type->id == ZigTypeIdPointer && op1_type->data.pointer.ptr_len == PtrLenSingle &&
op1_type->data.pointer.child_type->id == ZigTypeIdArray)
{
ZigType *array_type = op1_type->data.pointer.child_type;
child_type = array_type->data.array.child_type;
op1_array_val = const_ptr_pointee(ira, ira->codegen, op1_val, op1->source_node);
if (op1_array_val == nullptr)
return ira->codegen->invalid_instruction;
op1_array_index = 0;
op1_array_end = array_type->data.array.len;
sentinel1 = array_type->data.array.sentinel;
} else {
ir_add_error(ira, op1, buf_sprintf("expected array, found '%s'", buf_ptr(&op1->value->type->name)));
return ira->codegen->invalid_instruction;
}
ZigValue *sentinel2 = nullptr;
ZigValue *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;
sentinel2 = op2_type->data.array.sentinel;
} else if (op2_type->id == ZigTypeIdPointer &&
op2_type->data.pointer.sentinel != nullptr &&
op2_val->data.x_ptr.special == ConstPtrSpecialBaseArray)
{
op2_type_valid = op2_type->data.pointer.child_type == child_type;
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;
sentinel2 = op2_type->data.pointer.sentinel;
} 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;
ZigValue *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;
ZigValue *len_val = op2_val->data.x_struct.fields[slice_len_index];
op2_array_end = op2_array_index + bigint_as_usize(&len_val->data.x_bigint);
sentinel2 = ptr_type->data.pointer.sentinel;
} else if (op2_type->id == ZigTypeIdPointer && op2_type->data.pointer.ptr_len == PtrLenSingle &&
op2_type->data.pointer.child_type->id == ZigTypeIdArray)
{
ZigType *array_type = op2_type->data.pointer.child_type;
op2_type_valid = array_type->data.array.child_type == child_type;
op2_array_val = const_ptr_pointee(ira, ira->codegen, op2_val, op2->source_node);
if (op2_array_val == nullptr)
return ira->codegen->invalid_instruction;
op2_array_index = 0;
op2_array_end = array_type->data.array.len;
sentinel2 = array_type->data.array.sentinel;
} 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;
}
ZigValue *sentinel;
if (sentinel1 != nullptr && sentinel2 != nullptr) {
// When there is a sentinel mismatch, no sentinel on the result. The type system
// will catch this if it is a problem.
sentinel = const_values_equal(ira->codegen, sentinel1, sentinel2) ? sentinel1 : nullptr;
} else if (sentinel1 != nullptr) {
sentinel = sentinel1;
} else if (sentinel2 != nullptr) {
sentinel = sentinel2;
} else {
sentinel = nullptr;
}
// The type of result is populated in the following if blocks
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
ZigValue *out_val = result->value;
ZigValue *out_array_val;
size_t new_len = (op1_array_end - op1_array_index) + (op2_array_end - op2_array_index);
if (op1_type->id == ZigTypeIdPointer || op2_type->id == ZigTypeIdPointer) {
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, sentinel);
out_val->data.x_ptr.special = ConstPtrSpecialRef;
out_val->data.x_ptr.data.ref.pointee = out_array_val;
out_val->type = get_pointer_to_type(ira->codegen, out_array_val->type, true);
} else if (is_slice(op1_type) || is_slice(op2_type)) {
ZigType *ptr_type = get_pointer_to_type_extra2(ira->codegen, child_type,
true, false, PtrLenUnknown, 0, 0, 0, false,
VECTOR_INDEX_NONE, nullptr, sentinel);
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, sentinel);
out_val->data.x_struct.fields = alloc_const_vals_ptrs(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 if (op1_type->id == ZigTypeIdArray || op2_type->id == ZigTypeIdArray) {
result->value->type = get_array_type(ira->codegen, child_type, new_len, sentinel);
out_array_val = out_val;
} else {
result->value->type = get_pointer_to_type_extra2(ira->codegen, child_type, true, false, PtrLenUnknown,
0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, sentinel);
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, sentinel);
out_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
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;
}
uint64_t full_len = new_len + ((sentinel != nullptr) ? 1 : 0);
out_array_val->data.x_array.data.s_none.elements = create_const_vals(full_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) {
ZigValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index];
copy_const_val(elem_dest_val, &op1_array_val->data.x_array.data.s_none.elements[i]);
elem_dest_val->parent.id = ConstParentIdArray;
elem_dest_val->parent.data.p_array.array_val = out_array_val;
elem_dest_val->parent.data.p_array.elem_index = next_index;
}
for (size_t i = op2_array_index; i < op2_array_end; i += 1, next_index += 1) {
ZigValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index];
copy_const_val(elem_dest_val, &op2_array_val->data.x_array.data.s_none.elements[i]);
elem_dest_val->parent.id = ConstParentIdArray;
elem_dest_val->parent.data.p_array.array_val = out_array_val;
elem_dest_val->parent.data.p_array.elem_index = next_index;
}
if (next_index < full_len) {
ZigValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index];
copy_const_val(elem_dest_val, sentinel);
elem_dest_val->parent.id = ConstParentIdArray;
elem_dest_val->parent.data.p_array.array_val = out_array_val;
elem_dest_val->parent.data.p_array.elem_index = next_index;
next_index += 1;
}
assert(next_index == full_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;
bool want_ptr_to_array = false;
ZigType *array_type;
ZigValue *array_val;
if (op1->value->type->id == ZigTypeIdArray) {
array_type = op1->value->type;
array_val = ir_resolve_const(ira, op1, UndefOk);
if (array_val == nullptr)
return ira->codegen->invalid_instruction;
} else if (op1->value->type->id == ZigTypeIdPointer && op1->value->type->data.pointer.ptr_len == PtrLenSingle &&
op1->value->type->data.pointer.child_type->id == ZigTypeIdArray)
{
array_type = op1->value->type->data.pointer.child_type;
IrInstruction *array_inst = ir_get_deref(ira, op1, op1, nullptr);
if (type_is_invalid(array_inst->value->type))
return ira->codegen->invalid_instruction;
array_val = ir_resolve_const(ira, array_inst, UndefOk);
if (array_val == nullptr)
return ira->codegen->invalid_instruction;
want_ptr_to_array = true;
} else {
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 mult_amt;
if (!ir_resolve_usize(ira, op2, &mult_amt))
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;
ZigType *result_array_type = get_array_type(ira->codegen, child_type, new_array_len,
array_type->data.array.sentinel);
IrInstruction *array_result;
if (array_val->special == ConstValSpecialUndef || array_val->data.x_array.special == ConstArraySpecialUndef) {
array_result = ir_const_undef(ira, &instruction->base, result_array_type);
} else {
array_result = ir_const(ira, &instruction->base, result_array_type);
ZigValue *out_val = array_result->value;
switch (type_has_one_possible_value(ira->codegen, result_array_type)) {
case OnePossibleValueInvalid:
return ira->codegen->invalid_instruction;
case OnePossibleValueYes:
goto skip_computation;
case OnePossibleValueNo:
break;
}
// TODO optimize the buf case
expand_undef_array(ira->codegen, array_val);
size_t extra_null_term = (array_type->data.array.sentinel != nullptr) ? 1 : 0;
out_val->data.x_array.data.s_none.elements = create_const_vals(new_array_len + extra_null_term);
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) {
ZigValue *elem_dest_val = &out_val->data.x_array.data.s_none.elements[i];
copy_const_val(elem_dest_val, &array_val->data.x_array.data.s_none.elements[y]);
elem_dest_val->parent.id = ConstParentIdArray;
elem_dest_val->parent.data.p_array.array_val = out_val;
elem_dest_val->parent.data.p_array.elem_index = i;
i += 1;
}
}
assert(i == new_array_len);
if (array_type->data.array.sentinel != nullptr) {
ZigValue *elem_dest_val = &out_val->data.x_array.data.s_none.elements[i];
copy_const_val(elem_dest_val, array_type->data.array.sentinel);
elem_dest_val->parent.id = ConstParentIdArray;
elem_dest_val->parent.data.p_array.array_val = out_val;
elem_dest_val->parent.data.p_array.elem_index = i;
i += 1;
}
}
skip_computation:
if (want_ptr_to_array) {
return ir_get_ref(ira, &instruction->base, array_result, true, false);
} else {
return array_result;
}
}
static IrInstruction *ir_analyze_instruction_merge_err_sets(IrAnalyze *ira,
IrInstructionMergeErrSets *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;
}
size_t errors_count = ira->codegen->errors_by_index.length;
ErrorTableEntry **errors = allocate<ErrorTableEntry *>(errors_count, "ErrorTableEntry *");
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, instruction->type_name);
deallocate(errors, errors_count, "ErrorTableEntry *");
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);
}
zig_unreachable();
}
static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira,
IrInstructionDeclVarSrc *decl_var_instruction)
{
Error err;
ZigVar *var = decl_var_instruction->var;
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;
bool is_comptime_var = ir_get_var_is_comptime(var);
bool var_class_requires_const = false;
IrInstruction *var_ptr = decl_var_instruction->ptr->child;
// if this is null, a compiler error happened and did not initialize the variable.
// if there are no compile errors there may be a missing ir_expr_wrap in pass1 IR generation.
if (var_ptr == nullptr || type_is_invalid(var_ptr->value->type)) {
ir_assert(var_ptr != nullptr || ira->codegen->errors.length != 0, &decl_var_instruction->base);
var->var_type = ira->codegen->builtin_types.entry_invalid;
return ira->codegen->invalid_instruction;
}
// The ir_build_var_decl_src call is supposed to pass a pointer to the allocation, not an initialization value.
ir_assert(var_ptr->value->type->id == ZigTypeIdPointer, &decl_var_instruction->base);
ZigType *result_type = var_ptr->value->type->data.pointer.child_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) {
zig_unreachable();
}
ZigValue *init_val = nullptr;
if (instr_is_comptime(var_ptr) && var_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) {
init_val = const_ptr_pointee(ira, ira->codegen, var_ptr->value, decl_var_instruction->base.source_node);
if (is_comptime_var) {
if (var->gen_is_const) {
var->const_value = init_val;
} else {
var->const_value = create_const_vals(1);
copy_const_val(var->const_value, init_val);
}
}
}
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 (init_val != nullptr && value_is_comptime(init_val)) {
if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec,
decl_var_instruction->base.source_node, init_val, UndefOk)))
{
result_type = ira->codegen->builtin_types.entry_invalid;
} else if (init_val->type->id == ZigTypeIdFn &&
init_val->special != ConstValSpecialUndef &&
init_val->data.x_ptr.special == ConstPtrSpecialFunction &&
init_val->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 = init_val->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;
}
while (var->next_var != nullptr) {
var = var->next_var;
}
// This must be done after possibly creating a new variable above
var->ref_count = 0;
var->ptr_instruction = var_ptr;
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, nullptr, &var->align_bytes)) {
var->var_type = ira->codegen->builtin_types.entry_invalid;
}
}
if (init_val != nullptr && value_is_comptime(init_val)) {
// Resolve ConstPtrMutInfer
if (var->gen_is_const) {
var_ptr->value->data.x_ptr.mut = ConstPtrMutComptimeConst;
} else if (is_comptime_var) {
var_ptr->value->data.x_ptr.mut = ConstPtrMutComptimeVar;
} else {
// we need a runtime ptr but we have a comptime val.
// since it's a comptime val there are no instructions for it.
// we memcpy the init value here
IrInstruction *deref = ir_get_deref(ira, var_ptr, var_ptr, nullptr);
if (type_is_invalid(deref->value->type)) {
var->var_type = ira->codegen->builtin_types.entry_invalid;
return ira->codegen->invalid_instruction;
}
// If this assertion trips, something is wrong with the IR instructions, because
// we expected the above deref to return a constant value, but it created a runtime
// instruction.
assert(deref->value->special != ConstValSpecialRuntime);
var_ptr->value->special = ConstValSpecialRuntime;
ir_analyze_store_ptr(ira, var_ptr, var_ptr, deref, false);
}
if (instr_is_comptime(var_ptr) &&
(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, var_ptr);
}
static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructionExport *instruction) {
IrInstruction *target = instruction->target->child;
if (type_is_invalid(target->value->type))
return ira->codegen->invalid_instruction;
IrInstruction *options = instruction->options->child;
if (type_is_invalid(options->value->type))
return ira->codegen->invalid_instruction;
ZigType *options_type = options->value->type;
assert(options_type->id == ZigTypeIdStruct);
TypeStructField *name_field = find_struct_type_field(options_type, buf_create_from_str("name"));
ir_assert(name_field != nullptr, &instruction->base);
IrInstruction *name_inst = ir_analyze_struct_value_field_value(ira, &instruction->base, options, name_field);
if (type_is_invalid(name_inst->value->type))
return ira->codegen->invalid_instruction;
TypeStructField *linkage_field = find_struct_type_field(options_type, buf_create_from_str("linkage"));
ir_assert(linkage_field != nullptr, &instruction->base);
IrInstruction *linkage_inst = ir_analyze_struct_value_field_value(ira, &instruction->base, options, linkage_field);
if (type_is_invalid(linkage_inst->value->type))
return ira->codegen->invalid_instruction;
TypeStructField *section_field = find_struct_type_field(options_type, buf_create_from_str("section"));
ir_assert(section_field != nullptr, &instruction->base);
IrInstruction *section_inst = ir_analyze_struct_value_field_value(ira, &instruction->base, options, section_field);
if (type_is_invalid(section_inst->value->type))
return ira->codegen->invalid_instruction;
// The `section` field is optional, we have to unwrap it first
IrInstruction *non_null_check = ir_analyze_test_non_null(ira, &instruction->base, section_inst);
bool is_non_null;
if (!ir_resolve_bool(ira, non_null_check, &is_non_null))
return ira->codegen->invalid_instruction;
IrInstruction *section_str_inst = nullptr;
if (is_non_null) {
section_str_inst = ir_analyze_optional_value_payload_value(ira, &instruction->base, section_inst, false);
if (type_is_invalid(section_str_inst->value->type))
return ira->codegen->invalid_instruction;
}
// Resolve all the comptime values
Buf *symbol_name = ir_resolve_str(ira, name_inst);
if (!symbol_name)
return ira->codegen->invalid_instruction;
if (buf_len(symbol_name) < 1) {
ir_add_error(ira, name_inst,
buf_sprintf("exported symbol name cannot be empty"));
return ira->codegen->invalid_instruction;
}
GlobalLinkageId global_linkage_id;
if (!ir_resolve_global_linkage(ira, linkage_inst, &global_linkage_id))
return ira->codegen->invalid_instruction;
Buf *section_name = nullptr;
if (section_str_inst != nullptr && !(section_name = ir_resolve_str(ira, section_str_inst)))
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;
}
Error err;
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 CallingConventionCold:
case CallingConventionNaked:
case CallingConventionInterrupt:
case CallingConventionSignal:
case CallingConventionStdcall:
case CallingConventionFastcall:
case CallingConventionVectorcall:
case CallingConventionThiscall:
case CallingConventionAPCS:
case CallingConventionAAPCS:
case CallingConventionAAPCSVFP:
add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, cc);
fn_entry->section_name = section_name;
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 ZigTypeIdArray: {
bool ok_type;
if ((err = type_allowed_in_extern(ira->codegen, target->value->type->data.array.child_type, &ok_type)))
return ira->codegen->invalid_instruction;
if (!ok_type) {
ir_add_error(ira, target,
buf_sprintf("array element type '%s' not extern-compatible",
buf_ptr(&target->value->type->data.array.child_type->name)));
} 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 ZigTypeIdOpaque:
case ZigTypeIdFnFrame:
case ZigTypeIdAnyFrame:
ir_add_error(ira, target,
buf_sprintf("invalid export target '%s'", buf_ptr(&type_value->name)));
break;
}
} break;
case ZigTypeIdInt:
break;
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdFloat:
case ZigTypeIdPointer:
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 ZigTypeIdOpaque:
case ZigTypeIdEnumLiteral:
case ZigTypeIdFnFrame:
case ZigTypeIdAnyFrame:
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;
add_var_export(ira->codegen, var, buf_ptr(symbol_name), global_linkage_id);
var->section_name = section_name;
}
}
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)
{
ZigType *ptr_to_stack_trace_type = get_pointer_to_type(ira->codegen, get_stack_trace_type(ira->codegen), false);
if (instruction->optional == IrInstructionErrorReturnTrace::Null) {
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);
ZigValue *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 = ptr_to_stack_trace_type;
return new_instruction;
}
}
static IrInstruction *ir_analyze_instruction_error_union(IrAnalyze *ira,
IrInstructionErrorUnion *instruction)
{
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type);
result->value->special = ConstValSpecialLazy;
LazyValueErrUnionType *lazy_err_union_type = allocate<LazyValueErrUnionType>(1, "LazyValueErrUnionType");
lazy_err_union_type->ira = ira; ira_ref(ira);
result->value->data.x_lazy = &lazy_err_union_type->base;
lazy_err_union_type->base.id = LazyValueIdErrUnionType;
lazy_err_union_type->err_set_type = instruction->err_set->child;
if (ir_resolve_type_lazy(ira, lazy_err_union_type->err_set_type) == nullptr)
return ira->codegen->invalid_instruction;
lazy_err_union_type->payload_type = instruction->payload->child;
if (ir_resolve_type_lazy(ira, lazy_err_union_type->payload_type) == nullptr)
return ira->codegen->invalid_instruction;
return result;
}
static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_inst, ZigType *var_type,
uint32_t align, const char *name_hint, bool force_comptime)
{
Error err;
ZigValue *pointee = create_const_vals(1);
pointee->special = ConstValSpecialUndef;
pointee->llvm_align = align;
IrInstructionAllocaGen *result = ir_build_alloca_gen(ira, source_inst, align, name_hint);
result->base.value->special = ConstValSpecialStatic;
result->base.value->data.x_ptr.special = ConstPtrSpecialRef;
result->base.value->data.x_ptr.mut = force_comptime ? ConstPtrMutComptimeVar : ConstPtrMutInfer;
result->base.value->data.x_ptr.data.ref.pointee = pointee;
bool var_type_has_bits;
if ((err = type_has_bits2(ira->codegen, var_type, &var_type_has_bits)))
return ira->codegen->invalid_instruction;
if (align != 0) {
if ((err = type_resolve(ira->codegen, var_type, ResolveStatusAlignmentKnown)))
return ira->codegen->invalid_instruction;
if (!var_type_has_bits) {
ir_add_error(ira, source_inst,
buf_sprintf("variable '%s' of zero-bit type '%s' has no in-memory representation, it cannot be aligned",
name_hint, buf_ptr(&var_type->name)));
return ira->codegen->invalid_instruction;
}
}
assert(result->base.value->data.x_ptr.special != ConstPtrSpecialInvalid);
pointee->type = var_type;
result->base.value->type = get_pointer_to_type_extra(ira->codegen, var_type, false, false,
PtrLenSingle, align, 0, 0, false);
if (!force_comptime) {
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
if (fn_entry != nullptr) {
fn_entry->alloca_gen_list.append(result);
}
}
result->base.is_gen = true;
return &result->base;
}
static ZigType *ir_result_loc_expected_type(IrAnalyze *ira, IrInstruction *suspend_source_instr,
ResultLoc *result_loc)
{
switch (result_loc->id) {
case ResultLocIdInvalid:
case ResultLocIdPeerParent:
zig_unreachable();
case ResultLocIdNone:
case ResultLocIdVar:
case ResultLocIdBitCast:
case ResultLocIdCast:
return nullptr;
case ResultLocIdInstruction:
return result_loc->source_instruction->child->value->type;
case ResultLocIdReturn:
return ira->explicit_return_type;
case ResultLocIdPeer:
return reinterpret_cast<ResultLocPeer*>(result_loc)->parent->resolved_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 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 void set_up_result_loc_for_inferred_comptime(IrInstruction *ptr) {
ZigValue *undef_child = create_const_vals(1);
undef_child->type = ptr->value->type->data.pointer.child_type;
undef_child->special = ConstValSpecialUndef;
ptr->value->special = ConstValSpecialStatic;
ptr->value->data.x_ptr.mut = ConstPtrMutInfer;
ptr->value->data.x_ptr.special = ConstPtrSpecialRef;
ptr->value->data.x_ptr.data.ref.pointee = undef_child;
}
static Error ir_result_has_type(IrAnalyze *ira, ResultLoc *result_loc, bool *out) {
switch (result_loc->id) {
case ResultLocIdInvalid:
case ResultLocIdPeerParent:
zig_unreachable();
case ResultLocIdNone:
case ResultLocIdPeer:
*out = false;
return ErrorNone;
case ResultLocIdReturn:
case ResultLocIdInstruction:
case ResultLocIdBitCast:
*out = true;
return ErrorNone;
case ResultLocIdCast: {
ResultLocCast *result_cast = reinterpret_cast<ResultLocCast *>(result_loc);
ZigType *dest_type = ir_resolve_type(ira, result_cast->base.source_instruction->child);
if (type_is_invalid(dest_type))
return ErrorSemanticAnalyzeFail;
*out = (dest_type != ira->codegen->builtin_types.entry_var);
return ErrorNone;
}
case ResultLocIdVar:
*out = reinterpret_cast<ResultLocVar *>(result_loc)->var->decl_node->data.variable_declaration.type != nullptr;
return ErrorNone;
}
zig_unreachable();
}
static IrInstruction *ir_resolve_no_result_loc(IrAnalyze *ira, IrInstruction *suspend_source_instr,
ResultLoc *result_loc, ZigType *value_type)
{
if (type_is_invalid(value_type))
return ira->codegen->invalid_instruction;
IrInstructionAllocaGen *alloca_gen = ir_build_alloca_gen(ira, suspend_source_instr, 0, "");
alloca_gen->base.value->type = get_pointer_to_type_extra(ira->codegen, value_type, false, false,
PtrLenSingle, 0, 0, 0, false);
set_up_result_loc_for_inferred_comptime(&alloca_gen->base);
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
if (fn_entry != nullptr && get_scope_typeof(suspend_source_instr->scope) == nullptr) {
fn_entry->alloca_gen_list.append(alloca_gen);
}
result_loc->written = true;
result_loc->resolved_loc = &alloca_gen->base;
return result_loc->resolved_loc;
}
// when calling this function, at the callsite must check for result type noreturn and propagate it up
static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspend_source_instr,
ResultLoc *result_loc, ZigType *value_type, IrInstruction *value, bool force_runtime,
bool allow_discard)
{
Error err;
if (result_loc->resolved_loc != nullptr) {
// allow to redo the result location if the value is known and comptime and the previous one isn't
if (value == nullptr || !instr_is_comptime(value) || instr_is_comptime(result_loc->resolved_loc)) {
return result_loc->resolved_loc;
}
}
result_loc->gen_instruction = value;
result_loc->implicit_elem_type = value_type;
switch (result_loc->id) {
case ResultLocIdInvalid:
case ResultLocIdPeerParent:
zig_unreachable();
case ResultLocIdNone: {
if (value != nullptr) {
return nullptr;
}
// need to return a result location and don't have one. use a stack allocation
return ir_resolve_no_result_loc(ira, suspend_source_instr, result_loc, value_type);
}
case ResultLocIdVar: {
ResultLocVar *result_loc_var = reinterpret_cast<ResultLocVar *>(result_loc);
assert(result_loc->source_instruction->id == IrInstructionIdAllocaSrc);
IrInstructionAllocaSrc *alloca_src =
reinterpret_cast<IrInstructionAllocaSrc *>(result_loc->source_instruction);
ZigVar *var = result_loc_var->var;
if (var->var_type != nullptr && !ir_get_var_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,
buf_create_from_str(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;
var->next_var = new_var;
var = new_var;
}
if (value_type->id == ZigTypeIdUnreachable || value_type->id == ZigTypeIdOpaque) {
ir_add_error(ira, result_loc->source_instruction,
buf_sprintf("variable of type '%s' not allowed", buf_ptr(&value_type->name)));
return ira->codegen->invalid_instruction;
}
if (alloca_src->base.child == nullptr || var->ptr_instruction == nullptr) {
bool force_comptime;
if (!ir_resolve_comptime(ira, alloca_src->is_comptime->child, &force_comptime))
return ira->codegen->invalid_instruction;
uint32_t align = 0;
if (alloca_src->align != nullptr && !ir_resolve_align(ira, alloca_src->align->child, nullptr, &align)) {
return ira->codegen->invalid_instruction;
}
IrInstruction *alloca_gen = ir_analyze_alloca(ira, result_loc->source_instruction,
value_type, align, alloca_src->name_hint, force_comptime);
if (force_runtime) {
alloca_gen->value->data.x_ptr.mut = ConstPtrMutRuntimeVar;
alloca_gen->value->special = ConstValSpecialRuntime;
}
if (alloca_src->base.child != nullptr && !result_loc->written) {
alloca_src->base.child->ref_count = 0;
}
alloca_src->base.child = alloca_gen;
var->ptr_instruction = alloca_gen;
}
result_loc->written = true;
result_loc->resolved_loc = alloca_src->base.child;
return alloca_src->base.child;
}
case ResultLocIdInstruction: {
result_loc->written = true;
result_loc->resolved_loc = result_loc->source_instruction->child;
return result_loc->resolved_loc;
}
case ResultLocIdReturn: {
if (value != nullptr) {
reinterpret_cast<ResultLocReturn *>(result_loc)->implicit_return_type_done = true;
ira->src_implicit_return_type_list.append(value);
}
result_loc->written = true;
result_loc->resolved_loc = ira->return_ptr;
return result_loc->resolved_loc;
}
case ResultLocIdPeer: {
ResultLocPeer *result_peer = reinterpret_cast<ResultLocPeer *>(result_loc);
ResultLocPeerParent *peer_parent = result_peer->parent;
if (peer_parent->peers.length == 1) {
IrInstruction *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, peer_parent->parent,
value_type, value, force_runtime, true);
result_peer->suspend_pos.basic_block_index = SIZE_MAX;
result_peer->suspend_pos.instruction_index = SIZE_MAX;
if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) ||
parent_result_loc->value->type->id == ZigTypeIdUnreachable)
{
return parent_result_loc;
}
result_loc->written = true;
result_loc->resolved_loc = parent_result_loc;
return result_loc->resolved_loc;
}
bool is_condition_comptime;
if (!ir_resolve_comptime(ira, peer_parent->is_comptime->child, &is_condition_comptime))
return ira->codegen->invalid_instruction;
if (is_condition_comptime) {
peer_parent->skipped = true;
return ir_resolve_result(ira, suspend_source_instr, peer_parent->parent,
value_type, value, force_runtime, true);
}
bool peer_parent_has_type;
if ((err = ir_result_has_type(ira, peer_parent->parent, &peer_parent_has_type)))
return ira->codegen->invalid_instruction;
if (peer_parent_has_type) {
peer_parent->skipped = true;
IrInstruction *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, peer_parent->parent,
value_type, value, force_runtime || !is_condition_comptime, true);
if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) ||
parent_result_loc->value->type->id == ZigTypeIdUnreachable)
{
return parent_result_loc;
}
peer_parent->parent->written = true;
result_loc->written = true;
result_loc->resolved_loc = parent_result_loc;
return result_loc->resolved_loc;
}
if (peer_parent->resolved_type == nullptr) {
if (peer_parent->end_bb->suspend_instruction_ref == nullptr) {
peer_parent->end_bb->suspend_instruction_ref = suspend_source_instr;
}
IrInstruction *unreach_inst = ira_suspend(ira, suspend_source_instr, result_peer->next_bb,
&result_peer->suspend_pos);
if (result_peer->next_bb == nullptr) {
ir_start_next_bb(ira);
}
return unreach_inst;
}
IrInstruction *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, peer_parent->parent,
peer_parent->resolved_type, nullptr, force_runtime, true);
if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) ||
parent_result_loc->value->type->id == ZigTypeIdUnreachable)
{
return parent_result_loc;
}
// because is_condition_comptime is false, we mark this a runtime pointer
parent_result_loc->value->special = ConstValSpecialRuntime;
result_loc->written = true;
result_loc->resolved_loc = parent_result_loc;
return result_loc->resolved_loc;
}
case ResultLocIdCast: {
ResultLocCast *result_cast = reinterpret_cast<ResultLocCast *>(result_loc);
ZigType *dest_type = ir_resolve_type(ira, result_cast->base.source_instruction->child);
if (type_is_invalid(dest_type))
return ira->codegen->invalid_instruction;
if (dest_type == ira->codegen->builtin_types.entry_var) {
return ir_resolve_no_result_loc(ira, suspend_source_instr, result_loc, value_type);
}
IrInstruction *casted_value;
if (value != nullptr) {
casted_value = ir_implicit_cast(ira, value, dest_type);
if (type_is_invalid(casted_value->value->type))
return ira->codegen->invalid_instruction;
dest_type = casted_value->value->type;
} else {
casted_value = nullptr;
}
IrInstruction *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, result_cast->parent,
dest_type, casted_value, force_runtime, true);
if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) ||
parent_result_loc->value->type->id == ZigTypeIdUnreachable)
{
return parent_result_loc;
}
ZigType *parent_ptr_type = parent_result_loc->value->type;
assert(parent_ptr_type->id == ZigTypeIdPointer);
if ((err = type_resolve(ira->codegen, parent_ptr_type->data.pointer.child_type,
ResolveStatusAlignmentKnown)))
{
return ira->codegen->invalid_instruction;
}
uint64_t parent_ptr_align = get_ptr_align(ira->codegen, parent_ptr_type);
if ((err = type_resolve(ira->codegen, value_type, ResolveStatusAlignmentKnown))) {
return ira->codegen->invalid_instruction;
}
if (!type_has_bits(value_type)) {
parent_ptr_align = 0;
}
// If we're casting from a sentinel-terminated array to a non-sentinel-terminated array,
// we actually need the result location pointer to *not* have a sentinel. Otherwise the generated
// memcpy will write an extra byte to the destination, and THAT'S NO GOOD.
ZigType *ptr_elem_type;
if (value_type->id == ZigTypeIdArray && value_type->data.array.sentinel != nullptr &&
dest_type->id == ZigTypeIdArray && dest_type->data.array.sentinel == nullptr)
{
ptr_elem_type = get_array_type(ira->codegen, value_type->data.array.child_type,
value_type->data.array.len, nullptr);
} else {
ptr_elem_type = value_type;
}
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, ptr_elem_type,
parent_ptr_type->data.pointer.is_const, parent_ptr_type->data.pointer.is_volatile, PtrLenSingle,
parent_ptr_align, 0, 0, parent_ptr_type->data.pointer.allow_zero);
ConstCastOnly const_cast_result = types_match_const_cast_only(ira,
parent_result_loc->value->type, ptr_type,
result_cast->base.source_instruction->source_node, false);
if (const_cast_result.id == ConstCastResultIdInvalid)
return ira->codegen->invalid_instruction;
if (const_cast_result.id != ConstCastResultIdOk) {
if (allow_discard) {
return parent_result_loc;
}
// We will not be able to provide a result location for this value. Create
// a new result location.
result_cast->parent->written = false;
return ir_resolve_no_result_loc(ira, suspend_source_instr, result_loc, value_type);
}
result_loc->written = true;
result_loc->resolved_loc = ir_analyze_ptr_cast(ira, suspend_source_instr, parent_result_loc,
ptr_type, result_cast->base.source_instruction, false);
return result_loc->resolved_loc;
}
case ResultLocIdBitCast: {
ResultLocBitCast *result_bit_cast = reinterpret_cast<ResultLocBitCast *>(result_loc);
ZigType *dest_type = ir_resolve_type(ira, result_bit_cast->base.source_instruction->child);
if (type_is_invalid(dest_type))
return ira->codegen->invalid_instruction;
if (get_codegen_ptr_type(dest_type) != nullptr) {
ir_add_error(ira, result_loc->source_instruction,
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, result_loc->source_instruction,
buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
if (get_codegen_ptr_type(value_type) != nullptr) {
ir_add_error(ira, suspend_source_instr,
buf_sprintf("unable to @bitCast from pointer type '%s'", buf_ptr(&value_type->name)));
return ira->codegen->invalid_instruction;
}
if (!type_can_bit_cast(value_type)) {
ir_add_error(ira, suspend_source_instr,
buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&value_type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *bitcasted_value;
if (value != nullptr) {
bitcasted_value = ir_analyze_bit_cast(ira, result_loc->source_instruction, value, dest_type);
dest_type = bitcasted_value->value->type;
} else {
bitcasted_value = nullptr;
}
if (bitcasted_value != nullptr && type_is_invalid(bitcasted_value->value->type)) {
return bitcasted_value;
}
IrInstruction *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, result_bit_cast->parent,
dest_type, bitcasted_value, force_runtime, true);
if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) ||
parent_result_loc->value->type->id == ZigTypeIdUnreachable)
{
return parent_result_loc;
}
ZigType *parent_ptr_type = parent_result_loc->value->type;
assert(parent_ptr_type->id == ZigTypeIdPointer);
ZigType *child_type = parent_ptr_type->data.pointer.child_type;
bool has_bits;
if ((err = type_has_bits2(ira->codegen, child_type, &has_bits))) {
return ira->codegen->invalid_instruction;
}
// This happens when the bitCast result is assigned to _
if (!has_bits) {
assert(allow_discard);
return parent_result_loc;
}
if ((err = type_resolve(ira->codegen, child_type, ResolveStatusAlignmentKnown))) {
return ira->codegen->invalid_instruction;
}
uint64_t parent_ptr_align = get_ptr_align(ira->codegen, parent_ptr_type);
if ((err = type_resolve(ira->codegen, value_type, ResolveStatusAlignmentKnown))) {
return ira->codegen->invalid_instruction;
}
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value_type,
parent_ptr_type->data.pointer.is_const, parent_ptr_type->data.pointer.is_volatile, PtrLenSingle,
parent_ptr_align, 0, 0, parent_ptr_type->data.pointer.allow_zero);
result_loc->written = true;
result_loc->resolved_loc = ir_analyze_ptr_cast(ira, suspend_source_instr, parent_result_loc,
ptr_type, result_bit_cast->base.source_instruction, false);
return result_loc->resolved_loc;
}
}
zig_unreachable();
}
static IrInstruction *ir_resolve_result(IrAnalyze *ira, IrInstruction *suspend_source_instr,
ResultLoc *result_loc_pass1, ZigType *value_type, IrInstruction *value, bool force_runtime,
bool allow_discard)
{
Error err;
if (!allow_discard && result_loc_pass1->id == ResultLocIdInstruction &&
instr_is_comptime(result_loc_pass1->source_instruction) &&
result_loc_pass1->source_instruction->value->type->id == ZigTypeIdPointer &&
result_loc_pass1->source_instruction->value->data.x_ptr.special == ConstPtrSpecialDiscard)
{
result_loc_pass1 = no_result_loc();
}
bool was_written = result_loc_pass1->written;
IrInstruction *result_loc = ir_resolve_result_raw(ira, suspend_source_instr, result_loc_pass1, value_type,
value, force_runtime, allow_discard);
if (result_loc == nullptr || (instr_is_unreachable(result_loc) || type_is_invalid(result_loc->value->type)))
return result_loc;
if ((force_runtime || (value != nullptr && !instr_is_comptime(value))) &&
result_loc_pass1->written && result_loc->value->data.x_ptr.mut == ConstPtrMutInfer)
{
result_loc->value->special = ConstValSpecialRuntime;
}
InferredStructField *isf = result_loc->value->type->data.pointer.inferred_struct_field;
if (isf != nullptr) {
TypeStructField *field;
IrInstruction *casted_ptr;
if (isf->already_resolved) {
field = find_struct_type_field(isf->inferred_struct_type, isf->field_name);
casted_ptr = result_loc;
} else {
isf->already_resolved = true;
// Now it's time to add the field to the struct type.
uint32_t old_field_count = isf->inferred_struct_type->data.structure.src_field_count;
uint32_t new_field_count = old_field_count + 1;
isf->inferred_struct_type->data.structure.src_field_count = new_field_count;
isf->inferred_struct_type->data.structure.fields = realloc_type_struct_fields(
isf->inferred_struct_type->data.structure.fields, old_field_count, new_field_count);
field = isf->inferred_struct_type->data.structure.fields[old_field_count];
field->name = isf->field_name;
field->type_entry = value_type;
field->type_val = create_const_type(ira->codegen, field->type_entry);
field->src_index = old_field_count;
field->decl_node = value ? value->source_node : suspend_source_instr->source_node;
if (value && instr_is_comptime(value)) {
ZigValue *val = ir_resolve_const(ira, value, UndefOk);
if (!val)
return ira->codegen->invalid_instruction;
field->is_comptime = true;
field->init_val = create_const_vals(1);
copy_const_val(field->init_val, val);
return result_loc;
}
ZigType *struct_ptr_type = get_pointer_to_type(ira->codegen, isf->inferred_struct_type, false);
if (instr_is_comptime(result_loc)) {
casted_ptr = ir_const(ira, suspend_source_instr, struct_ptr_type);
copy_const_val(casted_ptr->value, result_loc->value);
casted_ptr->value->type = struct_ptr_type;
} else {
casted_ptr = result_loc;
}
if (instr_is_comptime(casted_ptr)) {
ZigValue *ptr_val = ir_resolve_const(ira, casted_ptr, UndefBad);
if (!ptr_val)
return ira->codegen->invalid_instruction;
if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) {
ZigValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val,
suspend_source_instr->source_node);
struct_val->special = ConstValSpecialStatic;
struct_val->data.x_struct.fields = realloc_const_vals_ptrs(struct_val->data.x_struct.fields,
old_field_count, new_field_count);
ZigValue *field_val = struct_val->data.x_struct.fields[old_field_count];
field_val->special = ConstValSpecialUndef;
field_val->type = field->type_entry;
field_val->parent.id = ConstParentIdStruct;
field_val->parent.data.p_struct.struct_val = struct_val;
field_val->parent.data.p_struct.field_index = old_field_count;
}
}
}
result_loc = ir_analyze_struct_field_ptr(ira, suspend_source_instr, field, casted_ptr,
isf->inferred_struct_type, true);
result_loc_pass1->resolved_loc = result_loc;
}
if (was_written) {
return result_loc;
}
ir_assert(result_loc->value->type->id == ZigTypeIdPointer, suspend_source_instr);
ZigType *actual_elem_type = result_loc->value->type->data.pointer.child_type;
if (actual_elem_type->id == ZigTypeIdOptional && value_type->id != ZigTypeIdOptional &&
value_type->id != ZigTypeIdNull)
{
bool same_comptime_repr = types_have_same_zig_comptime_repr(ira->codegen, actual_elem_type, value_type);
if (!same_comptime_repr) {
bool has_bits;
if ((err = type_has_bits2(ira->codegen, value_type, &has_bits)))
return ira->codegen->invalid_instruction;
if (has_bits) {
result_loc_pass1->written = false;
return ir_analyze_unwrap_optional_payload(ira, suspend_source_instr, result_loc, false, true);
}
}
} else if (actual_elem_type->id == ZigTypeIdErrorUnion && value_type->id != ZigTypeIdErrorUnion) {
bool has_bits;
if ((err = type_has_bits2(ira->codegen, value_type, &has_bits)))
return ira->codegen->invalid_instruction;
if (has_bits) {
if (value_type->id == ZigTypeIdErrorSet) {
return ir_analyze_unwrap_err_code(ira, suspend_source_instr, result_loc, true);
} else {
IrInstruction *unwrapped_err_ptr = ir_analyze_unwrap_error_payload(ira, suspend_source_instr,
result_loc, false, true);
ZigType *actual_payload_type = actual_elem_type->data.error_union.payload_type;
if (actual_payload_type->id == ZigTypeIdOptional && value_type->id != ZigTypeIdOptional &&
value_type->id != ZigTypeIdNull)
{
return ir_analyze_unwrap_optional_payload(ira, suspend_source_instr, unwrapped_err_ptr, false, true);
} else {
return unwrapped_err_ptr;
}
}
}
}
return result_loc;
}
static IrInstruction *ir_analyze_instruction_resolve_result(IrAnalyze *ira,
IrInstructionResolveResult *instruction)
{
ZigType *implicit_elem_type;
if (instruction->ty == nullptr) {
if (instruction->result_loc->id == ResultLocIdCast) {
implicit_elem_type = ir_resolve_type(ira,
instruction->result_loc->source_instruction->child);
if (type_is_invalid(implicit_elem_type))
return ira->codegen->invalid_instruction;
} else if (instruction->result_loc->id == ResultLocIdReturn) {
implicit_elem_type = ira->explicit_return_type;
if (type_is_invalid(implicit_elem_type))
return ira->codegen->invalid_instruction;
} else {
implicit_elem_type = ira->codegen->builtin_types.entry_var;
}
if (implicit_elem_type == ira->codegen->builtin_types.entry_var) {
Buf *bare_name = buf_alloc();
Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct),
instruction->base.scope, instruction->base.source_node, bare_name);
StructSpecial struct_special = StructSpecialInferredStruct;
if (instruction->base.source_node->type == NodeTypeContainerInitExpr &&
instruction->base.source_node->data.container_init_expr.kind == ContainerInitKindArray)
{
struct_special = StructSpecialInferredTuple;
}
ZigType *inferred_struct_type = get_partial_container_type(ira->codegen,
instruction->base.scope, ContainerKindStruct, instruction->base.source_node,
buf_ptr(name), bare_name, ContainerLayoutAuto);
inferred_struct_type->data.structure.special = struct_special;
inferred_struct_type->data.structure.resolve_status = ResolveStatusBeingInferred;
implicit_elem_type = inferred_struct_type;
}
} else {
implicit_elem_type = ir_resolve_type(ira, instruction->ty->child);
if (type_is_invalid(implicit_elem_type))
return ira->codegen->invalid_instruction;
}
IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc,
implicit_elem_type, nullptr, false, true);
if (result_loc != nullptr)
return result_loc;
ZigFn *fn = exec_fn_entry(ira->new_irb.exec);
if (fn != nullptr && fn->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync &&
instruction->result_loc->id == ResultLocIdReturn)
{
result_loc = ir_resolve_result(ira, &instruction->base, no_result_loc(),
implicit_elem_type, nullptr, false, true);
if (result_loc != nullptr &&
(type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc)))
{
return result_loc;
}
result_loc->value->special = ConstValSpecialRuntime;
return result_loc;
}
IrInstruction *result = ir_const(ira, &instruction->base, implicit_elem_type);
result->value->special = ConstValSpecialUndef;
IrInstruction *ptr = ir_get_ref(ira, &instruction->base, result, false, false);
ptr->value->data.x_ptr.mut = ConstPtrMutComptimeVar;
return ptr;
}
static void ir_reset_result(ResultLoc *result_loc) {
result_loc->written = false;
result_loc->resolved_loc = nullptr;
result_loc->gen_instruction = nullptr;
result_loc->implicit_elem_type = nullptr;
switch (result_loc->id) {
case ResultLocIdInvalid:
zig_unreachable();
case ResultLocIdPeerParent: {
ResultLocPeerParent *peer_parent = reinterpret_cast<ResultLocPeerParent *>(result_loc);
peer_parent->skipped = false;
peer_parent->done_resuming = false;
peer_parent->resolved_type = nullptr;
for (size_t i = 0; i < peer_parent->peers.length; i += 1) {
ir_reset_result(&peer_parent->peers.at(i)->base);
}
break;
}
case ResultLocIdVar: {
IrInstructionAllocaSrc *alloca_src =
reinterpret_cast<IrInstructionAllocaSrc *>(result_loc->source_instruction);
alloca_src->base.child = nullptr;
break;
}
case ResultLocIdReturn:
reinterpret_cast<ResultLocReturn *>(result_loc)->implicit_return_type_done = false;
break;
case ResultLocIdPeer:
case ResultLocIdNone:
case ResultLocIdInstruction:
case ResultLocIdBitCast:
case ResultLocIdCast:
break;
}
}
static IrInstruction *ir_analyze_instruction_reset_result(IrAnalyze *ira, IrInstructionResetResult *instruction) {
ir_reset_result(instruction->result_loc);
return ir_const_void(ira, &instruction->base);
}
static IrInstruction *get_async_call_result_loc(IrAnalyze *ira, IrInstruction *source_instr,
ZigType *fn_ret_type, bool is_async_call_builtin, IrInstruction **args_ptr, size_t args_len,
IrInstruction *ret_ptr_uncasted)
{
ir_assert(is_async_call_builtin, source_instr);
if (type_is_invalid(ret_ptr_uncasted->value->type))
return ira->codegen->invalid_instruction;
if (ret_ptr_uncasted->value->type->id == ZigTypeIdVoid) {
// Result location will be inside the async frame.
return nullptr;
}
return ir_implicit_cast(ira, ret_ptr_uncasted, get_pointer_to_type(ira->codegen, fn_ret_type, false));
}
static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstruction *source_instr, ZigFn *fn_entry,
ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count,
IrInstruction *casted_new_stack, bool is_async_call_builtin, IrInstruction *ret_ptr_uncasted,
ResultLoc *call_result_loc)
{
if (fn_entry == nullptr) {
if (fn_type->data.fn.fn_type_id.cc != CallingConventionAsync) {
ir_add_error(ira, fn_ref,
buf_sprintf("expected async function, found '%s'", buf_ptr(&fn_type->name)));
return ira->codegen->invalid_instruction;
}
if (casted_new_stack == nullptr) {
ir_add_error(ira, fn_ref, buf_sprintf("function is not comptime-known; @asyncCall required"));
return ira->codegen->invalid_instruction;
}
}
if (casted_new_stack != nullptr) {
ZigType *fn_ret_type = fn_type->data.fn.fn_type_id.return_type;
IrInstruction *ret_ptr = get_async_call_result_loc(ira, source_instr, fn_ret_type, is_async_call_builtin,
casted_args, arg_count, ret_ptr_uncasted);
if (ret_ptr != nullptr && type_is_invalid(ret_ptr->value->type))
return ira->codegen->invalid_instruction;
ZigType *anyframe_type = get_any_frame_type(ira->codegen, fn_ret_type);
IrInstructionCallGen *call_gen = ir_build_call_gen(ira, source_instr, fn_entry, fn_ref,
arg_count, casted_args, CallModifierAsync, casted_new_stack,
is_async_call_builtin, ret_ptr, anyframe_type);
return &call_gen->base;
} else {
ZigType *frame_type = get_fn_frame_type(ira->codegen, fn_entry);
IrInstruction *result_loc = ir_resolve_result(ira, source_instr, call_result_loc,
frame_type, nullptr, true, false);
if (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc)) {
return result_loc;
}
result_loc = ir_implicit_cast(ira, result_loc, get_pointer_to_type(ira->codegen, frame_type, false));
if (type_is_invalid(result_loc->value->type))
return ira->codegen->invalid_instruction;
return &ir_build_call_gen(ira, source_instr, fn_entry, fn_ref, arg_count,
casted_args, CallModifierAsync, casted_new_stack,
is_async_call_builtin, result_loc, frame_type)->base;
}
}
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;
}
ZigValue *arg_val = ir_resolve_const(ira, casted_arg, UndefOk);
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_comptime;
if (!comptime_arg) {
switch (type_requires_comptime(ira->codegen, casted_arg->value->type)) {
case ReqCompTimeInvalid:
return false;
case ReqCompTimeYes:
comptime_arg = true;
break;
case ReqCompTimeNo:
break;
}
}
ZigValue *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) {
copy_const_val(&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) {
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 IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ZigVar *var) {
while (var->next_var != nullptr) {
var = var->next_var;
}
if (var->var_type == nullptr || type_is_invalid(var->var_type))
return ira->codegen->invalid_instruction;
bool is_volatile = false;
ZigType *var_ptr_type = get_pointer_to_type_extra(ira->codegen, var->var_type,
var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0, false);
if (var->ptr_instruction != nullptr) {
return ir_implicit_cast(ira, var->ptr_instruction, var_ptr_type);
}
bool comptime_var_mem = ir_get_var_is_comptime(var);
bool linkage_makes_it_runtime = var->decl_node->data.variable_declaration.is_extern;
IrInstruction *result = ir_build_var_ptr(&ira->new_irb,
instruction->scope, instruction->source_node, var);
result->value->type = var_ptr_type;
if (!linkage_makes_it_runtime && !var->is_thread_local && value_is_comptime(var->const_value)) {
ZigValue *val = var->const_value;
switch (val->special) {
case ConstValSpecialRuntime:
break;
case ConstValSpecialStatic: // fallthrough
case ConstValSpecialLazy: // 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;
}
result->value->special = ConstValSpecialStatic;
result->value->data.x_ptr.mut = ptr_mut;
result->value->data.x_ptr.special = ConstPtrSpecialRef;
result->value->data.x_ptr.data.ref.pointee = val;
return result;
}
}
}
bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr);
result->value->data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack;
return result;
}
// This function is called when a comptime value becomes accessible at runtime.
static void mark_comptime_value_escape(IrAnalyze *ira, IrInstruction *source_instr, ZigValue *val) {
ir_assert(value_is_comptime(val), source_instr);
if (val->special == ConstValSpecialUndef)
return;
if (val->type->id == ZigTypeIdFn && val->type->data.fn.fn_type_id.cc == CallingConventionUnspecified) {
ir_assert(val->data.x_ptr.special == ConstPtrSpecialFunction, source_instr);
if (val->data.x_ptr.data.fn.fn_entry->non_async_node == nullptr) {
val->data.x_ptr.data.fn.fn_entry->non_async_node = source_instr->source_node;
}
}
}
static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *ptr, IrInstruction *uncasted_value, bool allow_write_through_const)
{
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);
}
if (ptr->value->type->data.pointer.is_const && !allow_write_through_const) {
ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant"));
return ira->codegen->invalid_instruction;
}
ZigType *child_type = ptr->value->type->data.pointer.child_type;
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 (!allow_write_through_const && 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 ((allow_write_through_const && ptr->value->data.x_ptr.mut == ConstPtrMutComptimeConst) ||
ptr->value->data.x_ptr.mut == ConstPtrMutComptimeVar ||
ptr->value->data.x_ptr.mut == ConstPtrMutInfer)
{
if (instr_is_comptime(value)) {
ZigValue *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) {
copy_const_val(dest_val, value->value);
if (ptr->value->data.x_ptr.mut == ConstPtrMutComptimeVar &&
!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);
}
}
if (ptr->value->data.x_ptr.mut == ConstPtrMutInfer) {
ptr->value->special = ConstValSpecialRuntime;
} else {
ir_add_error(ira, source_instr,
buf_sprintf("cannot store runtime value in compile time variable"));
ZigValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, ptr->value);
dest_val->type = ira->codegen->builtin_types.entry_invalid;
return ira->codegen->invalid_instruction;
}
}
}
if (ptr->value->type->data.pointer.inferred_struct_field != nullptr &&
child_type == ira->codegen->builtin_types.entry_var)
{
child_type = ptr->value->type->data.pointer.inferred_struct_field->inferred_struct_type;
}
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;
}
if (instr_is_comptime(value)) {
mark_comptime_value_escape(ira, source_instr, value->value);
}
// If this is a store to a pointer with a runtime-known vector index,
// we have to figure out the IrInstruction which represents the index and
// emit a IrInstructionVectorStoreElem, or emit a compile error
// explaining why it is impossible for this store to work. Which is that
// the pointer address is of the vector; without the element index being known
// we cannot properly perform the insertion.
if (ptr->value->type->data.pointer.vector_index == VECTOR_INDEX_RUNTIME) {
if (ptr->id == IrInstructionIdElemPtr) {
IrInstructionElemPtr *elem_ptr = (IrInstructionElemPtr *)ptr;
return ir_build_vector_store_elem(ira, source_instr, elem_ptr->array_ptr,
elem_ptr->elem_index, value);
}
ir_add_error(ira, ptr,
buf_sprintf("unable to determine vector element index of type '%s'",
buf_ptr(&ptr->value->type->name)));
return ira->codegen->invalid_instruction;
}
IrInstructionStorePtr *store_ptr = ir_build_store_ptr(&ira->new_irb, source_instr->scope,
source_instr->source_node, ptr, value);
return &store_ptr->base;
}
static IrInstruction *analyze_casted_new_stack(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *new_stack, bool is_async_call_builtin, ZigFn *fn_entry)
{
if (new_stack == nullptr)
return nullptr;
if (!is_async_call_builtin &&
arch_stack_pointer_register_name(ira->codegen->zig_target->arch) == nullptr)
{
ir_add_error(ira, source_instr,
buf_sprintf("target arch '%s' does not support calling with a new stack",
target_arch_name(ira->codegen->zig_target->arch)));
}
if (is_async_call_builtin &&
fn_entry != nullptr && new_stack->value->type->id == ZigTypeIdPointer &&
new_stack->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame)
{
ZigType *needed_frame_type = get_pointer_to_type(ira->codegen,
get_fn_frame_type(ira->codegen, fn_entry), false);
return ir_implicit_cast(ira, new_stack, needed_frame_type);
} else {
ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
false, false, PtrLenUnknown, target_fn_align(ira->codegen->zig_target), 0, 0, false);
ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
ira->codegen->need_frame_size_prefix_data = true;
return ir_implicit_cast(ira, new_stack, u8_slice);
}
}
static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstruction *source_instr,
ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref,
IrInstruction *first_arg_ptr, CallModifier modifier,
IrInstruction *new_stack, bool is_async_call_builtin,
IrInstruction **args_ptr, size_t args_len, IrInstruction *ret_ptr, ResultLoc *call_result_loc)
{
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 = args_len + first_arg_1_or_0;
AstNode *source_node = source_instr->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->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 (modifier == CallModifierCompileTime) {
// 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, source_instr))
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, nullptr);
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;
}
for (size_t call_i = 0; call_i < args_len; call_i += 1) {
IrInstruction *old_arg = args_ptr[call_i];
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);
ZigValue *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;
ZigValue *result_ptr;
create_result_ptr(ira->codegen, return_type, &result, &result_ptr);
if ((err = ir_eval_const_value(ira->codegen, exec_scope, body_node, result_ptr,
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota,
fn_entry, nullptr, source_instr->source_node, nullptr, ira->new_irb.exec, return_type_node,
UndefOk)))
{
return ira->codegen->invalid_instruction;
}
destroy(result_ptr, "ZigValue");
result_ptr = nullptr;
if (inferred_err_set_type != nullptr) {
inferred_err_set_type->data.error_set.incomplete = false;
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_move(ira, source_instr, result);
return ir_finish_anal(ira, new_instruction);
}
if (fn_type->data.fn.is_generic) {
if (!fn_entry) {
ir_add_error(ira, fn_ref,
buf_sprintf("calling a generic function requires compile-time known function value"));
return ira->codegen->invalid_instruction;
}
size_t new_fn_arg_count = first_arg_1_or_0 + args_len;
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, fn_type_id->cc, 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, nullptr);
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;
}
}
ZigFn *parent_fn_entry = exec_fn_entry(ira->new_irb.exec);
assert(parent_fn_entry);
for (size_t call_i = 0; call_i < args_len; call_i += 1) {
IrInstruction *arg = args_ptr[call_i];
AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i);
assert(param_decl_node->type == NodeTypeParamDecl);
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.align_expr != nullptr) {
ZigValue *align_result;
ZigValue *result_ptr;
create_result_ptr(ira->codegen, get_align_amt_type(ira->codegen), &align_result, &result_ptr);
if ((err = ir_eval_const_value(ira->codegen, impl_fn->child_scope,
fn_proto_node->data.fn_proto.align_expr, result_ptr,
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, UndefBad)))
{
return ira->codegen->invalid_instruction;
}
IrInstructionConst *const_instruction = ir_create_instruction_noval<IrInstructionConst>(&ira->new_irb,
impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr);
const_instruction->base.value = align_result;
destroy(result_ptr, "ZigValue");
uint32_t align_bytes = 0;
ir_resolve_align(ira, &const_instruction->base, nullptr, &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, source_instr, fn_entry, fn_type, fn_ref, first_arg_ptr,
CallModifierCompileTime, new_stack, is_async_call_builtin, args_ptr, args_len,
ret_ptr, call_result_loc);
case ReqCompTimeInvalid:
return ira->codegen->invalid_instruction;
case ReqCompTimeNo:
break;
}
}
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 = source_instr->source_node;
impl_fn->ir_executable->parent_exec = ira->new_irb.exec;
impl_fn->analyzed_executable.source_node = source_instr->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;
}
IrInstruction *casted_new_stack = analyze_casted_new_stack(ira, source_instr, new_stack,
is_async_call_builtin, impl_fn);
if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value->type))
return ira->codegen->invalid_instruction;
size_t impl_param_count = impl_fn_type_id->param_count;
if (modifier == CallModifierAsync) {
IrInstruction *result = ir_analyze_async_call(ira, source_instr, impl_fn, impl_fn->type_entry,
nullptr, casted_args, impl_param_count, casted_new_stack, is_async_call_builtin, ret_ptr,
call_result_loc);
return ir_finish_anal(ira, result);
}
IrInstruction *result_loc;
if (handle_is_ptr(impl_fn_type_id->return_type)) {
result_loc = ir_resolve_result(ira, source_instr, call_result_loc,
impl_fn_type_id->return_type, nullptr, true, false);
if (result_loc != nullptr) {
if (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc)) {
return result_loc;
}
ZigType *res_child_type = result_loc->value->type->data.pointer.child_type;
if (res_child_type == ira->codegen->builtin_types.entry_var) {
res_child_type = impl_fn_type_id->return_type;
}
if (!handle_is_ptr(res_child_type)) {
ir_reset_result(call_result_loc);
result_loc = nullptr;
}
}
} else if (is_async_call_builtin) {
result_loc = get_async_call_result_loc(ira, source_instr, impl_fn_type_id->return_type,
is_async_call_builtin, args_ptr, args_len, ret_ptr);
if (result_loc != nullptr && type_is_invalid(result_loc->value->type))
return ira->codegen->invalid_instruction;
} else {
result_loc = nullptr;
}
if (impl_fn_type_id->cc == CallingConventionAsync &&
parent_fn_entry->inferred_async_node == nullptr &&
modifier != CallModifierNoAsync)
{
parent_fn_entry->inferred_async_node = fn_ref->source_node;
parent_fn_entry->inferred_async_fn = impl_fn;
}
IrInstructionCallGen *new_call_instruction = ir_build_call_gen(ira, source_instr,
impl_fn, nullptr, impl_param_count, casted_args, modifier, casted_new_stack,
is_async_call_builtin, result_loc, impl_fn_type_id->return_type);
if (get_scope_typeof(source_instr->scope) == nullptr) {
parent_fn_entry->call_list.append(new_call_instruction);
}
return ir_finish_anal(ira, &new_call_instruction->base);
}
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, nullptr);
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 < args_len; call_i += 1) {
IrInstruction *old_arg = args_ptr[call_i];
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 (fn_entry != nullptr && fn_entry->fn_inline == FnInlineAlways && modifier == CallModifierNeverInline) {
ir_add_error(ira, source_instr,
buf_sprintf("no-inline call of inline function"));
return ira->codegen->invalid_instruction;
}
IrInstruction *casted_new_stack = analyze_casted_new_stack(ira, source_instr, new_stack,
is_async_call_builtin, fn_entry);
if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value->type))
return ira->codegen->invalid_instruction;
if (modifier == CallModifierAsync) {
IrInstruction *result = ir_analyze_async_call(ira, source_instr, fn_entry, fn_type, fn_ref,
casted_args, call_param_count, casted_new_stack, is_async_call_builtin, ret_ptr, call_result_loc);
return ir_finish_anal(ira, result);
}
if (fn_type_id->cc == CallingConventionAsync &&
parent_fn_entry->inferred_async_node == nullptr &&
modifier != CallModifierNoAsync)
{
parent_fn_entry->inferred_async_node = fn_ref->source_node;
parent_fn_entry->inferred_async_fn = fn_entry;
}
IrInstruction *result_loc;
if (handle_is_ptr(return_type)) {
result_loc = ir_resolve_result(ira, source_instr, call_result_loc,
return_type, nullptr, true, false);
if (result_loc != nullptr) {
if (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc)) {
return result_loc;
}
ZigType *res_child_type = result_loc->value->type->data.pointer.child_type;
if (res_child_type == ira->codegen->builtin_types.entry_var) {
res_child_type = return_type;
}
if (!handle_is_ptr(res_child_type)) {
ir_reset_result(call_result_loc);
result_loc = nullptr;
}
}
} else if (is_async_call_builtin) {
result_loc = get_async_call_result_loc(ira, source_instr, return_type, is_async_call_builtin,
args_ptr, args_len, ret_ptr);
if (result_loc != nullptr && type_is_invalid(result_loc->value->type))
return ira->codegen->invalid_instruction;
} else {
result_loc = nullptr;
}
IrInstructionCallGen *new_call_instruction = ir_build_call_gen(ira, source_instr, fn_entry, fn_ref,
call_param_count, casted_args, modifier, casted_new_stack,
is_async_call_builtin, result_loc, return_type);
if (get_scope_typeof(source_instr->scope) == nullptr) {
parent_fn_entry->call_list.append(new_call_instruction);
}
return ir_finish_anal(ira, &new_call_instruction->base);
}
static IrInstruction *ir_analyze_fn_call_src(IrAnalyze *ira, IrInstructionCallSrc *call_instruction,
ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref,
IrInstruction *first_arg_ptr, CallModifier modifier)
{
IrInstruction *new_stack = nullptr;
if (call_instruction->new_stack) {
new_stack = call_instruction->new_stack->child;
if (type_is_invalid(new_stack->value->type))
return ira->codegen->invalid_instruction;
}
IrInstruction **args_ptr = allocate<IrInstruction *>(call_instruction->arg_count, "IrInstruction *");
for (size_t i = 0; i < call_instruction->arg_count; i += 1) {
args_ptr[i] = call_instruction->args[i]->child;
if (type_is_invalid(args_ptr[i]->value->type))
return ira->codegen->invalid_instruction;
}
IrInstruction *ret_ptr = nullptr;
if (call_instruction->ret_ptr != nullptr) {
ret_ptr = call_instruction->ret_ptr->child;
if (type_is_invalid(ret_ptr->value->type))
return ira->codegen->invalid_instruction;
}
IrInstruction *result = ir_analyze_fn_call(ira, &call_instruction->base, fn_entry, fn_type, fn_ref,
first_arg_ptr, modifier, new_stack, call_instruction->is_async_call_builtin,
args_ptr, call_instruction->arg_count, ret_ptr, call_instruction->result_loc);
deallocate(args_ptr, call_instruction->arg_count, "IrInstruction *");
return result;
}
static IrInstruction *ir_analyze_call_extra(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *pass1_options, IrInstruction *pass1_fn_ref, IrInstruction **args_ptr, size_t args_len,
ResultLoc *result_loc)
{
IrInstruction *options = pass1_options->child;
if (type_is_invalid(options->value->type))
return ira->codegen->invalid_instruction;
IrInstruction *fn_ref = pass1_fn_ref->child;
if (type_is_invalid(fn_ref->value->type))
return ira->codegen->invalid_instruction;
TypeStructField *modifier_field = find_struct_type_field(options->value->type, buf_create_from_str("modifier"));
ir_assert(modifier_field != nullptr, source_instr);
IrInstruction *modifier_inst = ir_analyze_struct_value_field_value(ira, source_instr, options, modifier_field);
ZigValue *modifier_val = ir_resolve_const(ira, modifier_inst, UndefBad);
if (modifier_val == nullptr)
return ira->codegen->invalid_instruction;
CallModifier modifier = (CallModifier)bigint_as_u32(&modifier_val->data.x_enum_tag);
if (ir_should_inline(ira->new_irb.exec, source_instr->scope)) {
switch (modifier) {
case CallModifierBuiltin:
zig_unreachable();
case CallModifierAsync:
ir_add_error(ira, source_instr, buf_sprintf("TODO: comptime @call with async modifier"));
return ira->codegen->invalid_instruction;
case CallModifierCompileTime:
case CallModifierNone:
case CallModifierAlwaysInline:
case CallModifierAlwaysTail:
case CallModifierNoAsync:
modifier = CallModifierCompileTime;
break;
case CallModifierNeverInline:
ir_add_error(ira, source_instr,
buf_sprintf("unable to perform 'never_inline' call at compile-time"));
return ira->codegen->invalid_instruction;
case CallModifierNeverTail:
ir_add_error(ira, source_instr,
buf_sprintf("unable to perform 'never_tail' call at compile-time"));
return ira->codegen->invalid_instruction;
}
}
IrInstruction *first_arg_ptr = nullptr;
ZigFn *fn = nullptr;
if (instr_is_comptime(fn_ref)) {
if (fn_ref->value->type->id == ZigTypeIdBoundFn) {
assert(fn_ref->value->special == ConstValSpecialStatic);
fn = fn_ref->value->data.x_bound_fn.fn;
first_arg_ptr = fn_ref->value->data.x_bound_fn.first_arg;
if (type_is_invalid(first_arg_ptr->value->type))
return ira->codegen->invalid_instruction;
} else {
fn = ir_resolve_fn(ira, fn_ref);
}
}
// Some modifiers require the callee to be comptime-known
switch (modifier) {
case CallModifierCompileTime:
case CallModifierAlwaysInline:
case CallModifierAsync:
if (fn == nullptr) {
ir_add_error(ira, modifier_inst,
buf_sprintf("the specified modifier requires a comptime-known function"));
return ira->codegen->invalid_instruction;
}
default:
break;
}
ZigType *fn_type = (fn != nullptr) ? fn->type_entry : fn_ref->value->type;
TypeStructField *stack_field = find_struct_type_field(options->value->type, buf_create_from_str("stack"));
ir_assert(stack_field != nullptr, source_instr);
IrInstruction *opt_stack = ir_analyze_struct_value_field_value(ira, source_instr, options, stack_field);
if (type_is_invalid(opt_stack->value->type))
return ira->codegen->invalid_instruction;
IrInstruction *stack_is_non_null_inst = ir_analyze_test_non_null(ira, source_instr, opt_stack);
bool stack_is_non_null;
if (!ir_resolve_bool(ira, stack_is_non_null_inst, &stack_is_non_null))
return ira->codegen->invalid_instruction;
IrInstruction *stack = nullptr;
if (stack_is_non_null) {
stack = ir_analyze_optional_value_payload_value(ira, source_instr, opt_stack, false);
if (type_is_invalid(stack->value->type))
return ira->codegen->invalid_instruction;
}
return ir_analyze_fn_call(ira, source_instr, fn, fn_type, fn_ref, first_arg_ptr,
modifier, stack, false, args_ptr, args_len, nullptr, result_loc);
}
static IrInstruction *ir_analyze_instruction_call_extra(IrAnalyze *ira, IrInstructionCallExtra *instruction) {
IrInstruction *args = instruction->args->child;
ZigType *args_type = args->value->type;
if (type_is_invalid(args_type))
return ira->codegen->invalid_instruction;
if (args_type->id != ZigTypeIdStruct) {
ir_add_error(ira, args,
buf_sprintf("expected tuple or struct, found '%s'", buf_ptr(&args_type->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction **args_ptr = nullptr;
size_t args_len = 0;
if (is_tuple(args_type)) {
args_len = args_type->data.structure.src_field_count;
args_ptr = allocate<IrInstruction *>(args_len, "IrInstruction *");
for (size_t i = 0; i < args_len; i += 1) {
TypeStructField *arg_field = args_type->data.structure.fields[i];
args_ptr[i] = ir_analyze_struct_value_field_value(ira, &instruction->base, args, arg_field);
if (type_is_invalid(args_ptr[i]->value->type))
return ira->codegen->invalid_instruction;
}
} else {
ir_add_error(ira, args, buf_sprintf("TODO: struct args"));
return ira->codegen->invalid_instruction;
}
IrInstruction *result = ir_analyze_call_extra(ira, &instruction->base, instruction->options,
instruction->fn_ref, args_ptr, args_len, instruction->result_loc);
deallocate(args_ptr, args_len, "IrInstruction *");
return result;
}
static IrInstruction *ir_analyze_instruction_call_args(IrAnalyze *ira, IrInstructionCallSrcArgs *instruction) {
IrInstruction **args_ptr = allocate<IrInstruction *>(instruction->args_len, "IrInstruction *");
for (size_t i = 0; i < instruction->args_len; i += 1) {
args_ptr[i] = instruction->args_ptr[i]->child;
if (type_is_invalid(args_ptr[i]->value->type))
return ira->codegen->invalid_instruction;
}
IrInstruction *result = ir_analyze_call_extra(ira, &instruction->base, instruction->options,
instruction->fn_ref, args_ptr, instruction->args_len, instruction->result_loc);
deallocate(args_ptr, instruction->args_len, "IrInstruction *");
return result;
}
static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionCallSrc *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->modifier == CallModifierCompileTime) ||
ir_should_inline(ira->new_irb.exec, call_instruction->base.scope);
CallModifier modifier = is_comptime ? CallModifierCompileTime : call_instruction->modifier;
if (is_comptime || instr_is_comptime(fn_ref)) {
if (fn_ref->value->type->id == ZigTypeIdMetaType) {
ZigType *ty = ir_resolve_type(ira, fn_ref);
if (ty == nullptr)
return ira->codegen->invalid_instruction;
ErrorMsg *msg = ir_add_error_node(ira, fn_ref->source_node,
buf_sprintf("type '%s' not a function", buf_ptr(&ty->name)));
add_error_note(ira->codegen, msg, call_instruction->base.source_node,
buf_sprintf("use @as builtin for type coercion"));
return ira->codegen->invalid_instruction;
} else if (fn_ref->value->type->id == ZigTypeIdFn) {
ZigFn *fn_table_entry = ir_resolve_fn(ira, fn_ref);
ZigType *fn_type = fn_table_entry ? fn_table_entry->type_entry : fn_ref->value->type;
CallModifier modifier = is_comptime ? CallModifierCompileTime : call_instruction->modifier;
return ir_analyze_fn_call_src(ira, call_instruction, fn_table_entry, fn_type,
fn_ref, nullptr, modifier);
} 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;
CallModifier modifier = is_comptime ? CallModifierCompileTime : call_instruction->modifier;
return ir_analyze_fn_call_src(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,
fn_ref, first_arg_ptr, modifier);
} 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_src(ira, call_instruction, nullptr, fn_ref->value->type,
fn_ref, nullptr, modifier);
} 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,
ZigValue *out_val, ZigValue *ptr_val)
{
Error err;
assert(out_val->type != nullptr);
ZigValue *pointee = const_ptr_pointee_unchecked(codegen, ptr_val);
src_assert(pointee->type != nullptr, source_node);
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(codegen, out_val->type, pointee->type)) {
copy_const_val(out_val, pointee);
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: {
ZigValue *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) {
ZigValue *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_optional_type(IrAnalyze *ira, IrInstructionUnOp *instruction) {
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type);
result->value->special = ConstValSpecialLazy;
LazyValueOptType *lazy_opt_type = allocate<LazyValueOptType>(1, "LazyValueOptType");
lazy_opt_type->ira = ira; ira_ref(ira);
result->value->data.x_lazy = &lazy_opt_type->base;
lazy_opt_type->base.id = LazyValueIdOptType;
lazy_opt_type->payload_type = instruction->value->child;
if (ir_resolve_type_lazy(ira, lazy_opt_type->payload_type) == nullptr)
return ira->codegen->invalid_instruction;
return result;
}
static ErrorMsg *ir_eval_negation_scalar(IrAnalyze *ira, IrInstruction *source_instr, ZigType *scalar_type,
ZigValue *operand_val, ZigValue *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;
if (!(expr_type->id == ZigTypeIdInt || expr_type->id == ZigTypeIdComptimeInt ||
expr_type->id == ZigTypeIdFloat || expr_type->id == ZigTypeIdComptimeFloat ||
expr_type->id == ZigTypeIdVector))
{
ir_add_error(ira, &instruction->base,
buf_sprintf("negation of type '%s'", buf_ptr(&expr_type->name)));
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)) {
ZigValue *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);
ZigValue *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) {
ZigValue *scalar_operand_val = &operand_val->data.x_array.data.s_none.elements[i];
ZigValue *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)) {
ZigValue *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, instruction->result_loc);
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_optional_type(ira, instruction);
}
zig_unreachable();
}
static void ir_push_resume(IrAnalyze *ira, IrSuspendPosition pos) {
IrBasicBlock *old_bb = ira->old_irb.exec->basic_block_list.at(pos.basic_block_index);
if (old_bb->in_resume_stack) return;
ira->resume_stack.append(pos);
old_bb->in_resume_stack = true;
}
static void ir_push_resume_block(IrAnalyze *ira, IrBasicBlock *old_bb) {
if (ira->resume_stack.length != 0) {
ir_push_resume(ira, {old_bb->index, 0});
}
}
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 && old_dest_block->suspend_instruction_ref == nullptr))
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);
ir_push_resume_block(ira, old_dest_block);
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 && old_dest_block->suspend_instruction_ref == nullptr))
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);
ir_push_resume_block(ira, old_dest_block);
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);
ir_push_resume_block(ira, cond_br_instruction->else_block);
ir_push_resume_block(ira, cond_br_instruction->then_block);
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) {
Error err;
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);
return result;
} else {
return value;
}
}
zig_unreachable();
}
ResultLocPeerParent *peer_parent = phi_instruction->peer_parent;
if (peer_parent != nullptr && !peer_parent->skipped && !peer_parent->done_resuming &&
peer_parent->peers.length >= 2)
{
if (peer_parent->resolved_type == nullptr) {
IrInstruction **instructions = allocate<IrInstruction *>(peer_parent->peers.length);
for (size_t i = 0; i < peer_parent->peers.length; i += 1) {
ResultLocPeer *this_peer = peer_parent->peers.at(i);
IrInstruction *gen_instruction = this_peer->base.gen_instruction;
if (gen_instruction == nullptr) {
// unreachable instructions will cause implicit_elem_type to be null
if (this_peer->base.implicit_elem_type == nullptr) {
instructions[i] = ir_const_unreachable(ira, this_peer->base.source_instruction);
} else {
instructions[i] = ir_const(ira, this_peer->base.source_instruction,
this_peer->base.implicit_elem_type);
instructions[i]->value->special = ConstValSpecialRuntime;
}
} else {
instructions[i] = gen_instruction;
}
}
ZigType *expected_type = ir_result_loc_expected_type(ira, &phi_instruction->base, peer_parent->parent);
peer_parent->resolved_type = ir_resolve_peer_types(ira,
peer_parent->base.source_instruction->source_node, expected_type, instructions,
peer_parent->peers.length);
if (type_is_invalid(peer_parent->resolved_type))
return ira->codegen->invalid_instruction;
// the logic below assumes there are no instructions in the new current basic block yet
ir_assert(ira->new_irb.current_basic_block->instruction_list.length == 0, &phi_instruction->base);
// In case resolving the parent activates a suspend, do it now
IrInstruction *parent_result_loc = ir_resolve_result(ira, &phi_instruction->base, peer_parent->parent,
peer_parent->resolved_type, nullptr, false, true);
if (parent_result_loc != nullptr &&
(type_is_invalid(parent_result_loc->value->type) || instr_is_unreachable(parent_result_loc)))
{
return parent_result_loc;
}
// If the above code generated any instructions in the current basic block, we need
// to move them to the peer parent predecessor.
ZigList<IrInstruction *> instrs_to_move = {};
while (ira->new_irb.current_basic_block->instruction_list.length != 0) {
instrs_to_move.append(ira->new_irb.current_basic_block->instruction_list.pop());
}
if (instrs_to_move.length != 0) {
IrBasicBlock *predecessor = peer_parent->base.source_instruction->child->owner_bb;
IrInstruction *branch_instruction = predecessor->instruction_list.pop();
ir_assert(branch_instruction->value->type->id == ZigTypeIdUnreachable, &phi_instruction->base);
while (instrs_to_move.length != 0) {
predecessor->instruction_list.append(instrs_to_move.pop());
}
predecessor->instruction_list.append(branch_instruction);
}
}
IrSuspendPosition suspend_pos;
ira_suspend(ira, &phi_instruction->base, nullptr, &suspend_pos);
ir_push_resume(ira, suspend_pos);
for (size_t i = 0; i < peer_parent->peers.length; i += 1) {
ResultLocPeer *opposite_peer = peer_parent->peers.at(peer_parent->peers.length - i - 1);
if (opposite_peer->base.implicit_elem_type != nullptr &&
opposite_peer->base.implicit_elem_type->id != ZigTypeIdUnreachable)
{
ir_push_resume(ira, opposite_peer->suspend_pos);
}
}
peer_parent->done_resuming = true;
return ira_resume(ira);
}
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 = nullptr;
if (peer_parent != nullptr) {
bool peer_parent_has_type;
if ((err = ir_result_has_type(ira, peer_parent->parent, &peer_parent_has_type)))
return ira->codegen->invalid_instruction;
if (peer_parent_has_type) {
if (peer_parent->parent->id == ResultLocIdReturn) {
resolved_type = ira->explicit_return_type;
} else if (peer_parent->parent->id == ResultLocIdCast) {
resolved_type = ir_resolve_type(ira, peer_parent->parent->source_instruction->child);
} else if (peer_parent->parent->resolved_loc) {
ZigType *resolved_loc_ptr_type = peer_parent->parent->resolved_loc->value->type;
ir_assert(resolved_loc_ptr_type->id == ZigTypeIdPointer, &phi_instruction->base);
resolved_type = resolved_loc_ptr_type->data.pointer.child_type;
}
if (resolved_type != nullptr && type_is_invalid(resolved_type))
return ira->codegen->invalid_instruction;
}
}
if (resolved_type == nullptr) {
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_move(ira, &phi_instruction->base,
get_the_one_possible_value(ira->codegen, 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);
ir_assert(predecessor->instruction_list.length != 0, &phi_instruction->base);
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 (type_is_invalid(casted_value->value->type)) {
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, nullptr);
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", 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_extra2(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,
ptr_type->data.pointer.vector_index,
ptr_type->data.pointer.inferred_struct_field,
ptr_type->data.pointer.sentinel);
}
static ZigType *adjust_ptr_sentinel(CodeGen *g, ZigType *ptr_type, ZigValue *new_sentinel) {
assert(ptr_type->id == ZigTypeIdPointer);
return get_pointer_to_type_extra2(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,
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,
ptr_type->data.pointer.vector_index,
ptr_type->data.pointer.inferred_struct_field,
new_sentinel);
}
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;
ZigValue *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 == ZigTypeIdVector) {
// This depends on whether the element index is comptime, so it is computed later.
return_type = nullptr;
} else if (elem_ptr_instruction->init_array_type_source_node != nullptr &&
array_type->id == ZigTypeIdStruct &&
array_type->data.structure.resolve_status == ResolveStatusBeingInferred)
{
ZigType *usize = ira->codegen->builtin_types.entry_usize;
IrInstruction *casted_elem_index = ir_implicit_cast(ira, elem_index, usize);
if (type_is_invalid(casted_elem_index->value->type))
return ira->codegen->invalid_instruction;
ir_assert(instr_is_comptime(casted_elem_index), &elem_ptr_instruction->base);
Buf *field_name = buf_alloc();
bigint_append_buf(field_name, &casted_elem_index->value->data.x_bigint, 10);
return ir_analyze_inferred_field_ptr(ira, field_name, &elem_ptr_instruction->base,
array_ptr, array_type);
} else if (is_tuple(array_type)) {
uint64_t elem_index_scalar;
if (!ir_resolve_usize(ira, elem_index, &elem_index_scalar))
return ira->codegen->invalid_instruction;
if (elem_index_scalar >= array_type->data.structure.src_field_count) {
ir_add_error(ira, &elem_ptr_instruction->base, buf_sprintf(
"field index %" ZIG_PRI_u64 " outside tuple '%s' which has %" PRIu32 " fields",
elem_index_scalar, buf_ptr(&array_type->name),
array_type->data.structure.src_field_count));
return ira->codegen->invalid_instruction;
}
TypeStructField *field = array_type->data.structure.fields[elem_index_scalar];
return ir_analyze_struct_field_ptr(ira, &elem_ptr_instruction->base, field, array_ptr,
array_type, false);
} 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 (instr_is_comptime(casted_elem_index)) {
uint64_t index = bigint_as_u64(&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 && array_type->data.array.sentinel != nullptr) {
ZigType *elem_type = array_type->data.array.child_type;
IrInstruction *sentinel_elem = ir_const(ira, &elem_ptr_instruction->base, elem_type);
copy_const_val(sentinel_elem->value, array_type->data.array.sentinel);
return ir_get_ref(ira, &elem_ptr_instruction->base, sentinel_elem, true, false);
}
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;
}
if (array_type->id == ZigTypeIdVector) {
ZigType *elem_type = array_type->data.vector.elem_type;
uint32_t host_vec_len = array_type->data.vector.len;
return_type = get_pointer_to_type_extra2(ira->codegen, elem_type,
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
elem_ptr_instruction->ptr_len,
get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, (uint32_t)index,
nullptr, nullptr);
} else if (return_type->data.pointer.explicit_alignment != 0) {
// figure out the largest alignment possible
if ((err = type_resolve(ira->codegen, return_type->data.pointer.child_type, ResolveStatusSizeKnown)))
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);
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);
}
// TODO The `array_type->id == ZigTypeIdArray` exception here should not be an exception;
// the `orig_array_ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar` clause should be omitted completely.
// However there are bugs to fix before this improvement can be made.
if (orig_array_ptr_val->special != ConstValSpecialRuntime &&
orig_array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr &&
(orig_array_ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar || array_type->id == ZigTypeIdArray))
{
ZigValue *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 == ConstValSpecialUndef &&
elem_ptr_instruction->init_array_type_source_node != nullptr)
{
if (array_type->id == ZigTypeIdArray || array_type->id == ZigTypeIdVector) {
array_ptr_val->data.x_array.special = ConstArraySpecialNone;
array_ptr_val->data.x_array.data.s_none.elements = create_const_vals(array_type->data.array.len);
array_ptr_val->special = ConstValSpecialStatic;
for (size_t i = 0; i < array_type->data.array.len; i += 1) {
ZigValue *elem_val = &array_ptr_val->data.x_array.data.s_none.elements[i];
elem_val->special = ConstValSpecialUndef;
elem_val->type = array_type->data.array.child_type;
elem_val->parent.id = ConstParentIdArray;
elem_val->parent.data.p_array.array_val = array_ptr_val;
elem_val->parent.data.p_array.elem_index = i;
}
} else if (is_slice(array_type)) {
ir_assert(array_ptr->value->type->id == ZigTypeIdPointer, &elem_ptr_instruction->base);
ZigType *actual_array_type = array_ptr->value->type->data.pointer.child_type;
if (type_is_invalid(actual_array_type))
return ira->codegen->invalid_instruction;
if (actual_array_type->id != ZigTypeIdArray) {
ir_add_error_node(ira, elem_ptr_instruction->init_array_type_source_node,
buf_sprintf("array literal requires address-of operator to coerce to slice type '%s'",
buf_ptr(&actual_array_type->name)));
return ira->codegen->invalid_instruction;
}
ZigValue *array_init_val = create_const_vals(1);
array_init_val->special = ConstValSpecialStatic;
array_init_val->type = actual_array_type;
array_init_val->data.x_array.special = ConstArraySpecialNone;
array_init_val->data.x_array.data.s_none.elements = create_const_vals(actual_array_type->data.array.len);
array_init_val->special = ConstValSpecialStatic;
for (size_t i = 0; i < actual_array_type->data.array.len; i += 1) {
ZigValue *elem_val = &array_init_val->data.x_array.data.s_none.elements[i];
elem_val->special = ConstValSpecialUndef;
elem_val->type = actual_array_type->data.array.child_type;
elem_val->parent.id = ConstParentIdArray;
elem_val->parent.data.p_array.array_val = array_init_val;
elem_val->parent.data.p_array.elem_index = i;
}
init_const_slice(ira->codegen, array_ptr_val, array_init_val, 0, actual_array_type->data.array.len,
false);
array_ptr_val->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = ConstPtrMutInfer;
} else {
ir_add_error_node(ira, elem_ptr_instruction->init_array_type_source_node,
buf_sprintf("expected array type or [_], found '%s'",
buf_ptr(&array_type->name)));
return ira->codegen->invalid_instruction;
}
}
if (array_ptr_val->special != ConstValSpecialRuntime &&
(array_type->id != ZigTypeIdPointer ||
array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr))
{
if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec,
elem_ptr_instruction->base.source_node, array_ptr_val, UndefOk)))
{
return ira->codegen->invalid_instruction;
}
if (array_type->id == ZigTypeIdPointer) {
IrInstruction *result = ir_const(ira, &elem_ptr_instruction->base, return_type);
ZigValue *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:
if (array_ptr_val->data.x_ptr.data.ref.pointee->type->id == ZigTypeIdArray) {
ZigValue *array_val = array_ptr_val->data.x_ptr.data.ref.pointee;
new_index = index;
ZigType *array_type = array_val->type;
mem_size = array_type->data.array.len;
if (array_type->data.array.sentinel != nullptr) {
mem_size += 1;
}
old_size = mem_size;
out_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
out_val->data.x_ptr.data.base_array.array_val = array_val;
out_val->data.x_ptr.data.base_array.elem_index = new_index;
} else {
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;
ZigType *array_type = array_ptr_val->data.x_ptr.data.base_array.array_val->type;
mem_size = array_type->data.array.len;
if (array_type->data.array.sentinel != nullptr) {
mem_size += 1;
}
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;
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)) {
ZigValue *ptr_field = array_ptr_val->data.x_struct.fields[slice_ptr_index];
ir_assert(ptr_field != nullptr, &elem_ptr_instruction->base);
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, nullptr);
result->value->type = return_type;
return result;
}
ZigValue *len_field = array_ptr_val->data.x_struct.fields[slice_len_index];
IrInstruction *result = ir_const(ira, &elem_ptr_instruction->base, return_type);
ZigValue *out_val = result->value;
ZigType *slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry;
uint64_t slice_len = bigint_as_u64(&len_field->data.x_bigint);
uint64_t full_slice_len = slice_len +
((slice_ptr_type->data.pointer.sentinel != nullptr) ? 1 : 0);
if (index >= full_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;
if (ptr_field->data.x_ptr.data.base_array.array_val->data.x_array.special !=
ConstArraySpecialBuf)
{
ir_assert(new_index <
ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len,
&elem_ptr_instruction->base);
}
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;
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 || array_type->id == ZigTypeIdVector) {
IrInstruction *result;
if (orig_array_ptr_val->data.x_ptr.mut == ConstPtrMutInfer) {
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, nullptr);
result->value->type = return_type;
result->value->special = ConstValSpecialStatic;
} else {
result = ir_const(ira, &elem_ptr_instruction->base, return_type);
}
ZigValue *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 if (array_type->id == ZigTypeIdVector) {
// runtime known element index
ZigType *elem_type = array_type->data.vector.elem_type;
uint32_t host_vec_len = array_type->data.vector.len;
return_type = get_pointer_to_type_extra2(ira->codegen, elem_type,
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
elem_ptr_instruction->ptr_len,
get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, VECTOR_INDEX_RUNTIME,
nullptr, nullptr);
} 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 (return_type->data.pointer.explicit_alignment != 0) {
if ((err = type_resolve(ira->codegen, return_type->data.pointer.child_type, ResolveStatusSizeKnown)))
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 (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, nullptr);
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 tld = find_container_decl(ira->codegen, container_scope, field_name);
if (tld) {
if (tld->id == TldIdFn) {
resolve_top_level_decl(ira->codegen, tld, source_instr->source_node, false);
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);
} else if (tld->id == TldIdVar) {
resolve_top_level_decl(ira->codegen, tld, source_instr->source_node, false);
if (tld->resolution == TldResolutionInvalid)
return ira->codegen->invalid_instruction;
TldVar *tld_var = (TldVar *)tld;
ZigVar *var = tld_var->var;
if (type_is_invalid(var->var_type))
return ira->codegen->invalid_instruction;
if (var->const_value->type->id == ZigTypeIdFn) {
ir_assert(var->const_value->data.x_ptr.special == ConstPtrSpecialFunction, source_instr);
ZigFn *fn = var->const_value->data.x_ptr.data.fn.fn_entry;
IrInstruction *bound_fn_value = ir_build_const_bound_fn(&ira->new_irb, source_instr->scope,
source_instr->source_node, fn, 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 void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field) {
if (field->init_val != nullptr) return;
if (field->decl_node->type != NodeTypeStructField) return;
AstNode *init_node = field->decl_node->data.struct_field.value;
if (init_node == nullptr) return;
// scope is not the scope of the struct init, it's the scope of the struct type decl
Scope *analyze_scope = &get_container_scope(container_type)->base;
// memoize it
field->init_val = analyze_const_value(codegen, analyze_scope, init_node,
field->type_entry, nullptr, UndefOk);
}
static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction *source_instr,
TypeStructField *field, IrInstruction *struct_ptr, ZigType *struct_type, bool initializing)
{
Error err;
ZigType *field_type = resolve_struct_field_type(ira->codegen, field);
if (field_type == nullptr)
return ira->codegen->invalid_instruction;
if (field->is_comptime) {
IrInstruction *elem = ir_const(ira, source_instr, field_type);
memoize_field_init_val(ira->codegen, struct_type, field);
copy_const_val(elem->value, field->init_val);
return ir_get_ref(ira, source_instr, elem, true, false);
}
switch (type_has_one_possible_value(ira->codegen, field_type)) {
case OnePossibleValueInvalid:
return ira->codegen->invalid_instruction;
case OnePossibleValueYes: {
IrInstruction *elem = ir_const_move(ira, source_instr,
get_the_one_possible_value(ira->codegen, field_type));
return ir_get_ref(ira, source_instr, elem, false, false);
}
case OnePossibleValueNo:
break;
}
bool is_const = struct_ptr->value->type->data.pointer.is_const;
bool is_volatile = struct_ptr->value->type->data.pointer.is_volatile;
ZigType *ptr_type;
if (is_anon_container(struct_type)) {
ptr_type = get_pointer_to_type_extra(ira->codegen, field_type,
is_const, is_volatile, PtrLenSingle, 0, 0, 0, false);
} else {
ResolveStatus needed_resolve_status =
(struct_type->data.structure.layout == ContainerLayoutAuto) ?
ResolveStatusZeroBitsKnown : ResolveStatusSizeKnown;
if ((err = type_resolve(ira->codegen, struct_type, needed_resolve_status)))
return ira->codegen->invalid_instruction;
assert(struct_ptr->value->type->id == ZigTypeIdPointer);
uint32_t ptr_bit_offset = struct_ptr->value->type->data.pointer.bit_offset_in_host;
uint32_t ptr_host_int_bytes = struct_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, struct_type, field) : ptr_host_int_bytes;
ptr_type = get_pointer_to_type_extra(ira->codegen, field_type,
is_const, is_volatile, PtrLenSingle, field->align,
(uint32_t)(ptr_bit_offset + field->bit_offset_in_host),
(uint32_t)host_int_bytes_for_result_type, false);
}
if (instr_is_comptime(struct_ptr)) {
ZigValue *ptr_val = ir_resolve_const(ira, struct_ptr, UndefBad);
if (!ptr_val)
return ira->codegen->invalid_instruction;
if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) {
ZigValue *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;
if (initializing && struct_val->special == ConstValSpecialUndef) {
struct_val->data.x_struct.fields = alloc_const_vals_ptrs(struct_type->data.structure.src_field_count);
struct_val->special = ConstValSpecialStatic;
for (size_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) {
ZigValue *field_val = struct_val->data.x_struct.fields[i];
field_val->special = ConstValSpecialUndef;
field_val->type = resolve_struct_field_type(ira->codegen,
struct_type->data.structure.fields[i]);
field_val->parent.id = ConstParentIdStruct;
field_val->parent.data.p_struct.struct_val = struct_val;
field_val->parent.data.p_struct.field_index = i;
}
}
IrInstruction *result;
if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) {
result = ir_build_struct_field_ptr(&ira->new_irb, source_instr->scope,
source_instr->source_node, struct_ptr, field);
result->value->type = ptr_type;
result->value->special = ConstValSpecialStatic;
} else {
result = ir_const(ira, source_instr, ptr_type);
}
ZigValue *const_val = result->value;
const_val->data.x_ptr.special = ConstPtrSpecialBaseStruct;
const_val->data.x_ptr.mut = ptr_val->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,
struct_ptr, field);
result->value->type = ptr_type;
return result;
}
static IrInstruction *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_name,
IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type)
{
// The type of the field is not available until a store using this pointer happens.
// So, here we create a special pointer type which has the inferred struct type and
// field name encoded in the type. Later, when there is a store via this pointer,
// the field type will then be available, and the field will be added to the inferred
// struct.
ZigType *container_ptr_type = container_ptr->value->type;
ir_assert(container_ptr_type->id == ZigTypeIdPointer, source_instr);
InferredStructField *inferred_struct_field = allocate<InferredStructField>(1, "InferredStructField");
inferred_struct_field->inferred_struct_type = container_type;
inferred_struct_field->field_name = field_name;
ZigType *elem_type = ira->codegen->builtin_types.entry_var;
ZigType *field_ptr_type = get_pointer_to_type_extra2(ira->codegen, elem_type,
container_ptr_type->data.pointer.is_const, container_ptr_type->data.pointer.is_volatile,
PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, inferred_struct_field, nullptr);
if (instr_is_comptime(container_ptr)) {
ZigValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad);
if (ptr_val == nullptr)
return ira->codegen->invalid_instruction;
IrInstruction *result;
if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) {
result = ir_build_cast(&ira->new_irb, source_instr->scope,
source_instr->source_node, container_ptr_type, container_ptr, CastOpNoop);
} else {
result = ir_const(ira, source_instr, field_ptr_type);
}
copy_const_val(result->value, ptr_val);
result->value->type = field_ptr_type;
return result;
}
IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope,
source_instr->source_node, field_ptr_type, container_ptr, CastOpNoop);
result->value->type = field_ptr_type;
return result;
}
static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name,
IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type, bool initializing)
{
Error err;
ZigType *bare_type = container_ref_type(container_type);
if (initializing && bare_type->id == ZigTypeIdStruct &&
bare_type->data.structure.resolve_status == ResolveStatusBeingInferred)
{
return ir_analyze_inferred_field_ptr(ira, field_name, source_instr, container_ptr, bare_type);
}
if ((err = type_resolve(ira->codegen, bare_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
assert(container_ptr->value->type->id == ZigTypeIdPointer);
if (bare_type->id == ZigTypeIdStruct) {
TypeStructField *field = find_struct_type_field(bare_type, field_name);
if (field != nullptr) {
return ir_analyze_struct_field_ptr(ira, source_instr, field, container_ptr, bare_type, initializing);
} else {
return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
source_instr, container_ptr, container_type);
}
}
if (bare_type->id == ZigTypeIdEnum) {
return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
source_instr, container_ptr, container_type);
}
if (bare_type->id == ZigTypeIdUnion) {
bool is_const = container_ptr->value->type->data.pointer.is_const;
bool is_volatile = container_ptr->value->type->data.pointer.is_volatile;
TypeUnionField *field = find_union_type_field(bare_type, field_name);
if (field == nullptr) {
return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
source_instr, container_ptr, container_type);
}
ZigType *field_type = resolve_union_field_type(ira->codegen, field);
if (field_type == nullptr)
return ira->codegen->invalid_instruction;
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type,
is_const, is_volatile, PtrLenSingle, 0, 0, 0, false);
if (instr_is_comptime(container_ptr)) {
ZigValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad);
if (!ptr_val)
return ira->codegen->invalid_instruction;
if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar &&
ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) {
ZigValue *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;
if (initializing) {
ZigValue *payload_val = create_const_vals(1);
payload_val->special = ConstValSpecialUndef;
payload_val->type = field_type;
payload_val->parent.id = ConstParentIdUnion;
payload_val->parent.data.p_union.union_val = union_val;
union_val->special = ConstValSpecialStatic;
bigint_init_bigint(&union_val->data.x_union.tag, &field->enum_field->value);
union_val->data.x_union.payload = payload_val;
} else if (bare_type->data.unionation.layout != ContainerLayoutExtern) {
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;
}
}
ZigValue *payload_val = union_val->data.x_union.payload;
IrInstruction *result;
if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) {
result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope,
source_instr->source_node, container_ptr, field, true, initializing);
result->value->type = ptr_type;
result->value->special = ConstValSpecialStatic;
} else {
result = ir_const(ira, source_instr, ptr_type);
}
ZigValue *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, true, initializing);
result->value->type = ptr_type;
return result;
}
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 `-fPIC`", 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_error_dependency_loop(IrAnalyze *ira, IrInstruction *source_instr) {
ir_add_error(ira, source_instr, buf_sprintf("dependency loop detected"));
return ira->codegen->invalid_instruction;
}
static IrInstruction *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source_instruction, Tld *tld) {
resolve_top_level_decl(ira->codegen, tld, source_instruction->source_node, true);
if (tld->resolution == TldResolutionInvalid) {
return ira->codegen->invalid_instruction;
}
switch (tld->id) {
case TldIdContainer:
case TldIdCompTime:
case TldIdUsingNamespace:
zig_unreachable();
case TldIdVar: {
TldVar *tld_var = (TldVar *)tld;
ZigVar *var = tld_var->var;
if (var == nullptr) {
return ir_error_dependency_loop(ira, source_instruction);
}
if (tld_var->extern_lib_name != nullptr) {
add_link_lib_symbol(ira, tld_var->extern_lib_name, buf_create_from_str(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;
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);
}
IrInstruction *fn_inst = ir_create_const_fn(&ira->new_irb, source_instruction->scope,
source_instruction->source_node, fn_entry);
return ir_get_ref(ira, source_instruction, fn_inst, true, false);
}
}
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_tuple(container_type) && !field_ptr_instruction->initializing && buf_eql_str(field_name, "len")) {
IrInstruction *len_inst = ir_const_unsigned(ira, &field_ptr_instruction->base,
container_type->data.structure.src_field_count);
return ir_get_ref(ira, &field_ptr_instruction->base, len_inst, true, false);
} else if (is_slice(container_type) || 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, nullptr);
IrInstruction *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base, container_child, bare_type, field_ptr_instruction->initializing);
return result;
} else {
IrInstruction *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base, container_ptr, container_type, field_ptr_instruction->initializing);
return result;
}
} else if (is_array_ref(container_type) && !field_ptr_instruction->initializing) {
if (buf_eql_str(field_name, "len")) {
ZigValue *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 == ZigTypeIdMetaType) {
ZigValue *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);
ZigValue *child_val = const_ptr_pointee(ira, ira->codegen, container_ptr_val, source_node);
if (child_val == nullptr)
return ira->codegen->invalid_instruction;
if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec,
field_ptr_instruction->base.source_node, child_val, UndefBad)))
{
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 (child_type->id == ZigTypeIdEnum) {
if ((err = type_resolve(ira->codegen, child_type, ResolveStatusSizeKnown)))
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 = type_resolve(ira->codegen, child_type, ResolveStatusSizeKnown)))
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;
}
ZigValue *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 if (field_ptr_instruction->initializing) {
ir_add_error(ira, &field_ptr_instruction->base,
buf_sprintf("type '%s' does not support struct initialization syntax", buf_ptr(&container_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, instruction->allow_write_through_const);
}
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, nullptr);
}
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_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_any_frame_type(IrAnalyze *ira,
IrInstructionAnyFrameType *instruction)
{
ZigType *payload_type = nullptr;
if (instruction->payload_type != nullptr) {
payload_type = ir_resolve_type(ira, instruction->payload_type->child);
if (type_is_invalid(payload_type))
return ira->codegen->invalid_instruction;
}
ZigType *any_frame_type = get_any_frame_type(ira->codegen, payload_type);
return ir_const_type(ira, &instruction->base, any_frame_type);
}
static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira,
IrInstructionSliceType *slice_type_instruction)
{
IrInstruction *result = ir_const(ira, &slice_type_instruction->base, ira->codegen->builtin_types.entry_type);
result->value->special = ConstValSpecialLazy;
LazyValueSliceType *lazy_slice_type = allocate<LazyValueSliceType>(1, "LazyValueSliceType");
lazy_slice_type->ira = ira; ira_ref(ira);
result->value->data.x_lazy = &lazy_slice_type->base;
lazy_slice_type->base.id = LazyValueIdSliceType;
if (slice_type_instruction->align_value != nullptr) {
lazy_slice_type->align_inst = slice_type_instruction->align_value->child;
if (ir_resolve_const(ira, lazy_slice_type->align_inst, LazyOk) == nullptr)
return ira->codegen->invalid_instruction;
}
if (slice_type_instruction->sentinel != nullptr) {
lazy_slice_type->sentinel = slice_type_instruction->sentinel->child;
if (ir_resolve_const(ira, lazy_slice_type->sentinel, LazyOk) == nullptr)
return ira->codegen->invalid_instruction;
}
lazy_slice_type->elem_type = slice_type_instruction->child_type->child;
if (ir_resolve_type_lazy(ira, lazy_slice_type->elem_type) == nullptr)
return ira->codegen->invalid_instruction;
lazy_slice_type->is_const = slice_type_instruction->is_const;
lazy_slice_type->is_volatile = slice_type_instruction->is_volatile;
lazy_slice_type->is_allowzero = slice_type_instruction->is_allow_zero;
return result;
}
static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAsmSrc *asm_instruction) {
Error err;
assert(asm_instruction->base.source_node->type == NodeTypeAsmExpr);
AstNode *node = asm_instruction->base.source_node;
AstNodeAsmExpr *asm_expr = &asm_instruction->base.source_node->data.asm_expr;
Buf *template_buf = ir_resolve_str(ira, asm_instruction->asm_template->child);
if (template_buf == nullptr)
return ira->codegen->invalid_instruction;
if (asm_instruction->is_global) {
buf_append_char(&ira->codegen->global_asm, '\n');
buf_append_buf(&ira->codegen->global_asm, template_buf);
return ir_const_void(ira, &asm_instruction->base);
}
if (!ir_emit_global_runtime_side_effect(ira, &asm_instruction->base))
return ira->codegen->invalid_instruction;
ZigList<AsmToken> tok_list = {};
if ((err = parse_asm_template(ira, node, template_buf, &tok_list))) {
return ira->codegen->invalid_instruction;
}
for (size_t token_i = 0; token_i < tok_list.length; token_i += 1) {
AsmToken asm_token = tok_list.at(token_i);
if (asm_token.id == AsmTokenIdVar) {
size_t index = find_asm_index(ira->codegen, node, &asm_token, template_buf);
if (index == SIZE_MAX) {
const char *ptr = buf_ptr(template_buf) + asm_token.start + 2;
uint32_t len = asm_token.end - asm_token.start - 2;
add_node_error(ira->codegen, node,
buf_sprintf("could not find '%.*s' in the inputs or outputs",
len, ptr));
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_gen(ira,
asm_instruction->base.scope, asm_instruction->base.source_node,
template_buf, tok_list.items, tok_list.length,
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)
{
IrInstruction *result = ir_const(ira, &array_type_instruction->base, ira->codegen->builtin_types.entry_type);
result->value->special = ConstValSpecialLazy;
LazyValueArrayType *lazy_array_type = allocate<LazyValueArrayType>(1, "LazyValueArrayType");
lazy_array_type->ira = ira; ira_ref(ira);
result->value->data.x_lazy = &lazy_array_type->base;
lazy_array_type->base.id = LazyValueIdArrayType;
lazy_array_type->elem_type = array_type_instruction->child_type->child;
if (ir_resolve_type_lazy(ira, lazy_array_type->elem_type) == nullptr)
return ira->codegen->invalid_instruction;
if (!ir_resolve_usize(ira, array_type_instruction->size->child, &lazy_array_type->length))
return ira->codegen->invalid_instruction;
if (array_type_instruction->sentinel != nullptr) {
lazy_array_type->sentinel = array_type_instruction->sentinel->child;
if (ir_resolve_const(ira, lazy_array_type->sentinel, LazyOk) == nullptr)
return ira->codegen->invalid_instruction;
}
return result;
}
static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, IrInstructionSizeOf *instruction) {
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_num_lit_int);
result->value->special = ConstValSpecialLazy;
LazyValueSizeOf *lazy_size_of = allocate<LazyValueSizeOf>(1, "LazyValueSizeOf");
lazy_size_of->ira = ira; ira_ref(ira);
result->value->data.x_lazy = &lazy_size_of->base;
lazy_size_of->base.id = LazyValueIdSizeOf;
lazy_size_of->bit_size = instruction->bit_size;
lazy_size_of->target_type = instruction->type_value->child;
if (ir_resolve_type_lazy(ira, lazy_size_of->target_type) == nullptr)
return ira->codegen->invalid_instruction;
return result;
}
static IrInstruction *ir_analyze_test_non_null(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *value) {
ZigType *type_entry = value->value->type;
if (type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.allow_zero) {
if (instr_is_comptime(value)) {
ZigValue *c_ptr_val = ir_resolve_const(ira, value, UndefOk);
if (c_ptr_val == nullptr)
return ira->codegen->invalid_instruction;
if (c_ptr_val->special == ConstValSpecialUndef)
return ir_const_undef(ira, source_inst, 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);
return ir_const_bool(ira, source_inst, !is_null);
}
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 == ZigTypeIdOptional) {
if (instr_is_comptime(value)) {
ZigValue *maybe_val = ir_resolve_const(ira, value, UndefOk);
if (maybe_val == nullptr)
return ira->codegen->invalid_instruction;
if (maybe_val->special == ConstValSpecialUndef)
return ir_const_undef(ira, source_inst, ira->codegen->builtin_types.entry_bool);
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, bool initializing)
{
Error err;
ZigType *type_entry = get_ptr_elem_type(ira->codegen, base_ptr);
if (type_is_invalid(type_entry))
return ira->codegen->invalid_instruction;
if (type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.ptr_len == PtrLenC) {
if (instr_is_comptime(base_ptr)) {
ZigValue *val = ir_resolve_const(ira, base_ptr, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) {
ZigValue *c_ptr_val = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node);
if (c_ptr_val == nullptr)
return ira->codegen->invalid_instruction;
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);
if (is_null) {
ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null"));
return ira->codegen->invalid_instruction;
}
return base_ptr;
}
}
if (!safety_check_on)
return base_ptr;
IrInstruction *c_ptr_val = ir_get_deref(ira, source_instr, base_ptr, nullptr);
ir_build_assert_non_null(ira, source_instr, c_ptr_val);
return base_ptr;
}
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,
base_ptr->value->type->data.pointer.is_const, base_ptr->value->type->data.pointer.is_volatile,
PtrLenSingle, 0, 0, 0, false);
bool same_comptime_repr = types_have_same_zig_comptime_repr(ira->codegen, child_type, type_entry);
if (instr_is_comptime(base_ptr)) {
ZigValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad);
if (ptr_val == nullptr)
return ira->codegen->invalid_instruction;
if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) {
ZigValue *optional_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node);
if (optional_val == nullptr)
return ira->codegen->invalid_instruction;
if (initializing) {
switch (type_has_one_possible_value(ira->codegen, child_type)) {
case OnePossibleValueInvalid:
return ira->codegen->invalid_instruction;
case OnePossibleValueNo:
if (!same_comptime_repr) {
ZigValue *payload_val = create_const_vals(1);
payload_val->type = child_type;
payload_val->special = ConstValSpecialUndef;
payload_val->parent.id = ConstParentIdOptionalPayload;
payload_val->parent.data.p_optional_payload.optional_val = optional_val;
optional_val->data.x_optional = payload_val;
optional_val->special = ConstValSpecialStatic;
}
break;
case OnePossibleValueYes: {
ZigValue *pointee = create_const_vals(1);
pointee->special = ConstValSpecialStatic;
pointee->type = child_type;
pointee->parent.id = ConstParentIdOptionalPayload;
pointee->parent.data.p_optional_payload.optional_val = optional_val;
optional_val->special = ConstValSpecialStatic;
optional_val->data.x_optional = pointee;
break;
}
}
} else {
if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec,
source_instr->source_node, optional_val, UndefBad)))
return ira->codegen->invalid_instruction;
if (optional_value_is_null(optional_val)) {
ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null"));
return ira->codegen->invalid_instruction;
}
}
IrInstruction *result;
if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) {
result = ir_build_optional_unwrap_ptr(&ira->new_irb, source_instr->scope,
source_instr->source_node, base_ptr, false, initializing);
result->value->type = result_type;
result->value->special = ConstValSpecialStatic;
} else {
result = ir_const(ira, source_instr, result_type);
}
ZigValue *result_val = result->value;
result_val->data.x_ptr.special = ConstPtrSpecialRef;
result_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut;
switch (type_has_one_possible_value(ira->codegen, child_type)) {
case OnePossibleValueInvalid:
return ira->codegen->invalid_instruction;
case OnePossibleValueNo:
if (same_comptime_repr) {
result_val->data.x_ptr.data.ref.pointee = optional_val;
} else {
assert(optional_val->data.x_optional != nullptr);
result_val->data.x_ptr.data.ref.pointee = optional_val->data.x_optional;
}
break;
case OnePossibleValueYes:
assert(optional_val->data.x_optional != nullptr);
result_val->data.x_ptr.data.ref.pointee = optional_val->data.x_optional;
break;
}
return result;
}
}
IrInstruction *result = ir_build_optional_unwrap_ptr(&ira->new_irb, source_instr->scope,
source_instr->source_node, base_ptr, safety_check_on, initializing);
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, false);
}
static IrInstruction *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstructionCtz *instruction) {
ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);
if (type_is_invalid(int_type))
return ira->codegen->invalid_instruction;
IrInstruction *op = ir_implicit_cast(ira, instruction->op->child, int_type);
if (type_is_invalid(op->value->type))
return ira->codegen->invalid_instruction;
if (int_type->data.integral.bit_count == 0)
return ir_const_unsigned(ira, &instruction->base, 0);
if (instr_is_comptime(op)) {
ZigValue *val = ir_resolve_const(ira, op, UndefOk);
if (val == nullptr)
return ira->codegen->invalid_instruction;
if (val->special == ConstValSpecialUndef)
return ir_const_undef(ira, &instruction->base, ira->codegen->builtin_types.entry_num_lit_int);
size_t result_usize = bigint_ctz(&op->value->data.x_bigint, int_type->data.integral.bit_count);
return ir_const_unsigned(ira, &instruction->base, result_usize);
}
ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, int_type->data.integral.bit_count);
IrInstruction *result = ir_build_ctz(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, nullptr, op);
result->value->type = return_type;
return result;
}
static IrInstruction *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstructionClz *instruction) {
ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);
if (type_is_invalid(int_type))
return ira->codegen->invalid_instruction;
IrInstruction *op = ir_implicit_cast(ira, instruction->op->child, int_type);
if (type_is_invalid(op->value->type))
return ira->codegen->invalid_instruction;
if (int_type->data.integral.bit_count == 0)
return ir_const_unsigned(ira, &instruction->base, 0);
if (instr_is_comptime(op)) {
ZigValue *val = ir_resolve_const(ira, op, UndefOk);
if (val == nullptr)
return ira->codegen->invalid_instruction;
if (val->special == ConstValSpecialUndef)
return ir_const_undef(ira, &instruction->base, ira->codegen->builtin_types.entry_num_lit_int);
size_t result_usize = bigint_clz(&op->value->data.x_bigint, int_type->data.integral.bit_count);
return ir_const_unsigned(ira, &instruction->base, result_usize);
}
ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, int_type->data.integral.bit_count);
IrInstruction *result = ir_build_clz(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, nullptr, op);
result->value->type = return_type;
return result;
}
static IrInstruction *ir_analyze_instruction_pop_count(IrAnalyze *ira, IrInstructionPopCount *instruction) {
ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);
if (type_is_invalid(int_type))
return ira->codegen->invalid_instruction;
IrInstruction *op = ir_implicit_cast(ira, instruction->op->child, int_type);
if (type_is_invalid(op->value->type))
return ira->codegen->invalid_instruction;
if (int_type->data.integral.bit_count == 0)
return ir_const_unsigned(ira, &instruction->base, 0);
if (instr_is_comptime(op)) {
ZigValue *val = ir_resolve_const(ira, op, UndefOk);
if (val == nullptr)
return ira->codegen->invalid_instruction;
if (val->special == ConstValSpecialUndef)
return ir_const_undef(ira, &instruction->base, ira->codegen->builtin_types.entry_num_lit_int);
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);
}
size_t result = bigint_popcount_signed(&val->data.x_bigint, int_type->data.integral.bit_count);
return ir_const_unsigned(ira, &instruction->base, result);
}
ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, int_type->data.integral.bit_count);
IrInstruction *result = ir_build_pop_count(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, nullptr, op);
result->value->type = return_type;
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)) {
ZigValue *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)) {
ZigValue *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);
ZigValue *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);
IrInstructionSwitchBr *switch_br = 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);
switch_br->base.value->type = ira->codegen->builtin_types.entry_unreachable;
return ir_finish_anal(ira, &switch_br->base);
}
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;
ZigValue *pointee_val = nullptr;
if (instr_is_comptime(target_value_ptr) && target_value_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) {
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 = type_resolve(ira->codegen, target_type, ResolveStatusSizeKnown)))
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 ZigTypeIdFn:
case ZigTypeIdErrorSet: {
if (pointee_val) {
IrInstruction *result = ir_const(ira, &switch_target_instruction->base, nullptr);
copy_const_val(result->value, pointee_val);
result->value->type = target_type;
return result;
}
IrInstruction *result = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr, nullptr);
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, nullptr);
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, nullptr);
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 ZigTypeIdOpaque:
case ZigTypeIdVector:
case ZigTypeIdFnFrame:
case ZigTypeIdAnyFrame:
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;
ZigType *ref_type = target_value_ptr->value->type;
assert(ref_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);
assert(instruction->prongs_len > 0);
IrInstruction *first_prong_value = instruction->prongs_ptr[0]->child;
if (type_is_invalid(first_prong_value->value->type))
return ira->codegen->invalid_instruction;
IrInstruction *first_casted_prong_value = ir_implicit_cast(ira, first_prong_value, enum_type);
if (type_is_invalid(first_casted_prong_value->value->type))
return ira->codegen->invalid_instruction;
ZigValue *first_prong_val = ir_resolve_const(ira, first_casted_prong_value, UndefBad);
if (first_prong_val == nullptr)
return ira->codegen->invalid_instruction;
TypeUnionField *first_field = find_union_field_by_tag(target_type, &first_prong_val->data.x_enum_tag);
ErrorMsg *invalid_payload_msg = nullptr;
for (size_t prong_i = 1; prong_i < instruction->prongs_len; prong_i += 1) {
IrInstruction *this_prong_inst = instruction->prongs_ptr[prong_i]->child;
if (type_is_invalid(this_prong_inst->value->type))
return ira->codegen->invalid_instruction;
IrInstruction *this_casted_prong_value = ir_implicit_cast(ira, this_prong_inst, enum_type);
if (type_is_invalid(this_casted_prong_value->value->type))
return ira->codegen->invalid_instruction;
ZigValue *this_prong = ir_resolve_const(ira, this_casted_prong_value, UndefBad);
if (this_prong == nullptr)
return ira->codegen->invalid_instruction;
TypeUnionField *payload_field = find_union_field_by_tag(target_type, &this_prong->data.x_enum_tag);
ZigType *payload_type = payload_field->type_entry;
if (first_field->type_entry != payload_type) {
if (invalid_payload_msg == nullptr) {
invalid_payload_msg = ir_add_error(ira, &instruction->base,
buf_sprintf("capture group with incompatible types"));
add_error_note(ira->codegen, invalid_payload_msg, first_prong_value->source_node,
buf_sprintf("type '%s' here", buf_ptr(&first_field->type_entry->name)));
}
add_error_note(ira->codegen, invalid_payload_msg, this_prong_inst->source_node,
buf_sprintf("type '%s' here", buf_ptr(&payload_field->type_entry->name)));
}
}
if (invalid_payload_msg != nullptr) {
return ira->codegen->invalid_instruction;
}
if (instr_is_comptime(target_value_ptr)) {
ZigValue *target_val_ptr = ir_resolve_const(ira, target_value_ptr, UndefBad);
if (!target_value_ptr)
return ira->codegen->invalid_instruction;
ZigValue *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, first_field->type_entry,
target_val_ptr->type->data.pointer.is_const));
ZigValue *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, first_field, false, false);
result->value->type = get_pointer_to_type(ira->codegen, first_field->type_entry,
target_value_ptr->value->type->data.pointer.is_const);
return result;
} else if (target_type->id == ZigTypeIdErrorSet) {
// construct an error set from the prong values
ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
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;
ZigList<ErrorTableEntry *> error_list = {};
buf_resize(&err_set_type->name, 0);
buf_appendf(&err_set_type->name, "error{");
for (size_t i = 0; i < instruction->prongs_len; i += 1) {
ErrorTableEntry *err = ir_resolve_error(ira, instruction->prongs_ptr[i]->child);
if (err == nullptr)
return ira->codegen->invalid_instruction;
error_list.append(err);
buf_appendf(&err_set_type->name, "%s,", buf_ptr(&err->name));
}
err_set_type->data.error_set.errors = error_list.items;
err_set_type->data.error_set.err_count = error_list.length;
buf_appendf(&err_set_type->name, "}");
ZigType *new_target_value_ptr_type = get_pointer_to_type_extra(ira->codegen,
err_set_type,
ref_type->data.pointer.is_const, ref_type->data.pointer.is_volatile,
ref_type->data.pointer.ptr_len,
ref_type->data.pointer.explicit_alignment,
ref_type->data.pointer.bit_offset_in_host, ref_type->data.pointer.host_int_bytes,
ref_type->data.pointer.allow_zero);
return ir_analyze_ptr_cast(ira, &instruction->base, target_value_ptr, new_target_value_ptr_type,
&instruction->base, false);
} 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_switch_else_var(IrAnalyze *ira,
IrInstructionSwitchElseVar *instruction)
{
IrInstruction *target_value_ptr = instruction->target_value_ptr->child;
if (type_is_invalid(target_value_ptr->value->type))
return ira->codegen->invalid_instruction;
ZigType *ref_type = target_value_ptr->value->type;
assert(ref_type->id == ZigTypeIdPointer);
ZigType *target_type = target_value_ptr->value->type->data.pointer.child_type;
if (target_type->id == ZigTypeIdErrorSet) {
// make a new set that has the other cases removed
if (!resolve_inferred_error_set(ira->codegen, target_type, instruction->base.source_node)) {
return ira->codegen->invalid_instruction;
}
if (type_is_global_error_set(target_type)) {
// the type of the else capture variable still has to be the global error set.
// once the runtime hint system is more sophisticated, we could add some hint information here.
return target_value_ptr;
}
// Make note of the errors handled by other cases
ErrorTableEntry **errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);
for (size_t case_i = 0; case_i < instruction->switch_br->case_count; case_i += 1) {
IrInstructionSwitchBrCase *br_case = &instruction->switch_br->cases[case_i];
IrInstruction *case_expr = br_case->value->child;
if (case_expr->value->type->id == ZigTypeIdErrorSet) {
ErrorTableEntry *err = ir_resolve_error(ira, case_expr);
if (err == nullptr)
return ira->codegen->invalid_instruction;
errors[err->value] = err;
} else if (case_expr->value->type->id == ZigTypeIdMetaType) {
ZigType *err_set_type = ir_resolve_type(ira, case_expr);
if (type_is_invalid(err_set_type))
return ira->codegen->invalid_instruction;
populate_error_set_table(errors, err_set_type);
} else {
zig_unreachable();
}
}
ZigList<ErrorTableEntry *> result_list = {};
ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
buf_resize(&err_set_type->name, 0);
buf_appendf(&err_set_type->name, "error{");
// Look at all the errors in the type switched on and add them to the result_list
// if they are not handled by cases.
for (uint32_t i = 0; i < target_type->data.error_set.err_count; i += 1) {
ErrorTableEntry *error_entry = target_type->data.error_set.errors[i];
ErrorTableEntry *existing_entry = errors[error_entry->value];
if (existing_entry == nullptr) {
result_list.append(error_entry);
buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name));
}
}
free(errors);
err_set_type->data.error_set.err_count = result_list.length;
err_set_type->data.error_set.errors = result_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, "}");
ZigType *new_target_value_ptr_type = get_pointer_to_type_extra(ira->codegen,
err_set_type,
ref_type->data.pointer.is_const, ref_type->data.pointer.is_volatile,
ref_type->data.pointer.ptr_len,
ref_type->data.pointer.explicit_alignment,
ref_type->data.pointer.bit_offset_in_host, ref_type->data.pointer.host_int_bytes,
ref_type->data.pointer.allow_zero);
return ir_analyze_ptr_cast(ira, &instruction->base, target_value_ptr, new_target_value_ptr_type,
&instruction->base, false);
}
return target_value_ptr;
}
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;
ZigType *target_import;
Buf *import_target_path;
Buf full_path = BUF_INIT;
if ((err = analyze_import(ira->codegen, import, import_target_str, &target_import,
&import_target_path, &full_path)))
{
if (err == ErrorImportOutsidePkgPath) {
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;
} else 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;
}
}
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_union_init(IrAnalyze *ira, IrInstruction *source_instruction,
AstNode *field_source_node, ZigType *union_type, Buf *field_name, IrInstruction *field_result_loc,
IrInstruction *result_loc)
{
Error err;
assert(union_type->id == ZigTypeIdUnion);
if ((err = type_resolve(ira->codegen, union_type, ResolveStatusSizeKnown)))
return ira->codegen->invalid_instruction;
TypeUnionField *type_field = find_union_type_field(union_type, field_name);
if (type_field == nullptr) {
ir_add_error_node(ira, field_source_node,
buf_sprintf("no member named '%s' in union '%s'",
buf_ptr(field_name), buf_ptr(&union_type->name)));
return ira->codegen->invalid_instruction;
}
if (type_is_invalid(type_field->type_entry))
return ira->codegen->invalid_instruction;
if (result_loc->value->data.x_ptr.mut == ConstPtrMutInfer) {
if (instr_is_comptime(field_result_loc) &&
field_result_loc->value->data.x_ptr.mut != ConstPtrMutRuntimeVar)
{
// nothing
} else {
result_loc->value->special = ConstValSpecialRuntime;
}
}
bool is_comptime = ir_should_inline(ira->new_irb.exec, source_instruction->scope)
|| type_requires_comptime(ira->codegen, union_type) == ReqCompTimeYes;
IrInstruction *result = ir_get_deref(ira, source_instruction, result_loc, nullptr);
if (is_comptime && !instr_is_comptime(result)) {
ir_add_error(ira, field_result_loc,
buf_sprintf("unable to evaluate constant expression"));
return ira->codegen->invalid_instruction;
}
return result;
}
static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruction *instruction,
ZigType *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields,
IrInstruction *result_loc)
{
Error err;
if (container_type->id == ZigTypeIdUnion) {
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_result_loc = field->result_loc->child;
if (type_is_invalid(field_result_loc->value->type))
return ira->codegen->invalid_instruction;
return ir_analyze_union_init(ira, instruction, field->source_node, container_type, field->name,
field_result_loc, result_loc);
}
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 (container_type->data.structure.resolve_status == ResolveStatusBeingInferred) {
// We're now done inferring the type.
container_type->data.structure.resolve_status = ResolveStatusUnstarted;
}
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);
ZigList<IrInstruction *> const_ptrs = {};
bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope)
|| type_requires_comptime(ira->codegen, container_type) == ReqCompTimeYes;
// Here we iterate over the fields that have been initialized, and emit
// compile errors for missing fields and duplicate fields.
// It is only now that we find out whether the struct initialization can be a comptime
// value, but we have already emitted runtime instructions for the fields that
// were initialized with runtime values, and have omitted instructions that would have
// initialized fields with comptime values.
// So now we must clean up this situation. If it turns out the struct initialization can
// be a comptime value, overwrite ConstPtrMutInfer with ConstPtrMutComptimeConst.
// Otherwise, we must emit instructions to runtime-initialize the fields that have
// comptime-known values.
for (size_t i = 0; i < instr_field_count; i += 1) {
IrInstructionContainerInitFieldsField *field = &fields[i];
IrInstruction *field_result_loc = field->result_loc->child;
if (type_is_invalid(field_result_loc->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;
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;
if (instr_is_comptime(field_result_loc) &&
field_result_loc->value->data.x_ptr.mut != ConstPtrMutRuntimeVar)
{
const_ptrs.append(field_result_loc);
} else {
first_non_const_instruction = field_result_loc;
}
}
bool any_missing = false;
for (size_t i = 0; i < actual_field_count; i += 1) {
if (field_assign_nodes[i] != nullptr) continue;
// look for a default field value
TypeStructField *field = container_type->data.structure.fields[i];
memoize_field_init_val(ira->codegen, container_type, field);
if (field->init_val == nullptr) {
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;
continue;
}
if (type_is_invalid(field->init_val->type))
return ira->codegen->invalid_instruction;
IrInstruction *runtime_inst = ir_const(ira, instruction, field->init_val->type);
copy_const_val(runtime_inst->value, field->init_val);
IrInstruction *field_ptr = ir_analyze_struct_field_ptr(ira, instruction, field, result_loc,
container_type, true);
ir_analyze_store_ptr(ira, instruction, field_ptr, runtime_inst, false);
if (instr_is_comptime(field_ptr) && field_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) {
const_ptrs.append(field_ptr);
} else {
first_non_const_instruction = result_loc;
}
}
if (any_missing)
return ira->codegen->invalid_instruction;
if (result_loc->value->data.x_ptr.mut == ConstPtrMutInfer) {
if (const_ptrs.length != actual_field_count) {
result_loc->value->special = ConstValSpecialRuntime;
for (size_t i = 0; i < const_ptrs.length; i += 1) {
IrInstruction *field_result_loc = const_ptrs.at(i);
IrInstruction *deref = ir_get_deref(ira, field_result_loc, field_result_loc, nullptr);
field_result_loc->value->special = ConstValSpecialRuntime;
ir_analyze_store_ptr(ira, field_result_loc, field_result_loc, deref, false);
}
}
}
IrInstruction *result = ir_get_deref(ira, instruction, result_loc, nullptr);
if (is_comptime && !instr_is_comptime(result)) {
ir_add_error_node(ira, first_non_const_instruction->source_node,
buf_sprintf("unable to evaluate constant expression"));
return ira->codegen->invalid_instruction;
}
return result;
}
static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira,
IrInstructionContainerInitList *instruction)
{
ir_assert(instruction->result_loc != nullptr, &instruction->base);
IrInstruction *result_loc = instruction->result_loc->child;
if (type_is_invalid(result_loc->value->type))
return result_loc;
ir_assert(result_loc->value->type->id == ZigTypeIdPointer, &instruction->base);
ZigType *container_type = result_loc->value->type->data.pointer.child_type;
size_t elem_count = instruction->item_count;
if (is_slice(container_type)) {
ir_add_error_node(ira, instruction->init_array_type_source_node,
buf_sprintf("array literal requires address-of operator to coerce to slice type '%s'",
buf_ptr(&container_type->name)));
return ira->codegen->invalid_instruction;
}
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);
}
if (container_type->id == ZigTypeIdStruct && elem_count == 0) {
ir_assert(instruction->result_loc != nullptr, &instruction->base);
IrInstruction *result_loc = instruction->result_loc->child;
if (type_is_invalid(result_loc->value->type))
return result_loc;
return ir_analyze_container_init_fields(ira, &instruction->base, container_type, 0, nullptr, result_loc);
}
if (container_type->id == ZigTypeIdArray) {
ZigType *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, nullptr);
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 if (container_type->id == ZigTypeIdStruct &&
container_type->data.structure.resolve_status == ResolveStatusBeingInferred)
{
// We're now done inferring the type.
container_type->data.structure.resolve_status = ResolveStatusUnstarted;
} else if (container_type->id == ZigTypeIdVector) {
// OK
} 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;
}
switch (type_has_one_possible_value(ira->codegen, container_type)) {
case OnePossibleValueInvalid:
return ira->codegen->invalid_instruction;
case OnePossibleValueYes:
return ir_const_move(ira, &instruction->base,
get_the_one_possible_value(ira->codegen, container_type));
case OnePossibleValueNo:
break;
}
bool is_comptime;
switch (type_requires_comptime(ira->codegen, container_type)) {
case ReqCompTimeInvalid:
return ira->codegen->invalid_instruction;
case ReqCompTimeNo:
is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope);
break;
case ReqCompTimeYes:
is_comptime = true;
break;
}
IrInstruction *first_non_const_instruction = nullptr;
// The Result Location Mechanism has already emitted runtime instructions to
// initialize runtime elements and has omitted instructions for the comptime
// elements. However it is only now that we find out whether the array initialization
// can be a comptime value. So we must clean up the situation. If it turns out
// array initialization can be a comptime value, overwrite ConstPtrMutInfer with
// ConstPtrMutComptimeConst. Otherwise, emit instructions to runtime-initialize the
// elements that have comptime-known values.
ZigList<IrInstruction *> const_ptrs = {};
for (size_t i = 0; i < elem_count; i += 1) {
IrInstruction *elem_result_loc = instruction->elem_result_loc_list[i]->child;
if (type_is_invalid(elem_result_loc->value->type))
return ira->codegen->invalid_instruction;
assert(elem_result_loc->value->type->id == ZigTypeIdPointer);
if (instr_is_comptime(elem_result_loc) &&
elem_result_loc->value->data.x_ptr.mut != ConstPtrMutRuntimeVar)
{
const_ptrs.append(elem_result_loc);
} else {
first_non_const_instruction = elem_result_loc;
}
}
if (result_loc->value->data.x_ptr.mut == ConstPtrMutInfer) {
if (const_ptrs.length != elem_count) {
result_loc->value->special = ConstValSpecialRuntime;
for (size_t i = 0; i < const_ptrs.length; i += 1) {
IrInstruction *elem_result_loc = const_ptrs.at(i);
assert(elem_result_loc->value->special == ConstValSpecialStatic);
if (elem_result_loc->value->type->data.pointer.inferred_struct_field != nullptr) {
// This field will be generated comptime; no need to do this.
continue;
}
IrInstruction *deref = ir_get_deref(ira, elem_result_loc, elem_result_loc, nullptr);
elem_result_loc->value->special = ConstValSpecialRuntime;
ir_analyze_store_ptr(ira, elem_result_loc, elem_result_loc, deref, false);
}
}
}
IrInstruction *result = ir_get_deref(ira, &instruction->base, result_loc, nullptr);
if (instr_is_comptime(result))
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;
}
ZigType *result_elem_type = result_loc->value->type->data.pointer.child_type;
if (is_slice(result_elem_type)) {
ErrorMsg *msg = ir_add_error(ira, &instruction->base,
buf_sprintf("runtime-initialized array cannot be casted to slice type '%s'",
buf_ptr(&result_elem_type->name)));
add_error_note(ira->codegen, msg, first_non_const_instruction->source_node,
buf_sprintf("this value is not comptime-known"));
return ira->codegen->invalid_instruction;
}
return result;
}
static IrInstruction *ir_analyze_instruction_container_init_fields(IrAnalyze *ira,
IrInstructionContainerInitFields *instruction)
{
ir_assert(instruction->result_loc != nullptr, &instruction->base);
IrInstruction *result_loc = instruction->result_loc->child;
if (type_is_invalid(result_loc->value->type))
return result_loc;
ir_assert(result_loc->value->type->id == ZigTypeIdPointer, &instruction->base);
ZigType *container_type = result_loc->value->type->data.pointer.child_type;
return ir_analyze_container_init_fields(ira, &instruction->base, container_type,
instruction->field_count, instruction->fields, result_loc);
}
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;
ir_add_error(ira, &instruction->base, msg_buf);
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);
if (msg->value->special == ConstValSpecialLazy) {
// Resolve any lazy value that's passed, we need its value
if (ir_resolve_lazy(ira->codegen, msg->source_node, msg->value))
return ira->codegen->invalid_instruction;
}
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 (instr_is_comptime(casted_value)) {
ZigValue *val = ir_resolve_const(ira, casted_value, UndefBad);
if (val == nullptr)
return ira->codegen->invalid_instruction;
ErrorTableEntry *err = casted_value->value->data.x_err_set;
if (!err->cached_error_name_val) {
ZigValue *array_val = create_const_str_lit(ira->codegen, &err->name)->data.x_ptr.data.ref.pointee;
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);
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);
ZigValue *array_val = create_const_str_lit(ira->codegen, field->name)->data.x_ptr.data.ref.pointee;
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 = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown)))
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)) {
ZigValue *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);
ZigValue *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 = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown)))
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 void ensure_field_index(ZigType *type, const char *field_name, size_t index) {
Buf *field_name_buf;
assert(type != nullptr && !type_is_invalid(type));
field_name_buf = buf_create_from_str(field_name);
TypeStructField *field = find_struct_type_field(type, field_name_buf);
buf_deinit(field_name_buf);
if (field == nullptr || field->src_index != index)
zig_panic("reference to unknown field %s", field_name);
}
static ZigType *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, ZigType *root) {
Error err;
ZigType *type_info_type = get_builtin_type(ira->codegen, "TypeInfo");
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;
assert(var->const_value->type->id == ZigTypeIdMetaType);
return ir_resolve_const_type(ira->codegen, ira->new_irb.exec, nullptr, var->const_value);
}
static Error ir_make_type_info_decls(IrAnalyze *ira, IrInstruction *source_instr, ZigValue *out_val,
ScopeDecls *decls_scope)
{
Error err;
ZigType *type_info_declaration_type = ir_type_info_get_type(ira, "Declaration", nullptr);
if ((err = type_resolve(ira->codegen, type_info_declaration_type, ResolveStatusSizeKnown)))
return err;
ensure_field_index(type_info_declaration_type, "name", 0);
ensure_field_index(type_info_declaration_type, "is_pub", 1);
ensure_field_index(type_info_declaration_type, "data", 2);
ZigType *type_info_declaration_data_type = ir_type_info_get_type(ira, "Data", type_info_declaration_type);
if ((err = type_resolve(ira->codegen, type_info_declaration_data_type, ResolveStatusSizeKnown)))
return err;
ZigType *type_info_fn_decl_type = ir_type_info_get_type(ira, "FnDecl", type_info_declaration_data_type);
if ((err = type_resolve(ira->codegen, type_info_fn_decl_type, ResolveStatusSizeKnown)))
return err;
ZigType *type_info_fn_decl_inline_type = ir_type_info_get_type(ira, "Inline", type_info_fn_decl_type);
if ((err = type_resolve(ira->codegen, type_info_fn_decl_inline_type, ResolveStatusSizeKnown)))
return err;
// Loop through our declarations once to figure out how many declarations we will generate info for.
auto decl_it = decls_scope->decl_table.entry_iterator();
decltype(decls_scope->decl_table)::Entry *curr_entry = nullptr;
int declaration_count = 0;
while ((curr_entry = decl_it.next()) != nullptr) {
// If the declaration 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, false);
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;
}
declaration_count += 1;
}
}
ZigValue *declaration_array = create_const_vals(1);
declaration_array->special = ConstValSpecialStatic;
declaration_array->type = get_array_type(ira->codegen, type_info_declaration_type, declaration_count, nullptr);
declaration_array->data.x_array.special = ConstArraySpecialNone;
declaration_array->data.x_array.data.s_none.elements = create_const_vals(declaration_count);
init_const_slice(ira->codegen, out_val, declaration_array, 0, declaration_count, false);
// Loop through the declarations and generate info.
decl_it = decls_scope->decl_table.entry_iterator();
curr_entry = nullptr;
int declaration_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;
}
ZigValue *declaration_val = &declaration_array->data.x_array.data.s_none.elements[declaration_index];
declaration_val->special = ConstValSpecialStatic;
declaration_val->type = type_info_declaration_type;
ZigValue **inner_fields = alloc_const_vals_ptrs(3);
ZigValue *name = create_const_str_lit(ira->codegen, curr_entry->key)->data.x_ptr.data.ref.pointee;
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_declaration_data_type;
inner_fields[2]->parent.id = ConstParentIdStruct;
inner_fields[2]->parent.data.p_struct.struct_val = declaration_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 = type_resolve(ira->codegen, var->const_value->type, ResolveStatusSizeKnown)))
return ErrorSemanticAnalyzeFail;
if (var->const_value->type->id == ZigTypeIdMetaType) {
// We have a variable of type 'type', so it's actually a type declaration.
// 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);
ZigValue *payload = create_const_vals(1);
payload->special = ConstValSpecialStatic;
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.FnDecl
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 = &fn_entry->proto_node->data.fn_proto;
ZigValue *fn_decl_val = create_const_vals(1);
fn_decl_val->special = ConstValSpecialStatic;
fn_decl_val->type = type_info_fn_decl_type;
fn_decl_val->parent.id = ConstParentIdUnion;
fn_decl_val->parent.data.p_union.union_val = inner_fields[2];
ZigValue **fn_decl_fields = alloc_const_vals_ptrs(9);
fn_decl_val->data.x_struct.fields = fn_decl_fields;
// fn_type: type
ensure_field_index(fn_decl_val->type, "fn_type", 0);
fn_decl_fields[0]->special = ConstValSpecialStatic;
fn_decl_fields[0]->type = ira->codegen->builtin_types.entry_type;
fn_decl_fields[0]->data.x_type = fn_entry->type_entry;
// inline_type: Data.FnDecl.Inline
ensure_field_index(fn_decl_val->type, "inline_type", 1);
fn_decl_fields[1]->special = ConstValSpecialStatic;
fn_decl_fields[1]->type = type_info_fn_decl_inline_type;
bigint_init_unsigned(&fn_decl_fields[1]->data.x_enum_tag, fn_entry->fn_inline);
// is_var_args: bool
ensure_field_index(fn_decl_val->type, "is_var_args", 2);
bool is_varargs = fn_node->is_var_args;
fn_decl_fields[2]->special = ConstValSpecialStatic;
fn_decl_fields[2]->type = ira->codegen->builtin_types.entry_bool;
fn_decl_fields[2]->data.x_bool = is_varargs;
// is_extern: bool
ensure_field_index(fn_decl_val->type, "is_extern", 3);
fn_decl_fields[3]->special = ConstValSpecialStatic;
fn_decl_fields[3]->type = ira->codegen->builtin_types.entry_bool;
fn_decl_fields[3]->data.x_bool = fn_node->is_extern;
// is_export: bool
ensure_field_index(fn_decl_val->type, "is_export", 4);
fn_decl_fields[4]->special = ConstValSpecialStatic;
fn_decl_fields[4]->type = ira->codegen->builtin_types.entry_bool;
fn_decl_fields[4]->data.x_bool = fn_node->is_export;
// lib_name: ?[]const u8
ensure_field_index(fn_decl_val->type, "lib_name", 5);
fn_decl_fields[5]->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_decl_fields[5]->type = get_optional_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr));
if (fn_node->is_extern && fn_node->lib_name != nullptr && buf_len(fn_node->lib_name) > 0) {
fn_decl_fields[5]->data.x_optional = create_const_vals(1);
ZigValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name)->data.x_ptr.data.ref.pointee;
init_const_slice(ira->codegen, fn_decl_fields[5]->data.x_optional, lib_name, 0,
buf_len(fn_node->lib_name), true);
} else {
fn_decl_fields[5]->data.x_optional = nullptr;
}
// return_type: type
ensure_field_index(fn_decl_val->type, "return_type", 6);
fn_decl_fields[6]->special = ConstValSpecialStatic;
fn_decl_fields[6]->type = ira->codegen->builtin_types.entry_type;
fn_decl_fields[6]->data.x_type = fn_entry->type_entry->data.fn.fn_type_id.return_type;
// arg_names: [][] const u8
ensure_field_index(fn_decl_val->type, "arg_names", 7);
size_t fn_arg_count = fn_entry->variable_list.length;
ZigValue *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, nullptr);
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_decl_fields[7], 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);
ZigValue *fn_arg_name_val = &fn_arg_name_array->data.x_array.data.s_none.elements[fn_arg_index];
ZigValue *arg_name = create_const_str_lit(ira->codegen,
buf_create_from_str(arg_var->name))->data.x_ptr.data.ref.pointee;
init_const_slice(ira->codegen, fn_arg_name_val, arg_name, 0, strlen(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_decl_val;
break;
}
case TldIdContainer:
{
ZigType *type_entry = ((TldContainer *)curr_entry->value)->type_entry;
if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown)))
return ErrorSemanticAnalyzeFail;
// This is a type.
bigint_init_unsigned(&inner_fields[2]->data.x_union.tag, 0);
ZigValue *payload = create_const_vals(1);
payload->special = ConstValSpecialStatic;
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();
}
declaration_val->data.x_struct.fields = inner_fields;
declaration_index += 1;
}
assert(declaration_index == declaration_count);
return ErrorNone;
}
static BuiltinPtrSize ptr_len_to_size_enum_index(PtrLen ptr_len) {
switch (ptr_len) {
case PtrLenSingle:
return BuiltinPtrSizeOne;
case PtrLenUnknown:
return BuiltinPtrSizeMany;
case PtrLenC:
return BuiltinPtrSizeC;
}
zig_unreachable();
}
static PtrLen size_enum_index_to_ptr_len(BuiltinPtrSize size_enum_index) {
switch (size_enum_index) {
case BuiltinPtrSizeOne:
return PtrLenSingle;
case BuiltinPtrSizeMany:
case BuiltinPtrSizeSlice:
return PtrLenUnknown;
case BuiltinPtrSizeC:
return PtrLenC;
}
zig_unreachable();
}
static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_entry) {
Error err;
ZigType *attrs_type;
BuiltinPtrSize 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 = BuiltinPtrSizeSlice;
} 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, ResolveStatusSizeKnown)))
return nullptr;
ZigType *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer", nullptr);
assertNoError(type_resolve(ira->codegen, type_info_pointer_type, ResolveStatusSizeKnown));
ZigValue *result = create_const_vals(1);
result->special = ConstValSpecialStatic;
result->type = type_info_pointer_type;
ZigValue **fields = alloc_const_vals_ptrs(7);
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(type_resolve(ira->codegen, type_info_pointer_size_type, ResolveStatusSizeKnown));
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;
// sentinel: var
ensure_field_index(result->type, "sentinel", 6);
fields[6]->special = ConstValSpecialStatic;
if (attrs_type->data.pointer.child_type->id != ZigTypeIdOpaque) {
fields[6]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type);
set_optional_payload(fields[6], attrs_type->data.pointer.sentinel);
} else {
fields[6]->type = ira->codegen->builtin_types.entry_null;
}
return result;
};
static void make_enum_field_val(IrAnalyze *ira, ZigValue *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;
ZigValue **inner_fields = alloc_const_vals_ptrs(2);
inner_fields[1]->special = ConstValSpecialStatic;
inner_fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int;
ZigValue *name = create_const_str_lit(ira->codegen, enum_field->name)->data.x_ptr.data.ref.pointee;
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,
ZigValue **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;
}
ZigValue *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 ZigTypeIdOpaque:
result = ira->codegen->intern.for_void();
break;
case ZigTypeIdInt:
{
result = create_const_vals(1);
result->special = ConstValSpecialStatic;
result->type = ir_type_info_get_type(ira, "Int", nullptr);
ZigValue **fields = alloc_const_vals_ptrs(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);
ZigValue **fields = alloc_const_vals_ptrs(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[0]->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);
ZigValue **fields = alloc_const_vals_ptrs(3);
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;
// sentinel: var
fields[2]->special = ConstValSpecialStatic;
fields[2]->type = get_optional_type(ira->codegen, type_entry->data.array.child_type);
fields[2]->data.x_optional = type_entry->data.array.sentinel;
break;
}
case ZigTypeIdVector: {
result = create_const_vals(1);
result->special = ConstValSpecialStatic;
result->type = ir_type_info_get_type(ira, "Vector", nullptr);
ZigValue **fields = alloc_const_vals_ptrs(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);
ZigValue **fields = alloc_const_vals_ptrs(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 ZigTypeIdAnyFrame: {
result = create_const_vals(1);
result->special = ConstValSpecialStatic;
result->type = ir_type_info_get_type(ira, "AnyFrame", nullptr);
ZigValue **fields = alloc_const_vals_ptrs(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);
fields[0]->data.x_optional = (type_entry->data.any_frame.result_type == nullptr) ? nullptr :
create_const_type(ira->codegen, type_entry->data.any_frame.result_type);
break;
}
case ZigTypeIdEnum:
{
result = create_const_vals(1);
result->special = ConstValSpecialStatic;
result->type = ir_type_info_get_type(ira, "Enum", nullptr);
ZigValue **fields = alloc_const_vals_ptrs(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;
ZigValue *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, nullptr);
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];
ZigValue *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;
}
// decls: []TypeInfo.Declaration
ensure_field_index(result->type, "decls", 3);
if ((err = ir_make_type_info_decls(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();
}
ZigValue *slice_val = create_const_vals(1);
result->data.x_optional = slice_val;
uint32_t error_count = type_entry->data.error_set.err_count;
ZigValue *error_array = create_const_vals(1);
error_array->special = ConstValSpecialStatic;
error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count, nullptr);
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];
ZigValue *error_val = &error_array->data.x_array.data.s_none.elements[error_index];
error_val->special = ConstValSpecialStatic;
error_val->type = type_info_error_type;
ZigValue **inner_fields = alloc_const_vals_ptrs(2);
inner_fields[1]->special = ConstValSpecialStatic;
inner_fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int;
ZigValue *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)->data.x_ptr.data.ref.pointee;
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);
ZigValue **fields = alloc_const_vals_ptrs(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);
ZigValue **fields = alloc_const_vals_ptrs(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)
{
ZigValue *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;
ZigValue *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, nullptr);
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];
ZigValue *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;
ZigValue **inner_fields = alloc_const_vals_ptrs(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;
ZigValue *name = create_const_str_lit(ira->codegen, union_field->name)->data.x_ptr.data.ref.pointee;
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;
}
// decls: []TypeInfo.Declaration
ensure_field_index(result->type, "decls", 3);
if ((err = ir_make_type_info_decls(ira, source_instr, fields[3],
type_entry->data.unionation.decls_scope)))
{
return err;
}
break;
}
case ZigTypeIdStruct:
{
if (type_entry->data.structure.special == StructSpecialSlice) {
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);
ZigValue **fields = alloc_const_vals_ptrs(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;
ZigValue *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, nullptr);
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];
ZigValue *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;
ZigValue **inner_fields = alloc_const_vals_ptrs(4);
inner_fields[1]->special = ConstValSpecialStatic;
inner_fields[1]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int);
ZigType *field_type = resolve_struct_field_type(ira->codegen, struct_field);
if (field_type == nullptr)
return ErrorSemanticAnalyzeFail;
if ((err = type_resolve(ira->codegen, field_type, ResolveStatusZeroBitsKnown)))
return err;
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;
// default_value: var
inner_fields[3]->special = ConstValSpecialStatic;
inner_fields[3]->type = get_optional_type(ira->codegen, struct_field->type_entry);
memoize_field_init_val(ira->codegen, type_entry, struct_field);
set_optional_payload(inner_fields[3], struct_field->init_val);
ZigValue *name = create_const_str_lit(ira->codegen, struct_field->name)->data.x_ptr.data.ref.pointee;
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;
}
// decls: []TypeInfo.Declaration
ensure_field_index(result->type, "decls", 2);
if ((err = ir_make_type_info_decls(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);
ZigValue **fields = alloc_const_vals_ptrs(5);
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 = get_builtin_type(ira->codegen, "CallingConvention");
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 {
ZigValue *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;
}
// 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);
ZigValue *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, nullptr);
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[4], 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];
ZigValue *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);
ZigValue **inner_fields = alloc_const_vals_ptrs(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 {
ZigValue *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;
}
case ZigTypeIdFnFrame:
zig_panic("TODO @typeInfo for async function frames");
}
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);
ZigValue *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);
ZigValue *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 ZigValue *get_const_field(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value,
const char *name, size_t field_index)
{
Error err;
ensure_field_index(struct_value->type, name, field_index);
ZigValue *val = struct_value->data.x_struct.fields[field_index];
if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, source_node, val, UndefBad)))
return nullptr;
return val;
}
static Error get_const_field_sentinel(IrAnalyze *ira, IrInstruction *source_instr, ZigValue *struct_value,
const char *name, size_t field_index, ZigType *elem_type, ZigValue **result)
{
ZigValue *field_val = get_const_field(ira, source_instr->source_node, struct_value, name, field_index);
if (field_val == nullptr)
return ErrorSemanticAnalyzeFail;
IrInstruction *field_inst = ir_const_move(ira, source_instr, field_val);
IrInstruction *casted_field_inst = ir_implicit_cast(ira, field_inst,
get_optional_type(ira->codegen, elem_type));
if (type_is_invalid(casted_field_inst->value->type))
return ErrorSemanticAnalyzeFail;
if (optional_value_is_null(casted_field_inst->value)) {
*result = nullptr;
} else {
assert(type_has_optional_repr(casted_field_inst->value->type));
*result = casted_field_inst->value->data.x_optional;
}
return ErrorNone;
}
static Error get_const_field_bool(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value,
const char *name, size_t field_index, bool *out)
{
ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index);
if (value == nullptr)
return ErrorSemanticAnalyzeFail;
assert(value->type == ira->codegen->builtin_types.entry_bool);
*out = value->data.x_bool;
return ErrorNone;
}
static BigInt *get_const_field_lit_int(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, const char *name, size_t field_index)
{
ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index);
if (value == nullptr)
return nullptr;
assert(value->type == ira->codegen->builtin_types.entry_num_lit_int);
return &value->data.x_bigint;
}
static ZigType *get_const_field_meta_type(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, const char *name, size_t field_index)
{
ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index);
if (value == nullptr)
return ira->codegen->invalid_instruction->value->type;
assert(value->type == ira->codegen->builtin_types.entry_type);
return value->data.x_type;
}
static ZigType *type_info_to_type(IrAnalyze *ira, IrInstruction *instruction, ZigTypeId tagTypeId, ZigValue *payload) {
Error err;
switch (tagTypeId) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdMetaType:
return ira->codegen->builtin_types.entry_type;
case ZigTypeIdVoid:
return ira->codegen->builtin_types.entry_void;
case ZigTypeIdBool:
return ira->codegen->builtin_types.entry_bool;
case ZigTypeIdUnreachable:
return ira->codegen->builtin_types.entry_unreachable;
case ZigTypeIdInt: {
assert(payload->special == ConstValSpecialStatic);
assert(payload->type == ir_type_info_get_type(ira, "Int", nullptr));
BigInt *bi = get_const_field_lit_int(ira, instruction->source_node, payload, "bits", 1);
if (bi == nullptr)
return ira->codegen->invalid_instruction->value->type;
bool is_signed;
if ((err = get_const_field_bool(ira, instruction->source_node, payload, "is_signed", 0, &is_signed)))
return ira->codegen->invalid_instruction->value->type;
return get_int_type(ira->codegen, is_signed, bigint_as_u32(bi));
}
case ZigTypeIdFloat:
{
assert(payload->special == ConstValSpecialStatic);
assert(payload->type == ir_type_info_get_type(ira, "Float", nullptr));
BigInt *bi = get_const_field_lit_int(ira, instruction->source_node, payload, "bits", 0);
if (bi == nullptr)
return ira->codegen->invalid_instruction->value->type;
uint32_t bits = bigint_as_u32(bi);
switch (bits) {
case 16: return ira->codegen->builtin_types.entry_f16;
case 32: return ira->codegen->builtin_types.entry_f32;
case 64: return ira->codegen->builtin_types.entry_f64;
case 128: return ira->codegen->builtin_types.entry_f128;
}
ir_add_error(ira, instruction,
buf_sprintf("%d-bit float unsupported", bits));
return ira->codegen->invalid_instruction->value->type;
}
case ZigTypeIdPointer:
{
ZigType *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer", nullptr);
assert(payload->special == ConstValSpecialStatic);
assert(payload->type == type_info_pointer_type);
ZigValue *size_value = get_const_field(ira, instruction->source_node, payload, "size", 0);
assert(size_value->type == ir_type_info_get_type(ira, "Size", type_info_pointer_type));
BuiltinPtrSize size_enum_index = (BuiltinPtrSize)bigint_as_u32(&size_value->data.x_enum_tag);
PtrLen ptr_len = size_enum_index_to_ptr_len(size_enum_index);
ZigType *elem_type = get_const_field_meta_type(ira, instruction->source_node, payload, "child", 4);
if (type_is_invalid(elem_type))
return ira->codegen->invalid_instruction->value->type;
ZigValue *sentinel;
if ((err = get_const_field_sentinel(ira, instruction, payload, "sentinel", 6,
elem_type, &sentinel)))
{
return ira->codegen->invalid_instruction->value->type;
}
BigInt *bi = get_const_field_lit_int(ira, instruction->source_node, payload, "alignment", 3);
if (bi == nullptr)
return ira->codegen->invalid_instruction->value->type;
bool is_const;
if ((err = get_const_field_bool(ira, instruction->source_node, payload, "is_const", 1, &is_const)))
return ira->codegen->invalid_instruction->value->type;
bool is_volatile;
if ((err = get_const_field_bool(ira, instruction->source_node, payload, "is_volatile", 2,
&is_volatile)))
{
return ira->codegen->invalid_instruction->value->type;
}
bool is_allowzero;
if ((err = get_const_field_bool(ira, instruction->source_node, payload, "is_allowzero", 5,
&is_allowzero)))
{
return ira->codegen->invalid_instruction->value->type;
}
ZigType *ptr_type = get_pointer_to_type_extra2(ira->codegen,
elem_type,
is_const,
is_volatile,
ptr_len,
bigint_as_u32(bi),
0, // bit_offset_in_host
0, // host_int_bytes
is_allowzero,
VECTOR_INDEX_NONE, nullptr, sentinel);
if (size_enum_index != 2)
return ptr_type;
return get_slice_type(ira->codegen, ptr_type);
}
case ZigTypeIdArray: {
assert(payload->special == ConstValSpecialStatic);
assert(payload->type == ir_type_info_get_type(ira, "Array", nullptr));
ZigType *elem_type = get_const_field_meta_type(ira, instruction->source_node, payload, "child", 1);
if (type_is_invalid(elem_type))
return ira->codegen->invalid_instruction->value->type;
ZigValue *sentinel;
if ((err = get_const_field_sentinel(ira, instruction, payload, "sentinel", 2,
elem_type, &sentinel)))
{
return ira->codegen->invalid_instruction->value->type;
}
BigInt *bi = get_const_field_lit_int(ira, instruction->source_node, payload, "len", 0);
if (bi == nullptr)
return ira->codegen->invalid_instruction->value->type;
return get_array_type(ira->codegen, elem_type, bigint_as_u64(bi), sentinel);
}
case ZigTypeIdComptimeFloat:
return ira->codegen->builtin_types.entry_num_lit_float;
case ZigTypeIdComptimeInt:
return ira->codegen->builtin_types.entry_num_lit_int;
case ZigTypeIdUndefined:
return ira->codegen->builtin_types.entry_undef;
case ZigTypeIdNull:
return ira->codegen->builtin_types.entry_null;
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
case ZigTypeIdOpaque:
case ZigTypeIdFnFrame:
case ZigTypeIdAnyFrame:
case ZigTypeIdVector:
case ZigTypeIdEnumLiteral:
ir_add_error(ira, instruction, buf_sprintf(
"TODO implement @Type for 'TypeInfo.%s': see https://github.com/ziglang/zig/issues/2907", type_id_name(tagTypeId)));
return ira->codegen->invalid_instruction->value->type;
case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdBoundFn:
case ZigTypeIdStruct:
ir_add_error(ira, instruction, buf_sprintf(
"@Type not availble for 'TypeInfo.%s'", type_id_name(tagTypeId)));
return ira->codegen->invalid_instruction->value->type;
}
zig_unreachable();
}
static IrInstruction *ir_analyze_instruction_type(IrAnalyze *ira, IrInstructionType *instruction) {
IrInstruction *type_info_ir = instruction->type_info->child;
if (type_is_invalid(type_info_ir->value->type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_ir = ir_implicit_cast(ira, type_info_ir, ir_type_info_get_type(ira, nullptr, nullptr));
if (type_is_invalid(casted_ir->value->type))
return ira->codegen->invalid_instruction;
ZigValue *type_info_value = ir_resolve_const(ira, casted_ir, UndefBad);
if (!type_info_value)
return ira->codegen->invalid_instruction;
ZigTypeId typeId = type_id_at_index(bigint_as_usize(&type_info_value->data.x_union.tag));
ZigType *type = type_info_to_type(ira, type_info_ir, typeId, type_info_value->data.x_union.payload);
if (type_is_invalid(type))
return ira->codegen->invalid_instruction;
return ir_const_type(ira, &instruction->base, type);
}
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;
ZigType *result_type = get_builtin_type(ira->codegen, "TypeId");
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);
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;
ZigValue *cimport_result;
ZigValue *result_ptr;
create_result_ptr(ira->codegen, void_type, &cimport_result, &result_ptr);
if ((err = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, result_ptr,
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr,
&cimport_scope->buf, block_node, nullptr, nullptr, nullptr, UndefBad)))
{
return ira->codegen->invalid_instruction;
}
if (type_is_invalid(cimport_result->type))
return ira->codegen->invalid_instruction;
destroy(result_ptr, "ZigValue");
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
Stage2ErrorMsg *errors_ptr;
size_t errors_len;
Stage2Ast *ast;
const char *resources_path = buf_ptr(ira->codegen->zig_c_headers_dir);
if ((err = stage2_translate_c(&ast, &errors_ptr, &errors_len,
&clang_argv.at(0), &clang_argv.last(), 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];
// Clang can emit "too many errors, stopping now", in which case `source` and `filename_ptr` are null
if (clang_err->source && clang_err->filename_ptr) {
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;
}
stage2_render_ast(ast, out_file);
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 = nullptr;
// The second parameter is either a string or void (equivalent to "")
if (value->value->type->id != ZigTypeIdVoid) {
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),
define_value ? 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), nullptr);
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;
if (operand_type->id == ZigTypeIdFloat) {
ir_add_error(ira, instruction->type_value->child,
buf_sprintf("expected integer, enum or pointer type, found '%s'", buf_ptr(&operand_type->name)));
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) && casted_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar &&
instr_is_comptime(casted_cmp_value) && instr_is_comptime(casted_new_value)) {
zig_panic("TODO compile-time execution of cmpxchg");
}
ZigType *result_type = get_optional_type(ira->codegen, operand_type);
IrInstruction *result_loc;
if (handle_is_ptr(result_type)) {
result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc,
result_type, nullptr, true, true);
if (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc)) {
return result_loc;
}
} else {
result_loc = nullptr;
}
return ir_build_cmpxchg_gen(ira, &instruction->base, result_type,
casted_ptr, casted_cmp_value, casted_new_value,
success_order, failure_order, instruction->is_weak, result_loc);
}
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;
if (order < AtomicOrderAcquire) {
ir_add_error(ira, order_value,
buf_sprintf("atomic ordering must be Acquire or stricter"));
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)) {
ZigValue *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);
} 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 (src_ptr_align != 0) {
if ((err = type_resolve(ira->codegen, dest_child_type, ResolveStatusAlignmentKnown)))
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)) {
ZigValue *val = ir_resolve_const(ira, casted_value, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
ZigValue *len_val = val->data.x_struct.fields[slice_len_index];
if (value_is_comptime(len_val)) {
known_len = bigint_as_u64(&len_val->data.x_bigint);
have_known_len = true;
}
}
IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc,
dest_slice_type, nullptr, true, true);
if (result_loc != nullptr && (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc))) {
return result_loc;
}
if (target->value->type->id == ZigTypeIdPointer &&
target->value->type->data.pointer.ptr_len == PtrLenSingle &&
target->value->type->data.pointer.child_type->id == ZigTypeIdArray)
{
known_len = target->value->type->data.pointer.child_type->data.array.len;
have_known_len = true;
} else 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) {
if ((err = type_resolve(ira->codegen, dest_child_type, ResolveStatusSizeKnown)))
return ira->codegen->invalid_instruction;
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;
}
}
return ir_build_resize_slice(ira, &instruction->base, casted_value, dest_slice_type, result_loc);
}
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)) {
ZigValue *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 = alloc_const_vals_ptrs(2);
ZigValue *ptr_val = result->value->data.x_struct.fields[slice_ptr_index];
ZigValue *target_ptr_val = target_val->data.x_struct.fields[slice_ptr_index];
copy_const_val(ptr_val, target_ptr_val);
ptr_val->type = dest_ptr_type;
ZigValue *len_val = result->value->data.x_struct.fields[slice_len_index];
len_val->special = ConstValSpecialStatic;
len_val->type = ira->codegen->builtin_types.entry_usize;
ZigValue *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_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc,
dest_slice_type, nullptr, true, true);
if (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc)) {
return result_loc;
}
return ir_build_resize_slice(ira, &instruction->base, target, dest_slice_type, result_loc);
}
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);
}
static IrInstruction *ir_analyze_float_to_int(IrAnalyze *ira, IrInstruction *source_instr,
ZigType *dest_type, IrInstruction *operand, AstNode *operand_source_node)
{
if (operand->value->type->id == ZigTypeIdComptimeInt) {
return ir_implicit_cast(ira, operand, dest_type);
}
if (operand->value->type->id != ZigTypeIdFloat && operand->value->type->id != ZigTypeIdComptimeFloat) {
ir_add_error_node(ira, operand_source_node, buf_sprintf("expected float type, found '%s'",
buf_ptr(&operand->value->type->name)));
return ira->codegen->invalid_instruction;
}
return ir_resolve_cast(ira, source_instr, operand, dest_type, CastOpFloatToInt);
}
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 *operand = instruction->target->child;
if (type_is_invalid(operand->value->type))
return ira->codegen->invalid_instruction;
return ir_analyze_float_to_int(ira, &instruction->base, dest_type, operand, instruction->target->source_node);
}
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);
}
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_vector_elem_type(ira, instruction->elem_type->child);
if (type_is_invalid(elem_type))
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_shuffle_vector(IrAnalyze *ira, IrInstruction *source_instr,
ZigType *scalar_type, IrInstruction *a, IrInstruction *b, IrInstruction *mask)
{
ir_assert(source_instr && scalar_type && a && b && mask, source_instr);
ir_assert(is_valid_vector_elem_type(scalar_type), source_instr);
uint32_t len_mask;
if (mask->value->type->id == ZigTypeIdVector) {
len_mask = mask->value->type->data.vector.len;
} else if (mask->value->type->id == ZigTypeIdArray) {
len_mask = mask->value->type->data.array.len;
} else {
ir_add_error(ira, mask,
buf_sprintf("expected vector or array, found '%s'",
buf_ptr(&mask->value->type->name)));
return ira->codegen->invalid_instruction;
}
mask = ir_implicit_cast(ira, mask, get_vector_type(ira->codegen, len_mask,
ira->codegen->builtin_types.entry_i32));
if (type_is_invalid(mask->value->type))
return ira->codegen->invalid_instruction;
uint32_t len_a;
if (a->value->type->id == ZigTypeIdVector) {
len_a = a->value->type->data.vector.len;
} else if (a->value->type->id == ZigTypeIdArray) {
len_a = a->value->type->data.array.len;
} else if (a->value->type->id == ZigTypeIdUndefined) {
len_a = UINT32_MAX;
} else {
ir_add_error(ira, a,
buf_sprintf("expected vector or array with element type '%s', found '%s'",
buf_ptr(&scalar_type->name),
buf_ptr(&a->value->type->name)));
return ira->codegen->invalid_instruction;
}
uint32_t len_b;
if (b->value->type->id == ZigTypeIdVector) {
len_b = b->value->type->data.vector.len;
} else if (b->value->type->id == ZigTypeIdArray) {
len_b = b->value->type->data.array.len;
} else if (b->value->type->id == ZigTypeIdUndefined) {
len_b = UINT32_MAX;
} else {
ir_add_error(ira, b,
buf_sprintf("expected vector or array with element type '%s', found '%s'",
buf_ptr(&scalar_type->name),
buf_ptr(&b->value->type->name)));
return ira->codegen->invalid_instruction;
}
if (len_a == UINT32_MAX && len_b == UINT32_MAX) {
return ir_const_undef(ira, a, get_vector_type(ira->codegen, len_mask, scalar_type));
}
if (len_a == UINT32_MAX) {
len_a = len_b;
a = ir_const_undef(ira, a, get_vector_type(ira->codegen, len_a, scalar_type));
} else {
a = ir_implicit_cast(ira, a, get_vector_type(ira->codegen, len_a, scalar_type));
if (type_is_invalid(a->value->type))
return ira->codegen->invalid_instruction;
}
if (len_b == UINT32_MAX) {
len_b = len_a;
b = ir_const_undef(ira, b, get_vector_type(ira->codegen, len_b, scalar_type));
} else {
b = ir_implicit_cast(ira, b, get_vector_type(ira->codegen, len_b, scalar_type));
if (type_is_invalid(b->value->type))
return ira->codegen->invalid_instruction;
}
ZigValue *mask_val = ir_resolve_const(ira, mask, UndefOk);
if (mask_val == nullptr)
return ira->codegen->invalid_instruction;
expand_undef_array(ira->codegen, mask_val);
for (uint32_t i = 0; i < len_mask; i += 1) {
ZigValue *mask_elem_val = &mask_val->data.x_array.data.s_none.elements[i];
if (mask_elem_val->special == ConstValSpecialUndef)
continue;
int32_t v_i32 = bigint_as_signed(&mask_elem_val->data.x_bigint);
uint32_t v;
IrInstruction *chosen_operand;
if (v_i32 >= 0) {
v = (uint32_t)v_i32;
chosen_operand = a;
} else {
v = (uint32_t)~v_i32;
chosen_operand = b;
}
if (v >= chosen_operand->value->type->data.vector.len) {
ErrorMsg *msg = ir_add_error(ira, mask,
buf_sprintf("mask index '%u' has out-of-bounds selection", i));
add_error_note(ira->codegen, msg, chosen_operand->source_node,
buf_sprintf("selected index '%u' out of bounds of %s", v,
buf_ptr(&chosen_operand->value->type->name)));
if (chosen_operand == a && v < len_a + len_b) {
add_error_note(ira->codegen, msg, b->source_node,
buf_create_from_str("selections from the second vector are specified with negative numbers"));
}
return ira->codegen->invalid_instruction;
}
}
ZigType *result_type = get_vector_type(ira->codegen, len_mask, scalar_type);
if (instr_is_comptime(a) && instr_is_comptime(b)) {
ZigValue *a_val = ir_resolve_const(ira, a, UndefOk);
if (a_val == nullptr)
return ira->codegen->invalid_instruction;
ZigValue *b_val = ir_resolve_const(ira, b, UndefOk);
if (b_val == nullptr)
return ira->codegen->invalid_instruction;
expand_undef_array(ira->codegen, a_val);
expand_undef_array(ira->codegen, b_val);
IrInstruction *result = ir_const(ira, source_instr, result_type);
result->value->data.x_array.data.s_none.elements = create_const_vals(len_mask);
for (uint32_t i = 0; i < mask_val->type->data.vector.len; i += 1) {
ZigValue *mask_elem_val = &mask_val->data.x_array.data.s_none.elements[i];
ZigValue *result_elem_val = &result->value->data.x_array.data.s_none.elements[i];
if (mask_elem_val->special == ConstValSpecialUndef) {
result_elem_val->special = ConstValSpecialUndef;
continue;
}
int32_t v = bigint_as_signed(&mask_elem_val->data.x_bigint);
// We've already checked for and emitted compile errors for index out of bounds here.
ZigValue *src_elem_val = (v >= 0) ?
&a->value->data.x_array.data.s_none.elements[v] :
&b->value->data.x_array.data.s_none.elements[~v];
copy_const_val(result_elem_val, src_elem_val);
ir_assert(result_elem_val->special == ConstValSpecialStatic, source_instr);
}
result->value->special = ConstValSpecialStatic;
return result;
}
// All static analysis passed, and not comptime.
// For runtime codegen, vectors a and b must be the same length. Here we
// recursively @shuffle the smaller vector to append undefined elements
// to it up to the length of the longer vector. This recursion terminates
// in 1 call because these calls to ir_analyze_shuffle_vector guarantee
// len_a == len_b.
if (len_a != len_b) {
uint32_t len_min = min(len_a, len_b);
uint32_t len_max = max(len_a, len_b);
IrInstruction *expand_mask = ir_const(ira, mask,
get_vector_type(ira->codegen, len_max, ira->codegen->builtin_types.entry_i32));
expand_mask->value->data.x_array.data.s_none.elements = create_const_vals(len_max);
uint32_t i = 0;
for (; i < len_min; i += 1)
bigint_init_unsigned(&expand_mask->value->data.x_array.data.s_none.elements[i].data.x_bigint, i);
for (; i < len_max; i += 1)
bigint_init_signed(&expand_mask->value->data.x_array.data.s_none.elements[i].data.x_bigint, -1);
IrInstruction *undef = ir_const_undef(ira, source_instr,
get_vector_type(ira->codegen, len_min, scalar_type));
if (len_b < len_a) {
b = ir_analyze_shuffle_vector(ira, source_instr, scalar_type, b, undef, expand_mask);
} else {
a = ir_analyze_shuffle_vector(ira, source_instr, scalar_type, a, undef, expand_mask);
}
}
IrInstruction *result = ir_build_shuffle_vector(&ira->new_irb,
source_instr->scope, source_instr->source_node,
nullptr, a, b, mask);
result->value->type = result_type;
return result;
}
static IrInstruction *ir_analyze_instruction_shuffle_vector(IrAnalyze *ira, IrInstructionShuffleVector *instruction) {
ZigType *scalar_type = ir_resolve_vector_elem_type(ira, instruction->scalar_type);
if (type_is_invalid(scalar_type))
return ira->codegen->invalid_instruction;
IrInstruction *a = instruction->a->child;
if (type_is_invalid(a->value->type))
return ira->codegen->invalid_instruction;
IrInstruction *b = instruction->b->child;
if (type_is_invalid(b->value->type))
return ira->codegen->invalid_instruction;
IrInstruction *mask = instruction->mask->child;
if (type_is_invalid(mask->value->type))
return ira->codegen->invalid_instruction;
return ir_analyze_shuffle_vector(ira, &instruction->base, scalar_type, a, b, mask);
}
static IrInstruction *ir_analyze_instruction_splat(IrAnalyze *ira, IrInstructionSplatSrc *instruction) {
Error err;
IrInstruction *len = instruction->len->child;
if (type_is_invalid(len->value->type))
return ira->codegen->invalid_instruction;
IrInstruction *scalar = instruction->scalar->child;
if (type_is_invalid(scalar->value->type))
return ira->codegen->invalid_instruction;
uint64_t len_u64;
if (!ir_resolve_unsigned(ira, len, ira->codegen->builtin_types.entry_u32, &len_u64))
return ira->codegen->invalid_instruction;
uint32_t len_int = len_u64;
if ((err = ir_validate_vector_elem_type(ira, scalar, scalar->value->type)))
return ira->codegen->invalid_instruction;
ZigType *return_type = get_vector_type(ira->codegen, len_int, scalar->value->type);
if (instr_is_comptime(scalar)) {
ZigValue *scalar_val = ir_resolve_const(ira, scalar, UndefOk);
if (scalar_val == nullptr)
return ira->codegen->invalid_instruction;
if (scalar_val->special == ConstValSpecialUndef)
return ir_const_undef(ira, &instruction->base, return_type);
IrInstruction *result = ir_const(ira, &instruction->base, return_type);
result->value->data.x_array.data.s_none.elements = create_const_vals(len_int);
for (uint32_t i = 0; i < len_int; i += 1) {
copy_const_val(&result->value->data.x_array.data.s_none.elements[i], scalar_val);
}
return result;
}
return ir_build_splat_gen(ira, &instruction->base, return_type, scalar);
}
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)) {
ZigValue *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 (instr_is_comptime(casted_dest_ptr) &&
instr_is_comptime(casted_byte) &&
instr_is_comptime(casted_count))
{
ZigValue *dest_ptr_val = ir_resolve_const(ira, casted_dest_ptr, UndefBad);
if (dest_ptr_val == nullptr)
return ira->codegen->invalid_instruction;
ZigValue *byte_val = ir_resolve_const(ira, casted_byte, UndefOk);
if (byte_val == nullptr)
return ira->codegen->invalid_instruction;
ZigValue *count_val = ir_resolve_const(ira, casted_count, UndefBad);
if (count_val == nullptr)
return ira->codegen->invalid_instruction;
if (casted_dest_ptr->value->data.x_ptr.special != ConstPtrSpecialHardCodedAddr &&
casted_dest_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar)
{
ZigValue *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:
{
ZigValue *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_usize(&count_val->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;
}
for (size_t i = start; i < end; i += 1) {
copy_const_val(&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 (instr_is_comptime(casted_dest_ptr) &&
instr_is_comptime(casted_src_ptr) &&
instr_is_comptime(casted_count))
{
ZigValue *dest_ptr_val = ir_resolve_const(ira, casted_dest_ptr, UndefBad);
if (dest_ptr_val == nullptr)
return ira->codegen->invalid_instruction;
ZigValue *src_ptr_val = ir_resolve_const(ira, casted_src_ptr, UndefBad);
if (src_ptr_val == nullptr)
return ira->codegen->invalid_instruction;
ZigValue *count_val = ir_resolve_const(ira, casted_count, UndefBad);
if (count_val == nullptr)
return ira->codegen->invalid_instruction;
if (dest_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) {
size_t count = bigint_as_usize(&count_val->data.x_bigint);
ZigValue *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:
{
ZigValue *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;
}
ZigValue *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:
{
ZigValue *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) {
copy_const_val(&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, IrInstructionSliceSrc *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 *non_sentinel_slice_ptr_type;
ZigType *elem_type;
if (array_type->id == ZigTypeIdArray) {
elem_type = array_type->data.array.child_type;
bool is_comptime_const = ptr_ptr->value->special == ConstValSpecialStatic &&
ptr_ptr->value->data.x_ptr.mut == ConstPtrMutComptimeConst;
non_sentinel_slice_ptr_type = get_pointer_to_type_extra(ira->codegen, elem_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);
} 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) {
elem_type = main_type->data.pointer.child_type;
non_sentinel_slice_ptr_type = get_pointer_to_type_extra(ira->codegen,
elem_type,
array_type->data.pointer.is_const, array_type->data.pointer.is_volatile,
PtrLenUnknown,
array_type->data.pointer.explicit_alignment, 0, 0, false);
} else {
ir_add_error(ira, &instruction->base, buf_sprintf("slice of single-item pointer"));
return ira->codegen->invalid_instruction;
}
} else {
elem_type = array_type->data.pointer.child_type;
if (array_type->data.pointer.ptr_len == PtrLenC) {
array_type = adjust_ptr_len(ira->codegen, array_type, PtrLenUnknown);
}
ZigType *maybe_sentineled_slice_ptr_type = array_type;
non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr);
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 *maybe_sentineled_slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry;
non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr);
elem_type = non_sentinel_slice_ptr_type->data.pointer.child_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;
}
ZigType *return_type;
ZigValue *sentinel_val = nullptr;
if (instruction->sentinel) {
IrInstruction *uncasted_sentinel = instruction->sentinel->child;
if (type_is_invalid(uncasted_sentinel->value->type))
return ira->codegen->invalid_instruction;
IrInstruction *sentinel = ir_implicit_cast(ira, uncasted_sentinel, elem_type);
if (type_is_invalid(sentinel->value->type))
return ira->codegen->invalid_instruction;
sentinel_val = ir_resolve_const(ira, sentinel, UndefBad);
if (sentinel_val == nullptr)
return ira->codegen->invalid_instruction;
ZigType *slice_ptr_type = adjust_ptr_sentinel(ira->codegen, non_sentinel_slice_ptr_type, sentinel_val);
return_type = get_slice_type(ira->codegen, slice_ptr_type);
} else {
return_type = get_slice_type(ira->codegen, non_sentinel_slice_ptr_type);
}
if (instr_is_comptime(ptr_ptr) &&
value_is_comptime(casted_start->value) &&
(!end || value_is_comptime(end->value)))
{
ZigValue *array_val;
ZigValue *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;
if (parent_ptr->special == ConstValSpecialUndef) {
array_val = nullptr;
abs_offset = 0;
rel_end = SIZE_MAX;
ptr_is_undef = true;
} else {
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)) {
ZigValue *slice_ptr = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.source_node);
if (slice_ptr == nullptr)
return ira->codegen->invalid_instruction;
if (slice_ptr->special == ConstValSpecialUndef) {
ir_add_error(ira, &instruction->base, buf_sprintf("slice of undefined"));
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;
}
ZigValue *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_usize(&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_usize(&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();
}
ZigValue *start_val = ir_resolve_const(ira, casted_start, UndefBad);
if (!start_val)
return ira->codegen->invalid_instruction;
uint64_t start_scalar = bigint_as_u64(&start_val->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 = rel_end;
if (end) {
ZigValue *end_val = ir_resolve_const(ira, end, UndefBad);
if (!end_val)
return ira->codegen->invalid_instruction;
end_scalar = bigint_as_u64(&end_val->data.x_bigint);
}
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);
ZigValue *out_val = result->value;
out_val->data.x_struct.fields = alloc_const_vals_ptrs(2);
ZigValue *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");
}
ZigValue *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 *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc,
return_type, nullptr, true, true);
if (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc)) {
return result_loc;
}
return ir_build_slice_gen(ira, &instruction->base, return_type,
ptr_ptr, casted_start, end, instruction->safety_check_on, result_loc);
}
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 = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown)))
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 = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown)))
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 = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown)))
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_has_field(IrAnalyze *ira, IrInstructionHasField *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;
if ((err = type_resolve(ira->codegen, container_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
Buf *field_name = ir_resolve_str(ira, instruction->field_name->child);
if (field_name == nullptr)
return ira->codegen->invalid_instruction;
bool result;
if (container_type->id == ZigTypeIdStruct) {
result = find_struct_type_field(container_type, field_name) != nullptr;
} else if (container_type->id == ZigTypeIdEnum) {
result = find_enum_type_field(container_type, field_name) != nullptr;
} else if (container_type->id == ZigTypeIdUnion) {
result = find_union_type_field(container_type, field_name) != nullptr;
} else {
ir_add_error(ira, instruction->container_type,
buf_sprintf("type '%s' does not support @hasField", buf_ptr(&container_type->name)));
return ira->codegen->invalid_instruction;
}
return ir_const_bool(ira, &instruction->base, result);
}
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_frame_handle(IrAnalyze *ira, IrInstructionFrameHandle *instruction) {
ZigFn *fn = exec_fn_entry(ira->new_irb.exec);
ir_assert(fn != nullptr, &instruction->base);
if (fn->inferred_async_node == nullptr) {
fn->inferred_async_node = instruction->base.source_node;
}
ZigType *frame_type = get_fn_frame_type(ira->codegen, fn);
ZigType *ptr_frame_type = get_pointer_to_type(ira->codegen, frame_type, false);
IrInstruction *result = ir_build_handle(&ira->new_irb, instruction->base.scope, instruction->base.source_node);
result->value->type = ptr_frame_type;
return result;
}
static IrInstruction *ir_analyze_instruction_frame_type(IrAnalyze *ira, IrInstructionFrameType *instruction) {
ZigFn *fn = ir_resolve_fn(ira, instruction->fn->child);
if (fn == nullptr)
return ira->codegen->invalid_instruction;
if (fn->type_entry->data.fn.is_generic) {
ir_add_error(ira, &instruction->base,
buf_sprintf("@Frame() of generic function"));
return ira->codegen->invalid_instruction;
}
ZigType *ty = get_fn_frame_type(ira->codegen, fn);
return ir_const_type(ira, &instruction->base, ty);
}
static IrInstruction *ir_analyze_instruction_frame_size(IrAnalyze *ira, IrInstructionFrameSizeSrc *instruction) {
IrInstruction *fn = instruction->fn->child;
if (type_is_invalid(fn->value->type))
return ira->codegen->invalid_instruction;
if (fn->value->type->id != ZigTypeIdFn) {
ir_add_error(ira, fn,
buf_sprintf("expected function, found '%s'", buf_ptr(&fn->value->type->name)));
return ira->codegen->invalid_instruction;
}
ira->codegen->need_frame_size_prefix_data = true;
IrInstruction *result = ir_build_frame_size_gen(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, fn);
result->value->type = ira->codegen->builtin_types.entry_usize;
return result;
}
static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) {
// Here we create a lazy value in order to avoid resolving the alignment of the type
// immediately. This avoids false positive dependency loops such as:
// const Node = struct {
// field: []align(@alignOf(Node)) Node,
// };
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_num_lit_int);
result->value->special = ConstValSpecialLazy;
LazyValueAlignOf *lazy_align_of = allocate<LazyValueAlignOf>(1, "LazyValueAlignOf");
lazy_align_of->ira = ira; ira_ref(ira);
result->value->data.x_lazy = &lazy_align_of->base;
lazy_align_of->base.id = LazyValueIdAlignOf;
lazy_align_of->target_type = instruction->type_value->child;
if (ir_resolve_type_lazy(ira, lazy_align_of->target_type) == nullptr)
return ira->codegen->invalid_instruction;
return result;
}
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 (instr_is_comptime(casted_op1) &&
instr_is_comptime(casted_op2) &&
instr_is_comptime(casted_result_ptr))
{
ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad);
if (op1_val == nullptr)
return ira->codegen->invalid_instruction;
ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);
if (op2_val == nullptr)
return ira->codegen->invalid_instruction;
ZigValue *result_val = ir_resolve_const(ira, casted_result_ptr, UndefBad);
if (result_val == nullptr)
return ira->codegen->invalid_instruction;
BigInt *op1_bigint = &op1_val->data.x_bigint;
BigInt *op2_bigint = &op2_val->data.x_bigint;
ZigValue *pointee_val = const_ptr_pointee(ira, ira->codegen, result_val,
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 void ir_eval_mul_add(IrAnalyze *ira, IrInstructionMulAdd *source_instr, ZigType *float_type,
ZigValue *op1, ZigValue *op2, ZigValue *op3, ZigValue *out_val) {
if (float_type->id == ZigTypeIdComptimeFloat) {
f128M_mulAdd(&out_val->data.x_bigfloat.value, &op1->data.x_bigfloat.value, &op2->data.x_bigfloat.value,
&op3->data.x_bigfloat.value);
} else if (float_type->id == ZigTypeIdFloat) {
switch (float_type->data.floating.bit_count) {
case 16:
out_val->data.x_f16 = f16_mulAdd(op1->data.x_f16, op2->data.x_f16, op3->data.x_f16);
break;
case 32:
out_val->data.x_f32 = fmaf(op1->data.x_f32, op2->data.x_f32, op3->data.x_f32);
break;
case 64:
out_val->data.x_f64 = fma(op1->data.x_f64, op2->data.x_f64, op3->data.x_f64);
break;
case 128:
f128M_mulAdd(&op1->data.x_f128, &op2->data.x_f128, &op3->data.x_f128, &out_val->data.x_f128);
break;
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static IrInstruction *ir_analyze_instruction_mul_add(IrAnalyze *ira, IrInstructionMulAdd *instruction) {
IrInstruction *type_value = instruction->type_value->child;
if (type_is_invalid(type_value->value->type))
return ira->codegen->invalid_instruction;
ZigType *expr_type = ir_resolve_type(ira, type_value);
if (type_is_invalid(expr_type))
return ira->codegen->invalid_instruction;
// Only allow float types, and vectors of floats.
ZigType *float_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type;
if (float_type->id != ZigTypeIdFloat) {
ir_add_error(ira, type_value,
buf_sprintf("expected float or vector of float type, found '%s'", buf_ptr(&float_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, expr_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 = ir_implicit_cast(ira, op2, expr_type);
if (type_is_invalid(casted_op2->value->type))
return ira->codegen->invalid_instruction;
IrInstruction *op3 = instruction->op3->child;
if (type_is_invalid(op3->value->type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_op3 = ir_implicit_cast(ira, op3, expr_type);
if (type_is_invalid(casted_op3->value->type))
return ira->codegen->invalid_instruction;
if (instr_is_comptime(casted_op1) &&
instr_is_comptime(casted_op2) &&
instr_is_comptime(casted_op3)) {
ZigValue *op1_const = ir_resolve_const(ira, casted_op1, UndefBad);
if (!op1_const)
return ira->codegen->invalid_instruction;
ZigValue *op2_const = ir_resolve_const(ira, casted_op2, UndefBad);
if (!op2_const)
return ira->codegen->invalid_instruction;
ZigValue *op3_const = ir_resolve_const(ira, casted_op3, UndefBad);
if (!op3_const)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_const(ira, &instruction->base, expr_type);
ZigValue *out_val = result->value;
if (expr_type->id == ZigTypeIdVector) {
expand_undef_array(ira->codegen, op1_const);
expand_undef_array(ira->codegen, op2_const);
expand_undef_array(ira->codegen, op3_const);
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) {
ZigValue *float_operand_op1 = &op1_const->data.x_array.data.s_none.elements[i];
ZigValue *float_operand_op2 = &op2_const->data.x_array.data.s_none.elements[i];
ZigValue *float_operand_op3 = &op3_const->data.x_array.data.s_none.elements[i];
ZigValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i];
assert(float_operand_op1->type == float_type);
assert(float_operand_op2->type == float_type);
assert(float_operand_op3->type == float_type);
assert(float_out_val->type == float_type);
ir_eval_mul_add(ira, instruction, float_type,
op1_const, op2_const, op3_const, float_out_val);
float_out_val->type = float_type;
}
out_val->type = expr_type;
out_val->special = ConstValSpecialStatic;
} else {
ir_eval_mul_add(ira, instruction, float_type, op1_const, op2_const, op3_const, out_val);
}
return result;
}
IrInstruction *result = ir_build_mul_add(&ira->new_irb,
instruction->base.scope, instruction->base.source_node,
type_value, casted_op1, casted_op2, casted_op3);
result->value->type = expr_type;
return result;
}
static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstructionTestErrSrc *instruction) {
IrInstruction *base_ptr = instruction->base_ptr->child;
if (type_is_invalid(base_ptr->value->type))
return ira->codegen->invalid_instruction;
IrInstruction *value;
if (instruction->base_ptr_is_payload) {
value = base_ptr;
} else {
value = ir_get_deref(ira, &instruction->base, base_ptr, nullptr);
}
ZigType *type_entry = value->value->type;
if (type_is_invalid(type_entry))
return ira->codegen->invalid_instruction;
if (type_entry->id == ZigTypeIdErrorUnion) {
if (instr_is_comptime(value)) {
ZigValue *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));
}
}
if (instruction->resolve_err_set) {
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.incomplete);
return ir_const_bool(ira, &instruction->base, false);
}
}
return ir_build_test_err_gen(ira, &instruction->base, value);
} 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_unwrap_err_code(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *base_ptr, bool initializing)
{
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;
}
ZigType *err_set_type = type_entry->data.error_union.err_set_type;
ZigType *result_type = get_pointer_to_type_extra(ira->codegen, err_set_type,
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle,
ptr_type->data.pointer.explicit_alignment, 0, 0, false);
if (instr_is_comptime(base_ptr)) {
ZigValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad);
if (!ptr_val)
return ira->codegen->invalid_instruction;
if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar &&
ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr)
{
ZigValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node);
if (err_union_val == nullptr)
return ira->codegen->invalid_instruction;
if (initializing && err_union_val->special == ConstValSpecialUndef) {
ZigValue *vals = create_const_vals(2);
ZigValue *err_set_val = &vals[0];
ZigValue *payload_val = &vals[1];
err_set_val->special = ConstValSpecialUndef;
err_set_val->type = err_set_type;
err_set_val->parent.id = ConstParentIdErrUnionCode;
err_set_val->parent.data.p_err_union_code.err_union_val = err_union_val;
payload_val->special = ConstValSpecialUndef;
payload_val->type = type_entry->data.error_union.payload_type;
payload_val->parent.id = ConstParentIdErrUnionPayload;
payload_val->parent.data.p_err_union_payload.err_union_val = err_union_val;
err_union_val->special = ConstValSpecialStatic;
err_union_val->data.x_err_union.error_set = err_set_val;
err_union_val->data.x_err_union.payload = payload_val;
}
ir_assert(err_union_val->special != ConstValSpecialRuntime, source_instr);
IrInstruction *result;
if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) {
result = ir_build_unwrap_err_code(&ira->new_irb, source_instr->scope,
source_instr->source_node, base_ptr);
result->value->type = result_type;
result->value->special = ConstValSpecialStatic;
} else {
result = ir_const(ira, source_instr, result_type);
}
ZigValue *const_val = result->value;
const_val->data.x_ptr.special = ConstPtrSpecialBaseErrorUnionCode;
const_val->data.x_ptr.data.base_err_union_code.err_union_val = err_union_val;
const_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut;
return result;
}
}
IrInstruction *result = ir_build_unwrap_err_code(&ira->new_irb,
source_instr->scope, source_instr->source_node, base_ptr);
result->value->type = result_type;
return result;
}
static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira,
IrInstructionUnwrapErrCode *instruction)
{
IrInstruction *base_ptr = instruction->err_union_ptr->child;
if (type_is_invalid(base_ptr->value->type))
return ira->codegen->invalid_instruction;
return ir_analyze_unwrap_err_code(ira, &instruction->base, base_ptr, false);
}
static IrInstruction *ir_analyze_unwrap_error_payload(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *base_ptr, bool safety_check_on, bool initializing)
{
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;
}
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(base_ptr)) {
ZigValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad);
if (!ptr_val)
return ira->codegen->invalid_instruction;
if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) {
ZigValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node);
if (err_union_val == nullptr)
return ira->codegen->invalid_instruction;
if (initializing && err_union_val->special == ConstValSpecialUndef) {
ZigValue *vals = create_const_vals(2);
ZigValue *err_set_val = &vals[0];
ZigValue *payload_val = &vals[1];
err_set_val->special = ConstValSpecialStatic;
err_set_val->type = type_entry->data.error_union.err_set_type;
err_set_val->data.x_err_set = nullptr;
payload_val->special = ConstValSpecialUndef;
payload_val->type = payload_type;
err_union_val->special = ConstValSpecialStatic;
err_union_val->data.x_err_union.error_set = err_set_val;
err_union_val->data.x_err_union.payload = payload_val;
}
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, source_instr,
buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *result;
if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) {
result = ir_build_unwrap_err_payload(&ira->new_irb, source_instr->scope,
source_instr->source_node, base_ptr, safety_check_on, initializing);
result->value->type = result_type;
result->value->special = ConstValSpecialStatic;
} else {
result = ir_const(ira, source_instr, 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;
result->value->data.x_ptr.mut = ptr_val->data.x_ptr.mut;
return result;
}
}
}
IrInstruction *result = ir_build_unwrap_err_payload(&ira->new_irb, source_instr->scope,
source_instr->source_node, base_ptr, safety_check_on, initializing);
result->value->type = result_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;
return ir_analyze_unwrap_error_payload(ira, &instruction->base, value, instruction->safety_check_on, false);
}
static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnProto *instruction) {
AstNode *proto_node = instruction->base.source_node;
assert(proto_node->type == NodeTypeFnProto);
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type);
result->value->special = ConstValSpecialLazy;
LazyValueFnType *lazy_fn_type = allocate<LazyValueFnType>(1, "LazyValueFnType");
lazy_fn_type->ira = ira; ira_ref(ira);
result->value->data.x_lazy = &lazy_fn_type->base;
lazy_fn_type->base.id = LazyValueIdFnType;
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;
}
lazy_fn_type->cc = cc_from_fn_proto(&proto_node->data.fn_proto);
if (instruction->callconv_value != nullptr) {
ZigType *cc_enum_type = get_builtin_type(ira->codegen, "CallingConvention");
IrInstruction *casted_value = ir_implicit_cast(ira, instruction->callconv_value, cc_enum_type);
if (type_is_invalid(casted_value->value->type))
return ira->codegen->invalid_instruction;
ZigValue *const_value = ir_resolve_const(ira, casted_value, UndefBad);
if (const_value == nullptr)
return ira->codegen->invalid_instruction;
lazy_fn_type->cc = (CallingConvention)bigint_as_u32(&const_value->data.x_enum_tag);
}
size_t param_count = proto_node->data.fn_proto.params.length;
lazy_fn_type->proto_node = proto_node;
lazy_fn_type->param_types = allocate<IrInstruction *>(param_count);
for (size_t param_index = 0; param_index < param_count; param_index += 1) {
AstNode *param_node = proto_node->data.fn_proto.params.at(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) {
const CallingConvention cc = lazy_fn_type->cc;
if (cc == CallingConventionC) {
break;
} else if (cc == CallingConventionUnspecified) {
lazy_fn_type->is_generic = true;
return result;
} else {
zig_unreachable();
}
}
if (instruction->param_types[param_index] == nullptr) {
lazy_fn_type->is_generic = true;
return result;
}
IrInstruction *param_type_value = instruction->param_types[param_index]->child;
if (type_is_invalid(param_type_value->value->type))
return ira->codegen->invalid_instruction;
if (ir_resolve_const(ira, param_type_value, LazyOk) == nullptr)
return ira->codegen->invalid_instruction;
lazy_fn_type->param_types[param_index] = param_type_value;
}
if (instruction->align_value != nullptr) {
lazy_fn_type->align_inst = instruction->align_value->child;
if (ir_resolve_const(ira, lazy_fn_type->align_inst, LazyOk) == nullptr)
return ira->codegen->invalid_instruction;
}
lazy_fn_type->return_type = instruction->return_type->child;
if (ir_resolve_const(ira, lazy_fn_type->return_type, LazyOk) == nullptr)
return ira->codegen->invalid_instruction;
return result;
}
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;
assert(start_value->value->type->id == ZigTypeIdEnum);
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);
if (bigint_cmp(&start_index, &end_index) == CmpGT) {
ir_add_error(ira, start_value,
buf_sprintf("range start value is greater than the end value"));
}
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) {
if (switch_type->data.enumeration.layout == ContainerLayoutExtern) {
ir_add_error(ira, &instruction->base,
buf_sprintf("switch on an extern enum must have an 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;
}
size_t field_prev_uses_count = ira->codegen->errors_by_index.length;
AstNode **field_prev_uses = allocate<AstNode *>(field_prev_uses_count, "AstNode *");
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;
ir_assert(start_value->value->type->id == ZigTypeIdErrorSet, &instruction->base);
uint32_t start_index = start_value->value->data.x_err_set->value;
ir_assert(end_value->value->type->id == ZigTypeIdErrorSet, &instruction->base);
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)));
}
}
}
}
deallocate(field_prev_uses, field_prev_uses_count, "AstNode *");
} 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;
ZigValue *start_val = ir_resolve_const(ira, casted_start_value, UndefBad);
if (!start_val)
return ira->codegen->invalid_instruction;
ZigValue *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);
if (bigint_cmp(&start_val->data.x_bigint, &end_val->data.x_bigint) == CmpGT) {
ir_add_error(ira, start_value,
buf_sprintf("range start value is greater than the end value"));
}
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;
ZigValue *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 && statement_type->id != ZigTypeIdUnreachable) {
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)) {
ZigValue *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);
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));
if (src_type == dest_type) {
return ptr;
}
// 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 ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
if ((err = type_resolve(ira->codegen, src_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
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;
}
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;
ZigValue *val = ir_resolve_const(ira, ptr, is_undef_allowed);
if (!val)
return ira->codegen->invalid_instruction;
if (value_is_comptime(val) && val->special != ConstValSpecialUndef) {
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;
if (ptr->value->data.x_ptr.mut == ConstPtrMutInfer) {
result = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr, safety_check_on);
if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
} else {
result = ir_const(ira, source_instr, dest_type);
}
copy_const_val(result->value, val);
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);
// 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, ZigValue *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) {
ZigValue *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, ZigValue *val) {
if (val->special == ConstValSpecialUndef) {
expand_undef_struct(codegen, val);
val->special = ConstValSpecialStatic;
}
assert(val->special == ConstValSpecialStatic);
switch (val->type->id) {
case ZigTypeIdInvalid:
case ZigTypeIdMetaType:
case ZigTypeIdOpaque:
case ZigTypeIdBoundFn:
case ZigTypeIdUnreachable:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
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;
ZigValue *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 ZigTypeIdFn:
zig_panic("TODO buf_write_value_bytes fn type");
case ZigTypeIdUnion:
zig_panic("TODO buf_write_value_bytes union type");
case ZigTypeIdFnFrame:
zig_panic("TODO buf_write_value_bytes async fn frame type");
case ZigTypeIdAnyFrame:
zig_panic("TODO buf_write_value_bytes anyframe type");
}
zig_unreachable();
}
static Error buf_read_value_bytes_array(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf,
ZigValue *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++) {
ZigValue *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, ZigValue *val) {
Error err;
src_assert(val->special == ConstValSpecialStatic, source_node);
switch (val->type->id) {
case ZigTypeIdInvalid:
case ZigTypeIdMetaType:
case ZigTypeIdOpaque:
case ZigTypeIdBoundFn:
case ZigTypeIdUnreachable:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
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_usize(&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;
src_assert(tag_int_type->id == ZigTypeIdInt, source_node);
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 = alloc_const_vals_ptrs(src_field_count);
for (size_t field_i = 0; field_i < src_field_count; field_i += 1) {
ZigValue *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 = alloc_const_vals_ptrs(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];
src_assert(field->gen_index != SIZE_MAX, source_node);
if (field->gen_index != gen_i)
break;
ZigValue *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");
case ZigTypeIdFnFrame:
zig_panic("TODO buf_read_value_bytes async fn frame type");
case ZigTypeIdAnyFrame:
zig_panic("TODO buf_read_value_bytes anyframe type");
}
zig_unreachable();
}
static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
ZigType *dest_type)
{
Error err;
ZigType *src_type = value->value->type;
ir_assert(get_codegen_ptr_type(src_type) == nullptr, source_instr);
ir_assert(type_can_bit_cast(src_type), source_instr);
ir_assert(get_codegen_ptr_type(dest_type) == nullptr, source_instr);
ir_assert(type_can_bit_cast(dest_type), source_instr);
if (dest_type->id == ZigTypeIdEnum) {
ErrorMsg *msg = ir_add_error_node(ira, source_instr->source_node,
buf_sprintf("cannot cast a value of type '%s'", buf_ptr(&dest_type->name)));
add_error_note(ira->codegen, msg, source_instr->source_node,
buf_sprintf("use @intToEnum for type coercion"));
return ira->codegen->invalid_instruction;
}
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)) {
ZigValue *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;
}
return ir_build_bit_cast_gen(ira, source_instr, value, dest_type);
}
static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
ZigType *ptr_type)
{
Error err;
ir_assert(get_src_ptr_type(ptr_type) != nullptr, source_instr);
ir_assert(type_has_bits(ptr_type), source_instr);
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)) {
ZigValue *val = ir_resolve_const(ira, casted_int, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
uint64_t addr = bigint_as_u64(&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;
}
uint32_t align_bytes;
if ((err = resolve_ptr_align(ira, ptr_type, &align_bytes)))
return ira->codegen->invalid_instruction;
if (addr != 0 && addr % align_bytes != 0) {
ir_add_error(ira, source_instr,
buf_sprintf("pointer type '%s' requires aligned address",
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;
}
bool has_bits;
if ((err = type_has_bits2(ira->codegen, dest_type, &has_bits)))
return ira->codegen->invalid_instruction;
if (!has_bits) {
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)
{
IrInstruction *ref_instruction = ir_analyze_decl_ref(ira, &instruction->base, instruction->tld);
if (type_is_invalid(ref_instruction->value->type)) {
return ira->codegen->invalid_instruction;
}
if (instruction->lval == LValPtr) {
return ref_instruction;
} else {
return ir_get_deref(ira, &instruction->base, ref_instruction, nullptr);
}
}
static IrInstruction *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstructionPtrToInt *instruction) {
Error err;
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;
}
bool has_bits;
if ((err = type_has_bits2(ira->codegen, target->value->type, &has_bits)))
return ira->codegen->invalid_instruction;
if (!has_bits) {
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)) {
ZigValue *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) {
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type);
result->value->special = ConstValSpecialLazy;
LazyValuePtrType *lazy_ptr_type = allocate<LazyValuePtrType>(1, "LazyValuePtrType");
lazy_ptr_type->ira = ira; ira_ref(ira);
result->value->data.x_lazy = &lazy_ptr_type->base;
lazy_ptr_type->base.id = LazyValueIdPtrType;
if (instruction->sentinel != nullptr) {
lazy_ptr_type->sentinel = instruction->sentinel->child;
if (ir_resolve_const(ira, lazy_ptr_type->sentinel, LazyOk) == nullptr)
return ira->codegen->invalid_instruction;
}
lazy_ptr_type->elem_type = instruction->child_type->child;
if (ir_resolve_type_lazy(ira, lazy_ptr_type->elem_type) == nullptr)
return ira->codegen->invalid_instruction;
if (instruction->align_value != nullptr) {
lazy_ptr_type->align_inst = instruction->align_value->child;
if (ir_resolve_const(ira, lazy_ptr_type->align_inst, LazyOk) == nullptr)
return ira->codegen->invalid_instruction;
}
lazy_ptr_type->ptr_len = instruction->ptr_len;
lazy_ptr_type->is_const = instruction->is_const;
lazy_ptr_type->is_volatile = instruction->is_volatile;
lazy_ptr_type->is_allowzero = instruction->is_allow_zero;
lazy_ptr_type->bit_offset_in_host = instruction->bit_offset_start;
lazy_ptr_type->host_int_bytes = instruction->host_int_bytes;
return result;
}
static IrInstruction *ir_analyze_instruction_align_cast(IrAnalyze *ira, IrInstructionAlignCast *instruction) {
IrInstruction *target = instruction->target->child;
if (type_is_invalid(target->value->type))
return ira->codegen->invalid_instruction;
ZigType *elem_type = nullptr;
if (is_slice(target->value->type)) {
ZigType *slice_ptr_type = target->value->type->data.structure.fields[slice_ptr_index]->type_entry;
elem_type = slice_ptr_type->data.pointer.child_type;
} else if (target->value->type->id == ZigTypeIdPointer) {
elem_type = target->value->type->data.pointer.child_type;
}
uint32_t align_bytes;
IrInstruction *align_bytes_inst = instruction->align_bytes->child;
if (!ir_resolve_align(ira, align_bytes_inst, elem_type, &align_bytes))
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, nullptr, &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 == ZigTypeIdBoundFn) {
fn_type = fn_type->data.bound_fn.fn_type;
arg_index += 1;
}
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) {
if (instruction->allow_var) {
// TODO remove this with var args
return ir_const_type(ira, &instruction->base, ira->codegen->builtin_types.entry_var);
}
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.
ir_assert(fn_type->data.fn.is_generic, &instruction->base);
if (instruction->allow_var) {
return ir_const_type(ira, &instruction->base, ira->codegen->builtin_types.entry_var);
} else {
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 = type_resolve(ira->codegen, enum_type, ResolveStatusSizeKnown)))
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 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;
}
uint32_t max_atomic_bits = target_arch_largest_atomic_bits(ira->codegen->zig_target->arch);
if (operand_type->data.integral.bit_count > max_atomic_bits) {
ir_add_error(ira, op,
buf_sprintf("expected %" PRIu32 "-bit integer type or smaller, found %" PRIu32 "-bit integer type",
max_atomic_bits, 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 (operand_type->id == ZigTypeIdEnum) {
ZigType *int_type = operand_type->data.enumeration.tag_int_type;
if (int_type->data.integral.bit_count < 8) {
ir_add_error(ira, op,
buf_sprintf("expected enum tag type 8 bits or larger, found %" PRIu32 "-bit tag type",
int_type->data.integral.bit_count));
return ira->codegen->builtin_types.entry_invalid;
}
uint32_t max_atomic_bits = target_arch_largest_atomic_bits(ira->codegen->zig_target->arch);
if (int_type->data.integral.bit_count > max_atomic_bits) {
ir_add_error(ira, op,
buf_sprintf("expected %" PRIu32 "-bit enum tag type or smaller, found %" PRIu32 "-bit tag type",
max_atomic_bits, int_type->data.integral.bit_count));
return ira->codegen->builtin_types.entry_invalid;
}
if (!is_power_of_2(int_type->data.integral.bit_count)) {
ir_add_error(ira, op,
buf_sprintf("%" PRIu32 "-bit enum tag type is not a power of 2", int_type->data.integral.bit_count));
return ira->codegen->builtin_types.entry_invalid;
}
} else if (operand_type->id == ZigTypeIdFloat) {
uint32_t max_atomic_bits = target_arch_largest_atomic_bits(ira->codegen->zig_target->arch);
if (operand_type->data.floating.bit_count > max_atomic_bits) {
ir_add_error(ira, op,
buf_sprintf("expected %" PRIu32 "-bit float or smaller, found %" PRIu32 "-bit float",
max_atomic_bits, (uint32_t) operand_type->data.floating.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, float, enum 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;
}
}
if (operand_type->id == ZigTypeIdEnum && op != AtomicRmwOp_xchg) {
ir_add_error(ira, instruction->op,
buf_sprintf("@atomicRmw on enum only works with .Xchg"));
return ira->codegen->invalid_instruction;
} else if (operand_type->id == ZigTypeIdFloat && op > AtomicRmwOp_sub) {
ir_add_error(ira, instruction->op,
buf_sprintf("@atomicRmw with float only works with .Xchg, .Add and .Sub"));
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) {
ir_assert(instruction->ordering != nullptr, &instruction->base);
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, nullptr);
ir_assert(result->value->type != nullptr, &instruction->base);
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_atomic_store(IrAnalyze *ira, IrInstructionAtomicStore *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, 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;
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, operand_type);
if (type_is_invalid(casted_value->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 == AtomicOrderAcquire || ordering == AtomicOrderAcqRel) {
ir_assert(instruction->ordering != nullptr, &instruction->base);
ir_add_error(ira, instruction->ordering,
buf_sprintf("@atomicStore atomic ordering must not be Acquire or AcqRel"));
return ira->codegen->invalid_instruction;
}
if (instr_is_comptime(casted_value) && instr_is_comptime(casted_ptr)) {
IrInstruction *result = ir_analyze_store_ptr(ira, &instruction->base, casted_ptr, value, false);
result->value->type = ira->codegen->builtin_types.entry_void;
return result;
}
IrInstruction *result = ir_build_atomic_store(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, nullptr, casted_ptr, casted_value, nullptr, ordering);
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 ErrorMsg *ir_eval_float_op(IrAnalyze *ira, IrInstruction *source_instr, BuiltinFnId fop, ZigType *float_type,
ZigValue *op, ZigValue *out_val)
{
assert(ira && source_instr && float_type && out_val && op);
assert(float_type->id == ZigTypeIdFloat ||
float_type->id == ZigTypeIdComptimeFloat);
unsigned bits;
switch (float_type->id) {
case ZigTypeIdComptimeFloat:
bits = 128;
break;
case ZigTypeIdFloat:
bits = float_type->data.floating.bit_count;
break;
default:
zig_unreachable();
}
switch (bits) {
case 16: {
switch (fop) {
case BuiltinFnIdSqrt:
out_val->data.x_f16 = f16_sqrt(op->data.x_f16);
break;
case BuiltinFnIdSin:
out_val->data.x_f16 = zig_double_to_f16(sin(zig_f16_to_double(op->data.x_f16)));
break;
case BuiltinFnIdCos:
out_val->data.x_f16 = zig_double_to_f16(cos(zig_f16_to_double(op->data.x_f16)));
break;
case BuiltinFnIdExp:
out_val->data.x_f16 = zig_double_to_f16(exp(zig_f16_to_double(op->data.x_f16)));
break;
case BuiltinFnIdExp2:
out_val->data.x_f16 = zig_double_to_f16(exp2(zig_f16_to_double(op->data.x_f16)));
break;
case BuiltinFnIdLog:
out_val->data.x_f16 = zig_double_to_f16(log(zig_f16_to_double(op->data.x_f16)));
break;
case BuiltinFnIdLog10:
out_val->data.x_f16 = zig_double_to_f16(log10(zig_f16_to_double(op->data.x_f16)));
break;
case BuiltinFnIdLog2:
out_val->data.x_f16 = zig_double_to_f16(log2(zig_f16_to_double(op->data.x_f16)));
break;
case BuiltinFnIdFabs:
out_val->data.x_f16 = zig_double_to_f16(fabs(zig_f16_to_double(op->data.x_f16)));
break;
case BuiltinFnIdFloor:
out_val->data.x_f16 = zig_double_to_f16(floor(zig_f16_to_double(op->data.x_f16)));
break;
case BuiltinFnIdCeil:
out_val->data.x_f16 = zig_double_to_f16(ceil(zig_f16_to_double(op->data.x_f16)));
break;
case BuiltinFnIdTrunc:
out_val->data.x_f16 = zig_double_to_f16(trunc(zig_f16_to_double(op->data.x_f16)));
break;
case BuiltinFnIdNearbyInt:
out_val->data.x_f16 = zig_double_to_f16(nearbyint(zig_f16_to_double(op->data.x_f16)));
break;
case BuiltinFnIdRound:
out_val->data.x_f16 = zig_double_to_f16(round(zig_f16_to_double(op->data.x_f16)));
break;
default:
zig_unreachable();
};
break;
}
case 32: {
switch (fop) {
case BuiltinFnIdSqrt:
out_val->data.x_f32 = sqrtf(op->data.x_f32);
break;
case BuiltinFnIdSin:
out_val->data.x_f32 = sinf(op->data.x_f32);
break;
case BuiltinFnIdCos:
out_val->data.x_f32 = cosf(op->data.x_f32);
break;
case BuiltinFnIdExp:
out_val->data.x_f32 = expf(op->data.x_f32);
break;
case BuiltinFnIdExp2:
out_val->data.x_f32 = exp2f(op->data.x_f32);
break;
case BuiltinFnIdLog:
out_val->data.x_f32 = logf(op->data.x_f32);
break;
case BuiltinFnIdLog10:
out_val->data.x_f32 = log10f(op->data.x_f32);
break;
case BuiltinFnIdLog2:
out_val->data.x_f32 = log2f(op->data.x_f32);
break;
case BuiltinFnIdFabs:
out_val->data.x_f32 = fabsf(op->data.x_f32);
break;
case BuiltinFnIdFloor:
out_val->data.x_f32 = floorf(op->data.x_f32);
break;
case BuiltinFnIdCeil:
out_val->data.x_f32 = ceilf(op->data.x_f32);
break;
case BuiltinFnIdTrunc:
out_val->data.x_f32 = truncf(op->data.x_f32);
break;
case BuiltinFnIdNearbyInt:
out_val->data.x_f32 = nearbyintf(op->data.x_f32);
break;
case BuiltinFnIdRound:
out_val->data.x_f32 = roundf(op->data.x_f32);
break;
default:
zig_unreachable();
};
break;
}
case 64: {
switch (fop) {
case BuiltinFnIdSqrt:
out_val->data.x_f64 = sqrt(op->data.x_f64);
break;
case BuiltinFnIdSin:
out_val->data.x_f64 = sin(op->data.x_f64);
break;
case BuiltinFnIdCos:
out_val->data.x_f64 = cos(op->data.x_f64);
break;
case BuiltinFnIdExp:
out_val->data.x_f64 = exp(op->data.x_f64);
break;
case BuiltinFnIdExp2:
out_val->data.x_f64 = exp2(op->data.x_f64);
break;
case BuiltinFnIdLog:
out_val->data.x_f64 = log(op->data.x_f64);
break;
case BuiltinFnIdLog10:
out_val->data.x_f64 = log10(op->data.x_f64);
break;
case BuiltinFnIdLog2:
out_val->data.x_f64 = log2(op->data.x_f64);
break;
case BuiltinFnIdFabs:
out_val->data.x_f64 = fabs(op->data.x_f64);
break;
case BuiltinFnIdFloor:
out_val->data.x_f64 = floor(op->data.x_f64);
break;
case BuiltinFnIdCeil:
out_val->data.x_f64 = ceil(op->data.x_f64);
break;
case BuiltinFnIdTrunc:
out_val->data.x_f64 = trunc(op->data.x_f64);
break;
case BuiltinFnIdNearbyInt:
out_val->data.x_f64 = nearbyint(op->data.x_f64);
break;
case BuiltinFnIdRound:
out_val->data.x_f64 = round(op->data.x_f64);
break;
default:
zig_unreachable();
}
break;
}
case 80:
return ir_add_error(ira, source_instr,
buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026",
float_op_to_name(fop), buf_ptr(&float_type->name)));
case 128: {
float128_t *out, *in;
if (float_type->id == ZigTypeIdComptimeFloat) {
out = &out_val->data.x_bigfloat.value;
in = &op->data.x_bigfloat.value;
} else {
out = &out_val->data.x_f128;
in = &op->data.x_f128;
}
switch (fop) {
case BuiltinFnIdSqrt:
f128M_sqrt(in, out);
break;
case BuiltinFnIdNearbyInt:
case BuiltinFnIdSin:
case BuiltinFnIdCos:
case BuiltinFnIdExp:
case BuiltinFnIdExp2:
case BuiltinFnIdLog:
case BuiltinFnIdLog10:
case BuiltinFnIdLog2:
case BuiltinFnIdFabs:
case BuiltinFnIdFloor:
case BuiltinFnIdCeil:
case BuiltinFnIdTrunc:
case BuiltinFnIdRound:
return ir_add_error(ira, source_instr,
buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026",
float_op_to_name(fop), buf_ptr(&float_type->name)));
default:
zig_unreachable();
}
break;
}
default:
zig_unreachable();
}
out_val->special = ConstValSpecialStatic;
return nullptr;
}
static IrInstruction *ir_analyze_instruction_float_op(IrAnalyze *ira, IrInstructionFloatOp *instruction) {
IrInstruction *operand = instruction->operand->child;
ZigType *operand_type = operand->value->type;
if (type_is_invalid(operand_type))
return ira->codegen->invalid_instruction;
// This instruction accepts floats and vectors of floats.
ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ?
operand_type->data.vector.elem_type : operand_type;
if (scalar_type->id != ZigTypeIdFloat && scalar_type->id != ZigTypeIdComptimeFloat) {
ir_add_error(ira, operand,
buf_sprintf("expected float type, found '%s'", buf_ptr(&scalar_type->name)));
return ira->codegen->invalid_instruction;
}
if (instr_is_comptime(operand)) {
ZigValue *operand_val = ir_resolve_const(ira, operand, UndefOk);
if (operand_val == nullptr)
return ira->codegen->invalid_instruction;
if (operand_val->special == ConstValSpecialUndef)
return ir_const_undef(ira, &instruction->base, operand_type);
IrInstruction *result = ir_const(ira, &instruction->base, operand_type);
ZigValue *out_val = result->value;
if (operand_type->id == ZigTypeIdVector) {
expand_undef_array(ira->codegen, operand_val);
out_val->special = ConstValSpecialUndef;
expand_undef_array(ira->codegen, out_val);
size_t len = operand_type->data.vector.len;
for (size_t i = 0; i < len; i += 1) {
ZigValue *elem_operand = &operand_val->data.x_array.data.s_none.elements[i];
ZigValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i];
ir_assert(elem_operand->type == scalar_type, &instruction->base);
ir_assert(float_out_val->type == scalar_type, &instruction->base);
ErrorMsg *msg = ir_eval_float_op(ira, &instruction->base, instruction->fn_id, scalar_type,
elem_operand, float_out_val);
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;
}
float_out_val->type = scalar_type;
}
out_val->type = operand_type;
out_val->special = ConstValSpecialStatic;
} else {
if (ir_eval_float_op(ira, &instruction->base, instruction->fn_id, scalar_type,
operand_val, out_val) != nullptr)
{
return ira->codegen->invalid_instruction;
}
}
return result;
}
ir_assert(scalar_type->id == ZigTypeIdFloat, &instruction->base);
IrInstruction *result = ir_build_float_op(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, operand, instruction->fn_id);
result->value->type = operand_type;
return result;
}
static IrInstruction *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstructionBswap *instruction) {
Error err;
ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);
if (type_is_invalid(int_type))
return ira->codegen->invalid_instruction;
IrInstruction *uncasted_op = instruction->op->child;
if (type_is_invalid(uncasted_op->value->type))
return ira->codegen->invalid_instruction;
uint32_t vector_len; // UINT32_MAX means not a vector
if (uncasted_op->value->type->id == ZigTypeIdArray &&
is_valid_vector_elem_type(uncasted_op->value->type->data.array.child_type))
{
vector_len = uncasted_op->value->type->data.array.len;
} else if (uncasted_op->value->type->id == ZigTypeIdVector) {
vector_len = uncasted_op->value->type->data.vector.len;
} else {
vector_len = UINT32_MAX;
}
bool is_vector = (vector_len != UINT32_MAX);
ZigType *op_type = is_vector ? get_vector_type(ira->codegen, vector_len, int_type) : int_type;
IrInstruction *op = ir_implicit_cast(ira, uncasted_op, op_type);
if (type_is_invalid(op->value->type))
return ira->codegen->invalid_instruction;
if (int_type->data.integral.bit_count == 8 || int_type->data.integral.bit_count == 0)
return op;
if (int_type->data.integral.bit_count % 8 != 0) {
ir_add_error(ira, instruction->op,
buf_sprintf("@byteSwap 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;
}
if (instr_is_comptime(op)) {
ZigValue *val = ir_resolve_const(ira, op, UndefOk);
if (val == nullptr)
return ira->codegen->invalid_instruction;
if (val->special == ConstValSpecialUndef)
return ir_const_undef(ira, &instruction->base, op_type);
IrInstruction *result = ir_const(ira, &instruction->base, op_type);
size_t buf_size = int_type->data.integral.bit_count / 8;
uint8_t *buf = allocate_nonzero<uint8_t>(buf_size);
if (is_vector) {
expand_undef_array(ira->codegen, val);
result->value->data.x_array.data.s_none.elements = create_const_vals(op_type->data.vector.len);
for (unsigned i = 0; i < op_type->data.vector.len; i += 1) {
ZigValue *op_elem_val = &val->data.x_array.data.s_none.elements[i];
if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, instruction->base.source_node,
op_elem_val, UndefOk)))
{
return ira->codegen->invalid_instruction;
}
ZigValue *result_elem_val = &result->value->data.x_array.data.s_none.elements[i];
result_elem_val->type = int_type;
result_elem_val->special = op_elem_val->special;
if (op_elem_val->special == ConstValSpecialUndef)
continue;
bigint_write_twos_complement(&op_elem_val->data.x_bigint, buf, int_type->data.integral.bit_count, true);
bigint_read_twos_complement(&result->value->data.x_array.data.s_none.elements[i].data.x_bigint,
buf, int_type->data.integral.bit_count, false,
int_type->data.integral.is_signed);
}
} else {
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);
}
free(buf);
return result;
}
IrInstruction *result = ir_build_bswap(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, nullptr, op);
result->value->type = op_type;
return result;
}
static IrInstruction *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstructionBitReverse *instruction) {
ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);
if (type_is_invalid(int_type))
return ira->codegen->invalid_instruction;
IrInstruction *op = ir_implicit_cast(ira, instruction->op->child, int_type);
if (type_is_invalid(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(op)) {
ZigValue *val = ir_resolve_const(ira, op, UndefOk);
if (val == nullptr)
return ira->codegen->invalid_instruction;
if (val->special == ConstValSpecialUndef)
return ir_const_undef(ira, &instruction->base, int_type);
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, 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_has_decl(IrAnalyze *ira, IrInstructionHasDecl *instruction) {
ZigType *container_type = ir_resolve_type(ira, instruction->container->child);
if (type_is_invalid(container_type))
return ira->codegen->invalid_instruction;
Buf *name = ir_resolve_str(ira, instruction->name->child);
if (name == nullptr)
return ira->codegen->invalid_instruction;
if (!is_container(container_type)) {
ir_add_error(ira, instruction->container,
buf_sprintf("expected struct, enum, or union; found '%s'", buf_ptr(&container_type->name)));
return ira->codegen->invalid_instruction;
}
ScopeDecls *container_scope = get_container_scope(container_type);
Tld *tld = find_container_decl(ira->codegen, container_scope, name);
if (tld == nullptr)
return ir_const_bool(ira, &instruction->base, false);
if (tld->visib_mod == VisibModPrivate && tld->import != get_scope_import(instruction->base.scope)) {
return ir_const_bool(ira, &instruction->base, false);
}
return ir_const_bool(ira, &instruction->base, true);
}
static IrInstruction *ir_analyze_instruction_undeclared_ident(IrAnalyze *ira, IrInstructionUndeclaredIdent *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(ira->codegen, instruction->base.scope, instruction->base.source_node,
instruction->name);
ir_add_error(ira, &instruction->base,
buf_sprintf("use of undeclared identifier '%s'", buf_ptr(instruction->name)));
return ira->codegen->invalid_instruction;
}
static IrInstruction *ir_analyze_instruction_end_expr(IrAnalyze *ira, IrInstructionEndExpr *instruction) {
IrInstruction *value = instruction->value->child;
if (type_is_invalid(value->value->type))
return ira->codegen->invalid_instruction;
bool was_written = instruction->result_loc->written;
IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc,
value->value->type, value, false, true);
if (result_loc != nullptr) {
if (type_is_invalid(result_loc->value->type))
return ira->codegen->invalid_instruction;
if (result_loc->value->type->id == ZigTypeIdUnreachable)
return result_loc;
if (!was_written || instruction->result_loc->id == ResultLocIdPeer) {
IrInstruction *store_ptr = ir_analyze_store_ptr(ira, &instruction->base, result_loc, value,
instruction->result_loc->allow_write_through_const);
if (type_is_invalid(store_ptr->value->type)) {
return ira->codegen->invalid_instruction;
}
}
if (result_loc->value->data.x_ptr.mut == ConstPtrMutInfer &&
instruction->result_loc->id != ResultLocIdPeer)
{
if (instr_is_comptime(value)) {
result_loc->value->data.x_ptr.mut = ConstPtrMutComptimeConst;
} else {
result_loc->value->special = ConstValSpecialRuntime;
}
}
}
return ir_const_void(ira, &instruction->base);
}
static IrInstruction *ir_analyze_instruction_implicit_cast(IrAnalyze *ira, IrInstructionImplicitCast *instruction) {
IrInstruction *operand = instruction->operand->child;
if (type_is_invalid(operand->value->type))
return operand;
ZigType *dest_type = ir_resolve_type(ira, instruction->result_loc_cast->base.source_instruction->child);
if (type_is_invalid(dest_type))
return ira->codegen->invalid_instruction;
return ir_implicit_cast2(ira, &instruction->base, operand, dest_type);
}
static IrInstruction *ir_analyze_instruction_bit_cast_src(IrAnalyze *ira, IrInstructionBitCastSrc *instruction) {
IrInstruction *operand = instruction->operand->child;
if (type_is_invalid(operand->value->type))
return operand;
IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base,
&instruction->result_loc_bit_cast->base, operand->value->type, operand, false, true);
if (result_loc != nullptr && (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc)))
return result_loc;
ZigType *dest_type = ir_resolve_type(ira,
instruction->result_loc_bit_cast->base.source_instruction->child);
if (type_is_invalid(dest_type))
return ira->codegen->invalid_instruction;
return ir_analyze_bit_cast(ira, &instruction->base, operand, dest_type);
}
static IrInstruction *ir_analyze_instruction_union_init_named_field(IrAnalyze *ira,
IrInstructionUnionInitNamedField *instruction)
{
ZigType *union_type = ir_resolve_type(ira, instruction->union_type->child);
if (type_is_invalid(union_type))
return ira->codegen->invalid_instruction;
if (union_type->id != ZigTypeIdUnion) {
ir_add_error(ira, instruction->union_type,
buf_sprintf("non-union type '%s' passed to @unionInit", buf_ptr(&union_type->name)));
return ira->codegen->invalid_instruction;
}
Buf *field_name = ir_resolve_str(ira, instruction->field_name->child);
if (field_name == nullptr)
return ira->codegen->invalid_instruction;
IrInstruction *field_result_loc = instruction->field_result_loc->child;
if (type_is_invalid(field_result_loc->value->type))
return ira->codegen->invalid_instruction;
IrInstruction *result_loc = instruction->result_loc->child;
if (type_is_invalid(result_loc->value->type))
return ira->codegen->invalid_instruction;
return ir_analyze_union_init(ira, &instruction->base, instruction->base.source_node,
union_type, field_name, field_result_loc, result_loc);
}
static IrInstruction *ir_analyze_instruction_suspend_begin(IrAnalyze *ira, IrInstructionSuspendBegin *instruction) {
IrInstructionSuspendBegin *result = ir_build_suspend_begin(&ira->new_irb, instruction->base.scope,
instruction->base.source_node);
return &result->base;
}
static IrInstruction *ir_analyze_instruction_suspend_finish(IrAnalyze *ira,
IrInstructionSuspendFinish *instruction)
{
IrInstruction *begin_base = instruction->begin->base.child;
if (type_is_invalid(begin_base->value->type))
return ira->codegen->invalid_instruction;
ir_assert(begin_base->id == IrInstructionIdSuspendBegin, &instruction->base);
IrInstructionSuspendBegin *begin = reinterpret_cast<IrInstructionSuspendBegin *>(begin_base);
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
ir_assert(fn_entry != nullptr, &instruction->base);
if (fn_entry->inferred_async_node == nullptr) {
fn_entry->inferred_async_node = instruction->base.source_node;
}
return ir_build_suspend_finish(&ira->new_irb, instruction->base.scope, instruction->base.source_node, begin);
}
static IrInstruction *analyze_frame_ptr_to_anyframe_T(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *frame_ptr, ZigFn **target_fn)
{
if (type_is_invalid(frame_ptr->value->type))
return ira->codegen->invalid_instruction;
*target_fn = nullptr;
ZigType *result_type;
IrInstruction *frame;
if (frame_ptr->value->type->id == ZigTypeIdPointer &&
frame_ptr->value->type->data.pointer.ptr_len == PtrLenSingle &&
frame_ptr->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame)
{
ZigFn *func = frame_ptr->value->type->data.pointer.child_type->data.frame.fn;
result_type = func->type_entry->data.fn.fn_type_id.return_type;
*target_fn = func;
frame = frame_ptr;
} else {
frame = ir_get_deref(ira, source_instr, frame_ptr, nullptr);
if (frame->value->type->id == ZigTypeIdPointer &&
frame->value->type->data.pointer.ptr_len == PtrLenSingle &&
frame->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame)
{
ZigFn *func = frame->value->type->data.pointer.child_type->data.frame.fn;
result_type = func->type_entry->data.fn.fn_type_id.return_type;
*target_fn = func;
} else if (frame->value->type->id != ZigTypeIdAnyFrame ||
frame->value->type->data.any_frame.result_type == nullptr)
{
ir_add_error(ira, source_instr,
buf_sprintf("expected anyframe->T, found '%s'", buf_ptr(&frame->value->type->name)));
return ira->codegen->invalid_instruction;
} else {
result_type = frame->value->type->data.any_frame.result_type;
}
}
ZigType *any_frame_type = get_any_frame_type(ira->codegen, result_type);
IrInstruction *casted_frame = ir_implicit_cast(ira, frame, any_frame_type);
if (type_is_invalid(casted_frame->value->type))
return ira->codegen->invalid_instruction;
return casted_frame;
}
static IrInstruction *ir_analyze_instruction_await(IrAnalyze *ira, IrInstructionAwaitSrc *instruction) {
IrInstruction *operand = instruction->frame->child;
if (type_is_invalid(operand->value->type))
return ira->codegen->invalid_instruction;
ZigFn *target_fn;
IrInstruction *frame = analyze_frame_ptr_to_anyframe_T(ira, &instruction->base, operand, &target_fn);
if (type_is_invalid(frame->value->type))
return ira->codegen->invalid_instruction;
ZigType *result_type = frame->value->type->data.any_frame.result_type;
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
ir_assert(fn_entry != nullptr, &instruction->base);
// If it's not @Frame(func) then it's definitely a suspend point
if (target_fn == nullptr) {
if (fn_entry->inferred_async_node == nullptr) {
fn_entry->inferred_async_node = instruction->base.source_node;
}
}
if (type_can_fail(result_type)) {
fn_entry->calls_or_awaits_errorable_fn = true;
}
IrInstruction *result_loc;
if (type_has_bits(result_type)) {
result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc,
result_type, nullptr, true, true);
if (result_loc != nullptr && (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc)))
return result_loc;
} else {
result_loc = nullptr;
}
IrInstructionAwaitGen *result = ir_build_await_gen(ira, &instruction->base, frame, result_type, result_loc);
result->target_fn = target_fn;
fn_entry->await_list.append(result);
return ir_finish_anal(ira, &result->base);
}
static IrInstruction *ir_analyze_instruction_resume(IrAnalyze *ira, IrInstructionResume *instruction) {
IrInstruction *frame_ptr = instruction->frame->child;
if (type_is_invalid(frame_ptr->value->type))
return ira->codegen->invalid_instruction;
IrInstruction *frame;
if (frame_ptr->value->type->id == ZigTypeIdPointer &&
frame_ptr->value->type->data.pointer.ptr_len == PtrLenSingle &&
frame_ptr->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame)
{
frame = frame_ptr;
} else {
frame = ir_get_deref(ira, &instruction->base, frame_ptr, nullptr);
}
ZigType *any_frame_type = get_any_frame_type(ira->codegen, nullptr);
IrInstruction *casted_frame = ir_implicit_cast(ira, frame, any_frame_type);
if (type_is_invalid(casted_frame->value->type))
return ira->codegen->invalid_instruction;
return ir_build_resume(&ira->new_irb, instruction->base.scope, instruction->base.source_node, casted_frame);
}
static IrInstruction *ir_analyze_instruction_spill_begin(IrAnalyze *ira, IrInstructionSpillBegin *instruction) {
if (ir_should_inline(ira->new_irb.exec, instruction->base.scope))
return ir_const_void(ira, &instruction->base);
IrInstruction *operand = instruction->operand->child;
if (type_is_invalid(operand->value->type))
return ira->codegen->invalid_instruction;
if (!type_has_bits(operand->value->type))
return ir_const_void(ira, &instruction->base);
ir_assert(instruction->spill_id == SpillIdRetErrCode, &instruction->base);
ira->new_irb.exec->need_err_code_spill = true;
IrInstructionSpillBegin *result = ir_build_spill_begin(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, operand, instruction->spill_id);
return &result->base;
}
static IrInstruction *ir_analyze_instruction_spill_end(IrAnalyze *ira, IrInstructionSpillEnd *instruction) {
IrInstruction *operand = instruction->begin->operand->child;
if (type_is_invalid(operand->value->type))
return ira->codegen->invalid_instruction;
if (ir_should_inline(ira->new_irb.exec, instruction->base.scope) || !type_has_bits(operand->value->type))
return operand;
ir_assert(instruction->begin->base.child->id == IrInstructionIdSpillBegin, &instruction->base);
IrInstructionSpillBegin *begin = reinterpret_cast<IrInstructionSpillBegin *>(instruction->begin->base.child);
IrInstruction *result = ir_build_spill_end(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, begin);
result->value->type = operand->value->type;
return result;
}
static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction *instruction) {
switch (instruction->id) {
case IrInstructionIdInvalid:
case IrInstructionIdWidenOrShorten:
case IrInstructionIdStructFieldPtr:
case IrInstructionIdUnionFieldPtr:
case IrInstructionIdOptionalWrap:
case IrInstructionIdErrWrapCode:
case IrInstructionIdErrWrapPayload:
case IrInstructionIdCast:
case IrInstructionIdDeclVarGen:
case IrInstructionIdPtrCastGen:
case IrInstructionIdCmpxchgGen:
case IrInstructionIdArrayToVector:
case IrInstructionIdVectorToArray:
case IrInstructionIdPtrOfArrayToSlice:
case IrInstructionIdAssertZero:
case IrInstructionIdAssertNonNull:
case IrInstructionIdResizeSlice:
case IrInstructionIdLoadPtrGen:
case IrInstructionIdBitCastGen:
case IrInstructionIdCallGen:
case IrInstructionIdReturnPtr:
case IrInstructionIdAllocaGen:
case IrInstructionIdSliceGen:
case IrInstructionIdRefGen:
case IrInstructionIdTestErrGen:
case IrInstructionIdFrameSizeGen:
case IrInstructionIdAwaitGen:
case IrInstructionIdSplatGen:
case IrInstructionIdVectorExtractElem:
case IrInstructionIdVectorStoreElem:
case IrInstructionIdAsmGen:
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 IrInstructionIdMergeErrSets:
return ir_analyze_instruction_merge_err_sets(ira, (IrInstructionMergeErrSets *)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 IrInstructionIdCallSrc:
return ir_analyze_instruction_call(ira, (IrInstructionCallSrc *)instruction);
case IrInstructionIdCallSrcArgs:
return ir_analyze_instruction_call_args(ira, (IrInstructionCallSrcArgs *)instruction);
case IrInstructionIdCallExtra:
return ir_analyze_instruction_call_extra(ira, (IrInstructionCallExtra *)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 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 IrInstructionIdAnyFrameType:
return ir_analyze_instruction_any_frame_type(ira, (IrInstructionAnyFrameType *)instruction);
case IrInstructionIdSliceType:
return ir_analyze_instruction_slice_type(ira, (IrInstructionSliceType *)instruction);
case IrInstructionIdAsmSrc:
return ir_analyze_instruction_asm(ira, (IrInstructionAsmSrc *)instruction);
case IrInstructionIdArrayType:
return ir_analyze_instruction_array_type(ira, (IrInstructionArrayType *)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 IrInstructionIdBswap:
return ir_analyze_instruction_bswap(ira, (IrInstructionBswap *)instruction);
case IrInstructionIdBitReverse:
return ir_analyze_instruction_bit_reverse(ira, (IrInstructionBitReverse *)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 IrInstructionIdSwitchElseVar:
return ir_analyze_instruction_switch_else_var(ira, (IrInstructionSwitchElseVar *)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 IrInstructionIdShuffleVector:
return ir_analyze_instruction_shuffle_vector(ira, (IrInstructionShuffleVector *)instruction);
case IrInstructionIdSplatSrc:
return ir_analyze_instruction_splat(ira, (IrInstructionSplatSrc *)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 IrInstructionIdSliceSrc:
return ir_analyze_instruction_slice(ira, (IrInstructionSliceSrc *)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 IrInstructionIdFrameHandle:
return ir_analyze_instruction_frame_handle(ira, (IrInstructionFrameHandle *)instruction);
case IrInstructionIdFrameType:
return ir_analyze_instruction_frame_type(ira, (IrInstructionFrameType *)instruction);
case IrInstructionIdFrameSizeSrc:
return ir_analyze_instruction_frame_size(ira, (IrInstructionFrameSizeSrc *)instruction);
case IrInstructionIdAlignOf:
return ir_analyze_instruction_align_of(ira, (IrInstructionAlignOf *)instruction);
case IrInstructionIdOverflowOp:
return ir_analyze_instruction_overflow_op(ira, (IrInstructionOverflowOp *)instruction);
case IrInstructionIdTestErrSrc:
return ir_analyze_instruction_test_err(ira, (IrInstructionTestErrSrc *)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 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 IrInstructionIdType:
return ir_analyze_instruction_type(ira, (IrInstructionType *)instruction);
case IrInstructionIdHasField:
return ir_analyze_instruction_has_field(ira, (IrInstructionHasField *) 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 IrInstructionIdImplicitCast:
return ir_analyze_instruction_implicit_cast(ira, (IrInstructionImplicitCast *)instruction);
case IrInstructionIdResolveResult:
return ir_analyze_instruction_resolve_result(ira, (IrInstructionResolveResult *)instruction);
case IrInstructionIdResetResult:
return ir_analyze_instruction_reset_result(ira, (IrInstructionResetResult *)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 IrInstructionIdAtomicRmw:
return ir_analyze_instruction_atomic_rmw(ira, (IrInstructionAtomicRmw *)instruction);
case IrInstructionIdAtomicLoad:
return ir_analyze_instruction_atomic_load(ira, (IrInstructionAtomicLoad *)instruction);
case IrInstructionIdAtomicStore:
return ir_analyze_instruction_atomic_store(ira, (IrInstructionAtomicStore *)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 IrInstructionIdFloatOp:
return ir_analyze_instruction_float_op(ira, (IrInstructionFloatOp *)instruction);
case IrInstructionIdMulAdd:
return ir_analyze_instruction_mul_add(ira, (IrInstructionMulAdd *)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);
case IrInstructionIdHasDecl:
return ir_analyze_instruction_has_decl(ira, (IrInstructionHasDecl *)instruction);
case IrInstructionIdUndeclaredIdent:
return ir_analyze_instruction_undeclared_ident(ira, (IrInstructionUndeclaredIdent *)instruction);
case IrInstructionIdAllocaSrc:
return nullptr;
case IrInstructionIdEndExpr:
return ir_analyze_instruction_end_expr(ira, (IrInstructionEndExpr *)instruction);
case IrInstructionIdBitCastSrc:
return ir_analyze_instruction_bit_cast_src(ira, (IrInstructionBitCastSrc *)instruction);
case IrInstructionIdUnionInitNamedField:
return ir_analyze_instruction_union_init_named_field(ira, (IrInstructionUnionInitNamedField *)instruction);
case IrInstructionIdSuspendBegin:
return ir_analyze_instruction_suspend_begin(ira, (IrInstructionSuspendBegin *)instruction);
case IrInstructionIdSuspendFinish:
return ir_analyze_instruction_suspend_finish(ira, (IrInstructionSuspendFinish *)instruction);
case IrInstructionIdResume:
return ir_analyze_instruction_resume(ira, (IrInstructionResume *)instruction);
case IrInstructionIdAwaitSrc:
return ir_analyze_instruction_await(ira, (IrInstructionAwaitSrc *)instruction);
case IrInstructionIdSpillBegin:
return ir_analyze_instruction_spill_begin(ira, (IrInstructionSpillBegin *)instruction);
case IrInstructionIdSpillEnd:
return ir_analyze_instruction_spill_end(ira, (IrInstructionSpillEnd *)instruction);
}
zig_unreachable();
}
// 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, ZigValue *result_ptr)
{
assert(old_exec->first_err_trace_msg == nullptr);
assert(expected_type == nullptr || !type_is_invalid(expected_type));
IrAnalyze *ira = allocate<IrAnalyze>(1, "IrAnalyze");
ira->ref_count = 1;
old_exec->analysis = ira;
ira->codegen = codegen;
ira->explicit_return_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;
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);
if (result_ptr != nullptr) {
assert(result_ptr->type->id == ZigTypeIdPointer);
IrInstructionConst *const_inst = ir_create_instruction<IrInstructionConst>(
&ira->new_irb, new_exec->begin_scope, new_exec->source_node);
const_inst->base.value = result_ptr;
ira->return_ptr = &const_inst->base;
} else {
assert(new_exec->begin_scope != nullptr);
assert(new_exec->source_node != nullptr);
ira->return_ptr = ir_build_return_ptr(ira, new_exec->begin_scope, new_exec->source_node,
get_pointer_to_type(codegen, expected_type, false));
}
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;
}
if (ira->codegen->verbose_ir) {
fprintf(stderr, "~ ");
old_instruction->src();
fprintf(stderr, "~ ");
ir_print_instruction(codegen, stderr, old_instruction, 0, IrPassSrc);
bool want_break = false;
if (ira->break_debug_id == old_instruction->debug_id) {
want_break = true;
} else if (old_instruction->source_node != nullptr) {
for (size_t i = 0; i < dbg_ir_breakpoints_count; i += 1) {
if (dbg_ir_breakpoints_buf[i].line == old_instruction->source_node->line + 1 &&
buf_ends_with_str(old_instruction->source_node->owner->data.structure.root_struct->path,
dbg_ir_breakpoints_buf[i].src_file))
{
want_break = true;
}
}
}
if (want_break) BREAKPOINT;
}
IrInstruction *new_instruction = ir_analyze_instruction_base(ira, old_instruction);
if (new_instruction != nullptr) {
ir_assert(new_instruction->value->type != nullptr || new_instruction->value->type != nullptr, old_instruction);
old_instruction->child = new_instruction;
if (type_is_invalid(new_instruction->value->type)) {
if (ira->codegen->verbose_ir) {
fprintf(stderr, "-> (invalid)");
}
if (new_exec->first_err_trace_msg != nullptr) {
ira->codegen->trace_err = new_exec->first_err_trace_msg;
} else {
new_exec->first_err_trace_msg = ira->codegen->trace_err;
}
if (new_exec->first_err_trace_msg != nullptr &&
!old_instruction->source_node->already_traced_this_node)
{
old_instruction->source_node->already_traced_this_node = true;
new_exec->first_err_trace_msg = add_error_note(ira->codegen, new_exec->first_err_trace_msg,
old_instruction->source_node, buf_create_from_str("referenced here"));
}
return ira->codegen->builtin_types.entry_invalid;
} else if (ira->codegen->verbose_ir) {
fprintf(stderr, "-> ");
if (instr_is_unreachable(new_instruction)) {
fprintf(stderr, "(noreturn)\n");
} else {
ir_print_instruction(codegen, stderr, new_instruction, 0, IrPassGen);
}
}
// unreachable instructions do their own control flow.
if (new_instruction->value->type->id == ZigTypeIdUnreachable)
continue;
} else {
if (ira->codegen->verbose_ir) {
fprintf(stderr, "-> (null");
}
}
ira->instruction_index += 1;
}
ZigType *res_type;
if (new_exec->first_err_trace_msg != nullptr) {
codegen->trace_err = new_exec->first_err_trace_msg;
if (codegen->trace_err != nullptr && new_exec->source_node != nullptr &&
!new_exec->source_node->already_traced_this_node)
{
new_exec->source_node->already_traced_this_node = true;
codegen->trace_err = add_error_note(codegen, codegen->trace_err,
new_exec->source_node, buf_create_from_str("referenced here"));
}
res_type = ira->codegen->builtin_types.entry_invalid;
} else if (ira->src_implicit_return_type_list.length == 0) {
res_type = codegen->builtin_types.entry_unreachable;
} else {
res_type = 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);
}
// It is now safe to free Pass 1 IR instructions.
ira_deref(ira);
return res_type;
}
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 IrInstructionIdVectorStoreElem:
case IrInstructionIdCallExtra:
case IrInstructionIdCallSrc:
case IrInstructionIdCallSrcArgs:
case IrInstructionIdCallGen:
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 IrInstructionIdSaveErrRetAddr:
case IrInstructionIdAddImplicitReturnType:
case IrInstructionIdAtomicRmw:
case IrInstructionIdAtomicStore:
case IrInstructionIdCmpxchgGen:
case IrInstructionIdCmpxchgSrc:
case IrInstructionIdAssertZero:
case IrInstructionIdAssertNonNull:
case IrInstructionIdResizeSlice:
case IrInstructionIdUndeclaredIdent:
case IrInstructionIdEndExpr:
case IrInstructionIdPtrOfArrayToSlice:
case IrInstructionIdSliceGen:
case IrInstructionIdOptionalWrap:
case IrInstructionIdVectorToArray:
case IrInstructionIdResetResult:
case IrInstructionIdSuspendBegin:
case IrInstructionIdSuspendFinish:
case IrInstructionIdResume:
case IrInstructionIdAwaitSrc:
case IrInstructionIdAwaitGen:
case IrInstructionIdSpillBegin:
return true;
case IrInstructionIdPhi:
case IrInstructionIdUnOp:
case IrInstructionIdBinOp:
case IrInstructionIdMergeErrSets:
case IrInstructionIdLoadPtr:
case IrInstructionIdConst:
case IrInstructionIdCast:
case IrInstructionIdContainerInitList:
case IrInstructionIdContainerInitFields:
case IrInstructionIdUnionInitNamedField:
case IrInstructionIdFieldPtr:
case IrInstructionIdElemPtr:
case IrInstructionIdVarPtr:
case IrInstructionIdReturnPtr:
case IrInstructionIdTypeOf:
case IrInstructionIdStructFieldPtr:
case IrInstructionIdArrayType:
case IrInstructionIdSliceType:
case IrInstructionIdAnyFrameType:
case IrInstructionIdSizeOf:
case IrInstructionIdTestNonNull:
case IrInstructionIdOptionalUnwrapPtr:
case IrInstructionIdClz:
case IrInstructionIdCtz:
case IrInstructionIdPopCount:
case IrInstructionIdBswap:
case IrInstructionIdBitReverse:
case IrInstructionIdSwitchVar:
case IrInstructionIdSwitchElseVar:
case IrInstructionIdSwitchTarget:
case IrInstructionIdUnionTag:
case IrInstructionIdRef:
case IrInstructionIdEmbedFile:
case IrInstructionIdTruncate:
case IrInstructionIdIntType:
case IrInstructionIdVectorType:
case IrInstructionIdShuffleVector:
case IrInstructionIdSplatSrc:
case IrInstructionIdSplatGen:
case IrInstructionIdBoolNot:
case IrInstructionIdSliceSrc:
case IrInstructionIdMemberCount:
case IrInstructionIdMemberType:
case IrInstructionIdMemberName:
case IrInstructionIdAlignOf:
case IrInstructionIdReturnAddress:
case IrInstructionIdFrameAddress:
case IrInstructionIdFrameHandle:
case IrInstructionIdFrameType:
case IrInstructionIdFrameSizeSrc:
case IrInstructionIdFrameSizeGen:
case IrInstructionIdTestErrSrc:
case IrInstructionIdTestErrGen:
case IrInstructionIdFnProto:
case IrInstructionIdTestComptime:
case IrInstructionIdPtrCastSrc:
case IrInstructionIdPtrCastGen:
case IrInstructionIdBitCastSrc:
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 IrInstructionIdType:
case IrInstructionIdHasField:
case IrInstructionIdTypeId:
case IrInstructionIdAlignCast:
case IrInstructionIdImplicitCast:
case IrInstructionIdResolveResult:
case IrInstructionIdOpaqueType:
case IrInstructionIdArgType:
case IrInstructionIdTagType:
case IrInstructionIdErrorReturnTrace:
case IrInstructionIdErrorUnion:
case IrInstructionIdFloatOp:
case IrInstructionIdMulAdd:
case IrInstructionIdAtomicLoad:
case IrInstructionIdIntCast:
case IrInstructionIdFloatCast:
case IrInstructionIdErrSetCast:
case IrInstructionIdIntToFloat:
case IrInstructionIdFloatToInt:
case IrInstructionIdBoolToInt:
case IrInstructionIdFromBytes:
case IrInstructionIdToBytes:
case IrInstructionIdEnumToInt:
case IrInstructionIdArrayToVector:
case IrInstructionIdHasDecl:
case IrInstructionIdAllocaSrc:
case IrInstructionIdAllocaGen:
case IrInstructionIdSpillEnd:
case IrInstructionIdVectorExtractElem:
return false;
case IrInstructionIdAsmSrc:
{
IrInstructionAsmSrc *asm_instruction = (IrInstructionAsmSrc *)instruction;
return asm_instruction->has_side_effects;
}
case IrInstructionIdAsmGen:
{
IrInstructionAsmGen *asm_instruction = (IrInstructionAsmGen *)instruction;
return asm_instruction->has_side_effects;
}
case IrInstructionIdUnwrapErrPayload:
{
IrInstructionUnwrapErrPayload *unwrap_err_payload_instruction =
(IrInstructionUnwrapErrPayload *)instruction;
return unwrap_err_payload_instruction->safety_check_on ||
unwrap_err_payload_instruction->initializing;
}
case IrInstructionIdUnwrapErrCode:
return reinterpret_cast<IrInstructionUnwrapErrCode *>(instruction)->initializing;
case IrInstructionIdUnionFieldPtr:
return reinterpret_cast<IrInstructionUnionFieldPtr *>(instruction)->initializing;
case IrInstructionIdErrWrapPayload:
return reinterpret_cast<IrInstructionErrWrapPayload *>(instruction)->result_loc != nullptr;
case IrInstructionIdErrWrapCode:
return reinterpret_cast<IrInstructionErrWrapCode *>(instruction)->result_loc != nullptr;
case IrInstructionIdLoadPtrGen:
return reinterpret_cast<IrInstructionLoadPtrGen *>(instruction)->result_loc != nullptr;
case IrInstructionIdRefGen:
return reinterpret_cast<IrInstructionRefGen *>(instruction)->result_loc != nullptr;
}
zig_unreachable();
}
static ZigType *ir_resolve_lazy_fn_type(IrAnalyze *ira, AstNode *source_node, LazyValueFnType *lazy_fn_type) {
Error err;
AstNode *proto_node = lazy_fn_type->proto_node;
FnTypeId fn_type_id = {0};
init_fn_type_id(&fn_type_id, proto_node, lazy_fn_type->cc, 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 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 (lazy_fn_type->param_types[fn_type_id.next_param_index] == nullptr) {
param_info->type = nullptr;
return get_generic_fn_type(ira->codegen, &fn_type_id);
} else {
IrInstruction *param_type_inst = lazy_fn_type->param_types[fn_type_id.next_param_index];
ZigType *param_type = ir_resolve_type(ira, param_type_inst);
if (type_is_invalid(param_type))
return nullptr;
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_inst,
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 nullptr;
}
param_info->type = param_type;
fn_type_id.next_param_index += 1;
return get_generic_fn_type(ira->codegen, &fn_type_id);
case ReqCompTimeInvalid:
return nullptr;
case ReqCompTimeNo:
break;
}
if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
bool has_bits;
if ((err = type_has_bits2(ira->codegen, param_type, &has_bits)))
return nullptr;
if (!has_bits) {
ir_add_error(ira, param_type_inst,
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 nullptr;
}
}
param_info->type = param_type;
}
}
if (lazy_fn_type->align_inst != nullptr) {
if (!ir_resolve_align(ira, lazy_fn_type->align_inst, nullptr, &fn_type_id.alignment))
return nullptr;
}
fn_type_id.return_type = ir_resolve_type(ira, lazy_fn_type->return_type);
if (type_is_invalid(fn_type_id.return_type))
return nullptr;
if (fn_type_id.return_type->id == ZigTypeIdOpaque) {
ir_add_error(ira, lazy_fn_type->return_type, buf_create_from_str("return type cannot be opaque"));
return nullptr;
}
return get_fn_type(ira->codegen, &fn_type_id);
}
static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) {
Error err;
if (val->special != ConstValSpecialLazy)
return ErrorNone;
switch (val->data.x_lazy->id) {
case LazyValueIdInvalid:
zig_unreachable();
case LazyValueIdAlignOf: {
LazyValueAlignOf *lazy_align_of = reinterpret_cast<LazyValueAlignOf *>(val->data.x_lazy);
IrAnalyze *ira = lazy_align_of->ira;
if (lazy_align_of->target_type->value->special == ConstValSpecialStatic) {
switch (lazy_align_of->target_type->value->data.x_type->id) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdMetaType:
case ZigTypeIdUnreachable:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdBoundFn:
case ZigTypeIdVoid:
case ZigTypeIdOpaque:
ir_add_error(ira, lazy_align_of->target_type,
buf_sprintf("no align available for type '%s'",
buf_ptr(&lazy_align_of->target_type->value->data.x_type->name)));
return ErrorSemanticAnalyzeFail;
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 ZigTypeIdVector:
case ZigTypeIdFnFrame:
case ZigTypeIdAnyFrame:
break;
}
}
uint32_t align_in_bytes;
if ((err = type_val_resolve_abi_align(ira->codegen, source_node,
lazy_align_of->target_type->value, &align_in_bytes)))
{
return err;
}
val->special = ConstValSpecialStatic;
assert(val->type->id == ZigTypeIdComptimeInt || val->type->id == ZigTypeIdInt);
bigint_init_unsigned(&val->data.x_bigint, align_in_bytes);
// We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
return ErrorNone;
}
case LazyValueIdSizeOf: {
LazyValueSizeOf *lazy_size_of = reinterpret_cast<LazyValueSizeOf *>(val->data.x_lazy);
IrAnalyze *ira = lazy_size_of->ira;
if (lazy_size_of->target_type->value->special == ConstValSpecialStatic) {
switch (lazy_size_of->target_type->value->data.x_type->id) {
case ZigTypeIdInvalid: // handled above
zig_unreachable();
case ZigTypeIdUnreachable:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdBoundFn:
case ZigTypeIdOpaque:
ir_add_error(ira, lazy_size_of->target_type,
buf_sprintf("no size available for type '%s'",
buf_ptr(&lazy_size_of->target_type->value->data.x_type->name)));
return ErrorSemanticAnalyzeFail;
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 ZigTypeIdVector:
case ZigTypeIdFnFrame:
case ZigTypeIdAnyFrame:
break;
}
}
size_t abi_size;
size_t size_in_bits;
if ((err = type_val_resolve_abi_size(ira->codegen, source_node, lazy_size_of->target_type->value,
&abi_size, &size_in_bits)))
{
return err;
}
val->special = ConstValSpecialStatic;
assert(val->type->id == ZigTypeIdComptimeInt || val->type->id == ZigTypeIdInt);
if (lazy_size_of->bit_size)
bigint_init_unsigned(&val->data.x_bigint, size_in_bits);
else
bigint_init_unsigned(&val->data.x_bigint, abi_size);
// We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
return ErrorNone;
}
case LazyValueIdSliceType: {
LazyValueSliceType *lazy_slice_type = reinterpret_cast<LazyValueSliceType *>(val->data.x_lazy);
IrAnalyze *ira = lazy_slice_type->ira;
ZigType *elem_type = ir_resolve_type(ira, lazy_slice_type->elem_type);
if (type_is_invalid(elem_type))
return ErrorSemanticAnalyzeFail;
ZigValue *sentinel_val;
if (lazy_slice_type->sentinel != nullptr) {
if (type_is_invalid(lazy_slice_type->sentinel->value->type))
return ErrorSemanticAnalyzeFail;
IrInstruction *sentinel = ir_implicit_cast(ira, lazy_slice_type->sentinel, elem_type);
if (type_is_invalid(sentinel->value->type))
return ErrorSemanticAnalyzeFail;
sentinel_val = ir_resolve_const(ira, sentinel, UndefBad);
if (sentinel_val == nullptr)
return ErrorSemanticAnalyzeFail;
} else {
sentinel_val = nullptr;
}
uint32_t align_bytes = 0;
if (lazy_slice_type->align_inst != nullptr) {
if (!ir_resolve_align(ira, lazy_slice_type->align_inst, elem_type, &align_bytes))
return ErrorSemanticAnalyzeFail;
}
switch (elem_type->id) {
case ZigTypeIdInvalid: // handled above
zig_unreachable();
case ZigTypeIdUnreachable:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdOpaque:
ir_add_error(ira, lazy_slice_type->elem_type,
buf_sprintf("slice of type '%s' not allowed", buf_ptr(&elem_type->name)));
return ErrorSemanticAnalyzeFail;
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 ZigTypeIdVector:
case ZigTypeIdFnFrame:
case ZigTypeIdAnyFrame:
break;
}
ResolveStatus needed_status = (align_bytes == 0) ?
ResolveStatusZeroBitsKnown : ResolveStatusAlignmentKnown;
if ((err = type_resolve(ira->codegen, elem_type, needed_status)))
return err;
ZigType *slice_ptr_type = get_pointer_to_type_extra2(ira->codegen, elem_type,
lazy_slice_type->is_const, lazy_slice_type->is_volatile,
PtrLenUnknown,
align_bytes,
0, 0, lazy_slice_type->is_allowzero,
VECTOR_INDEX_NONE, nullptr, sentinel_val);
val->special = ConstValSpecialStatic;
assert(val->type->id == ZigTypeIdMetaType);
val->data.x_type = get_slice_type(ira->codegen, slice_ptr_type);
// We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
return ErrorNone;
}
case LazyValueIdPtrType: {
LazyValuePtrType *lazy_ptr_type = reinterpret_cast<LazyValuePtrType *>(val->data.x_lazy);
IrAnalyze *ira = lazy_ptr_type->ira;
ZigType *elem_type = ir_resolve_type(ira, lazy_ptr_type->elem_type);
if (type_is_invalid(elem_type))
return ErrorSemanticAnalyzeFail;
ZigValue *sentinel_val;
if (lazy_ptr_type->sentinel != nullptr) {
if (type_is_invalid(lazy_ptr_type->sentinel->value->type))
return ErrorSemanticAnalyzeFail;
IrInstruction *sentinel = ir_implicit_cast(ira, lazy_ptr_type->sentinel, elem_type);
if (type_is_invalid(sentinel->value->type))
return ErrorSemanticAnalyzeFail;
sentinel_val = ir_resolve_const(ira, sentinel, UndefBad);
if (sentinel_val == nullptr)
return ErrorSemanticAnalyzeFail;
} else {
sentinel_val = nullptr;
}
uint32_t align_bytes = 0;
if (lazy_ptr_type->align_inst != nullptr) {
if (!ir_resolve_align(ira, lazy_ptr_type->align_inst, elem_type, &align_bytes))
return ErrorSemanticAnalyzeFail;
}
if (elem_type->id == ZigTypeIdUnreachable) {
ir_add_error(ira, lazy_ptr_type->elem_type,
buf_create_from_str("pointer to noreturn not allowed"));
return ErrorSemanticAnalyzeFail;
} else if (elem_type->id == ZigTypeIdOpaque && lazy_ptr_type->ptr_len == PtrLenUnknown) {
ir_add_error(ira, lazy_ptr_type->elem_type,
buf_create_from_str("unknown-length pointer to opaque"));
return ErrorSemanticAnalyzeFail;
} else if (lazy_ptr_type->ptr_len == PtrLenC) {
bool ok_type;
if ((err = type_allowed_in_extern(ira->codegen, elem_type, &ok_type)))
return err;
if (!ok_type) {
ir_add_error(ira, lazy_ptr_type->elem_type,
buf_sprintf("C pointers cannot point to non-C-ABI-compatible type '%s'",
buf_ptr(&elem_type->name)));
return ErrorSemanticAnalyzeFail;
} else if (elem_type->id == ZigTypeIdOpaque) {
ir_add_error(ira, lazy_ptr_type->elem_type,
buf_sprintf("C pointers cannot point opaque types"));
return ErrorSemanticAnalyzeFail;
} else if (lazy_ptr_type->is_allowzero) {
ir_add_error(ira, lazy_ptr_type->elem_type,
buf_sprintf("C pointers always allow address zero"));
return ErrorSemanticAnalyzeFail;
}
}
if (align_bytes != 0) {
if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusAlignmentKnown)))
return err;
if (!type_has_bits(elem_type))
align_bytes = 0;
}
bool allow_zero = lazy_ptr_type->is_allowzero || lazy_ptr_type->ptr_len == PtrLenC;
assert(val->type->id == ZigTypeIdMetaType);
val->data.x_type = get_pointer_to_type_extra2(ira->codegen, elem_type,
lazy_ptr_type->is_const, lazy_ptr_type->is_volatile, lazy_ptr_type->ptr_len, align_bytes,
lazy_ptr_type->bit_offset_in_host, lazy_ptr_type->host_int_bytes,
allow_zero, VECTOR_INDEX_NONE, nullptr, sentinel_val);
val->special = ConstValSpecialStatic;
// We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
return ErrorNone;
}
case LazyValueIdArrayType: {
LazyValueArrayType *lazy_array_type = reinterpret_cast<LazyValueArrayType *>(val->data.x_lazy);
IrAnalyze *ira = lazy_array_type->ira;
ZigType *elem_type = ir_resolve_type(ira, lazy_array_type->elem_type);
if (type_is_invalid(elem_type))
return ErrorSemanticAnalyzeFail;
switch (elem_type->id) {
case ZigTypeIdInvalid: // handled above
zig_unreachable();
case ZigTypeIdUnreachable:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdOpaque:
ir_add_error(ira, lazy_array_type->elem_type,
buf_sprintf("array of type '%s' not allowed",
buf_ptr(&elem_type->name)));
return ErrorSemanticAnalyzeFail;
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 ZigTypeIdVector:
case ZigTypeIdFnFrame:
case ZigTypeIdAnyFrame:
break;
}
if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown)))
return err;
ZigValue *sentinel_val = nullptr;
if (lazy_array_type->sentinel != nullptr) {
if (type_is_invalid(lazy_array_type->sentinel->value->type))
return ErrorSemanticAnalyzeFail;
IrInstruction *sentinel = ir_implicit_cast(ira, lazy_array_type->sentinel, elem_type);
if (type_is_invalid(sentinel->value->type))
return ErrorSemanticAnalyzeFail;
sentinel_val = ir_resolve_const(ira, sentinel, UndefBad);
if (sentinel_val == nullptr)
return ErrorSemanticAnalyzeFail;
}
assert(val->type->id == ZigTypeIdMetaType);
val->data.x_type = get_array_type(ira->codegen, elem_type, lazy_array_type->length, sentinel_val);
val->special = ConstValSpecialStatic;
// We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
return ErrorNone;
}
case LazyValueIdOptType: {
LazyValueOptType *lazy_opt_type = reinterpret_cast<LazyValueOptType *>(val->data.x_lazy);
IrAnalyze *ira = lazy_opt_type->ira;
ZigType *payload_type = ir_resolve_type(ira, lazy_opt_type->payload_type);
if (type_is_invalid(payload_type))
return ErrorSemanticAnalyzeFail;
if (payload_type->id == ZigTypeIdOpaque || payload_type->id == ZigTypeIdUnreachable) {
ir_add_error(ira, lazy_opt_type->payload_type,
buf_sprintf("type '%s' cannot be optional", buf_ptr(&payload_type->name)));
return ErrorSemanticAnalyzeFail;
}
if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown)))
return err;
assert(val->type->id == ZigTypeIdMetaType);
val->data.x_type = get_optional_type(ira->codegen, payload_type);
val->special = ConstValSpecialStatic;
// We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
return ErrorNone;
}
case LazyValueIdFnType: {
LazyValueFnType *lazy_fn_type = reinterpret_cast<LazyValueFnType *>(val->data.x_lazy);
IrAnalyze *ira = lazy_fn_type->ira;
ZigType *fn_type = ir_resolve_lazy_fn_type(ira, source_node, lazy_fn_type);
if (fn_type == nullptr)
return ErrorSemanticAnalyzeFail;
val->special = ConstValSpecialStatic;
assert(val->type->id == ZigTypeIdMetaType);
val->data.x_type = fn_type;
// We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
return ErrorNone;
}
case LazyValueIdErrUnionType: {
LazyValueErrUnionType *lazy_err_union_type =
reinterpret_cast<LazyValueErrUnionType *>(val->data.x_lazy);
IrAnalyze *ira = lazy_err_union_type->ira;
ZigType *err_set_type = ir_resolve_type(ira, lazy_err_union_type->err_set_type);
if (type_is_invalid(err_set_type))
return ErrorSemanticAnalyzeFail;
ZigType *payload_type = ir_resolve_type(ira, lazy_err_union_type->payload_type);
if (type_is_invalid(payload_type))
return ErrorSemanticAnalyzeFail;
if (err_set_type->id != ZigTypeIdErrorSet) {
ir_add_error(ira, lazy_err_union_type->err_set_type,
buf_sprintf("expected error set type, found type '%s'",
buf_ptr(&err_set_type->name)));
return ErrorSemanticAnalyzeFail;
}
if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown)))
return ErrorSemanticAnalyzeFail;
assert(val->type->id == ZigTypeIdMetaType);
val->data.x_type = get_error_union_type(ira->codegen, err_set_type, payload_type);
val->special = ConstValSpecialStatic;
// We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
return ErrorNone;
}
}
zig_unreachable();
}
Error ir_resolve_lazy(CodeGen *codegen, AstNode *source_node, ZigValue *val) {
Error err;
if ((err = ir_resolve_lazy_raw(source_node, val))) {
if (codegen->trace_err != nullptr && source_node != nullptr && !source_node->already_traced_this_node) {
source_node->already_traced_this_node = true;
codegen->trace_err = add_error_note(codegen, codegen->trace_err, source_node,
buf_create_from_str("referenced here"));
}
return err;
}
if (type_is_invalid(val->type)) {
return ErrorSemanticAnalyzeFail;
}
return ErrorNone;
}
void IrInstruction::src() {
IrInstruction *inst = this;
if (inst->source_node != nullptr) {
inst->source_node->src();
} else {
fprintf(stderr, "(null source node)\n");
}
}
void IrInstruction::dump() {
IrInstruction *inst = this;
inst->src();
IrPass pass = (inst->child == nullptr) ? IrPassGen : IrPassSrc;
if (inst->scope == nullptr) {
fprintf(stderr, "(null scope)\n");
} else {
ir_print_instruction(inst->scope->codegen, stderr, inst, 0, pass);
if (pass == IrPassSrc) {
fprintf(stderr, "-> ");
ir_print_instruction(inst->scope->codegen, stderr, inst->child, 0, IrPassGen);
}
}
}
void IrAnalyze::dump() {
ir_print(this->codegen, stderr, this->new_irb.exec, 0, IrPassGen);
if (this->new_irb.current_basic_block != nullptr) {
fprintf(stderr, "Current basic block:\n");
ir_print_basic_block(this->codegen, stderr, this->new_irb.current_basic_block, 1, IrPassGen);
}
}
void dbg_ir_break(const char *src_file, uint32_t line) {
dbg_ir_breakpoints_buf[dbg_ir_breakpoints_count] = {src_file, line};
dbg_ir_breakpoints_count += 1;
}
void dbg_ir_clear(void) {
dbg_ir_breakpoints_count = 0;
}