/* * Copyright (c) 2016 Andrew Kelley * * This file is part of zig, which is MIT licensed. * See http://opensource.org/licenses/MIT */ #include "analyze.hpp" #include "ast_render.hpp" #include "error.hpp" #include "ir.hpp" #include "ir_print.hpp" #include "os.hpp" #include "range_set.hpp" #include "softfloat.hpp" #include "translate_c.hpp" #include "util.hpp" struct IrExecContext { ZigList mem_slot_list; }; struct IrBuilder { CodeGen *codegen; IrExecutable *exec; IrBasicBlock *current_basic_block; }; struct IrAnalyze { CodeGen *codegen; IrBuilder old_irb; IrBuilder new_irb; IrExecContext exec_context; size_t old_bb_index; size_t instruction_index; ZigType *explicit_return_type; ZigList src_implicit_return_type_list; IrBasicBlock *const_predecessor_bb; }; enum ConstCastResultId { ConstCastResultIdOk, ConstCastResultIdInvalid, ConstCastResultIdErrSet, ConstCastResultIdErrSetGlobal, ConstCastResultIdPointerChild, ConstCastResultIdSliceChild, ConstCastResultIdOptionalChild, ConstCastResultIdErrorUnionPayload, ConstCastResultIdErrorUnionErrorSet, ConstCastResultIdFnAlign, ConstCastResultIdFnCC, ConstCastResultIdFnVarArgs, ConstCastResultIdFnIsGeneric, ConstCastResultIdFnReturnType, ConstCastResultIdFnArgCount, ConstCastResultIdFnGenericArgCount, ConstCastResultIdFnArg, ConstCastResultIdFnArgNoAlias, ConstCastResultIdType, ConstCastResultIdUnresolvedInferredErrSet, ConstCastResultIdAsyncAllocatorType, ConstCastResultIdNullWrapPtr }; struct ConstCastOnly; struct ConstCastArg { size_t arg_index; ConstCastOnly *child; }; struct ConstCastArgNoAlias { size_t arg_index; }; struct ConstCastOptionalMismatch; struct ConstCastPointerMismatch; struct ConstCastSliceMismatch; struct ConstCastErrUnionErrSetMismatch; struct ConstCastErrUnionPayloadMismatch; struct ConstCastErrSetMismatch; struct ConstCastTypeMismatch; struct ConstCastOnly { ConstCastResultId id; union { ConstCastErrSetMismatch *error_set_mismatch; ConstCastPointerMismatch *pointer_mismatch; ConstCastSliceMismatch *slice_mismatch; ConstCastOptionalMismatch *optional; ConstCastErrUnionPayloadMismatch *error_union_payload; ConstCastErrUnionErrSetMismatch *error_union_error_set; ConstCastTypeMismatch *type_mismatch; ConstCastOnly *return_type; ConstCastOnly *async_allocator_type; ConstCastOnly *null_wrap_ptr_child; ConstCastArg fn_arg; ConstCastArgNoAlias arg_no_alias; } data; }; struct ConstCastTypeMismatch { ZigType *wanted_type; ZigType *actual_type; }; struct ConstCastOptionalMismatch { ConstCastOnly child; ZigType *wanted_child; ZigType *actual_child; }; struct ConstCastPointerMismatch { ConstCastOnly child; ZigType *wanted_child; ZigType *actual_child; }; struct ConstCastSliceMismatch { ConstCastOnly child; ZigType *wanted_child; ZigType *actual_child; }; struct ConstCastErrUnionErrSetMismatch { ConstCastOnly child; ZigType *wanted_err_set; ZigType *actual_err_set; }; struct ConstCastErrUnionPayloadMismatch { ConstCastOnly child; ZigType *wanted_payload; ZigType *actual_payload; }; struct ConstCastErrSetMismatch { ZigList missing_errors; }; static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope); static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval); static ZigType *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction); static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, ZigType *expected_type); static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr); static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutable *exec, AstNode *source_node, Buf *msg); static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name, IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type); static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ZigVar *var); static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op); static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval); static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align); static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align); static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val); static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val); static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) { assert(get_codegen_ptr_type(const_val->type) != nullptr); assert(const_val->special == ConstValSpecialStatic); ConstExprValue *result; 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: expand_undef_array(g, const_val->data.x_ptr.data.base_array.array_val); result = &const_val->data.x_ptr.data.base_array.array_val->data.x_array.s_none.elements[ const_val->data.x_ptr.data.base_array.elem_index]; break; case ConstPtrSpecialBaseStruct: result = &const_val->data.x_ptr.data.base_struct.struct_val->data.x_struct.fields[ const_val->data.x_ptr.data.base_struct.field_index]; break; case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialDiscard: zig_unreachable(); case ConstPtrSpecialFunction: zig_unreachable(); } assert(result != nullptr); return result; } static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) { if (a == b) return true; if (a->id == b->id) return true; if (get_codegen_ptr_type(a) != nullptr && get_codegen_ptr_type(b) != nullptr) return true; return false; } ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { ConstExprValue *result = const_ptr_pointee_unchecked(g, const_val); if (const_val->type->id == ZigTypeIdPointer) { assert(types_have_same_zig_comptime_repr(const_val->type->data.pointer.child_type, result->type)); } return result; } static bool ir_should_inline(IrExecutable *exec, Scope *scope) { if (exec->is_inline) return true; while (scope != nullptr) { if (scope->id == ScopeIdCompTime) return true; if (scope->id == ScopeIdFnDef) break; scope = scope->parent; } return false; } static void ir_instruction_append(IrBasicBlock *basic_block, IrInstruction *instruction) { assert(basic_block); assert(instruction); basic_block->instruction_list.append(instruction); } static size_t exec_next_debug_id(IrExecutable *exec) { size_t result = exec->next_debug_id; exec->next_debug_id += 1; return result; } static size_t exec_next_mem_slot(IrExecutable *exec) { size_t result = exec->mem_slot_count; exec->mem_slot_count += 1; return result; } static ZigFn *exec_fn_entry(IrExecutable *exec) { return exec->fn_entry; } static Buf *exec_c_import_buf(IrExecutable *exec) { return exec->c_import_buf; } static bool value_is_comptime(ConstExprValue *const_val) { return const_val->special != ConstValSpecialRuntime; } static bool instr_is_comptime(IrInstruction *instruction) { return value_is_comptime(&instruction->value); } static bool instr_is_unreachable(IrInstruction *instruction) { return instruction->value.type && instruction->value.type->id == ZigTypeIdUnreachable; } static void ir_link_new_instruction(IrInstruction *new_instruction, IrInstruction *old_instruction) { new_instruction->other = old_instruction; old_instruction->other = new_instruction; } static void ir_link_new_bb(IrBasicBlock *new_bb, IrBasicBlock *old_bb) { new_bb->other = old_bb; old_bb->other = new_bb; } static void ir_ref_bb(IrBasicBlock *bb) { bb->ref_count += 1; } static void ir_ref_instruction(IrInstruction *instruction, IrBasicBlock *cur_bb) { assert(instruction->id != IrInstructionIdInvalid); instruction->ref_count += 1; if (instruction->owner_bb != cur_bb && !instr_is_comptime(instruction)) ir_ref_bb(instruction->owner_bb); } static void ir_ref_var(ZigVar *var) { var->ref_count += 1; } static IrBasicBlock *ir_create_basic_block(IrBuilder *irb, Scope *scope, const char *name_hint) { IrBasicBlock *result = allocate(1); result->scope = scope; result->name_hint = name_hint; result->debug_id = exec_next_debug_id(irb->exec); return result; } static IrBasicBlock *ir_build_bb_from(IrBuilder *irb, IrBasicBlock *other_bb) { IrBasicBlock *new_bb = ir_create_basic_block(irb, other_bb->scope, other_bb->name_hint); ir_link_new_bb(new_bb, other_bb); return new_bb; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCondBr *) { return IrInstructionIdCondBr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionBr *) { return IrInstructionIdBr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSwitchBr *) { return IrInstructionIdSwitchBr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSwitchVar *) { return IrInstructionIdSwitchVar; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSwitchTarget *) { return IrInstructionIdSwitchTarget; } static constexpr IrInstructionId ir_instruction_id(IrInstructionPhi *) { return IrInstructionIdPhi; } static constexpr IrInstructionId ir_instruction_id(IrInstructionUnOp *) { return IrInstructionIdUnOp; } static constexpr IrInstructionId ir_instruction_id(IrInstructionBinOp *) { return IrInstructionIdBinOp; } static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVar *) { return IrInstructionIdDeclVar; } static constexpr IrInstructionId ir_instruction_id(IrInstructionExport *) { return IrInstructionIdExport; } static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadPtr *) { return IrInstructionIdLoadPtr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionStorePtr *) { return IrInstructionIdStorePtr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionFieldPtr *) { return IrInstructionIdFieldPtr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionStructFieldPtr *) { return IrInstructionIdStructFieldPtr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionFieldPtr *) { return IrInstructionIdUnionFieldPtr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionElemPtr *) { return IrInstructionIdElemPtr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionVarPtr *) { return IrInstructionIdVarPtr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCall *) { return IrInstructionIdCall; } static constexpr IrInstructionId ir_instruction_id(IrInstructionConst *) { return IrInstructionIdConst; } static constexpr IrInstructionId ir_instruction_id(IrInstructionReturn *) { return IrInstructionIdReturn; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCast *) { return IrInstructionIdCast; } static constexpr IrInstructionId ir_instruction_id(IrInstructionContainerInitList *) { return IrInstructionIdContainerInitList; } static constexpr IrInstructionId ir_instruction_id(IrInstructionContainerInitFields *) { return IrInstructionIdContainerInitFields; } static constexpr IrInstructionId ir_instruction_id(IrInstructionUnreachable *) { return IrInstructionIdUnreachable; } static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeOf *) { return IrInstructionIdTypeOf; } static constexpr IrInstructionId ir_instruction_id(IrInstructionToPtrType *) { return IrInstructionIdToPtrType; } static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrTypeChild *) { return IrInstructionIdPtrTypeChild; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSetCold *) { return IrInstructionIdSetCold; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSetRuntimeSafety *) { return IrInstructionIdSetRuntimeSafety; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSetFloatMode *) { return IrInstructionIdSetFloatMode; } static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayType *) { return IrInstructionIdArrayType; } static constexpr IrInstructionId ir_instruction_id(IrInstructionPromiseType *) { return IrInstructionIdPromiseType; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSliceType *) { return IrInstructionIdSliceType; } static constexpr IrInstructionId ir_instruction_id(IrInstructionAsm *) { return IrInstructionIdAsm; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSizeOf *) { return IrInstructionIdSizeOf; } static constexpr IrInstructionId ir_instruction_id(IrInstructionTestNonNull *) { return IrInstructionIdTestNonNull; } static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapOptional *) { return IrInstructionIdUnwrapOptional; } static constexpr IrInstructionId ir_instruction_id(IrInstructionClz *) { return IrInstructionIdClz; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCtz *) { return IrInstructionIdCtz; } static constexpr IrInstructionId ir_instruction_id(IrInstructionPopCount *) { return IrInstructionIdPopCount; } static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionTag *) { return IrInstructionIdUnionTag; } static constexpr IrInstructionId ir_instruction_id(IrInstructionImport *) { return IrInstructionIdImport; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCImport *) { return IrInstructionIdCImport; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCInclude *) { return IrInstructionIdCInclude; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCDefine *) { return IrInstructionIdCDefine; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCUndef *) { return IrInstructionIdCUndef; } static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayLen *) { return IrInstructionIdArrayLen; } static constexpr IrInstructionId ir_instruction_id(IrInstructionRef *) { return IrInstructionIdRef; } static constexpr IrInstructionId ir_instruction_id(IrInstructionStructInit *) { return IrInstructionIdStructInit; } static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionInit *) { return IrInstructionIdUnionInit; } static constexpr IrInstructionId ir_instruction_id(IrInstructionMinValue *) { return IrInstructionIdMinValue; } static constexpr IrInstructionId ir_instruction_id(IrInstructionMaxValue *) { return IrInstructionIdMaxValue; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCompileErr *) { return IrInstructionIdCompileErr; } static constexpr IrInstructionId ir_instruction_id(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(IrInstructionCmpxchg *) { return IrInstructionIdCmpxchg; } 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(IrInstructionBoolNot *) { return IrInstructionIdBoolNot; } static constexpr IrInstructionId ir_instruction_id(IrInstructionMemset *) { return IrInstructionIdMemset; } static constexpr IrInstructionId ir_instruction_id(IrInstructionMemcpy *) { return IrInstructionIdMemcpy; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSlice *) { return IrInstructionIdSlice; } static constexpr IrInstructionId ir_instruction_id(IrInstructionMemberCount *) { return IrInstructionIdMemberCount; } static constexpr IrInstructionId ir_instruction_id(IrInstructionMemberType *) { return IrInstructionIdMemberType; } static constexpr IrInstructionId ir_instruction_id(IrInstructionMemberName *) { return IrInstructionIdMemberName; } static constexpr IrInstructionId ir_instruction_id(IrInstructionBreakpoint *) { return IrInstructionIdBreakpoint; } static constexpr IrInstructionId ir_instruction_id(IrInstructionReturnAddress *) { return IrInstructionIdReturnAddress; } static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameAddress *) { return IrInstructionIdFrameAddress; } static constexpr IrInstructionId ir_instruction_id(IrInstructionHandle *) { return IrInstructionIdHandle; } static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) { return IrInstructionIdAlignOf; } static constexpr IrInstructionId ir_instruction_id(IrInstructionOverflowOp *) { return IrInstructionIdOverflowOp; } static constexpr IrInstructionId ir_instruction_id(IrInstructionTestErr *) { return IrInstructionIdTestErr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapErrCode *) { return IrInstructionIdUnwrapErrCode; } static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapErrPayload *) { return IrInstructionIdUnwrapErrPayload; } static constexpr IrInstructionId ir_instruction_id(IrInstructionOptionalWrap *) { return IrInstructionIdOptionalWrap; } static constexpr IrInstructionId ir_instruction_id(IrInstructionErrWrapPayload *) { return IrInstructionIdErrWrapPayload; } static constexpr IrInstructionId ir_instruction_id(IrInstructionErrWrapCode *) { return IrInstructionIdErrWrapCode; } static constexpr IrInstructionId ir_instruction_id(IrInstructionFnProto *) { return IrInstructionIdFnProto; } static constexpr IrInstructionId ir_instruction_id(IrInstructionTestComptime *) { return IrInstructionIdTestComptime; } static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCast *) { return IrInstructionIdPtrCast; } static constexpr IrInstructionId ir_instruction_id(IrInstructionBitCast *) { return IrInstructionIdBitCast; } 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(IrInstructionOffsetOf *) { return IrInstructionIdOffsetOf; } static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeInfo *) { return IrInstructionIdTypeInfo; } static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeId *) { return IrInstructionIdTypeId; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSetEvalBranchQuota *) { return IrInstructionIdSetEvalBranchQuota; } static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrType *) { return IrInstructionIdPtrType; } static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignCast *) { return IrInstructionIdAlignCast; } static constexpr IrInstructionId ir_instruction_id(IrInstructionOpaqueType *) { return IrInstructionIdOpaqueType; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSetAlignStack *) { return IrInstructionIdSetAlignStack; } static constexpr IrInstructionId ir_instruction_id(IrInstructionArgType *) { return IrInstructionIdArgType; } static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorReturnTrace *) { return IrInstructionIdErrorReturnTrace; } static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorUnion *) { return IrInstructionIdErrorUnion; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCancel *) { return IrInstructionIdCancel; } static constexpr IrInstructionId ir_instruction_id(IrInstructionGetImplicitAllocator *) { return IrInstructionIdGetImplicitAllocator; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroId *) { return IrInstructionIdCoroId; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroAlloc *) { return IrInstructionIdCoroAlloc; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroSize *) { return IrInstructionIdCoroSize; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroBegin *) { return IrInstructionIdCoroBegin; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroAllocFail *) { return IrInstructionIdCoroAllocFail; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroSuspend *) { return IrInstructionIdCoroSuspend; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroEnd *) { return IrInstructionIdCoroEnd; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroFree *) { return IrInstructionIdCoroFree; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroResume *) { return IrInstructionIdCoroResume; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroSave *) { return IrInstructionIdCoroSave; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroPromise *) { return IrInstructionIdCoroPromise; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroAllocHelper *) { return IrInstructionIdCoroAllocHelper; } static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicRmw *) { return IrInstructionIdAtomicRmw; } static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicLoad *) { return IrInstructionIdAtomicLoad; } static constexpr IrInstructionId ir_instruction_id(IrInstructionPromiseResultType *) { return IrInstructionIdPromiseResultType; } static constexpr IrInstructionId ir_instruction_id(IrInstructionAwaitBookkeeping *) { return IrInstructionIdAwaitBookkeeping; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSaveErrRetAddr *) { return IrInstructionIdSaveErrRetAddr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionAddImplicitReturnType *) { return IrInstructionIdAddImplicitReturnType; } static constexpr IrInstructionId ir_instruction_id(IrInstructionMergeErrRetTraces *) { return IrInstructionIdMergeErrRetTraces; } static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTracePtr *) { return IrInstructionIdMarkErrRetTracePtr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSqrt *) { return IrInstructionIdSqrt; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScope *) { return IrInstructionIdCheckRuntimeScope; } template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); special_instruction->base.id = ir_instruction_id(special_instruction); special_instruction->base.scope = scope; special_instruction->base.source_node = source_node; special_instruction->base.debug_id = exec_next_debug_id(irb->exec); special_instruction->base.owner_bb = irb->current_basic_block; special_instruction->base.value.global_refs = allocate(1); return special_instruction; } template static T *ir_build_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = ir_create_instruction(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(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(irb, scope, source_node); cond_br_instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; cond_br_instruction->base.value.special = ConstValSpecialStatic; cond_br_instruction->condition = condition; cond_br_instruction->then_block = then_block; cond_br_instruction->else_block = else_block; cond_br_instruction->is_comptime = is_comptime; ir_ref_instruction(condition, irb->current_basic_block); ir_ref_bb(then_block); ir_ref_bb(else_block); if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block); return &cond_br_instruction->base; } static IrInstruction *ir_build_cond_br_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *condition, IrBasicBlock *then_block, IrBasicBlock *else_block, IrInstruction *is_comptime) { IrInstruction *new_instruction = ir_build_cond_br(irb, old_instruction->scope, old_instruction->source_node, condition, then_block, else_block, is_comptime); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_return(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *return_value) { IrInstructionReturn *return_instruction = ir_build_instruction(irb, scope, source_node); return_instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; return_instruction->base.value.special = ConstValSpecialStatic; return_instruction->value = return_value; ir_ref_instruction(return_value, irb->current_basic_block); return &return_instruction->base; } static IrInstruction *ir_create_const(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigType *type_entry) { assert(type_entry); IrInstructionConst *const_instruction = ir_create_instruction(irb, scope, source_node); const_instruction->base.value.type = type_entry; const_instruction->base.value.special = ConstValSpecialStatic; return &const_instruction->base; } static IrInstruction *ir_build_const_void(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.value.type = irb->codegen->builtin_types.entry_void; const_instruction->base.value.special = ConstValSpecialStatic; return &const_instruction->base; } static IrInstruction *ir_build_const_undefined(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.value.special = ConstValSpecialUndef; const_instruction->base.value.type = irb->codegen->builtin_types.entry_undef; return &const_instruction->base; } static IrInstruction *ir_build_const_uint(IrBuilder *irb, Scope *scope, AstNode *source_node, uint64_t value) { IrInstructionConst *const_instruction = ir_build_instruction(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(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(irb, scope, source_node); const_instruction->base.value.type = irb->codegen->builtin_types.entry_num_lit_float; const_instruction->base.value.special = ConstValSpecialStatic; bigfloat_init_bigfloat(&const_instruction->base.value.data.x_bigfloat, bigfloat); return &const_instruction->base; } static IrInstruction *ir_build_const_null(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.value.type = irb->codegen->builtin_types.entry_null; const_instruction->base.value.special = ConstValSpecialStatic; return &const_instruction->base; } static IrInstruction *ir_build_const_usize(IrBuilder *irb, Scope *scope, AstNode *source_node, uint64_t value) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.value.type = irb->codegen->builtin_types.entry_usize; const_instruction->base.value.special = ConstValSpecialStatic; bigint_init_unsigned(&const_instruction->base.value.data.x_bigint, value); return &const_instruction->base; } static IrInstruction *ir_build_const_u8(IrBuilder *irb, Scope *scope, AstNode *source_node, uint8_t value) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.value.type = irb->codegen->builtin_types.entry_u8; const_instruction->base.value.special = ConstValSpecialStatic; bigint_init_unsigned(&const_instruction->base.value.data.x_bigint, value); return &const_instruction->base; } static IrInstruction *ir_create_const_type(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigType *type_entry) { IrInstructionConst *const_instruction = ir_create_instruction(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(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, ImportTableEntry *import) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.value.type = irb->codegen->builtin_types.entry_namespace; const_instruction->base.value.special = ConstValSpecialStatic; const_instruction->base.value.data.x_import = import; return &const_instruction->base; } static IrInstruction *ir_build_const_bool(IrBuilder *irb, Scope *scope, AstNode *source_node, bool value) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.value.type = irb->codegen->builtin_types.entry_bool; const_instruction->base.value.special = ConstValSpecialStatic; const_instruction->base.value.data.x_bool = value; return &const_instruction->base; } static IrInstruction *ir_build_const_bound_fn(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigFn *fn_entry, IrInstruction *first_arg) { IrInstructionConst *const_instruction = ir_build_instruction(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(irb, scope, source_node); init_const_str_lit(irb->codegen, &const_instruction->base.value, str); return &const_instruction->base; } static IrInstruction *ir_build_const_str_lit(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *str) { IrInstruction *instruction = ir_create_const_str_lit(irb, scope, source_node, str); ir_instruction_append(irb->current_basic_block, instruction); return instruction; } static IrInstruction *ir_build_const_c_str_lit(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *str) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); init_const_c_str_lit(irb->codegen, &const_instruction->base.value, str); return &const_instruction->base; } static IrInstruction *ir_build_bin_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrBinOp op_id, IrInstruction *op1, IrInstruction *op2, bool safety_check_on) { IrInstructionBinOp *bin_op_instruction = ir_build_instruction(irb, scope, source_node); bin_op_instruction->op_id = op_id; bin_op_instruction->op1 = op1; bin_op_instruction->op2 = op2; bin_op_instruction->safety_check_on = safety_check_on; ir_ref_instruction(op1, irb->current_basic_block); ir_ref_instruction(op2, irb->current_basic_block); return &bin_op_instruction->base; } static IrInstruction *ir_build_bin_op_from(IrBuilder *irb, IrInstruction *old_instruction, IrBinOp op_id, IrInstruction *op1, IrInstruction *op2, bool safety_check_on) { IrInstruction *new_instruction = ir_build_bin_op(irb, old_instruction->scope, old_instruction->source_node, op_id, op1, op2, safety_check_on); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_var_ptr_x(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var, ScopeFnDef *crossed_fndef_scope) { IrInstructionVarPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->var = var; instruction->crossed_fndef_scope = crossed_fndef_scope; ir_ref_var(var); return &instruction->base; } static IrInstruction *ir_build_var_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var) { return ir_build_var_ptr_x(irb, scope, source_node, var, nullptr); } static IrInstruction *ir_build_elem_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *array_ptr, IrInstruction *elem_index, bool safety_check_on, PtrLen ptr_len) { IrInstructionElemPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->array_ptr = array_ptr; instruction->elem_index = elem_index; instruction->safety_check_on = safety_check_on; instruction->ptr_len = ptr_len; ir_ref_instruction(array_ptr, irb->current_basic_block); ir_ref_instruction(elem_index, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_field_ptr_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *container_ptr, IrInstruction *field_name_expr) { IrInstructionFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->container_ptr = container_ptr; instruction->field_name_buffer = nullptr; instruction->field_name_expr = field_name_expr; ir_ref_instruction(container_ptr, irb->current_basic_block); ir_ref_instruction(field_name_expr, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *container_ptr, Buf *field_name) { IrInstructionFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->container_ptr = container_ptr; instruction->field_name_buffer = field_name; instruction->field_name_expr = nullptr; ir_ref_instruction(container_ptr, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_struct_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *struct_ptr, TypeStructField *field) { IrInstructionStructFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->struct_ptr = struct_ptr; instruction->field = field; ir_ref_instruction(struct_ptr, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_union_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *union_ptr, TypeUnionField *field) { IrInstructionUnionFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->union_ptr = union_ptr; instruction->field = field; ir_ref_instruction(union_ptr, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_union_field_ptr_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *union_ptr, TypeUnionField *type_union_field) { IrInstruction *new_instruction = ir_build_union_field_ptr(irb, old_instruction->scope, old_instruction->source_node, union_ptr, type_union_field); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigFn *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args, bool is_comptime, FnInline fn_inline, bool is_async, IrInstruction *async_allocator, IrInstruction *new_stack) { IrInstructionCall *call_instruction = ir_build_instruction(irb, scope, source_node); call_instruction->fn_entry = fn_entry; call_instruction->fn_ref = fn_ref; call_instruction->is_comptime = is_comptime; call_instruction->fn_inline = fn_inline; call_instruction->args = args; call_instruction->arg_count = arg_count; call_instruction->is_async = is_async; call_instruction->async_allocator = async_allocator; call_instruction->new_stack = new_stack; if (fn_ref) ir_ref_instruction(fn_ref, irb->current_basic_block); for (size_t i = 0; i < arg_count; i += 1) ir_ref_instruction(args[i], irb->current_basic_block); if (async_allocator) ir_ref_instruction(async_allocator, irb->current_basic_block); if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block); return &call_instruction->base; } static IrInstruction *ir_build_call_from(IrBuilder *irb, IrInstruction *old_instruction, ZigFn *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args, bool is_comptime, FnInline fn_inline, bool is_async, IrInstruction *async_allocator, IrInstruction *new_stack) { IrInstruction *new_instruction = ir_build_call(irb, old_instruction->scope, old_instruction->source_node, fn_entry, fn_ref, arg_count, args, is_comptime, fn_inline, is_async, async_allocator, new_stack); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_phi(IrBuilder *irb, Scope *scope, AstNode *source_node, size_t incoming_count, IrBasicBlock **incoming_blocks, IrInstruction **incoming_values) { assert(incoming_count != 0); assert(incoming_count != SIZE_MAX); IrInstructionPhi *phi_instruction = ir_build_instruction(irb, scope, source_node); phi_instruction->incoming_count = incoming_count; phi_instruction->incoming_blocks = incoming_blocks; phi_instruction->incoming_values = incoming_values; for (size_t i = 0; i < incoming_count; i += 1) { ir_ref_bb(incoming_blocks[i]); ir_ref_instruction(incoming_values[i], irb->current_basic_block); } return &phi_instruction->base; } static IrInstruction *ir_build_phi_from(IrBuilder *irb, IrInstruction *old_instruction, size_t incoming_count, IrBasicBlock **incoming_blocks, IrInstruction **incoming_values) { IrInstruction *new_instruction = ir_build_phi(irb, old_instruction->scope, old_instruction->source_node, incoming_count, incoming_blocks, incoming_values); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_create_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrBasicBlock *dest_block, IrInstruction *is_comptime) { IrInstructionBr *br_instruction = ir_create_instruction(irb, scope, source_node); br_instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; br_instruction->base.value.special = ConstValSpecialStatic; br_instruction->dest_block = dest_block; br_instruction->is_comptime = is_comptime; ir_ref_bb(dest_block); if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block); return &br_instruction->base; } static IrInstruction *ir_build_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrBasicBlock *dest_block, IrInstruction *is_comptime) { IrInstruction *instruction = ir_create_br(irb, scope, source_node, dest_block, is_comptime); ir_instruction_append(irb->current_basic_block, instruction); return instruction; } static IrInstruction *ir_build_br_from(IrBuilder *irb, IrInstruction *old_instruction, IrBasicBlock *dest_block) { IrInstruction *new_instruction = ir_build_br(irb, old_instruction->scope, old_instruction->source_node, dest_block, nullptr); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_ptr_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *child_type, bool is_const, bool is_volatile, PtrLen ptr_len, IrInstruction *align_value, uint32_t bit_offset_start, uint32_t bit_offset_end) { IrInstructionPtrType *ptr_type_of_instruction = ir_build_instruction(irb, scope, source_node); ptr_type_of_instruction->align_value = align_value; ptr_type_of_instruction->child_type = child_type; ptr_type_of_instruction->is_const = is_const; ptr_type_of_instruction->is_volatile = is_volatile; ptr_type_of_instruction->ptr_len = ptr_len; ptr_type_of_instruction->bit_offset_start = bit_offset_start; ptr_type_of_instruction->bit_offset_end = bit_offset_end; 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(IrBuilder *irb, Scope *scope, AstNode *source_node, IrUnOp op_id, IrInstruction *value) { IrInstructionUnOp *br_instruction = ir_build_instruction(irb, scope, source_node); br_instruction->op_id = op_id; br_instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &br_instruction->base; } static IrInstruction *ir_build_un_op_from(IrBuilder *irb, IrInstruction *old_instruction, IrUnOp op_id, IrInstruction *value) { IrInstruction *new_instruction = ir_build_un_op(irb, old_instruction->scope, old_instruction->source_node, op_id, value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_container_init_list(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *container_type, size_t item_count, IrInstruction **items) { IrInstructionContainerInitList *container_init_list_instruction = ir_build_instruction(irb, scope, source_node); container_init_list_instruction->container_type = container_type; container_init_list_instruction->item_count = item_count; container_init_list_instruction->items = items; ir_ref_instruction(container_type, irb->current_basic_block); for (size_t i = 0; i < item_count; i += 1) { ir_ref_instruction(items[i], irb->current_basic_block); } return &container_init_list_instruction->base; } static IrInstruction *ir_build_container_init_list_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *container_type, size_t item_count, IrInstruction **items) { IrInstruction *new_instruction = ir_build_container_init_list(irb, old_instruction->scope, old_instruction->source_node, container_type, item_count, items); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_container_init_fields(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *container_type, size_t field_count, IrInstructionContainerInitFieldsField *fields) { IrInstructionContainerInitFields *container_init_fields_instruction = ir_build_instruction(irb, scope, source_node); container_init_fields_instruction->container_type = container_type; container_init_fields_instruction->field_count = field_count; container_init_fields_instruction->fields = fields; ir_ref_instruction(container_type, irb->current_basic_block); for (size_t i = 0; i < field_count; i += 1) { ir_ref_instruction(fields[i].value, irb->current_basic_block); } return &container_init_fields_instruction->base; } static IrInstruction *ir_build_struct_init(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigType *struct_type, size_t field_count, IrInstructionStructInitField *fields) { IrInstructionStructInit *struct_init_instruction = ir_build_instruction(irb, scope, source_node); struct_init_instruction->struct_type = struct_type; struct_init_instruction->field_count = field_count; struct_init_instruction->fields = fields; for (size_t i = 0; i < field_count; i += 1) ir_ref_instruction(fields[i].value, irb->current_basic_block); return &struct_init_instruction->base; } static IrInstruction *ir_build_struct_init_from(IrBuilder *irb, IrInstruction *old_instruction, ZigType *struct_type, size_t field_count, IrInstructionStructInitField *fields) { IrInstruction *new_instruction = ir_build_struct_init(irb, old_instruction->scope, old_instruction->source_node, struct_type, field_count, fields); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_union_init(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigType *union_type, TypeUnionField *field, IrInstruction *init_value) { IrInstructionUnionInit *union_init_instruction = ir_build_instruction(irb, scope, source_node); union_init_instruction->union_type = union_type; union_init_instruction->field = field; union_init_instruction->init_value = init_value; ir_ref_instruction(init_value, irb->current_basic_block); return &union_init_instruction->base; } static IrInstruction *ir_build_union_init_from(IrBuilder *irb, IrInstruction *old_instruction, ZigType *union_type, TypeUnionField *field, IrInstruction *init_value) { IrInstruction *new_instruction = ir_build_union_init(irb, old_instruction->scope, old_instruction->source_node, union_type, field, init_value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionUnreachable *unreachable_instruction = ir_build_instruction(irb, scope, source_node); unreachable_instruction->base.value.special = ConstValSpecialStatic; unreachable_instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; return &unreachable_instruction->base; } static IrInstruction *ir_build_unreachable_from(IrBuilder *irb, IrInstruction *old_instruction) { IrInstruction *new_instruction = ir_build_unreachable(irb, old_instruction->scope, old_instruction->source_node); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_store_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr, IrInstruction *value) { IrInstructionStorePtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->base.value.special = ConstValSpecialStatic; instruction->base.value.type = irb->codegen->builtin_types.entry_void; instruction->ptr = ptr; instruction->value = value; ir_ref_instruction(ptr, irb->current_basic_block); ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_store_ptr_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *ptr, IrInstruction *value) { IrInstruction *new_instruction = ir_build_store_ptr(irb, old_instruction->scope, old_instruction->source_node, ptr, value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_var_decl(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var, IrInstruction *var_type, IrInstruction *align_value, IrInstruction *init_value) { IrInstructionDeclVar *decl_var_instruction = ir_build_instruction(irb, scope, source_node); decl_var_instruction->base.value.special = ConstValSpecialStatic; decl_var_instruction->base.value.type = irb->codegen->builtin_types.entry_void; decl_var_instruction->var = var; decl_var_instruction->var_type = var_type; decl_var_instruction->align_value = align_value; decl_var_instruction->init_value = init_value; if (var_type) ir_ref_instruction(var_type, irb->current_basic_block); if (align_value) ir_ref_instruction(align_value, irb->current_basic_block); ir_ref_instruction(init_value, irb->current_basic_block); return &decl_var_instruction->base; } static IrInstruction *ir_build_var_decl_from(IrBuilder *irb, IrInstruction *old_instruction, ZigVar *var, IrInstruction *var_type, IrInstruction *align_value, IrInstruction *init_value) { IrInstruction *new_instruction = ir_build_var_decl(irb, old_instruction->scope, old_instruction->source_node, var, var_type, align_value, init_value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_export(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name, IrInstruction *target, IrInstruction *linkage) { IrInstructionExport *export_instruction = ir_build_instruction( irb, scope, source_node); export_instruction->base.value.special = ConstValSpecialStatic; export_instruction->base.value.type = irb->codegen->builtin_types.entry_void; export_instruction->name = name; export_instruction->target = target; export_instruction->linkage = linkage; ir_ref_instruction(name, irb->current_basic_block); ir_ref_instruction(target, irb->current_basic_block); if (linkage) ir_ref_instruction(linkage, irb->current_basic_block); return &export_instruction->base; } static IrInstruction *ir_build_load_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr) { IrInstructionLoadPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->ptr = ptr; ir_ref_instruction(ptr, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_load_ptr_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *ptr) { IrInstruction *new_instruction = ir_build_load_ptr(irb, old_instruction->scope, old_instruction->source_node, ptr); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_typeof(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionTypeOf *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_to_ptr_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionToPtrType *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_ptr_type_child(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionPtrTypeChild *instruction = ir_build_instruction( 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(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(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(irb, scope, source_node); instruction->mode_value = mode_value; ir_ref_instruction(mode_value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_array_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *size, IrInstruction *child_type) { IrInstructionArrayType *instruction = ir_build_instruction(irb, scope, source_node); instruction->size = size; instruction->child_type = child_type; ir_ref_instruction(size, irb->current_basic_block); ir_ref_instruction(child_type, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_promise_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *payload_type) { IrInstructionPromiseType *instruction = ir_build_instruction(irb, scope, source_node); instruction->payload_type = payload_type; if (payload_type != nullptr) ir_ref_instruction(payload_type, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_slice_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *child_type, bool is_const, bool is_volatile, IrInstruction *align_value) { IrInstructionSliceType *instruction = ir_build_instruction(irb, scope, source_node); instruction->is_const = is_const; instruction->is_volatile = is_volatile; instruction->child_type = child_type; instruction->align_value = align_value; ir_ref_instruction(child_type, irb->current_basic_block); if (align_value) ir_ref_instruction(align_value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_asm(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction **input_list, IrInstruction **output_types, ZigVar **output_vars, size_t return_count, bool has_side_effects) { IrInstructionAsm *instruction = ir_build_instruction(irb, scope, source_node); instruction->input_list = input_list; instruction->output_types = output_types; instruction->output_vars = output_vars; instruction->return_count = return_count; instruction->has_side_effects = has_side_effects; assert(source_node->type == NodeTypeAsmExpr); for (size_t i = 0; i < source_node->data.asm_expr.output_list.length; i += 1) { IrInstruction *output_type = output_types[i]; if (output_type) ir_ref_instruction(output_type, irb->current_basic_block); } for (size_t i = 0; i < source_node->data.asm_expr.input_list.length; i += 1) { IrInstruction *input_value = input_list[i]; ir_ref_instruction(input_value, irb->current_basic_block); } return &instruction->base; } static IrInstruction *ir_build_asm_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction **input_list, IrInstruction **output_types, ZigVar **output_vars, size_t return_count, bool has_side_effects) { IrInstruction *new_instruction = ir_build_asm(irb, old_instruction->scope, old_instruction->source_node, input_list, output_types, output_vars, return_count, has_side_effects); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_size_of(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) { IrInstructionSizeOf *instruction = ir_build_instruction(irb, scope, source_node); instruction->type_value = type_value; ir_ref_instruction(type_value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_test_nonnull(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionTestNonNull *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_test_nonnull_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) { IrInstruction *new_instruction = ir_build_test_nonnull(irb, old_instruction->scope, old_instruction->source_node, value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_unwrap_maybe(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value, bool safety_check_on) { IrInstructionUnwrapOptional *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; instruction->safety_check_on = safety_check_on; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_unwrap_maybe_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value, bool safety_check_on) { IrInstruction *new_instruction = ir_build_unwrap_maybe(irb, old_instruction->scope, old_instruction->source_node, value, safety_check_on); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_maybe_wrap(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionOptionalWrap *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_err_wrap_payload(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionErrWrapPayload *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_err_wrap_code(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionErrWrapCode *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_clz(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionClz *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_clz_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) { IrInstruction *new_instruction = ir_build_clz(irb, old_instruction->scope, old_instruction->source_node, value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_ctz(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionCtz *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_ctz_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) { IrInstruction *new_instruction = ir_build_ctz(irb, old_instruction->scope, old_instruction->source_node, value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_pop_count(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionPopCount *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target_value, IrBasicBlock *else_block, size_t case_count, IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime, IrInstruction *switch_prongs_void) { IrInstructionSwitchBr *instruction = ir_build_instruction(irb, scope, source_node); instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; instruction->base.value.special = ConstValSpecialStatic; instruction->target_value = target_value; instruction->else_block = else_block; instruction->case_count = case_count; instruction->cases = cases; instruction->is_comptime = is_comptime; instruction->switch_prongs_void = switch_prongs_void; ir_ref_instruction(target_value, irb->current_basic_block); if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block); ir_ref_bb(else_block); if (switch_prongs_void) ir_ref_instruction(switch_prongs_void, irb->current_basic_block); for (size_t i = 0; i < case_count; i += 1) { ir_ref_instruction(cases[i].value, irb->current_basic_block); ir_ref_bb(cases[i].block); } return &instruction->base; } static IrInstruction *ir_build_switch_br_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *target_value, IrBasicBlock *else_block, size_t case_count, IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime, IrInstruction *switch_prongs_void) { IrInstruction *new_instruction = ir_build_switch_br(irb, old_instruction->scope, old_instruction->source_node, target_value, else_block, case_count, cases, is_comptime, switch_prongs_void); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_switch_target(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target_value_ptr) { IrInstructionSwitchTarget *instruction = ir_build_instruction(irb, scope, source_node); instruction->target_value_ptr = target_value_ptr; ir_ref_instruction(target_value_ptr, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_switch_var(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target_value_ptr, IrInstruction *prong_value) { IrInstructionSwitchVar *instruction = ir_build_instruction(irb, scope, source_node); instruction->target_value_ptr = target_value_ptr; instruction->prong_value = prong_value; ir_ref_instruction(target_value_ptr, irb->current_basic_block); ir_ref_instruction(prong_value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_union_tag(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionUnionTag *instruction = ir_build_instruction(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(irb, scope, source_node); instruction->name = name; ir_ref_instruction(name, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_array_len(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *array_value) { IrInstructionArrayLen *instruction = ir_build_instruction(irb, scope, source_node); instruction->array_value = array_value; ir_ref_instruction(array_value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_ref(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value, bool is_const, bool is_volatile) { IrInstructionRef *instruction = ir_build_instruction(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_min_value(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionMinValue *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_max_value(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionMaxValue *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_compile_err(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *msg) { IrInstructionCompileErr *instruction = ir_build_instruction(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(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(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_err_name_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) { IrInstruction *new_instruction = ir_build_err_name(irb, old_instruction->scope, old_instruction->source_node, value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_c_import(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionCImport *instruction = ir_build_instruction(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(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(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(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(irb, scope, source_node); instruction->name = name; ir_ref_instruction(name, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value, IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value, bool is_weak, ZigType *type, AtomicOrder success_order, AtomicOrder failure_order) { IrInstructionCmpxchg *instruction = ir_build_instruction(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->type = type; instruction->success_order = success_order; instruction->failure_order = failure_order; if (type_value != nullptr) 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); if (type_value != nullptr) ir_ref_instruction(success_order_value, irb->current_basic_block); if (type_value != nullptr) ir_ref_instruction(failure_order_value, 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(irb, scope, source_node); instruction->order_value = order_value; instruction->order = order; ir_ref_instruction(order_value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_fence_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *order_value, AtomicOrder order) { IrInstruction *new_instruction = ir_build_fence(irb, old_instruction->scope, old_instruction->source_node, order_value, order); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_truncate(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) { IrInstructionTruncate *instruction = ir_build_instruction(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(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(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(irb, scope, source_node); instruction->dest_type = dest_type; instruction->target = target; ir_ref_instruction(dest_type, irb->current_basic_block); ir_ref_instruction(target, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_to_bytes(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target) { IrInstructionToBytes *instruction = ir_build_instruction(irb, scope, source_node); instruction->target = target; ir_ref_instruction(target, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_from_bytes(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_child_type, IrInstruction *target) { IrInstructionFromBytes *instruction = ir_build_instruction(irb, scope, source_node); instruction->dest_child_type = dest_child_type; instruction->target = target; ir_ref_instruction(dest_child_type, irb->current_basic_block); ir_ref_instruction(target, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_int_to_float(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) { IrInstructionIntToFloat *instruction = ir_build_instruction(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(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(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(irb, scope, source_node); instruction->is_signed = is_signed; instruction->bit_count = bit_count; ir_ref_instruction(is_signed, irb->current_basic_block); ir_ref_instruction(bit_count, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_bool_not(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionBoolNot *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_bool_not_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) { IrInstruction *new_instruction = ir_build_bool_not(irb, old_instruction->scope, old_instruction->source_node, value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_memset(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_ptr, IrInstruction *byte, IrInstruction *count) { IrInstructionMemset *instruction = ir_build_instruction(irb, scope, source_node); instruction->dest_ptr = dest_ptr; instruction->byte = byte; instruction->count = count; ir_ref_instruction(dest_ptr, irb->current_basic_block); ir_ref_instruction(byte, irb->current_basic_block); ir_ref_instruction(count, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_memset_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *dest_ptr, IrInstruction *byte, IrInstruction *count) { IrInstruction *new_instruction = ir_build_memset(irb, old_instruction->scope, old_instruction->source_node, dest_ptr, byte, count); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_memcpy(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_ptr, IrInstruction *src_ptr, IrInstruction *count) { IrInstructionMemcpy *instruction = ir_build_instruction(irb, scope, source_node); instruction->dest_ptr = dest_ptr; instruction->src_ptr = src_ptr; instruction->count = count; ir_ref_instruction(dest_ptr, irb->current_basic_block); ir_ref_instruction(src_ptr, irb->current_basic_block); ir_ref_instruction(count, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_memcpy_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *dest_ptr, IrInstruction *src_ptr, IrInstruction *count) { IrInstruction *new_instruction = ir_build_memcpy(irb, old_instruction->scope, old_instruction->source_node, dest_ptr, src_ptr, count); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_slice(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool safety_check_on) { IrInstructionSlice *instruction = ir_build_instruction(irb, scope, source_node); instruction->ptr = ptr; instruction->start = start; instruction->end = end; instruction->safety_check_on = safety_check_on; ir_ref_instruction(ptr, irb->current_basic_block); ir_ref_instruction(start, irb->current_basic_block); if (end) ir_ref_instruction(end, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_slice_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool safety_check_on) { IrInstruction *new_instruction = ir_build_slice(irb, old_instruction->scope, old_instruction->source_node, ptr, start, end, safety_check_on); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_member_count(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *container) { IrInstructionMemberCount *instruction = ir_build_instruction(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(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(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(irb, scope, source_node); return &instruction->base; } static IrInstruction *ir_build_breakpoint_from(IrBuilder *irb, IrInstruction *old_instruction) { IrInstruction *new_instruction = ir_build_breakpoint(irb, old_instruction->scope, old_instruction->source_node); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_return_address(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionReturnAddress *instruction = ir_build_instruction(irb, scope, source_node); return &instruction->base; } static IrInstruction *ir_build_return_address_from(IrBuilder *irb, IrInstruction *old_instruction) { IrInstruction *new_instruction = ir_build_return_address(irb, old_instruction->scope, old_instruction->source_node); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_frame_address(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionFrameAddress *instruction = ir_build_instruction(irb, scope, source_node); return &instruction->base; } static IrInstruction *ir_build_frame_address_from(IrBuilder *irb, IrInstruction *old_instruction) { IrInstruction *new_instruction = ir_build_frame_address(irb, old_instruction->scope, old_instruction->source_node); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_handle(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionHandle *instruction = ir_build_instruction(irb, scope, source_node); return &instruction->base; } static IrInstruction *ir_build_handle_from(IrBuilder *irb, IrInstruction *old_instruction) { IrInstruction *new_instruction = ir_build_handle(irb, old_instruction->scope, old_instruction->source_node); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2, IrInstruction *result_ptr, ZigType *result_ptr_type) { IrInstructionOverflowOp *instruction = ir_build_instruction(irb, scope, source_node); instruction->op = op; instruction->type_value = type_value; instruction->op1 = op1; instruction->op2 = op2; instruction->result_ptr = result_ptr; instruction->result_ptr_type = result_ptr_type; ir_ref_instruction(type_value, irb->current_basic_block); ir_ref_instruction(op1, irb->current_basic_block); ir_ref_instruction(op2, irb->current_basic_block); ir_ref_instruction(result_ptr, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_overflow_op_from(IrBuilder *irb, IrInstruction *old_instruction, IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2, IrInstruction *result_ptr, ZigType *result_ptr_type) { IrInstruction *new_instruction = ir_build_overflow_op(irb, old_instruction->scope, old_instruction->source_node, op, type_value, op1, op2, result_ptr, result_ptr_type); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_align_of(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) { IrInstructionAlignOf *instruction = ir_build_instruction(irb, scope, source_node); instruction->type_value = type_value; ir_ref_instruction(type_value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_test_err(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionTestErr *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_test_err_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) { IrInstruction *new_instruction = ir_build_test_err(irb, old_instruction->scope, old_instruction->source_node, value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_unwrap_err_code(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionUnwrapErrCode *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_unwrap_err_code_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) { IrInstruction *new_instruction = ir_build_unwrap_err_code(irb, old_instruction->scope, old_instruction->source_node, value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_unwrap_err_payload(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value, bool safety_check_on) { IrInstructionUnwrapErrPayload *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; instruction->safety_check_on = safety_check_on; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_unwrap_err_payload_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value, bool safety_check_on) { IrInstruction *new_instruction = ir_build_unwrap_err_payload(irb, old_instruction->scope, old_instruction->source_node, value, safety_check_on); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction **param_types, IrInstruction *align_value, IrInstruction *return_type, IrInstruction *async_allocator_type_value, bool is_var_args) { IrInstructionFnProto *instruction = ir_build_instruction(irb, scope, source_node); instruction->param_types = param_types; instruction->align_value = align_value; instruction->return_type = return_type; instruction->async_allocator_type_value = async_allocator_type_value; instruction->is_var_args = is_var_args; assert(source_node->type == NodeTypeFnProto); size_t param_count = source_node->data.fn_proto.params.length; if (is_var_args) param_count -= 1; for (size_t i = 0; i < param_count; i += 1) { if (param_types[i] != nullptr) ir_ref_instruction(param_types[i], irb->current_basic_block); } if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); if (async_allocator_type_value != nullptr) ir_ref_instruction(async_allocator_type_value, irb->current_basic_block); ir_ref_instruction(return_type, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_test_comptime(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionTestComptime *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_ptr_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *ptr) { IrInstructionPtrCast *instruction = ir_build_instruction( irb, scope, source_node); instruction->dest_type = dest_type; instruction->ptr = ptr; if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_bit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *value) { IrInstructionBitCast *instruction = ir_build_instruction( irb, scope, source_node); instruction->dest_type = dest_type; instruction->value = value; if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block); ir_ref_instruction(value, 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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(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(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(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( 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_offset_of(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value, IrInstruction *field_name) { IrInstructionOffsetOf *instruction = ir_build_instruction(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(irb, scope, source_node); instruction->type_value = type_value; ir_ref_instruction(type_value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_type_id(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) { IrInstructionTypeId *instruction = ir_build_instruction(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(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(irb, scope, source_node); instruction->align_bytes = align_bytes; instruction->target = target; if (align_bytes) ir_ref_instruction(align_bytes, irb->current_basic_block); ir_ref_instruction(target, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_opaque_type(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionOpaqueType *instruction = ir_build_instruction(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(irb, scope, source_node); instruction->align_bytes = align_bytes; ir_ref_instruction(align_bytes, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *fn_type, IrInstruction *arg_index) { IrInstructionArgType *instruction = ir_build_instruction(irb, scope, source_node); instruction->fn_type = fn_type; instruction->arg_index = arg_index; ir_ref_instruction(fn_type, irb->current_basic_block); ir_ref_instruction(arg_index, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstructionErrorReturnTrace::Optional optional) { IrInstructionErrorReturnTrace *instruction = ir_build_instruction(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(irb, scope, source_node); instruction->err_set = err_set; instruction->payload = payload; ir_ref_instruction(err_set, irb->current_basic_block); ir_ref_instruction(payload, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_cancel(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target) { IrInstructionCancel *instruction = ir_build_instruction(irb, scope, source_node); instruction->target = target; ir_ref_instruction(target, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_get_implicit_allocator(IrBuilder *irb, Scope *scope, AstNode *source_node, ImplicitAllocatorId id) { IrInstructionGetImplicitAllocator *instruction = ir_build_instruction(irb, scope, source_node); instruction->id = id; return &instruction->base; } static IrInstruction *ir_build_coro_id(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *promise_ptr) { IrInstructionCoroId *instruction = ir_build_instruction(irb, scope, source_node); instruction->promise_ptr = promise_ptr; ir_ref_instruction(promise_ptr, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_coro_alloc(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *coro_id) { IrInstructionCoroAlloc *instruction = ir_build_instruction(irb, scope, source_node); instruction->coro_id = coro_id; ir_ref_instruction(coro_id, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_coro_size(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionCoroSize *instruction = ir_build_instruction(irb, scope, source_node); return &instruction->base; } static IrInstruction *ir_build_coro_begin(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *coro_id, IrInstruction *coro_mem_ptr) { IrInstructionCoroBegin *instruction = ir_build_instruction(irb, scope, source_node); instruction->coro_id = coro_id; instruction->coro_mem_ptr = coro_mem_ptr; ir_ref_instruction(coro_id, irb->current_basic_block); ir_ref_instruction(coro_mem_ptr, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_coro_alloc_fail(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *err_val) { IrInstructionCoroAllocFail *instruction = ir_build_instruction(irb, scope, source_node); instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; instruction->base.value.special = ConstValSpecialStatic; instruction->err_val = err_val; ir_ref_instruction(err_val, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_coro_suspend(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *save_point, IrInstruction *is_final) { IrInstructionCoroSuspend *instruction = ir_build_instruction(irb, scope, source_node); instruction->save_point = save_point; instruction->is_final = is_final; if (save_point != nullptr) ir_ref_instruction(save_point, irb->current_basic_block); ir_ref_instruction(is_final, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_coro_end(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionCoroEnd *instruction = ir_build_instruction(irb, scope, source_node); return &instruction->base; } static IrInstruction *ir_build_coro_free(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *coro_id, IrInstruction *coro_handle) { IrInstructionCoroFree *instruction = ir_build_instruction(irb, scope, source_node); instruction->coro_id = coro_id; instruction->coro_handle = coro_handle; ir_ref_instruction(coro_id, irb->current_basic_block); ir_ref_instruction(coro_handle, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_coro_resume(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *awaiter_handle) { IrInstructionCoroResume *instruction = ir_build_instruction(irb, scope, source_node); instruction->awaiter_handle = awaiter_handle; ir_ref_instruction(awaiter_handle, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_coro_save(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *coro_handle) { IrInstructionCoroSave *instruction = ir_build_instruction(irb, scope, source_node); instruction->coro_handle = coro_handle; ir_ref_instruction(coro_handle, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_coro_promise(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *coro_handle) { IrInstructionCoroPromise *instruction = ir_build_instruction(irb, scope, source_node); instruction->coro_handle = coro_handle; ir_ref_instruction(coro_handle, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_coro_alloc_helper(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *alloc_fn, IrInstruction *coro_size) { IrInstructionCoroAllocHelper *instruction = ir_build_instruction(irb, scope, source_node); instruction->alloc_fn = alloc_fn; instruction->coro_size = coro_size; ir_ref_instruction(alloc_fn, irb->current_basic_block); ir_ref_instruction(coro_size, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_atomic_rmw(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *operand_type, IrInstruction *ptr, IrInstruction *op, IrInstruction *operand, IrInstruction *ordering, AtomicRmwOp resolved_op, AtomicOrder resolved_ordering) { IrInstructionAtomicRmw *instruction = ir_build_instruction(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(irb, scope, source_node); instruction->operand_type = operand_type; instruction->ptr = ptr; instruction->ordering = ordering; instruction->resolved_ordering = resolved_ordering; if (operand_type != nullptr) ir_ref_instruction(operand_type, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); if (ordering != nullptr) ir_ref_instruction(ordering, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_promise_result_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *promise_type) { IrInstructionPromiseResultType *instruction = ir_build_instruction(irb, scope, source_node); instruction->promise_type = promise_type; ir_ref_instruction(promise_type, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_await_bookkeeping(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *promise_result_type) { IrInstructionAwaitBookkeeping *instruction = ir_build_instruction(irb, scope, source_node); instruction->promise_result_type = promise_result_type; ir_ref_instruction(promise_result_type, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_save_err_ret_addr(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionSaveErrRetAddr *instruction = ir_build_instruction(irb, scope, source_node); return &instruction->base; } static IrInstruction *ir_build_add_implicit_return_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionAddImplicitReturnType *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_merge_err_ret_traces(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *coro_promise_ptr, IrInstruction *src_err_ret_trace_ptr, IrInstruction *dest_err_ret_trace_ptr) { IrInstructionMergeErrRetTraces *instruction = ir_build_instruction(irb, scope, source_node); instruction->coro_promise_ptr = coro_promise_ptr; instruction->src_err_ret_trace_ptr = src_err_ret_trace_ptr; instruction->dest_err_ret_trace_ptr = dest_err_ret_trace_ptr; ir_ref_instruction(coro_promise_ptr, irb->current_basic_block); ir_ref_instruction(src_err_ret_trace_ptr, irb->current_basic_block); ir_ref_instruction(dest_err_ret_trace_ptr, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *err_ret_trace_ptr) { IrInstructionMarkErrRetTracePtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->err_ret_trace_ptr = err_ret_trace_ptr; ir_ref_instruction(err_ret_trace_ptr, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_sqrt(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) { IrInstructionSqrt *instruction = ir_build_instruction(irb, scope, source_node); instruction->type = type; instruction->op = op; if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block); ir_ref_instruction(op, irb->current_basic_block); return &instruction->base; } static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *scope_is_comptime, IrInstruction *is_comptime) { IrInstructionCheckRuntimeScope *instruction = ir_build_instruction(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 void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; Scope *scope = inner_scope; while (scope != outer_scope) { assert(scope); switch (scope->id) { case ScopeIdDefer: { AstNode *defer_node = scope->source_node; assert(defer_node->type == NodeTypeDefer); ReturnKind defer_kind = defer_node->data.defer.kind; results[defer_kind] += 1; scope = scope->parent; continue; } case ScopeIdDecls: case ScopeIdFnDef: return; case ScopeIdBlock: case ScopeIdVarDecl: case ScopeIdLoop: case ScopeIdSuspend: case ScopeIdCompTime: case ScopeIdRuntime: scope = scope->parent; continue; case ScopeIdDeferExpr: case ScopeIdCImport: case ScopeIdCoroPrelude: zig_unreachable(); } } } static IrInstruction *ir_mark_gen(IrInstruction *instruction) { instruction->is_gen = true; return instruction; } static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, bool gen_error_defers) { Scope *scope = inner_scope; bool is_noreturn = false; while (scope != outer_scope) { if (!scope) return is_noreturn; switch (scope->id) { case ScopeIdDefer: { AstNode *defer_node = scope->source_node; assert(defer_node->type == NodeTypeDefer); ReturnKind defer_kind = defer_node->data.defer.kind; if (defer_kind == ReturnKindUnconditional || (gen_error_defers && defer_kind == ReturnKindError)) { AstNode *defer_expr_node = defer_node->data.defer.expr; Scope *defer_expr_scope = defer_node->data.defer.expr_scope; IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); if (defer_expr_value != irb->codegen->invalid_instruction) { if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == ZigTypeIdUnreachable) { is_noreturn = true; } else { ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value)); } } } scope = scope->parent; continue; } case ScopeIdDecls: case ScopeIdFnDef: return is_noreturn; case ScopeIdBlock: case ScopeIdVarDecl: case ScopeIdLoop: case ScopeIdSuspend: case ScopeIdCompTime: case ScopeIdRuntime: scope = scope->parent; continue; case ScopeIdDeferExpr: case ScopeIdCImport: case ScopeIdCoroPrelude: zig_unreachable(); } } return is_noreturn; } static void ir_set_cursor_at_end(IrBuilder *irb, IrBasicBlock *basic_block) { assert(basic_block); irb->current_basic_block = basic_block; } static void ir_set_cursor_at_end_and_append_block(IrBuilder *irb, IrBasicBlock *basic_block) { irb->exec->basic_block_list.append(basic_block); ir_set_cursor_at_end(irb, basic_block); } static ScopeSuspend *get_scope_suspend(Scope *scope) { while (scope) { if (scope->id == ScopeIdSuspend) return (ScopeSuspend *)scope; if (scope->id == ScopeIdFnDef) return nullptr; scope = scope->parent; } return nullptr; } static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) { while (scope) { if (scope->id == ScopeIdDeferExpr) return (ScopeDeferExpr *)scope; if (scope->id == ScopeIdFnDef) return nullptr; scope = scope->parent; } return nullptr; } static bool exec_is_async(IrExecutable *exec) { ZigFn *fn_entry = exec_fn_entry(exec); return fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; } static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *return_value, bool is_generated_code) { ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, return_value)); bool is_async = exec_is_async(irb->exec); if (!is_async) { IrInstruction *return_inst = ir_build_return(irb, scope, node, return_value); return_inst->is_gen = is_generated_code; return return_inst; } IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "Suspended"); IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, scope, "NotSuspended"); IrBasicBlock *store_awaiter_block = ir_create_basic_block(irb, scope, "StoreAwaiter"); IrBasicBlock *check_canceled_block = ir_create_basic_block(irb, scope, "CheckCanceled"); IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise); IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value); IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, ptr_mask, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, suspended_block); ir_build_unreachable(irb, scope, node); ir_set_cursor_at_end_and_append_block(irb, not_suspended_block); IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); // if we ever add null checking safety to the ptrtoint instruction, it needs to be disabled here IrInstruction *have_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); ir_build_cond_br(irb, scope, node, have_await_handle, store_awaiter_block, check_canceled_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, store_awaiter_block); IrInstruction *await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, await_handle_addr); ir_build_store_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, await_handle); ir_build_br(irb, scope, node, irb->exec->coro_normal_final, is_comptime); ir_set_cursor_at_end_and_append_block(irb, check_canceled_block); IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); return ir_build_cond_br(irb, scope, node, is_canceled_bool, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, is_comptime); } static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { assert(node->type == NodeTypeReturnExpr); ZigFn *fn_entry = exec_fn_entry(irb->exec); if (!fn_entry) { add_node_error(irb->codegen, node, buf_sprintf("return expression outside function definition")); return irb->codegen->invalid_instruction; } ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(scope); if (scope_defer_expr) { if (!scope_defer_expr->reported_err) { add_node_error(irb->codegen, node, buf_sprintf("cannot return from defer expression")); scope_defer_expr->reported_err = true; } return irb->codegen->invalid_instruction; } Scope *outer_scope = irb->exec->begin_scope; AstNode *expr_node = node->data.return_expr.expr; switch (node->data.return_expr.kind) { case ReturnKindUnconditional: { IrInstruction *return_value; if (expr_node) { // Temporarily set this so that if we return a type it gets the name of the function ZigFn *prev_name_fn = irb->exec->name_fn; irb->exec->name_fn = exec_fn_entry(irb->exec); return_value = ir_gen_node(irb, expr_node, scope); irb->exec->name_fn = prev_name_fn; if (return_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } else { return_value = ir_build_const_void(irb, scope, node); } size_t defer_counts[2]; ir_count_defers(irb, scope, outer_scope, defer_counts); bool have_err_defers = defer_counts[ReturnKindError] > 0; if (have_err_defers || irb->codegen->have_err_ret_tracing) { IrBasicBlock *err_block = ir_create_basic_block(irb, scope, "ErrRetErr"); IrBasicBlock *ok_block = ir_create_basic_block(irb, scope, "ErrRetOk"); if (!have_err_defers) { ir_gen_defers_for_block(irb, scope, outer_scope, false); } IrInstruction *is_err = ir_build_test_err(irb, scope, node, return_value); bool should_inline = ir_should_inline(irb->exec, scope); IrInstruction *is_comptime; if (should_inline) { is_comptime = ir_build_const_bool(irb, scope, node, true); } else { is_comptime = ir_build_test_comptime(irb, scope, node, is_err); } ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err, err_block, ok_block, is_comptime)); IrBasicBlock *ret_stmt_block = ir_create_basic_block(irb, scope, "RetStmt"); ir_set_cursor_at_end_and_append_block(irb, err_block); if (have_err_defers) { ir_gen_defers_for_block(irb, scope, outer_scope, true); } if (irb->codegen->have_err_ret_tracing && !should_inline) { ir_build_save_err_ret_addr(irb, scope, node); } ir_build_br(irb, scope, node, ret_stmt_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, ok_block); if (have_err_defers) { ir_gen_defers_for_block(irb, scope, outer_scope, false); } ir_build_br(irb, scope, node, ret_stmt_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, ret_stmt_block); return ir_gen_async_return(irb, scope, node, return_value, false); } else { // generate unconditional defers ir_gen_defers_for_block(irb, scope, outer_scope, false); return ir_gen_async_return(irb, scope, node, return_value, false); } } case ReturnKindError: { assert(expr_node); IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *err_union_val = ir_build_load_ptr(irb, scope, node, err_union_ptr); IrInstruction *is_err_val = ir_build_test_err(irb, scope, node, err_union_val); IrBasicBlock *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn"); IrBasicBlock *continue_block = ir_create_basic_block(irb, scope, "ErrRetContinue"); IrInstruction *is_comptime; bool should_inline = ir_should_inline(irb->exec, scope); if (should_inline) { is_comptime = ir_build_const_bool(irb, scope, node, true); } else { is_comptime = ir_build_test_comptime(irb, scope, node, is_err_val); } ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, return_block); if (!ir_gen_defers_for_block(irb, scope, outer_scope, true)) { IrInstruction *err_val = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr); if (irb->codegen->have_err_ret_tracing && !should_inline) { ir_build_save_err_ret_addr(irb, scope, node); } ir_gen_async_return(irb, scope, node, err_val, false); } ir_set_cursor_at_end_and_append_block(irb, continue_block); IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, scope, node, err_union_ptr, false); if (lval == LValPtr) return unwrapped_ptr; else return ir_build_load_ptr(irb, scope, node, unwrapped_ptr); } } zig_unreachable(); } static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_scope, Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime, bool skip_name_check) { ZigVar *variable_entry = allocate(1); variable_entry->parent_scope = parent_scope; variable_entry->shadowable = is_shadowable; variable_entry->mem_slot_index = SIZE_MAX; variable_entry->is_comptime = is_comptime; variable_entry->src_arg_index = SIZE_MAX; variable_entry->value = create_const_vals(1); if (name) { buf_init_from_buf(&variable_entry->name, name); if (!skip_name_check) { ZigVar *existing_var = find_variable(codegen, parent_scope, name, nullptr); if (existing_var && !existing_var->shadowable) { ErrorMsg *msg = add_node_error(codegen, node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); variable_entry->value->type = codegen->builtin_types.entry_invalid; } else { ZigType *type = get_primitive_type(codegen, name); if (type != nullptr) { add_node_error(codegen, node, buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name))); variable_entry->value->type = codegen->builtin_types.entry_invalid; } else { Tld *tld = find_decl(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->value->type = codegen->builtin_types.entry_invalid; } } } } } else { assert(is_shadowable); // TODO make this name not actually be in scope. user should be able to make a variable called "_anon" // might already be solved, let's just make sure it has test coverage // maybe we put a prefix on this so the debug info doesn't clobber user debug info for same named variables buf_init_from_str(&variable_entry->name, "_anon"); } variable_entry->src_is_const = src_is_const; variable_entry->gen_is_const = gen_is_const; variable_entry->decl_node = node; variable_entry->child_scope = create_var_scope(node, parent_scope, variable_entry); return variable_entry; } // Set name to nullptr to make the variable anonymous (not visible to programmer). // After you call this function var->child_scope has the variable in scope static ZigVar *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime) { bool is_underscored = name ? buf_eql_str(name, "_") : false; ZigVar *var = create_local_var(irb->codegen, node, scope, (is_underscored ? nullptr : name), src_is_const, gen_is_const, (is_underscored ? true : is_shadowable), is_comptime, false); if (is_comptime != nullptr || gen_is_const) { var->mem_slot_index = exec_next_mem_slot(irb->exec); var->owner_exec = irb->exec; } assert(var->child_scope); return var; } static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node) { assert(block_node->type == NodeTypeBlock); ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; ScopeBlock *scope_block = create_block_scope(block_node, parent_scope); Scope *outer_block_scope = &scope_block->base; Scope *child_scope = outer_block_scope; ZigFn *fn_entry = scope_fn_entry(parent_scope); if (fn_entry && fn_entry->child_scope == parent_scope) { fn_entry->def_scope = scope_block; } if (block_node->data.block.statements.length == 0) { // {} return ir_build_const_void(irb, child_scope, block_node); } if (block_node->data.block.name != nullptr) { scope_block->incoming_blocks = &incoming_blocks; scope_block->incoming_values = &incoming_values; scope_block->end_block = ir_create_basic_block(irb, parent_scope, "BlockEnd"); scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node, ir_should_inline(irb->exec, parent_scope)); } bool is_continuation_unreachable = false; IrInstruction *noreturn_return_value = nullptr; for (size_t i = 0; i < block_node->data.block.statements.length; i += 1) { AstNode *statement_node = block_node->data.block.statements.at(i); IrInstruction *statement_value = ir_gen_node(irb, statement_node, child_scope); is_continuation_unreachable = instr_is_unreachable(statement_value); if (is_continuation_unreachable) { // keep the last noreturn statement value around in case we need to return it noreturn_return_value = statement_value; } if (statement_node->type == NodeTypeDefer && statement_value != irb->codegen->invalid_instruction) { // defer starts a new scope child_scope = statement_node->data.defer.child_scope; assert(child_scope); } else if (statement_value->id == IrInstructionIdDeclVar) { // variable declarations start a new scope IrInstructionDeclVar *decl_var_instruction = (IrInstructionDeclVar *)statement_value; child_scope = decl_var_instruction->var->child_scope; } else if (statement_value != irb->codegen->invalid_instruction && !is_continuation_unreachable) { // this statement's value must be void ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, statement_node, statement_value)); } } if (is_continuation_unreachable) { assert(noreturn_return_value != nullptr); if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) { return noreturn_return_value; } ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block); return ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } else { incoming_blocks.append(irb->current_basic_block); incoming_values.append(ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node))); } if (block_node->data.block.name != nullptr) { ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false); ir_mark_gen(ir_build_br(irb, parent_scope, block_node, scope_block->end_block, scope_block->is_comptime)); ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block); return ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } else { ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false); return ir_mark_gen(ir_mark_gen(ir_build_const_void(irb, child_scope, block_node))); } } static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) { IrInstruction *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); if (op1 == irb->codegen->invalid_instruction || op2 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; return ir_build_bin_op(irb, scope, node, op_id, op1, op2, true); } static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) { IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr); IrInstruction *rvalue = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); if (lvalue == irb->codegen->invalid_instruction || rvalue == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; ir_build_store_ptr(irb, scope, node, lvalue, rvalue); return ir_build_const_void(irb, scope, node); } static IrInstruction *ir_gen_assign_op(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) { IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr); if (lvalue == irb->codegen->invalid_instruction) return lvalue; IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue); IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); if (op2 == irb->codegen->invalid_instruction) return op2; IrInstruction *result = ir_build_bin_op(irb, scope, node, op_id, op1, op2, true); ir_build_store_ptr(irb, scope, node, lvalue, result); return ir_build_const_void(irb, scope, node); } static IrInstruction *ir_gen_bool_or(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); IrInstruction *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); if (val1 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrBasicBlock *post_val1_block = irb->current_basic_block; IrInstruction *is_comptime; if (ir_should_inline(irb->exec, scope)) { is_comptime = ir_build_const_bool(irb, scope, node, true); } else { is_comptime = ir_build_test_comptime(irb, scope, node, val1); } // block for when val1 == false IrBasicBlock *false_block = ir_create_basic_block(irb, scope, "BoolOrFalse"); // block for when val1 == true (don't even evaluate the second part) IrBasicBlock *true_block = ir_create_basic_block(irb, scope, "BoolOrTrue"); ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, false_block); IrInstruction *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); if (val2 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrBasicBlock *post_val2_block = irb->current_basic_block; ir_build_br(irb, scope, node, true_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, true_block); IrInstruction **incoming_values = allocate(2); incoming_values[0] = val1; incoming_values[1] = val2; IrBasicBlock **incoming_blocks = allocate(2); incoming_blocks[0] = post_val1_block; incoming_blocks[1] = post_val2_block; return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } static IrInstruction *ir_gen_bool_and(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); IrInstruction *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); if (val1 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrBasicBlock *post_val1_block = irb->current_basic_block; IrInstruction *is_comptime; if (ir_should_inline(irb->exec, scope)) { is_comptime = ir_build_const_bool(irb, scope, node, true); } else { is_comptime = ir_build_test_comptime(irb, scope, node, val1); } // block for when val1 == true IrBasicBlock *true_block = ir_create_basic_block(irb, scope, "BoolAndTrue"); // block for when val1 == false (don't even evaluate the second part) IrBasicBlock *false_block = ir_create_basic_block(irb, scope, "BoolAndFalse"); ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, true_block); IrInstruction *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); if (val2 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrBasicBlock *post_val2_block = irb->current_basic_block; ir_build_br(irb, scope, node, false_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, false_block); IrInstruction **incoming_values = allocate(2); incoming_values[0] = val1; incoming_values[1] = val2; IrBasicBlock **incoming_blocks = allocate(2); incoming_blocks[0] = post_val1_block; incoming_blocks[1] = post_val2_block; return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); AstNode *op1_node = node->data.bin_op_expr.op1; AstNode *op2_node = node->data.bin_op_expr.op2; IrInstruction *maybe_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr); if (maybe_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *maybe_val = ir_build_load_ptr(irb, parent_scope, node, maybe_ptr); IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_val); IrInstruction *is_comptime; if (ir_should_inline(irb->exec, parent_scope)) { is_comptime = ir_build_const_bool(irb, parent_scope, node, true); } else { is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_non_null); } IrBasicBlock *ok_block = ir_create_basic_block(irb, parent_scope, "OptionalNonNull"); IrBasicBlock *null_block = ir_create_basic_block(irb, parent_scope, "OptionalNull"); IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "OptionalEnd"); ir_build_cond_br(irb, parent_scope, node, is_non_null, ok_block, null_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, null_block); IrInstruction *null_result = ir_gen_node(irb, op2_node, parent_scope); if (null_result == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrBasicBlock *after_null_block = irb->current_basic_block; if (!instr_is_unreachable(null_result)) ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, ok_block); IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, parent_scope, node, maybe_ptr, false); IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr); IrBasicBlock *after_ok_block = irb->current_basic_block; ir_build_br(irb, parent_scope, node, end_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, end_block); IrInstruction **incoming_values = allocate(2); incoming_values[0] = null_result; incoming_values[1] = unwrapped_payload; IrBasicBlock **incoming_blocks = allocate(2); incoming_blocks[0] = after_null_block; incoming_blocks[1] = after_ok_block; return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values); } static IrInstruction *ir_gen_error_union(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); AstNode *op1_node = node->data.bin_op_expr.op1; AstNode *op2_node = node->data.bin_op_expr.op2; IrInstruction *err_set = ir_gen_node(irb, op1_node, parent_scope); if (err_set == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *payload = ir_gen_node(irb, op2_node, parent_scope); if (payload == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; return ir_build_error_union(irb, parent_scope, node, err_set, payload); } static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); BinOpType bin_op_type = node->data.bin_op_expr.bin_op; switch (bin_op_type) { case BinOpTypeInvalid: zig_unreachable(); case BinOpTypeAssign: return ir_gen_assign(irb, scope, node); case BinOpTypeAssignTimes: return ir_gen_assign_op(irb, scope, node, IrBinOpMult); case BinOpTypeAssignTimesWrap: return ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap); case BinOpTypeAssignDiv: return ir_gen_assign_op(irb, scope, node, IrBinOpDivUnspecified); case BinOpTypeAssignMod: return ir_gen_assign_op(irb, scope, node, IrBinOpRemUnspecified); case BinOpTypeAssignPlus: return ir_gen_assign_op(irb, scope, node, IrBinOpAdd); case BinOpTypeAssignPlusWrap: return ir_gen_assign_op(irb, scope, node, IrBinOpAddWrap); case BinOpTypeAssignMinus: return ir_gen_assign_op(irb, scope, node, IrBinOpSub); case BinOpTypeAssignMinusWrap: return ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap); case BinOpTypeAssignBitShiftLeft: return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftLossy); case BinOpTypeAssignBitShiftRight: return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRightLossy); case BinOpTypeAssignBitAnd: return ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd); case BinOpTypeAssignBitXor: return ir_gen_assign_op(irb, scope, node, IrBinOpBinXor); case BinOpTypeAssignBitOr: return ir_gen_assign_op(irb, scope, node, IrBinOpBinOr); case BinOpTypeAssignMergeErrorSets: return ir_gen_assign_op(irb, scope, node, IrBinOpMergeErrorSets); case BinOpTypeBoolOr: return ir_gen_bool_or(irb, scope, node); case BinOpTypeBoolAnd: return ir_gen_bool_and(irb, scope, node); case BinOpTypeCmpEq: return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpEq); case BinOpTypeCmpNotEq: return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpNotEq); case BinOpTypeCmpLessThan: return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessThan); case BinOpTypeCmpGreaterThan: return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterThan); case BinOpTypeCmpLessOrEq: return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessOrEq); case BinOpTypeCmpGreaterOrEq: return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterOrEq); case BinOpTypeBinOr: return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinOr); case BinOpTypeBinXor: return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinXor); case BinOpTypeBinAnd: return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd); case BinOpTypeBitShiftLeft: return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftLossy); case BinOpTypeBitShiftRight: return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRightLossy); case BinOpTypeAdd: return ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd); case BinOpTypeAddWrap: return ir_gen_bin_op_id(irb, scope, node, IrBinOpAddWrap); case BinOpTypeSub: return ir_gen_bin_op_id(irb, scope, node, IrBinOpSub); case BinOpTypeSubWrap: return ir_gen_bin_op_id(irb, scope, node, IrBinOpSubWrap); case BinOpTypeMult: return ir_gen_bin_op_id(irb, scope, node, IrBinOpMult); case BinOpTypeMultWrap: return ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap); case BinOpTypeDiv: return ir_gen_bin_op_id(irb, scope, node, IrBinOpDivUnspecified); case BinOpTypeMod: return ir_gen_bin_op_id(irb, scope, node, IrBinOpRemUnspecified); case BinOpTypeArrayCat: return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat); case BinOpTypeArrayMult: return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult); case BinOpTypeMergeErrorSets: return ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets); case BinOpTypeUnwrapOptional: return ir_gen_maybe_ok_or(irb, scope, node); case BinOpTypeErrorUnion: return ir_gen_error_union(irb, scope, node); } zig_unreachable(); } static IrInstruction *ir_gen_int_lit(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeIntLiteral); return ir_build_const_bigint(irb, scope, node, node->data.int_literal.bigint); } static IrInstruction *ir_gen_float_lit(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeFloatLiteral); if (node->data.float_literal.overflow) { add_node_error(irb->codegen, node, buf_sprintf("float literal out of range of any type")); return irb->codegen->invalid_instruction; } return ir_build_const_bigfloat(irb, scope, node, node->data.float_literal.bigfloat); } static IrInstruction *ir_gen_char_lit(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeCharLiteral); return ir_build_const_uint(irb, scope, node, node->data.char_literal.value); } static IrInstruction *ir_gen_null_literal(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeNullLiteral); return ir_build_const_null(irb, scope, node); } static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { assert(node->type == NodeTypeSymbol); Buf *variable_name = node->data.symbol_expr.symbol; if (buf_eql_str(variable_name, "_") && lval == LValPtr) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, node); const_instruction->base.value.type = get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_void, false); const_instruction->base.value.special = ConstValSpecialStatic; const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialDiscard; return &const_instruction->base; } ZigType *primitive_type = get_primitive_type(irb->codegen, variable_name); if (primitive_type != nullptr) { IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_type); if (lval == LValPtr) { return ir_build_ref(irb, scope, node, value, false, false); } else { return value; } } ScopeFnDef *crossed_fndef_scope; ZigVar *var = find_variable(irb->codegen, scope, variable_name, &crossed_fndef_scope); if (var) { IrInstruction *var_ptr = ir_build_var_ptr_x(irb, scope, node, var, crossed_fndef_scope); if (lval == LValPtr) return var_ptr; else return ir_build_load_ptr(irb, scope, node, var_ptr); } Tld *tld = find_decl(irb->codegen, scope, variable_name); if (tld) return ir_build_decl_ref(irb, scope, node, tld, lval); if (node->owner->any_imports_failed) { // skip the error message since we had a failing import in this file // if an import breaks we don't need redundant undeclared identifier errors return irb->codegen->invalid_instruction; } // TODO put a variable of same name with invalid type in global scope // so that future references to this same name will find a variable with an invalid type add_node_error(irb->codegen, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name))); return irb->codegen->invalid_instruction; } static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { assert(node->type == NodeTypeArrayAccessExpr); AstNode *array_ref_node = node->data.array_access_expr.array_ref_expr; IrInstruction *array_ref_instruction = ir_gen_node_extra(irb, array_ref_node, scope, LValPtr); if (array_ref_instruction == irb->codegen->invalid_instruction) return array_ref_instruction; AstNode *subscript_node = node->data.array_access_expr.subscript; IrInstruction *subscript_instruction = ir_gen_node(irb, subscript_node, scope); if (subscript_instruction == irb->codegen->invalid_instruction) return subscript_instruction; IrInstruction *ptr_instruction = ir_build_elem_ptr(irb, scope, node, array_ref_instruction, subscript_instruction, true, PtrLenSingle); if (lval == LValPtr) return ptr_instruction; return ir_build_load_ptr(irb, scope, node, ptr_instruction); } static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeFieldAccessExpr); AstNode *container_ref_node = node->data.field_access_expr.struct_expr; Buf *field_name = node->data.field_access_expr.field_name; IrInstruction *container_ref_instruction = ir_gen_node_extra(irb, container_ref_node, scope, LValPtr); if (container_ref_instruction == irb->codegen->invalid_instruction) return container_ref_instruction; return ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name); } static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *node, IrOverflowOp op) { assert(node->type == NodeTypeFnCallExpr); AstNode *type_node = node->data.fn_call_expr.params.at(0); AstNode *op1_node = node->data.fn_call_expr.params.at(1); AstNode *op2_node = node->data.fn_call_expr.params.at(2); AstNode *result_ptr_node = node->data.fn_call_expr.params.at(3); IrInstruction *type_value = ir_gen_node(irb, type_node, scope); if (type_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *op1 = ir_gen_node(irb, op1_node, scope); if (op1 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *op2 = ir_gen_node(irb, op2_node, scope); if (op2 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *result_ptr = ir_gen_node(irb, result_ptr_node, scope); if (result_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr); } static IrInstruction *ir_gen_this(IrBuilder *irb, Scope *orig_scope, AstNode *node) { for (Scope *it_scope = orig_scope; it_scope != nullptr; it_scope = it_scope->parent) { if (it_scope->id == ScopeIdDecls) { ScopeDecls *decls_scope = (ScopeDecls *)it_scope; ZigType *container_type = decls_scope->container_type; if (container_type != nullptr) { return ir_build_const_type(irb, orig_scope, node, container_type); } else { return ir_build_const_import(irb, orig_scope, node, decls_scope->import); } } } zig_unreachable(); } static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { assert(node->type == NodeTypeFnCallExpr); AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; Buf *name = fn_ref_expr->data.symbol_expr.symbol; auto entry = irb->codegen->builtin_fn_table.maybe_get(name); if (!entry) { add_node_error(irb->codegen, node, buf_sprintf("invalid builtin function: '%s'", buf_ptr(name))); return irb->codegen->invalid_instruction; } BuiltinFnEntry *builtin_fn = entry->value; size_t actual_param_count = node->data.fn_call_expr.params.length; if (builtin_fn->param_count != SIZE_MAX && builtin_fn->param_count != actual_param_count) { add_node_error(irb->codegen, node, buf_sprintf("expected %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize, builtin_fn->param_count, actual_param_count)); return irb->codegen->invalid_instruction; } bool is_async = exec_is_async(irb->exec); switch (builtin_fn->id) { case BuiltinFnIdInvalid: zig_unreachable(); case BuiltinFnIdTypeof: { AstNode *arg_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg = ir_gen_node(irb, arg_node, scope); if (arg == irb->codegen->invalid_instruction) return arg; IrInstruction *type_of = ir_build_typeof(irb, scope, node, arg); return ir_lval_wrap(irb, scope, type_of, lval); } case BuiltinFnIdSetCold: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *set_cold = ir_build_set_cold(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, set_cold, lval); } case BuiltinFnIdSetRuntimeSafety: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *set_safety = ir_build_set_runtime_safety(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, set_safety, lval); } case BuiltinFnIdSetFloatMode: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *set_float_mode = ir_build_set_float_mode(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, set_float_mode, lval); } case BuiltinFnIdSizeof: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *size_of = ir_build_size_of(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, size_of, lval); } case BuiltinFnIdCtz: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *ctz = ir_build_ctz(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, ctz, lval); } case BuiltinFnIdPopCount: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *instr = ir_build_pop_count(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, instr, lval); } case BuiltinFnIdClz: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *clz = ir_build_clz(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, clz, lval); } case BuiltinFnIdImport: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *import = ir_build_import(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, import, lval); } case BuiltinFnIdCImport: { IrInstruction *c_import = ir_build_c_import(irb, scope, node); return ir_lval_wrap(irb, scope, c_import, lval); } case BuiltinFnIdCInclude: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; if (!exec_c_import_buf(irb->exec)) { add_node_error(irb->codegen, node, buf_sprintf("C include valid only inside C import block")); return irb->codegen->invalid_instruction; } IrInstruction *c_include = ir_build_c_include(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, c_include, lval); } case BuiltinFnIdCDefine: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; if (!exec_c_import_buf(irb->exec)) { add_node_error(irb->codegen, node, buf_sprintf("C define valid only inside C import block")); return irb->codegen->invalid_instruction; } IrInstruction *c_define = ir_build_c_define(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, c_define, lval); } case BuiltinFnIdCUndef: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; if (!exec_c_import_buf(irb->exec)) { add_node_error(irb->codegen, node, buf_sprintf("C undef valid only inside C import block")); return irb->codegen->invalid_instruction; } IrInstruction *c_undef = ir_build_c_undef(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, c_undef, lval); } case BuiltinFnIdMaxValue: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *max_value = ir_build_max_value(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, max_value, lval); } case BuiltinFnIdMinValue: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *min_value = ir_build_min_value(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, min_value, lval); } case BuiltinFnIdCompileErr: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *compile_err = ir_build_compile_err(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, compile_err, lval); } case BuiltinFnIdCompileLog: { IrInstruction **args = allocate(actual_param_count); for (size_t i = 0; i < actual_param_count; i += 1) { AstNode *arg_node = node->data.fn_call_expr.params.at(i); args[i] = ir_gen_node(irb, arg_node, scope); if (args[i] == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } IrInstruction *compile_log = ir_build_compile_log(irb, scope, node, actual_param_count, args); return ir_lval_wrap(irb, scope, compile_log, lval); } case BuiltinFnIdErrName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *err_name = ir_build_err_name(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, err_name, lval); } case BuiltinFnIdEmbedFile: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *embed_file = ir_build_embed_file(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, embed_file, lval); } case BuiltinFnIdCmpxchgWeak: case BuiltinFnIdCmpxchgStrong: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; AstNode *arg3_node = node->data.fn_call_expr.params.at(3); IrInstruction *arg3_value = ir_gen_node(irb, arg3_node, scope); if (arg3_value == irb->codegen->invalid_instruction) return arg3_value; AstNode *arg4_node = node->data.fn_call_expr.params.at(4); IrInstruction *arg4_value = ir_gen_node(irb, arg4_node, scope); if (arg4_value == irb->codegen->invalid_instruction) return arg4_value; AstNode *arg5_node = node->data.fn_call_expr.params.at(5); IrInstruction *arg5_value = ir_gen_node(irb, arg5_node, scope); if (arg5_value == irb->codegen->invalid_instruction) return arg5_value; IrInstruction *cmpxchg = ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value, arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak), nullptr, AtomicOrderUnordered, AtomicOrderUnordered); return ir_lval_wrap(irb, scope, cmpxchg, lval); } case BuiltinFnIdFence: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *fence = ir_build_fence(irb, scope, node, arg0_value, AtomicOrderUnordered); return ir_lval_wrap(irb, scope, fence, lval); } case BuiltinFnIdDivExact: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivExact, arg0_value, arg1_value, true); return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdDivTrunc: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivTrunc, arg0_value, arg1_value, true); return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdDivFloor: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivFloor, arg0_value, arg1_value, true); return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdRem: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemRem, arg0_value, arg1_value, true); return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdMod: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true); return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdSqrt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *ir_sqrt = ir_build_sqrt(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, ir_sqrt, lval); } case BuiltinFnIdTruncate: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *truncate = ir_build_truncate(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, truncate, lval); } case BuiltinFnIdIntCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *result = ir_build_int_cast(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval); } case BuiltinFnIdFloatCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *result = ir_build_float_cast(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval); } case BuiltinFnIdErrSetCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *result = ir_build_err_set_cast(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval); } case BuiltinFnIdFromBytes: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *result = ir_build_from_bytes(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval); } case BuiltinFnIdToBytes: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *result = ir_build_to_bytes(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, result, lval); } case BuiltinFnIdIntToFloat: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *result = ir_build_int_to_float(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval); } case BuiltinFnIdFloatToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *result = ir_build_float_to_int(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval); } case BuiltinFnIdErrToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *result = ir_build_err_to_int(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, result, lval); } case BuiltinFnIdIntToErr: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *result = ir_build_int_to_err(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, result, lval); } case BuiltinFnIdBoolToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *result = ir_build_bool_to_int(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, result, lval); } case BuiltinFnIdIntType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *int_type = ir_build_int_type(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, int_type, lval); } case BuiltinFnIdMemcpy: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; IrInstruction *ir_memcpy = ir_build_memcpy(irb, scope, node, arg0_value, arg1_value, arg2_value); return ir_lval_wrap(irb, scope, ir_memcpy, lval); } case BuiltinFnIdMemset: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; IrInstruction *ir_memset = ir_build_memset(irb, scope, node, arg0_value, arg1_value, arg2_value); return ir_lval_wrap(irb, scope, ir_memset, lval); } case BuiltinFnIdMemberCount: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *member_count = ir_build_member_count(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, member_count, lval); } case BuiltinFnIdMemberType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *member_type = ir_build_member_type(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, member_type, lval); } case BuiltinFnIdMemberName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *member_name = ir_build_member_name(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, member_name, lval); } case BuiltinFnIdField: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LValPtr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node, arg0_value, arg1_value); if (lval == LValPtr) return ptr_instruction; return ir_build_load_ptr(irb, scope, node, ptr_instruction); } case BuiltinFnIdTypeInfo: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *type_info = ir_build_type_info(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, type_info, lval); } case BuiltinFnIdBreakpoint: return ir_lval_wrap(irb, scope, ir_build_breakpoint(irb, scope, node), lval); case BuiltinFnIdReturnAddress: return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval); case BuiltinFnIdFrameAddress: return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval); case BuiltinFnIdHandle: if (!irb->exec->fn_entry) { add_node_error(irb->codegen, node, buf_sprintf("@handle() called outside of function definition")); return irb->codegen->invalid_instruction; } if (!is_async) { add_node_error(irb->codegen, node, buf_sprintf("@handle() in non-async function")); return irb->codegen->invalid_instruction; } return ir_lval_wrap(irb, scope, ir_build_handle(irb, scope, node), lval); case BuiltinFnIdAlignOf: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *align_of = ir_build_align_of(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, align_of, lval); } case BuiltinFnIdAddWithOverflow: return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd), lval); case BuiltinFnIdSubWithOverflow: return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub), lval); case BuiltinFnIdMulWithOverflow: return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval); case BuiltinFnIdShlWithOverflow: return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval); case BuiltinFnIdTypeName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *type_name = ir_build_type_name(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, type_name, lval); } case BuiltinFnIdPanic: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *panic = ir_build_panic(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, panic, lval); } case BuiltinFnIdPtrCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *ptr_cast = ir_build_ptr_cast(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, ptr_cast, lval); } case BuiltinFnIdBitCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *bit_cast = ir_build_bit_cast(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, bit_cast, lval); } case BuiltinFnIdIntToPtr: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *int_to_ptr = ir_build_int_to_ptr(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, int_to_ptr, lval); } case BuiltinFnIdPtrToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *ptr_to_int = ir_build_ptr_to_int(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, ptr_to_int, lval); } case BuiltinFnIdTagName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *actual_tag = ir_build_union_tag(irb, scope, node, arg0_value); IrInstruction *tag_name = ir_build_tag_name(irb, scope, node, actual_tag); return ir_lval_wrap(irb, scope, tag_name, lval); } case BuiltinFnIdTagType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *tag_type = ir_build_tag_type(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, tag_type, lval); } case BuiltinFnIdFieldParentPtr: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; IrInstruction *field_parent_ptr = ir_build_field_parent_ptr(irb, scope, node, arg0_value, arg1_value, arg2_value, nullptr); return ir_lval_wrap(irb, scope, field_parent_ptr, lval); } case BuiltinFnIdOffsetOf: { 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_offset_of(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, offset_of, lval); } case BuiltinFnIdInlineCall: case BuiltinFnIdNoInlineCall: { if (node->data.fn_call_expr.params.length == 0) { add_node_error(irb->codegen, node, buf_sprintf("expected at least 1 argument, found 0")); return irb->codegen->invalid_instruction; } AstNode *fn_ref_node = node->data.fn_call_expr.params.at(0); IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); if (fn_ref == irb->codegen->invalid_instruction) return fn_ref; size_t arg_count = node->data.fn_call_expr.params.length - 1; IrInstruction **args = allocate(arg_count); for (size_t i = 0; i < arg_count; i += 1) { AstNode *arg_node = node->data.fn_call_expr.params.at(i + 1); args[i] = ir_gen_node(irb, arg_node, scope); if (args[i] == irb->codegen->invalid_instruction) return args[i]; } FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever; IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr, nullptr); return ir_lval_wrap(irb, scope, call, lval); } case BuiltinFnIdNewStackCall: { if (node->data.fn_call_expr.params.length == 0) { add_node_error(irb->codegen, node, buf_sprintf("expected at least 1 argument, found 0")); return irb->codegen->invalid_instruction; } AstNode *new_stack_node = node->data.fn_call_expr.params.at(0); IrInstruction *new_stack = ir_gen_node(irb, new_stack_node, scope); if (new_stack == irb->codegen->invalid_instruction) return new_stack; AstNode *fn_ref_node = node->data.fn_call_expr.params.at(1); IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); if (fn_ref == irb->codegen->invalid_instruction) return fn_ref; size_t arg_count = node->data.fn_call_expr.params.length - 2; IrInstruction **args = allocate(arg_count); for (size_t i = 0; i < arg_count; i += 1) { AstNode *arg_node = node->data.fn_call_expr.params.at(i + 2); args[i] = ir_gen_node(irb, arg_node, scope); if (args[i] == irb->codegen->invalid_instruction) return args[i]; } IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, false, nullptr, new_stack); return ir_lval_wrap(irb, scope, call, lval); } case BuiltinFnIdTypeId: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *type_id = ir_build_type_id(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, type_id, lval); } case BuiltinFnIdShlExact: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, arg0_value, arg1_value, true); return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdShrExact: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, arg0_value, arg1_value, true); return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdSetEvalBranchQuota: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *set_eval_branch_quota = ir_build_set_eval_branch_quota(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, set_eval_branch_quota, lval); } case BuiltinFnIdAlignCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *align_cast = ir_build_align_cast(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, align_cast, lval); } case BuiltinFnIdOpaqueType: { IrInstruction *opaque_type = ir_build_opaque_type(irb, scope, node); return ir_lval_wrap(irb, scope, opaque_type, lval); } case BuiltinFnIdThis: { IrInstruction *this_inst = ir_gen_this(irb, scope, node); return ir_lval_wrap(irb, scope, this_inst, lval); } case BuiltinFnIdSetAlignStack: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *set_align_stack = ir_build_set_align_stack(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, set_align_stack, lval); } case BuiltinFnIdArgType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *arg_type = ir_build_arg_type(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, arg_type, lval); } case BuiltinFnIdExport: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; IrInstruction *ir_export = ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value); return ir_lval_wrap(irb, scope, ir_export, lval); } case BuiltinFnIdErrorReturnTrace: { IrInstruction *error_return_trace = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::Null); return ir_lval_wrap(irb, scope, error_return_trace, lval); } case BuiltinFnIdAtomicRmw: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; AstNode *arg3_node = node->data.fn_call_expr.params.at(3); IrInstruction *arg3_value = ir_gen_node(irb, arg3_node, scope); if (arg3_value == irb->codegen->invalid_instruction) return arg3_value; AstNode *arg4_node = node->data.fn_call_expr.params.at(4); IrInstruction *arg4_value = ir_gen_node(irb, arg4_node, scope); if (arg4_value == irb->codegen->invalid_instruction) return arg4_value; return ir_build_atomic_rmw(irb, scope, node, arg0_value, arg1_value, arg2_value, arg3_value, arg4_value, // these 2 values don't mean anything since we passed non-null values for other args AtomicRmwOp_xchg, AtomicOrderMonotonic); } case BuiltinFnIdAtomicLoad: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; return ir_build_atomic_load(irb, scope, node, arg0_value, arg1_value, arg2_value, // this value does not mean anything since we passed non-null values for other arg AtomicOrderMonotonic); } case BuiltinFnIdIntToEnum: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *result = ir_build_int_to_enum(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval); } case BuiltinFnIdEnumToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *result = ir_build_enum_to_int(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, result, lval); } } zig_unreachable(); } static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { assert(node->type == NodeTypeFnCallExpr); if (node->data.fn_call_expr.is_builtin) return ir_gen_builtin_fn_call(irb, scope, node, lval); AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr; IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); if (fn_ref == irb->codegen->invalid_instruction) return fn_ref; size_t arg_count = node->data.fn_call_expr.params.length; IrInstruction **args = allocate(arg_count); for (size_t i = 0; i < arg_count; i += 1) { AstNode *arg_node = node->data.fn_call_expr.params.at(i); args[i] = ir_gen_node(irb, arg_node, scope); if (args[i] == irb->codegen->invalid_instruction) return args[i]; } bool is_async = node->data.fn_call_expr.is_async; IrInstruction *async_allocator = nullptr; if (is_async) { if (node->data.fn_call_expr.async_allocator) { async_allocator = ir_gen_node(irb, node->data.fn_call_expr.async_allocator, scope); if (async_allocator == irb->codegen->invalid_instruction) return async_allocator; } } IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator, nullptr); return ir_lval_wrap(irb, scope, fn_call, lval); } static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeIfBoolExpr); IrInstruction *condition = ir_gen_node(irb, node->data.if_bool_expr.condition, scope); if (condition == irb->codegen->invalid_instruction) return condition; IrInstruction *is_comptime; if (ir_should_inline(irb->exec, scope)) { is_comptime = ir_build_const_bool(irb, scope, node, true); } else { is_comptime = ir_build_test_comptime(irb, scope, node, condition); } AstNode *then_node = node->data.if_bool_expr.then_block; AstNode *else_node = node->data.if_bool_expr.else_node; IrBasicBlock *then_block = ir_create_basic_block(irb, scope, "Then"); IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "Else"); IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "EndIf"); ir_build_cond_br(irb, scope, condition->source_node, condition, then_block, else_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, then_block); Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime); IrInstruction *then_expr_result = ir_gen_node(irb, then_node, subexpr_scope); if (then_expr_result == irb->codegen->invalid_instruction) return then_expr_result; IrBasicBlock *after_then_block = irb->current_basic_block; if (!instr_is_unreachable(then_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, else_block); IrInstruction *else_expr_result; if (else_node) { else_expr_result = ir_gen_node(irb, else_node, subexpr_scope); if (else_expr_result == irb->codegen->invalid_instruction) return else_expr_result; } else { else_expr_result = ir_build_const_void(irb, scope, node); } IrBasicBlock *after_else_block = irb->current_basic_block; if (!instr_is_unreachable(else_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, endif_block); IrInstruction **incoming_values = allocate(2); incoming_values[0] = then_expr_result; incoming_values[1] = else_expr_result; IrBasicBlock **incoming_blocks = allocate(2); incoming_blocks[0] = after_then_block; incoming_blocks[1] = after_else_block; return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } static IrInstruction *ir_gen_prefix_op_id_lval(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id, LVal lval) { assert(node->type == NodeTypePrefixOpExpr); AstNode *expr_node = node->data.prefix_op_expr.primary_expr; IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval); if (value == irb->codegen->invalid_instruction) return value; return ir_build_un_op(irb, scope, node, op_id, value); } static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id) { return ir_gen_prefix_op_id_lval(irb, scope, node, op_id, LValNone); } static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval) { if (lval != LValPtr) return value; if (value == irb->codegen->invalid_instruction) return value; // We needed a pointer to a value, but we got a value. So we create // an instruction which just makes a const pointer of it. return ir_build_ref(irb, scope, value->source_node, value, false, false); } static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypePointerType); PtrLen ptr_len = (node->data.pointer_type.star_token->id == TokenIdStar || node->data.pointer_type.star_token->id == TokenIdStarStar) ? PtrLenSingle : PtrLenUnknown; bool is_const = node->data.pointer_type.is_const; bool is_volatile = node->data.pointer_type.is_volatile; AstNode *expr_node = node->data.pointer_type.op_expr; AstNode *align_expr = node->data.pointer_type.align_expr; IrInstruction *align_value; if (align_expr != nullptr) { align_value = ir_gen_node(irb, align_expr, scope); if (align_value == irb->codegen->invalid_instruction) return align_value; } else { align_value = nullptr; } IrInstruction *child_type = ir_gen_node(irb, expr_node, scope); if (child_type == irb->codegen->invalid_instruction) return child_type; uint32_t bit_offset_start = 0; if (node->data.pointer_type.bit_offset_start != nullptr) { if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_start, 32, false)) { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_start, 10); exec_add_error_node(irb->codegen, irb->exec, node, buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf))); return irb->codegen->invalid_instruction; } bit_offset_start = bigint_as_unsigned(node->data.pointer_type.bit_offset_start); } uint32_t bit_offset_end = 0; if (node->data.pointer_type.bit_offset_end != nullptr) { if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_end, 32, false)) { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_end, 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_end = bigint_as_unsigned(node->data.pointer_type.bit_offset_end); } if ((bit_offset_start != 0 || bit_offset_end != 0) && bit_offset_start >= bit_offset_end) { exec_add_error_node(irb->codegen, irb->exec, node, buf_sprintf("bit offset start must be less than bit offset end")); return irb->codegen->invalid_instruction; } return ir_build_ptr_type(irb, scope, node, child_type, is_const, is_volatile, ptr_len, align_value, bit_offset_start, bit_offset_end); } static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, LVal lval) { IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, scope, source_node, err_union_ptr, true); if (payload_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; if (lval == LValPtr) return payload_ptr; return ir_build_load_ptr(irb, scope, source_node, payload_ptr); } static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypePrefixOpExpr); AstNode *expr_node = node->data.prefix_op_expr.primary_expr; IrInstruction *value = ir_gen_node(irb, expr_node, scope); if (value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; return ir_build_bool_not(irb, scope, node, value); } static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { assert(node->type == NodeTypePrefixOpExpr); PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op; switch (prefix_op) { case PrefixOpInvalid: zig_unreachable(); case PrefixOpBoolNot: return ir_lval_wrap(irb, scope, ir_gen_bool_not(irb, scope, node), lval); case PrefixOpBinNot: return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpBinNot), lval); case PrefixOpNegation: return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation), lval); case PrefixOpNegationWrap: return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval); case PrefixOpOptional: return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval); case PrefixOpAddrOf: { AstNode *expr_node = node->data.prefix_op_expr.primary_expr; return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LValPtr), lval); } } zig_unreachable(); } static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeContainerInitExpr); AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr; ContainerInitKind kind = container_init_expr->kind; IrInstruction *container_type = ir_gen_node(irb, container_init_expr->type, scope); if (container_type == irb->codegen->invalid_instruction) return container_type; if (kind == ContainerInitKindStruct) { size_t field_count = container_init_expr->entries.length; IrInstructionContainerInitFieldsField *fields = allocate(field_count); for (size_t i = 0; i < field_count; i += 1) { AstNode *entry_node = container_init_expr->entries.at(i); assert(entry_node->type == NodeTypeStructValueField); Buf *name = entry_node->data.struct_val_field.name; AstNode *expr_node = entry_node->data.struct_val_field.expr; IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope); if (expr_value == irb->codegen->invalid_instruction) return expr_value; fields[i].name = name; fields[i].value = expr_value; fields[i].source_node = entry_node; } return ir_build_container_init_fields(irb, scope, node, container_type, field_count, fields); } else if (kind == ContainerInitKindArray) { size_t item_count = container_init_expr->entries.length; IrInstruction **values = allocate(item_count); for (size_t i = 0; i < item_count; i += 1) { AstNode *expr_node = container_init_expr->entries.at(i); IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope); if (expr_value == irb->codegen->invalid_instruction) return expr_value; values[i] = expr_value; } return ir_build_container_init_list(irb, scope, node, container_type, item_count, values); } else { zig_unreachable(); } } static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeVariableDeclaration); AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration; if (buf_eql_str(variable_declaration->symbol, "_")) { add_node_error(irb->codegen, node, buf_sprintf("`_` is not a declarable symbol")); return irb->codegen->invalid_instruction; } IrInstruction *type_instruction; if (variable_declaration->type != nullptr) { type_instruction = ir_gen_node(irb, variable_declaration->type, scope); if (type_instruction == irb->codegen->invalid_instruction) return type_instruction; } else { type_instruction = nullptr; } bool is_shadowable = false; bool is_const = variable_declaration->is_const; bool is_extern = variable_declaration->is_extern; IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime); ZigVar *var = ir_create_var(irb, node, scope, variable_declaration->symbol, is_const, is_const, is_shadowable, is_comptime); // we detect IrInstructionIdDeclVar in gen_block to make sure the next node // is inside var->child_scope if (!is_extern && !variable_declaration->expr) { var->value->type = irb->codegen->builtin_types.entry_invalid; add_node_error(irb->codegen, node, buf_sprintf("variables must be initialized")); return irb->codegen->invalid_instruction; } IrInstruction *align_value = nullptr; if (variable_declaration->align_expr != nullptr) { align_value = ir_gen_node(irb, variable_declaration->align_expr, scope); if (align_value == irb->codegen->invalid_instruction) return align_value; } if (variable_declaration->section_expr != nullptr) { add_node_error(irb->codegen, variable_declaration->section_expr, buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol))); } // Temporarily set the name of the IrExecutable to the VariableDeclaration // so that the struct or enum from the init expression inherits the name. Buf *old_exec_name = irb->exec->name; irb->exec->name = variable_declaration->symbol; IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope); irb->exec->name = old_exec_name; if (init_value == irb->codegen->invalid_instruction) return init_value; return ir_build_var_decl(irb, scope, node, var, type_instruction, align_value, init_value); } static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeWhileExpr); AstNode *continue_expr_node = node->data.while_expr.continue_expr; AstNode *else_node = node->data.while_expr.else_node; IrBasicBlock *cond_block = ir_create_basic_block(irb, scope, "WhileCond"); IrBasicBlock *body_block = ir_create_basic_block(irb, scope, "WhileBody"); IrBasicBlock *continue_block = continue_expr_node ? ir_create_basic_block(irb, scope, "WhileContinue") : cond_block; IrBasicBlock *end_block = ir_create_basic_block(irb, scope, "WhileEnd"); IrBasicBlock *else_block = else_node ? ir_create_basic_block(irb, scope, "WhileElse") : end_block; IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, ir_should_inline(irb->exec, scope) || node->data.while_expr.is_inline); ir_build_br(irb, scope, node, cond_block, is_comptime); Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime); Buf *var_symbol = node->data.while_expr.var_symbol; Buf *err_symbol = node->data.while_expr.err_symbol; if (err_symbol != nullptr) { ir_set_cursor_at_end_and_append_block(irb, cond_block); Scope *payload_scope; AstNode *symbol_node = node; // TODO make more accurate ZigVar *payload_var; if (var_symbol) { // TODO make it an error to write to payload variable payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol, true, false, false, is_comptime); payload_scope = payload_var->child_scope; } else { payload_scope = subexpr_scope; } IrInstruction *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope, LValPtr); if (err_val_ptr == irb->codegen->invalid_instruction) return err_val_ptr; IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr); IrInstruction *is_err = ir_build_test_err(irb, scope, node->data.while_expr.condition, err_val); IrBasicBlock *after_cond_block = irb->current_basic_block; IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); if (!instr_is_unreachable(is_err)) { ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_err, else_block, body_block, is_comptime)); } ir_set_cursor_at_end_and_append_block(irb, body_block); if (var_symbol) { IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, payload_scope, symbol_node, err_val_ptr, false); IrInstruction *var_value = node->data.while_expr.var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, payload_scope, symbol_node, var_ptr_value); ir_build_var_decl(irb, payload_scope, symbol_node, payload_var, nullptr, nullptr, var_value); } ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; ScopeLoop *loop_scope = create_loop_scope(node, payload_scope); loop_scope->break_block = end_block; loop_scope->continue_block = continue_block; loop_scope->is_comptime = is_comptime; loop_scope->incoming_blocks = &incoming_blocks; loop_scope->incoming_values = &incoming_values; IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base); if (body_result == irb->codegen->invalid_instruction) return body_result; if (!instr_is_unreachable(body_result)) { ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, node->data.while_expr.body, body_result)); ir_mark_gen(ir_build_br(irb, payload_scope, node, continue_block, is_comptime)); } if (continue_expr_node) { ir_set_cursor_at_end_and_append_block(irb, continue_block); IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, payload_scope); if (expr_result == irb->codegen->invalid_instruction) return expr_result; if (!instr_is_unreachable(expr_result)) ir_mark_gen(ir_build_br(irb, payload_scope, node, cond_block, is_comptime)); } IrInstruction *else_result = nullptr; if (else_node) { ir_set_cursor_at_end_and_append_block(irb, else_block); // TODO make it an error to write to error variable AstNode *err_symbol_node = else_node; // TODO make more accurate ZigVar *err_var = ir_create_var(irb, err_symbol_node, scope, err_symbol, true, false, false, is_comptime); Scope *err_scope = err_var->child_scope; IrInstruction *err_var_value = ir_build_unwrap_err_code(irb, err_scope, err_symbol_node, err_val_ptr); ir_build_var_decl(irb, err_scope, symbol_node, err_var, nullptr, nullptr, err_var_value); else_result = ir_gen_node(irb, else_node, err_scope); if (else_result == irb->codegen->invalid_instruction) return else_result; if (!instr_is_unreachable(else_result)) ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); } IrBasicBlock *after_else_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, end_block); if (else_result) { incoming_blocks.append(after_else_block); incoming_values.append(else_result); } else { incoming_blocks.append(after_cond_block); incoming_values.append(void_else_result); } return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } else if (var_symbol != nullptr) { ir_set_cursor_at_end_and_append_block(irb, cond_block); Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime); // TODO make it an error to write to payload variable AstNode *symbol_node = node; // TODO make more accurate ZigVar *payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol, true, false, false, is_comptime); Scope *child_scope = payload_var->child_scope; IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope, LValPtr); if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr); IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node->data.while_expr.condition, maybe_val); IrBasicBlock *after_cond_block = irb->current_basic_block; IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); if (!instr_is_unreachable(is_non_null)) { ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_non_null, body_block, else_block, is_comptime)); } ir_set_cursor_at_end_and_append_block(irb, body_block); IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, child_scope, symbol_node, maybe_val_ptr, false); IrInstruction *var_value = node->data.while_expr.var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, child_scope, symbol_node, var_ptr_value); ir_build_var_decl(irb, child_scope, symbol_node, payload_var, nullptr, nullptr, var_value); ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; ScopeLoop *loop_scope = create_loop_scope(node, child_scope); loop_scope->break_block = end_block; loop_scope->continue_block = continue_block; loop_scope->is_comptime = is_comptime; loop_scope->incoming_blocks = &incoming_blocks; loop_scope->incoming_values = &incoming_values; IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base); if (body_result == irb->codegen->invalid_instruction) return body_result; if (!instr_is_unreachable(body_result)) { ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.while_expr.body, body_result)); ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime)); } if (continue_expr_node) { ir_set_cursor_at_end_and_append_block(irb, continue_block); IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, child_scope); if (expr_result == irb->codegen->invalid_instruction) return expr_result; if (!instr_is_unreachable(expr_result)) ir_mark_gen(ir_build_br(irb, child_scope, node, cond_block, is_comptime)); } IrInstruction *else_result = nullptr; if (else_node) { ir_set_cursor_at_end_and_append_block(irb, else_block); else_result = ir_gen_node(irb, else_node, scope); if (else_result == irb->codegen->invalid_instruction) return else_result; if (!instr_is_unreachable(else_result)) ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); } IrBasicBlock *after_else_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, end_block); if (else_result) { incoming_blocks.append(after_else_block); incoming_values.append(else_result); } else { incoming_blocks.append(after_cond_block); incoming_values.append(void_else_result); } return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } else { ir_set_cursor_at_end_and_append_block(irb, cond_block); IrInstruction *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope); if (cond_val == irb->codegen->invalid_instruction) return cond_val; IrBasicBlock *after_cond_block = irb->current_basic_block; IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); if (!instr_is_unreachable(cond_val)) { ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val, body_block, else_block, is_comptime)); } ir_set_cursor_at_end_and_append_block(irb, body_block); ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime); ScopeLoop *loop_scope = create_loop_scope(node, subexpr_scope); loop_scope->break_block = end_block; loop_scope->continue_block = continue_block; loop_scope->is_comptime = is_comptime; loop_scope->incoming_blocks = &incoming_blocks; loop_scope->incoming_values = &incoming_values; IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base); if (body_result == irb->codegen->invalid_instruction) return body_result; if (!instr_is_unreachable(body_result)) { ir_mark_gen(ir_build_check_statement_is_void(irb, scope, node->data.while_expr.body, body_result)); ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime)); } if (continue_expr_node) { ir_set_cursor_at_end_and_append_block(irb, continue_block); IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, subexpr_scope); if (expr_result == irb->codegen->invalid_instruction) return expr_result; if (!instr_is_unreachable(expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, cond_block, is_comptime)); } IrInstruction *else_result = nullptr; if (else_node) { ir_set_cursor_at_end_and_append_block(irb, else_block); else_result = ir_gen_node(irb, else_node, subexpr_scope); if (else_result == irb->codegen->invalid_instruction) return else_result; if (!instr_is_unreachable(else_result)) ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); } IrBasicBlock *after_else_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, end_block); if (else_result) { incoming_blocks.append(after_else_block); incoming_values.append(else_result); } else { incoming_blocks.append(after_cond_block); incoming_values.append(void_else_result); } return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } } static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeForExpr); AstNode *array_node = node->data.for_expr.array_expr; AstNode *elem_node = node->data.for_expr.elem_node; AstNode *index_node = node->data.for_expr.index_node; AstNode *body_node = node->data.for_expr.body; AstNode *else_node = node->data.for_expr.else_node; if (!elem_node) { add_node_error(irb->codegen, node, buf_sprintf("for loop expression missing element parameter")); return irb->codegen->invalid_instruction; } assert(elem_node->type == NodeTypeSymbol); IrInstruction *array_val_ptr = ir_gen_node_extra(irb, array_node, parent_scope, LValPtr); if (array_val_ptr == irb->codegen->invalid_instruction) return array_val_ptr; IrInstruction *array_val = ir_build_load_ptr(irb, parent_scope, array_node, array_val_ptr); IrInstruction *pointer_type = ir_build_to_ptr_type(irb, parent_scope, array_node, array_val); IrInstruction *elem_var_type; if (node->data.for_expr.elem_is_ptr) { elem_var_type = pointer_type; } else { elem_var_type = ir_build_ptr_type_child(irb, parent_scope, elem_node, pointer_type); } IrInstruction *is_comptime = ir_build_const_bool(irb, parent_scope, node, ir_should_inline(irb->exec, parent_scope) || node->data.for_expr.is_inline); // TODO make it an error to write to element variable or i variable. Buf *elem_var_name = elem_node->data.symbol_expr.symbol; ZigVar *elem_var = ir_create_var(irb, elem_node, parent_scope, elem_var_name, true, false, false, is_comptime); Scope *child_scope = elem_var->child_scope; IrInstruction *undefined_value = ir_build_const_undefined(irb, child_scope, elem_node); ir_build_var_decl(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, undefined_value); IrInstruction *elem_var_ptr = ir_build_var_ptr(irb, child_scope, node, elem_var); AstNode *index_var_source_node; ZigVar *index_var; if (index_node) { index_var_source_node = index_node; Buf *index_var_name = index_node->data.symbol_expr.symbol; index_var = ir_create_var(irb, index_node, child_scope, index_var_name, true, false, false, is_comptime); } else { index_var_source_node = node; index_var = ir_create_var(irb, node, child_scope, nullptr, true, false, true, is_comptime); } child_scope = index_var->child_scope; IrInstruction *usize = ir_build_const_type(irb, child_scope, node, irb->codegen->builtin_types.entry_usize); IrInstruction *zero = ir_build_const_usize(irb, child_scope, node, 0); IrInstruction *one = ir_build_const_usize(irb, child_scope, node, 1); ir_build_var_decl(irb, child_scope, index_var_source_node, index_var, usize, nullptr, zero); IrInstruction *index_ptr = ir_build_var_ptr(irb, child_scope, node, index_var); IrBasicBlock *cond_block = ir_create_basic_block(irb, child_scope, "ForCond"); IrBasicBlock *body_block = ir_create_basic_block(irb, child_scope, "ForBody"); IrBasicBlock *end_block = ir_create_basic_block(irb, child_scope, "ForEnd"); IrBasicBlock *else_block = else_node ? ir_create_basic_block(irb, child_scope, "ForElse") : end_block; IrBasicBlock *continue_block = ir_create_basic_block(irb, child_scope, "ForContinue"); IrInstruction *len_val = ir_build_array_len(irb, child_scope, node, array_val); ir_build_br(irb, child_scope, node, cond_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, cond_block); IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_ptr); IrInstruction *cond = ir_build_bin_op(irb, child_scope, node, IrBinOpCmpLessThan, index_val, len_val, false); IrBasicBlock *after_cond_block = irb->current_basic_block; IrInstruction *void_else_value = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); ir_mark_gen(ir_build_cond_br(irb, child_scope, node, cond, body_block, else_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, body_block); IrInstruction *elem_ptr = ir_build_elem_ptr(irb, child_scope, node, array_val_ptr, index_val, false, PtrLenSingle); IrInstruction *elem_val; if (node->data.for_expr.elem_is_ptr) { elem_val = elem_ptr; } else { elem_val = ir_build_load_ptr(irb, child_scope, node, elem_ptr); } ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, elem_var_ptr, elem_val)); ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; ScopeLoop *loop_scope = create_loop_scope(node, child_scope); loop_scope->break_block = end_block; loop_scope->continue_block = continue_block; loop_scope->is_comptime = is_comptime; loop_scope->incoming_blocks = &incoming_blocks; loop_scope->incoming_values = &incoming_values; IrInstruction *body_result = ir_gen_node(irb, body_node, &loop_scope->base); if (!instr_is_unreachable(body_result)) ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, continue_block); IrInstruction *new_index_val = ir_build_bin_op(irb, child_scope, node, IrBinOpAdd, index_val, one, false); ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, index_ptr, new_index_val)); ir_build_br(irb, child_scope, node, cond_block, is_comptime); IrInstruction *else_result = nullptr; if (else_node) { ir_set_cursor_at_end_and_append_block(irb, else_block); else_result = ir_gen_node(irb, else_node, parent_scope); if (else_result == irb->codegen->invalid_instruction) return else_result; if (!instr_is_unreachable(else_result)) ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); } IrBasicBlock *after_else_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, end_block); if (else_result) { incoming_blocks.append(after_else_block); incoming_values.append(else_result); } else { incoming_blocks.append(after_cond_block); incoming_values.append(void_else_value); } return ir_build_phi(irb, parent_scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } static IrInstruction *ir_gen_bool_literal(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeBoolLiteral); return ir_build_const_bool(irb, scope, node, node->data.bool_literal.value); } static IrInstruction *ir_gen_string_literal(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeStringLiteral); if (node->data.string_literal.c) { return ir_build_const_c_str_lit(irb, scope, node, node->data.string_literal.buf); } else { return ir_build_const_str_lit(irb, scope, node, node->data.string_literal.buf); } } static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeArrayType); AstNode *size_node = node->data.array_type.size; AstNode *child_type_node = node->data.array_type.child_type; bool is_const = node->data.array_type.is_const; bool is_volatile = node->data.array_type.is_volatile; AstNode *align_expr = node->data.array_type.align_expr; 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 (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, scope); if (size_value == irb->codegen->invalid_instruction) return size_value; IrInstruction *child_type = ir_gen_node(irb, child_type_node, scope); if (child_type == irb->codegen->invalid_instruction) return child_type; return ir_build_array_type(irb, scope, node, size_value, child_type); } else { IrInstruction *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, child_type_node, scope); if (child_type == irb->codegen->invalid_instruction) return child_type; return ir_build_slice_type(irb, scope, node, child_type, is_const, is_volatile, align_value); } } static IrInstruction *ir_gen_promise_type(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypePromiseType); AstNode *payload_type_node = node->data.promise_type.payload_type; IrInstruction *payload_type_value = nullptr; if (payload_type_node != nullptr) { payload_type_value = ir_gen_node(irb, payload_type_node, scope); if (payload_type_value == irb->codegen->invalid_instruction) return payload_type_value; } return ir_build_promise_type(irb, scope, node, payload_type_value); } static IrInstruction *ir_gen_undefined_literal(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeUndefinedLiteral); return ir_build_const_undefined(irb, scope, node); } static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeAsmExpr); IrInstruction **input_list = allocate(node->data.asm_expr.input_list.length); IrInstruction **output_types = allocate(node->data.asm_expr.output_list.length); ZigVar **output_vars = allocate(node->data.asm_expr.output_list.length); size_t return_count = 0; bool is_volatile = node->data.asm_expr.is_volatile; if (!is_volatile && node->data.asm_expr.output_list.length == 0) { add_node_error(irb->codegen, node, buf_sprintf("assembly expression with no output must be marked volatile")); return irb->codegen->invalid_instruction; } for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1) { AsmOutput *asm_output = node->data.asm_expr.output_list.at(i); if (asm_output->return_type) { return_count += 1; IrInstruction *return_type = ir_gen_node(irb, asm_output->return_type, scope); if (return_type == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; if (return_count > 1) { add_node_error(irb->codegen, node, buf_sprintf("inline assembly allows up to one output value")); return irb->codegen->invalid_instruction; } output_types[i] = return_type; } else { Buf *variable_name = asm_output->variable_name; // 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; } } } for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1) { AsmInput *asm_input = node->data.asm_expr.input_list.at(i); IrInstruction *input_value = ir_gen_node(irb, asm_input->expr, scope); if (input_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; input_list[i] = input_value; } return ir_build_asm(irb, scope, node, input_list, output_types, output_vars, return_count, is_volatile); } static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeTestExpr); Buf *var_symbol = node->data.test_expr.var_symbol; AstNode *expr_node = node->data.test_expr.target_node; AstNode *then_node = node->data.test_expr.then_node; AstNode *else_node = node->data.test_expr.else_node; bool var_is_ptr = node->data.test_expr.var_is_ptr; IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr); IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_val); IrBasicBlock *then_block = ir_create_basic_block(irb, scope, "OptionalThen"); IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "OptionalElse"); IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "OptionalEndIf"); IrInstruction *is_comptime; if (ir_should_inline(irb->exec, scope)) { is_comptime = ir_build_const_bool(irb, scope, node, true); } else { is_comptime = ir_build_test_comptime(irb, scope, node, is_non_null); } ir_build_cond_br(irb, scope, node, is_non_null, then_block, else_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, then_block); Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime); Scope *var_scope; if (var_symbol) { IrInstruction *var_type = nullptr; bool is_shadowable = false; bool is_const = true; ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_symbol, is_const, is_const, is_shadowable, is_comptime); IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, subexpr_scope, node, maybe_val_ptr, false); IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value); ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); var_scope = var->child_scope; } else { var_scope = subexpr_scope; } IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope); if (then_expr_result == irb->codegen->invalid_instruction) return then_expr_result; IrBasicBlock *after_then_block = irb->current_basic_block; if (!instr_is_unreachable(then_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, else_block); IrInstruction *else_expr_result; if (else_node) { else_expr_result = ir_gen_node(irb, else_node, subexpr_scope); if (else_expr_result == irb->codegen->invalid_instruction) return else_expr_result; } else { else_expr_result = ir_build_const_void(irb, scope, node); } IrBasicBlock *after_else_block = irb->current_basic_block; if (!instr_is_unreachable(else_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, endif_block); IrInstruction **incoming_values = allocate(2); incoming_values[0] = then_expr_result; incoming_values[1] = else_expr_result; IrBasicBlock **incoming_blocks = allocate(2); incoming_blocks[0] = after_then_block; incoming_blocks[1] = after_else_block; return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeIfErrorExpr); AstNode *target_node = node->data.if_err_expr.target_node; AstNode *then_node = node->data.if_err_expr.then_node; AstNode *else_node = node->data.if_err_expr.else_node; bool var_is_ptr = node->data.if_err_expr.var_is_ptr; bool var_is_const = true; Buf *var_symbol = node->data.if_err_expr.var_symbol; Buf *err_symbol = node->data.if_err_expr.err_symbol; IrInstruction *err_val_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr); if (err_val_ptr == irb->codegen->invalid_instruction) return err_val_ptr; IrInstruction *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr); IrInstruction *is_err = ir_build_test_err(irb, scope, node, err_val); IrBasicBlock *ok_block = ir_create_basic_block(irb, scope, "TryOk"); IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "TryElse"); IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "TryEnd"); bool force_comptime = ir_should_inline(irb->exec, scope); IrInstruction *is_comptime = force_comptime ? ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, is_err); ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, ok_block); Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime); Scope *var_scope; if (var_symbol) { IrInstruction *var_type = nullptr; bool is_shadowable = false; IrInstruction *var_is_comptime = force_comptime ? ir_build_const_bool(irb, subexpr_scope, node, true) : ir_build_test_comptime(irb, subexpr_scope, node, err_val); ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_symbol, var_is_const, var_is_const, is_shadowable, var_is_comptime); IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, subexpr_scope, node, err_val_ptr, false); IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value); ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); var_scope = var->child_scope; } else { var_scope = subexpr_scope; } IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope); if (then_expr_result == irb->codegen->invalid_instruction) return then_expr_result; IrBasicBlock *after_then_block = irb->current_basic_block; if (!instr_is_unreachable(then_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, else_block); IrInstruction *else_expr_result; if (else_node) { Scope *err_var_scope; if (err_symbol) { IrInstruction *var_type = nullptr; bool is_shadowable = false; bool is_const = true; ZigVar *var = ir_create_var(irb, node, subexpr_scope, err_symbol, is_const, is_const, is_shadowable, is_comptime); IrInstruction *var_value = ir_build_unwrap_err_code(irb, subexpr_scope, node, err_val_ptr); ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); err_var_scope = var->child_scope; } else { err_var_scope = subexpr_scope; } else_expr_result = ir_gen_node(irb, else_node, err_var_scope); if (else_expr_result == irb->codegen->invalid_instruction) return else_expr_result; } else { else_expr_result = ir_build_const_void(irb, scope, node); } IrBasicBlock *after_else_block = irb->current_basic_block; if (!instr_is_unreachable(else_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, endif_block); IrInstruction **incoming_values = allocate(2); incoming_values[0] = then_expr_result; incoming_values[1] = else_expr_result; IrBasicBlock **incoming_blocks = allocate(2); incoming_blocks[0] = after_then_block; incoming_blocks[1] = after_else_block; return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *switch_node, AstNode *prong_node, IrBasicBlock *end_block, IrInstruction *is_comptime, IrInstruction *var_is_comptime, IrInstruction *target_value_ptr, IrInstruction *prong_value, ZigList *incoming_blocks, ZigList *incoming_values) { assert(switch_node->type == NodeTypeSwitchExpr); assert(prong_node->type == NodeTypeSwitchProng); AstNode *expr_node = prong_node->data.switch_prong.expr; AstNode *var_symbol_node = prong_node->data.switch_prong.var_symbol; Scope *child_scope; if (var_symbol_node) { assert(var_symbol_node->type == NodeTypeSymbol); Buf *var_name = var_symbol_node->data.symbol_expr.symbol; bool var_is_ptr = prong_node->data.switch_prong.var_is_ptr; bool is_shadowable = false; bool is_const = true; ZigVar *var = ir_create_var(irb, var_symbol_node, scope, var_name, is_const, is_const, is_shadowable, var_is_comptime); child_scope = var->child_scope; IrInstruction *var_value; if (prong_value) { IrInstruction *var_ptr_value = ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr, prong_value); var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, var_symbol_node, var_ptr_value); } else { var_value = var_is_ptr ? target_value_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, target_value_ptr); } IrInstruction *var_type = nullptr; // infer the type ir_build_var_decl(irb, scope, var_symbol_node, var, var_type, nullptr, var_value); } else { child_scope = scope; } IrInstruction *expr_result = ir_gen_node(irb, expr_node, child_scope); if (expr_result == irb->codegen->invalid_instruction) return false; if (!instr_is_unreachable(expr_result)) ir_mark_gen(ir_build_br(irb, scope, switch_node, end_block, is_comptime)); incoming_blocks->append(irb->current_basic_block); incoming_values->append(expr_result); return true; } static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeSwitchExpr); AstNode *target_node = node->data.switch_expr.expr; IrInstruction *target_value_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr); if (target_value_ptr == irb->codegen->invalid_instruction) return target_value_ptr; IrInstruction *target_value = ir_build_switch_target(irb, scope, node, target_value_ptr); IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "SwitchElse"); IrBasicBlock *end_block = ir_create_basic_block(irb, scope, "SwitchEnd"); size_t prong_count = node->data.switch_expr.prongs.length; ZigList 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 incoming_values = {0}; ZigList incoming_blocks = {0}; ZigList check_ranges = {0}; // First do the else and the ranges Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime); Scope *comptime_scope = create_comptime_scope(node, scope); AstNode *else_prong = nullptr; for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) { AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); size_t prong_item_count = prong_node->data.switch_prong.items.length; if (prong_item_count == 0) { if (else_prong) { ErrorMsg *msg = add_node_error(irb->codegen, prong_node, buf_sprintf("multiple else prongs in switch expression")); add_error_note(irb->codegen, msg, else_prong, buf_sprintf("previous else prong is here")); return irb->codegen->invalid_instruction; } else_prong = prong_node; IrBasicBlock *prev_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, else_block); if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, is_comptime, var_is_comptime, target_value_ptr, nullptr, &incoming_blocks, &incoming_values)) { return irb->codegen->invalid_instruction; } ir_set_cursor_at_end(irb, prev_block); } else if (prong_node->data.switch_prong.any_items_are_range) { IrInstruction *ok_bit = nullptr; AstNode *last_item_node = nullptr; for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) { AstNode *item_node = prong_node->data.switch_prong.items.at(item_i); last_item_node = item_node; if (item_node->type == NodeTypeSwitchRange) { AstNode *start_node = item_node->data.switch_range.start; AstNode *end_node = item_node->data.switch_range.end; IrInstruction *start_value = ir_gen_node(irb, start_node, comptime_scope); if (start_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *end_value = ir_gen_node(irb, end_node, comptime_scope); if (end_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstructionCheckSwitchProngsRange *check_range = check_ranges.add_one(); check_range->start = start_value; check_range->end = end_value; IrInstruction *lower_range_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpGreaterOrEq, target_value, start_value, false); IrInstruction *upper_range_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpLessOrEq, target_value, end_value, false); IrInstruction *both_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolAnd, lower_range_ok, upper_range_ok, false); if (ok_bit) { ok_bit = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolOr, both_ok, ok_bit, false); } else { ok_bit = both_ok; } } else { IrInstruction *item_value = ir_gen_node(irb, item_node, comptime_scope); if (item_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstructionCheckSwitchProngsRange *check_range = check_ranges.add_one(); check_range->start = item_value; check_range->end = item_value; IrInstruction *cmp_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpEq, item_value, target_value, false); if (ok_bit) { ok_bit = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolOr, cmp_ok, ok_bit, false); } else { ok_bit = cmp_ok; } } } IrBasicBlock *range_block_yes = ir_create_basic_block(irb, scope, "SwitchRangeYes"); IrBasicBlock *range_block_no = ir_create_basic_block(irb, scope, "SwitchRangeNo"); assert(ok_bit); assert(last_item_node); ir_mark_gen(ir_build_cond_br(irb, scope, last_item_node, ok_bit, range_block_yes, range_block_no, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, range_block_yes); if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, is_comptime, var_is_comptime, target_value_ptr, nullptr, &incoming_blocks, &incoming_values)) { return irb->codegen->invalid_instruction; } ir_set_cursor_at_end_and_append_block(irb, range_block_no); } } // next do the non-else non-ranges for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) { AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); size_t prong_item_count = prong_node->data.switch_prong.items.length; if (prong_item_count == 0) continue; if (prong_node->data.switch_prong.any_items_are_range) continue; IrBasicBlock *prong_block = ir_create_basic_block(irb, scope, "SwitchProng"); IrInstruction *last_item_value = nullptr; for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) { AstNode *item_node = prong_node->data.switch_prong.items.at(item_i); assert(item_node->type != NodeTypeSwitchRange); IrInstruction *item_value = ir_gen_node(irb, item_node, comptime_scope); if (item_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstructionCheckSwitchProngsRange *check_range = check_ranges.add_one(); check_range->start = item_value; check_range->end = item_value; IrInstructionSwitchBrCase *this_case = cases.add_one(); this_case->value = item_value; this_case->block = prong_block; last_item_value = item_value; } IrInstruction *only_item_value = (prong_item_count == 1) ? last_item_value : nullptr; IrBasicBlock *prev_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, prong_block); if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, is_comptime, var_is_comptime, target_value_ptr, only_item_value, &incoming_blocks, &incoming_values)) { return irb->codegen->invalid_instruction; } ir_set_cursor_at_end(irb, prev_block); } IrInstruction *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value, check_ranges.items, check_ranges.length, else_prong != nullptr); if (cases.length == 0) { ir_build_br(irb, scope, node, else_block, is_comptime); } else { ir_build_switch_br(irb, scope, node, target_value, else_block, cases.length, cases.items, is_comptime, switch_prongs_void); } if (!else_prong) { ir_set_cursor_at_end_and_append_block(irb, else_block); ir_build_unreachable(irb, scope, node); } ir_set_cursor_at_end_and_append_block(irb, end_block); assert(incoming_blocks.length == incoming_values.length); if (incoming_blocks.length == 0) { return ir_build_const_void(irb, scope, node); } else { return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } } static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval) { assert(node->type == NodeTypeCompTime); Scope *child_scope = create_comptime_scope(node, parent_scope); return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval); } static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scope, AstNode *node, ScopeBlock *block_scope) { IrInstruction *is_comptime; if (ir_should_inline(irb->exec, break_scope)) { is_comptime = ir_build_const_bool(irb, break_scope, node, true); } else { is_comptime = block_scope->is_comptime; } IrInstruction *result_value; if (node->data.break_expr.expr) { result_value = ir_gen_node(irb, node->data.break_expr.expr, break_scope); if (result_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } else { result_value = ir_build_const_void(irb, break_scope, node); } IrBasicBlock *dest_block = block_scope->end_block; ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false); block_scope->incoming_blocks->append(irb->current_basic_block); block_scope->incoming_values->append(result_value); return ir_build_br(irb, break_scope, node, dest_block, is_comptime); } static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) { assert(node->type == NodeTypeBreak); // Search up the scope. We'll find one of these things first: // * function definition scope or global scope => error, break outside loop // * defer expression scope => error, cannot break out of defer expression // * loop scope => OK // * (if it's a labeled break) labeled block => OK Scope *search_scope = break_scope; ScopeLoop *loop_scope; for (;;) { if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) { if (node->data.break_expr.name != nullptr) { add_node_error(irb->codegen, node, buf_sprintf("label not found: '%s'", buf_ptr(node->data.break_expr.name))); return irb->codegen->invalid_instruction; } else { add_node_error(irb->codegen, node, buf_sprintf("break expression outside loop")); return irb->codegen->invalid_instruction; } } else if (search_scope->id == ScopeIdDeferExpr) { add_node_error(irb->codegen, node, buf_sprintf("cannot break out of defer expression")); return irb->codegen->invalid_instruction; } else if (search_scope->id == ScopeIdLoop) { ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope; if (node->data.break_expr.name == nullptr || (this_loop_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_loop_scope->name))) { loop_scope = this_loop_scope; break; } } else if (search_scope->id == ScopeIdBlock) { ScopeBlock *this_block_scope = (ScopeBlock *)search_scope; if (node->data.break_expr.name != nullptr && (this_block_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_block_scope->name))) { assert(this_block_scope->end_block != nullptr); return ir_gen_return_from_block(irb, break_scope, node, this_block_scope); } } else if (search_scope->id == ScopeIdSuspend) { add_node_error(irb->codegen, node, buf_sprintf("cannot break out of suspend block")); return irb->codegen->invalid_instruction; } search_scope = search_scope->parent; } IrInstruction *is_comptime; if (ir_should_inline(irb->exec, break_scope)) { is_comptime = ir_build_const_bool(irb, break_scope, node, true); } else { is_comptime = loop_scope->is_comptime; } IrInstruction *result_value; if (node->data.break_expr.expr) { result_value = ir_gen_node(irb, node->data.break_expr.expr, break_scope); if (result_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } else { result_value = ir_build_const_void(irb, break_scope, node); } IrBasicBlock *dest_block = loop_scope->break_block; ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false); loop_scope->incoming_blocks->append(irb->current_basic_block); loop_scope->incoming_values->append(result_value); return ir_build_br(irb, break_scope, node, dest_block, is_comptime); } static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, AstNode *node) { assert(node->type == NodeTypeContinue); // Search up the scope. We'll find one of these things first: // * function definition scope or global scope => error, break outside loop // * defer expression scope => error, cannot break out of defer expression // * loop scope => OK ZigList 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(node, parent_scope); node->data.defer.child_scope = &defer_child_scope->base; ScopeDeferExpr *defer_expr_scope = create_defer_expr_scope(node, parent_scope); node->data.defer.expr_scope = &defer_expr_scope->base; return ir_build_const_void(irb, parent_scope, node); } static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeSliceExpr); AstNodeSliceExpr *slice_expr = &node->data.slice_expr; AstNode *array_node = slice_expr->array_ref_expr; AstNode *start_node = slice_expr->start; AstNode *end_node = slice_expr->end; IrInstruction *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr); if (ptr_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *start_value = ir_gen_node(irb, start_node, scope); if (start_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *end_value; if (end_node) { end_value = ir_gen_node(irb, end_node, scope); if (end_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } else { end_value = nullptr; } return ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, true); } static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeUnwrapErrorExpr); AstNode *op1_node = node->data.unwrap_err_expr.op1; AstNode *op2_node = node->data.unwrap_err_expr.op2; AstNode *var_node = node->data.unwrap_err_expr.symbol; 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_err_assert_ok(irb, parent_scope, node, op1_node, LValNone); } IrInstruction *err_union_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr); if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *err_union_val = ir_build_load_ptr(irb, parent_scope, node, err_union_ptr); IrInstruction *is_err = ir_build_test_err(irb, parent_scope, node, err_union_val); IrInstruction *is_comptime; if (ir_should_inline(irb->exec, parent_scope)) { is_comptime = ir_build_const_bool(irb, parent_scope, node, true); } else { is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_err); } IrBasicBlock *ok_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrOk"); IrBasicBlock *err_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrError"); IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrEnd"); ir_build_cond_br(irb, parent_scope, node, is_err, err_block, ok_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, err_block); Scope *err_scope; if (var_node) { assert(var_node->type == NodeTypeSymbol); Buf *var_name = var_node->data.symbol_expr.symbol; bool is_const = true; bool is_shadowable = false; ZigVar *var = ir_create_var(irb, node, parent_scope, var_name, is_const, is_const, is_shadowable, is_comptime); err_scope = var->child_scope; IrInstruction *err_val = ir_build_unwrap_err_code(irb, err_scope, node, err_union_ptr); ir_build_var_decl(irb, err_scope, var_node, var, nullptr, nullptr, err_val); } else { err_scope = parent_scope; } IrInstruction *err_result = ir_gen_node(irb, op2_node, err_scope); if (err_result == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrBasicBlock *after_err_block = irb->current_basic_block; if (!instr_is_unreachable(err_result)) ir_mark_gen(ir_build_br(irb, err_scope, node, end_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, ok_block); IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, parent_scope, node, err_union_ptr, false); IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr); IrBasicBlock *after_ok_block = irb->current_basic_block; ir_build_br(irb, parent_scope, node, end_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, end_block); IrInstruction **incoming_values = allocate(2); incoming_values[0] = err_result; incoming_values[1] = unwrapped_payload; IrBasicBlock **incoming_blocks = allocate(2); incoming_blocks[0] = after_err_block; incoming_blocks[1] = after_ok_block; return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values); } static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *outer_scope, Scope *inner_scope) { if (inner_scope == nullptr || inner_scope == outer_scope) return false; bool need_comma = render_instance_name_recursive(codegen, name, outer_scope, inner_scope->parent); if (inner_scope->id != ScopeIdVarDecl) return need_comma; ScopeVarDecl *var_scope = (ScopeVarDecl *)inner_scope; if (need_comma) buf_append_char(name, ','); render_const_value(codegen, name, var_scope->var->value); return true; } static Buf *get_anon_type_name(CodeGen *codegen, IrExecutable *exec, const char *kind_name, AstNode *source_node) { if (exec->name) { return exec->name; } else if (exec->name_fn != nullptr) { Buf *name = buf_alloc(); buf_append_buf(name, &exec->name_fn->symbol_name); buf_appendf(name, "("); render_instance_name_recursive(codegen, name, &exec->name_fn->fndef_scope->base, exec->begin_scope); buf_appendf(name, ")"); return name; } else { //Note: C-imports do not have valid location information return buf_sprintf("(anonymous %s at %s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ")", kind_name, (source_node->owner->path != nullptr) ? buf_ptr(source_node->owner->path) : "(null)", source_node->line + 1, source_node->column + 1); } } static IrInstruction *ir_gen_container_decl(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeContainerDecl); ContainerKind kind = node->data.container_decl.kind; Buf *name = get_anon_type_name(irb->codegen, irb->exec, container_string(kind), node); VisibMod visib_mod = VisibModPub; TldContainer *tld_container = allocate(1); init_tld(&tld_container->base, TldIdContainer, name, visib_mod, node, parent_scope); ContainerLayout layout = node->data.container_decl.layout; ZigType *container_type = get_partial_container_type(irb->codegen, parent_scope, kind, node, buf_ptr(name), layout); ScopeDecls *child_scope = get_container_scope(container_type); tld_container->type_entry = container_type; tld_container->decls_scope = child_scope; for (size_t i = 0; i < node->data.container_decl.decls.length; i += 1) { AstNode *child_node = node->data.container_decl.decls.at(i); scan_decls(irb->codegen, child_scope, child_node); } irb->codegen->resolve_queue.append(&tld_container->base); // Add this to the list to mark as invalid if analyzing this exec fails. irb->exec->tld_list.append(&tld_container->base); return ir_build_const_type(irb, parent_scope, node, container_type); } // errors should be populated with set1's values static ZigType *get_error_set_union(CodeGen *g, ErrorTableEntry **errors, ZigType *set1, ZigType *set2) { assert(set1->id == ZigTypeIdErrorSet); assert(set2->id == ZigTypeIdErrorSet); ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); buf_resize(&err_set_type->name, 0); buf_appendf(&err_set_type->name, "error{"); for (uint32_t i = 0, count = set1->data.error_set.err_count; i < count; i += 1) { assert(errors[set1->data.error_set.errors[i]->value] == set1->data.error_set.errors[i]); } uint32_t count = set1->data.error_set.err_count; for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = set2->data.error_set.errors[i]; if (errors[error_entry->value] == nullptr) { count += 1; } } err_set_type->is_copyable = true; err_set_type->type_ref = g->builtin_types.entry_global_error_set->type_ref; err_set_type->di_type = g->builtin_types.entry_global_error_set->di_type; err_set_type->data.error_set.err_count = count; err_set_type->data.error_set.errors = allocate(count); for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = set1->data.error_set.errors[i]; buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name)); err_set_type->data.error_set.errors[i] = error_entry; } uint32_t index = set1->data.error_set.err_count; for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = set2->data.error_set.errors[i]; if (errors[error_entry->value] == nullptr) { errors[error_entry->value] = error_entry; buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name)); err_set_type->data.error_set.errors[index] = error_entry; index += 1; } } assert(index == count); assert(count != 0); buf_appendf(&err_set_type->name, "}"); g->error_di_types.append(&err_set_type->di_type); 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->is_copyable = true; err_set_type->type_ref = g->builtin_types.entry_global_error_set->type_ref; err_set_type->di_type = g->builtin_types.entry_global_error_set->di_type; err_set_type->data.error_set.err_count = 1; err_set_type->data.error_set.errors = allocate(1); g->error_di_types.append(&err_set_type->di_type); err_set_type->data.error_set.errors[0] = err_entry; return err_set_type; } static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeErrorSetDecl); uint32_t err_count = node->data.err_set_decl.decls.length; Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error set", node); ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); buf_init_from_buf(&err_set_type->name, type_name); err_set_type->is_copyable = true; err_set_type->data.error_set.err_count = err_count; err_set_type->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref; err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type; irb->codegen->error_di_types.append(&err_set_type->di_type); err_set_type->data.error_set.errors = allocate(err_count); ErrorTableEntry **errors = allocate(irb->codegen->errors_by_index.length + err_count); for (uint32_t i = 0; i < err_count; i += 1) { AstNode *symbol_node = node->data.err_set_decl.decls.at(i); assert(symbol_node->type == NodeTypeSymbol); Buf *err_name = symbol_node->data.symbol_expr.symbol; ErrorTableEntry *err = allocate(1); err->decl_node = symbol_node; buf_init_from_buf(&err->name, err_name); auto existing_entry = irb->codegen->error_table.put_unique(err_name, err); if (existing_entry) { err->value = existing_entry->value->value; } else { size_t error_value_count = irb->codegen->errors_by_index.length; assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)irb->codegen->err_tag_type->data.integral.bit_count)); err->value = error_value_count; irb->codegen->errors_by_index.append(err); irb->codegen->err_enumerators.append(ZigLLVMCreateDebugEnumerator(irb->codegen->dbuilder, buf_ptr(err_name), error_value_count)); } err_set_type->data.error_set.errors[i] = err; ErrorTableEntry *prev_err = errors[err->value]; if (prev_err != nullptr) { ErrorMsg *msg = add_node_error(irb->codegen, err->decl_node, buf_sprintf("duplicate error: '%s'", buf_ptr(&err->name))); add_error_note(irb->codegen, msg, prev_err->decl_node, buf_sprintf("other error here")); return irb->codegen->invalid_instruction; } errors[err->value] = err; } free(errors); return ir_build_const_type(irb, parent_scope, node, err_set_type); } static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeFnProto); size_t param_count = node->data.fn_proto.params.length; IrInstruction **param_types = allocate(param_count); bool is_var_args = false; for (size_t i = 0; i < param_count; i += 1) { AstNode *param_node = node->data.fn_proto.params.at(i); if (param_node->data.param_decl.is_var_args) { is_var_args = true; break; } if (param_node->data.param_decl.var_token == nullptr) { AstNode *type_node = param_node->data.param_decl.type; IrInstruction *type_value = ir_gen_node(irb, type_node, parent_scope); if (type_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; param_types[i] = type_value; } else { param_types[i] = nullptr; } } IrInstruction *align_value = nullptr; if (node->data.fn_proto.align_expr != nullptr) { align_value = ir_gen_node(irb, node->data.fn_proto.align_expr, parent_scope); if (align_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } IrInstruction *return_type; if (node->data.fn_proto.return_var_token == nullptr) { if (node->data.fn_proto.return_type == nullptr) { return_type = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_void); } else { return_type = ir_gen_node(irb, node->data.fn_proto.return_type, parent_scope); if (return_type == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } } else { add_node_error(irb->codegen, node, buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447")); return irb->codegen->invalid_instruction; //return_type = nullptr; } IrInstruction *async_allocator_type_value = nullptr; if (node->data.fn_proto.async_allocator_type != nullptr) { async_allocator_type_value = ir_gen_node(irb, node->data.fn_proto.async_allocator_type, parent_scope); if (async_allocator_type_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type, async_allocator_type_value, is_var_args); } static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *target_inst, bool cancel_non_suspended, bool cancel_awaited) { IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "CancelDone"); IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); IrBasicBlock *pre_return_block = ir_create_basic_block(irb, scope, "PreReturn"); IrBasicBlock *post_return_block = ir_create_basic_block(irb, scope, "PostReturn"); IrBasicBlock *do_cancel_block = ir_create_basic_block(irb, scope, "DoCancel"); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false); IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node, get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void)); IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 // TODO relies on Zig not re-ordering fields IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, atomic_state_field_name); // set the is_canceled bit IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, atomic_state_ptr, nullptr, is_canceled_mask, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); IrInstruction *awaiter_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); IrInstruction *is_returned_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, awaiter_addr, ptr_mask, false); ir_build_cond_br(irb, scope, node, is_returned_bool, post_return_block, pre_return_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, post_return_block); if (cancel_awaited) { ir_build_br(irb, scope, node, do_cancel_block, is_comptime); } else { IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false); IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false); ir_build_cond_br(irb, scope, node, is_awaited_bool, done_block, do_cancel_block, is_comptime); } ir_set_cursor_at_end_and_append_block(irb, pre_return_block); if (cancel_awaited) { if (cancel_non_suspended) { ir_build_br(irb, scope, node, do_cancel_block, is_comptime); } else { IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); ir_build_cond_br(irb, scope, node, is_suspended_bool, do_cancel_block, done_block, is_comptime); } } else { ir_build_br(irb, scope, node, done_block, is_comptime); } ir_set_cursor_at_end_and_append_block(irb, do_cancel_block); ir_build_cancel(irb, scope, node, target_inst); ir_build_br(irb, scope, node, done_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, done_block); return ir_build_const_void(irb, scope, node); } static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeCancel); IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, scope); if (target_inst == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; return ir_gen_cancel_target(irb, scope, node, target_inst, false, true); } static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *target_inst) { IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "ResumeDone"); IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "IsSuspended"); IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, scope, "IsNotSuspended"); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 IrInstruction *and_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, is_suspended_mask); IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false); IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node, get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void)); // TODO relies on Zig not re-ordering fields IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, atomic_state_field_name); // clear the is_suspended bit IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, atomic_state_ptr, nullptr, and_mask, nullptr, AtomicRmwOp_and, AtomicOrderSeqCst); IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, not_suspended_block); ir_build_unreachable(irb, scope, node); ir_set_cursor_at_end_and_append_block(irb, suspended_block); ir_build_coro_resume(irb, scope, node, target_inst); ir_build_br(irb, scope, node, done_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, done_block); return ir_build_const_void(irb, scope, node); } static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeResume); IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, scope); if (target_inst == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; return ir_gen_resume_target(irb, scope, node, target_inst); } static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeAwaitExpr); IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, scope); if (target_inst == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; ZigFn *fn_entry = exec_fn_entry(irb->exec); if (!fn_entry) { add_node_error(irb->codegen, node, buf_sprintf("await outside function definition")); return irb->codegen->invalid_instruction; } if (fn_entry->type_entry->data.fn.fn_type_id.cc != CallingConventionAsync) { add_node_error(irb->codegen, node, buf_sprintf("await in non-async function")); return irb->codegen->invalid_instruction; } ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(scope); if (scope_defer_expr) { if (!scope_defer_expr->reported_err) { add_node_error(irb->codegen, node, buf_sprintf("cannot await inside defer expression")); scope_defer_expr->reported_err = true; } return irb->codegen->invalid_instruction; } Scope *outer_scope = irb->exec->begin_scope; IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, target_inst); Buf *result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name); if (irb->codegen->have_err_ret_tracing) { IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull); Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME); IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name); ir_build_store_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr); } IrBasicBlock *already_awaited_block = ir_create_basic_block(irb, scope, "AlreadyAwaited"); IrBasicBlock *not_awaited_block = ir_create_basic_block(irb, scope, "NotAwaited"); IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, scope, "YesSuspend"); IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, scope, "NoSuspend"); IrBasicBlock *merge_block = ir_create_basic_block(irb, scope, "MergeSuspend"); IrBasicBlock *cleanup_block = ir_create_basic_block(irb, scope, "SuspendCleanup"); IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "SuspendResume"); IrBasicBlock *cancel_target_block = ir_create_basic_block(irb, scope, "CancelTarget"); IrBasicBlock *do_cancel_block = ir_create_basic_block(irb, scope, "DoCancel"); IrBasicBlock *do_defers_block = ir_create_basic_block(irb, scope, "DoDefers"); IrBasicBlock *destroy_block = ir_create_basic_block(irb, scope, "DestroyBlock"); IrBasicBlock *my_suspended_block = ir_create_basic_block(irb, scope, "AlreadySuspended"); IrBasicBlock *my_not_suspended_block = ir_create_basic_block(irb, scope, "NotAlreadySuspended"); IrBasicBlock *do_suspend_block = ir_create_basic_block(irb, scope, "DoSuspend"); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, atomic_state_field_name); IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise); IrInstruction *const_bool_false = ir_build_const_bool(irb, scope, node, false); IrInstruction *undefined_value = ir_build_const_undefined(irb, scope, node); IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100 IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 ZigVar *result_var = ir_create_var(irb, node, scope, nullptr, false, false, true, const_bool_false); IrInstruction *target_promise_type = ir_build_typeof(irb, scope, node, target_inst); IrInstruction *promise_result_type = ir_build_promise_result_type(irb, scope, node, target_promise_type); ir_build_await_bookkeeping(irb, scope, node, promise_result_type); ir_build_var_decl(irb, scope, node, result_var, promise_result_type, nullptr, undefined_value); IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, scope, node, result_var); ir_build_store_ptr(irb, scope, node, result_ptr_field_ptr, my_result_var_ptr); IrInstruction *save_token = ir_build_coro_save(irb, scope, node, irb->exec->coro_handle); IrInstruction *coro_handle_addr = ir_build_ptr_to_int(irb, scope, node, irb->exec->coro_handle); IrInstruction *mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, coro_handle_addr, await_mask, false); IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, atomic_state_ptr, nullptr, mask_bits, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false); IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false); ir_build_cond_br(irb, scope, node, is_awaited_bool, already_awaited_block, not_awaited_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, already_awaited_block); ir_build_unreachable(irb, scope, node); ir_set_cursor_at_end_and_append_block(irb, not_awaited_block); IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); ir_build_cond_br(irb, scope, node, is_canceled_bool, cancel_target_block, not_canceled_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); ir_build_cond_br(irb, scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, cancel_target_block); ir_build_cancel(irb, scope, node, target_inst); ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, no_suspend_block); if (irb->codegen->have_err_ret_tracing) { Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME); IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_field_name); IrInstruction *dest_err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull); ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr); } Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to, // because we're about to destroy the memory. So we store it into our result variable. IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr); ir_build_store_ptr(irb, scope, node, my_result_var_ptr, no_suspend_result); ir_build_cancel(irb, scope, node, target_inst); ir_build_br(irb, scope, node, merge_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, yes_suspend_block); IrInstruction *my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); IrInstruction *my_is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_suspended_mask, false); IrInstruction *my_is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_is_suspended_value, zero, false); ir_build_cond_br(irb, scope, node, my_is_suspended_bool, my_suspended_block, my_not_suspended_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, my_suspended_block); ir_build_unreachable(irb, scope, node); ir_set_cursor_at_end_and_append_block(irb, my_not_suspended_block); IrInstruction *my_is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_canceled_mask, false); IrInstruction *my_is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_is_canceled_value, zero, false); ir_build_cond_br(irb, scope, node, my_is_canceled_bool, cleanup_block, do_suspend_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, do_suspend_block); IrInstruction *suspend_code = ir_build_coro_suspend(irb, scope, node, save_token, const_bool_false); IrInstructionSwitchBrCase *cases = allocate(2); cases[0].value = ir_build_const_u8(irb, scope, node, 0); cases[0].block = resume_block; cases[1].value = ir_build_const_u8(irb, scope, node, 1); cases[1].block = destroy_block; ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, destroy_block); ir_gen_cancel_target(irb, scope, node, target_inst, false, true); ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, cleanup_block); IrInstruction *my_mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, ptr_mask, is_canceled_mask, false); IrInstruction *b_my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, my_mask_bits, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); IrInstruction *my_await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, b_my_prev_atomic_value, ptr_mask, false); IrInstruction *dont_have_my_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, my_await_handle_addr, zero, false); IrInstruction *dont_destroy_ourselves = ir_build_bin_op(irb, scope, node, IrBinOpBoolAnd, dont_have_my_await_handle, is_canceled_bool, false); ir_build_cond_br(irb, scope, node, dont_have_my_await_handle, do_defers_block, do_cancel_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, do_cancel_block); IrInstruction *my_await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, my_await_handle_addr); ir_gen_cancel_target(irb, scope, node, my_await_handle, true, false); ir_mark_gen(ir_build_br(irb, scope, node, do_defers_block, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, do_defers_block); ir_gen_defers_for_block(irb, scope, outer_scope, true); ir_mark_gen(ir_build_cond_br(irb, scope, node, dont_destroy_ourselves, irb->exec->coro_early_final, irb->exec->coro_final_cleanup_block, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, resume_block); ir_build_br(irb, scope, node, merge_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, merge_block); return ir_build_load_ptr(irb, scope, node, my_result_var_ptr); } static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeSuspend); ZigFn *fn_entry = exec_fn_entry(irb->exec); if (!fn_entry) { add_node_error(irb->codegen, node, buf_sprintf("suspend outside function definition")); return irb->codegen->invalid_instruction; } if (fn_entry->type_entry->data.fn.fn_type_id.cc != CallingConventionAsync) { add_node_error(irb->codegen, node, buf_sprintf("suspend in non-async function")); return irb->codegen->invalid_instruction; } ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope); if (scope_defer_expr) { if (!scope_defer_expr->reported_err) { ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside defer expression")); add_error_note(irb->codegen, msg, scope_defer_expr->base.source_node, buf_sprintf("defer here")); scope_defer_expr->reported_err = true; } return irb->codegen->invalid_instruction; } ScopeSuspend *existing_suspend_scope = get_scope_suspend(parent_scope); if (existing_suspend_scope) { if (!existing_suspend_scope->reported_err) { ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside suspend block")); add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("other suspend block here")); existing_suspend_scope->reported_err = true; } return irb->codegen->invalid_instruction; } Scope *outer_scope = irb->exec->begin_scope; IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup"); IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume"); IrBasicBlock *suspended_block = ir_create_basic_block(irb, parent_scope, "AlreadySuspended"); IrBasicBlock *canceled_block = ir_create_basic_block(irb, parent_scope, "IsCanceled"); IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, parent_scope, "NotCanceled"); IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, parent_scope, "NotAlreadySuspended"); IrBasicBlock *cancel_awaiter_block = ir_create_basic_block(irb, parent_scope, "CancelAwaiter"); IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_promise); IrInstruction *const_bool_true = ir_build_const_bool(irb, parent_scope, node, true); IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false); IrInstruction *usize_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_usize); IrInstruction *is_canceled_mask = ir_build_const_usize(irb, parent_scope, node, 0x1); // 0b001 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, parent_scope, node, 0x2); // 0b010 IrInstruction *zero = ir_build_const_usize(irb, parent_scope, node, 0); IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, parent_scope, node, 0x7); // 0b111 IrInstruction *ptr_mask = ir_build_un_op(irb, parent_scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, parent_scope, node, usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); IrInstruction *is_canceled_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); ir_build_cond_br(irb, parent_scope, node, is_canceled_bool, canceled_block, not_canceled_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, canceled_block); IrInstruction *await_handle_addr = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); IrInstruction *have_await_handle = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); IrBasicBlock *post_canceled_block = irb->current_basic_block; ir_build_cond_br(irb, parent_scope, node, have_await_handle, cancel_awaiter_block, cleanup_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, cancel_awaiter_block); IrInstruction *await_handle = ir_build_int_to_ptr(irb, parent_scope, node, promise_type_val, await_handle_addr); ir_gen_cancel_target(irb, parent_scope, node, await_handle, true, false); IrBasicBlock *post_cancel_awaiter_block = irb->current_basic_block; ir_build_br(irb, parent_scope, node, cleanup_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); IrInstruction *is_suspended_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); IrInstruction *is_suspended_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); ir_build_cond_br(irb, parent_scope, node, is_suspended_bool, suspended_block, not_suspended_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, suspended_block); ir_build_unreachable(irb, parent_scope, node); ir_set_cursor_at_end_and_append_block(irb, not_suspended_block); IrInstruction *suspend_code; if (node->data.suspend.block == nullptr) { suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false); } else { Scope *child_scope; ScopeSuspend *suspend_scope = create_suspend_scope(node, parent_scope); suspend_scope->resume_block = resume_block; child_scope = &suspend_scope->base; IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle); ir_gen_node(irb, node->data.suspend.block, child_scope); suspend_code = ir_mark_gen(ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false)); } IrInstructionSwitchBrCase *cases = allocate(2); cases[0].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 0)); cases[0].block = resume_block; cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1)); cases[1].block = canceled_block; ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr)); ir_set_cursor_at_end_and_append_block(irb, cleanup_block); IrBasicBlock **incoming_blocks = allocate(2); IrInstruction **incoming_values = allocate(2); incoming_blocks[0] = post_canceled_block; incoming_values[0] = const_bool_true; incoming_blocks[1] = post_cancel_awaiter_block; incoming_values[1] = const_bool_false; IrInstruction *destroy_ourselves = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values); ir_gen_defers_for_block(irb, parent_scope, outer_scope, true); ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, destroy_ourselves, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, resume_block); return ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); } static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval) { assert(scope); switch (node->type) { case NodeTypeStructValueField: case NodeTypeRoot: case NodeTypeParamDecl: case NodeTypeUse: case NodeTypeSwitchProng: case NodeTypeSwitchRange: case NodeTypeStructField: case NodeTypeFnDef: case NodeTypeTestDecl: zig_unreachable(); case NodeTypeBlock: return ir_lval_wrap(irb, scope, ir_gen_block(irb, scope, node), lval); case NodeTypeGroupedExpr: return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval); case NodeTypeBinOpExpr: return ir_lval_wrap(irb, scope, ir_gen_bin_op(irb, scope, node), lval); case NodeTypeIntLiteral: return ir_lval_wrap(irb, scope, ir_gen_int_lit(irb, scope, node), lval); case NodeTypeFloatLiteral: return ir_lval_wrap(irb, scope, ir_gen_float_lit(irb, scope, node), lval); case NodeTypeCharLiteral: return ir_lval_wrap(irb, scope, ir_gen_char_lit(irb, scope, node), lval); case NodeTypeSymbol: return ir_gen_symbol(irb, scope, node, lval); case NodeTypeFnCallExpr: return ir_gen_fn_call(irb, scope, node, lval); case NodeTypeIfBoolExpr: return ir_lval_wrap(irb, scope, ir_gen_if_bool_expr(irb, scope, node), lval); case NodeTypePrefixOpExpr: return ir_gen_prefix_op_expr(irb, scope, node, lval); case NodeTypeContainerInitExpr: return ir_lval_wrap(irb, scope, ir_gen_container_init_expr(irb, scope, node), lval); case NodeTypeVariableDeclaration: return ir_lval_wrap(irb, scope, ir_gen_var_decl(irb, scope, node), lval); case NodeTypeWhileExpr: return ir_lval_wrap(irb, scope, ir_gen_while_expr(irb, scope, node), lval); case NodeTypeForExpr: return ir_lval_wrap(irb, scope, ir_gen_for_expr(irb, scope, node), lval); case NodeTypeArrayAccessExpr: return ir_gen_array_access(irb, scope, node, lval); case NodeTypeReturnExpr: return ir_gen_return(irb, scope, node, lval); case NodeTypeFieldAccessExpr: { IrInstruction *ptr_instruction = ir_gen_field_access(irb, scope, node); if (ptr_instruction == irb->codegen->invalid_instruction) return ptr_instruction; if (lval == LValPtr) return ptr_instruction; return ir_build_load_ptr(irb, scope, node, ptr_instruction); } case NodeTypePtrDeref: { AstNode *expr_node = node->data.ptr_deref_expr.target; IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval); if (value == irb->codegen->invalid_instruction) return value; return ir_build_un_op(irb, scope, node, IrUnOpDereference, value); } case NodeTypeUnwrapOptional: { AstNode *expr_node = node->data.unwrap_optional.expr; IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); if (maybe_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true); if (lval == LValPtr) return unwrapped_ptr; return ir_build_load_ptr(irb, scope, node, unwrapped_ptr); } case NodeTypeBoolLiteral: return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval); case NodeTypeArrayType: return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval); case NodeTypePointerType: return ir_lval_wrap(irb, scope, ir_gen_pointer_type(irb, scope, node), lval); case NodeTypePromiseType: return ir_lval_wrap(irb, scope, ir_gen_promise_type(irb, scope, node), lval); case NodeTypeStringLiteral: return ir_lval_wrap(irb, scope, ir_gen_string_literal(irb, scope, node), lval); case NodeTypeUndefinedLiteral: return ir_lval_wrap(irb, scope, ir_gen_undefined_literal(irb, scope, node), lval); case NodeTypeAsmExpr: return ir_lval_wrap(irb, scope, ir_gen_asm_expr(irb, scope, node), lval); case NodeTypeNullLiteral: return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval); case NodeTypeIfErrorExpr: return ir_lval_wrap(irb, scope, ir_gen_if_err_expr(irb, scope, node), lval); case NodeTypeTestExpr: return ir_lval_wrap(irb, scope, ir_gen_test_expr(irb, scope, node), lval); case NodeTypeSwitchExpr: return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node), lval); case NodeTypeCompTime: return ir_gen_comptime(irb, scope, node, lval); case NodeTypeErrorType: return ir_lval_wrap(irb, scope, ir_gen_error_type(irb, scope, node), lval); case NodeTypeBreak: return ir_lval_wrap(irb, scope, ir_gen_break(irb, scope, node), lval); case NodeTypeContinue: return ir_lval_wrap(irb, scope, ir_gen_continue(irb, scope, node), lval); case NodeTypeUnreachable: return ir_lval_wrap(irb, scope, ir_build_unreachable(irb, scope, node), lval); case NodeTypeDefer: return ir_lval_wrap(irb, scope, ir_gen_defer(irb, scope, node), lval); case NodeTypeSliceExpr: return ir_lval_wrap(irb, scope, ir_gen_slice(irb, scope, node), lval); case NodeTypeUnwrapErrorExpr: return ir_lval_wrap(irb, scope, ir_gen_err_ok_or(irb, scope, node), lval); case NodeTypeContainerDecl: return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval); case NodeTypeFnProto: return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval); case NodeTypeErrorSetDecl: return ir_lval_wrap(irb, scope, ir_gen_err_set_decl(irb, scope, node), lval); case NodeTypeCancel: return ir_lval_wrap(irb, scope, ir_gen_cancel(irb, scope, node), lval); case NodeTypeResume: return ir_lval_wrap(irb, scope, ir_gen_resume(irb, scope, node), lval); case NodeTypeAwaitExpr: return ir_lval_wrap(irb, scope, ir_gen_await_expr(irb, scope, node), lval); case NodeTypeSuspend: return ir_lval_wrap(irb, scope, ir_gen_suspend(irb, scope, node), lval); } zig_unreachable(); } static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval) { IrInstruction *result = ir_gen_node_raw(irb, node, scope, lval); irb->exec->invalid = irb->exec->invalid || (result == irb->codegen->invalid_instruction); return result; } static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope) { return ir_gen_node_extra(irb, node, scope, LValNone); } static void invalidate_exec(IrExecutable *exec) { if (exec->invalid) return; exec->invalid = true; for (size_t i = 0; i < exec->tld_list.length; i += 1) { exec->tld_list.items[i]->resolution = TldResolutionInvalid; } if (exec->source_exec != nullptr) invalidate_exec(exec->source_exec); } bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_executable) { assert(node->owner); IrBuilder ir_builder = {0}; IrBuilder *irb = &ir_builder; irb->codegen = codegen; irb->exec = ir_executable; IrBasicBlock *entry_block = ir_create_basic_block(irb, scope, "Entry"); ir_set_cursor_at_end_and_append_block(irb, entry_block); // Entry block gets a reference because we enter it to begin. ir_ref_bb(irb->current_basic_block); ZigFn *fn_entry = exec_fn_entry(irb->exec); bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; IrInstruction *coro_id; IrInstruction *u8_ptr_type; IrInstruction *const_bool_false; IrInstruction *coro_promise_ptr; IrInstruction *err_ret_trace_ptr; ZigType *return_type; Buf *result_ptr_field_name; ZigVar *coro_size_var; if (is_async) { // create the coro promise Scope *coro_scope = create_coro_prelude_scope(node, scope); const_bool_false = ir_build_const_bool(irb, coro_scope, node, false); ZigVar *promise_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); return_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; IrInstruction *undef = ir_build_const_undefined(irb, coro_scope, node); ZigType *coro_frame_type = get_promise_frame_type(irb->codegen, return_type); IrInstruction *coro_frame_type_value = ir_build_const_type(irb, coro_scope, node, coro_frame_type); // TODO mark this var decl as "no safety" e.g. disable initializing the undef value to 0xaa ir_build_var_decl(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, undef); coro_promise_ptr = ir_build_var_ptr(irb, coro_scope, node, promise_var); ZigVar *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *null_value = ir_build_const_null(irb, coro_scope, node); IrInstruction *await_handle_type_val = ir_build_const_type(irb, coro_scope, node, get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); ir_build_var_decl(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, null_value); irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, coro_scope, node, await_handle_var); u8_ptr_type = ir_build_const_type(irb, coro_scope, node, get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_u8, false)); IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, coro_promise_ptr); coro_id = ir_build_coro_id(irb, coro_scope, node, promise_as_u8_ptr); coro_size_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *coro_size = ir_build_coro_size(irb, coro_scope, node); ir_build_var_decl(irb, coro_scope, node, coro_size_var, nullptr, nullptr, coro_size); IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, coro_scope, node, ImplicitAllocatorIdArg); irb->exec->coro_allocator_var = ir_create_var(irb, node, coro_scope, nullptr, true, true, true, const_bool_false); ir_build_var_decl(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr); Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME); IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, alloc_field_name); IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr); IrInstruction *maybe_coro_mem_ptr = ir_build_coro_alloc_helper(irb, coro_scope, node, alloc_fn, coro_size); IrInstruction *alloc_result_is_ok = ir_build_test_nonnull(irb, coro_scope, node, maybe_coro_mem_ptr); IrBasicBlock *alloc_err_block = ir_create_basic_block(irb, coro_scope, "AllocError"); IrBasicBlock *alloc_ok_block = ir_create_basic_block(irb, coro_scope, "AllocOk"); ir_build_cond_br(irb, coro_scope, node, alloc_result_is_ok, alloc_ok_block, alloc_err_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, alloc_err_block); // we can return undefined here, because the caller passes a pointer to the error struct field // in the error union result, and we populate it in case of allocation failure. ir_build_return(irb, coro_scope, node, undef); ir_set_cursor_at_end_and_append_block(irb, alloc_ok_block); IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr); irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); irb->exec->atomic_state_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, atomic_state_field_name); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); ir_build_store_ptr(irb, scope, node, irb->exec->atomic_state_field_ptr, zero); Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name); ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, irb->exec->coro_result_field_ptr); if (irb->codegen->have_err_ret_tracing) { // initialize the error return trace Buf *return_addresses_field_name = buf_create_from_str(RETURN_ADDRESSES_FIELD_NAME); IrInstruction *return_addresses_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, return_addresses_field_name); Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME); err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_field_name); ir_build_mark_err_ret_trace_ptr(irb, scope, node, err_ret_trace_ptr); // coordinate with builtin.zig Buf *index_name = buf_create_from_str("index"); IrInstruction *index_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, index_name); ir_build_store_ptr(irb, scope, node, index_ptr, zero); Buf *instruction_addresses_name = buf_create_from_str("instruction_addresses"); IrInstruction *addrs_slice_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, instruction_addresses_name); IrInstruction *slice_value = ir_build_slice(irb, scope, node, return_addresses_ptr, zero, nullptr, false); ir_build_store_ptr(irb, scope, node, addrs_slice_ptr, slice_value); } irb->exec->coro_early_final = ir_create_basic_block(irb, scope, "CoroEarlyFinal"); irb->exec->coro_normal_final = ir_create_basic_block(irb, scope, "CoroNormalFinal"); irb->exec->coro_suspend_block = ir_create_basic_block(irb, scope, "Suspend"); irb->exec->coro_final_cleanup_block = ir_create_basic_block(irb, scope, "FinalCleanup"); } IrInstruction *result = ir_gen_node_extra(irb, node, scope, LValNone); assert(result); if (irb->exec->invalid) return false; if (!instr_is_unreachable(result)) { // no need for save_err_ret_addr because this cannot return error ir_gen_async_return(irb, scope, result->source_node, result, true); } if (is_async) { IrBasicBlock *invalid_resume_block = ir_create_basic_block(irb, scope, "InvalidResume"); IrBasicBlock *check_free_block = ir_create_basic_block(irb, scope, "CheckFree"); ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_early_final); IrInstruction *const_bool_true = ir_build_const_bool(irb, scope, node, true); IrInstruction *suspend_code = ir_build_coro_suspend(irb, scope, node, nullptr, const_bool_true); IrInstructionSwitchBrCase *cases = allocate(2); cases[0].value = ir_build_const_u8(irb, scope, node, 0); cases[0].block = invalid_resume_block; cases[1].value = ir_build_const_u8(irb, scope, node, 1); cases[1].block = irb->exec->coro_final_cleanup_block; ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_suspend_block); ir_build_coro_end(irb, scope, node); ir_build_return(irb, scope, node, irb->exec->coro_handle); ir_set_cursor_at_end_and_append_block(irb, invalid_resume_block); ir_build_unreachable(irb, scope, node); ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_normal_final); if (type_has_bits(return_type)) { IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, false, false, PtrLenUnknown, 0, 0, 0)); IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr); IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, result_ptr); IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, irb->exec->coro_result_field_ptr); IrInstruction *return_type_inst = ir_build_const_type(irb, scope, node, fn_entry->type_entry->data.fn.fn_type_id.return_type); IrInstruction *size_of_ret_val = ir_build_size_of(irb, scope, node, return_type_inst); ir_build_memcpy(irb, scope, node, result_ptr_as_u8_ptr, return_value_ptr_as_u8_ptr, size_of_ret_val); } if (irb->codegen->have_err_ret_tracing) { Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME); IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name); IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr); ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr, dest_err_ret_trace_ptr); } // Before we destroy the coroutine frame, we need to load the target promise into // a register or local variable which does not get spilled into the frame, // otherwise llvm tries to access memory inside the destroyed frame. IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node, irb->exec->await_handle_var_ptr, false); IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr); ir_build_br(irb, scope, node, check_free_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_final_cleanup_block); ir_build_br(irb, scope, node, check_free_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, check_free_block); IrBasicBlock **incoming_blocks = allocate(2); IrInstruction **incoming_values = allocate(2); incoming_blocks[0] = irb->exec->coro_final_cleanup_block; incoming_values[0] = const_bool_false; incoming_blocks[1] = irb->exec->coro_normal_final; incoming_values[1] = const_bool_true; IrInstruction *resume_awaiter = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); IrBasicBlock **merge_incoming_blocks = allocate(2); IrInstruction **merge_incoming_values = allocate(2); merge_incoming_blocks[0] = irb->exec->coro_final_cleanup_block; merge_incoming_values[0] = ir_build_const_undefined(irb, scope, node); merge_incoming_blocks[1] = irb->exec->coro_normal_final; merge_incoming_values[1] = await_handle_in_block; IrInstruction *awaiter_handle = ir_build_phi(irb, scope, node, 2, merge_incoming_blocks, merge_incoming_values); Buf *free_field_name = buf_create_from_str(ASYNC_FREE_FIELD_NAME); IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node, ImplicitAllocatorIdLocalVar); IrInstruction *free_fn_ptr = ir_build_field_ptr(irb, scope, node, implicit_allocator_ptr, free_field_name); IrInstruction *free_fn = ir_build_load_ptr(irb, scope, node, free_fn_ptr); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *coro_mem_ptr_maybe = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle); IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, false, false, PtrLenUnknown, 0, 0, 0)); IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe); IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr); IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false); size_t arg_count = 2; IrInstruction **args = allocate(arg_count); args[0] = implicit_allocator_ptr; // self args[1] = mem_slice; // old_mem ir_build_call(irb, scope, node, nullptr, free_fn, arg_count, args, false, FnInlineAuto, false, nullptr, nullptr); IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "Resume"); ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, resume_block); ir_gen_resume_target(irb, scope, node, awaiter_handle); ir_build_br(irb, scope, node, irb->exec->coro_suspend_block, const_bool_false); } return true; } bool ir_gen_fn(CodeGen *codegen, ZigFn *fn_entry) { assert(fn_entry); IrExecutable *ir_executable = &fn_entry->ir_executable; AstNode *body_node = fn_entry->body_node; assert(fn_entry->child_scope); return ir_gen(codegen, body_node, fn_entry->child_scope, ir_executable); } static void add_call_stack_errors(CodeGen *codegen, IrExecutable *exec, ErrorMsg *err_msg, int limit) { if (!exec || !exec->source_node || limit < 0) return; add_error_note(codegen, err_msg, exec->source_node, buf_sprintf("called from here")); add_call_stack_errors(codegen, exec->parent_exec, err_msg, limit - 1); } static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutable *exec, AstNode *source_node, Buf *msg) { invalidate_exec(exec); ErrorMsg *err_msg = add_node_error(codegen, source_node, msg); if (exec->parent_exec) { add_call_stack_errors(codegen, exec, err_msg, 10); } return err_msg; } static ErrorMsg *ir_add_error_node(IrAnalyze *ira, AstNode *source_node, Buf *msg) { return exec_add_error_node(ira->codegen, ira->new_irb.exec, source_node, msg); } static ErrorMsg *ir_add_error(IrAnalyze *ira, IrInstruction *source_instruction, Buf *msg) { return ir_add_error_node(ira, source_instruction->source_node, msg); } static ConstExprValue *ir_const_ptr_pointee(IrAnalyze *ira, ConstExprValue *const_val, AstNode *source_node) { ConstExprValue *val = const_ptr_pointee_unchecked(ira->codegen, const_val); assert(val != nullptr); assert(const_val->type->id == ZigTypeIdPointer); ZigType *expected_type = const_val->type->data.pointer.child_type; if (!types_have_same_zig_comptime_repr(val->type, expected_type)) { ir_add_error_node(ira, source_node, buf_sprintf("TODO handle comptime reinterpreted pointer. See https://github.com/ziglang/zig/issues/955")); return nullptr; } return val; } static IrInstruction *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec) { IrBasicBlock *bb = exec->basic_block_list.at(0); for (size_t i = 0; i < bb->instruction_list.length; i += 1) { IrInstruction *instruction = bb->instruction_list.at(i); if (instruction->id == IrInstructionIdReturn) { IrInstructionReturn *ret_inst = (IrInstructionReturn *)instruction; IrInstruction *value = ret_inst->value; if (value->value.special == ConstValSpecialRuntime) { exec_add_error_node(codegen, exec, value->source_node, buf_sprintf("unable to evaluate constant expression")); return codegen->invalid_instruction; } return value; } else if (ir_has_side_effects(instruction)) { exec_add_error_node(codegen, exec, instruction->source_node, buf_sprintf("unable to evaluate constant expression")); return codegen->invalid_instruction; } } return codegen->invalid_instruction; } static bool ir_emit_global_runtime_side_effect(IrAnalyze *ira, IrInstruction *source_instruction) { if (ir_should_inline(ira->new_irb.exec, source_instruction->scope)) { ir_add_error(ira, source_instruction, buf_sprintf("unable to evaluate constant expression")); return false; } return true; } static bool const_val_fits_in_num_lit(ConstExprValue *const_val, ZigType *num_lit_type) { return ((num_lit_type->id == ZigTypeIdComptimeFloat && (const_val->type->id == ZigTypeIdFloat || const_val->type->id == ZigTypeIdComptimeFloat)) || (num_lit_type->id == ZigTypeIdComptimeInt && (const_val->type->id == ZigTypeIdInt || const_val->type->id == ZigTypeIdComptimeInt))); } static bool float_has_fraction(ConstExprValue *const_val) { if (const_val->type->id == ZigTypeIdComptimeFloat) { return bigfloat_has_fraction(&const_val->data.x_bigfloat); } else if (const_val->type->id == ZigTypeIdFloat) { switch (const_val->type->data.floating.bit_count) { case 16: { float16_t floored = f16_roundToInt(const_val->data.x_f16, softfloat_round_minMag, false); return !f16_eq(floored, const_val->data.x_f16); } case 32: return floorf(const_val->data.x_f32) != const_val->data.x_f32; case 64: return floor(const_val->data.x_f64) != const_val->data.x_f64; case 128: { float128_t floored; f128M_roundToInt(&const_val->data.x_f128, softfloat_round_minMag, false, &floored); return !f128M_eq(&floored, &const_val->data.x_f128); } default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_append_buf(Buf *buf, ConstExprValue *const_val) { if (const_val->type->id == ZigTypeIdComptimeFloat) { bigfloat_append_buf(buf, &const_val->data.x_bigfloat); } else if (const_val->type->id == ZigTypeIdFloat) { switch (const_val->type->data.floating.bit_count) { case 16: buf_appendf(buf, "%f", zig_f16_to_double(const_val->data.x_f16)); break; case 32: buf_appendf(buf, "%f", const_val->data.x_f32); break; case 64: buf_appendf(buf, "%f", const_val->data.x_f64); break; case 128: { // TODO actual implementation const size_t extra_len = 100; size_t old_len = buf_len(buf); buf_resize(buf, old_len + extra_len); float64_t f64_value = f128M_to_f64(&const_val->data.x_f128); double double_value; memcpy(&double_value, &f64_value, sizeof(double)); int len = snprintf(buf_ptr(buf) + old_len, extra_len, "%f", double_value); assert(len > 0); buf_resize(buf, old_len + len); break; } default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_init_bigint(BigInt *bigint, ConstExprValue *const_val) { if (const_val->type->id == ZigTypeIdComptimeFloat) { bigint_init_bigfloat(bigint, &const_val->data.x_bigfloat); } else if (const_val->type->id == ZigTypeIdFloat) { switch (const_val->type->data.floating.bit_count) { case 16: { double x = zig_f16_to_double(const_val->data.x_f16); if (x >= 0) { bigint_init_unsigned(bigint, (uint64_t)x); } else { bigint_init_unsigned(bigint, (uint64_t)-x); bigint->is_negative = true; } break; } case 32: if (const_val->data.x_f32 >= 0) { bigint_init_unsigned(bigint, (uint64_t)(const_val->data.x_f32)); } else { bigint_init_unsigned(bigint, (uint64_t)(-const_val->data.x_f32)); bigint->is_negative = true; } break; case 64: if (const_val->data.x_f64 >= 0) { bigint_init_unsigned(bigint, (uint64_t)(const_val->data.x_f64)); } else { bigint_init_unsigned(bigint, (uint64_t)(-const_val->data.x_f64)); bigint->is_negative = true; } break; case 128: { BigFloat tmp_float; bigfloat_init_128(&tmp_float, const_val->data.x_f128); bigint_init_bigfloat(bigint, &tmp_float); } break; default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_init_bigfloat(ConstExprValue *dest_val, BigFloat *bigfloat) { if (dest_val->type->id == ZigTypeIdComptimeFloat) { bigfloat_init_bigfloat(&dest_val->data.x_bigfloat, bigfloat); } else if (dest_val->type->id == ZigTypeIdFloat) { switch (dest_val->type->data.floating.bit_count) { case 16: dest_val->data.x_f16 = bigfloat_to_f16(bigfloat); break; case 32: dest_val->data.x_f32 = bigfloat_to_f32(bigfloat); break; case 64: dest_val->data.x_f64 = bigfloat_to_f64(bigfloat); break; case 128: dest_val->data.x_f128 = bigfloat_to_f128(bigfloat); break; default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_init_f16(ConstExprValue *dest_val, float16_t x) { if (dest_val->type->id == ZigTypeIdComptimeFloat) { bigfloat_init_16(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == ZigTypeIdFloat) { switch (dest_val->type->data.floating.bit_count) { case 16: dest_val->data.x_f16 = x; break; case 32: dest_val->data.x_f32 = zig_f16_to_double(x); break; case 64: dest_val->data.x_f64 = zig_f16_to_double(x); break; case 128: f16_to_f128M(x, &dest_val->data.x_f128); break; default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_init_f32(ConstExprValue *dest_val, float x) { if (dest_val->type->id == ZigTypeIdComptimeFloat) { bigfloat_init_32(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == ZigTypeIdFloat) { switch (dest_val->type->data.floating.bit_count) { case 16: dest_val->data.x_f16 = zig_double_to_f16(x); break; case 32: dest_val->data.x_f32 = x; break; case 64: dest_val->data.x_f64 = x; break; case 128: { float32_t x_f32; memcpy(&x_f32, &x, sizeof(float)); f32_to_f128M(x_f32, &dest_val->data.x_f128); break; } default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_init_f64(ConstExprValue *dest_val, double x) { if (dest_val->type->id == ZigTypeIdComptimeFloat) { bigfloat_init_64(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == ZigTypeIdFloat) { switch (dest_val->type->data.floating.bit_count) { case 16: dest_val->data.x_f16 = zig_double_to_f16(x); break; case 32: dest_val->data.x_f32 = x; break; case 64: dest_val->data.x_f64 = x; break; case 128: { float64_t x_f64; memcpy(&x_f64, &x, sizeof(double)); f64_to_f128M(x_f64, &dest_val->data.x_f128); break; } default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_init_f128(ConstExprValue *dest_val, float128_t x) { if (dest_val->type->id == ZigTypeIdComptimeFloat) { bigfloat_init_128(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == ZigTypeIdFloat) { switch (dest_val->type->data.floating.bit_count) { case 16: dest_val->data.x_f16 = f128M_to_f16(&x); break; case 32: { float32_t f32_val = f128M_to_f32(&x); memcpy(&dest_val->data.x_f32, &f32_val, sizeof(float)); break; } case 64: { float64_t f64_val = f128M_to_f64(&x); memcpy(&dest_val->data.x_f64, &f64_val, sizeof(double)); break; } case 128: { memcpy(&dest_val->data.x_f128, &x, sizeof(float128_t)); break; } default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_init_float(ConstExprValue *dest_val, ConstExprValue *src_val) { if (src_val->type->id == ZigTypeIdComptimeFloat) { float_init_bigfloat(dest_val, &src_val->data.x_bigfloat); } else if (src_val->type->id == ZigTypeIdFloat) { switch (src_val->type->data.floating.bit_count) { case 16: float_init_f16(dest_val, src_val->data.x_f16); break; case 32: float_init_f32(dest_val, src_val->data.x_f32); break; case 64: float_init_f64(dest_val, src_val->data.x_f64); break; case 128: float_init_f128(dest_val, src_val->data.x_f128); break; default: zig_unreachable(); } } else { zig_unreachable(); } } static Cmp float_cmp(ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); if (op1->type->id == ZigTypeIdComptimeFloat) { return bigfloat_cmp(&op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == ZigTypeIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: if (f16_lt(op1->data.x_f16, op2->data.x_f16)) { return CmpLT; } else if (f16_lt(op2->data.x_f16, op1->data.x_f16)) { return CmpGT; } else { return CmpEQ; } case 32: if (op1->data.x_f32 > op2->data.x_f32) { return CmpGT; } else if (op1->data.x_f32 < op2->data.x_f32) { return CmpLT; } else { return CmpEQ; } case 64: if (op1->data.x_f64 > op2->data.x_f64) { return CmpGT; } else if (op1->data.x_f64 < op2->data.x_f64) { return CmpLT; } else { return CmpEQ; } case 128: if (f128M_lt(&op1->data.x_f128, &op2->data.x_f128)) { return CmpLT; } else if (f128M_eq(&op1->data.x_f128, &op2->data.x_f128)) { return CmpEQ; } else { return CmpGT; } default: zig_unreachable(); } } else { zig_unreachable(); } } static Cmp float_cmp_zero(ConstExprValue *op) { if (op->type->id == ZigTypeIdComptimeFloat) { return bigfloat_cmp_zero(&op->data.x_bigfloat); } else if (op->type->id == ZigTypeIdFloat) { switch (op->type->data.floating.bit_count) { case 16: { const float16_t zero = zig_double_to_f16(0); if (f16_lt(op->data.x_f16, zero)) { return CmpLT; } else if (f16_lt(zero, op->data.x_f16)) { return CmpGT; } else { return CmpEQ; } } case 32: if (op->data.x_f32 < 0.0) { return CmpLT; } else if (op->data.x_f32 > 0.0) { return CmpGT; } else { return CmpEQ; } case 64: if (op->data.x_f64 < 0.0) { return CmpLT; } else if (op->data.x_f64 > 0.0) { return CmpGT; } else { return CmpEQ; } case 128: float128_t zero_float; ui32_to_f128M(0, &zero_float); if (f128M_lt(&op->data.x_f128, &zero_float)) { return CmpLT; } else if (f128M_eq(&op->data.x_f128, &zero_float)) { return CmpEQ; } else { return CmpGT; } default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_add(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { bigfloat_add(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == ZigTypeIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: out_val->data.x_f16 = f16_add(op1->data.x_f16, op2->data.x_f16); return; case 32: out_val->data.x_f32 = op1->data.x_f32 + op2->data.x_f32; return; case 64: out_val->data.x_f64 = op1->data.x_f64 + op2->data.x_f64; return; case 128: f128M_add(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); return; default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_sub(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { bigfloat_sub(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == ZigTypeIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: out_val->data.x_f16 = f16_sub(op1->data.x_f16, op2->data.x_f16); return; case 32: out_val->data.x_f32 = op1->data.x_f32 - op2->data.x_f32; return; case 64: out_val->data.x_f64 = op1->data.x_f64 - op2->data.x_f64; return; case 128: f128M_sub(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); return; default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_mul(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { bigfloat_mul(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == ZigTypeIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: out_val->data.x_f16 = f16_mul(op1->data.x_f16, op2->data.x_f16); return; case 32: out_val->data.x_f32 = op1->data.x_f32 * op2->data.x_f32; return; case 64: out_val->data.x_f64 = op1->data.x_f64 * op2->data.x_f64; return; case 128: f128M_mul(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); return; default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_div(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { bigfloat_div(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == ZigTypeIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16); return; case 32: out_val->data.x_f32 = op1->data.x_f32 / op2->data.x_f32; return; case 64: out_val->data.x_f64 = op1->data.x_f64 / op2->data.x_f64; return; case 128: f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); return; default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_div_trunc(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { bigfloat_div_trunc(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == ZigTypeIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16); out_val->data.x_f16 = f16_roundToInt(out_val->data.x_f16, softfloat_round_minMag, false); return; case 32: out_val->data.x_f32 = truncf(op1->data.x_f32 / op2->data.x_f32); return; case 64: out_val->data.x_f64 = trunc(op1->data.x_f64 / op2->data.x_f64); return; case 128: f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); f128M_roundToInt(&out_val->data.x_f128, softfloat_round_minMag, false, &out_val->data.x_f128); return; default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_div_floor(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { bigfloat_div_floor(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == ZigTypeIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16); out_val->data.x_f16 = f16_roundToInt(out_val->data.x_f16, softfloat_round_min, false); return; case 32: out_val->data.x_f32 = floorf(op1->data.x_f32 / op2->data.x_f32); return; case 64: out_val->data.x_f64 = floor(op1->data.x_f64 / op2->data.x_f64); return; case 128: f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); f128M_roundToInt(&out_val->data.x_f128, softfloat_round_min, false, &out_val->data.x_f128); return; default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_rem(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { bigfloat_rem(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == ZigTypeIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: out_val->data.x_f16 = f16_rem(op1->data.x_f16, op2->data.x_f16); return; case 32: out_val->data.x_f32 = fmodf(op1->data.x_f32, op2->data.x_f32); return; case 64: out_val->data.x_f64 = fmod(op1->data.x_f64, op2->data.x_f64); return; case 128: f128M_rem(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); return; default: zig_unreachable(); } } else { zig_unreachable(); } } // c = a - b * trunc(a / b) static float16_t zig_f16_mod(float16_t a, float16_t b) { float16_t c; c = f16_div(a, b); c = f16_roundToInt(c, softfloat_round_min, true); c = f16_mul(b, c); c = f16_sub(a, c); return c; } // c = a - b * trunc(a / b) static void zig_f128M_mod(const float128_t* a, const float128_t* b, float128_t* c) { f128M_div(a, b, c); f128M_roundToInt(c, softfloat_round_min, true, c); f128M_mul(b, c, c); f128M_sub(a, c, c); } static void float_mod(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { bigfloat_mod(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == ZigTypeIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: out_val->data.x_f16 = zig_f16_mod(op1->data.x_f16, op2->data.x_f16); return; case 32: out_val->data.x_f32 = fmodf(fmodf(op1->data.x_f32, op2->data.x_f32) + op2->data.x_f32, op2->data.x_f32); return; case 64: out_val->data.x_f64 = fmod(fmod(op1->data.x_f64, op2->data.x_f64) + op2->data.x_f64, op2->data.x_f64); return; case 128: zig_f128M_mod(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); return; default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_negate(ConstExprValue *out_val, ConstExprValue *op) { out_val->type = op->type; if (op->type->id == ZigTypeIdComptimeFloat) { bigfloat_negate(&out_val->data.x_bigfloat, &op->data.x_bigfloat); } else if (op->type->id == ZigTypeIdFloat) { switch (op->type->data.floating.bit_count) { case 16: { const float16_t zero = zig_double_to_f16(0); out_val->data.x_f16 = f16_sub(zero, op->data.x_f16); return; } case 32: out_val->data.x_f32 = -op->data.x_f32; return; case 64: out_val->data.x_f64 = -op->data.x_f64; return; case 128: float128_t zero_f128; ui32_to_f128M(0, &zero_f128); f128M_sub(&zero_f128, &op->data.x_f128, &out_val->data.x_f128); return; default: zig_unreachable(); } } else { zig_unreachable(); } } void float_write_ieee597(ConstExprValue *op, uint8_t *buf, bool is_big_endian) { if (op->type->id == ZigTypeIdFloat) { switch (op->type->data.floating.bit_count) { case 16: memcpy(buf, &op->data.x_f16, 2); // TODO wrong when compiler is big endian return; case 32: memcpy(buf, &op->data.x_f32, 4); // TODO wrong when compiler is big endian return; case 64: memcpy(buf, &op->data.x_f64, 8); // TODO wrong when compiler is big endian return; case 128: memcpy(buf, &op->data.x_f128, 16); // TODO wrong when compiler is big endian return; default: zig_unreachable(); } } else { zig_unreachable(); } } void float_read_ieee597(ConstExprValue *val, uint8_t *buf, bool is_big_endian) { if (val->type->id == ZigTypeIdFloat) { switch (val->type->data.floating.bit_count) { case 16: memcpy(&val->data.x_f16, buf, 2); // TODO wrong when compiler is big endian return; case 32: memcpy(&val->data.x_f32, buf, 4); // TODO wrong when compiler is big endian return; case 64: memcpy(&val->data.x_f64, buf, 8); // TODO wrong when compiler is big endian return; case 128: memcpy(&val->data.x_f128, buf, 16); // TODO wrong when compiler is big endian return; default: zig_unreachable(); } } else { zig_unreachable(); } } static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruction, ZigType *other_type, bool explicit_cast) { if (type_is_invalid(other_type)) { return false; } ConstExprValue *const_val = &instruction->value; assert(const_val->special != ConstValSpecialRuntime); 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); if (other_type->id == ZigTypeIdFloat) { return true; } else if (other_type->id == ZigTypeIdInt && const_val_is_int) { if (!other_type->data.integral.is_signed && const_val->data.x_bigint.is_negative) { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, &const_val->data.x_bigint, 10); ir_add_error(ira, instruction, buf_sprintf("cannot cast negative value %s to unsigned integer type '%s'", buf_ptr(val_buf), buf_ptr(&other_type->name))); return false; } if (bigint_fits_in_bits(&const_val->data.x_bigint, other_type->data.integral.bit_count, other_type->data.integral.is_signed)) { return true; } } else if (const_val_fits_in_num_lit(const_val, other_type)) { return true; } else if (other_type->id == ZigTypeIdOptional) { ZigType *child_type = other_type->data.maybe.child_type; if (const_val_fits_in_num_lit(const_val, child_type)) { return true; } else if (child_type->id == ZigTypeIdInt && const_val_is_int) { if (!child_type->data.integral.is_signed && const_val->data.x_bigint.is_negative) { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, &const_val->data.x_bigint, 10); ir_add_error(ira, instruction, buf_sprintf("cannot cast negative value %s to unsigned integer type '%s'", buf_ptr(val_buf), buf_ptr(&child_type->name))); return false; } if (bigint_fits_in_bits(&const_val->data.x_bigint, child_type->data.integral.bit_count, child_type->data.integral.is_signed)) { return true; } } else if (child_type->id == ZigTypeIdFloat && const_val_is_float) { return true; } } if (explicit_cast && (other_type->id == ZigTypeIdInt || other_type->id == ZigTypeIdComptimeInt) && const_val_is_float) { if (float_has_fraction(const_val)) { Buf *val_buf = buf_alloc(); float_append_buf(val_buf, const_val); ir_add_error(ira, instruction, buf_sprintf("fractional component prevents float value %s from being casted to type '%s'", buf_ptr(val_buf), buf_ptr(&other_type->name))); return false; } else { if (other_type->id == ZigTypeIdComptimeInt) { return true; } else { BigInt bigint; float_init_bigint(&bigint, const_val); if (bigint_fits_in_bits(&bigint, other_type->data.integral.bit_count, other_type->data.integral.is_signed)) { return true; } } } } const char *num_lit_str; Buf *val_buf = buf_alloc(); if (const_val_is_float) { num_lit_str = "float"; float_append_buf(val_buf, const_val); } else { num_lit_str = "integer"; bigint_append_buf(val_buf, &const_val->data.x_bigint, 10); } ir_add_error(ira, instruction, buf_sprintf("%s value %s cannot be implicitly casted to type '%s'", num_lit_str, buf_ptr(val_buf), buf_ptr(&other_type->name))); return false; } static bool is_slice(ZigType *type) { return type->id == ZigTypeIdStruct && type->data.structure.is_slice; } static bool slice_is_const(ZigType *type) { assert(is_slice(type)); return type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const; } static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigType *set2, AstNode *source_node) { assert(set1->id == ZigTypeIdErrorSet); assert(set2->id == ZigTypeIdErrorSet); if (!resolve_inferred_error_set(ira->codegen, set1, source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (!resolve_inferred_error_set(ira->codegen, set2, source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (type_is_global_error_set(set1)) { return set2; } if (type_is_global_error_set(set2)) { return set1; } ErrorTableEntry **errors = allocate(ira->codegen->errors_by_index.length); for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = set1->data.error_set.errors[i]; assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } ZigList intersection_list = {}; ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); buf_resize(&err_set_type->name, 0); buf_appendf(&err_set_type->name, "error{"); for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = set2->data.error_set.errors[i]; ErrorTableEntry *existing_entry = errors[error_entry->value]; if (existing_entry != nullptr) { intersection_list.append(existing_entry); buf_appendf(&err_set_type->name, "%s,", buf_ptr(&existing_entry->name)); } } free(errors); err_set_type->is_copyable = true; err_set_type->type_ref = ira->codegen->builtin_types.entry_global_error_set->type_ref; err_set_type->di_type = ira->codegen->builtin_types.entry_global_error_set->di_type; err_set_type->data.error_set.err_count = intersection_list.length; err_set_type->data.error_set.errors = intersection_list.items; err_set_type->zero_bits = intersection_list.length == 0; buf_appendf(&err_set_type->name, "}"); ira->codegen->error_di_types.append(&err_set_type->di_type); 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; // *T and [*]T may const-cast-only to ?*U and ?[*]U, respectively // but not if we want a mutable pointer // and not if the actual pointer has zero bits if (!wanted_is_mutable && wanted_type->id == ZigTypeIdOptional && wanted_type->data.maybe.child_type->id == ZigTypeIdPointer && actual_type->id == ZigTypeIdPointer && type_has_bits(actual_type)) { ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.maybe.child_type, actual_type, source_node, wanted_is_mutable); if (child.id == ConstCastResultIdInvalid) return child; if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdNullWrapPtr; result.data.null_wrap_ptr_child = allocate_nonzero(1); *result.data.null_wrap_ptr_child = child; } return result; } // *T and [*]T can always cast to *c_void if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && wanted_type->data.pointer.child_type == g->builtin_types.entry_c_void && actual_type->id == ZigTypeIdPointer && (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile)) { return result; } // pointer const if (wanted_type->id == ZigTypeIdPointer && actual_type->id == ZigTypeIdPointer) { ConstCastOnly child = 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); if (child.id == ConstCastResultIdInvalid) return child; if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdPointerChild; result.data.pointer_mismatch = allocate_nonzero(1); result.data.pointer_mismatch->child = child; result.data.pointer_mismatch->wanted_child = wanted_type->data.pointer.child_type; result.data.pointer_mismatch->actual_child = actual_type->data.pointer.child_type; return result; } if ((err = type_resolve(g, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { result.id = ConstCastResultIdInvalid; return result; } if ((err = type_resolve(g, wanted_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { result.id = ConstCastResultIdInvalid; return result; } if ((actual_type->data.pointer.ptr_len == wanted_type->data.pointer.ptr_len) && (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile) && actual_type->data.pointer.bit_offset == wanted_type->data.pointer.bit_offset && actual_type->data.pointer.unaligned_bit_count == wanted_type->data.pointer.unaligned_bit_count && get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_type)) { return result; } } // slice const if (is_slice(wanted_type) && is_slice(actual_type)) { ZigType *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry; ZigType *wanted_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; if ((err = type_resolve(g, actual_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { result.id = ConstCastResultIdInvalid; return result; } if ((err = type_resolve(g, wanted_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { result.id = ConstCastResultIdInvalid; return result; } if ((!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) && (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) && actual_ptr_type->data.pointer.bit_offset == wanted_ptr_type->data.pointer.bit_offset && actual_ptr_type->data.pointer.unaligned_bit_count == wanted_ptr_type->data.pointer.unaligned_bit_count && 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(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(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(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(1); result.data.error_union_error_set->child = error_set_child; result.data.error_union_error_set->wanted_err_set = wanted_type->data.error_union.err_set_type; result.data.error_union_error_set->actual_err_set = actual_type->data.error_union.err_set_type; return result; } return result; } // error set if (wanted_type->id == ZigTypeIdErrorSet && actual_type->id == ZigTypeIdErrorSet) { ZigType *contained_set = actual_type; ZigType *container_set = wanted_type; // if the container set is inferred, then this will always work. if (container_set->data.error_set.infer_fn != nullptr) { return result; } // if the container set is the global one, it will always work. if (type_is_global_error_set(container_set)) { return result; } if (!resolve_inferred_error_set(ira->codegen, contained_set, source_node)) { result.id = ConstCastResultIdUnresolvedInferredErrSet; return result; } if (type_is_global_error_set(contained_set)) { result.id = ConstCastResultIdErrSetGlobal; return result; } ErrorTableEntry **errors = allocate(g->errors_by_index.length); for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = container_set->data.error_set.errors[i]; assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } for (uint32_t i = 0; i < contained_set->data.error_set.err_count; i += 1) { ErrorTableEntry *contained_error_entry = contained_set->data.error_set.errors[i]; ErrorTableEntry *error_entry = errors[contained_error_entry->value]; if (error_entry == nullptr) { if (result.id == ConstCastResultIdOk) { result.id = ConstCastResultIdErrSet; result.data.error_set_mismatch = allocate(1); } result.data.error_set_mismatch->missing_errors.append(contained_error_entry); } } free(errors); return result; } if (wanted_type == ira->codegen->builtin_types.entry_promise && actual_type->id == ZigTypeIdPromise) { return result; } // fn if (wanted_type->id == ZigTypeIdFn && actual_type->id == ZigTypeIdFn) { if (wanted_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) { result.id = ConstCastResultIdFnAlign; return result; } if (wanted_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) { result.id = ConstCastResultIdFnCC; return result; } if (wanted_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) { result.id = ConstCastResultIdFnVarArgs; return result; } if (wanted_type->data.fn.is_generic != actual_type->data.fn.is_generic) { result.id = ConstCastResultIdFnIsGeneric; return result; } if (!wanted_type->data.fn.is_generic && actual_type->data.fn.fn_type_id.return_type->id != ZigTypeIdUnreachable) { ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.fn.fn_type_id.return_type, actual_type->data.fn.fn_type_id.return_type, source_node, false); if (child.id == ConstCastResultIdInvalid) return child; if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdFnReturnType; result.data.return_type = allocate_nonzero(1); *result.data.return_type = child; return result; } } if (!wanted_type->data.fn.is_generic && wanted_type->data.fn.fn_type_id.cc == CallingConventionAsync) { ConstCastOnly child = types_match_const_cast_only(ira, actual_type->data.fn.fn_type_id.async_allocator_type, wanted_type->data.fn.fn_type_id.async_allocator_type, source_node, false); if (child.id == ConstCastResultIdInvalid) return child; if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdAsyncAllocatorType; result.data.async_allocator_type = allocate_nonzero(1); *result.data.async_allocator_type = child; return result; } } if (wanted_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) { result.id = ConstCastResultIdFnArgCount; return result; } if (wanted_type->data.fn.fn_type_id.next_param_index != actual_type->data.fn.fn_type_id.next_param_index) { result.id = ConstCastResultIdFnGenericArgCount; return result; } assert(wanted_type->data.fn.is_generic || wanted_type->data.fn.fn_type_id.next_param_index == wanted_type->data.fn.fn_type_id.param_count); for (size_t i = 0; i < wanted_type->data.fn.fn_type_id.next_param_index; i += 1) { // note it's reversed for parameters FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i]; FnTypeParamInfo *expected_param_info = &wanted_type->data.fn.fn_type_id.param_info[i]; ConstCastOnly arg_child = types_match_const_cast_only(ira, actual_param_info->type, expected_param_info->type, source_node, false); if (arg_child.id == ConstCastResultIdInvalid) return arg_child; if (arg_child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdFnArg; result.data.fn_arg.arg_index = i; result.data.fn_arg.child = allocate_nonzero(1); *result.data.fn_arg.child = arg_child; return result; } if (expected_param_info->is_noalias != actual_param_info->is_noalias) { result.id = ConstCastResultIdFnArgNoAlias; result.data.arg_no_alias.arg_index = i; return result; } } return result; } result.id = ConstCastResultIdType; result.data.type_mismatch = allocate_nonzero(1); result.data.type_mismatch->wanted_type = wanted_type; result.data.type_mismatch->actual_type = actual_type; return result; } static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *errors_count) { size_t old_errors_count = *errors_count; *errors_count = g->errors_by_index.length; *errors = reallocate(*errors, old_errors_count, *errors_count); } static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigType *expected_type, IrInstruction **instructions, size_t instruction_count) { Error err; assert(instruction_count >= 1); IrInstruction *prev_inst = instructions[0]; if (type_is_invalid(prev_inst->value.type)) { return ira->codegen->builtin_types.entry_invalid; } ErrorTableEntry **errors = nullptr; size_t errors_count = 0; ZigType *err_set_type = nullptr; if (prev_inst->value.type->id == ZigTypeIdErrorSet) { if (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; if (!resolve_inferred_error_set(ira->codegen, err_set_type, prev_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } update_errors_helper(ira->codegen, &errors, &errors_count); for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } } } bool any_are_null = (prev_inst->value.type->id == ZigTypeIdNull); bool convert_to_const_slice = false; for (size_t i = 1; i < instruction_count; i += 1) { IrInstruction *cur_inst = instructions[i]; ZigType *cur_type = cur_inst->value.type; ZigType *prev_type = prev_inst->value.type; if (type_is_invalid(cur_type)) { return cur_type; } if (prev_type->id == ZigTypeIdUnreachable) { prev_inst = cur_inst; continue; } if (cur_type->id == ZigTypeIdUnreachable) { continue; } if (prev_type->id == ZigTypeIdErrorSet) { assert(err_set_type != nullptr); if (cur_type->id == ZigTypeIdErrorSet) { if (type_is_global_error_set(err_set_type)) { continue; } if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (type_is_global_error_set(cur_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; prev_inst = cur_inst; continue; } // number of declared errors might have increased now update_errors_helper(ira->codegen, &errors, &errors_count); // if err_set_type is a superset of cur_type, keep err_set_type. // if cur_type is a superset of err_set_type, switch err_set_type to cur_type bool prev_is_superset = true; for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) { ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i]; ErrorTableEntry *error_entry = errors[contained_error_entry->value]; if (error_entry == nullptr) { prev_is_superset = false; break; } } if (prev_is_superset) { continue; } // unset everything in errors for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; errors[error_entry->value] = nullptr; } for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) { assert(errors[i] == nullptr); } for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = cur_type->data.error_set.errors[i]; assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } bool cur_is_superset = true; for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *contained_error_entry = err_set_type->data.error_set.errors[i]; ErrorTableEntry *error_entry = errors[contained_error_entry->value]; if (error_entry == nullptr) { cur_is_superset = false; break; } } if (cur_is_superset) { err_set_type = cur_type; prev_inst = cur_inst; assert(errors != nullptr); continue; } // neither of them are supersets. so we invent a new error set type that is a union of both of them err_set_type = get_error_set_union(ira->codegen, errors, cur_type, err_set_type); assert(errors != nullptr); continue; } else if (cur_type->id == ZigTypeIdErrorUnion) { if (type_is_global_error_set(err_set_type)) { prev_inst = cur_inst; continue; } ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type; if (!resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (type_is_global_error_set(cur_err_set_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; prev_inst = cur_inst; continue; } update_errors_helper(ira->codegen, &errors, &errors_count); // test if err_set_type is a subset of cur_type's error set // unset everything in errors for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; errors[error_entry->value] = nullptr; } for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) { assert(errors[i] == nullptr); } for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i]; assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } bool cur_is_superset = true; for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *contained_error_entry = err_set_type->data.error_set.errors[i]; ErrorTableEntry *error_entry = errors[contained_error_entry->value]; if (error_entry == nullptr) { cur_is_superset = false; break; } } if (cur_is_superset) { err_set_type = cur_err_set_type; prev_inst = cur_inst; assert(errors != nullptr); continue; } // not a subset. invent new error set type, union of both of them err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, err_set_type); prev_inst = cur_inst; assert(errors != nullptr); continue; } else { prev_inst = cur_inst; continue; } } if (cur_type->id == ZigTypeIdErrorSet) { if (prev_type->id == ZigTypeIdArray) { convert_to_const_slice = true; } if (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; } if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } update_errors_helper(ira->codegen, &errors, &errors_count); if (err_set_type == nullptr) { if (prev_type->id == ZigTypeIdErrorUnion) { err_set_type = prev_type->data.error_union.err_set_type; } else { err_set_type = cur_type; } for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } if (err_set_type == cur_type) { continue; } } // check if the cur type error set is a subset bool prev_is_superset = true; for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) { ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i]; ErrorTableEntry *error_entry = errors[contained_error_entry->value]; if (error_entry == nullptr) { prev_is_superset = false; break; } } if (prev_is_superset) { continue; } // not a subset. invent new error set type, union of both of them err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_type); assert(errors != nullptr); continue; } if (prev_type->id == ZigTypeIdErrorUnion && cur_type->id == ZigTypeIdErrorUnion) { ZigType *prev_payload_type = prev_type->data.error_union.payload_type; ZigType *cur_payload_type = cur_type->data.error_union.payload_type; bool const_cast_prev = types_match_const_cast_only(ira, prev_payload_type, cur_payload_type, source_node, false).id == ConstCastResultIdOk; bool const_cast_cur = types_match_const_cast_only(ira, cur_payload_type, prev_payload_type, source_node, false).id == ConstCastResultIdOk; if (const_cast_prev || const_cast_cur) { if (const_cast_cur) { prev_inst = cur_inst; } ZigType *prev_err_set_type = (err_set_type == nullptr) ? prev_type->data.error_union.err_set_type : err_set_type; ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type; if (!resolve_inferred_error_set(ira->codegen, prev_err_set_type, cur_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (!resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (type_is_global_error_set(prev_err_set_type) || type_is_global_error_set(cur_err_set_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; continue; } update_errors_helper(ira->codegen, &errors, &errors_count); if (err_set_type == nullptr) { err_set_type = prev_err_set_type; for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = prev_err_set_type->data.error_set.errors[i]; assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } } bool prev_is_superset = true; for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *contained_error_entry = cur_err_set_type->data.error_set.errors[i]; ErrorTableEntry *error_entry = errors[contained_error_entry->value]; if (error_entry == nullptr) { prev_is_superset = false; break; } } if (prev_is_superset) { continue; } // unset all the errors for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; errors[error_entry->value] = nullptr; } for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) { assert(errors[i] == nullptr); } for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i]; assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } bool cur_is_superset = true; for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *contained_error_entry = prev_err_set_type->data.error_set.errors[i]; ErrorTableEntry *error_entry = errors[contained_error_entry->value]; if (error_entry == nullptr) { cur_is_superset = false; break; } } if (cur_is_superset) { err_set_type = cur_err_set_type; continue; } err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, prev_err_set_type); continue; } } if (prev_type->id == ZigTypeIdNull) { prev_inst = cur_inst; continue; } if (cur_type->id == ZigTypeIdNull) { any_are_null = true; continue; } if (types_match_const_cast_only(ira, prev_type, cur_type, source_node, false).id == ConstCastResultIdOk) { continue; } if (types_match_const_cast_only(ira, cur_type, prev_type, source_node, false).id == ConstCastResultIdOk) { prev_inst = cur_inst; continue; } if (prev_type->id == ZigTypeIdInt && cur_type->id == ZigTypeIdInt && prev_type->data.integral.is_signed == cur_type->data.integral.is_signed) { if (cur_type->data.integral.bit_count > prev_type->data.integral.bit_count) { prev_inst = cur_inst; } continue; } if (prev_type->id == ZigTypeIdFloat && cur_type->id == ZigTypeIdFloat) { if (cur_type->data.floating.bit_count > prev_type->data.floating.bit_count) { prev_inst = cur_inst; } continue; } if (prev_type->id == ZigTypeIdErrorUnion && types_match_const_cast_only(ira, prev_type->data.error_union.payload_type, cur_type, source_node, false).id == ConstCastResultIdOk) { continue; } if (cur_type->id == ZigTypeIdErrorUnion && types_match_const_cast_only(ira, cur_type->data.error_union.payload_type, prev_type, source_node, false).id == ConstCastResultIdOk) { if (err_set_type != nullptr) { ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type; if (!resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (type_is_global_error_set(cur_err_set_type) || type_is_global_error_set(err_set_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; prev_inst = cur_inst; continue; } update_errors_helper(ira->codegen, &errors, &errors_count); err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_err_set_type); } prev_inst = cur_inst; continue; } if (prev_type->id == ZigTypeIdOptional && types_match_const_cast_only(ira, prev_type->data.maybe.child_type, cur_type, source_node, false).id == ConstCastResultIdOk) { continue; } if (cur_type->id == ZigTypeIdOptional && types_match_const_cast_only(ira, cur_type->data.maybe.child_type, prev_type, source_node, false).id == ConstCastResultIdOk) { prev_inst = cur_inst; continue; } if (cur_type->id == ZigTypeIdUndefined) { continue; } if (prev_type->id == ZigTypeIdUndefined) { prev_inst = cur_inst; continue; } if (prev_type->id == ZigTypeIdComptimeInt || prev_type->id == ZigTypeIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, prev_inst, cur_type, false)) { prev_inst = cur_inst; continue; } else { return ira->codegen->builtin_types.entry_invalid; } } if (cur_type->id == ZigTypeIdComptimeInt || cur_type->id == ZigTypeIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, cur_inst, prev_type, false)) { continue; } else { return ira->codegen->builtin_types.entry_invalid; } } if (cur_type->id == ZigTypeIdArray && prev_type->id == ZigTypeIdArray && cur_type->data.array.len != prev_type->data.array.len && types_match_const_cast_only(ira, cur_type->data.array.child_type, prev_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) { convert_to_const_slice = true; prev_inst = cur_inst; continue; } if (cur_type->id == ZigTypeIdArray && prev_type->id == ZigTypeIdArray && cur_type->data.array.len != prev_type->data.array.len && types_match_const_cast_only(ira, prev_type->data.array.child_type, cur_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) { convert_to_const_slice = true; continue; } if (cur_type->id == ZigTypeIdArray && is_slice(prev_type) && (prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const || cur_type->data.array.len == 0) && types_match_const_cast_only(ira, prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, cur_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) { convert_to_const_slice = false; continue; } if (prev_type->id == ZigTypeIdArray && is_slice(cur_type) && (cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const || prev_type->data.array.len == 0) && types_match_const_cast_only(ira, cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, prev_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) { prev_inst = cur_inst; convert_to_const_slice = false; continue; } if (prev_type->id == ZigTypeIdEnum && cur_type->id == ZigTypeIdUnion && (cur_type->data.unionation.decl_node->data.container_decl.auto_enum || cur_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) { if ((err = type_resolve(ira->codegen, cur_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; if (cur_type->data.unionation.tag_type == prev_type) { continue; } } if (cur_type->id == ZigTypeIdEnum && prev_type->id == ZigTypeIdUnion && (prev_type->data.unionation.decl_node->data.container_decl.auto_enum || prev_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) { if ((err = type_resolve(ira->codegen, prev_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; if (prev_type->data.unionation.tag_type == cur_type) { prev_inst = cur_inst; continue; } } ErrorMsg *msg = ir_add_error_node(ira, source_node, buf_sprintf("incompatible types: '%s' and '%s'", buf_ptr(&prev_type->name), buf_ptr(&cur_type->name))); add_error_note(ira->codegen, msg, prev_inst->source_node, buf_sprintf("type '%s' here", buf_ptr(&prev_type->name))); add_error_note(ira->codegen, msg, cur_inst->source_node, buf_sprintf("type '%s' here", buf_ptr(&cur_type->name))); return ira->codegen->builtin_types.entry_invalid; } free(errors); if (convert_to_const_slice) { assert(prev_inst->value.type->id == ZigTypeIdArray); ZigType *ptr_type = get_pointer_to_type_extra( ira->codegen, prev_inst->value.type->data.array.child_type, true, false, PtrLenUnknown, 0, 0, 0); ZigType *slice_type = get_slice_type(ira->codegen, ptr_type); if (err_set_type != nullptr) { return get_error_union_type(ira->codegen, err_set_type, slice_type); } else { return slice_type; } } else if (err_set_type != nullptr) { if (prev_inst->value.type->id == ZigTypeIdErrorSet) { return err_set_type; } else if (prev_inst->value.type->id == ZigTypeIdErrorUnion) { return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type->data.error_union.payload_type); } else if (expected_type != nullptr && expected_type->id == ZigTypeIdErrorUnion) { return get_error_union_type(ira->codegen, err_set_type, expected_type->data.error_union.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 { return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type); } } } else if (any_are_null && prev_inst->value.type->id != ZigTypeIdNull) { if (prev_inst->value.type->id == ZigTypeIdComptimeInt || prev_inst->value.type->id == ZigTypeIdComptimeFloat) { ir_add_error_node(ira, source_node, buf_sprintf("unable to make maybe out of number literal")); return ira->codegen->builtin_types.entry_invalid; } else if (prev_inst->value.type->id == ZigTypeIdOptional) { return prev_inst->value.type; } else { return get_optional_type(ira->codegen, prev_inst->value.type); } } else { return prev_inst->value.type; } } static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, ZigType *type_entry) { if (type_has_bits(type_entry) && handle_is_ptr(type_entry)) { ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); if (fn_entry != nullptr) { fn_entry->alloca_list.append(instruction); } } } static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs) { ConstGlobalRefs *global_refs = dest->global_refs; *dest = *src; if (!same_global_refs) { dest->global_refs = global_refs; if (dest->type->id == ZigTypeIdStruct) { dest->data.x_struct.fields = allocate_nonzero(dest->type->data.structure.src_field_count); memcpy(dest->data.x_struct.fields, src->data.x_struct.fields, sizeof(ConstExprValue) * dest->type->data.structure.src_field_count); } } } static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_instr, CastOp cast_op, ConstExprValue *other_val, ZigType *other_type, ConstExprValue *const_val, ZigType *new_type) { const_val->special = other_val->special; assert(other_val != const_val); switch (cast_op) { case CastOpNoCast: zig_unreachable(); case CastOpErrSet: case CastOpBitCast: case CastOpPtrOfArrayToSlice: zig_panic("TODO"); case CastOpNoop: { bool same_global_refs = other_val->special == ConstValSpecialStatic; copy_const_val(const_val, other_val, same_global_refs); const_val->type = new_type; break; } case CastOpNumLitToConcrete: if (other_val->type->id == ZigTypeIdComptimeFloat) { assert(new_type->id == ZigTypeIdFloat); switch (new_type->data.floating.bit_count) { case 16: const_val->data.x_f16 = bigfloat_to_f16(&other_val->data.x_bigfloat); break; case 32: const_val->data.x_f32 = bigfloat_to_f32(&other_val->data.x_bigfloat); break; case 64: const_val->data.x_f64 = bigfloat_to_f64(&other_val->data.x_bigfloat); break; case 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 CastOpResizeSlice: case CastOpBytesToSlice: // can't do it zig_unreachable(); 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 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_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type, CastOp cast_op, bool need_alloca) { if ((instr_is_comptime(value) || !type_has_bits(wanted_type)) && cast_op != CastOpResizeSlice && cast_op != CastOpBytesToSlice) { IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, &value->value, value->value.type, &result->value, wanted_type)) { return ira->codegen->invalid_instruction; } return result; } else { IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, cast_op); result->value.type = wanted_type; if (need_alloca) { ir_add_alloca(ira, result, wanted_type); } return result; } } static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { assert(value->value.type->id == ZigTypeIdPointer); Error err; if ((err = type_resolve(ira->codegen, value->value.type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { return ira->codegen->invalid_instruction; } wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value.type)); if (instr_is_comptime(value)) { ConstExprValue *pointee = ir_const_ptr_pointee(ira, &value->value, source_instr->source_node); if (pointee == nullptr) return ira->codegen->invalid_instruction; if (pointee->special != ConstValSpecialRuntime) { IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); result->value.type = wanted_type; result->value.data.x_ptr.special = ConstPtrSpecialBaseArray; result->value.data.x_ptr.mut = value->value.data.x_ptr.mut; result->value.data.x_ptr.data.base_array.array_val = pointee; result->value.data.x_ptr.data.base_array.elem_index = 0; result->value.data.x_ptr.data.base_array.is_cstr = false; return result; } } IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, CastOpBitCast); result->value.type = wanted_type; return result; } static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { Error err; if ((err = type_resolve(ira->codegen, value->value.type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { return ira->codegen->invalid_instruction; } wanted_type = adjust_slice_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value.type)); if (instr_is_comptime(value)) { ConstExprValue *pointee = ir_const_ptr_pointee(ira, &value->value, source_instr->source_node); if (pointee == nullptr) return ira->codegen->invalid_instruction; if (pointee->special != ConstValSpecialRuntime) { assert(value->value.type->id == ZigTypeIdPointer); ZigType *array_type = value->value.type->data.pointer.child_type; assert(is_slice(wanted_type)); bool is_const = wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const; IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); init_const_slice(ira->codegen, &result->value, pointee, 0, array_type->data.array.len, is_const); result->value.data.x_struct.fields[slice_ptr_index].data.x_ptr.mut = value->value.data.x_ptr.mut; result->value.type = wanted_type; return result; } } IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, CastOpPtrOfArrayToSlice); result->value.type = wanted_type; ir_add_alloca(ira, result, wanted_type); return result; } static bool is_container(ZigType *type) { return type->id == ZigTypeIdStruct || type->id == ZigTypeIdEnum || type->id == ZigTypeIdUnion; } static IrBasicBlock *ir_get_new_bb(IrAnalyze *ira, IrBasicBlock *old_bb, IrInstruction *ref_old_instruction) { assert(old_bb); if (old_bb->other) { if (ref_old_instruction == nullptr || old_bb->other->ref_instruction != ref_old_instruction) { return old_bb->other; } } IrBasicBlock *new_bb = ir_build_bb_from(&ira->new_irb, old_bb); new_bb->ref_instruction = ref_old_instruction; return new_bb; } static IrBasicBlock *ir_get_new_bb_runtime(IrAnalyze *ira, IrBasicBlock *old_bb, IrInstruction *ref_old_instruction) { assert(ref_old_instruction != nullptr); IrBasicBlock *new_bb = ir_get_new_bb(ira, old_bb, ref_old_instruction); if (new_bb->must_be_comptime_source_instr) { ErrorMsg *msg = ir_add_error(ira, ref_old_instruction, buf_sprintf("control flow attempts to use compile-time variable at runtime")); add_error_note(ira->codegen, msg, new_bb->must_be_comptime_source_instr->source_node, buf_sprintf("compile-time variable assigned here")); return nullptr; } return new_bb; } static void ir_start_bb(IrAnalyze *ira, IrBasicBlock *old_bb, IrBasicBlock *const_predecessor_bb) { ira->instruction_index = 0; ira->old_irb.current_basic_block = old_bb; ira->const_predecessor_bb = const_predecessor_bb; } static void ir_finish_bb(IrAnalyze *ira) { ira->new_irb.exec->basic_block_list.append(ira->new_irb.current_basic_block); ira->instruction_index += 1; while (ira->instruction_index < ira->old_irb.current_basic_block->instruction_list.length) { IrInstruction *next_instruction = ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index); if (!next_instruction->is_gen) { ir_add_error(ira, next_instruction, buf_sprintf("unreachable code")); break; } ira->instruction_index += 1; } ira->old_bb_index += 1; bool need_repeat = true; for (;;) { while (ira->old_bb_index < ira->old_irb.exec->basic_block_list.length) { IrBasicBlock *old_bb = ira->old_irb.exec->basic_block_list.at(ira->old_bb_index); if (old_bb->other == nullptr) { ira->old_bb_index += 1; continue; } if (old_bb->other->instruction_list.length != 0) { ira->old_bb_index += 1; continue; } ira->new_irb.current_basic_block = old_bb->other; ir_start_bb(ira, old_bb, nullptr); return; } if (!need_repeat) return; need_repeat = false; ira->old_bb_index = 0; continue; } } static ZigType *ir_unreach_error(IrAnalyze *ira) { ira->old_bb_index = SIZE_MAX; ira->new_irb.exec->invalid = true; return ira->codegen->builtin_types.entry_unreachable; } static bool ir_emit_backward_branch(IrAnalyze *ira, IrInstruction *source_instruction) { size_t *bbc = ira->new_irb.exec->backward_branch_count; size_t quota = ira->new_irb.exec->backward_branch_quota; // If we're already over quota, we've already given an error message for this. if (*bbc > quota) { return false; } *bbc += 1; if (*bbc > quota) { ir_add_error(ira, source_instruction, buf_sprintf("evaluation exceeded %" ZIG_PRI_usize " backwards branches", quota)); return false; } return true; } static ZigType *ir_inline_bb(IrAnalyze *ira, IrInstruction *source_instruction, IrBasicBlock *old_bb) { if (old_bb->debug_id <= ira->old_irb.current_basic_block->debug_id) { if (!ir_emit_backward_branch(ira, source_instruction)) return ir_unreach_error(ira); } old_bb->other = ira->old_irb.current_basic_block->other; ir_start_bb(ira, old_bb, ira->old_irb.current_basic_block); return ira->codegen->builtin_types.entry_unreachable; } static ZigType *ir_finish_anal(IrAnalyze *ira, ZigType *result_type) { if (result_type->id == ZigTypeIdUnreachable) ir_finish_bb(ira); return result_type; } static IrInstruction *ir_get_const(IrAnalyze *ira, IrInstruction *old_instruction) { IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, old_instruction->scope, old_instruction->source_node); IrInstruction *new_instruction = &const_instruction->base; new_instruction->value.special = ConstValSpecialStatic; return new_instruction; } static ConstExprValue *ir_build_const_from(IrAnalyze *ira, IrInstruction *old_instruction) { IrInstruction *new_instruction = ir_get_const(ira, old_instruction); ir_link_new_instruction(new_instruction, old_instruction); return &new_instruction->value; } static ZigType *ir_analyze_void(IrAnalyze *ira, IrInstruction *instruction) { ir_build_const_from(ira, instruction); return ira->codegen->builtin_types.entry_void; } static IrInstruction *ir_get_const_ptr(IrAnalyze *ira, IrInstruction *instruction, ConstExprValue *pointee, ZigType *pointee_type, ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile, uint32_t ptr_align) { ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, pointee_type, ptr_is_const, ptr_is_volatile, PtrLenSingle, ptr_align, 0, 0); IrInstruction *const_instr = ir_get_const(ira, instruction); ConstExprValue *const_val = &const_instr->value; const_val->type = ptr_type; 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 ZigType *ir_analyze_const_ptr(IrAnalyze *ira, IrInstruction *instruction, ConstExprValue *pointee, ZigType *pointee_type, ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile) { IrInstruction *const_instr = ir_get_const_ptr(ira, instruction, pointee, pointee_type, ptr_mut, ptr_is_const, ptr_is_volatile, 0); ir_link_new_instruction(const_instr, instruction); return const_instr->value.type; } static ZigType *ir_analyze_const_usize(IrAnalyze *ira, IrInstruction *instruction, uint64_t value) { ConstExprValue *const_val = ir_build_const_from(ira, instruction); bigint_init_unsigned(&const_val->data.x_bigint, value); return ira->codegen->builtin_types.entry_usize; } enum UndefAllowed { UndefOk, UndefBad, }; static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed) { switch (value->value.special) { case ConstValSpecialStatic: return &value->value; case ConstValSpecialRuntime: if (!type_has_bits(value->value.type)) { return &value->value; } ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression")); return nullptr; case ConstValSpecialUndef: if (undef_allowed == UndefOk) { return &value->value; } else { ir_add_error(ira, value, buf_sprintf("use of undefined value")); return nullptr; } } zig_unreachable(); } IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, ZigType *expected_type, size_t *backward_branch_count, size_t backward_branch_quota, ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name, IrExecutable *parent_exec) { if (expected_type != nullptr && type_is_invalid(expected_type)) return codegen->invalid_instruction; IrExecutable *ir_executable = allocate(1); ir_executable->source_node = source_node; ir_executable->parent_exec = parent_exec; ir_executable->name = exec_name; ir_executable->is_inline = true; ir_executable->fn_entry = fn_entry; ir_executable->c_import_buf = c_import_buf; ir_executable->begin_scope = scope; ir_gen(codegen, node, scope, ir_executable); if (ir_executable->invalid) return codegen->invalid_instruction; if (codegen->verbose_ir) { fprintf(stderr, "\nSource: "); ast_render(codegen, stderr, node, 4); fprintf(stderr, "\n{ // (IR)\n"); ir_print(codegen, stderr, ir_executable, 4); fprintf(stderr, "}\n"); } IrExecutable *analyzed_executable = allocate(1); analyzed_executable->source_node = source_node; analyzed_executable->parent_exec = parent_exec; analyzed_executable->source_exec = ir_executable; analyzed_executable->name = exec_name; analyzed_executable->is_inline = true; analyzed_executable->fn_entry = fn_entry; analyzed_executable->c_import_buf = c_import_buf; analyzed_executable->backward_branch_count = backward_branch_count; analyzed_executable->backward_branch_quota = backward_branch_quota; analyzed_executable->begin_scope = scope; ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, node); if (type_is_invalid(result_type)) return codegen->invalid_instruction; if (codegen->verbose_ir) { fprintf(stderr, "{ // (analyzed)\n"); ir_print(codegen, stderr, analyzed_executable, 4); fprintf(stderr, "}\n"); } return ir_exec_const_result(codegen, analyzed_executable); } static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value) { if (type_is_invalid(type_value->value.type)) return ira->codegen->builtin_types.entry_invalid; if (type_value->value.type->id != ZigTypeIdMetaType) { ir_add_error(ira, type_value, buf_sprintf("expected type 'type', found '%s'", buf_ptr(&type_value->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *const_val = ir_resolve_const(ira, type_value, UndefBad); if (!const_val) return ira->codegen->builtin_types.entry_invalid; return const_val->data.x_type; } static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { if (fn_value == ira->codegen->invalid_instruction) return nullptr; if (type_is_invalid(fn_value->value.type)) return nullptr; if (fn_value->value.type->id != ZigTypeIdFn) { ir_add_error_node(ira, fn_value->source_node, buf_sprintf("expected function type, found '%s'", buf_ptr(&fn_value->value.type->name))); return nullptr; } ConstExprValue *const_val = ir_resolve_const(ira, fn_value, UndefBad); if (!const_val) return nullptr; assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction); return const_val->data.x_ptr.data.fn.fn_entry; } static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { assert(wanted_type->id == ZigTypeIdOptional); if (instr_is_comptime(value)) { ZigType *payload_type = wanted_type->data.maybe.child_type; IrInstruction *casted_payload = ir_implicit_cast(ira, value, payload_type); if (type_is_invalid(casted_payload->value.type)) return ira->codegen->invalid_instruction; ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefOk); if (!val) return ira->codegen->invalid_instruction; IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.special = ConstValSpecialStatic; if (get_codegen_ptr_type(wanted_type) != nullptr) { copy_const_val(&const_instruction->base.value, val, val->data.x_ptr.mut == ConstPtrMutComptimeConst); } else { const_instruction->base.value.data.x_optional = val; } const_instruction->base.value.type = wanted_type; return &const_instruction->base; } IrInstruction *result = ir_build_maybe_wrap(&ira->new_irb, source_instr->scope, source_instr->source_node, value); result->value.type = wanted_type; result->value.data.rh_maybe = RuntimeHintOptionalNonNull; ir_add_alloca(ira, result, wanted_type); return result; } static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { assert(wanted_type->id == ZigTypeIdErrorUnion); if (instr_is_comptime(value)) { ZigType *payload_type = wanted_type->data.error_union.payload_type; IrInstruction *casted_payload = ir_implicit_cast(ira, value, payload_type); if (type_is_invalid(casted_payload->value.type)) return ira->codegen->invalid_instruction; ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefBad); if (!val) return ira->codegen->invalid_instruction; IrInstructionConst *const_instruction = ir_create_instruction(&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.err = nullptr; const_instruction->base.value.data.x_err_union.payload = val; return &const_instruction->base; } IrInstruction *result = ir_build_err_wrap_payload(&ira->new_irb, source_instr->scope, source_instr->source_node, value); result->value.type = wanted_type; result->value.data.rh_error_union = RuntimeHintErrorUnionNonError; ir_add_alloca(ira, result, wanted_type); return result; } static IrInstruction *ir_analyze_err_set_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { assert(value->value.type->id == ZigTypeIdErrorSet); assert(wanted_type->id == ZigTypeIdErrorSet); if (instr_is_comptime(value)) { ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); if (!val) return ira->codegen->invalid_instruction; if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) { return ira->codegen->invalid_instruction; } if (!type_is_global_error_set(wanted_type)) { bool subset = false; for (uint32_t i = 0, count = wanted_type->data.error_set.err_count; i < count; i += 1) { if (wanted_type->data.error_set.errors[i]->value == val->data.x_err_set->value) { subset = true; break; } } if (!subset) { ir_add_error(ira, source_instr, buf_sprintf("error.%s not a member of error set '%s'", buf_ptr(&val->data.x_err_set->name), buf_ptr(&wanted_type->name))); return ira->codegen->invalid_instruction; } } IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.type = wanted_type; const_instruction->base.value.special = ConstValSpecialStatic; const_instruction->base.value.data.x_err_set = val->data.x_err_set; return &const_instruction->base; } IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, CastOpErrSet); result->value.type = wanted_type; return result; } static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { assert(wanted_type->id == ZigTypeIdErrorUnion); IrInstruction *casted_value = ir_implicit_cast(ira, value, wanted_type->data.error_union.err_set_type); if (instr_is_comptime(casted_value)) { ConstExprValue *val = ir_resolve_const(ira, casted_value, UndefBad); if (!val) return ira->codegen->invalid_instruction; IrInstructionConst *const_instruction = ir_create_instruction(&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.err = val->data.x_err_set; const_instruction->base.value.data.x_err_union.payload = nullptr; return &const_instruction->base; } IrInstruction *result = ir_build_err_wrap_code(&ira->new_irb, source_instr->scope, source_instr->source_node, value); result->value.type = wanted_type; result->value.data.rh_error_union = RuntimeHintErrorUnionError; ir_add_alloca(ira, result, wanted_type); return result; } static IrInstruction *ir_analyze_cast_ref(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { if (instr_is_comptime(value)) { ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); if (!val) return ira->codegen->invalid_instruction; IrInstructionConst *const_instruction = ir_create_instruction(&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 = val; return &const_instruction->base; } if (value->id == IrInstructionIdLoadPtr) { IrInstructionLoadPtr *load_ptr_inst = (IrInstructionLoadPtr *)value; return load_ptr_inst->ptr; } else { IrInstruction *new_instruction = ir_build_ref(&ira->new_irb, source_instr->scope, source_instr->source_node, value, true, false); new_instruction->value.type = wanted_type; ZigType *child_type = wanted_type->data.pointer.child_type; if (type_has_bits(child_type)) { ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); assert(fn_entry); fn_entry->alloca_list.append(new_instruction); } ir_add_alloca(ira, new_instruction, child_type); return new_instruction; } } static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { assert(wanted_type->id == ZigTypeIdOptional); assert(instr_is_comptime(value)); ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); assert(val); IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.special = ConstValSpecialStatic; if (get_codegen_ptr_type(wanted_type) != nullptr) { const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr; const_instruction->base.value.data.x_ptr.data.hard_coded_addr.addr = 0; } else { const_instruction->base.value.data.x_optional = nullptr; } const_instruction->base.value.type = wanted_type; return &const_instruction->base; } static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *value, bool is_const, bool is_volatile) { Error err; if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; if ((err = type_resolve(ira->codegen, value->value.type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; if (instr_is_comptime(value)) { ConstExprValue *val = ir_resolve_const(ira, value, UndefOk); if (!val) return ira->codegen->invalid_instruction; return ir_get_const_ptr(ira, source_instruction, val, value->value.type, ConstPtrMutComptimeConst, is_const, is_volatile, 0); } ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value->value.type, is_const, is_volatile, PtrLenSingle, 0, 0, 0); IrInstruction *new_instruction = ir_build_ref(&ira->new_irb, source_instruction->scope, source_instruction->source_node, value, is_const, is_volatile); new_instruction->value.type = ptr_type; new_instruction->value.data.rh_ptr = RuntimeHintPtrStack; if (type_has_bits(ptr_type)) { ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); assert(fn_entry); fn_entry->alloca_list.append(new_instruction); } return new_instruction; } static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *array_arg, ZigType *wanted_type) { assert(is_slice(wanted_type)); // In this function we honor the const-ness of wanted_type, because // we may be casting [0]T to []const T which is perfectly valid. IrInstruction *array_ptr = nullptr; IrInstruction *array; if (array_arg->value.type->id == ZigTypeIdPointer) { array = ir_get_deref(ira, source_instr, array_arg); array_ptr = array_arg; } else { array = array_arg; } ZigType *array_type = array->value.type; assert(array_type->id == ZigTypeIdArray); if (instr_is_comptime(array)) { IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); init_const_slice(ira->codegen, &result->value, &array->value, 0, array_type->data.array.len, true); result->value.type = wanted_type; return result; } IrInstruction *start = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, ira->codegen->builtin_types.entry_usize); init_const_usize(ira->codegen, &start->value, 0); IrInstruction *end = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, ira->codegen->builtin_types.entry_usize); init_const_usize(ira->codegen, &end->value, array_type->data.array.len); if (!array_ptr) array_ptr = ir_get_ref(ira, source_instr, array, true, false); IrInstruction *result = ir_build_slice(&ira->new_irb, source_instr->scope, source_instr->source_node, array_ptr, start, end, false); result->value.type = wanted_type; result->value.data.rh_slice.id = RuntimeHintSliceIdLen; result->value.data.rh_slice.len = array_type->data.array.len; ir_add_alloca(ira, result, result->value.type); return result; } static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, ZigType *wanted_type) { Error err; assert(wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdComptimeInt); ZigType *actual_type = target->value.type; if ((err = ensure_complete_type(ira->codegen, actual_type))) return ira->codegen->invalid_instruction; if (wanted_type != actual_type->data.enumeration.tag_int_type) { ir_add_error(ira, source_instr, buf_sprintf("enum to integer cast to '%s' instead of its tag type, '%s'", buf_ptr(&wanted_type->name), buf_ptr(&actual_type->data.enumeration.tag_int_type->name))); return ira->codegen->invalid_instruction; } assert(actual_type->id == ZigTypeIdEnum); if (instr_is_comptime(target)) { ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); init_const_bigint(&result->value, wanted_type, &val->data.x_enum_tag); return result; } // If there is only one possible tag, then we know at comptime what it is. if (actual_type->data.enumeration.layout == ContainerLayoutAuto && actual_type->data.enumeration.src_field_count == 1) { assert(wanted_type== ira->codegen->builtin_types.entry_num_lit_int); IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); init_const_bigint(&result->value, wanted_type, &actual_type->data.enumeration.fields[0].value); 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_union_to_tag(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, ZigType *wanted_type) { assert(target->value.type->id == ZigTypeIdUnion); assert(wanted_type->id == ZigTypeIdEnum); assert(wanted_type == target->value.type->data.unionation.tag_type); if (instr_is_comptime(target)) { ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, 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_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, 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_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); init_const_undefined(ira->codegen, &result->value); return result; } static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, ZigType *wanted_type) { Error err; assert(wanted_type->id == ZigTypeIdUnion); assert(target->value.type->id == ZigTypeIdEnum); if (instr_is_comptime(target)) { ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag); assert(union_field != nullptr); if ((err = type_resolve(ira->codegen, union_field->type_entry, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; if (type_has_bits(union_field->type_entry)) { AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at( union_field->enum_field->decl_index); ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast to union '%s' must initialize '%s' field '%s'", buf_ptr(&wanted_type->name), buf_ptr(&union_field->type_entry->name), buf_ptr(union_field->name))); add_error_note(ira->codegen, msg, field_node, buf_sprintf("field '%s' declared here", buf_ptr(union_field->name))); return ira->codegen->invalid_instruction; } IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, 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); return result; } // if the union has all fields 0 bits, we can do it // and in fact it's a noop cast because the union value is just the enum value if (wanted_type->data.unionation.gen_field_count == 0) { IrInstruction *result = ir_build_cast(&ira->new_irb, target->scope, target->source_node, wanted_type, target, CastOpNoop); result->value.type = wanted_type; return result; } ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("runtime cast to union '%s' which has non-void fields", buf_ptr(&wanted_type->name))); for (uint32_t i = 0; i < wanted_type->data.unionation.src_field_count; i += 1) { TypeUnionField *union_field = &wanted_type->data.unionation.fields[i]; if (type_has_bits(union_field->type_entry)) { AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(i); add_error_note(ira->codegen, msg, field_node, buf_sprintf("field '%s' has type '%s'", buf_ptr(union_field->name), buf_ptr(&union_field->type_entry->name))); } } return ira->codegen->invalid_instruction; } static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, ZigType *wanted_type) { assert(wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdFloat); if (instr_is_comptime(target)) { ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; if (wanted_type->id == ZigTypeIdInt) { if (bigint_cmp_zero(&val->data.x_bigint) == CmpLT && !wanted_type->data.integral.is_signed) { ir_add_error(ira, source_instr, buf_sprintf("attempt to cast negative value to unsigned integer")); return ira->codegen->invalid_instruction; } if (!bigint_fits_in_bits(&val->data.x_bigint, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) { ir_add_error(ira, source_instr, buf_sprintf("cast from '%s' to '%s' truncates bits", buf_ptr(&target->value.type->name), buf_ptr(&wanted_type->name))); return ira->codegen->invalid_instruction; } } IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, 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; } IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope, source_instr->source_node, target); result->value.type = wanted_type; return result; } static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, ZigType *wanted_type) { Error err; assert(wanted_type->id == ZigTypeIdEnum); ZigType *actual_type = target->value.type; if ((err = ensure_complete_type(ira->codegen, wanted_type))) return ira->codegen->invalid_instruction; if (actual_type != wanted_type->data.enumeration.tag_int_type) { ir_add_error(ira, source_instr, buf_sprintf("integer to enum cast from '%s' instead of its tag type, '%s'", buf_ptr(&actual_type->name), buf_ptr(&wanted_type->data.enumeration.tag_int_type->name))); return ira->codegen->invalid_instruction; } assert(actual_type->id == ZigTypeIdInt); if (instr_is_comptime(target)) { ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; TypeEnumField *field = find_enum_field_by_tag(wanted_type, &val->data.x_bigint); if (field == nullptr) { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, &val->data.x_bigint, 10); ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("enum '%s' has no tag matching integer value %s", buf_ptr(&wanted_type->name), buf_ptr(val_buf))); add_error_note(ira->codegen, msg, wanted_type->data.enumeration.decl_node, buf_sprintf("'%s' declared here", buf_ptr(&wanted_type->name))); return ira->codegen->invalid_instruction; } IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); bigint_init_bigint(&result->value.data.x_enum_tag, &val->data.x_bigint); return result; } IrInstruction *result = ir_build_int_to_enum(&ira->new_irb, source_instr->scope, source_instr->source_node, nullptr, target); result->value.type = wanted_type; return result; } static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, ZigType *wanted_type) { ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); if (wanted_type->id == ZigTypeIdComptimeFloat) { float_init_float(&result->value, val); } else if (wanted_type->id == ZigTypeIdComptimeInt) { bigint_init_bigint(&result->value.data.x_bigint, &val->data.x_bigint); } else { zig_unreachable(); } return result; } static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, ZigType *wanted_type) { assert(target->value.type->id == ZigTypeIdInt); assert(!target->value.type->data.integral.is_signed); assert(wanted_type->id == ZigTypeIdErrorSet); if (instr_is_comptime(target)) { ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) { return ira->codegen->invalid_instruction; } if (type_is_global_error_set(wanted_type)) { BigInt err_count; bigint_init_unsigned(&err_count, ira->codegen->errors_by_index.length); if (bigint_cmp_zero(&val->data.x_bigint) == CmpEQ || bigint_cmp(&val->data.x_bigint, &err_count) != CmpLT) { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, &val->data.x_bigint, 10); ir_add_error(ira, source_instr, buf_sprintf("integer value %s represents no error", buf_ptr(val_buf))); return ira->codegen->invalid_instruction; } size_t index = bigint_as_unsigned(&val->data.x_bigint); result->value.data.x_err_set = ira->codegen->errors_by_index.at(index); return result; } else { ErrorTableEntry *err = nullptr; BigInt err_int; for (uint32_t i = 0, count = wanted_type->data.error_set.err_count; i < count; i += 1) { ErrorTableEntry *this_err = wanted_type->data.error_set.errors[i]; bigint_init_unsigned(&err_int, this_err->value); if (bigint_cmp(&val->data.x_bigint, &err_int) == CmpEQ) { err = this_err; break; } } if (err == nullptr) { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, &val->data.x_bigint, 10); ir_add_error(ira, source_instr, buf_sprintf("integer value %s represents no error in '%s'", buf_ptr(val_buf), buf_ptr(&wanted_type->name))); return ira->codegen->invalid_instruction; } result->value.data.x_err_set = err; return result; } } IrInstruction *result = ir_build_int_to_err(&ira->new_irb, source_instr->scope, source_instr->source_node, target); result->value.type = wanted_type; return result; } static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, ZigType *wanted_type) { assert(wanted_type->id == ZigTypeIdInt); ZigType *err_type = target->value.type; if (instr_is_comptime(target)) { ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); ErrorTableEntry *err; if (err_type->id == ZigTypeIdErrorUnion) { err = val->data.x_err_union.err; } 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_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); result->value.type = 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_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); result->value.type = 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; wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, target->value.type)); ZigType *array_type = wanted_type->data.pointer.child_type; assert(array_type->id == ZigTypeIdArray); assert(array_type->data.array.len == 1); if (instr_is_comptime(target)) { ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; assert(val->type->id == ZigTypeIdPointer); ConstExprValue *pointee = ir_const_ptr_pointee(ira, val, source_instr->source_node); if (pointee == nullptr) return ira->codegen->invalid_instruction; if (pointee->special != ConstValSpecialRuntime) { ConstExprValue *array_val = create_const_vals(1); array_val->special = ConstValSpecialStatic; array_val->type = array_type; array_val->data.x_array.special = ConstArraySpecialNone; array_val->data.x_array.s_none.elements = pointee; array_val->data.x_array.s_none.parent.id = ConstParentIdScalar; array_val->data.x_array.s_none.parent.data.p_scalar.scalar_val = pointee; IrInstructionConst *const_instruction = ir_create_instruction(&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 *missing_errors = &cast_result->data.error_set_mismatch->missing_errors; for (size_t i = 0; i < missing_errors->length; i += 1) { ErrorTableEntry *error_entry = missing_errors->at(i); add_error_note(ira->codegen, parent_msg, error_entry->decl_node, buf_sprintf("'error.%s' not a member of destination error set", buf_ptr(&error_entry->name))); } break; } case ConstCastResultIdErrSetGlobal: { add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("cannot cast global error set into smaller set")); break; } case ConstCastResultIdPointerChild: { ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("pointer type child '%s' cannot cast into pointer type child '%s'", buf_ptr(&cast_result->data.pointer_mismatch->actual_child->name), buf_ptr(&cast_result->data.pointer_mismatch->wanted_child->name))); report_recursive_error(ira, source_node, &cast_result->data.pointer_mismatch->child, msg); break; } case ConstCastResultIdSliceChild: { ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("slice type child '%s' cannot cast into slice type child '%s'", buf_ptr(&cast_result->data.slice_mismatch->actual_child->name), buf_ptr(&cast_result->data.slice_mismatch->wanted_child->name))); report_recursive_error(ira, source_node, &cast_result->data.slice_mismatch->child, msg); break; } case ConstCastResultIdErrorUnionPayload: { ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("error union payload '%s' cannot cast into error union payload '%s'", buf_ptr(&cast_result->data.error_union_payload->actual_payload->name), buf_ptr(&cast_result->data.error_union_payload->wanted_payload->name))); report_recursive_error(ira, source_node, &cast_result->data.error_union_payload->child, msg); break; } case ConstCastResultIdType: { AstNode *wanted_decl_node = type_decl_node(cast_result->data.type_mismatch->wanted_type); AstNode *actual_decl_node = type_decl_node(cast_result->data.type_mismatch->actual_type); if (wanted_decl_node != nullptr) { add_error_note(ira->codegen, parent_msg, wanted_decl_node, buf_sprintf("%s declared here", buf_ptr(&cast_result->data.type_mismatch->wanted_type->name))); } if (actual_decl_node != nullptr) { add_error_note(ira->codegen, parent_msg, actual_decl_node, buf_sprintf("%s declared here", buf_ptr(&cast_result->data.type_mismatch->actual_type->name))); } break; } case ConstCastResultIdFnAlign: // TODO case ConstCastResultIdFnCC: // TODO case ConstCastResultIdFnVarArgs: // TODO case ConstCastResultIdFnIsGeneric: // TODO case ConstCastResultIdFnReturnType: // TODO case ConstCastResultIdFnArgCount: // TODO case ConstCastResultIdFnGenericArgCount: // TODO case ConstCastResultIdFnArg: // TODO case ConstCastResultIdFnArgNoAlias: // TODO case ConstCastResultIdUnresolvedInferredErrSet: // TODO case ConstCastResultIdAsyncAllocatorType: // TODO case ConstCastResultIdNullWrapPtr: // TODO break; } } static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, ZigType *wanted_type, IrInstruction *value) { Error err; ZigType *actual_type = value->value.type; AstNode *source_node = source_instr->source_node; if (type_is_invalid(wanted_type) || type_is_invalid(actual_type)) { return ira->codegen->invalid_instruction; } // perfect match or non-const to const ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type, source_node, false); if (const_cast_result.id == ConstCastResultIdInvalid) return ira->codegen->invalid_instruction; if (const_cast_result.id == ConstCastResultIdOk) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); } // widening conversion if (wanted_type->id == ZigTypeIdInt && actual_type->id == ZigTypeIdInt && wanted_type->data.integral.is_signed == actual_type->data.integral.is_signed && wanted_type->data.integral.bit_count >= actual_type->data.integral.bit_count) { return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); } // small enough unsigned ints can get casted to large enough signed ints if (wanted_type->id == ZigTypeIdInt && wanted_type->data.integral.is_signed && actual_type->id == ZigTypeIdInt && !actual_type->data.integral.is_signed && wanted_type->data.integral.bit_count > actual_type->data.integral.bit_count) { return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); } // float widening conversion if (wanted_type->id == ZigTypeIdFloat && actual_type->id == ZigTypeIdFloat && wanted_type->data.floating.bit_count >= actual_type->data.floating.bit_count) { return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); } // cast from [N]T to []const T if (is_slice(wanted_type) && actual_type->id == ZigTypeIdArray) { ZigType *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == ZigTypeIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) { return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type); } } // cast from *const [N]T to []const T if (is_slice(wanted_type) && actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.is_const && actual_type->data.pointer.child_type->id == ZigTypeIdArray) { ZigType *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == ZigTypeIdPointer); ZigType *array_type = actual_type->data.pointer.child_type; if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) && types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) { return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type); } } // cast from [N]T to *const []const T if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.is_const && is_slice(wanted_type->data.pointer.child_type) && actual_type->id == ZigTypeIdArray) { ZigType *ptr_type = wanted_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == ZigTypeIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.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; } } // cast from [N]T to ?[]const T if (wanted_type->id == ZigTypeIdOptional && is_slice(wanted_type->data.maybe.child_type) && actual_type->id == ZigTypeIdArray) { ZigType *ptr_type = wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == ZigTypeIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value); if (type_is_invalid(cast1->value.type)) return ira->codegen->invalid_instruction; IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); if (type_is_invalid(cast2->value.type)) return ira->codegen->invalid_instruction; return cast2; } } // *[N]T to [*]T if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenUnknown && actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle && actual_type->data.pointer.child_type->id == ZigTypeIdArray) { if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) return ira->codegen->invalid_instruction; if ((err = type_resolve(ira->codegen, wanted_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) return ira->codegen->invalid_instruction; if (get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_type) && types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, actual_type->data.pointer.child_type->data.array.child_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) { return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type); } } // *[N]T to []T if (is_slice(wanted_type) && actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle && actual_type->data.pointer.child_type->id == ZigTypeIdArray) { ZigType *slice_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; assert(slice_ptr_type->id == ZigTypeIdPointer); if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, actual_type->data.pointer.child_type->data.array.child_type, source_node, !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk) { return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type); } } // cast from 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_maybe_wrap(ira, source_instr, value, wanted_type); } else if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) { return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); } else { return ira->codegen->invalid_instruction; } } else if (wanted_child_type->id == ZigTypeIdPointer && wanted_child_type->data.pointer.is_const && (actual_type->id == ZigTypeIdPointer || is_container(actual_type))) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_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; } 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_maybe_wrap(ira, source_instr, cast1, 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 child type of error type to error type if (wanted_type->id == ZigTypeIdErrorUnion) { if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type, source_node, false).id == ConstCastResultIdOk) { return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type); } else if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error_union.payload_type, true)) { return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type); } else { return ira->codegen->invalid_instruction; } } } // cast from [N]T to E![]const T if (wanted_type->id == ZigTypeIdErrorUnion && is_slice(wanted_type->data.error_union.payload_type) && actual_type->id == ZigTypeIdArray) { ZigType *ptr_type = wanted_type->data.error_union.payload_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == ZigTypeIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value); if (type_is_invalid(cast1->value.type)) return ira->codegen->invalid_instruction; IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); if (type_is_invalid(cast2->value.type)) return ira->codegen->invalid_instruction; return cast2; } } // cast from error set to error union type if (wanted_type->id == ZigTypeIdErrorUnion && actual_type->id == ZigTypeIdErrorSet) { return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type); } // 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 number literal to another type // cast from number literal to *const integer if (actual_type->id == ZigTypeIdComptimeFloat || actual_type->id == ZigTypeIdComptimeInt) { if ((err = ensure_complete_type(ira->codegen, wanted_type))) return ira->codegen->invalid_instruction; if (wanted_type->id == ZigTypeIdEnum) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.enumeration.tag_int_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 if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.is_const) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.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; } else if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) { CastOp op; if ((actual_type->id == ZigTypeIdComptimeFloat && wanted_type->id == ZigTypeIdFloat) || (actual_type->id == ZigTypeIdComptimeInt && wanted_type->id == ZigTypeIdInt)) { op = CastOpNumLitToConcrete; } else if (wanted_type->id == ZigTypeIdInt) { op = CastOpFloatToInt; } else if (wanted_type->id == ZigTypeIdFloat) { op = CastOpIntToFloat; } else { zig_unreachable(); } return ir_resolve_cast(ira, source_instr, value, wanted_type, op, false); } else { return ira->codegen->invalid_instruction; } } // 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 union to the enum type of the union if (actual_type->id == ZigTypeIdUnion && wanted_type->id == ZigTypeIdEnum) { if ((err = type_resolve(ira->codegen, actual_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; if (actual_type->data.unionation.tag_type == wanted_type) { return ir_analyze_union_to_tag(ira, source_instr, value, wanted_type); } } // enum to union which has the enum as the tag type if (wanted_type->id == ZigTypeIdUnion && actual_type->id == ZigTypeIdEnum && (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum || wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) { if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; if (wanted_type->data.unionation.tag_type == actual_type) { return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type); } } // enum to &const union which has the enum as the tag type if (actual_type->id == ZigTypeIdEnum && wanted_type->id == ZigTypeIdPointer) { ZigType *union_type = wanted_type->data.pointer.child_type; if (union_type->data.unionation.decl_node->data.container_decl.auto_enum || union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr) { if ((err = type_resolve(ira->codegen, union_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; if (union_type->data.unionation.tag_type == actual_type) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, union_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 *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) { if ((err = type_resolve(ira->codegen, wanted_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { return ira->codegen->invalid_instruction; } if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { return ira->codegen->invalid_instruction; } uint32_t wanted_align = get_ptr_align(ira->codegen, wanted_type); uint32_t actual_align = get_ptr_align(ira->codegen, actual_type); if (wanted_align > actual_align) { ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment")); add_error_note(ira->codegen, msg, value->source_node, buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name), actual_align)); add_error_note(ira->codegen, msg, source_instr->source_node, buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name), wanted_align)); return ira->codegen->invalid_instruction; } return ir_analyze_ptr_to_array(ira, source_instr, value, wanted_type); } } // cast from T to *T where T is zero bits if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) { if ((err = type_resolve(ira->codegen, actual_type, ResolveStatusZeroBitsKnown))) { return ira->codegen->invalid_instruction; } if (!type_has_bits(actual_type)) { return ir_get_ref(ira, source_instr, value, false, false); } } // cast from undefined to anything if (actual_type->id == ZigTypeIdUndefined) { return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); } // cast from something to const pointer of it if (!type_requires_comptime(actual_type)) { ZigType *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true); if (types_match_const_cast_only(ira, wanted_type, const_ptr_actual, source_node, false).id == ConstCastResultIdOk) { return ir_analyze_cast_ref(ira, source_instr, value, wanted_type); } } ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node, buf_sprintf("expected type '%s', found '%s'", buf_ptr(&wanted_type->name), buf_ptr(&actual_type->name))); report_recursive_error(ira, source_instr->source_node, &const_cast_result, parent_msg); return ira->codegen->invalid_instruction; } static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, ZigType *expected_type) { assert(value); assert(value != ira->codegen->invalid_instruction); assert(!expected_type || !type_is_invalid(expected_type)); assert(value->value.type); assert(!type_is_invalid(value->value.type)); if (expected_type == nullptr) return value; // anything will do if (expected_type == value->value.type) return value; // match if (value->value.type->id == ZigTypeIdUnreachable) return value; return ir_analyze_cast(ira, value, expected_type, value); } static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr) { ZigType *type_entry = ptr->value.type; if (type_is_invalid(type_entry)) { return ira->codegen->invalid_instruction; } else if (type_entry->id == ZigTypeIdPointer) { ZigType *child_type = type_entry->data.pointer.child_type; if (instr_is_comptime(ptr)) { if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst || ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) { ConstExprValue *pointee = ir_const_ptr_pointee(ira, &ptr->value, source_instruction->source_node); if (pointee == nullptr) return ira->codegen->invalid_instruction; if (pointee->special != ConstValSpecialRuntime) { IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope, source_instruction->source_node, child_type); copy_const_val(&result->value, pointee, ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst); result->value.type = child_type; return result; } } } // dereferencing a *u0 is comptime known to be 0 if (child_type->id == ZigTypeIdInt && child_type->data.integral.bit_count == 0) { IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope, source_instruction->source_node, child_type); init_const_unsigned_negative(&result->value, child_type, 0, false); return result; } // TODO if the instruction is a const ref instruction we can skip it IrInstruction *load_ptr_instruction = ir_build_load_ptr(&ira->new_irb, source_instruction->scope, source_instruction->source_node, ptr); load_ptr_instruction->value.type = child_type; return load_ptr_instruction; } else { ir_add_error_node(ira, source_instruction->source_node, buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; } } static ZigType *ir_analyze_ref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *value, bool is_const, bool is_volatile) { IrInstruction *result = ir_get_ref(ira, source_instruction, value, is_const, is_volatile); ir_link_new_instruction(result, source_instruction); return result->value.type; } static bool ir_resolve_align(IrAnalyze *ira, IrInstruction *value, uint32_t *out) { if (type_is_invalid(value->value.type)) return false; IrInstruction *casted_value = ir_implicit_cast(ira, value, get_align_amt_type(ira->codegen)); if (type_is_invalid(casted_value->value.type)) return false; ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return false; uint32_t align_bytes = bigint_as_unsigned(&const_val->data.x_bigint); if (align_bytes == 0) { ir_add_error(ira, value, buf_sprintf("alignment must be >= 1")); return false; } if (!is_power_of_2(align_bytes)) { ir_add_error(ira, value, buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes)); return false; } *out = align_bytes; return true; } static bool ir_resolve_unsigned(IrAnalyze *ira, IrInstruction *value, ZigType *int_type, uint64_t *out) { if (type_is_invalid(value->value.type)) return false; IrInstruction *casted_value = ir_implicit_cast(ira, value, int_type); if (type_is_invalid(casted_value->value.type)) return false; ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return false; *out = bigint_as_unsigned(&const_val->data.x_bigint); return true; } static bool ir_resolve_usize(IrAnalyze *ira, IrInstruction *value, uint64_t *out) { return ir_resolve_unsigned(ira, value, ira->codegen->builtin_types.entry_usize, out); } static bool ir_resolve_bool(IrAnalyze *ira, IrInstruction *value, bool *out) { if (type_is_invalid(value->value.type)) return false; IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_bool); if (type_is_invalid(casted_value->value.type)) return false; ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return false; *out = const_val->data.x_bool; return true; } static bool ir_resolve_comptime(IrAnalyze *ira, IrInstruction *value, bool *out) { if (!value) { *out = false; return true; } return ir_resolve_bool(ira, value, out); } static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, AtomicOrder *out) { if (type_is_invalid(value->value.type)) return false; ConstExprValue *atomic_order_val = get_builtin_value(ira->codegen, "AtomicOrder"); assert(atomic_order_val->type->id == ZigTypeIdMetaType); ZigType *atomic_order_type = atomic_order_val->data.x_type; IrInstruction *casted_value = ir_implicit_cast(ira, value, atomic_order_type); if (type_is_invalid(casted_value->value.type)) return false; ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return false; *out = (AtomicOrder)bigint_as_unsigned(&const_val->data.x_enum_tag); return true; } static bool ir_resolve_atomic_rmw_op(IrAnalyze *ira, IrInstruction *value, AtomicRmwOp *out) { if (type_is_invalid(value->value.type)) return false; ConstExprValue *atomic_rmw_op_val = get_builtin_value(ira->codegen, "AtomicRmwOp"); assert(atomic_rmw_op_val->type->id == ZigTypeIdMetaType); ZigType *atomic_rmw_op_type = atomic_rmw_op_val->data.x_type; IrInstruction *casted_value = ir_implicit_cast(ira, value, atomic_rmw_op_type); if (type_is_invalid(casted_value->value.type)) return false; ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return false; *out = (AtomicRmwOp)bigint_as_unsigned(&const_val->data.x_enum_tag); return true; } static bool ir_resolve_global_linkage(IrAnalyze *ira, IrInstruction *value, GlobalLinkageId *out) { if (type_is_invalid(value->value.type)) return false; ConstExprValue *global_linkage_val = get_builtin_value(ira->codegen, "GlobalLinkage"); assert(global_linkage_val->type->id == ZigTypeIdMetaType); ZigType *global_linkage_type = global_linkage_val->data.x_type; IrInstruction *casted_value = ir_implicit_cast(ira, value, global_linkage_type); if (type_is_invalid(casted_value->value.type)) return false; ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return false; *out = (GlobalLinkageId)bigint_as_unsigned(&const_val->data.x_enum_tag); return true; } static bool ir_resolve_float_mode(IrAnalyze *ira, IrInstruction *value, FloatMode *out) { if (type_is_invalid(value->value.type)) return false; ConstExprValue *float_mode_val = get_builtin_value(ira->codegen, "FloatMode"); assert(float_mode_val->type->id == ZigTypeIdMetaType); ZigType *float_mode_type = float_mode_val->data.x_type; IrInstruction *casted_value = ir_implicit_cast(ira, value, float_mode_type); if (type_is_invalid(casted_value->value.type)) return false; ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return false; *out = (FloatMode)bigint_as_unsigned(&const_val->data.x_enum_tag); return true; } static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) { if (type_is_invalid(value->value.type)) return nullptr; ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, true, false, PtrLenUnknown, 0, 0, 0); ZigType *str_type = get_slice_type(ira->codegen, ptr_type); IrInstruction *casted_value = ir_implicit_cast(ira, value, str_type); if (type_is_invalid(casted_value->value.type)) return nullptr; ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return nullptr; ConstExprValue *ptr_field = &const_val->data.x_struct.fields[slice_ptr_index]; ConstExprValue *len_field = &const_val->data.x_struct.fields[slice_len_index]; assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray); ConstExprValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val; expand_undef_array(ira->codegen, array_val); size_t len = bigint_as_unsigned(&len_field->data.x_bigint); Buf *result = buf_alloc(); buf_resize(result, len); for (size_t i = 0; i < len; i += 1) { size_t new_index = ptr_field->data.x_ptr.data.base_array.elem_index + i; ConstExprValue *char_val = &array_val->data.x_array.s_none.elements[new_index]; if (char_val->special == ConstValSpecialUndef) { ir_add_error(ira, casted_value, buf_sprintf("use of undefined value")); return nullptr; } uint64_t big_c = bigint_as_unsigned(&char_val->data.x_bigint); assert(big_c <= UINT8_MAX); uint8_t c = (uint8_t)big_c; buf_ptr(result)[i] = c; } return result; } static ZigType *ir_analyze_instruction_add_implicit_return_type(IrAnalyze *ira, IrInstructionAddImplicitReturnType *instruction) { IrInstruction *value = instruction->value->other; if (type_is_invalid(value->value.type)) return ir_unreach_error(ira); ira->src_implicit_return_type_list.append(value); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->type = ira->codegen->builtin_types.entry_void; return out_val->type; } static ZigType *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructionReturn *return_instruction) { IrInstruction *value = return_instruction->value->other; if (type_is_invalid(value->value.type)) return ir_unreach_error(ira); IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->explicit_return_type); if (casted_value == ira->codegen->invalid_instruction) return ir_unreach_error(ira); if (casted_value->value.special == ConstValSpecialRuntime && casted_value->value.type->id == ZigTypeIdPointer && casted_value->value.data.rh_ptr == RuntimeHintPtrStack) { ir_add_error(ira, casted_value, buf_sprintf("function returns address of local variable")); return ir_unreach_error(ira); } IrInstruction *result = ir_build_return(&ira->new_irb, return_instruction->base.scope, return_instruction->base.source_node, casted_value); result->value.type = ira->codegen->builtin_types.entry_unreachable; ir_link_new_instruction(result, &return_instruction->base); return ir_finish_anal(ira, result->value.type); } static ZigType *ir_analyze_instruction_const(IrAnalyze *ira, IrInstructionConst *const_instruction) { ConstExprValue *out_val = ir_build_const_from(ira, &const_instruction->base); *out_val = const_instruction->base.value; return const_instruction->base.value.type; } static ZigType *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrInstruction *op1 = bin_op_instruction->op1->other; if (type_is_invalid(op1->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *op2 = bin_op_instruction->op2->other; if (type_is_invalid(op2->value.type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, bool_type); if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base); ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); if (op1_val == nullptr) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->builtin_types.entry_invalid; assert(casted_op1->value.type->id == ZigTypeIdBool); assert(casted_op2->value.type->id == ZigTypeIdBool); if (bin_op_instruction->op_id == IrBinOpBoolOr) { out_val->data.x_bool = op1_val->data.x_bool || op2_val->data.x_bool; } else if (bin_op_instruction->op_id == IrBinOpBoolAnd) { out_val->data.x_bool = op1_val->data.x_bool && op2_val->data.x_bool; } else { zig_unreachable(); } return bool_type; } ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, bin_op_instruction->op_id, casted_op1, casted_op2, bin_op_instruction->safety_check_on); return bool_type; } static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) { if (op_id == IrBinOpCmpEq) { return cmp == CmpEQ; } else if (op_id == IrBinOpCmpNotEq) { return cmp != CmpEQ; } else if (op_id == IrBinOpCmpLessThan) { return cmp == CmpLT; } else if (op_id == IrBinOpCmpGreaterThan) { return cmp == CmpGT; } else if (op_id == IrBinOpCmpLessOrEq) { return cmp != CmpGT; } else if (op_id == IrBinOpCmpGreaterOrEq) { return cmp != CmpLT; } else { zig_unreachable(); } } static bool optional_value_is_null(ConstExprValue *val) { assert(val->special == ConstValSpecialStatic); if (get_codegen_ptr_type(val->type) != nullptr) { return val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && val->data.x_ptr.data.hard_coded_addr.addr == 0; } else { return val->data.x_optional == nullptr; } } static ZigType *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { Error err; IrInstruction *op1 = bin_op_instruction->op1->other; if (type_is_invalid(op1->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *op2 = bin_op_instruction->op2->other; if (type_is_invalid(op2->value.type)) return ira->codegen->builtin_types.entry_invalid; 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 == ZigTypeIdOptional) || (op2->value.type->id == ZigTypeIdNull && op1->value.type->id == ZigTypeIdOptional) || (op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdNull))) { if (op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdNull) { ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base); out_val->data.x_bool = (op_id == IrBinOpCmpEq); return ira->codegen->builtin_types.entry_bool; } IrInstruction *maybe_op; if (op1->value.type->id == ZigTypeIdNull) { maybe_op = op2; } else if (op2->value.type->id == ZigTypeIdNull) { maybe_op = op1; } else { zig_unreachable(); } if (instr_is_comptime(maybe_op)) { ConstExprValue *maybe_val = ir_resolve_const(ira, maybe_op, UndefBad); if (!maybe_val) return ira->codegen->builtin_types.entry_invalid; bool is_null = optional_value_is_null(maybe_val); ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base); out_val->data.x_bool = (op_id == IrBinOpCmpEq) ? is_null : !is_null; return ira->codegen->builtin_types.entry_bool; } IrInstruction *is_non_null = ir_build_test_nonnull(&ira->new_irb, bin_op_instruction->base.scope, source_node, maybe_op); is_non_null->value.type = ira->codegen->builtin_types.entry_bool; if (op_id == IrBinOpCmpEq) { ir_build_bool_not_from(&ira->new_irb, &bin_op_instruction->base, is_non_null); } else { ir_link_new_instruction(is_non_null, &bin_op_instruction->base); } return ira->codegen->builtin_types.entry_bool; } else if (op1->value.type->id == ZigTypeIdNull || op2->value.type->id == ZigTypeIdNull) { ir_add_error_node(ira, source_node, buf_sprintf("comparison against null can only be done with optionals")); return ira->codegen->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; } if (!resolve_inferred_error_set(ira->codegen, intersect_type, source_node)) { return ira->codegen->builtin_types.entry_invalid; } // 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(); } ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base); out_val->data.x_bool = answer; return ira->codegen->builtin_types.entry_bool; } 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->builtin_types.entry_invalid; } 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(); } ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base); out_val->data.x_bool = answer; return ira->codegen->builtin_types.entry_bool; } } if (instr_is_comptime(op1) && instr_is_comptime(op2)) { ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad); if (op1_val == nullptr) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad); if (op2_val == nullptr) return ira->codegen->builtin_types.entry_invalid; 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(); } ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base); out_val->data.x_bool = answer; return ira->codegen->builtin_types.entry_bool; } ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id, op1, op2, bin_op_instruction->safety_check_on); return ira->codegen->builtin_types.entry_bool; } IrInstruction *instructions[] = {op1, op2}; ZigType *resolved_type = ir_resolve_peer_types(ira, source_node, nullptr, instructions, 2); if (type_is_invalid(resolved_type)) return resolved_type; bool operator_allowed; switch (resolved_type->id) { case ZigTypeIdInvalid: zig_unreachable(); // handled above case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdInt: case ZigTypeIdFloat: operator_allowed = true; break; case ZigTypeIdBool: case ZigTypeIdMetaType: case ZigTypeIdVoid: case ZigTypeIdPointer: case ZigTypeIdErrorSet: case ZigTypeIdFn: case ZigTypeIdOpaque: case ZigTypeIdNamespace: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdPromise: case ZigTypeIdEnum: operator_allowed = is_equality_cmp; break; case ZigTypeIdUnreachable: case ZigTypeIdArray: case ZigTypeIdStruct: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdErrorUnion: case ZigTypeIdUnion: operator_allowed = false; break; case ZigTypeIdOptional: operator_allowed = is_equality_cmp && get_codegen_ptr_type(resolved_type) != nullptr; break; } if (!operator_allowed) { ir_add_error_node(ira, source_node, buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); return ira->codegen->builtin_types.entry_invalid; } IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, resolved_type); if (casted_op1 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, resolved_type); if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; if ((err = type_resolve(ira->codegen, resolved_type, ResolveStatusZeroBitsKnown))) return resolved_type; bool one_possible_value = !type_requires_comptime(resolved_type) && !type_has_bits(resolved_type); if (one_possible_value || (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2))) { ConstExprValue *op1_val = one_possible_value ? &casted_op1->value : ir_resolve_const(ira, casted_op1, UndefBad); if (op1_val == nullptr) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *op2_val = one_possible_value ? &casted_op2->value : ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->builtin_types.entry_invalid; bool answer; if (resolved_type->id == ZigTypeIdComptimeFloat || resolved_type->id == ZigTypeIdFloat) { Cmp cmp_result = float_cmp(op1_val, op2_val); answer = resolve_cmp_op_id(op_id, cmp_result); } else if (resolved_type->id == ZigTypeIdComptimeInt || resolved_type->id == ZigTypeIdInt) { Cmp cmp_result = bigint_cmp(&op1_val->data.x_bigint, &op2_val->data.x_bigint); answer = resolve_cmp_op_id(op_id, cmp_result); } else { bool are_equal = one_possible_value || const_values_equal(op1_val, op2_val); if (op_id == IrBinOpCmpEq) { answer = are_equal; } else if (op_id == IrBinOpCmpNotEq) { answer = !are_equal; } else { zig_unreachable(); } } ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base); out_val->data.x_bool = answer; return ira->codegen->builtin_types.entry_bool; } // some comparisons with unsigned numbers can be evaluated if (resolved_type->id == ZigTypeIdInt && !resolved_type->data.integral.is_signed) { ConstExprValue *known_left_val; IrBinOp flipped_op_id; if (instr_is_comptime(casted_op1)) { known_left_val = ir_resolve_const(ira, casted_op1, UndefBad); if (known_left_val == nullptr) return ira->codegen->builtin_types.entry_invalid; flipped_op_id = op_id; } else if (instr_is_comptime(casted_op2)) { known_left_val = ir_resolve_const(ira, casted_op2, UndefBad); if (known_left_val == nullptr) return ira->codegen->builtin_types.entry_invalid; if (op_id == IrBinOpCmpLessThan) { flipped_op_id = IrBinOpCmpGreaterThan; } else if (op_id == IrBinOpCmpGreaterThan) { flipped_op_id = IrBinOpCmpLessThan; } else if (op_id == IrBinOpCmpLessOrEq) { flipped_op_id = IrBinOpCmpGreaterOrEq; } else if (op_id == IrBinOpCmpGreaterOrEq) { flipped_op_id = IrBinOpCmpLessOrEq; } else { flipped_op_id = op_id; } } else { known_left_val = nullptr; } if (known_left_val != nullptr && bigint_cmp_zero(&known_left_val->data.x_bigint) == CmpEQ && (flipped_op_id == IrBinOpCmpLessOrEq || flipped_op_id == IrBinOpCmpGreaterThan)) { bool answer = (flipped_op_id == IrBinOpCmpLessOrEq); ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base); out_val->data.x_bool = answer; return ira->codegen->builtin_types.entry_bool; } } ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id, casted_op1, casted_op2, bin_op_instruction->safety_check_on); return ira->codegen->builtin_types.entry_bool; } static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val, IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val) { bool is_int; bool is_float; Cmp op2_zcmp; if (type_entry->id == ZigTypeIdInt || type_entry->id == ZigTypeIdComptimeInt) { is_int = true; is_float = false; op2_zcmp = bigint_cmp_zero(&op2_val->data.x_bigint); } else if (type_entry->id == ZigTypeIdFloat || type_entry->id == ZigTypeIdComptimeFloat) { is_int = false; is_float = true; op2_zcmp = float_cmp_zero(op2_val); } else { zig_unreachable(); } if ((op_id == IrBinOpDivUnspecified || op_id == IrBinOpRemRem || op_id == IrBinOpRemMod || op_id == IrBinOpDivTrunc || op_id == IrBinOpDivFloor) && op2_zcmp == CmpEQ) { return ErrorDivByZero; } if ((op_id == IrBinOpRemRem || op_id == IrBinOpRemMod) && op2_zcmp == CmpLT) { return ErrorNegativeDenominator; } switch (op_id) { case IrBinOpInvalid: case IrBinOpBoolOr: case IrBinOpBoolAnd: case IrBinOpCmpEq: case IrBinOpCmpNotEq: case IrBinOpCmpLessThan: case IrBinOpCmpGreaterThan: case IrBinOpCmpLessOrEq: case IrBinOpCmpGreaterOrEq: case IrBinOpArrayCat: case IrBinOpArrayMult: case IrBinOpRemUnspecified: case IrBinOpMergeErrorSets: zig_unreachable(); case IrBinOpBinOr: assert(is_int); bigint_or(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); break; case IrBinOpBinXor: assert(is_int); bigint_xor(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); break; case IrBinOpBinAnd: assert(is_int); bigint_and(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); break; case IrBinOpBitShiftLeftExact: assert(is_int); bigint_shl(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); break; case IrBinOpBitShiftLeftLossy: assert(type_entry->id == ZigTypeIdInt); bigint_shl_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); break; case IrBinOpBitShiftRightExact: { assert(is_int); bigint_shr(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); BigInt orig_bigint; bigint_shl(&orig_bigint, &out_val->data.x_bigint, &op2_val->data.x_bigint); if (bigint_cmp(&op1_val->data.x_bigint, &orig_bigint) != CmpEQ) { return ErrorShiftedOutOneBits; } 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 ErrorExactDivRemainder; } } else { float_div_trunc(out_val, op1_val, op2_val); ConstExprValue remainder; float_rem(&remainder, op1_val, op2_val); if (float_cmp_zero(&remainder) != CmpEQ) { return ErrorExactDivRemainder; } } 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 ErrorOverflow; } } out_val->type = type_entry; out_val->special = ConstValSpecialStatic; return 0; } static ZigType *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrInstruction *op1 = bin_op_instruction->op1->other; if (type_is_invalid(op1->value.type)) return ira->codegen->builtin_types.entry_invalid; if (op1->value.type->id != ZigTypeIdInt && op1->value.type->id != ZigTypeIdComptimeInt) { ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("bit shifting operation expected integer type, found '%s'", buf_ptr(&op1->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } IrInstruction *op2 = bin_op_instruction->op2->other; if (type_is_invalid(op2->value.type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } } else { ZigType *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen, op1->value.type->data.integral.bit_count - 1); if (bin_op_instruction->op_id == IrBinOpBitShiftLeftLossy && op2->value.type->id == ZigTypeIdComptimeInt) { if (!bigint_fits_in_bits(&op2->value.data.x_bigint, shift_amt_type->data.integral.bit_count, op2->value.data.x_bigint.is_negative)) { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, &op2->value.data.x_bigint, 10); ErrorMsg* msg = ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("RHS of shift is too large for LHS type")); add_error_note( ira->codegen, msg, op2->source_node, buf_sprintf("value %s cannot fit into type %s", buf_ptr(val_buf), buf_ptr(&shift_amt_type->name))); return ira->codegen->builtin_types.entry_invalid; } } casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type); if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; } if (instr_is_comptime(op1) && instr_is_comptime(casted_op2)) { ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad); if (op1_val == nullptr) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->builtin_types.entry_invalid; IrInstruction *result_instruction = ir_get_const(ira, &bin_op_instruction->base); ir_link_new_instruction(result_instruction, &bin_op_instruction->base); ConstExprValue *out_val = &result_instruction->value; int err; if ((err = ir_eval_math_op(op1->value.type, op1_val, op_id, op2_val, out_val))) { if (err == ErrorOverflow) { ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("operation caused overflow")); return ira->codegen->builtin_types.entry_invalid; } else if (err == ErrorShiftedOutOneBits) { ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("exact shift shifted out 1 bits")); return ira->codegen->builtin_types.entry_invalid; } else { zig_unreachable(); } return ira->codegen->builtin_types.entry_invalid; } ir_num_lit_fits_in_other_type(ira, result_instruction, op1->value.type, false); return op1->value.type; } 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->builtin_types.entry_invalid; } 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; ir_link_new_instruction(result, &bin_op_instruction->base); return result->value.type; } ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id, op1, casted_op2, bin_op_instruction->safety_check_on); return op1->value.type; } static ZigType *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrInstruction *op1 = bin_op_instruction->op1->other; if (type_is_invalid(op1->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *op2 = bin_op_instruction->op2->other; if (type_is_invalid(op2->value.type)) return ira->codegen->builtin_types.entry_invalid; IrBinOp op_id = bin_op_instruction->op_id; // look for pointer math if (op1->value.type->id == ZigTypeIdPointer && op1->value.type->data.pointer.ptr_len == PtrLenUnknown && (op_id == IrBinOpAdd || op_id == IrBinOpSub)) { IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, ira->codegen->builtin_types.entry_usize); if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; 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, true); result->value.type = op1->value.type; ir_link_new_instruction(result, &bin_op_instruction->base); return result->value.type; } IrInstruction *instructions[] = {op1, op2}; ZigType *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, nullptr, instructions, 2); if (type_is_invalid(resolved_type)) return resolved_type; bool is_int = resolved_type->id == ZigTypeIdInt || resolved_type->id == ZigTypeIdComptimeInt; bool is_float = resolved_type->id == ZigTypeIdFloat || resolved_type->id == ZigTypeIdComptimeFloat; bool is_signed_div = ( (resolved_type->id == ZigTypeIdInt && resolved_type->data.integral.is_signed) || resolved_type->id == ZigTypeIdFloat || (resolved_type->id == ZigTypeIdComptimeFloat && ((bigfloat_cmp_zero(&op1->value.data.x_bigfloat) != CmpGT) != (bigfloat_cmp_zero(&op2->value.data.x_bigfloat) != CmpGT))) || (resolved_type->id == ZigTypeIdComptimeInt && ((bigint_cmp_zero(&op1->value.data.x_bigint) != CmpGT) != (bigint_cmp_zero(&op2->value.data.x_bigint) != CmpGT))) ); if (op_id == IrBinOpDivUnspecified && is_int) { if (is_signed_div) { bool ok = false; if (instr_is_comptime(op1) && instr_is_comptime(op2)) { ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad); if (op1_val == nullptr) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad); if (op2_val == nullptr) return ira->codegen->builtin_types.entry_invalid; 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, &bin_op_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->builtin_types.entry_invalid; } } else { op_id = IrBinOpDivTrunc; } } else if (op_id == IrBinOpRemUnspecified) { if (is_signed_div && (is_int || is_float)) { bool ok = false; if (instr_is_comptime(op1) && instr_is_comptime(op2)) { ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad); if (op1_val == nullptr) return ira->codegen->builtin_types.entry_invalid; if (is_int) { ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad); if (op2_val == nullptr) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->builtin_types.entry_invalid; if (float_cmp_zero(&casted_op2->value) == CmpEQ) { // the division by zero error will be caught later, but we don't // have a remainder function ambiguity problem ok = true; } else { ConstExprValue rem_result; ConstExprValue mod_result; float_rem(&rem_result, op1_val, op2_val); float_mod(&mod_result, op1_val, op2_val); ok = float_cmp(&rem_result, &mod_result) == CmpEQ; } } } if (!ok) { ir_add_error(ira, &bin_op_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->builtin_types.entry_invalid; } } op_id = IrBinOpRemRem; } if (is_int) { // int } else if (is_float && (op_id == IrBinOpAdd || op_id == IrBinOpSub || op_id == IrBinOpMult || op_id == IrBinOpDivUnspecified || op_id == IrBinOpDivTrunc || op_id == IrBinOpDivFloor || op_id == IrBinOpDivExact || op_id == IrBinOpRemRem || op_id == IrBinOpRemMod)) { // float } else { AstNode *source_node = bin_op_instruction->base.source_node; ir_add_error_node(ira, source_node, buf_sprintf("invalid operands to binary expression: '%s' and '%s'", buf_ptr(&op1->value.type->name), buf_ptr(&op2->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, resolved_type); if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); if (op1_val == nullptr) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->builtin_types.entry_invalid; IrInstruction *result_instruction = ir_get_const(ira, &bin_op_instruction->base); ir_link_new_instruction(result_instruction, &bin_op_instruction->base); ConstExprValue *out_val = &result_instruction->value; int err; if ((err = ir_eval_math_op(resolved_type, op1_val, op_id, op2_val, out_val))) { if (err == ErrorDivByZero) { ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("division by zero")); return ira->codegen->builtin_types.entry_invalid; } else if (err == ErrorOverflow) { ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("operation caused overflow")); return ira->codegen->builtin_types.entry_invalid; } else if (err == ErrorExactDivRemainder) { ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("exact division had a remainder")); return ira->codegen->builtin_types.entry_invalid; } else if (err == ErrorNegativeDenominator) { ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("negative denominator")); return ira->codegen->builtin_types.entry_invalid; } else { zig_unreachable(); } return ira->codegen->builtin_types.entry_invalid; } ir_num_lit_fits_in_other_type(ira, result_instruction, resolved_type, false); return resolved_type; } ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id, casted_op1, casted_op2, bin_op_instruction->safety_check_on); return resolved_type; } static ZigType *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *instruction) { IrInstruction *op1 = instruction->op1->other; ZigType *op1_type = op1->value.type; if (type_is_invalid(op1_type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *op2 = instruction->op2->other; ZigType *op2_type = op2->value.type; if (type_is_invalid(op2_type)) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad); if (!op1_val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad); if (!op2_val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *op1_array_val; size_t op1_array_index; size_t op1_array_end; ZigType *child_type; if (op1_type->id == ZigTypeIdArray) { child_type = op1_type->data.array.child_type; op1_array_val = op1_val; op1_array_index = 0; op1_array_end = op1_type->data.array.len; } else if (op1_type->id == ZigTypeIdPointer && op1_type->data.pointer.child_type == ira->codegen->builtin_types.entry_u8 && op1_val->data.x_ptr.special == ConstPtrSpecialBaseArray && op1_val->data.x_ptr.data.base_array.is_cstr) { child_type = op1_type->data.pointer.child_type; op1_array_val = op1_val->data.x_ptr.data.base_array.array_val; op1_array_index = op1_val->data.x_ptr.data.base_array.elem_index; op1_array_end = op1_array_val->type->data.array.len - 1; } else if (is_slice(op1_type)) { ZigType *ptr_type = op1_type->data.structure.fields[slice_ptr_index].type_entry; child_type = ptr_type->data.pointer.child_type; ConstExprValue *ptr_val = &op1_val->data.x_struct.fields[slice_ptr_index]; assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray); op1_array_val = ptr_val->data.x_ptr.data.base_array.array_val; op1_array_index = ptr_val->data.x_ptr.data.base_array.elem_index; op1_array_end = op1_array_val->type->data.array.len; } else { ir_add_error(ira, op1, buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op1->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *op2_array_val; size_t op2_array_index; size_t op2_array_end; if (op2_type->id == ZigTypeIdArray) { if (op2_type->data.array.child_type != child_type) { ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", buf_ptr(&child_type->name), buf_ptr(&op2->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } op2_array_val = op2_val; op2_array_index = 0; op2_array_end = op2_array_val->type->data.array.len; } else if (op2_type->id == ZigTypeIdPointer && op2_type->data.pointer.child_type == ira->codegen->builtin_types.entry_u8 && op2_val->data.x_ptr.special == ConstPtrSpecialBaseArray && op2_val->data.x_ptr.data.base_array.is_cstr) { if (child_type != ira->codegen->builtin_types.entry_u8) { ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", buf_ptr(&child_type->name), buf_ptr(&op2->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } op2_array_val = op2_val->data.x_ptr.data.base_array.array_val; op2_array_index = op2_val->data.x_ptr.data.base_array.elem_index; op2_array_end = op2_array_val->type->data.array.len - 1; } else if (is_slice(op2_type)) { ZigType *ptr_type = op2_type->data.structure.fields[slice_ptr_index].type_entry; if (ptr_type->data.pointer.child_type != child_type) { ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", buf_ptr(&child_type->name), buf_ptr(&op2->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *ptr_val = &op2_val->data.x_struct.fields[slice_ptr_index]; assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray); op2_array_val = ptr_val->data.x_ptr.data.base_array.array_val; op2_array_index = ptr_val->data.x_ptr.data.base_array.elem_index; op2_array_end = op2_array_val->type->data.array.len; } 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->builtin_types.entry_invalid; } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); ZigType *result_type; ConstExprValue *out_array_val; size_t new_len = (op1_array_end - op1_array_index) + (op2_array_end - op2_array_index); if (op1_type->id == ZigTypeIdArray || op2_type->id == ZigTypeIdArray) { result_type = get_array_type(ira->codegen, child_type, new_len); out_array_val = out_val; } else if (is_slice(op1_type) || is_slice(op2_type)) { ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, child_type, true, false, PtrLenUnknown, 0, 0, 0); result_type = get_slice_type(ira->codegen, ptr_type); out_array_val = create_const_vals(1); out_array_val->special = ConstValSpecialStatic; out_array_val->type = get_array_type(ira->codegen, child_type, new_len); out_val->data.x_struct.fields = create_const_vals(2); out_val->data.x_struct.fields[slice_ptr_index].type = ptr_type; out_val->data.x_struct.fields[slice_ptr_index].special = ConstValSpecialStatic; out_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.special = ConstPtrSpecialBaseArray; out_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.data.base_array.array_val = out_array_val; out_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.data.base_array.elem_index = 0; out_val->data.x_struct.fields[slice_len_index].type = ira->codegen->builtin_types.entry_usize; out_val->data.x_struct.fields[slice_len_index].special = ConstValSpecialStatic; bigint_init_unsigned(&out_val->data.x_struct.fields[slice_len_index].data.x_bigint, new_len); } else { new_len += 1; // null byte // TODO make this `[*]null T` instead of `[*]T` result_type = get_pointer_to_type_extra(ira->codegen, child_type, true, false, PtrLenUnknown, 0, 0, 0); out_array_val = create_const_vals(1); out_array_val->special = ConstValSpecialStatic; out_array_val->type = get_array_type(ira->codegen, child_type, new_len); out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; out_val->data.x_ptr.data.base_array.is_cstr = true; out_val->data.x_ptr.data.base_array.array_val = out_array_val; out_val->data.x_ptr.data.base_array.elem_index = 0; } if (op1_array_val->data.x_array.special == ConstArraySpecialUndef && op2_array_val->data.x_array.special == ConstArraySpecialUndef) { out_array_val->data.x_array.special = ConstArraySpecialUndef; return result_type; } out_array_val->data.x_array.s_none.elements = create_const_vals(new_len); expand_undef_array(ira->codegen, op1_array_val); expand_undef_array(ira->codegen, op2_array_val); size_t next_index = 0; for (size_t i = op1_array_index; i < op1_array_end; i += 1, next_index += 1) { out_array_val->data.x_array.s_none.elements[next_index] = op1_array_val->data.x_array.s_none.elements[i]; } for (size_t i = op2_array_index; i < op2_array_end; i += 1, next_index += 1) { out_array_val->data.x_array.s_none.elements[next_index] = op2_array_val->data.x_array.s_none.elements[i]; } if (next_index < new_len) { ConstExprValue *null_byte = &out_array_val->data.x_array.s_none.elements[next_index]; init_const_unsigned_negative(null_byte, child_type, 0, false); next_index += 1; } assert(next_index == new_len); return result_type; } static ZigType *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp *instruction) { IrInstruction *op1 = instruction->op1->other; if (type_is_invalid(op1->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *op2 = instruction->op2->other; if (type_is_invalid(op2->value.type)) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *array_val = ir_resolve_const(ira, op1, UndefBad); if (!array_val) return ira->codegen->builtin_types.entry_invalid; uint64_t mult_amt; if (!ir_resolve_usize(ira, op2, &mult_amt)) return ira->codegen->builtin_types.entry_invalid; ZigType *array_type = op1->value.type; if (array_type->id != ZigTypeIdArray) { ir_add_error(ira, op1, buf_sprintf("expected array type, found '%s'", buf_ptr(&op1->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); if (array_val->data.x_array.special == ConstArraySpecialUndef) { out_val->data.x_array.special = ConstArraySpecialUndef; ZigType *child_type = array_type->data.array.child_type; return get_array_type(ira->codegen, child_type, new_array_len); } out_val->data.x_array.s_none.elements = create_const_vals(new_array_len); uint64_t i = 0; for (uint64_t x = 0; x < mult_amt; x += 1) { for (uint64_t y = 0; y < old_array_len; y += 1) { out_val->data.x_array.s_none.elements[i] = array_val->data.x_array.s_none.elements[y]; i += 1; } } assert(i == new_array_len); ZigType *child_type = array_type->data.array.child_type; return get_array_type(ira->codegen, child_type, new_array_len); } static ZigType *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstructionBinOp *instruction) { ZigType *op1_type = ir_resolve_type(ira, instruction->op1->other); if (type_is_invalid(op1_type)) return ira->codegen->builtin_types.entry_invalid; if (op1_type->id != ZigTypeIdErrorSet) { ir_add_error(ira, instruction->op1, buf_sprintf("expected error set type, found '%s'", buf_ptr(&op1_type->name))); return ira->codegen->builtin_types.entry_invalid; } ZigType *op2_type = ir_resolve_type(ira, instruction->op2->other); if (type_is_invalid(op2_type)) return ira->codegen->builtin_types.entry_invalid; if (op2_type->id != ZigTypeIdErrorSet) { ir_add_error(ira, instruction->op2, buf_sprintf("expected error set type, found '%s'", buf_ptr(&op2_type->name))); return ira->codegen->builtin_types.entry_invalid; } if (type_is_global_error_set(op1_type) || type_is_global_error_set(op2_type)) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = ira->codegen->builtin_types.entry_global_error_set; return ira->codegen->builtin_types.entry_type; } if (!resolve_inferred_error_set(ira->codegen, op1_type, instruction->op1->other->source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (!resolve_inferred_error_set(ira->codegen, op2_type, instruction->op2->other->source_node)) { return ira->codegen->builtin_types.entry_invalid; } ErrorTableEntry **errors = allocate(ira->codegen->errors_by_index.length); for (uint32_t i = 0, count = op1_type->data.error_set.err_count; i < count; i += 1) { ErrorTableEntry *error_entry = op1_type->data.error_set.errors[i]; assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } ZigType *result_type = get_error_set_union(ira->codegen, errors, op1_type, op2_type); free(errors); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = result_type; return ira->codegen->builtin_types.entry_type; } static ZigType *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrBinOp op_id = bin_op_instruction->op_id; switch (op_id) { case IrBinOpInvalid: zig_unreachable(); case IrBinOpBoolOr: case IrBinOpBoolAnd: return ir_analyze_bin_op_bool(ira, bin_op_instruction); case IrBinOpCmpEq: case IrBinOpCmpNotEq: case IrBinOpCmpLessThan: case IrBinOpCmpGreaterThan: case IrBinOpCmpLessOrEq: case IrBinOpCmpGreaterOrEq: return ir_analyze_bin_op_cmp(ira, bin_op_instruction); case IrBinOpBitShiftLeftLossy: case IrBinOpBitShiftLeftExact: case IrBinOpBitShiftRightLossy: case IrBinOpBitShiftRightExact: return ir_analyze_bit_shift(ira, bin_op_instruction); case IrBinOpBinOr: case IrBinOpBinXor: case IrBinOpBinAnd: case IrBinOpAdd: case IrBinOpAddWrap: case IrBinOpSub: case IrBinOpSubWrap: case IrBinOpMult: case IrBinOpMultWrap: case IrBinOpDivUnspecified: case IrBinOpDivTrunc: case IrBinOpDivFloor: case IrBinOpDivExact: case IrBinOpRemUnspecified: case IrBinOpRemRem: case IrBinOpRemMod: return ir_analyze_bin_op_math(ira, bin_op_instruction); case IrBinOpArrayCat: return ir_analyze_array_cat(ira, bin_op_instruction); case IrBinOpArrayMult: return ir_analyze_array_mult(ira, bin_op_instruction); case IrBinOpMergeErrorSets: return ir_analyze_merge_error_sets(ira, bin_op_instruction); } zig_unreachable(); } static ZigType *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDeclVar *decl_var_instruction) { Error err; ZigVar *var = decl_var_instruction->var; IrInstruction *init_value = decl_var_instruction->init_value->other; if (type_is_invalid(init_value->value.type)) { var->value->type = ira->codegen->builtin_types.entry_invalid; return var->value->type; } ZigType *explicit_type = nullptr; IrInstruction *var_type = nullptr; if (decl_var_instruction->var_type != nullptr) { var_type = decl_var_instruction->var_type->other; 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->value->type = ira->codegen->builtin_types.entry_invalid; return var->value->type; } } AstNode *source_node = decl_var_instruction->base.source_node; IrInstruction *casted_init_value = ir_implicit_cast(ira, init_value, explicit_type); bool is_comptime_var = ir_get_var_is_comptime(var); bool var_class_requires_const = false; ZigType *result_type = casted_init_value->value.type; if (type_is_invalid(result_type)) { result_type = ira->codegen->builtin_types.entry_invalid; } else { if ((err = type_resolve(ira->codegen, result_type, ResolveStatusZeroBitsKnown))) { result_type = ira->codegen->builtin_types.entry_invalid; } } if (!type_is_invalid(result_type)) { if (result_type->id == ZigTypeIdUnreachable || result_type->id == ZigTypeIdOpaque) { ir_add_error_node(ira, source_node, buf_sprintf("variable of type '%s' not allowed", buf_ptr(&result_type->name))); result_type = ira->codegen->builtin_types.entry_invalid; } else if (type_requires_comptime(result_type)) { 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; } } else { if (casted_init_value->value.special == ConstValSpecialStatic && casted_init_value->value.type->id == ZigTypeIdFn && casted_init_value->value.data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways) { var_class_requires_const = true; if (!var->src_is_const && !is_comptime_var) { ErrorMsg *msg = ir_add_error_node(ira, source_node, buf_sprintf("functions marked inline must be stored in const or comptime var")); AstNode *proto_node = casted_init_value->value.data.x_ptr.data.fn.fn_entry->proto_node; add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here")); result_type = ira->codegen->builtin_types.entry_invalid; } } } } if (var->value->type != nullptr && !is_comptime_var) { // This is at least the second time we've seen this variable declaration during analysis. // This means that this is actually a different variable due to, e.g. an inline while loop. // We make a new variable so that it can hold a different type, and so the debug info can // be distinct. ZigVar *new_var = create_local_var(ira->codegen, var->decl_node, var->child_scope, &var->name, var->src_is_const, var->gen_is_const, var->shadowable, var->is_comptime, true); new_var->owner_exec = var->owner_exec; new_var->align_bytes = var->align_bytes; if (var->mem_slot_index != SIZE_MAX) { ConstExprValue *vals = create_const_vals(1); new_var->mem_slot_index = ira->exec_context.mem_slot_list.length; ira->exec_context.mem_slot_list.append(vals); } var->next_var = new_var; var = new_var; } // This must be done after possibly creating a new variable above var->ref_count = 0; var->value->type = result_type; assert(var->value->type); if (type_is_invalid(result_type)) { decl_var_instruction->base.other = &decl_var_instruction->base; return ira->codegen->builtin_types.entry_void; } if (decl_var_instruction->align_value == nullptr) { if ((err = type_resolve(ira->codegen, result_type, ResolveStatusAlignmentKnown))) { var->value->type = ira->codegen->builtin_types.entry_invalid; decl_var_instruction->base.other = &decl_var_instruction->base; return ira->codegen->builtin_types.entry_void; } var->align_bytes = get_abi_alignment(ira->codegen, result_type); } else { if (!ir_resolve_align(ira, decl_var_instruction->align_value->other, &var->align_bytes)) { var->value->type = ira->codegen->builtin_types.entry_invalid; } } if (casted_init_value->value.special != ConstValSpecialRuntime) { if (var->mem_slot_index != SIZE_MAX) { assert(var->mem_slot_index < ira->exec_context.mem_slot_list.length); ConstExprValue *mem_slot = ira->exec_context.mem_slot_list.at(var->mem_slot_index); copy_const_val(mem_slot, &casted_init_value->value, !is_comptime_var || var->gen_is_const); if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) { ir_build_const_from(ira, &decl_var_instruction->base); return ira->codegen->builtin_types.entry_void; } } } else if (is_comptime_var) { ir_add_error(ira, &decl_var_instruction->base, buf_sprintf("cannot store runtime value in compile time variable")); var->value->type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->builtin_types.entry_invalid; } ir_build_var_decl_from(&ira->new_irb, &decl_var_instruction->base, var, var_type, nullptr, casted_init_value); ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); if (fn_entry) fn_entry->variable_list.append(var); return ira->codegen->builtin_types.entry_void; } static ZigType *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructionExport *instruction) { IrInstruction *name = instruction->name->other; Buf *symbol_name = ir_resolve_str(ira, name); if (symbol_name == nullptr) { return ira->codegen->builtin_types.entry_invalid; } IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) { return ira->codegen->builtin_types.entry_invalid; } GlobalLinkageId global_linkage_id = GlobalLinkageIdStrong; if (instruction->linkage != nullptr) { IrInstruction *linkage_value = instruction->linkage->other; if (!ir_resolve_global_linkage(ira, linkage_value, &global_linkage_id)) { return ira->codegen->builtin_types.entry_invalid; } } auto entry = ira->codegen->exported_symbol_names.put_unique(symbol_name, instruction->base.source_node); if (entry) { AstNode *other_export_node = entry->value; 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")); } 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; CallingConvention cc = fn_entry->type_entry->data.fn.fn_type_id.cc; switch (cc) { case CallingConventionUnspecified: { ErrorMsg *msg = ir_add_error(ira, target, buf_sprintf("exported function must specify calling convention")); add_error_note(ira->codegen, msg, fn_entry->proto_node, buf_sprintf("declared here")); } break; case CallingConventionAsync: { ErrorMsg *msg = ir_add_error(ira, target, buf_sprintf("exported function cannot be async")); add_error_note(ira->codegen, msg, fn_entry->proto_node, buf_sprintf("declared here")); } break; case CallingConventionC: case CallingConventionNaked: case CallingConventionCold: case CallingConventionStdcall: add_fn_export(ira->codegen, fn_entry, symbol_name, global_linkage_id, cc == CallingConventionC); break; } } break; case ZigTypeIdStruct: if (is_slice(target->value.type)) { ir_add_error(ira, target, buf_sprintf("unable to export value of type '%s'", buf_ptr(&target->value.type->name))); } else if (target->value.type->data.structure.layout != ContainerLayoutExtern) { ErrorMsg *msg = ir_add_error(ira, target, buf_sprintf("exported struct value must be declared extern")); add_error_note(ira->codegen, msg, target->value.type->data.structure.decl_node, buf_sprintf("declared here")); } 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")); } 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")); } 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: break; case ZigTypeIdMetaType: case ZigTypeIdVoid: case ZigTypeIdUnreachable: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdOptional: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdNamespace: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: case ZigTypeIdPromise: ir_add_error(ira, target, buf_sprintf("invalid export target '%s'", buf_ptr(&type_value->name))); break; } } break; case ZigTypeIdVoid: case ZigTypeIdBool: case ZigTypeIdInt: case ZigTypeIdFloat: case ZigTypeIdPointer: case ZigTypeIdArray: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdOptional: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name)); case ZigTypeIdNamespace: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: case ZigTypeIdPromise: ir_add_error(ira, target, buf_sprintf("invalid export target type '%s'", buf_ptr(&target->value.type->name))); break; } ir_build_const_from(ira, &instruction->base); return ira->codegen->builtin_types.entry_void; } 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 ZigType *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, IrInstructionErrorReturnTrace *instruction) { if (instruction->optional == IrInstructionErrorReturnTrace::Null) { ZigType *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen); ZigType *optional_type = get_optional_type(ira->codegen, ptr_to_stack_trace_type); if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); 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 optional_type; } IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope, instruction->base.source_node, instruction->optional); ir_link_new_instruction(new_instruction, &instruction->base); return optional_type; } 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); ir_link_new_instruction(new_instruction, &instruction->base); return get_ptr_to_stack_trace_type(ira->codegen); } } static ZigType *ir_analyze_instruction_error_union(IrAnalyze *ira, IrInstructionErrorUnion *instruction) { ZigType *err_set_type = ir_resolve_type(ira, instruction->err_set->other); if (type_is_invalid(err_set_type)) return ira->codegen->builtin_types.entry_invalid; ZigType *payload_type = ir_resolve_type(ira, instruction->payload->other); if (type_is_invalid(payload_type)) return ira->codegen->builtin_types.entry_invalid; if (err_set_type->id != ZigTypeIdErrorSet) { ir_add_error(ira, instruction->err_set->other, buf_sprintf("expected error set type, found type '%s'", buf_ptr(&err_set_type->name))); return ira->codegen->builtin_types.entry_invalid; } ZigType *result_type = get_error_union_type(ira->codegen, err_set_type, payload_type); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = result_type; return ira->codegen->builtin_types.entry_type; } IrInstruction *ir_get_implicit_allocator(IrAnalyze *ira, IrInstruction *source_instr, ImplicitAllocatorId id) { ZigFn *parent_fn_entry = exec_fn_entry(ira->new_irb.exec); if (parent_fn_entry == nullptr) { ir_add_error(ira, source_instr, buf_sprintf("no implicit allocator available")); return ira->codegen->invalid_instruction; } FnTypeId *parent_fn_type = &parent_fn_entry->type_entry->data.fn.fn_type_id; if (parent_fn_type->cc != CallingConventionAsync) { ir_add_error(ira, source_instr, buf_sprintf("async function call from non-async caller requires allocator parameter")); return ira->codegen->invalid_instruction; } assert(parent_fn_type->async_allocator_type != nullptr); switch (id) { case ImplicitAllocatorIdArg: { IrInstruction *result = ir_build_get_implicit_allocator(&ira->new_irb, source_instr->scope, source_instr->source_node, ImplicitAllocatorIdArg); result->value.type = parent_fn_type->async_allocator_type; return result; } case ImplicitAllocatorIdLocalVar: { ZigVar *coro_allocator_var = ira->old_irb.exec->coro_allocator_var; assert(coro_allocator_var != nullptr); IrInstruction *var_ptr_inst = ir_get_var_ptr(ira, source_instr, coro_allocator_var); IrInstruction *result = ir_get_deref(ira, source_instr, var_ptr_inst); assert(result->value.type != nullptr); return result; } } zig_unreachable(); } static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count, IrInstruction *async_allocator_inst) { Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME); //Buf *free_field_name = buf_create_from_str("freeFn"); assert(async_allocator_inst->value.type->id == ZigTypeIdPointer); ZigType *container_type = async_allocator_inst->value.type->data.pointer.child_type; IrInstruction *field_ptr_inst = ir_analyze_container_field_ptr(ira, alloc_field_name, &call_instruction->base, async_allocator_inst, container_type); if (type_is_invalid(field_ptr_inst->value.type)) { return ira->codegen->invalid_instruction; } ZigType *ptr_to_alloc_fn_type = field_ptr_inst->value.type; assert(ptr_to_alloc_fn_type->id == ZigTypeIdPointer); ZigType *alloc_fn_type = ptr_to_alloc_fn_type->data.pointer.child_type; if (alloc_fn_type->id != ZigTypeIdFn) { ir_add_error(ira, &call_instruction->base, buf_sprintf("expected allocation function, found '%s'", buf_ptr(&alloc_fn_type->name))); return ira->codegen->invalid_instruction; } ZigType *alloc_fn_return_type = alloc_fn_type->data.fn.fn_type_id.return_type; if (alloc_fn_return_type->id != ZigTypeIdErrorUnion) { ir_add_error(ira, fn_ref, buf_sprintf("expected allocation function to return error union, but it returns '%s'", buf_ptr(&alloc_fn_return_type->name))); return ira->codegen->invalid_instruction; } ZigType *alloc_fn_error_set_type = alloc_fn_return_type->data.error_union.err_set_type; ZigType *return_type = fn_type->data.fn.fn_type_id.return_type; ZigType *promise_type = get_promise_type(ira->codegen, return_type); ZigType *async_return_type = get_error_union_type(ira->codegen, alloc_fn_error_set_type, promise_type); IrInstruction *result = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, fn_entry, fn_ref, arg_count, casted_args, false, FnInlineAuto, true, async_allocator_inst, nullptr); result->value.type = async_return_type; return result; } static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node, IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i) { AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(*next_proto_i); assert(param_decl_node->type == NodeTypeParamDecl); IrInstruction *casted_arg; if (param_decl_node->data.param_decl.var_token == nullptr) { AstNode *param_type_node = param_decl_node->data.param_decl.type; ZigType *param_type = analyze_type_expr(ira->codegen, *exec_scope, param_type_node); if (type_is_invalid(param_type)) return false; casted_arg = ir_implicit_cast(ira, arg, param_type); if (type_is_invalid(casted_arg->value.type)) return false; } else { casted_arg = arg; } ConstExprValue *arg_val = ir_resolve_const(ira, casted_arg, UndefBad); if (!arg_val) return false; Buf *param_name = param_decl_node->data.param_decl.name; ZigVar *var = add_variable(ira->codegen, param_decl_node, *exec_scope, param_name, true, arg_val, nullptr); *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 = analyze_type_expr(ira->codegen, *child_scope, param_type_node); if (type_is_invalid(param_type)) return false; casted_arg = ir_implicit_cast(ira, arg, param_type); if (type_is_invalid(casted_arg->value.type)) return false; } else { arg_part_of_generic_id = true; casted_arg = arg; } } bool comptime_arg = param_decl_node->data.param_decl.is_inline || casted_arg->value.type->id == ZigTypeIdComptimeInt || casted_arg->value.type->id == ZigTypeIdComptimeFloat; ConstExprValue *arg_val; if (comptime_arg) { arg_part_of_generic_id = true; arg_val = ir_resolve_const(ira, casted_arg, UndefBad); if (!arg_val) return false; } else { arg_val = create_const_runtime(casted_arg->value.type); } if (arg_part_of_generic_id) { generic_id->params[generic_id->param_count] = *arg_val; generic_id->param_count += 1; } Buf *param_name = param_decl_node->data.param_decl.name; if (!param_name) return false; if (!is_var_args) { ZigVar *var = add_variable(ira->codegen, param_decl_node, *child_scope, param_name, true, arg_val, nullptr); *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) { if (type_requires_comptime(casted_arg->value.type)) { ir_add_error(ira, casted_arg, buf_sprintf("parameter of type '%s' requires comptime", buf_ptr(&casted_arg->value.type->name))); return false; } casted_args[fn_type_id->param_count] = casted_arg; FnTypeParamInfo *param_info = &fn_type_id->param_info[fn_type_id->param_count]; param_info->type = casted_arg->value.type; param_info->is_noalias = param_decl_node->data.param_decl.is_noalias; impl_fn->param_source_nodes[fn_type_id->param_count] = param_decl_node; fn_type_id->param_count += 1; } return true; } static ZigVar *get_fn_var_by_index(ZigFn *fn_entry, size_t index) { size_t next_var_i = 0; FnGenParamInfo *gen_param_info = fn_entry->type_entry->data.fn.gen_param_info; assert(gen_param_info != nullptr); for (size_t param_i = 0; param_i < index; param_i += 1) { FnGenParamInfo *info = &gen_param_info[param_i]; if (info->gen_index == SIZE_MAX) continue; next_var_i += 1; } FnGenParamInfo *info = &gen_param_info[index]; if (info->gen_index == SIZE_MAX) return nullptr; return fn_entry->variable_list.at(next_var_i); } static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ZigVar *var) { while (var->next_var != nullptr) { var = var->next_var; } if (var->mem_slot_index != SIZE_MAX && var->owner_exec->analysis == nullptr) { assert(ira->codegen->errors.length != 0); return ira->codegen->invalid_instruction; } if (var->value->type == nullptr || type_is_invalid(var->value->type)) return ira->codegen->invalid_instruction; bool comptime_var_mem = ir_get_var_is_comptime(var); ConstExprValue *mem_slot = nullptr; if (var->value->special == ConstValSpecialStatic) { mem_slot = var->value; } else { if (var->mem_slot_index != SIZE_MAX && (comptime_var_mem || var->gen_is_const)) { // find the relevant exec_context assert(var->owner_exec != nullptr); assert(var->owner_exec->analysis != nullptr); IrExecContext *exec_context = &var->owner_exec->analysis->exec_context; assert(var->mem_slot_index < exec_context->mem_slot_list.length); mem_slot = exec_context->mem_slot_list.at(var->mem_slot_index); } } bool is_const = var->src_is_const; bool is_volatile = false; if (mem_slot != nullptr) { switch (mem_slot->special) { case ConstValSpecialRuntime: goto no_mem_slot; case ConstValSpecialStatic: // fallthrough case ConstValSpecialUndef: { ConstPtrMut ptr_mut; if (comptime_var_mem) { ptr_mut = ConstPtrMutComptimeVar; } else if (var->gen_is_const) { ptr_mut = ConstPtrMutComptimeConst; } else { assert(!comptime_var_mem); ptr_mut = ConstPtrMutRuntimeVar; } return ir_get_const_ptr(ira, instruction, mem_slot, var->value->type, ptr_mut, is_const, is_volatile, var->align_bytes); } } zig_unreachable(); } no_mem_slot: IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb, instruction->scope, instruction->source_node, var); var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type, var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0); bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr); var_ptr_instruction->value.data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack; return var_ptr_instruction; } static ZigType *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref, IrInstruction *first_arg_ptr, bool comptime_fn_call, FnInline fn_inline) { Error err; FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; size_t first_arg_1_or_0 = first_arg_ptr ? 1 : 0; // for extern functions, the var args argument is not counted. // for zig functions, it is. size_t var_args_1_or_0; if (fn_type_id->cc == CallingConventionC) { var_args_1_or_0 = 0; } else { var_args_1_or_0 = fn_type_id->is_var_args ? 1 : 0; } size_t src_param_count = fn_type_id->param_count - var_args_1_or_0; size_t call_param_count = call_instruction->arg_count + first_arg_1_or_0; for (size_t i = 0; i < call_instruction->arg_count; i += 1) { ConstExprValue *arg_tuple_value = &call_instruction->args[i]->other->value; if (arg_tuple_value->type->id == ZigTypeIdArgTuple) { call_param_count -= 1; call_param_count += arg_tuple_value->data.x_arg_tuple.end_index - arg_tuple_value->data.x_arg_tuple.start_index; } } AstNode *source_node = call_instruction->base.source_node; AstNode *fn_proto_node = fn_entry ? fn_entry->proto_node : nullptr;; if (fn_type_id->cc == CallingConventionNaked) { ErrorMsg *msg = ir_add_error(ira, fn_ref, buf_sprintf("unable to call function with naked calling convention")); if (fn_proto_node) { add_error_note(ira->codegen, msg, fn_proto_node, buf_sprintf("declared here")); } return ira->codegen->builtin_types.entry_invalid; } if (fn_type_id->cc == CallingConventionAsync && !call_instruction->is_async) { ErrorMsg *msg = ir_add_error(ira, fn_ref, buf_sprintf("must use async keyword to call async function")); if (fn_proto_node) { add_error_note(ira->codegen, msg, fn_proto_node, buf_sprintf("declared here")); } return ira->codegen->builtin_types.entry_invalid; } if (fn_type_id->cc != CallingConventionAsync && call_instruction->is_async) { ErrorMsg *msg = ir_add_error(ira, fn_ref, buf_sprintf("cannot use async keyword to call non-async function")); if (fn_proto_node) { add_error_note(ira->codegen, msg, fn_proto_node, buf_sprintf("declared here")); } return ira->codegen->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; } } 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->builtin_types.entry_invalid; } if (comptime_fn_call) { // No special handling is needed for compile time evaluation of generic functions. if (!fn_entry || fn_entry->body_node == nullptr) { ir_add_error(ira, fn_ref, buf_sprintf("unable to evaluate constant expression")); return ira->codegen->builtin_types.entry_invalid; } if (!ir_emit_backward_branch(ira, &call_instruction->base)) return ira->codegen->builtin_types.entry_invalid; // Fork a scope of the function with known values for the parameters. Scope *exec_scope = &fn_entry->fndef_scope->base; size_t next_proto_i = 0; if (first_arg_ptr) { 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->builtin_types.entry_invalid; first_arg_known_bare = param_type->id != ZigTypeIdPointer; } IrInstruction *first_arg; if (!first_arg_known_bare && handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) { first_arg = first_arg_ptr; } else { first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr); if (type_is_invalid(first_arg->value.type)) return ira->codegen->builtin_types.entry_invalid; } if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, first_arg, &exec_scope, &next_proto_i)) return ira->codegen->builtin_types.entry_invalid; } if (fn_proto_node->data.fn_proto.is_var_args) { ir_add_error(ira, &call_instruction->base, buf_sprintf("compiler bug: unable to call var args function at compile time. https://github.com/ziglang/zig/issues/313")); return ira->codegen->builtin_types.entry_invalid; } for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) { IrInstruction *old_arg = call_instruction->args[call_i]->other; if (type_is_invalid(old_arg->value.type)) return ira->codegen->builtin_types.entry_invalid; if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, old_arg, &exec_scope, &next_proto_i)) return ira->codegen->builtin_types.entry_invalid; } AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type; ZigType *specified_return_type = analyze_type_expr(ira->codegen, exec_scope, return_type_node); if (type_is_invalid(specified_return_type)) return ira->codegen->builtin_types.entry_invalid; 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); 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); IrInstruction *result = nullptr; if (cacheable) { auto entry = ira->codegen->memoized_fn_eval_table.maybe_get(exec_scope); if (entry) result = entry->value; } if (result == nullptr) { // Analyze the fn body block like any other constant expression. AstNode *body_node = fn_entry->body_node; result = ir_eval_const_value(ira->codegen, exec_scope, body_node, return_type, ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry, nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec); if (inferred_err_set_type != nullptr) { inferred_err_set_type->data.error_set.infer_fn = nullptr; if (result->value.type->id == ZigTypeIdErrorUnion) { if (result->value.data.x_err_union.err != nullptr) { inferred_err_set_type->data.error_set.err_count = 1; inferred_err_set_type->data.error_set.errors = allocate(1); inferred_err_set_type->data.error_set.errors[0] = result->value.data.x_err_union.err; } ZigType *fn_inferred_err_set_type = result->value.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->value.type->id == ZigTypeIdErrorSet) { inferred_err_set_type->data.error_set.err_count = result->value.type->data.error_set.err_count; inferred_err_set_type->data.error_set.errors = result->value.type->data.error_set.errors; } } if (cacheable) { ira->codegen->memoized_fn_eval_table.put(exec_scope, result); } if (type_is_invalid(result->value.type)) return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *out_val = ir_build_const_from(ira, &call_instruction->base); *out_val = result->value; return ir_finish_anal(ira, return_type); } IrInstruction *casted_new_stack = nullptr; if (call_instruction->new_stack != nullptr) { ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, false, false, PtrLenUnknown, 0, 0, 0); ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr); IrInstruction *new_stack = call_instruction->new_stack->other; if (type_is_invalid(new_stack->value.type)) return ira->codegen->builtin_types.entry_invalid; casted_new_stack = ir_implicit_cast(ira, new_stack, u8_slice); if (type_is_invalid(casted_new_stack->value.type)) return ira->codegen->builtin_types.entry_invalid; } if (fn_type->data.fn.is_generic) { if (!fn_entry) { ir_add_error(ira, call_instruction->fn_ref, buf_sprintf("calling a generic function requires compile-time known function value")); return ira->codegen->builtin_types.entry_invalid; } // Count the arguments of the function type id we are creating size_t new_fn_arg_count = first_arg_1_or_0; for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) { IrInstruction *arg = call_instruction->args[call_i]->other; if (type_is_invalid(arg->value.type)) return ira->codegen->builtin_types.entry_invalid; if (arg->value.type->id == ZigTypeIdArgTuple) { new_fn_arg_count += arg->value.data.x_arg_tuple.end_index - arg->value.data.x_arg_tuple.start_index; } else { new_fn_arg_count += 1; } } IrInstruction **casted_args = allocate(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(fn_proto_node); impl_fn->param_source_nodes = allocate(new_fn_arg_count); buf_init_from_buf(&impl_fn->symbol_name, &fn_entry->symbol_name); impl_fn->fndef_scope = create_fndef_scope(impl_fn->body_node, parent_scope, impl_fn); impl_fn->child_scope = &impl_fn->fndef_scope->base; FnTypeId inst_fn_type_id = {0}; init_fn_type_id(&inst_fn_type_id, fn_proto_node, new_fn_arg_count); inst_fn_type_id.param_count = 0; inst_fn_type_id.is_var_args = false; // TODO maybe GenericFnTypeId can be replaced with using the child_scope directly // as the key in generic_table GenericFnTypeId *generic_id = allocate(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->builtin_types.entry_invalid; first_arg_known_bare = param_type->id != ZigTypeIdPointer; } IrInstruction *first_arg; if (!first_arg_known_bare && handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) { first_arg = first_arg_ptr; } else { first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr); if (type_is_invalid(first_arg->value.type)) return ira->codegen->builtin_types.entry_invalid; } if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, first_arg, &impl_fn->child_scope, &next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn)) { return ira->codegen->builtin_types.entry_invalid; } } bool found_first_var_arg = false; size_t first_var_arg; ZigFn *parent_fn_entry = exec_fn_entry(ira->new_irb.exec); assert(parent_fn_entry); for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) { IrInstruction *arg = call_instruction->args[call_i]->other; if (type_is_invalid(arg->value.type)) return ira->codegen->builtin_types.entry_invalid; if (arg->value.type->id == ZigTypeIdArgTuple) { for (size_t arg_tuple_i = arg->value.data.x_arg_tuple.start_index; arg_tuple_i < arg->value.data.x_arg_tuple.end_index; arg_tuple_i += 1) { AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i); assert(param_decl_node->type == NodeTypeParamDecl); bool is_var_args = param_decl_node->data.param_decl.is_var_args; if (is_var_args && !found_first_var_arg) { first_var_arg = inst_fn_type_id.param_count; found_first_var_arg = true; } ZigVar *arg_var = get_fn_var_by_index(parent_fn_entry, arg_tuple_i); if (arg_var == nullptr) { ir_add_error(ira, arg, buf_sprintf("compiler bug: var args can't handle void. https://github.com/ziglang/zig/issues/557")); return ira->codegen->builtin_types.entry_invalid; } IrInstruction *arg_var_ptr_inst = ir_get_var_ptr(ira, arg, arg_var); if (type_is_invalid(arg_var_ptr_inst->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *arg_tuple_arg = ir_get_deref(ira, arg, arg_var_ptr_inst); if (type_is_invalid(arg_tuple_arg->value.type)) return ira->codegen->builtin_types.entry_invalid; if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg_tuple_arg, &impl_fn->child_scope, &next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn)) { return ira->codegen->builtin_types.entry_invalid; } } } else { AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i); assert(param_decl_node->type == NodeTypeParamDecl); bool is_var_args = param_decl_node->data.param_decl.is_var_args; if (is_var_args && !found_first_var_arg) { first_var_arg = inst_fn_type_id.param_count; found_first_var_arg = true; } if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg, &impl_fn->child_scope, &next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn)) { return ira->codegen->builtin_types.entry_invalid; } } } if (fn_proto_node->data.fn_proto.is_var_args) { AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i); Buf *param_name = param_decl_node->data.param_decl.name; if (!found_first_var_arg) { first_var_arg = inst_fn_type_id.param_count; } ConstExprValue *var_args_val = create_const_arg_tuple(ira->codegen, first_var_arg, inst_fn_type_id.param_count); ZigVar *var = add_variable(ira->codegen, param_decl_node, impl_fn->child_scope, param_name, true, var_args_val, nullptr); impl_fn->child_scope = var->child_scope; } if (fn_proto_node->data.fn_proto.align_expr != nullptr) { IrInstruction *align_result = ir_eval_const_value(ira->codegen, impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr, get_align_amt_type(ira->codegen), ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec); uint32_t align_bytes = 0; ir_resolve_align(ira, align_result, &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 = analyze_type_expr(ira->codegen, impl_fn->child_scope, return_type_node); if (type_is_invalid(specified_return_type)) return ira->codegen->builtin_types.entry_invalid; if (fn_proto_node->data.fn_proto.auto_err_set) { ZigType *inferred_err_set_type = get_auto_err_set_type(ira->codegen, impl_fn); 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; } if ((err = type_resolve(ira->codegen, specified_return_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; if (type_requires_comptime(specified_return_type)) { // Throw out our work and call the function as if it were comptime. return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true, FnInlineAuto); } } IrInstruction *async_allocator_inst = nullptr; if (call_instruction->is_async) { AstNode *async_allocator_type_node = fn_proto_node->data.fn_proto.async_allocator_type; if (async_allocator_type_node != nullptr) { ZigType *async_allocator_type = analyze_type_expr(ira->codegen, impl_fn->child_scope, async_allocator_type_node); if (type_is_invalid(async_allocator_type)) return ira->codegen->builtin_types.entry_invalid; inst_fn_type_id.async_allocator_type = async_allocator_type; } IrInstruction *uncasted_async_allocator_inst; if (call_instruction->async_allocator == nullptr) { uncasted_async_allocator_inst = ir_get_implicit_allocator(ira, &call_instruction->base, ImplicitAllocatorIdLocalVar); if (type_is_invalid(uncasted_async_allocator_inst->value.type)) return ira->codegen->builtin_types.entry_invalid; } else { uncasted_async_allocator_inst = call_instruction->async_allocator->other; if (type_is_invalid(uncasted_async_allocator_inst->value.type)) return ira->codegen->builtin_types.entry_invalid; } if (inst_fn_type_id.async_allocator_type == nullptr) { inst_fn_type_id.async_allocator_type = uncasted_async_allocator_inst->value.type; } async_allocator_inst = ir_implicit_cast(ira, uncasted_async_allocator_inst, inst_fn_type_id.async_allocator_type); if (type_is_invalid(async_allocator_inst->value.type)) return ira->codegen->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; impl_fn->ir_executable.source_node = call_instruction->base.source_node; impl_fn->ir_executable.parent_exec = ira->new_irb.exec; impl_fn->analyzed_executable.source_node = call_instruction->base.source_node; impl_fn->analyzed_executable.parent_exec = ira->new_irb.exec; 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); } ZigType *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type; if (fn_type_can_fail(&impl_fn->type_entry->data.fn.fn_type_id)) { parent_fn_entry->calls_or_awaits_errorable_fn = true; } size_t impl_param_count = impl_fn->type_entry->data.fn.fn_type_id.param_count; if (call_instruction->is_async) { IrInstruction *result = ir_analyze_async_call(ira, call_instruction, impl_fn, impl_fn->type_entry, fn_ref, casted_args, impl_param_count, async_allocator_inst); ir_link_new_instruction(result, &call_instruction->base); ir_add_alloca(ira, result, result->value.type); return ir_finish_anal(ira, result->value.type); } assert(async_allocator_inst == nullptr); IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base, impl_fn, nullptr, impl_param_count, casted_args, false, fn_inline, call_instruction->is_async, nullptr, casted_new_stack); ir_add_alloca(ira, new_call_instruction, return_type); return ir_finish_anal(ira, return_type); } 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(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->builtin_types.entry_invalid; IrInstruction *first_arg; if (param_type->id == ZigTypeIdPointer && handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) { first_arg = first_arg_ptr; } else { first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr); if (type_is_invalid(first_arg->value.type)) return ira->codegen->builtin_types.entry_invalid; } IrInstruction *casted_arg = ir_implicit_cast(ira, first_arg, param_type); if (type_is_invalid(casted_arg->value.type)) return ira->codegen->builtin_types.entry_invalid; casted_args[next_arg_index] = casted_arg; next_arg_index += 1; } for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) { IrInstruction *old_arg = call_instruction->args[call_i]->other; if (type_is_invalid(old_arg->value.type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; casted_arg = ir_implicit_cast(ira, old_arg, param_type); if (type_is_invalid(casted_arg->value.type)) return ira->codegen->builtin_types.entry_invalid; } else { casted_arg = old_arg; } casted_args[next_arg_index] = casted_arg; next_arg_index += 1; } assert(next_arg_index == call_param_count); ZigType *return_type = fn_type_id->return_type; if (type_is_invalid(return_type)) return ira->codegen->builtin_types.entry_invalid; if (call_instruction->is_async) { IrInstruction *uncasted_async_allocator_inst; if (call_instruction->async_allocator == nullptr) { uncasted_async_allocator_inst = ir_get_implicit_allocator(ira, &call_instruction->base, ImplicitAllocatorIdLocalVar); if (type_is_invalid(uncasted_async_allocator_inst->value.type)) return ira->codegen->builtin_types.entry_invalid; } else { uncasted_async_allocator_inst = call_instruction->async_allocator->other; if (type_is_invalid(uncasted_async_allocator_inst->value.type)) return ira->codegen->builtin_types.entry_invalid; } IrInstruction *async_allocator_inst = ir_implicit_cast(ira, uncasted_async_allocator_inst, fn_type_id->async_allocator_type); if (type_is_invalid(async_allocator_inst->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *result = ir_analyze_async_call(ira, call_instruction, fn_entry, fn_type, fn_ref, casted_args, call_param_count, async_allocator_inst); ir_link_new_instruction(result, &call_instruction->base); ir_add_alloca(ira, result, result->value.type); return ir_finish_anal(ira, result->value.type); } if (fn_entry != nullptr && fn_entry->fn_inline == FnInlineAlways && fn_inline == FnInlineNever) { ir_add_error(ira, &call_instruction->base, buf_sprintf("no-inline call of inline function")); return ira->codegen->builtin_types.entry_invalid; } IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base, fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr, casted_new_stack); ir_add_alloca(ira, new_call_instruction, return_type); return ir_finish_anal(ira, return_type); } static ZigType *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionCall *call_instruction) { IrInstruction *fn_ref = call_instruction->fn_ref->other; if (type_is_invalid(fn_ref->value.type)) return ira->codegen->builtin_types.entry_invalid; bool is_comptime = call_instruction->is_comptime || ir_should_inline(ira->new_irb.exec, call_instruction->base.scope); if (is_comptime || instr_is_comptime(fn_ref)) { if (fn_ref->value.type->id == ZigTypeIdMetaType) { ZigType *dest_type = ir_resolve_type(ira, fn_ref); if (type_is_invalid(dest_type)) return ira->codegen->builtin_types.entry_invalid; size_t actual_param_count = call_instruction->arg_count; if (actual_param_count != 1) { ir_add_error_node(ira, call_instruction->base.source_node, buf_sprintf("cast expression expects exactly one parameter")); return ira->codegen->builtin_types.entry_invalid; } IrInstruction *arg = call_instruction->args[0]->other; IrInstruction *cast_instruction = ir_analyze_cast(ira, &call_instruction->base, dest_type, arg); if (type_is_invalid(cast_instruction->value.type)) return ira->codegen->builtin_types.entry_invalid; ir_link_new_instruction(cast_instruction, &call_instruction->base); return ir_finish_anal(ira, cast_instruction->value.type); } else if (fn_ref->value.type->id == ZigTypeIdFn) { ZigFn *fn_table_entry = ir_resolve_fn(ira, fn_ref); if (fn_table_entry == nullptr) return ira->codegen->builtin_types.entry_invalid; return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry, fn_ref, nullptr, is_comptime, call_instruction->fn_inline); } else if (fn_ref->value.type->id == ZigTypeIdBoundFn) { assert(fn_ref->value.special == ConstValSpecialStatic); ZigFn *fn_table_entry = fn_ref->value.data.x_bound_fn.fn; IrInstruction *first_arg_ptr = fn_ref->value.data.x_bound_fn.first_arg; return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry, fn_ref, first_arg_ptr, is_comptime, call_instruction->fn_inline); } else { ir_add_error_node(ira, fn_ref->source_node, buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } } if (fn_ref->value.type->id == ZigTypeIdFn) { return ir_analyze_fn_call(ira, call_instruction, nullptr, fn_ref->value.type, fn_ref, nullptr, false, FnInlineAuto); } else { ir_add_error_node(ira, fn_ref->source_node, buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } } // 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, AstNode *source_node, ConstExprValue *out_val, ConstExprValue *ptr_val) { assert(out_val->type != nullptr); ConstExprValue *pointee = const_ptr_pointee_unchecked(ira->codegen, ptr_val); size_t src_size = type_size(ira->codegen, pointee->type); size_t dst_size = type_size(ira->codegen, out_val->type); if (src_size == dst_size && types_have_same_zig_comptime_repr(pointee->type, out_val->type)) { copy_const_val(out_val, pointee, ptr_val->data.x_ptr.mut == ConstPtrMutComptimeConst); return ErrorNone; } if (dst_size > src_size) { ir_add_error_node(ira, source_node, buf_sprintf("attempt to read %zu bytes from pointer to %s which is %zu bytes", dst_size, buf_ptr(&pointee->type->name), src_size)); return ErrorSemanticAnalyzeFail; } Buf buf = BUF_INIT; buf_resize(&buf, src_size); buf_write_value_bytes(ira->codegen, (uint8_t*)buf_ptr(&buf), pointee); buf_read_value_bytes(ira->codegen, (uint8_t*)buf_ptr(&buf), out_val); return ErrorNone; } static ZigType *ir_analyze_dereference(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) { Error err; IrInstruction *value = un_op_instruction->value->other; ZigType *ptr_type = value->value.type; ZigType *child_type; if (type_is_invalid(ptr_type)) { return ira->codegen->builtin_types.entry_invalid; } else if (ptr_type->id == ZigTypeIdPointer) { if (ptr_type->data.pointer.ptr_len == PtrLenUnknown) { ir_add_error_node(ira, un_op_instruction->base.source_node, buf_sprintf("index syntax required for unknown-length pointer type '%s'", buf_ptr(&ptr_type->name))); return ira->codegen->builtin_types.entry_invalid; } child_type = ptr_type->data.pointer.child_type; } else { ir_add_error_node(ira, un_op_instruction->base.source_node, buf_sprintf("attempt to dereference non-pointer type '%s'", buf_ptr(&ptr_type->name))); return ira->codegen->builtin_types.entry_invalid; } // this dereference is always an rvalue because in the IR gen we identify lvalue and emit // one of the ptr instructions if (instr_is_comptime(value)) { ConstExprValue *comptime_value = ir_resolve_const(ira, value, UndefBad); if (comptime_value == nullptr) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base); out_val->type = child_type; if ((err = ir_read_const_ptr(ira, un_op_instruction->base.source_node, out_val, comptime_value))) return ira->codegen->builtin_types.entry_invalid; return child_type; } ir_build_load_ptr_from(&ira->new_irb, &un_op_instruction->base, value); return child_type; } static ZigType *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) { Error err; IrInstruction *value = un_op_instruction->value->other; ZigType *type_entry = ir_resolve_type(ira, value); if (type_is_invalid(type_entry)) return ira->codegen->builtin_types.entry_invalid; if ((err = ensure_complete_type(ira->codegen, type_entry))) return ira->codegen->builtin_types.entry_invalid; switch (type_entry->id) { case ZigTypeIdInvalid: zig_unreachable(); case ZigTypeIdMetaType: case ZigTypeIdVoid: case ZigTypeIdBool: case ZigTypeIdInt: case ZigTypeIdFloat: case ZigTypeIdPointer: case ZigTypeIdArray: case ZigTypeIdStruct: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdOptional: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdEnum: case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdNamespace: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdPromise: { ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base); out_val->data.x_type = get_optional_type(ira->codegen, type_entry); return ira->codegen->builtin_types.entry_type; } case ZigTypeIdUnreachable: case ZigTypeIdOpaque: ir_add_error_node(ira, un_op_instruction->base.source_node, buf_sprintf("type '%s' not optional", buf_ptr(&type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } zig_unreachable(); } static ZigType *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) { IrInstruction *value = un_op_instruction->value->other; ZigType *expr_type = value->value.type; if (type_is_invalid(expr_type)) return ira->codegen->builtin_types.entry_invalid; bool is_wrap_op = (un_op_instruction->op_id == IrUnOpNegationWrap); bool is_float = (expr_type->id == ZigTypeIdFloat || expr_type->id == ZigTypeIdComptimeFloat); if ((expr_type->id == ZigTypeIdInt && expr_type->data.integral.is_signed) || expr_type->id == ZigTypeIdComptimeInt || (is_float && !is_wrap_op)) { if (instr_is_comptime(value)) { ConstExprValue *target_const_val = ir_resolve_const(ira, value, UndefBad); if (!target_const_val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base); if (is_float) { float_negate(out_val, target_const_val); } else if (is_wrap_op) { bigint_negate_wrap(&out_val->data.x_bigint, &target_const_val->data.x_bigint, expr_type->data.integral.bit_count); } else { bigint_negate(&out_val->data.x_bigint, &target_const_val->data.x_bigint); } if (is_wrap_op || is_float || expr_type->id == ZigTypeIdComptimeInt) { return expr_type; } if (!bigint_fits_in_bits(&out_val->data.x_bigint, expr_type->data.integral.bit_count, true)) { ir_add_error(ira, &un_op_instruction->base, buf_sprintf("negation caused overflow")); return ira->codegen->builtin_types.entry_invalid; } return expr_type; } ir_build_un_op_from(&ira->new_irb, &un_op_instruction->base, un_op_instruction->op_id, value); return expr_type; } const char *fmt = is_wrap_op ? "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'"; ir_add_error(ira, &un_op_instruction->base, buf_sprintf(fmt, buf_ptr(&expr_type->name))); return ira->codegen->builtin_types.entry_invalid; } static ZigType *ir_analyze_bin_not(IrAnalyze *ira, IrInstructionUnOp *instruction) { IrInstruction *value = instruction->value->other; ZigType *expr_type = value->value.type; if (type_is_invalid(expr_type)) return ira->codegen->builtin_types.entry_invalid; if (expr_type->id == ZigTypeIdInt) { if (instr_is_comptime(value)) { ConstExprValue *target_const_val = ir_resolve_const(ira, value, UndefBad); if (target_const_val == nullptr) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); bigint_not(&out_val->data.x_bigint, &target_const_val->data.x_bigint, expr_type->data.integral.bit_count, expr_type->data.integral.is_signed); return expr_type; } ir_build_un_op_from(&ira->new_irb, &instruction->base, IrUnOpBinNot, value); return expr_type; } ir_add_error(ira, &instruction->base, buf_sprintf("unable to perform binary not operation on type '%s'", buf_ptr(&expr_type->name))); return ira->codegen->builtin_types.entry_invalid; } static ZigType *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) { IrUnOp op_id = un_op_instruction->op_id; switch (op_id) { case IrUnOpInvalid: zig_unreachable(); case IrUnOpBinNot: return ir_analyze_bin_not(ira, un_op_instruction); case IrUnOpNegation: case IrUnOpNegationWrap: return ir_analyze_negation(ira, un_op_instruction); case IrUnOpDereference: return ir_analyze_dereference(ira, un_op_instruction); case IrUnOpOptional: return ir_analyze_maybe(ira, un_op_instruction); } zig_unreachable(); } static ZigType *ir_analyze_instruction_br(IrAnalyze *ira, IrInstructionBr *br_instruction) { IrBasicBlock *old_dest_block = br_instruction->dest_block; bool is_comptime; if (!ir_resolve_comptime(ira, br_instruction->is_comptime->other, &is_comptime)) return ir_unreach_error(ira); if (is_comptime || old_dest_block->ref_count == 1) return ir_inline_bb(ira, &br_instruction->base, old_dest_block); IrBasicBlock *new_bb = ir_get_new_bb_runtime(ira, old_dest_block, &br_instruction->base); if (new_bb == nullptr) return ir_unreach_error(ira); ir_build_br_from(&ira->new_irb, &br_instruction->base, new_bb); return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); } static ZigType *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructionCondBr *cond_br_instruction) { IrInstruction *condition = cond_br_instruction->condition->other; 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->other, &is_comptime)) return ir_unreach_error(ira); if (is_comptime || instr_is_comptime(condition)) { bool cond_is_true; if (!ir_resolve_bool(ira, condition, &cond_is_true)) return ir_unreach_error(ira); IrBasicBlock *old_dest_block = cond_is_true ? cond_br_instruction->then_block : cond_br_instruction->else_block; if (is_comptime || old_dest_block->ref_count == 1) return ir_inline_bb(ira, &cond_br_instruction->base, old_dest_block); IrBasicBlock *new_dest_block = ir_get_new_bb_runtime(ira, old_dest_block, &cond_br_instruction->base); if (new_dest_block == nullptr) return ir_unreach_error(ira); ir_build_br_from(&ira->new_irb, &cond_br_instruction->base, new_dest_block); return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); } ZigType *bool_type = ira->codegen->builtin_types.entry_bool; IrInstruction *casted_condition = ir_implicit_cast(ira, condition, bool_type); if (casted_condition == ira->codegen->invalid_instruction) return ir_unreach_error(ira); assert(cond_br_instruction->then_block != cond_br_instruction->else_block); IrBasicBlock *new_then_block = ir_get_new_bb_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_build_cond_br_from(&ira->new_irb, &cond_br_instruction->base, casted_condition, new_then_block, new_else_block, nullptr); return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); } static ZigType *ir_analyze_instruction_unreachable(IrAnalyze *ira, IrInstructionUnreachable *unreachable_instruction) { ir_build_unreachable_from(&ira->new_irb, &unreachable_instruction->base); return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); } static ZigType *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPhi *phi_instruction) { if (ira->const_predecessor_bb) { for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { IrBasicBlock *predecessor = phi_instruction->incoming_blocks[i]; if (predecessor != ira->const_predecessor_bb) continue; IrInstruction *value = phi_instruction->incoming_values[i]->other; assert(value->value.type); if (type_is_invalid(value->value.type)) return ira->codegen->builtin_types.entry_invalid; if (value->value.special != ConstValSpecialRuntime) { ConstExprValue *out_val = ir_build_const_from(ira, &phi_instruction->base); *out_val = value->value; } else { phi_instruction->base.other = value; } return value->value.type; } zig_unreachable(); } ZigList new_incoming_blocks = {0}; ZigList new_incoming_values = {0}; for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { IrBasicBlock *predecessor = phi_instruction->incoming_blocks[i]; if (predecessor->ref_count == 0) continue; IrInstruction *old_value = phi_instruction->incoming_values[i]; assert(old_value); IrInstruction *new_value = old_value->other; if (!new_value || new_value->value.type->id == ZigTypeIdUnreachable || predecessor->other == nullptr) continue; if (type_is_invalid(new_value->value.type)) return ira->codegen->builtin_types.entry_invalid; assert(predecessor->other); new_incoming_blocks.append(predecessor->other); new_incoming_values.append(new_value); } if (new_incoming_blocks.length == 0) { ir_build_unreachable_from(&ira->new_irb, &phi_instruction->base); return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); } if (new_incoming_blocks.length == 1) { IrInstruction *first_value = new_incoming_values.at(0); phi_instruction->base.other = first_value; return first_value->value.type; } ZigType *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node, nullptr, new_incoming_values.items, new_incoming_values.length); if (type_is_invalid(resolved_type)) return resolved_type; if (resolved_type->id == ZigTypeIdComptimeFloat || resolved_type->id == ZigTypeIdComptimeInt || resolved_type->id == ZigTypeIdNull || resolved_type->id == ZigTypeIdUndefined) { ir_add_error_node(ira, phi_instruction->base.source_node, buf_sprintf("unable to infer expression type")); return ira->codegen->builtin_types.entry_invalid; } bool all_stack_ptrs = (resolved_type->id == ZigTypeIdPointer); // cast all values to the resolved type. however we can't put cast instructions in front of the phi instruction. // so we go back and insert the casts as the last instruction in the corresponding predecessor blocks, and // then make sure the branch instruction is preserved. IrBasicBlock *cur_bb = ira->new_irb.current_basic_block; for (size_t i = 0; i < new_incoming_values.length; i += 1) { IrInstruction *new_value = new_incoming_values.at(i); IrBasicBlock *predecessor = new_incoming_blocks.at(i); IrInstruction *branch_instruction = predecessor->instruction_list.pop(); ir_set_cursor_at_end(&ira->new_irb, predecessor); IrInstruction *casted_value = ir_implicit_cast(ira, new_value, resolved_type); if (casted_value == ira->codegen->invalid_instruction) { return ira->codegen->builtin_types.entry_invalid; } 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_from(&ira->new_irb, &phi_instruction->base, new_incoming_blocks.length, new_incoming_blocks.items, new_incoming_values.items); if (all_stack_ptrs) { assert(result->value.special == ConstValSpecialRuntime); result->value.data.rh_ptr = RuntimeHintPtrStack; } return resolved_type; } static ZigType *ir_analyze_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ZigVar *var) { IrInstruction *result = ir_get_var_ptr(ira, instruction, var); ir_link_new_instruction(result, instruction); return result->value.type; } static ZigType *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructionVarPtr *instruction) { ZigVar *var = instruction->var; IrInstruction *result = ir_get_var_ptr(ira, &instruction->base, var); if (instruction->crossed_fndef_scope != nullptr && !instr_is_comptime(result)) { ErrorMsg *msg = ir_add_error(ira, &instruction->base, buf_sprintf("'%s' not accessible from inner function", buf_ptr(&var->name))); add_error_note(ira->codegen, msg, instruction->crossed_fndef_scope->base.source_node, buf_sprintf("crossed function definition here")); add_error_note(ira->codegen, msg, var->decl_node, buf_sprintf("declared here")); return ira->codegen->builtin_types.entry_invalid; } ir_link_new_instruction(result, &instruction->base); return result->value.type; } static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align) { assert(ptr_type->id == ZigTypeIdPointer); return get_pointer_to_type_extra(g, ptr_type->data.pointer.child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, ptr_type->data.pointer.ptr_len, new_align, ptr_type->data.pointer.bit_offset, ptr_type->data.pointer.unaligned_bit_count); } 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, ptr_type->data.pointer.unaligned_bit_count); } static ZigType *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) { Error err; IrInstruction *array_ptr = elem_ptr_instruction->array_ptr->other; if (type_is_invalid(array_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *orig_array_ptr_val = &array_ptr->value; IrInstruction *elem_index = elem_ptr_instruction->elem_index->other; if (type_is_invalid(elem_index->value.type)) return ira->codegen->builtin_types.entry_invalid; 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 array_type; } 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 = ir_const_ptr_pointee(ira, orig_array_ptr_val, elem_ptr_instruction->base.source_node); if (orig_array_ptr_val == nullptr) return ira->codegen->builtin_types.entry_invalid; } } 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->builtin_types.entry_invalid; } ZigType *child_type = array_type->data.array.child_type; if (ptr_type->data.pointer.unaligned_bit_count == 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); } else { uint64_t elem_val_scalar; if (!ir_resolve_usize(ira, elem_index, &elem_val_scalar)) return ira->codegen->builtin_types.entry_invalid; 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, (uint32_t)bit_width); } } 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->builtin_types.entry_invalid; } return_type = adjust_ptr_len(ira->codegen, array_type, elem_ptr_instruction->ptr_len); } else if (is_slice(array_type)) { return_type = adjust_ptr_len(ira->codegen, array_type->data.structure.fields[slice_ptr_index].type_entry, elem_ptr_instruction->ptr_len); } else if (array_type->id == ZigTypeIdArgTuple) { ConstExprValue *ptr_val = ir_resolve_const(ira, array_ptr, UndefBad); if (!ptr_val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *args_val = ir_const_ptr_pointee(ira, ptr_val, elem_ptr_instruction->base.source_node); if (args_val == nullptr) return ira->codegen->builtin_types.entry_invalid; size_t start = args_val->data.x_arg_tuple.start_index; size_t end = args_val->data.x_arg_tuple.end_index; uint64_t elem_index_val; if (!ir_resolve_usize(ira, elem_index, &elem_index_val)) return ira->codegen->builtin_types.entry_invalid; size_t index = elem_index_val; size_t len = end - start; if (index >= len) { ir_add_error(ira, &elem_ptr_instruction->base, buf_sprintf("index %" ZIG_PRI_usize " outside argument list of size %" ZIG_PRI_usize "", index, len)); return ira->codegen->builtin_types.entry_invalid; } size_t abs_index = start + index; ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); assert(fn_entry); ZigVar *var = get_fn_var_by_index(fn_entry, abs_index); bool is_const = true; bool is_volatile = false; if (var) { return ir_analyze_var_ptr(ira, &elem_ptr_instruction->base, var); } else { return ir_analyze_const_ptr(ira, &elem_ptr_instruction->base, &ira->codegen->const_void_val, ira->codegen->builtin_types.entry_void, ConstPtrMutComptimeConst, is_const, is_volatile); } } else { ir_add_error_node(ira, elem_ptr_instruction->base.source_node, buf_sprintf("array access of non-array type '%s'", buf_ptr(&array_type->name))); return ira->codegen->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; bool safety_check_on = elem_ptr_instruction->safety_check_on; if ((err = ensure_complete_type(ira->codegen, return_type->data.pointer.child_type))) return ira->codegen->builtin_types.entry_invalid; uint64_t elem_size = type_size(ira->codegen, return_type->data.pointer.child_type); uint64_t abi_align = get_abi_alignment(ira->codegen, return_type->data.pointer.child_type); uint64_t ptr_align = get_ptr_align(ira->codegen, return_type); if (instr_is_comptime(casted_elem_index)) { uint64_t index = bigint_as_unsigned(&casted_elem_index->value.data.x_bigint); if (array_type->id == ZigTypeIdArray) { uint64_t array_len = array_type->data.array.len; if (index >= array_len) { ir_add_error_node(ira, elem_ptr_instruction->base.source_node, buf_sprintf("index %" ZIG_PRI_u64 " outside array of size %" ZIG_PRI_u64, index, array_len)); return ira->codegen->builtin_types.entry_invalid; } safety_check_on = false; } { // figure out the largest alignment possible uint64_t chosen_align = abi_align; if (ptr_align >= abi_align) { while (ptr_align > abi_align) { if ((index * elem_size) % ptr_align == 0) { chosen_align = ptr_align; break; } ptr_align >>= 1; } } else if (elem_size >= ptr_align && elem_size % ptr_align == 0) { chosen_align = ptr_align; } else { // can't get here because guaranteed elem_size >= abi_align zig_unreachable(); } return_type = adjust_ptr_align(ira->codegen, return_type, chosen_align); } if (orig_array_ptr_val->special != ConstValSpecialRuntime && (orig_array_ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar || array_type->id == ZigTypeIdArray)) { ConstExprValue *array_ptr_val = ir_const_ptr_pointee(ira, orig_array_ptr_val, elem_ptr_instruction->base.source_node); if (array_ptr_val == nullptr) return ira->codegen->builtin_types.entry_invalid; if (array_ptr_val->special != ConstValSpecialRuntime && (array_type->id != ZigTypeIdPointer || array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr)) { if (array_type->id == ZigTypeIdPointer) { ConstExprValue *out_val = ir_build_const_from(ira, &elem_ptr_instruction->base); out_val->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut; size_t new_index; size_t mem_size; size_t old_size; switch (array_ptr_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: case ConstPtrSpecialDiscard: zig_unreachable(); case ConstPtrSpecialRef: mem_size = 1; old_size = 1; new_index = index; out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.data.ref.pointee = array_ptr_val->data.x_ptr.data.ref.pointee; break; case ConstPtrSpecialBaseArray: { size_t offset = array_ptr_val->data.x_ptr.data.base_array.elem_index; new_index = offset + index; mem_size = array_ptr_val->data.x_ptr.data.base_array.array_val->type->data.array.len; old_size = mem_size - offset; assert(array_ptr_val->data.x_ptr.data.base_array.array_val); out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; out_val->data.x_ptr.data.base_array.array_val = array_ptr_val->data.x_ptr.data.base_array.array_val; out_val->data.x_ptr.data.base_array.elem_index = new_index; out_val->data.x_ptr.data.base_array.is_cstr = array_ptr_val->data.x_ptr.data.base_array.is_cstr; break; } case ConstPtrSpecialBaseStruct: zig_panic("TODO elem ptr on a const inner struct"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO element ptr of a function casted to a ptr"); } 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->builtin_types.entry_invalid; } return return_type; } else if (is_slice(array_type)) { ConstExprValue *ptr_field = &array_ptr_val->data.x_struct.fields[slice_ptr_index]; if (ptr_field->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { IrInstruction *result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope, elem_ptr_instruction->base.source_node, array_ptr, casted_elem_index, false, elem_ptr_instruction->ptr_len); result->value.type = return_type; ir_link_new_instruction(result, &elem_ptr_instruction->base); return return_type; } ConstExprValue *len_field = &array_ptr_val->data.x_struct.fields[slice_len_index]; ConstExprValue *out_val = ir_build_const_from(ira, &elem_ptr_instruction->base); uint64_t slice_len = bigint_as_unsigned(&len_field->data.x_bigint); if (index >= slice_len) { ir_add_error_node(ira, elem_ptr_instruction->base.source_node, buf_sprintf("index %" ZIG_PRI_u64 " outside slice of size %" ZIG_PRI_u64, index, slice_len)); return ira->codegen->builtin_types.entry_invalid; } out_val->data.x_ptr.mut = ptr_field->data.x_ptr.mut; switch (ptr_field->data.x_ptr.special) { case ConstPtrSpecialInvalid: case ConstPtrSpecialDiscard: zig_unreachable(); case ConstPtrSpecialRef: out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.data.ref.pointee = ptr_field->data.x_ptr.data.ref.pointee; break; case ConstPtrSpecialBaseArray: { size_t offset = ptr_field->data.x_ptr.data.base_array.elem_index; uint64_t new_index = offset + index; assert(new_index < ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len); out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; out_val->data.x_ptr.data.base_array.array_val = ptr_field->data.x_ptr.data.base_array.array_val; out_val->data.x_ptr.data.base_array.elem_index = new_index; out_val->data.x_ptr.data.base_array.is_cstr = ptr_field->data.x_ptr.data.base_array.is_cstr; break; } case ConstPtrSpecialBaseStruct: zig_panic("TODO elem ptr on a slice backed by const inner struct"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO elem ptr on a slice that was ptrcast from a function"); } return return_type; } else if (array_type->id == ZigTypeIdArray) { ConstExprValue *out_val = ir_build_const_from(ira, &elem_ptr_instruction->base); 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 return_type; } else { zig_unreachable(); } } } } else { // runtime known element index if (ptr_align < abi_align) { if (elem_size >= ptr_align && elem_size % ptr_align == 0) { return_type = adjust_ptr_align(ira->codegen, return_type, ptr_align); } else { // can't get here because guaranteed elem_size >= abi_align zig_unreachable(); } } else { return_type = adjust_ptr_align(ira->codegen, return_type, abi_align); } } IrInstruction *result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope, elem_ptr_instruction->base.source_node, array_ptr, casted_elem_index, safety_check_on, elem_ptr_instruction->ptr_len); result->value.type = return_type; ir_link_new_instruction(result, &elem_ptr_instruction->base); return return_type; } static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira, ZigType *bare_struct_type, Buf *field_name, IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type) { if (!is_slice(bare_struct_type)) { ScopeDecls *container_scope = get_container_scope(bare_struct_type); assert(container_scope != nullptr); auto entry = container_scope->decl_table.maybe_get(field_name); Tld *tld = entry ? entry->value : nullptr; if (tld && tld->id == TldIdFn) { resolve_top_level_decl(ira->codegen, tld, false, source_instr->source_node); if (tld->resolution == TldResolutionInvalid) return ira->codegen->invalid_instruction; TldFn *tld_fn = (TldFn *)tld; ZigFn *fn_entry = tld_fn->fn_entry; if (type_is_invalid(fn_entry->type_entry)) return ira->codegen->invalid_instruction; IrInstruction *bound_fn_value = ir_build_const_bound_fn(&ira->new_irb, source_instr->scope, source_instr->source_node, fn_entry, container_ptr); return ir_get_ref(ira, source_instr, bound_fn_value, true, false); } } const char *prefix_name; if (is_slice(bare_struct_type)) { prefix_name = ""; } else if (bare_struct_type->id == ZigTypeIdStruct) { prefix_name = "struct "; } else if (bare_struct_type->id == ZigTypeIdEnum) { prefix_name = "enum "; } else if (bare_struct_type->id == ZigTypeIdUnion) { prefix_name = "union "; } else { prefix_name = ""; } ir_add_error_node(ira, source_instr->source_node, buf_sprintf("no member named '%s' in %s'%s'", buf_ptr(field_name), prefix_name, buf_ptr(&bare_struct_type->name))); return ira->codegen->invalid_instruction; } static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name, IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type) { Error err; ZigType *bare_type = container_ref_type(container_type); if ((err = ensure_complete_type(ira->codegen, bare_type))) return ira->codegen->invalid_instruction; assert(container_ptr->value.type->id == ZigTypeIdPointer); bool is_const = container_ptr->value.type->data.pointer.is_const; bool is_volatile = container_ptr->value.type->data.pointer.is_volatile; if (bare_type->id == ZigTypeIdStruct) { TypeStructField *field = find_struct_type_field(bare_type, field_name); if (field) { bool is_packed = (bare_type->data.structure.layout == ContainerLayoutPacked); uint32_t align_bytes = is_packed ? 1 : get_abi_alignment(ira->codegen, field->type_entry); size_t ptr_bit_offset = container_ptr->value.type->data.pointer.bit_offset; size_t ptr_unaligned_bit_count = container_ptr->value.type->data.pointer.unaligned_bit_count; size_t unaligned_bit_count_for_result_type = (ptr_unaligned_bit_count == 0) ? field->unaligned_bit_count : type_size_bits(ira->codegen, field->type_entry); if (instr_is_comptime(container_ptr)) { ConstExprValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); if (!ptr_val) return ira->codegen->invalid_instruction; if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { ConstExprValue *struct_val = ir_const_ptr_pointee(ira, 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; ConstExprValue *field_val = &struct_val->data.x_struct.fields[field->src_index]; ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_val->type, is_const, is_volatile, PtrLenSingle, align_bytes, (uint32_t)(ptr_bit_offset + field->packed_bits_offset), (uint32_t)unaligned_bit_count_for_result_type); IrInstruction *result = ir_get_const(ira, source_instr); ConstExprValue *const_val = &result->value; const_val->data.x_ptr.special = ConstPtrSpecialBaseStruct; const_val->data.x_ptr.mut = container_ptr->value.data.x_ptr.mut; const_val->data.x_ptr.data.base_struct.struct_val = struct_val; const_val->data.x_ptr.data.base_struct.field_index = field->src_index; const_val->type = ptr_type; return result; } } IrInstruction *result = ir_build_struct_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, container_ptr, field); result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, PtrLenSingle, align_bytes, (uint32_t)(ptr_bit_offset + field->packed_bits_offset), (uint32_t)unaligned_bit_count_for_result_type); return result; } else { return ir_analyze_container_member_access_inner(ira, bare_type, field_name, source_instr, container_ptr, container_type); } } else if (bare_type->id == ZigTypeIdEnum) { return ir_analyze_container_member_access_inner(ira, bare_type, field_name, source_instr, container_ptr, container_type); } else if (bare_type->id == ZigTypeIdUnion) { TypeUnionField *field = find_union_type_field(bare_type, field_name); if (field) { if (instr_is_comptime(container_ptr)) { ConstExprValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); if (!ptr_val) return ira->codegen->invalid_instruction; if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { ConstExprValue *union_val = ir_const_ptr_pointee(ira, ptr_val, source_instr->source_node); if (union_val == nullptr) return ira->codegen->invalid_instruction; if (type_is_invalid(union_val->type)) return ira->codegen->invalid_instruction; TypeUnionField *actual_field = find_union_field_by_tag(bare_type, &union_val->data.x_union.tag); if (actual_field == nullptr) zig_unreachable(); if (field != actual_field) { ir_add_error_node(ira, source_instr->source_node, buf_sprintf("accessing union field '%s' while field '%s' is set", buf_ptr(field_name), buf_ptr(actual_field->name))); return ira->codegen->invalid_instruction; } ConstExprValue *payload_val = union_val->data.x_union.payload; ZigType *field_type = field->type_entry; if (field_type->id == ZigTypeIdVoid) { assert(payload_val == nullptr); payload_val = create_const_vals(1); payload_val->special = ConstValSpecialStatic; payload_val->type = field_type; } ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, is_const, is_volatile, PtrLenSingle, 0, 0, 0); IrInstruction *result = ir_get_const(ira, source_instr); ConstExprValue *const_val = &result->value; const_val->data.x_ptr.special = ConstPtrSpecialRef; const_val->data.x_ptr.mut = container_ptr->value.data.x_ptr.mut; const_val->data.x_ptr.data.ref.pointee = payload_val; const_val->type = ptr_type; return result; } } IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, container_ptr, field); result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, PtrLenSingle, 0, 0, 0); return result; } else { return ir_analyze_container_member_access_inner(ira, bare_type, field_name, source_instr, container_ptr, container_type); } } else { zig_unreachable(); } } static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name, AstNode *source_node) { 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; } } 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 ZigType *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source_instruction, Tld *tld) { bool pointer_only = false; resolve_top_level_decl(ira->codegen, tld, pointer_only, source_instruction->source_node); if (tld->resolution == TldResolutionInvalid) return ira->codegen->builtin_types.entry_invalid; switch (tld->id) { case TldIdContainer: case TldIdCompTime: zig_unreachable(); case TldIdVar: { TldVar *tld_var = (TldVar *)tld; ZigVar *var = tld_var->var; if (tld_var->extern_lib_name != nullptr) { add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, source_instruction->source_node); } return ir_analyze_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->builtin_types.entry_invalid; // TODO instead of allocating this every time, put it in the tld value and we can reference // the same one every time ConstExprValue *const_val = create_const_vals(1); const_val->special = ConstValSpecialStatic; const_val->type = fn_entry->type_entry; const_val->data.x_ptr.data.fn.fn_entry = fn_entry; const_val->data.x_ptr.special = ConstPtrSpecialFunction; const_val->data.x_ptr.mut = ConstPtrMutComptimeConst; if (tld_fn->extern_lib_name != nullptr) { add_link_lib_symbol(ira, tld_fn->extern_lib_name, &fn_entry->symbol_name, source_instruction->source_node); } bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, source_instruction, const_val, fn_entry->type_entry, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } } 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 ZigType *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) { Error err; IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other; if (type_is_invalid(container_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; ZigType *container_type = container_ptr->value.type->data.pointer.child_type; assert(container_ptr->value.type->id == ZigTypeIdPointer); Buf *field_name = field_ptr_instruction->field_name_buffer; if (!field_name) { IrInstruction *field_name_expr = field_ptr_instruction->field_name_expr->other; field_name = ir_resolve_str(ira, field_name_expr); if (!field_name) return ira->codegen->builtin_types.entry_invalid; } AstNode *source_node = field_ptr_instruction->base.source_node; if (type_is_invalid(container_type)) { return container_type; } else if (is_container_ref(container_type)) { assert(container_ptr->value.type->id == ZigTypeIdPointer); if (container_type->id == ZigTypeIdPointer) { ZigType *bare_type = container_ref_type(container_type); IrInstruction *container_child = ir_get_deref(ira, &field_ptr_instruction->base, container_ptr); IrInstruction *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base, container_child, bare_type); ir_link_new_instruction(result, &field_ptr_instruction->base); return result->value.type; } else { IrInstruction *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base, container_ptr, container_type); ir_link_new_instruction(result, &field_ptr_instruction->base); return result->value.type; } } else if (is_array_ref(container_type)) { if (buf_eql_str(field_name, "len")) { ConstExprValue *len_val = create_const_vals(1); if (container_type->id == ZigTypeIdPointer) { init_const_usize(ira->codegen, len_val, container_type->data.pointer.child_type->data.array.len); } else { init_const_usize(ira->codegen, len_val, container_type->data.array.len); } ZigType *usize = ira->codegen->builtin_types.entry_usize; bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, len_val, usize, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } else { ir_add_error_node(ira, source_node, buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&container_type->name))); return ira->codegen->builtin_types.entry_invalid; } } else if (container_type->id == ZigTypeIdArgTuple) { ConstExprValue *container_ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); if (!container_ptr_val) return ira->codegen->builtin_types.entry_invalid; assert(container_ptr->value.type->id == ZigTypeIdPointer); ConstExprValue *child_val = ir_const_ptr_pointee(ira, container_ptr_val, source_node); if (child_val == nullptr) return ira->codegen->builtin_types.entry_invalid; if (buf_eql_str(field_name, "len")) { ConstExprValue *len_val = create_const_vals(1); size_t len = child_val->data.x_arg_tuple.end_index - child_val->data.x_arg_tuple.start_index; init_const_usize(ira->codegen, len_val, len); ZigType *usize = ira->codegen->builtin_types.entry_usize; bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, len_val, usize, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } else { ir_add_error_node(ira, source_node, buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&container_type->name))); return ira->codegen->builtin_types.entry_invalid; } } else if (container_type->id == ZigTypeIdMetaType) { ConstExprValue *container_ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); if (!container_ptr_val) return ira->codegen->builtin_types.entry_invalid; assert(container_ptr->value.type->id == ZigTypeIdPointer); ConstExprValue *child_val = ir_const_ptr_pointee(ira, container_ptr_val, source_node); if (child_val == nullptr) return ira->codegen->builtin_types.entry_invalid; ZigType *child_type = child_val->data.x_type; if (type_is_invalid(child_type)) { return ira->codegen->builtin_types.entry_invalid; } else if (is_container(child_type)) { if (is_slice(child_type) && buf_eql_str(field_name, "Child")) { bool ptr_is_const = true; bool ptr_is_volatile = false; TypeStructField *ptr_field = &child_type->data.structure.fields[slice_ptr_index]; assert(ptr_field->type_entry->id == ZigTypeIdPointer); ZigType *child_type = ptr_field->type_entry->data.pointer.child_type; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, create_const_type(ira->codegen, child_type), ira->codegen->builtin_types.entry_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } if (child_type->id == ZigTypeIdEnum) { if ((err = ensure_complete_type(ira->codegen, child_type))) return ira->codegen->builtin_types.entry_invalid; TypeEnumField *field = find_enum_type_field(child_type, field_name); if (field) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, create_const_enum(child_type, &field->value), child_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } } ScopeDecls *container_scope = get_container_scope(child_type); if (container_scope != nullptr) { auto entry = container_scope->decl_table.maybe_get(field_name); Tld *tld = entry ? entry->value : nullptr; if (tld) { return ir_analyze_decl_ref(ira, &field_ptr_instruction->base, tld); } } if (child_type->id == ZigTypeIdUnion && (child_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr || child_type->data.unionation.decl_node->data.container_decl.auto_enum)) { if ((err = ensure_complete_type(ira->codegen, child_type))) return ira->codegen->builtin_types.entry_invalid; 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_analyze_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); } } ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("container '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->builtin_types.entry_invalid; } else if (child_type->id == 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(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->err_enumerators.append(ZigLLVMCreateDebugEnumerator(ira->codegen->dbuilder, buf_ptr(field_name), error_value_count)); 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->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; } err_set_type = child_type; } ConstExprValue *const_val = create_const_vals(1); const_val->special = ConstValSpecialStatic; const_val->type = err_set_type; const_val->data.x_err_set = err_entry; bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, const_val, err_set_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } 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_analyze_const_ptr(ira, &field_ptr_instruction->base, create_const_unsigned_negative(ira->codegen->builtin_types.entry_num_lit_int, child_type->data.integral.bit_count, false), ira->codegen->builtin_types.entry_num_lit_int, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } else if (buf_eql_str(field_name, "is_signed")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, create_const_bool(ira->codegen, child_type->data.integral.is_signed), ira->codegen->builtin_types.entry_bool, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } else { ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("type '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->builtin_types.entry_invalid; } } else 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_analyze_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); } else { ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("type '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->builtin_types.entry_invalid; } } else if (child_type->id == ZigTypeIdPointer) { if (buf_eql_str(field_name, "Child")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_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); } 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->builtin_types.entry_invalid; } return ir_analyze_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); } else { ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("type '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->builtin_types.entry_invalid; } } else if (child_type->id == ZigTypeIdArray) { if (buf_eql_str(field_name, "Child")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_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); } else if (buf_eql_str(field_name, "len")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, create_const_unsigned_negative(ira->codegen->builtin_types.entry_num_lit_int, child_type->data.array.len, false), ira->codegen->builtin_types.entry_num_lit_int, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } else { ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("type '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->builtin_types.entry_invalid; } } else if (child_type->id == ZigTypeIdErrorUnion) { if (buf_eql_str(field_name, "Payload")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_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); } else if (buf_eql_str(field_name, "ErrorSet")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_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); } else { ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("type '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->builtin_types.entry_invalid; } } else if (child_type->id == ZigTypeIdOptional) { if (buf_eql_str(field_name, "Child")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_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); } else { ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("type '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->builtin_types.entry_invalid; } } else 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->builtin_types.entry_invalid; } bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_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); } else if (buf_eql_str(field_name, "is_var_args")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_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); } else if (buf_eql_str(field_name, "arg_count")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_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); } else { ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("type '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->builtin_types.entry_invalid; } } else { ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("type '%s' does not support field access", buf_ptr(&child_type->name))); return ira->codegen->builtin_types.entry_invalid; } } else if (container_type->id == ZigTypeIdNamespace) { assert(container_ptr->value.type->id == ZigTypeIdPointer); ConstExprValue *container_ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); if (!container_ptr_val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *namespace_val = ir_const_ptr_pointee(ira, container_ptr_val, field_ptr_instruction->base.source_node); if (namespace_val == nullptr) return ira->codegen->builtin_types.entry_invalid; assert(namespace_val->special == ConstValSpecialStatic); ImportTableEntry *namespace_import = namespace_val->data.x_import; Tld *tld = find_decl(ira->codegen, &namespace_import->decls_scope->base, field_name); if (tld) { if (tld->visib_mod == VisibModPrivate && tld->import != source_node->owner) { ErrorMsg *msg = ir_add_error_node(ira, source_node, buf_sprintf("'%s' is private", buf_ptr(field_name))); add_error_note(ira->codegen, msg, tld->source_node, buf_sprintf("declared here")); return ira->codegen->builtin_types.entry_invalid; } return ir_analyze_decl_ref(ira, &field_ptr_instruction->base, tld); } else { const char *import_name = namespace_import->path ? buf_ptr(namespace_import->path) : "(C import)"; ir_add_error_node(ira, source_node, buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), import_name)); return ira->codegen->builtin_types.entry_invalid; } } else { ir_add_error_node(ira, field_ptr_instruction->base.source_node, buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); return ira->codegen->builtin_types.entry_invalid; } } static ZigType *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) { IrInstruction *ptr = load_ptr_instruction->ptr->other; if (type_is_invalid(ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *result = ir_get_deref(ira, &load_ptr_instruction->base, ptr); ir_link_new_instruction(result, &load_ptr_instruction->base); assert(result->value.type); return result->value.type; } static ZigType *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *store_ptr_instruction) { IrInstruction *ptr = store_ptr_instruction->ptr->other; if (type_is_invalid(ptr->value.type)) return ptr->value.type; IrInstruction *value = store_ptr_instruction->value->other; if (type_is_invalid(value->value.type)) return value->value.type; if (ptr->value.type->id != ZigTypeIdPointer) { ir_add_error(ira, ptr, buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&ptr->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } if (ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) { return ir_analyze_void(ira, &store_ptr_instruction->base); } if (ptr->value.type->data.pointer.is_const && !store_ptr_instruction->base.is_gen) { ir_add_error(ira, &store_ptr_instruction->base, buf_sprintf("cannot assign to constant")); return ira->codegen->builtin_types.entry_invalid; } ZigType *child_type = ptr->value.type->data.pointer.child_type; IrInstruction *casted_value = ir_implicit_cast(ira, value, child_type); if (casted_value == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; if (instr_is_comptime(ptr) && ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst) { ir_add_error(ira, &store_ptr_instruction->base, buf_sprintf("cannot assign to constant")); return ira->codegen->builtin_types.entry_invalid; } if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) { if (instr_is_comptime(casted_value)) { ConstExprValue *dest_val = ir_const_ptr_pointee(ira, &ptr->value, store_ptr_instruction->base.source_node); if (dest_val == nullptr) return ira->codegen->builtin_types.entry_invalid; if (dest_val->special != ConstValSpecialRuntime) { *dest_val = casted_value->value; if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr) { ira->new_irb.current_basic_block->must_be_comptime_source_instr = &store_ptr_instruction->base; } return ir_analyze_void(ira, &store_ptr_instruction->base); } } ir_add_error(ira, &store_ptr_instruction->base, buf_sprintf("cannot store runtime value in compile time variable")); ConstExprValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); dest_val->type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->builtin_types.entry_invalid; } } ir_build_store_ptr_from(&ira->new_irb, &store_ptr_instruction->base, ptr, casted_value); return ira->codegen->builtin_types.entry_void; } static ZigType *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) { IrInstruction *expr_value = typeof_instruction->value->other; ZigType *type_entry = expr_value->value.type; if (type_is_invalid(type_entry)) return ira->codegen->builtin_types.entry_invalid; switch (type_entry->id) { case ZigTypeIdInvalid: zig_unreachable(); // handled above case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdNamespace: case ZigTypeIdBoundFn: case ZigTypeIdMetaType: case ZigTypeIdVoid: case ZigTypeIdBool: case ZigTypeIdUnreachable: case ZigTypeIdInt: case ZigTypeIdFloat: case ZigTypeIdPointer: case ZigTypeIdArray: case ZigTypeIdStruct: case ZigTypeIdOptional: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdEnum: case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: case ZigTypeIdPromise: { ConstExprValue *out_val = ir_build_const_from(ira, &typeof_instruction->base); out_val->data.x_type = type_entry; return ira->codegen->builtin_types.entry_type; } } zig_unreachable(); } static ZigType *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira, IrInstructionToPtrType *to_ptr_type_instruction) { IrInstruction *value = to_ptr_type_instruction->value->other; ZigType *type_entry = value->value.type; if (type_is_invalid(type_entry)) return type_entry; ZigType *ptr_type; if (type_entry->id == ZigTypeIdArray) { ptr_type = get_pointer_to_type(ira->codegen, type_entry->data.array.child_type, false); } else if (is_slice(type_entry)) { ptr_type = adjust_ptr_len(ira->codegen, type_entry->data.structure.fields[0].type_entry, PtrLenSingle); } else if (type_entry->id == ZigTypeIdArgTuple) { ConstExprValue *arg_tuple_val = ir_resolve_const(ira, value, UndefBad); if (!arg_tuple_val) return ira->codegen->builtin_types.entry_invalid; zig_panic("TODO for loop on var args"); } else { ir_add_error_node(ira, to_ptr_type_instruction->base.source_node, buf_sprintf("expected array type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *out_val = ir_build_const_from(ira, &to_ptr_type_instruction->base); out_val->data.x_type = ptr_type; return ira->codegen->builtin_types.entry_type; } static ZigType *ir_analyze_instruction_ptr_type_child(IrAnalyze *ira, IrInstructionPtrTypeChild *ptr_type_child_instruction) { IrInstruction *type_value = ptr_type_child_instruction->value->other; ZigType *type_entry = ir_resolve_type(ira, type_value); if (type_is_invalid(type_entry)) return type_entry; if (type_entry->id != ZigTypeIdPointer) { ir_add_error_node(ira, ptr_type_child_instruction->base.source_node, buf_sprintf("expected pointer type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *out_val = ir_build_const_from(ira, &ptr_type_child_instruction->base); out_val->data.x_type = type_entry->data.pointer.child_type; return ira->codegen->builtin_types.entry_type; } static ZigType *ir_analyze_instruction_set_cold(IrAnalyze *ira, IrInstructionSetCold *instruction) { if (ira->new_irb.exec->is_inline) { // ignore setCold when running functions at compile time ir_build_const_from(ira, &instruction->base); return ira->codegen->builtin_types.entry_void; } IrInstruction *is_cold_value = instruction->is_cold->other; bool want_cold; if (!ir_resolve_bool(ira, is_cold_value, &want_cold)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; } fn_entry->set_cold_node = instruction->base.source_node; fn_entry->is_cold = want_cold; ir_build_const_from(ira, &instruction->base); return ira->codegen->builtin_types.entry_void; } static ZigType *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 ir_build_const_from(ira, &set_runtime_safety_instruction->base); return ira->codegen->builtin_types.entry_void; } 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->other; bool want_runtime_safety; if (!ir_resolve_bool(ira, safety_on_value, &want_runtime_safety)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } *safety_set_node_ptr = source_node; *safety_off_ptr = !want_runtime_safety; ir_build_const_from(ira, &set_runtime_safety_instruction->base); return ira->codegen->builtin_types.entry_void; } static ZigType *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 ir_build_const_from(ira, &instruction->base); return ira->codegen->builtin_types.entry_void; } 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->other; FloatMode float_mode_scalar; if (!ir_resolve_float_mode(ira, float_mode_value, &float_mode_scalar)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } *fast_math_set_node_ptr = source_node; *fast_math_on_ptr = (float_mode_scalar == FloatModeOptimized); ir_build_const_from(ira, &instruction->base); return ira->codegen->builtin_types.entry_void; } static ZigType *ir_analyze_instruction_slice_type(IrAnalyze *ira, IrInstructionSliceType *slice_type_instruction) { Error err; uint32_t align_bytes = 0; if (slice_type_instruction->align_value != nullptr) { if (!ir_resolve_align(ira, slice_type_instruction->align_value->other, &align_bytes)) return ira->codegen->builtin_types.entry_invalid; } ZigType *child_type = ir_resolve_type(ira, slice_type_instruction->child_type->other); if (type_is_invalid(child_type)) return ira->codegen->builtin_types.entry_invalid; bool is_const = slice_type_instruction->is_const; bool is_volatile = slice_type_instruction->is_volatile; switch (child_type->id) { case ZigTypeIdInvalid: // handled above zig_unreachable(); case ZigTypeIdUnreachable: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: ir_add_error_node(ira, slice_type_instruction->base.source_node, buf_sprintf("slice of type '%s' not allowed", buf_ptr(&child_type->name))); return ira->codegen->builtin_types.entry_invalid; case ZigTypeIdMetaType: case ZigTypeIdVoid: case ZigTypeIdBool: case ZigTypeIdInt: case ZigTypeIdFloat: case ZigTypeIdPointer: case ZigTypeIdArray: case ZigTypeIdStruct: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdOptional: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdEnum: case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdNamespace: case ZigTypeIdBoundFn: case ZigTypeIdPromise: { if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type, is_const, is_volatile, PtrLenUnknown, align_bytes, 0, 0); ZigType *result_type = get_slice_type(ira->codegen, slice_ptr_type); ConstExprValue *out_val = ir_build_const_from(ira, &slice_type_instruction->base); out_val->data.x_type = result_type; return ira->codegen->builtin_types.entry_type; } } zig_unreachable(); } static ZigType *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAsm *asm_instruction) { assert(asm_instruction->base.source_node->type == NodeTypeAsmExpr); AstNodeAsmExpr *asm_expr = &asm_instruction->base.source_node->data.asm_expr; bool global_scope = (scope_fn_entry(asm_instruction->base.scope) == nullptr); if (global_scope) { if (asm_expr->output_list.length != 0 || asm_expr->input_list.length != 0 || asm_expr->clobber_list.length != 0) { ir_add_error(ira, &asm_instruction->base, buf_sprintf("global assembly cannot have inputs, outputs, or clobbers")); return ira->codegen->builtin_types.entry_invalid; } buf_append_char(&ira->codegen->global_asm, '\n'); buf_append_buf(&ira->codegen->global_asm, asm_expr->asm_template); ir_build_const_from(ira, &asm_instruction->base); return ira->codegen->builtin_types.entry_void; } if (!ir_emit_global_runtime_side_effect(ira, &asm_instruction->base)) return ira->codegen->builtin_types.entry_invalid; // TODO validate the output types and variable types IrInstruction **input_list = allocate(asm_expr->input_list.length); IrInstruction **output_types = allocate(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]->other; return_type = ir_resolve_type(ira, output_types[i]); if (type_is_invalid(return_type)) return ira->codegen->builtin_types.entry_invalid; } } for (size_t i = 0; i < asm_expr->input_list.length; i += 1) { input_list[i] = asm_instruction->input_list[i]->other; if (type_is_invalid(input_list[i]->value.type)) return ira->codegen->builtin_types.entry_invalid; } ir_build_asm_from(&ira->new_irb, &asm_instruction->base, input_list, output_types, asm_instruction->output_vars, asm_instruction->return_count, asm_instruction->has_side_effects); return return_type; } static ZigType *ir_analyze_instruction_array_type(IrAnalyze *ira, IrInstructionArrayType *array_type_instruction) { Error err; IrInstruction *size_value = array_type_instruction->size->other; uint64_t size; if (!ir_resolve_usize(ira, size_value, &size)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *child_type_value = array_type_instruction->child_type->other; ZigType *child_type = ir_resolve_type(ira, child_type_value); if (type_is_invalid(child_type)) return ira->codegen->builtin_types.entry_invalid; switch (child_type->id) { case ZigTypeIdInvalid: // handled above zig_unreachable(); case ZigTypeIdUnreachable: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: ir_add_error_node(ira, array_type_instruction->base.source_node, buf_sprintf("array of type '%s' not allowed", buf_ptr(&child_type->name))); return ira->codegen->builtin_types.entry_invalid; case ZigTypeIdMetaType: case ZigTypeIdVoid: case ZigTypeIdBool: case ZigTypeIdInt: case ZigTypeIdFloat: case ZigTypeIdPointer: case ZigTypeIdArray: case ZigTypeIdStruct: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdOptional: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdEnum: case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdNamespace: case ZigTypeIdBoundFn: case ZigTypeIdPromise: { if ((err = ensure_complete_type(ira->codegen, child_type))) return ira->codegen->builtin_types.entry_invalid; ZigType *result_type = get_array_type(ira->codegen, child_type, size); ConstExprValue *out_val = ir_build_const_from(ira, &array_type_instruction->base); out_val->data.x_type = result_type; return ira->codegen->builtin_types.entry_type; } } zig_unreachable(); } static ZigType *ir_analyze_instruction_promise_type(IrAnalyze *ira, IrInstructionPromiseType *instruction) { ZigType *promise_type; if (instruction->payload_type == nullptr) { promise_type = ira->codegen->builtin_types.entry_promise; } else { ZigType *payload_type = ir_resolve_type(ira, instruction->payload_type->other); if (type_is_invalid(payload_type)) return ira->codegen->builtin_types.entry_invalid; promise_type = get_promise_type(ira->codegen, payload_type); } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = promise_type; return ira->codegen->builtin_types.entry_type; } static ZigType *ir_analyze_instruction_size_of(IrAnalyze *ira, IrInstructionSizeOf *size_of_instruction) { Error err; IrInstruction *type_value = size_of_instruction->type_value->other; ZigType *type_entry = ir_resolve_type(ira, type_value); if ((err = ensure_complete_type(ira->codegen, type_entry))) return ira->codegen->builtin_types.entry_invalid; switch (type_entry->id) { case ZigTypeIdInvalid: // handled above zig_unreachable(); case ZigTypeIdUnreachable: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdBoundFn: case ZigTypeIdMetaType: case ZigTypeIdNamespace: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: ir_add_error_node(ira, size_of_instruction->base.source_node, buf_sprintf("no size available for type '%s'", buf_ptr(&type_entry->name))); return ira->codegen->builtin_types.entry_invalid; case ZigTypeIdVoid: case ZigTypeIdBool: case ZigTypeIdInt: case ZigTypeIdFloat: case ZigTypeIdPointer: case ZigTypeIdArray: case ZigTypeIdStruct: case ZigTypeIdOptional: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdEnum: case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdPromise: { uint64_t size_in_bytes = type_size(ira->codegen, type_entry); ConstExprValue *out_val = ir_build_const_from(ira, &size_of_instruction->base); bigint_init_unsigned(&out_val->data.x_bigint, size_in_bytes); return ira->codegen->builtin_types.entry_num_lit_int; } } zig_unreachable(); } static ZigType *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrInstructionTestNonNull *instruction) { IrInstruction *value = instruction->value->other; if (type_is_invalid(value->value.type)) return ira->codegen->builtin_types.entry_invalid; ZigType *type_entry = value->value.type; if (type_entry->id == ZigTypeIdOptional) { if (instr_is_comptime(value)) { ConstExprValue *maybe_val = ir_resolve_const(ira, value, UndefBad); if (!maybe_val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_bool = !optional_value_is_null(maybe_val); return ira->codegen->builtin_types.entry_bool; } ir_build_test_nonnull_from(&ira->new_irb, &instruction->base, value); return ira->codegen->builtin_types.entry_bool; } else if (type_entry->id == ZigTypeIdNull) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_bool = false; return ira->codegen->builtin_types.entry_bool; } else { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_bool = true; return ira->codegen->builtin_types.entry_bool; } } static ZigType *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, IrInstructionUnwrapOptional *unwrap_maybe_instruction) { IrInstruction *value = unwrap_maybe_instruction->value->other; if (type_is_invalid(value->value.type)) return ira->codegen->builtin_types.entry_invalid; ZigType *ptr_type = value->value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *type_entry = ptr_type->data.pointer.child_type; if (type_is_invalid(type_entry)) { return ira->codegen->builtin_types.entry_invalid; } else if (type_entry->id != ZigTypeIdOptional) { ir_add_error_node(ira, unwrap_maybe_instruction->value->source_node, buf_sprintf("expected optional type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } ZigType *child_type = type_entry->data.maybe.child_type; ZigType *result_type = get_pointer_to_type_extra(ira->codegen, child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); if (instr_is_comptime(value)) { ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); if (!val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *maybe_val = ir_const_ptr_pointee(ira, val, unwrap_maybe_instruction->base.source_node); if (maybe_val == nullptr) return ira->codegen->builtin_types.entry_invalid; if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { if (optional_value_is_null(maybe_val)) { ir_add_error(ira, &unwrap_maybe_instruction->base, buf_sprintf("unable to unwrap null")); return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *out_val = ir_build_const_from(ira, &unwrap_maybe_instruction->base); out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.mut = val->data.x_ptr.mut; if (type_is_codegen_pointer(child_type)) { out_val->data.x_ptr.data.ref.pointee = maybe_val; } else { out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_optional; } return result_type; } } ir_build_unwrap_maybe_from(&ira->new_irb, &unwrap_maybe_instruction->base, value, unwrap_maybe_instruction->safety_check_on); return result_type; } static ZigType *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstructionCtz *ctz_instruction) { IrInstruction *value = ctz_instruction->value->other; if (type_is_invalid(value->value.type)) { return ira->codegen->builtin_types.entry_invalid; } else if (value->value.type->id == ZigTypeIdInt) { ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, value->value.type->data.integral.bit_count); if (value->value.special != ConstValSpecialRuntime) { size_t result = bigint_ctz(&value->value.data.x_bigint, value->value.type->data.integral.bit_count); ConstExprValue *out_val = ir_build_const_from(ira, &ctz_instruction->base); bigint_init_unsigned(&out_val->data.x_bigint, result); return return_type; } ir_build_ctz_from(&ira->new_irb, &ctz_instruction->base, value); return return_type; } else { ir_add_error_node(ira, ctz_instruction->base.source_node, buf_sprintf("expected integer type, found '%s'", buf_ptr(&value->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } } static ZigType *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstructionClz *clz_instruction) { IrInstruction *value = clz_instruction->value->other; if (type_is_invalid(value->value.type)) { return ira->codegen->builtin_types.entry_invalid; } else if (value->value.type->id == ZigTypeIdInt) { ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, value->value.type->data.integral.bit_count); if (value->value.special != ConstValSpecialRuntime) { size_t result = bigint_clz(&value->value.data.x_bigint, value->value.type->data.integral.bit_count); ConstExprValue *out_val = ir_build_const_from(ira, &clz_instruction->base); bigint_init_unsigned(&out_val->data.x_bigint, result); return return_type; } ir_build_clz_from(&ira->new_irb, &clz_instruction->base, value); return return_type; } else { ir_add_error_node(ira, clz_instruction->base.source_node, buf_sprintf("expected integer type, found '%s'", buf_ptr(&value->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } } static ZigType *ir_analyze_instruction_pop_count(IrAnalyze *ira, IrInstructionPopCount *instruction) { IrInstruction *value = instruction->value->other; if (type_is_invalid(value->value.type)) return ira->codegen->builtin_types.entry_invalid; if (value->value.type->id != ZigTypeIdInt && value->value.type->id != ZigTypeIdComptimeInt) { ir_add_error(ira, value, buf_sprintf("expected integer type, found '%s'", buf_ptr(&value->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } if (instr_is_comptime(value)) { ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); if (!val) return ira->codegen->builtin_types.entry_invalid; if (bigint_cmp_zero(&val->data.x_bigint) != CmpLT) { size_t result = bigint_popcount_unsigned(&val->data.x_bigint); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); bigint_init_unsigned(&out_val->data.x_bigint, result); return ira->codegen->builtin_types.entry_num_lit_int; } if (value->value.type->id == ZigTypeIdComptimeInt) { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, &val->data.x_bigint, 10); ir_add_error(ira, &instruction->base, buf_sprintf("@popCount on negative %s value %s", buf_ptr(&value->value.type->name), buf_ptr(val_buf))); return ira->codegen->builtin_types.entry_invalid; } size_t result = bigint_popcount_signed(&val->data.x_bigint, value->value.type->data.integral.bit_count); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); bigint_init_unsigned(&out_val->data.x_bigint, result); return ira->codegen->builtin_types.entry_num_lit_int; } IrInstruction *result = ir_build_pop_count(&ira->new_irb, instruction->base.scope, instruction->base.source_node, value); result->value.type = get_smallest_unsigned_int_type(ira->codegen, value->value.type->data.integral.bit_count); ir_link_new_instruction(result, &instruction->base); return result->value.type; } static IrInstruction *ir_analyze_union_tag(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value) { if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; if (value->value.type->id == ZigTypeIdEnum) { return value; } if (value->value.type->id != ZigTypeIdUnion) { ir_add_error(ira, value, buf_sprintf("expected enum or union type, found '%s'", buf_ptr(&value->value.type->name))); return ira->codegen->invalid_instruction; } if (!value->value.type->data.unionation.have_explicit_tag_type && !source_instr->is_gen) { ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("union has no associated enum")); if (value->value.type->data.unionation.decl_node != nullptr) { add_error_note(ira->codegen, msg, value->value.type->data.unionation.decl_node, buf_sprintf("declared here")); } return ira->codegen->invalid_instruction; } ZigType *tag_type = value->value.type->data.unionation.tag_type; assert(tag_type->id == ZigTypeIdEnum); if (instr_is_comptime(value)) { ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); if (!val) return ira->codegen->invalid_instruction; IrInstructionConst *const_instruction = ir_create_instruction(&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 ZigType *ir_analyze_instruction_switch_br(IrAnalyze *ira, IrInstructionSwitchBr *switch_br_instruction) { IrInstruction *target_value = switch_br_instruction->target_value->other; 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->other->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->other, &is_comptime)) return ira->codegen->builtin_types.entry_invalid; if (is_comptime || instr_is_comptime(target_value)) { ConstExprValue *target_val = ir_resolve_const(ira, target_value, UndefBad); if (!target_val) return ir_unreach_error(ira); IrBasicBlock *old_dest_block = switch_br_instruction->else_block; for (size_t i = 0; i < case_count; i += 1) { IrInstructionSwitchBrCase *old_case = &switch_br_instruction->cases[i]; IrInstruction *case_value = old_case->value->other; if (type_is_invalid(case_value->value.type)) return ir_unreach_error(ira); if (case_value->value.type->id == ZigTypeIdEnum) { case_value = ir_analyze_union_tag(ira, &switch_br_instruction->base, case_value); if (type_is_invalid(case_value->value.type)) return ir_unreach_error(ira); } IrInstruction *casted_case_value = ir_implicit_cast(ira, case_value, target_value->value.type); if (type_is_invalid(casted_case_value->value.type)) return ir_unreach_error(ira); ConstExprValue *case_val = ir_resolve_const(ira, casted_case_value, UndefBad); if (!case_val) return ir_unreach_error(ira); if (const_values_equal(target_val, case_val)) { old_dest_block = old_case->block; break; } } if (is_comptime || old_dest_block->ref_count == 1) { return ir_inline_bb(ira, &switch_br_instruction->base, old_dest_block); } else { IrBasicBlock *new_dest_block = ir_get_new_bb(ira, old_dest_block, &switch_br_instruction->base); ir_build_br_from(&ira->new_irb, &switch_br_instruction->base, new_dest_block); return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); } } IrInstructionSwitchBrCase *cases = allocate(case_count); for (size_t i = 0; i < case_count; i += 1) { IrInstructionSwitchBrCase *old_case = &switch_br_instruction->cases[i]; IrInstructionSwitchBrCase *new_case = &cases[i]; new_case->block = ir_get_new_bb(ira, old_case->block, &switch_br_instruction->base); new_case->value = ira->codegen->invalid_instruction; // Calling ir_get_new_bb set the ref_instruction on the new basic block. // However a switch br may branch to the same basic block which would trigger an // incorrect re-generation of the block. So we set it to null here and assign // it back after the loop. new_case->block->ref_instruction = nullptr; IrInstruction *old_value = old_case->value; IrInstruction *new_value = old_value->other; if (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); ir_build_switch_br_from(&ira->new_irb, &switch_br_instruction->base, target_value, new_else_block, case_count, cases, nullptr, nullptr); return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); } static ZigType *ir_analyze_instruction_switch_target(IrAnalyze *ira, IrInstructionSwitchTarget *switch_target_instruction) { Error err; IrInstruction *target_value_ptr = switch_target_instruction->target_value_ptr->other; if (type_is_invalid(target_value_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; 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); ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base); out_val->type = ira->codegen->builtin_types.entry_type; out_val->data.x_type = ptr_type->data.pointer.child_type; return out_val->type; } if (target_value_ptr->value.type->id != ZigTypeIdPointer) { ir_add_error(ira, target_value_ptr, buf_sprintf("invalid deref on switch target")); return ira->codegen->builtin_types.entry_invalid; } ZigType *target_type = target_value_ptr->value.type->data.pointer.child_type; ConstExprValue *pointee_val = nullptr; if (instr_is_comptime(target_value_ptr)) { pointee_val = ir_const_ptr_pointee(ira, &target_value_ptr->value, target_value_ptr->source_node); if (pointee_val == nullptr) return ira->codegen->builtin_types.entry_invalid; if (pointee_val->special == ConstValSpecialRuntime) pointee_val = nullptr; } if ((err = ensure_complete_type(ira->codegen, target_type))) return ira->codegen->builtin_types.entry_invalid; switch (target_type->id) { case ZigTypeIdInvalid: zig_unreachable(); case ZigTypeIdMetaType: case ZigTypeIdVoid: case ZigTypeIdBool: case ZigTypeIdInt: case ZigTypeIdFloat: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdPointer: case ZigTypeIdPromise: case ZigTypeIdFn: case ZigTypeIdNamespace: case ZigTypeIdErrorSet: if (pointee_val) { ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base); copy_const_val(out_val, pointee_val, true); out_val->type = target_type; return target_type; } ir_build_load_ptr_from(&ira->new_irb, &switch_target_instruction->base, target_value_ptr); return target_type; case 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->builtin_types.entry_invalid; } ZigType *tag_type = target_type->data.unionation.tag_type; assert(tag_type != nullptr); assert(tag_type->id == ZigTypeIdEnum); if (pointee_val) { ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base); bigint_init_bigint(&out_val->data.x_enum_tag, &pointee_val->data.x_union.tag); return tag_type; } if (tag_type->data.enumeration.src_field_count == 1) { ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base); TypeEnumField *only_field = &tag_type->data.enumeration.fields[0]; bigint_init_bigint(&out_val->data.x_enum_tag, &only_field->value); return tag_type; } IrInstruction *union_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, switch_target_instruction->base.source_node, target_value_ptr); union_value->value.type = target_type; IrInstruction *union_tag_inst = ir_build_union_tag(&ira->new_irb, switch_target_instruction->base.scope, switch_target_instruction->base.source_node, union_value); union_tag_inst->value.type = tag_type; ir_link_new_instruction(union_tag_inst, &switch_target_instruction->base); return tag_type; } case ZigTypeIdEnum: { if ((err = type_resolve(ira->codegen, target_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; if (target_type->data.enumeration.src_field_count < 2) { TypeEnumField *only_field = &target_type->data.enumeration.fields[0]; ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base); bigint_init_bigint(&out_val->data.x_enum_tag, &only_field->value); return target_type; } if (pointee_val) { ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base); bigint_init_bigint(&out_val->data.x_enum_tag, &pointee_val->data.x_enum_tag); return target_type; } IrInstruction *enum_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, switch_target_instruction->base.source_node, target_value_ptr); enum_value->value.type = target_type; ir_link_new_instruction(enum_value, &switch_target_instruction->base); return target_type; } case ZigTypeIdErrorUnion: case ZigTypeIdUnreachable: case ZigTypeIdArray: case ZigTypeIdStruct: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdOptional: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: ir_add_error(ira, &switch_target_instruction->base, buf_sprintf("invalid switch target type '%s'", buf_ptr(&target_type->name))); return ira->codegen->builtin_types.entry_invalid; } zig_unreachable(); } static ZigType *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstructionSwitchVar *instruction) { IrInstruction *target_value_ptr = instruction->target_value_ptr->other; if (type_is_invalid(target_value_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *prong_value = instruction->prong_value->other; if (type_is_invalid(prong_value->value.type)) return ira->codegen->builtin_types.entry_invalid; assert(target_value_ptr->value.type->id == ZigTypeIdPointer); ZigType *target_type = target_value_ptr->value.type->data.pointer.child_type; if (target_type->id == ZigTypeIdUnion) { ConstExprValue *prong_val = ir_resolve_const(ira, prong_value, UndefBad); if (!prong_val) return ira->codegen->builtin_types.entry_invalid; assert(prong_value->value.type->id == ZigTypeIdEnum); TypeUnionField *field = find_union_field_by_tag(target_type, &prong_val->data.x_enum_tag); if (instr_is_comptime(target_value_ptr)) { ConstExprValue *target_val_ptr = ir_resolve_const(ira, target_value_ptr, UndefBad); if (!target_value_ptr) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *pointee_val = ir_const_ptr_pointee(ira, target_val_ptr, instruction->base.source_node); if (pointee_val == nullptr) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); 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 get_pointer_to_type(ira->codegen, field->type_entry, target_val_ptr->type->data.pointer.is_const); } ir_build_union_field_ptr_from(&ira->new_irb, &instruction->base, target_value_ptr, field); return get_pointer_to_type(ira->codegen, field->type_entry, target_value_ptr->value.type->data.pointer.is_const); } else { ir_add_error(ira, &instruction->base, buf_sprintf("switch on type '%s' provides no expression parameter", buf_ptr(&target_type->name))); return ira->codegen->builtin_types.entry_invalid; } } static ZigType *ir_analyze_instruction_union_tag(IrAnalyze *ira, IrInstructionUnionTag *instruction) { IrInstruction *value = instruction->value->other; IrInstruction *new_instruction = ir_analyze_union_tag(ira, &instruction->base, value); ir_link_new_instruction(new_instruction, &instruction->base); return new_instruction->value.type; } static ZigType *ir_analyze_instruction_import(IrAnalyze *ira, IrInstructionImport *import_instruction) { Error err; IrInstruction *name_value = import_instruction->name->other; Buf *import_target_str = ir_resolve_str(ira, name_value); if (!import_target_str) return ira->codegen->builtin_types.entry_invalid; AstNode *source_node = import_instruction->base.source_node; ImportTableEntry *import = source_node->owner; Buf *import_target_path; Buf *search_dir; assert(import->package); PackageTableEntry *target_package; auto package_entry = import->package->package_table.maybe_get(import_target_str); if (package_entry) { target_package = package_entry->value; import_target_path = &target_package->root_src_path; search_dir = &target_package->root_src_dir; } else { // try it as a filename target_package = import->package; import_target_path = import_target_str; // search relative to importing file search_dir = buf_alloc(); os_path_dirname(import->path, search_dir); } Buf full_path = BUF_INIT; os_path_join(search_dir, import_target_path, &full_path); Buf *import_code = buf_alloc(); Buf *resolved_path = buf_alloc(); Buf *resolve_paths[] = { &full_path, }; *resolved_path = os_path_resolve(resolve_paths, 1); auto import_entry = ira->codegen->import_table.maybe_get(resolved_path); if (import_entry) { ConstExprValue *out_val = ir_build_const_from(ira, &import_instruction->base); out_val->data.x_import = import_entry->value; return ira->codegen->builtin_types.entry_namespace; } if ((err = file_fetch(ira->codegen, resolved_path, import_code))) { if (err == ErrorFileNotFound) { ir_add_error_node(ira, source_node, buf_sprintf("unable to find '%s'", buf_ptr(import_target_path))); return ira->codegen->builtin_types.entry_invalid; } else { ir_add_error_node(ira, source_node, buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err))); return ira->codegen->builtin_types.entry_invalid; } } ImportTableEntry *target_import = add_source_file(ira->codegen, target_package, resolved_path, import_code); scan_import(ira->codegen, target_import); ConstExprValue *out_val = ir_build_const_from(ira, &import_instruction->base); out_val->data.x_import = target_import; return ira->codegen->builtin_types.entry_namespace; } static ZigType *ir_analyze_instruction_array_len(IrAnalyze *ira, IrInstructionArrayLen *array_len_instruction) { IrInstruction *array_value = array_len_instruction->array_value->other; ZigType *type_entry = array_value->value.type; if (type_is_invalid(type_entry)) { return ira->codegen->builtin_types.entry_invalid; } else if (type_entry->id == ZigTypeIdArray) { return ir_analyze_const_usize(ira, &array_len_instruction->base, type_entry->data.array.len); } else if (is_slice(type_entry)) { if (array_value->value.special != ConstValSpecialRuntime) { ConstExprValue *len_val = &array_value->value.data.x_struct.fields[slice_len_index]; if (len_val->special != ConstValSpecialRuntime) { return ir_analyze_const_usize(ira, &array_len_instruction->base, bigint_as_unsigned(&len_val->data.x_bigint)); } } TypeStructField *field = &type_entry->data.structure.fields[slice_len_index]; IrInstruction *len_ptr = ir_build_struct_field_ptr(&ira->new_irb, array_len_instruction->base.scope, array_len_instruction->base.source_node, array_value, field); len_ptr->value.type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_usize, true); ir_build_load_ptr_from(&ira->new_irb, &array_len_instruction->base, len_ptr); return ira->codegen->builtin_types.entry_usize; } else { ir_add_error_node(ira, array_len_instruction->base.source_node, buf_sprintf("type '%s' has no field 'len'", buf_ptr(&array_value->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } } static ZigType *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionRef *ref_instruction) { IrInstruction *value = ref_instruction->value->other; return ir_analyze_ref(ira, &ref_instruction->base, value, ref_instruction->is_const, ref_instruction->is_volatile); } static ZigType *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrInstruction *instruction, ZigType *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields) { Error err; assert(container_type->id == ZigTypeIdUnion); if ((err = ensure_complete_type(ira->codegen, container_type))) return ira->codegen->builtin_types.entry_invalid; if (instr_field_count != 1) { ir_add_error(ira, instruction, buf_sprintf("union initialization expects exactly one field")); return ira->codegen->builtin_types.entry_invalid; } IrInstructionContainerInitFieldsField *field = &fields[0]; IrInstruction *field_value = field->value->other; if (type_is_invalid(field_value->value.type)) return ira->codegen->builtin_types.entry_invalid; TypeUnionField *type_field = find_union_type_field(container_type, field->name); if (!type_field) { ir_add_error_node(ira, field->source_node, buf_sprintf("no member named '%s' in union '%s'", buf_ptr(field->name), buf_ptr(&container_type->name))); return ira->codegen->builtin_types.entry_invalid; } if (type_is_invalid(type_field->type_entry)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_field_value = ir_implicit_cast(ira, field_value, type_field->type_entry); if (casted_field_value == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; if ((err = type_resolve(ira->codegen, casted_field_value->value.type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope); if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime || !type_has_bits(casted_field_value->value.type)) { ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk); if (!field_val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, instruction); out_val->data.x_union.payload = field_val; out_val->data.x_union.tag = type_field->enum_field->value; ConstParent *parent = get_const_val_parent(ira->codegen, field_val); if (parent != nullptr) { parent->id = ConstParentIdUnion; parent->data.p_union.union_val = out_val; } return container_type; } IrInstruction *new_instruction = ir_build_union_init_from(&ira->new_irb, instruction, container_type, type_field, casted_field_value); ir_add_alloca(ira, new_instruction, container_type); return container_type; } static ZigType *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruction *instruction, ZigType *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields) { Error err; if (container_type->id == ZigTypeIdUnion) { return ir_analyze_container_init_fields_union(ira, instruction, container_type, instr_field_count, fields); } if (container_type->id != ZigTypeIdStruct || is_slice(container_type)) { ir_add_error(ira, instruction, buf_sprintf("type '%s' does not support struct initialization syntax", buf_ptr(&container_type->name))); return ira->codegen->builtin_types.entry_invalid; } if ((err = ensure_complete_type(ira->codegen, container_type))) return ira->codegen->builtin_types.entry_invalid; size_t actual_field_count = container_type->data.structure.src_field_count; IrInstruction *first_non_const_instruction = nullptr; AstNode **field_assign_nodes = allocate(actual_field_count); IrInstructionStructInitField *new_fields = allocate(actual_field_count); bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope); ConstExprValue const_val = {}; const_val.special = ConstValSpecialStatic; const_val.type = container_type; const_val.data.x_struct.fields = create_const_vals(actual_field_count); for (size_t i = 0; i < instr_field_count; i += 1) { IrInstructionContainerInitFieldsField *field = &fields[i]; IrInstruction *field_value = field->value->other; if (type_is_invalid(field_value->value.type)) return ira->codegen->builtin_types.entry_invalid; TypeStructField *type_field = find_struct_type_field(container_type, field->name); if (!type_field) { ir_add_error_node(ira, field->source_node, buf_sprintf("no member named '%s' in struct '%s'", buf_ptr(field->name), buf_ptr(&container_type->name))); return ira->codegen->builtin_types.entry_invalid; } if (type_is_invalid(type_field->type_entry)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_field_value = ir_implicit_cast(ira, field_value, type_field->type_entry); if (casted_field_value == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; size_t field_index = type_field->src_index; AstNode *existing_assign_node = field_assign_nodes[field_index]; if (existing_assign_node) { ErrorMsg *msg = ir_add_error_node(ira, field->source_node, buf_sprintf("duplicate field")); add_error_note(ira->codegen, msg, existing_assign_node, buf_sprintf("other field here")); return ira->codegen->builtin_types.entry_invalid; } field_assign_nodes[field_index] = field->source_node; new_fields[field_index].value = casted_field_value; new_fields[field_index].type_struct_field = type_field; if (const_val.special == ConstValSpecialStatic) { if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime) { ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk); if (!field_val) return ira->codegen->builtin_types.entry_invalid; copy_const_val(&const_val.data.x_struct.fields[field_index], field_val, true); } else { first_non_const_instruction = casted_field_value; const_val.special = ConstValSpecialRuntime; } } } bool any_missing = false; for (size_t i = 0; i < actual_field_count; i += 1) { if (!field_assign_nodes[i]) { ir_add_error_node(ira, instruction->source_node, buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i].name))); any_missing = true; } } if (any_missing) return ira->codegen->builtin_types.entry_invalid; if (const_val.special == ConstValSpecialStatic) { ConstExprValue *out_val = ir_build_const_from(ira, instruction); *out_val = const_val; for (size_t i = 0; i < instr_field_count; i += 1) { ConstExprValue *field_val = &out_val->data.x_struct.fields[i]; ConstParent *parent = get_const_val_parent(ira->codegen, field_val); if (parent != nullptr) { parent->id = ConstParentIdStruct; parent->data.p_struct.field_index = i; parent->data.p_struct.struct_val = out_val; } } return container_type; } if (is_comptime) { ir_add_error_node(ira, first_non_const_instruction->source_node, buf_sprintf("unable to evaluate constant expression")); return ira->codegen->builtin_types.entry_invalid; } IrInstruction *new_instruction = ir_build_struct_init_from(&ira->new_irb, instruction, container_type, actual_field_count, new_fields); ir_add_alloca(ira, new_instruction, container_type); return container_type; } static ZigType *ir_analyze_instruction_container_init_list(IrAnalyze *ira, IrInstructionContainerInitList *instruction) { IrInstruction *container_type_value = instruction->container_type->other; if (type_is_invalid(container_type_value->value.type)) return ira->codegen->builtin_types.entry_invalid; size_t elem_count = instruction->item_count; if (container_type_value->value.type->id == ZigTypeIdMetaType) { ZigType *container_type = ir_resolve_type(ira, container_type_value); if (type_is_invalid(container_type)) return ira->codegen->builtin_types.entry_invalid; if (container_type->id == ZigTypeIdStruct && !is_slice(container_type) && elem_count == 0) { return ir_analyze_container_init_fields(ira, &instruction->base, container_type, 0, nullptr); } else if (is_slice(container_type) || container_type->id == ZigTypeIdArray) { // array is same as slice init but we make a compile error if the length is wrong ZigType *child_type; if (container_type->id == ZigTypeIdArray) { child_type = container_type->data.array.child_type; if (container_type->data.array.len != elem_count) { ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count); ir_add_error(ira, &instruction->base, buf_sprintf("expected %s literal, found %s literal", buf_ptr(&container_type->name), buf_ptr(&literal_type->name))); return ira->codegen->builtin_types.entry_invalid; } } else { ZigType *pointer_type = container_type->data.structure.fields[slice_ptr_index].type_entry; assert(pointer_type->id == ZigTypeIdPointer); child_type = pointer_type->data.pointer.child_type; } ZigType *fixed_size_array_type = get_array_type(ira->codegen, child_type, elem_count); ConstExprValue const_val = {}; const_val.special = ConstValSpecialStatic; const_val.type = fixed_size_array_type; const_val.data.x_array.s_none.elements = create_const_vals(elem_count); bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope); IrInstruction **new_items = allocate(elem_count); IrInstruction *first_non_const_instruction = nullptr; for (size_t i = 0; i < elem_count; i += 1) { IrInstruction *arg_value = instruction->items[i]->other; if (type_is_invalid(arg_value->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_arg = ir_implicit_cast(ira, arg_value, child_type); if (casted_arg == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; new_items[i] = casted_arg; if (const_val.special == ConstValSpecialStatic) { if (is_comptime || casted_arg->value.special != ConstValSpecialRuntime) { ConstExprValue *elem_val = ir_resolve_const(ira, casted_arg, UndefBad); if (!elem_val) return ira->codegen->builtin_types.entry_invalid; copy_const_val(&const_val.data.x_array.s_none.elements[i], elem_val, true); } else { first_non_const_instruction = casted_arg; const_val.special = ConstValSpecialRuntime; } } } if (const_val.special == ConstValSpecialStatic) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); *out_val = const_val; for (size_t i = 0; i < elem_count; i += 1) { ConstExprValue *elem_val = &out_val->data.x_array.s_none.elements[i]; ConstParent *parent = get_const_val_parent(ira->codegen, elem_val); if (parent != nullptr) { parent->id = ConstParentIdArray; parent->data.p_array.array_val = out_val; parent->data.p_array.elem_index = i; } } return fixed_size_array_type; } if (is_comptime) { ir_add_error_node(ira, first_non_const_instruction->source_node, buf_sprintf("unable to evaluate constant expression")); return ira->codegen->builtin_types.entry_invalid; } IrInstruction *new_instruction = ir_build_container_init_list_from(&ira->new_irb, &instruction->base, container_type_value, elem_count, new_items); ir_add_alloca(ira, new_instruction, fixed_size_array_type); return fixed_size_array_type; } else if (container_type->id == ZigTypeIdVoid) { if (elem_count != 0) { ir_add_error_node(ira, instruction->base.source_node, buf_sprintf("void expression expects no arguments")); return ira->codegen->builtin_types.entry_invalid; } return ir_analyze_void(ira, &instruction->base); } else { ir_add_error_node(ira, instruction->base.source_node, buf_sprintf("type '%s' does not support array initialization", buf_ptr(&container_type->name))); return ira->codegen->builtin_types.entry_invalid; } } else { ir_add_error(ira, container_type_value, buf_sprintf("expected type, found '%s' value", buf_ptr(&container_type_value->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } } static ZigType *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, IrInstructionContainerInitFields *instruction) { IrInstruction *container_type_value = instruction->container_type->other; ZigType *container_type = ir_resolve_type(ira, container_type_value); if (type_is_invalid(container_type)) return ira->codegen->builtin_types.entry_invalid; return ir_analyze_container_init_fields(ira, &instruction->base, container_type, instruction->field_count, instruction->fields); } static ZigType *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *target_type_value, bool is_max) { ZigType *target_type = ir_resolve_type(ira, target_type_value); if (type_is_invalid(target_type)) return ira->codegen->builtin_types.entry_invalid; switch (target_type->id) { case ZigTypeIdInvalid: zig_unreachable(); case ZigTypeIdInt: { ConstExprValue *out_val = ir_build_const_from(ira, source_instruction); eval_min_max_value(ira->codegen, target_type, out_val, is_max); return ira->codegen->builtin_types.entry_num_lit_int; } case ZigTypeIdBool: case ZigTypeIdVoid: { ConstExprValue *out_val = ir_build_const_from(ira, source_instruction); eval_min_max_value(ira->codegen, target_type, out_val, is_max); return target_type; } case ZigTypeIdEnum: case ZigTypeIdFloat: case ZigTypeIdMetaType: case ZigTypeIdUnreachable: case ZigTypeIdPointer: case ZigTypeIdPromise: case ZigTypeIdArray: case ZigTypeIdStruct: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdOptional: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdNamespace: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: { const char *err_format = is_max ? "no max value available for type '%s'" : "no min value available for type '%s'"; ir_add_error(ira, source_instruction, buf_sprintf(err_format, buf_ptr(&target_type->name))); return ira->codegen->builtin_types.entry_invalid; } } zig_unreachable(); } static ZigType *ir_analyze_instruction_min_value(IrAnalyze *ira, IrInstructionMinValue *instruction) { return ir_analyze_min_max(ira, &instruction->base, instruction->value->other, false); } static ZigType *ir_analyze_instruction_max_value(IrAnalyze *ira, IrInstructionMaxValue *instruction) { return ir_analyze_min_max(ira, &instruction->base, instruction->value->other, true); } static ZigType *ir_analyze_instruction_compile_err(IrAnalyze *ira, IrInstructionCompileErr *instruction) { IrInstruction *msg_value = instruction->msg->other; Buf *msg_buf = ir_resolve_str(ira, msg_value); if (!msg_buf) return ira->codegen->builtin_types.entry_invalid; ErrorMsg *msg = ir_add_error(ira, &instruction->base, msg_buf); size_t i = ira->codegen->tld_ref_source_node_stack.length; for (;;) { if (i == 0) break; i -= 1; AstNode *source_node = ira->codegen->tld_ref_source_node_stack.at(i); if (source_node) { add_error_note(ira->codegen, msg, source_node, buf_sprintf("referenced here")); } } return ira->codegen->builtin_types.entry_invalid; } static ZigType *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]->other; if (type_is_invalid(msg->value.type)) return ira->codegen->builtin_types.entry_invalid; buf_resize(&buf, 0); render_const_value(ira->codegen, &buf, &msg->value); const char *comma_str = (i != 0) ? ", " : ""; fprintf(stderr, "%s%s", comma_str, buf_ptr(&buf)); } fprintf(stderr, "\n"); ir_add_error(ira, &instruction->base, buf_sprintf("found compile log statement")); ir_build_const_from(ira, &instruction->base); return ira->codegen->builtin_types.entry_void; } static ZigType *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstructionErrName *instruction) { IrInstruction *value = instruction->value->other; if (type_is_invalid(value->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_value = ir_implicit_cast(ira, value, value->value.type); if (type_is_invalid(casted_value->value.type)) return ira->codegen->builtin_types.entry_invalid; ZigType *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, true, false, PtrLenUnknown, 0, 0, 0); ZigType *str_type = get_slice_type(ira->codegen, u8_ptr_type); if (casted_value->value.special == ConstValSpecialStatic) { ErrorTableEntry *err = casted_value->value.data.x_err_set; if (!err->cached_error_name_val) { ConstExprValue *array_val = create_const_str_lit(ira->codegen, &err->name); err->cached_error_name_val = create_const_slice(ira->codegen, array_val, 0, buf_len(&err->name), true); } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); copy_const_val(out_val, err->cached_error_name_val, true); return str_type; } ira->codegen->generate_error_name_table = true; ir_build_err_name_from(&ira->new_irb, &instruction->base, value); return str_type; } static ZigType *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstructionTagName *instruction) { Error err; IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; assert(target->value.type->id == ZigTypeIdEnum); if (instr_is_comptime(target)) { if ((err = type_resolve(ira->codegen, target->value.type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; TypeEnumField *field = find_enum_field_by_tag(target->value.type, &target->value.data.x_bigint); ConstExprValue *array_val = create_const_str_lit(ira->codegen, field->name); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); init_const_slice(ira->codegen, out_val, array_val, 0, buf_len(field->name), true); return out_val->type; } IrInstruction *result = ir_build_tag_name(&ira->new_irb, instruction->base.scope, instruction->base.source_node, target); ir_link_new_instruction(result, &instruction->base); ZigType *u8_ptr_type = get_pointer_to_type_extra( ira->codegen, ira->codegen->builtin_types.entry_u8, true, false, PtrLenUnknown, 0, 0, 0); result->value.type = get_slice_type(ira->codegen, u8_ptr_type); return result->value.type; } static ZigType *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, IrInstructionFieldParentPtr *instruction) { Error err; IrInstruction *type_value = instruction->type_value->other; ZigType *container_type = ir_resolve_type(ira, type_value); if (type_is_invalid(container_type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *field_name_value = instruction->field_name->other; Buf *field_name = ir_resolve_str(ira, field_name_value); if (!field_name) return ira->codegen->builtin_types.entry_invalid; IrInstruction *field_ptr = instruction->field_ptr->other; if (type_is_invalid(field_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } if ((err = ensure_complete_type(ira->codegen, container_type))) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; } 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); 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->builtin_types.entry_invalid; 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); if (instr_is_comptime(casted_field_ptr)) { ConstExprValue *field_ptr_val = ir_resolve_const(ira, casted_field_ptr, UndefBad); if (!field_ptr_val) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); 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_type; } 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); ir_link_new_instruction(result, &instruction->base); return result_type; } static ZigType *ir_analyze_instruction_offset_of(IrAnalyze *ira, IrInstructionOffsetOf *instruction) { Error err; IrInstruction *type_value = instruction->type_value->other; ZigType *container_type = ir_resolve_type(ira, type_value); if (type_is_invalid(container_type)) return ira->codegen->builtin_types.entry_invalid; if ((err = ensure_complete_type(ira->codegen, container_type))) return ira->codegen->builtin_types.entry_invalid; IrInstruction *field_name_value = instruction->field_name->other; Buf *field_name = ir_resolve_str(ira, field_name_value); if (!field_name) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; } 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 ira->codegen->builtin_types.entry_invalid; } size_t byte_offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, container_type->type_ref, field->gen_index); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); bigint_init_unsigned(&out_val->data.x_bigint, byte_offset); return ira->codegen->builtin_types.entry_num_lit_int; } static void ensure_field_index(ZigType *type, const char *field_name, size_t index) { Buf *field_name_buf; assert(type != nullptr && !type_is_invalid(type)); // Check for our field by creating a buffer in place then using the comma operator to free it so that we don't // leak memory in debug mode. assert(find_struct_type_field(type, field_name_buf = buf_create_from_str(field_name))->src_index == index && (buf_deinit(field_name_buf), true)); } static ZigType *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, ZigType *root) { Error err; static ConstExprValue *type_info_var = nullptr; static ZigType *type_info_type = nullptr; if (type_info_var == nullptr) { type_info_var = get_builtin_value(ira->codegen, "TypeInfo"); assert(type_info_var->type->id == ZigTypeIdMetaType); assertNoError(ensure_complete_type(ira->codegen, type_info_var->data.x_type)); type_info_type = type_info_var->data.x_type; assert(type_info_type->id == ZigTypeIdUnion); } if (type_name == nullptr && root == nullptr) return type_info_type; else if (type_name == nullptr) return root; ZigType *root_type = (root == nullptr) ? type_info_type : root; ScopeDecls *type_info_scope = get_container_scope(root_type); assert(type_info_scope != nullptr); Buf field_name = BUF_INIT; buf_init_from_str(&field_name, type_name); auto entry = type_info_scope->decl_table.get(&field_name); buf_deinit(&field_name); TldVar *tld = (TldVar *)entry; assert(tld->base.id == TldIdVar); ZigVar *var = tld->var; if ((err = ensure_complete_type(ira->codegen, var->value->type))) return ira->codegen->builtin_types.entry_invalid; assert(var->value->type->id == ZigTypeIdMetaType); return var->value->data.x_type; } static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope) { Error err; ZigType *type_info_definition_type = ir_type_info_get_type(ira, "Definition", nullptr); if ((err = ensure_complete_type(ira->codegen, type_info_definition_type))) return err; ensure_field_index(type_info_definition_type, "name", 0); ensure_field_index(type_info_definition_type, "is_pub", 1); ensure_field_index(type_info_definition_type, "data", 2); ZigType *type_info_definition_data_type = ir_type_info_get_type(ira, "Data", type_info_definition_type); if ((err = ensure_complete_type(ira->codegen, type_info_definition_data_type))) return err; ZigType *type_info_fn_def_type = ir_type_info_get_type(ira, "FnDef", type_info_definition_data_type); if ((err = ensure_complete_type(ira->codegen, type_info_fn_def_type))) return err; ZigType *type_info_fn_def_inline_type = ir_type_info_get_type(ira, "Inline", type_info_fn_def_type); if ((err = ensure_complete_type(ira->codegen, type_info_fn_def_inline_type))) return err; // Loop through our definitions once to figure out how many definitions we will generate info for. auto decl_it = decls_scope->decl_table.entry_iterator(); decltype(decls_scope->decl_table)::Entry *curr_entry = nullptr; int definition_count = 0; while ((curr_entry = decl_it.next()) != nullptr) { // If the definition is unresolved, force it to be resolved again. if (curr_entry->value->resolution == TldResolutionUnresolved) { resolve_top_level_decl(ira->codegen, curr_entry->value, false, curr_entry->value->source_node); if (curr_entry->value->resolution != TldResolutionOk) { return ErrorSemanticAnalyzeFail; } } // Skip comptime blocks and test functions. if (curr_entry->value->id != TldIdCompTime) { if (curr_entry->value->id == TldIdFn) { ZigFn *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; if (fn_entry->is_test) continue; } definition_count += 1; } } ConstExprValue *definition_array = create_const_vals(1); definition_array->special = ConstValSpecialStatic; definition_array->type = get_array_type(ira->codegen, type_info_definition_type, definition_count); definition_array->data.x_array.special = ConstArraySpecialNone; definition_array->data.x_array.s_none.parent.id = ConstParentIdNone; definition_array->data.x_array.s_none.elements = create_const_vals(definition_count); init_const_slice(ira->codegen, out_val, definition_array, 0, definition_count, false); // Loop through the definitions and generate info. decl_it = decls_scope->decl_table.entry_iterator(); curr_entry = nullptr; int definition_index = 0; while ((curr_entry = decl_it.next()) != nullptr) { // Skip comptime blocks and test functions. if (curr_entry->value->id == TldIdCompTime) { continue; } else if (curr_entry->value->id == TldIdFn) { ZigFn *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; if (fn_entry->is_test) continue; } ConstExprValue *definition_val = &definition_array->data.x_array.s_none.elements[definition_index]; definition_val->special = ConstValSpecialStatic; definition_val->type = type_info_definition_type; ConstExprValue *inner_fields = create_const_vals(3); ConstExprValue *name = create_const_str_lit(ira->codegen, curr_entry->key); init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(curr_entry->key), true); inner_fields[1].special = ConstValSpecialStatic; inner_fields[1].type = ira->codegen->builtin_types.entry_bool; inner_fields[1].data.x_bool = curr_entry->value->visib_mod == VisibModPub; inner_fields[2].special = ConstValSpecialStatic; inner_fields[2].type = type_info_definition_data_type; inner_fields[2].data.x_union.parent.id = ConstParentIdStruct; inner_fields[2].data.x_union.parent.data.p_struct.struct_val = definition_val; inner_fields[2].data.x_union.parent.data.p_struct.field_index = 1; switch (curr_entry->value->id) { case TldIdVar: { ZigVar *var = ((TldVar *)curr_entry->value)->var; if ((err = ensure_complete_type(ira->codegen, var->value->type))) return ErrorSemanticAnalyzeFail; if (var->value->type->id == ZigTypeIdMetaType) { // We have a variable of type 'type', so it's actually a type definition. // 0: Data.Type: type bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0); inner_fields[2].data.x_union.payload = var->value; } else { // We have a variable of another type, so we store the type of the variable. // 1: Data.Var: type bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 1); ConstExprValue *payload = create_const_vals(1); payload->type = ira->codegen->builtin_types.entry_type; payload->data.x_type = var->value->type; inner_fields[2].data.x_union.payload = payload; } break; } case TldIdFn: { // 2: Data.Fn: Data.FnDef bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 2); ZigFn *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; assert(!fn_entry->is_test); AstNodeFnProto *fn_node = (AstNodeFnProto *)(fn_entry->proto_node); ConstExprValue *fn_def_val = create_const_vals(1); fn_def_val->special = ConstValSpecialStatic; fn_def_val->type = type_info_fn_def_type; fn_def_val->data.x_struct.parent.id = ConstParentIdUnion; fn_def_val->data.x_struct.parent.data.p_union.union_val = &inner_fields[2]; ConstExprValue *fn_def_fields = create_const_vals(9); fn_def_val->data.x_struct.fields = fn_def_fields; // fn_type: type ensure_field_index(fn_def_val->type, "fn_type", 0); fn_def_fields[0].special = ConstValSpecialStatic; fn_def_fields[0].type = ira->codegen->builtin_types.entry_type; fn_def_fields[0].data.x_type = fn_entry->type_entry; // inline_type: Data.FnDef.Inline ensure_field_index(fn_def_val->type, "inline_type", 1); fn_def_fields[1].special = ConstValSpecialStatic; fn_def_fields[1].type = type_info_fn_def_inline_type; bigint_init_unsigned(&fn_def_fields[1].data.x_enum_tag, fn_entry->fn_inline); // calling_convention: TypeInfo.CallingConvention ensure_field_index(fn_def_val->type, "calling_convention", 2); fn_def_fields[2].special = ConstValSpecialStatic; fn_def_fields[2].type = ir_type_info_get_type(ira, "CallingConvention", nullptr); bigint_init_unsigned(&fn_def_fields[2].data.x_enum_tag, fn_node->cc); // is_var_args: bool ensure_field_index(fn_def_val->type, "is_var_args", 3); bool is_varargs = fn_node->is_var_args; fn_def_fields[3].special = ConstValSpecialStatic; fn_def_fields[3].type = ira->codegen->builtin_types.entry_bool; fn_def_fields[3].data.x_bool = is_varargs; // is_extern: bool ensure_field_index(fn_def_val->type, "is_extern", 4); fn_def_fields[4].special = ConstValSpecialStatic; fn_def_fields[4].type = ira->codegen->builtin_types.entry_bool; fn_def_fields[4].data.x_bool = fn_node->is_extern; // is_export: bool ensure_field_index(fn_def_val->type, "is_export", 5); fn_def_fields[5].special = ConstValSpecialStatic; fn_def_fields[5].type = ira->codegen->builtin_types.entry_bool; fn_def_fields[5].data.x_bool = fn_node->is_export; // lib_name: ?[]const u8 ensure_field_index(fn_def_val->type, "lib_name", 6); fn_def_fields[6].special = ConstValSpecialStatic; ZigType *u8_ptr = get_pointer_to_type_extra( ira->codegen, ira->codegen->builtin_types.entry_u8, true, false, PtrLenUnknown, 0, 0, 0); fn_def_fields[6].type = get_optional_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr)); if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) { fn_def_fields[6].data.x_optional = create_const_vals(1); ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name); init_const_slice(ira->codegen, fn_def_fields[6].data.x_optional, lib_name, 0, buf_len(fn_node->lib_name), true); } else { fn_def_fields[6].data.x_optional = nullptr; } // return_type: type ensure_field_index(fn_def_val->type, "return_type", 7); fn_def_fields[7].special = ConstValSpecialStatic; fn_def_fields[7].type = ira->codegen->builtin_types.entry_type; if (fn_entry->src_implicit_return_type != nullptr) fn_def_fields[7].data.x_type = fn_entry->src_implicit_return_type; else if (fn_entry->type_entry->data.fn.gen_return_type != nullptr) fn_def_fields[7].data.x_type = fn_entry->type_entry->data.fn.gen_return_type; else fn_def_fields[7].data.x_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; // arg_names: [][] const u8 ensure_field_index(fn_def_val->type, "arg_names", 8); size_t fn_arg_count = fn_entry->variable_list.length; ConstExprValue *fn_arg_name_array = create_const_vals(1); fn_arg_name_array->special = ConstValSpecialStatic; fn_arg_name_array->type = get_array_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr), fn_arg_count); fn_arg_name_array->data.x_array.special = ConstArraySpecialNone; fn_arg_name_array->data.x_array.s_none.parent.id = ConstParentIdNone; fn_arg_name_array->data.x_array.s_none.elements = create_const_vals(fn_arg_count); init_const_slice(ira->codegen, &fn_def_fields[8], fn_arg_name_array, 0, fn_arg_count, false); for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) { ZigVar *arg_var = fn_entry->variable_list.at(fn_arg_index); ConstExprValue *fn_arg_name_val = &fn_arg_name_array->data.x_array.s_none.elements[fn_arg_index]; ConstExprValue *arg_name = create_const_str_lit(ira->codegen, &arg_var->name); init_const_slice(ira->codegen, fn_arg_name_val, arg_name, 0, buf_len(&arg_var->name), true); fn_arg_name_val->data.x_struct.parent.id = ConstParentIdArray; fn_arg_name_val->data.x_struct.parent.data.p_array.array_val = fn_arg_name_array; fn_arg_name_val->data.x_struct.parent.data.p_array.elem_index = fn_arg_index; } inner_fields[2].data.x_union.payload = fn_def_val; break; } case TldIdContainer: { ZigType *type_entry = ((TldContainer *)curr_entry->value)->type_entry; if ((err = ensure_complete_type(ira->codegen, type_entry))) return ErrorSemanticAnalyzeFail; // This is a type. bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0); ConstExprValue *payload = create_const_vals(1); payload->type = ira->codegen->builtin_types.entry_type; payload->data.x_type = type_entry; inner_fields[2].data.x_union.payload = payload; break; } default: zig_unreachable(); } definition_val->data.x_struct.fields = inner_fields; definition_index++; } assert(definition_index == definition_count); return ErrorNone; } static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_entry) { ZigType *attrs_type; uint32_t size_enum_index; if (is_slice(ptr_type_entry)) { attrs_type = ptr_type_entry->data.structure.fields[slice_ptr_index].type_entry; size_enum_index = 2; } else if (ptr_type_entry->id == ZigTypeIdPointer) { attrs_type = ptr_type_entry; size_enum_index = (ptr_type_entry->data.pointer.ptr_len == PtrLenSingle) ? 0 : 1; } else { zig_unreachable(); } ZigType *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer", nullptr); assertNoError(ensure_complete_type(ira->codegen, type_info_pointer_type)); ConstExprValue *result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = type_info_pointer_type; ConstExprValue *fields = create_const_vals(5); result->data.x_struct.fields = fields; // size: Size ensure_field_index(result->type, "size", 0); ZigType *type_info_pointer_size_type = ir_type_info_get_type(ira, "Size", type_info_pointer_type); assertNoError(ensure_complete_type(ira->codegen, type_info_pointer_size_type)); fields[0].special = ConstValSpecialStatic; fields[0].type = type_info_pointer_size_type; bigint_init_unsigned(&fields[0].data.x_enum_tag, size_enum_index); // is_const: bool ensure_field_index(result->type, "is_const", 1); fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_bool; fields[1].data.x_bool = attrs_type->data.pointer.is_const; // is_volatile: bool ensure_field_index(result->type, "is_volatile", 2); fields[2].special = ConstValSpecialStatic; fields[2].type = ira->codegen->builtin_types.entry_bool; fields[2].data.x_bool = attrs_type->data.pointer.is_volatile; // alignment: u32 ensure_field_index(result->type, "alignment", 3); fields[3].special = ConstValSpecialStatic; fields[3].type = get_int_type(ira->codegen, false, 29); 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; return result; }; static void make_enum_field_val(IrAnalyze *ira, ConstExprValue *enum_field_val, TypeEnumField *enum_field, ZigType *type_info_enum_field_type) { enum_field_val->special = ConstValSpecialStatic; enum_field_val->type = type_info_enum_field_type; ConstExprValue *inner_fields = create_const_vals(2); inner_fields[1].special = ConstValSpecialStatic; inner_fields[1].type = ira->codegen->builtin_types.entry_usize; ConstExprValue *name = create_const_str_lit(ira->codegen, enum_field->name); init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(enum_field->name), true); bigint_init_bigint(&inner_fields[1].data.x_bigint, &enum_field->value); enum_field_val->data.x_struct.fields = inner_fields; } static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstExprValue **out) { Error err; assert(type_entry != nullptr); assert(!type_is_invalid(type_entry)); if ((err = ensure_complete_type(ira->codegen, type_entry))) return err; if (type_entry == ira->codegen->builtin_types.entry_global_error_set) { zig_panic("TODO implement @typeInfo for global error set"); } ConstExprValue *result = nullptr; switch (type_entry->id) { case ZigTypeIdInvalid: zig_unreachable(); case ZigTypeIdMetaType: case ZigTypeIdVoid: case ZigTypeIdBool: case ZigTypeIdUnreachable: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdNamespace: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: *out = nullptr; return ErrorNone; default: { // Lookup an available value in our cache. auto entry = ira->codegen->type_info_cache.maybe_get(type_entry); if (entry != nullptr) { *out = entry->value; return ErrorNone; } // Fallthrough if we don't find one. } case ZigTypeIdInt: { result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Int", nullptr); ConstExprValue *fields = create_const_vals(2); result->data.x_struct.fields = fields; // is_signed: bool ensure_field_index(result->type, "is_signed", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_bool; fields[0].data.x_bool = type_entry->data.integral.is_signed; // bits: u8 ensure_field_index(result->type, "bits", 1); fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_u8; bigint_init_unsigned(&fields[1].data.x_bigint, type_entry->data.integral.bit_count); break; } case ZigTypeIdFloat: { result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Float", nullptr); ConstExprValue *fields = create_const_vals(1); result->data.x_struct.fields = fields; // bits: u8 ensure_field_index(result->type, "bits", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_u8; bigint_init_unsigned(&fields->data.x_bigint, type_entry->data.floating.bit_count); break; } case ZigTypeIdPointer: { result = create_ptr_like_type_info(ira, type_entry); break; } case ZigTypeIdArray: { result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Array", nullptr); ConstExprValue *fields = create_const_vals(2); result->data.x_struct.fields = fields; // len: usize ensure_field_index(result->type, "len", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_usize; bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.array.len); // child: type ensure_field_index(result->type, "child", 1); fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_type; fields[1].data.x_type = type_entry->data.array.child_type; break; } case ZigTypeIdOptional: { result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Optional", nullptr); ConstExprValue *fields = create_const_vals(1); result->data.x_struct.fields = fields; // child: type ensure_field_index(result->type, "child", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_type; fields[0].data.x_type = type_entry->data.maybe.child_type; break; } case ZigTypeIdPromise: { result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Promise", nullptr); ConstExprValue *fields = create_const_vals(1); result->data.x_struct.fields = fields; // child: ?type ensure_field_index(result->type, "child", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (type_entry->data.promise.result_type == nullptr) fields[0].data.x_optional = nullptr; else { ConstExprValue *child_type = create_const_vals(1); child_type->special = ConstValSpecialStatic; child_type->type = ira->codegen->builtin_types.entry_type; child_type->data.x_type = type_entry->data.promise.result_type; fields[0].data.x_optional = child_type; } break; } case ZigTypeIdEnum: { result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Enum", nullptr); ConstExprValue *fields = create_const_vals(4); result->data.x_struct.fields = fields; // layout: ContainerLayout ensure_field_index(result->type, "layout", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ir_type_info_get_type(ira, "ContainerLayout", nullptr); bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.enumeration.layout); // tag_type: type ensure_field_index(result->type, "tag_type", 1); fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_type; fields[1].data.x_type = type_entry->data.enumeration.tag_int_type; // fields: []TypeInfo.EnumField ensure_field_index(result->type, "fields", 2); ZigType *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField", nullptr); uint32_t enum_field_count = type_entry->data.enumeration.src_field_count; ConstExprValue *enum_field_array = create_const_vals(1); enum_field_array->special = ConstValSpecialStatic; enum_field_array->type = get_array_type(ira->codegen, type_info_enum_field_type, enum_field_count); enum_field_array->data.x_array.special = ConstArraySpecialNone; enum_field_array->data.x_array.s_none.parent.id = ConstParentIdNone; enum_field_array->data.x_array.s_none.elements = create_const_vals(enum_field_count); init_const_slice(ira->codegen, &fields[2], enum_field_array, 0, enum_field_count, false); for (uint32_t enum_field_index = 0; enum_field_index < enum_field_count; enum_field_index++) { TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index]; ConstExprValue *enum_field_val = &enum_field_array->data.x_array.s_none.elements[enum_field_index]; make_enum_field_val(ira, enum_field_val, enum_field, type_info_enum_field_type); enum_field_val->data.x_struct.parent.id = ConstParentIdArray; enum_field_val->data.x_struct.parent.data.p_array.array_val = enum_field_array; enum_field_val->data.x_struct.parent.data.p_array.elem_index = enum_field_index; } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 3); if ((err = ir_make_type_info_defs(ira, &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); ConstExprValue *fields = create_const_vals(1); result->data.x_struct.fields = fields; // errors: []TypeInfo.Error ensure_field_index(result->type, "errors", 0); ZigType *type_info_error_type = ir_type_info_get_type(ira, "Error", nullptr); uint32_t error_count = type_entry->data.error_set.err_count; ConstExprValue *error_array = create_const_vals(1); error_array->special = ConstValSpecialStatic; error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count); error_array->data.x_array.special = ConstArraySpecialNone; error_array->data.x_array.s_none.parent.id = ConstParentIdNone; error_array->data.x_array.s_none.elements = create_const_vals(error_count); init_const_slice(ira->codegen, &fields[0], error_array, 0, error_count, false); for (uint32_t error_index = 0; error_index < error_count; error_index++) { ErrorTableEntry *error = type_entry->data.error_set.errors[error_index]; ConstExprValue *error_val = &error_array->data.x_array.s_none.elements[error_index]; error_val->special = ConstValSpecialStatic; error_val->type = type_info_error_type; ConstExprValue *inner_fields = create_const_vals(2); inner_fields[1].special = ConstValSpecialStatic; inner_fields[1].type = ira->codegen->builtin_types.entry_usize; ConstExprValue *name = nullptr; if (error->cached_error_name_val != nullptr) name = error->cached_error_name_val; if (name == nullptr) name = create_const_str_lit(ira->codegen, &error->name); init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(&error->name), true); bigint_init_unsigned(&inner_fields[1].data.x_bigint, error->value); error_val->data.x_struct.fields = inner_fields; error_val->data.x_struct.parent.id = ConstParentIdArray; error_val->data.x_struct.parent.data.p_array.array_val = error_array; error_val->data.x_struct.parent.data.p_array.elem_index = error_index; } break; } case ZigTypeIdErrorUnion: { result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "ErrorUnion", nullptr); ConstExprValue *fields = create_const_vals(2); result->data.x_struct.fields = fields; // error_set: type ensure_field_index(result->type, "error_set", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_type; fields[0].data.x_type = type_entry->data.error_union.err_set_type; // payload: type ensure_field_index(result->type, "payload", 1); fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_type; fields[1].data.x_type = type_entry->data.error_union.payload_type; break; } case ZigTypeIdUnion: { result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Union", nullptr); ConstExprValue *fields = create_const_vals(4); result->data.x_struct.fields = fields; // layout: ContainerLayout ensure_field_index(result->type, "layout", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ir_type_info_get_type(ira, "ContainerLayout", nullptr); bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.unionation.layout); // tag_type: ?type ensure_field_index(result->type, "tag_type", 1); fields[1].special = ConstValSpecialStatic; fields[1].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); AstNode *union_decl_node = type_entry->data.unionation.decl_node; if (union_decl_node->data.container_decl.auto_enum || union_decl_node->data.container_decl.init_arg_expr != nullptr) { ConstExprValue *tag_type = create_const_vals(1); tag_type->special = ConstValSpecialStatic; tag_type->type = ira->codegen->builtin_types.entry_type; tag_type->data.x_type = type_entry->data.unionation.tag_type; fields[1].data.x_optional = tag_type; } else { fields[1].data.x_optional = nullptr; } // fields: []TypeInfo.UnionField ensure_field_index(result->type, "fields", 2); ZigType *type_info_union_field_type = ir_type_info_get_type(ira, "UnionField", nullptr); uint32_t union_field_count = type_entry->data.unionation.src_field_count; ConstExprValue *union_field_array = create_const_vals(1); union_field_array->special = ConstValSpecialStatic; union_field_array->type = get_array_type(ira->codegen, type_info_union_field_type, union_field_count); union_field_array->data.x_array.special = ConstArraySpecialNone; union_field_array->data.x_array.s_none.parent.id = ConstParentIdNone; union_field_array->data.x_array.s_none.elements = create_const_vals(union_field_count); init_const_slice(ira->codegen, &fields[2], union_field_array, 0, union_field_count, false); ZigType *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField", nullptr); for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) { TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index]; ConstExprValue *union_field_val = &union_field_array->data.x_array.s_none.elements[union_field_index]; union_field_val->special = ConstValSpecialStatic; union_field_val->type = type_info_union_field_type; ConstExprValue *inner_fields = create_const_vals(3); inner_fields[1].special = ConstValSpecialStatic; inner_fields[1].type = get_optional_type(ira->codegen, type_info_enum_field_type); if (fields[1].data.x_optional == nullptr) { inner_fields[1].data.x_optional = nullptr; } else { inner_fields[1].data.x_optional = create_const_vals(1); make_enum_field_val(ira, inner_fields[1].data.x_optional, union_field->enum_field, type_info_enum_field_type); } inner_fields[2].special = ConstValSpecialStatic; inner_fields[2].type = ira->codegen->builtin_types.entry_type; inner_fields[2].data.x_type = union_field->type_entry; ConstExprValue *name = create_const_str_lit(ira->codegen, union_field->name); init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(union_field->name), true); union_field_val->data.x_struct.fields = inner_fields; union_field_val->data.x_struct.parent.id = ConstParentIdArray; union_field_val->data.x_struct.parent.data.p_array.array_val = union_field_array; union_field_val->data.x_struct.parent.data.p_array.elem_index = union_field_index; } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 3); if ((err = ir_make_type_info_defs(ira, &fields[3], type_entry->data.unionation.decls_scope))) return err; break; } case ZigTypeIdStruct: { if (type_entry->data.structure.is_slice) { result = create_ptr_like_type_info(ira, type_entry); break; } result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Struct", nullptr); ConstExprValue *fields = create_const_vals(3); result->data.x_struct.fields = fields; // layout: ContainerLayout ensure_field_index(result->type, "layout", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ir_type_info_get_type(ira, "ContainerLayout", nullptr); bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.structure.layout); // fields: []TypeInfo.StructField ensure_field_index(result->type, "fields", 1); ZigType *type_info_struct_field_type = ir_type_info_get_type(ira, "StructField", nullptr); uint32_t struct_field_count = type_entry->data.structure.src_field_count; ConstExprValue *struct_field_array = create_const_vals(1); struct_field_array->special = ConstValSpecialStatic; struct_field_array->type = get_array_type(ira->codegen, type_info_struct_field_type, struct_field_count); struct_field_array->data.x_array.special = ConstArraySpecialNone; struct_field_array->data.x_array.s_none.parent.id = ConstParentIdNone; struct_field_array->data.x_array.s_none.elements = create_const_vals(struct_field_count); init_const_slice(ira->codegen, &fields[1], struct_field_array, 0, struct_field_count, false); for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) { TypeStructField *struct_field = &type_entry->data.structure.fields[struct_field_index]; ConstExprValue *struct_field_val = &struct_field_array->data.x_array.s_none.elements[struct_field_index]; struct_field_val->special = ConstValSpecialStatic; struct_field_val->type = type_info_struct_field_type; ConstExprValue *inner_fields = create_const_vals(3); inner_fields[1].special = ConstValSpecialStatic; inner_fields[1].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_usize); if (!type_has_bits(struct_field->type_entry)) { inner_fields[1].data.x_optional = nullptr; } else { size_t byte_offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, type_entry->type_ref, struct_field->gen_index); 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_usize; bigint_init_unsigned(&inner_fields[1].data.x_optional->data.x_bigint, byte_offset); } inner_fields[2].special = ConstValSpecialStatic; inner_fields[2].type = ira->codegen->builtin_types.entry_type; inner_fields[2].data.x_type = struct_field->type_entry; ConstExprValue *name = create_const_str_lit(ira->codegen, struct_field->name); init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(struct_field->name), true); struct_field_val->data.x_struct.fields = inner_fields; struct_field_val->data.x_struct.parent.id = ConstParentIdArray; struct_field_val->data.x_struct.parent.data.p_array.array_val = struct_field_array; struct_field_val->data.x_struct.parent.data.p_array.elem_index = struct_field_index; } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 2); if ((err = ir_make_type_info_defs(ira, &fields[2], type_entry->data.structure.decls_scope))) return err; break; } case ZigTypeIdFn: { result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Fn", nullptr); ConstExprValue *fields = create_const_vals(6); result->data.x_struct.fields = fields; // calling_convention: TypeInfo.CallingConvention ensure_field_index(result->type, "calling_convention", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ir_type_info_get_type(ira, "CallingConvention", nullptr); bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.fn.fn_type_id.cc); // is_generic: bool ensure_field_index(result->type, "is_generic", 1); bool is_generic = type_entry->data.fn.is_generic; fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_bool; fields[1].data.x_bool = is_generic; // is_varargs: bool ensure_field_index(result->type, "is_var_args", 2); bool is_varargs = type_entry->data.fn.fn_type_id.is_var_args; fields[2].special = ConstValSpecialStatic; fields[2].type = ira->codegen->builtin_types.entry_bool; fields[2].data.x_bool = type_entry->data.fn.fn_type_id.is_var_args; // return_type: ?type ensure_field_index(result->type, "return_type", 3); fields[3].special = ConstValSpecialStatic; fields[3].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (type_entry->data.fn.fn_type_id.return_type == nullptr) fields[3].data.x_optional = nullptr; else { ConstExprValue *return_type = create_const_vals(1); return_type->special = ConstValSpecialStatic; return_type->type = ira->codegen->builtin_types.entry_type; return_type->data.x_type = type_entry->data.fn.fn_type_id.return_type; fields[3].data.x_optional = return_type; } // async_allocator_type: type ensure_field_index(result->type, "async_allocator_type", 4); fields[4].special = ConstValSpecialStatic; fields[4].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (type_entry->data.fn.fn_type_id.async_allocator_type == nullptr) fields[4].data.x_optional = nullptr; else { ConstExprValue *async_alloc_type = create_const_vals(1); async_alloc_type->special = ConstValSpecialStatic; async_alloc_type->type = ira->codegen->builtin_types.entry_type; async_alloc_type->data.x_type = type_entry->data.fn.fn_type_id.async_allocator_type; fields[4].data.x_optional = async_alloc_type; } // args: []TypeInfo.FnArg ZigType *type_info_fn_arg_type = ir_type_info_get_type(ira, "FnArg", nullptr); size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count - (is_varargs && type_entry->data.fn.fn_type_id.cc != CallingConventionC); ConstExprValue *fn_arg_array = create_const_vals(1); fn_arg_array->special = ConstValSpecialStatic; fn_arg_array->type = get_array_type(ira->codegen, type_info_fn_arg_type, fn_arg_count); fn_arg_array->data.x_array.special = ConstArraySpecialNone; fn_arg_array->data.x_array.s_none.parent.id = ConstParentIdNone; fn_arg_array->data.x_array.s_none.elements = create_const_vals(fn_arg_count); init_const_slice(ira->codegen, &fields[5], fn_arg_array, 0, fn_arg_count, false); for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) { FnTypeParamInfo *fn_param_info = &type_entry->data.fn.fn_type_id.param_info[fn_arg_index]; ConstExprValue *fn_arg_val = &fn_arg_array->data.x_array.s_none.elements[fn_arg_index]; fn_arg_val->special = ConstValSpecialStatic; fn_arg_val->type = type_info_fn_arg_type; bool arg_is_generic = fn_param_info->type == nullptr; if (arg_is_generic) assert(is_generic); ConstExprValue *inner_fields = create_const_vals(3); inner_fields[0].special = ConstValSpecialStatic; inner_fields[0].type = ira->codegen->builtin_types.entry_bool; inner_fields[0].data.x_bool = arg_is_generic; inner_fields[1].special = ConstValSpecialStatic; inner_fields[1].type = ira->codegen->builtin_types.entry_bool; inner_fields[1].data.x_bool = fn_param_info->is_noalias; inner_fields[2].special = ConstValSpecialStatic; inner_fields[2].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (arg_is_generic) inner_fields[2].data.x_optional = nullptr; else { ConstExprValue *arg_type = create_const_vals(1); arg_type->special = ConstValSpecialStatic; arg_type->type = ira->codegen->builtin_types.entry_type; arg_type->data.x_type = fn_param_info->type; inner_fields[2].data.x_optional = arg_type; } fn_arg_val->data.x_struct.fields = inner_fields; fn_arg_val->data.x_struct.parent.id = ConstParentIdArray; fn_arg_val->data.x_struct.parent.data.p_array.array_val = fn_arg_array; fn_arg_val->data.x_struct.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, fn_type, &result))) return err; break; } } assert(result != nullptr); ira->codegen->type_info_cache.put(type_entry, result); *out = result; return ErrorNone; } static ZigType *ir_analyze_instruction_type_info(IrAnalyze *ira, IrInstructionTypeInfo *instruction) { Error err; IrInstruction *type_value = instruction->type_value->other; ZigType *type_entry = ir_resolve_type(ira, type_value); if (type_is_invalid(type_entry)) return ira->codegen->builtin_types.entry_invalid; ZigType *result_type = ir_type_info_get_type(ira, nullptr, nullptr); ConstExprValue *payload; if ((err = ir_make_type_info_value(ira, type_entry, &payload))) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->type = result_type; bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry)); out_val->data.x_union.payload = payload; if (payload != nullptr) { assert(payload->type->id == ZigTypeIdStruct); payload->data.x_struct.parent.id = ConstParentIdUnion; payload->data.x_struct.parent.data.p_union.union_val = out_val; } return result_type; } static ZigType *ir_analyze_instruction_type_id(IrAnalyze *ira, IrInstructionTypeId *instruction) { IrInstruction *type_value = instruction->type_value->other; ZigType *type_entry = ir_resolve_type(ira, type_value); if (type_is_invalid(type_entry)) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *var_value = get_builtin_value(ira->codegen, "TypeId"); assert(var_value->type->id == ZigTypeIdMetaType); ZigType *result_type = var_value->data.x_type; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); bigint_init_unsigned(&out_val->data.x_enum_tag, type_id_index(type_entry)); return result_type; } static ZigType *ir_analyze_instruction_set_eval_branch_quota(IrAnalyze *ira, IrInstructionSetEvalBranchQuota *instruction) { if (ira->new_irb.exec->parent_exec != nullptr && !ira->new_irb.exec->is_generic_instantiation) { ir_add_error(ira, &instruction->base, buf_sprintf("@setEvalBranchQuota must be called from the top of the comptime stack")); return ira->codegen->builtin_types.entry_invalid; } uint64_t new_quota; if (!ir_resolve_usize(ira, instruction->new_quota->other, &new_quota)) return ira->codegen->builtin_types.entry_invalid; if (new_quota > ira->new_irb.exec->backward_branch_quota) { ira->new_irb.exec->backward_branch_quota = new_quota; } ir_build_const_from(ira, &instruction->base); return ira->codegen->builtin_types.entry_void; } static ZigType *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstructionTypeName *instruction) { IrInstruction *type_value = instruction->type_value->other; ZigType *type_entry = ir_resolve_type(ira, type_value); if (type_is_invalid(type_entry)) return ira->codegen->builtin_types.entry_invalid; if (!type_entry->cached_const_name_val) { type_entry->cached_const_name_val = create_const_str_lit(ira->codegen, &type_entry->name); } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); copy_const_val(out_val, type_entry->cached_const_name_val, true); return out_val->type; } static ZigType *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstructionCImport *instruction) { if (ira->codegen->enable_cache) { ir_add_error(ira, &instruction->base, buf_sprintf("TODO @cImport is incompatible with --cache on. The cache system currently is unable to detect subsequent changes in .h files.")); return ira->codegen->builtin_types.entry_invalid; } AstNode *node = instruction->base.source_node; assert(node->type == NodeTypeFnCallExpr); AstNode *block_node = node->data.fn_call_expr.params.at(0); ScopeCImport *cimport_scope = create_cimport_scope(node, instruction->base.scope); // Execute the C import block like an inline function ZigType *void_type = ira->codegen->builtin_types.entry_void; IrInstruction *result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type, ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, &cimport_scope->buf, block_node, nullptr, nullptr); if (type_is_invalid(result->value.type)) return ira->codegen->builtin_types.entry_invalid; find_libc_include_path(ira->codegen); ImportTableEntry *child_import = allocate(1); child_import->decls_scope = create_decls_scope(node, nullptr, nullptr, child_import); child_import->c_import_node = node; child_import->package = new_anonymous_package(); child_import->package->package_table.put(buf_create_from_str("builtin"), ira->codegen->compile_var_package); child_import->package->package_table.put(buf_create_from_str("std"), ira->codegen->std_package); child_import->di_file = ZigLLVMCreateFile(ira->codegen->dbuilder, buf_ptr(buf_create_from_str("cimport.h")), buf_ptr(buf_create_from_str("."))); ZigList errors = {0}; int err; if ((err = parse_h_buf(child_import, &errors, &cimport_scope->buf, ira->codegen, node))) { if (err != ErrorCCompileErrors) { ir_add_error_node(ira, node, buf_sprintf("C import failed: %s", err_str(err))); return ira->codegen->builtin_types.entry_invalid; } } if (errors.length > 0) { ErrorMsg *parent_err_msg = ir_add_error_node(ira, node, buf_sprintf("C import failed")); for (size_t i = 0; i < errors.length; i += 1) { ErrorMsg *err_msg = errors.at(i); err_msg_add_note(parent_err_msg, err_msg); } return ira->codegen->builtin_types.entry_invalid; } if (ira->codegen->verbose_cimport) { fprintf(stderr, "\nC imports:\n"); fprintf(stderr, "-----------\n"); ast_render(ira->codegen, stderr, child_import->root, 4); } scan_decls(ira->codegen, child_import->decls_scope, child_import->root); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_import = child_import; return ira->codegen->builtin_types.entry_namespace; } static ZigType *ir_analyze_instruction_c_include(IrAnalyze *ira, IrInstructionCInclude *instruction) { IrInstruction *name_value = instruction->name->other; if (type_is_invalid(name_value->value.type)) return ira->codegen->builtin_types.entry_invalid; Buf *include_name = ir_resolve_str(ira, name_value); if (!include_name) return ira->codegen->builtin_types.entry_invalid; Buf *c_import_buf = exec_c_import_buf(ira->new_irb.exec); // We check for this error in pass1 assert(c_import_buf); buf_appendf(c_import_buf, "#include <%s>\n", buf_ptr(include_name)); ir_build_const_from(ira, &instruction->base); return ira->codegen->builtin_types.entry_void; } static ZigType *ir_analyze_instruction_c_define(IrAnalyze *ira, IrInstructionCDefine *instruction) { IrInstruction *name = instruction->name->other; if (type_is_invalid(name->value.type)) return ira->codegen->builtin_types.entry_invalid; Buf *define_name = ir_resolve_str(ira, name); if (!define_name) return ira->codegen->builtin_types.entry_invalid; IrInstruction *value = instruction->value->other; if (type_is_invalid(value->value.type)) return ira->codegen->builtin_types.entry_invalid; Buf *define_value = ir_resolve_str(ira, value); if (!define_value) return ira->codegen->builtin_types.entry_invalid; Buf *c_import_buf = exec_c_import_buf(ira->new_irb.exec); // We check for this error in pass1 assert(c_import_buf); buf_appendf(c_import_buf, "#define %s %s\n", buf_ptr(define_name), buf_ptr(define_value)); ir_build_const_from(ira, &instruction->base); return ira->codegen->builtin_types.entry_void; } static ZigType *ir_analyze_instruction_c_undef(IrAnalyze *ira, IrInstructionCUndef *instruction) { IrInstruction *name = instruction->name->other; if (type_is_invalid(name->value.type)) return ira->codegen->builtin_types.entry_invalid; Buf *undef_name = ir_resolve_str(ira, name); if (!undef_name) return ira->codegen->builtin_types.entry_invalid; Buf *c_import_buf = exec_c_import_buf(ira->new_irb.exec); // We check for this error in pass1 assert(c_import_buf); buf_appendf(c_import_buf, "#undef %s\n", buf_ptr(undef_name)); ir_build_const_from(ira, &instruction->base); return ira->codegen->builtin_types.entry_void; } static ZigType *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstructionEmbedFile *instruction) { IrInstruction *name = instruction->name->other; if (type_is_invalid(name->value.type)) return ira->codegen->builtin_types.entry_invalid; Buf *rel_file_path = ir_resolve_str(ira, name); if (!rel_file_path) return ira->codegen->builtin_types.entry_invalid; ImportTableEntry *import = get_scope_import(instruction->base.scope); // figure out absolute path to resource Buf source_dir_path = BUF_INIT; os_path_dirname(import->path, &source_dir_path); Buf *resolve_paths[] = { &source_dir_path, rel_file_path, }; Buf file_path = os_path_resolve(resolve_paths, 2); // load from file system into const expr Buf *file_contents = buf_alloc(); int 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->builtin_types.entry_invalid; } else { ir_add_error(ira, instruction->name, buf_sprintf("unable to open '%s': %s", buf_ptr(&file_path), err_str(err))); return ira->codegen->builtin_types.entry_invalid; } } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); init_const_str_lit(ira->codegen, out_val, file_contents); return get_array_type(ira->codegen, ira->codegen->builtin_types.entry_u8, buf_len(file_contents)); } static ZigType *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchg *instruction) { ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->type_value->other); if (type_is_invalid(operand_type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *ptr = instruction->ptr->other; if (type_is_invalid(ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; // 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->builtin_types.entry_invalid; IrInstruction *cmp_value = instruction->cmp_value->other; if (type_is_invalid(cmp_value->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *new_value = instruction->new_value->other; if (type_is_invalid(new_value->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *success_order_value = instruction->success_order_value->other; if (type_is_invalid(success_order_value->value.type)) return ira->codegen->builtin_types.entry_invalid; AtomicOrder success_order; if (!ir_resolve_atomic_order(ira, success_order_value, &success_order)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *failure_order_value = instruction->failure_order_value->other; if (type_is_invalid(failure_order_value->value.type)) return ira->codegen->builtin_types.entry_invalid; AtomicOrder failure_order; if (!ir_resolve_atomic_order(ira, failure_order_value, &failure_order)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_cmp_value = ir_implicit_cast(ira, cmp_value, operand_type); if (type_is_invalid(casted_cmp_value->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, operand_type); if (type_is_invalid(casted_new_value->value.type)) return ira->codegen->builtin_types.entry_invalid; if (success_order < AtomicOrderMonotonic) { ir_add_error(ira, success_order_value, buf_sprintf("success atomic ordering must be Monotonic or stricter")); return ira->codegen->builtin_types.entry_invalid; } if (failure_order < AtomicOrderMonotonic) { ir_add_error(ira, failure_order_value, buf_sprintf("failure atomic ordering must be Monotonic or stricter")); return ira->codegen->builtin_types.entry_invalid; } if (failure_order > success_order) { ir_add_error(ira, failure_order_value, buf_sprintf("failure atomic ordering must be no stricter than success")); return ira->codegen->builtin_types.entry_invalid; } if (failure_order == AtomicOrderRelease || failure_order == AtomicOrderAcqRel) { ir_add_error(ira, failure_order_value, buf_sprintf("failure atomic ordering must not be Release or AcqRel")); return ira->codegen->builtin_types.entry_invalid; } if (instr_is_comptime(casted_ptr) && instr_is_comptime(casted_cmp_value) && instr_is_comptime(casted_new_value)) { zig_panic("TODO compile-time execution of cmpxchg"); } IrInstruction *result = ir_build_cmpxchg(&ira->new_irb, instruction->base.scope, instruction->base.source_node, nullptr, casted_ptr, casted_cmp_value, casted_new_value, nullptr, nullptr, instruction->is_weak, operand_type, success_order, failure_order); result->value.type = get_optional_type(ira->codegen, operand_type); ir_link_new_instruction(result, &instruction->base); ir_add_alloca(ira, result, result->value.type); return result->value.type; } static ZigType *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstructionFence *instruction) { IrInstruction *order_value = instruction->order_value->other; if (type_is_invalid(order_value->value.type)) return ira->codegen->builtin_types.entry_invalid; AtomicOrder order; if (!ir_resolve_atomic_order(ira, order_value, &order)) return ira->codegen->builtin_types.entry_invalid; ir_build_fence_from(&ira->new_irb, &instruction->base, order_value, order); return ira->codegen->builtin_types.entry_void; } static ZigType *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstructionTruncate *instruction) { IrInstruction *dest_type_value = instruction->dest_type->other; ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } IrInstruction *target = instruction->target->other; ZigType *src_type = target->value.type; if (type_is_invalid(src_type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; } if (target->value.special == ConstValSpecialStatic) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); bigint_truncate(&out_val->data.x_bigint, &target->value.data.x_bigint, dest_type->data.integral.bit_count, dest_type->data.integral.is_signed); return dest_type; } IrInstruction *new_instruction = ir_build_truncate(&ira->new_irb, instruction->base.scope, instruction->base.source_node, dest_type_value, target); ir_link_new_instruction(new_instruction, &instruction->base); return dest_type; } static ZigType *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstructionIntCast *instruction) { ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->other); if (type_is_invalid(dest_type)) return ira->codegen->builtin_types.entry_invalid; if (dest_type->id != ZigTypeIdInt) { ir_add_error(ira, instruction->dest_type, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); return ira->codegen->builtin_types.entry_invalid; } IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; if (target->value.type->id == ZigTypeIdComptimeInt) { if (ir_num_lit_fits_in_other_type(ira, target, dest_type, true)) { IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpNumLitToConcrete, false); if (type_is_invalid(result->value.type)) return ira->codegen->builtin_types.entry_invalid; ir_link_new_instruction(result, &instruction->base); return dest_type; } else { return ira->codegen->builtin_types.entry_invalid; } } if (target->value.type->id != ZigTypeIdInt) { ir_add_error(ira, instruction->target, buf_sprintf("expected integer type, found '%s'", buf_ptr(&target->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } IrInstruction *result = ir_analyze_widen_or_shorten(ira, &instruction->base, target, dest_type); if (type_is_invalid(result->value.type)) return ira->codegen->builtin_types.entry_invalid; ir_link_new_instruction(result, &instruction->base); return dest_type; } static ZigType *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstructionFloatCast *instruction) { ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->other); if (type_is_invalid(dest_type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; 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; } IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, dest_type, op, false); if (type_is_invalid(result->value.type)) return ira->codegen->builtin_types.entry_invalid; ir_link_new_instruction(result, &instruction->base); return dest_type; } else { return ira->codegen->builtin_types.entry_invalid; } } 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->builtin_types.entry_invalid; } IrInstruction *result = ir_analyze_widen_or_shorten(ira, &instruction->base, target, dest_type); if (type_is_invalid(result->value.type)) return ira->codegen->builtin_types.entry_invalid; ir_link_new_instruction(result, &instruction->base); return dest_type; } static ZigType *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInstructionErrSetCast *instruction) { ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->other); if (type_is_invalid(dest_type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } IrInstruction *result = ir_analyze_err_set_cast(ira, &instruction->base, target, dest_type); if (type_is_invalid(result->value.type)) return ira->codegen->builtin_types.entry_invalid; ir_link_new_instruction(result, &instruction->base); return dest_type; } static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) { Error err; if (ty->id == ZigTypeIdPointer) { if ((err = type_resolve(ira->codegen, ty->data.pointer.child_type, ResolveStatusAlignmentKnown))) return err; } *result_align = get_ptr_align(ira->codegen, ty); return ErrorNone; } static ZigType *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstructionFromBytes *instruction) { Error err; ZigType *dest_child_type = ir_resolve_type(ira, instruction->dest_child_type->other); if (type_is_invalid(dest_child_type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; } else { src_ptr_const = true; src_ptr_volatile = false; if ((err = type_resolve(ira->codegen, target->value.type, ResolveStatusAlignmentKnown))) return ira->codegen->builtin_types.entry_invalid; src_ptr_align = get_abi_alignment(ira->codegen, target->value.type); } 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); 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); 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->builtin_types.entry_invalid; bool have_known_len = false; uint64_t known_len; if (instr_is_comptime(casted_value)) { ConstExprValue *val = ir_resolve_const(ira, casted_value, UndefBad); if (!val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *len_val = &val->data.x_struct.fields[slice_len_index]; if (value_is_comptime(len_val)) { known_len = bigint_as_unsigned(&len_val->data.x_bigint); have_known_len = true; } } if (casted_value->value.data.rh_slice.id == RuntimeHintSliceIdLen) { known_len = casted_value->value.data.rh_slice.len; have_known_len = true; } if (have_known_len) { uint64_t child_type_size = type_size(ira->codegen, dest_child_type); uint64_t remainder = known_len % child_type_size; if (remainder != 0) { ErrorMsg *msg = ir_add_error(ira, &instruction->base, buf_sprintf("unable to convert [%" ZIG_PRI_u64 "]u8 to %s: size mismatch", known_len, buf_ptr(&dest_slice_type->name))); add_error_note(ira->codegen, msg, instruction->dest_child_type->source_node, buf_sprintf("%s has size %" ZIG_PRI_u64 "; remaining bytes: %" ZIG_PRI_u64, buf_ptr(&dest_child_type->name), child_type_size, remainder)); return ira->codegen->builtin_types.entry_invalid; } } IrInstruction *result = ir_resolve_cast(ira, &instruction->base, casted_value, dest_slice_type, CastOpResizeSlice, true); ir_link_new_instruction(result, &instruction->base); return dest_slice_type; } static ZigType *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstructionToBytes *instruction) { Error err; IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; 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); ZigType *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type); IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, dest_slice_type, CastOpResizeSlice, true); ir_link_new_instruction(result, &instruction->base); return dest_slice_type; } static ZigType *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstructionIntToFloat *instruction) { ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->other); if (type_is_invalid(dest_type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpIntToFloat, false); ir_link_new_instruction(result, &instruction->base); return dest_type; } static ZigType *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrInstructionFloatToInt *instruction) { ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->other); if (type_is_invalid(dest_type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; if (target->value.type->id == ZigTypeIdComptimeInt) { IrInstruction *casted_value = ir_implicit_cast(ira, target, dest_type); if (type_is_invalid(casted_value->value.type)) return ira->codegen->builtin_types.entry_invalid; ir_link_new_instruction(casted_value, &instruction->base); return casted_value->value.type; } if (target->value.type->id != ZigTypeIdFloat && target->value.type->id != ZigTypeIdComptimeFloat) { ir_add_error(ira, instruction->target, buf_sprintf("expected float type, found '%s'", buf_ptr(&target->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpFloatToInt, false); ir_link_new_instruction(result, &instruction->base); return dest_type; } static ZigType *ir_analyze_instruction_err_to_int(IrAnalyze *ira, IrInstructionErrToInt *instruction) { IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } IrInstruction *result = ir_analyze_err_to_int(ira, &instruction->base, casted_target, ira->codegen->err_tag_type); ir_link_new_instruction(result, &instruction->base); return result->value.type; } static ZigType *ir_analyze_instruction_int_to_err(IrAnalyze *ira, IrInstructionIntToErr *instruction) { IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_target = ir_implicit_cast(ira, target, ira->codegen->err_tag_type); if (type_is_invalid(casted_target->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *result = ir_analyze_int_to_err(ira, &instruction->base, casted_target, ira->codegen->builtin_types.entry_global_error_set); ir_link_new_instruction(result, &instruction->base); return result->value.type; } static ZigType *ir_analyze_instruction_bool_to_int(IrAnalyze *ira, IrInstructionBoolToInt *instruction) { IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } if (instr_is_comptime(target)) { bool is_true; if (!ir_resolve_bool(ira, target, &is_true)) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); bigint_init_unsigned(&out_val->data.x_bigint, is_true ? 1 : 0); return ira->codegen->builtin_types.entry_num_lit_int; } ZigType *u1_type = get_int_type(ira->codegen, false, 1); IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, u1_type, CastOpBoolToInt, false); ir_link_new_instruction(result, &instruction->base); return u1_type; } static ZigType *ir_analyze_instruction_int_type(IrAnalyze *ira, IrInstructionIntType *instruction) { IrInstruction *is_signed_value = instruction->is_signed->other; bool is_signed; if (!ir_resolve_bool(ira, is_signed_value, &is_signed)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *bit_count_value = instruction->bit_count->other; uint64_t bit_count; if (!ir_resolve_unsigned(ira, bit_count_value, ira->codegen->builtin_types.entry_u32, &bit_count)) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = get_int_type(ira->codegen, is_signed, (uint32_t)bit_count); return ira->codegen->builtin_types.entry_type; } static ZigType *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstructionBoolNot *instruction) { IrInstruction *value = instruction->value->other; if (type_is_invalid(value->value.type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; if (instr_is_comptime(casted_value)) { ConstExprValue *value = ir_resolve_const(ira, casted_value, UndefBad); if (value == nullptr) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_bool = !value->data.x_bool; return bool_type; } ir_build_bool_not_from(&ira->new_irb, &instruction->base, casted_value); return bool_type; } static ZigType *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructionMemset *instruction) { Error err; IrInstruction *dest_ptr = instruction->dest_ptr->other; if (type_is_invalid(dest_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *byte_value = instruction->byte->other; if (type_is_invalid(byte_value->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *count_value = instruction->count->other; if (type_is_invalid(count_value->value.type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } 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); IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr); if (type_is_invalid(casted_dest_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_byte = ir_implicit_cast(ira, byte_value, u8); if (type_is_invalid(casted_byte->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_count = ir_implicit_cast(ira, count_value, usize); if (type_is_invalid(casted_count->value.type)) return ira->codegen->builtin_types.entry_invalid; if (casted_dest_ptr->value.special == ConstValSpecialStatic && casted_byte->value.special == ConstValSpecialStatic && casted_count->value.special == ConstValSpecialStatic && casted_dest_ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { ConstExprValue *dest_ptr_val = &casted_dest_ptr->value; ConstExprValue *dest_elements; size_t start; size_t bound_end; switch (dest_ptr_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: case ConstPtrSpecialDiscard: zig_unreachable(); case ConstPtrSpecialRef: dest_elements = dest_ptr_val->data.x_ptr.data.ref.pointee; start = 0; bound_end = 1; break; case ConstPtrSpecialBaseArray: { ConstExprValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val; expand_undef_array(ira->codegen, array_val); dest_elements = array_val->data.x_array.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 ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memset on ptr cast from function"); } size_t count = bigint_as_unsigned(&casted_count->value.data.x_bigint); size_t end = start + count; if (end > bound_end) { ir_add_error(ira, count_value, buf_sprintf("out of bounds pointer access")); return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *byte_val = &casted_byte->value; for (size_t i = start; i < end; i += 1) { dest_elements[i] = *byte_val; } ir_build_const_from(ira, &instruction->base); return ira->codegen->builtin_types.entry_void; } ir_build_memset_from(&ira->new_irb, &instruction->base, casted_dest_ptr, casted_byte, casted_count); return ira->codegen->builtin_types.entry_void; } static ZigType *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructionMemcpy *instruction) { Error err; IrInstruction *dest_ptr = instruction->dest_ptr->other; if (type_is_invalid(dest_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *src_ptr = instruction->src_ptr->other; if (type_is_invalid(src_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *count_value = instruction->count->other; if (type_is_invalid(count_value->value.type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; } 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); ZigType *u8_ptr_const = get_pointer_to_type_extra(ira->codegen, u8, true, src_is_volatile, PtrLenUnknown, src_align, 0, 0); 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->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; IrInstruction *casted_count = ir_implicit_cast(ira, count_value, usize); if (type_is_invalid(casted_count->value.type)) return ira->codegen->builtin_types.entry_invalid; if (casted_dest_ptr->value.special == ConstValSpecialStatic && casted_src_ptr->value.special == ConstValSpecialStatic && casted_count->value.special == ConstValSpecialStatic && casted_dest_ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { size_t count = bigint_as_unsigned(&casted_count->value.data.x_bigint); ConstExprValue *dest_ptr_val = &casted_dest_ptr->value; ConstExprValue *dest_elements; size_t dest_start; size_t dest_end; switch (dest_ptr_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: case ConstPtrSpecialDiscard: zig_unreachable(); case ConstPtrSpecialRef: dest_elements = dest_ptr_val->data.x_ptr.data.ref.pointee; dest_start = 0; dest_end = 1; break; case ConstPtrSpecialBaseArray: { ConstExprValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val; expand_undef_array(ira->codegen, array_val); dest_elements = array_val->data.x_array.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 ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memcpy on ptr cast from function"); } if (dest_start + count > dest_end) { ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds pointer access")); return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *src_ptr_val = &casted_src_ptr->value; ConstExprValue *src_elements; size_t src_start; size_t src_end; switch (src_ptr_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: case ConstPtrSpecialDiscard: zig_unreachable(); case ConstPtrSpecialRef: src_elements = src_ptr_val->data.x_ptr.data.ref.pointee; src_start = 0; src_end = 1; break; case ConstPtrSpecialBaseArray: { ConstExprValue *array_val = src_ptr_val->data.x_ptr.data.base_array.array_val; expand_undef_array(ira->codegen, array_val); src_elements = array_val->data.x_array.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 ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memcpy on ptr cast from function"); } if (src_start + count > src_end) { ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds pointer access")); return ira->codegen->builtin_types.entry_invalid; } // TODO check for noalias violations - this should be generalized to work for any function for (size_t i = 0; i < count; i += 1) { dest_elements[dest_start + i] = src_elements[src_start + i]; } ir_build_const_from(ira, &instruction->base); return ira->codegen->builtin_types.entry_void; } ir_build_memcpy_from(&ira->new_irb, &instruction->base, casted_dest_ptr, casted_src_ptr, casted_count); return ira->codegen->builtin_types.entry_void; } static ZigType *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructionSlice *instruction) { IrInstruction *ptr_ptr = instruction->ptr->other; if (type_is_invalid(ptr_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; ZigType *ptr_type = ptr_ptr->value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *array_type = ptr_type->data.pointer.child_type; IrInstruction *start = instruction->start->other; if (type_is_invalid(start->value.type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; IrInstruction *end; if (instruction->end) { end = instruction->end->other; if (type_is_invalid(end->value.type)) return ira->codegen->builtin_types.entry_invalid; end = ir_implicit_cast(ira, end, usize); if (type_is_invalid(end->value.type)) return ira->codegen->builtin_types.entry_invalid; } else { end = nullptr; } ZigType *return_type; if (array_type->id == ZigTypeIdArray) { bool is_comptime_const = ptr_ptr->value.special == ConstValSpecialStatic && ptr_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst; ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type, ptr_type->data.pointer.is_const || is_comptime_const, ptr_type->data.pointer.is_volatile, PtrLenUnknown, ptr_type->data.pointer.explicit_alignment, 0, 0); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else if (array_type->id == ZigTypeIdPointer) { if (array_type->data.pointer.ptr_len == PtrLenSingle) { ZigType *main_type = array_type->data.pointer.child_type; if (main_type->id == ZigTypeIdArray) { ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, main_type->data.pointer.child_type, array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, PtrLenUnknown, array_type->data.pointer.explicit_alignment, 0, 0); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else { ir_add_error(ira, &instruction->base, buf_sprintf("slice of single-item pointer")); return ira->codegen->builtin_types.entry_invalid; } } else { ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.pointer.child_type, array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, PtrLenUnknown, array_type->data.pointer.explicit_alignment, 0, 0); return_type = get_slice_type(ira->codegen, slice_ptr_type); if (!end) { ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value")); return ira->codegen->builtin_types.entry_invalid; } } } else if (is_slice(array_type)) { ZigType *ptr_type = array_type->data.structure.fields[slice_ptr_index].type_entry; return_type = get_slice_type(ira->codegen, ptr_type); } else { ir_add_error(ira, &instruction->base, buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name))); return ira->codegen->builtin_types.entry_invalid; } if (instr_is_comptime(ptr_ptr) && value_is_comptime(&casted_start->value) && (!end || value_is_comptime(&end->value))) { ConstExprValue *array_val; ConstExprValue *parent_ptr; size_t abs_offset; size_t rel_end; bool ptr_is_undef = false; if (array_type->id == ZigTypeIdArray || (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) { if (array_type->id == ZigTypeIdPointer) { ZigType *child_array_type = array_type->data.pointer.child_type; assert(child_array_type->id == ZigTypeIdArray); parent_ptr = ir_const_ptr_pointee(ira, &ptr_ptr->value, instruction->base.source_node); if (parent_ptr == nullptr) return ira->codegen->builtin_types.entry_invalid; array_val = ir_const_ptr_pointee(ira, parent_ptr, instruction->base.source_node); if (array_val == nullptr) return ira->codegen->builtin_types.entry_invalid; rel_end = child_array_type->data.array.len; abs_offset = 0; } else { array_val = ir_const_ptr_pointee(ira, &ptr_ptr->value, instruction->base.source_node); if (array_val == nullptr) return ira->codegen->builtin_types.entry_invalid; 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 = ir_const_ptr_pointee(ira, &ptr_ptr->value, instruction->base.source_node); if (parent_ptr == nullptr) return ira->codegen->builtin_types.entry_invalid; 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: 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 ConstPtrSpecialHardCodedAddr: array_val = nullptr; abs_offset = 0; rel_end = SIZE_MAX; break; case ConstPtrSpecialFunction: zig_panic("TODO slice of ptr cast from function"); } } else if (is_slice(array_type)) { ConstExprValue *slice_ptr = ir_const_ptr_pointee(ira, &ptr_ptr->value, instruction->base.source_node); if (slice_ptr == nullptr) return ira->codegen->builtin_types.entry_invalid; parent_ptr = &slice_ptr->data.x_struct.fields[slice_ptr_index]; ConstExprValue *len_val = &slice_ptr->data.x_struct.fields[slice_len_index]; switch (parent_ptr->data.x_ptr.special) { case ConstPtrSpecialInvalid: case ConstPtrSpecialDiscard: zig_unreachable(); case ConstPtrSpecialRef: array_val = nullptr; abs_offset = SIZE_MAX; rel_end = 1; break; case ConstPtrSpecialBaseArray: array_val = parent_ptr->data.x_ptr.data.base_array.array_val; abs_offset = parent_ptr->data.x_ptr.data.base_array.elem_index; rel_end = bigint_as_unsigned(&len_val->data.x_bigint); break; case ConstPtrSpecialBaseStruct: zig_panic("TODO slice const inner struct"); case ConstPtrSpecialHardCodedAddr: array_val = nullptr; abs_offset = 0; rel_end = bigint_as_unsigned(&len_val->data.x_bigint); break; case ConstPtrSpecialFunction: zig_panic("TODO slice of slice cast from function"); } } else { zig_unreachable(); } uint64_t start_scalar = bigint_as_unsigned(&casted_start->value.data.x_bigint); if (!ptr_is_undef && start_scalar > rel_end) { ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice")); return ira->codegen->builtin_types.entry_invalid; } uint64_t end_scalar; if (end) { end_scalar = bigint_as_unsigned(&end->value.data.x_bigint); } else { end_scalar = rel_end; } if (!ptr_is_undef) { if (end_scalar > rel_end) { ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice")); return ira->codegen->builtin_types.entry_invalid; } if (start_scalar > end_scalar) { ir_add_error(ira, &instruction->base, buf_sprintf("slice start is greater than end")); return ira->codegen->builtin_types.entry_invalid; } } 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->builtin_types.entry_invalid; } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_struct.fields = create_const_vals(2); ConstExprValue *ptr_val = &out_val->data.x_struct.fields[slice_ptr_index]; if (array_val) { size_t index = abs_offset + start_scalar; bool is_const = slice_is_const(return_type); init_const_ptr_array(ira->codegen, ptr_val, array_val, index, is_const, PtrLenUnknown); if (array_type->id == ZigTypeIdArray) { ptr_val->data.x_ptr.mut = ptr_ptr->value.data.x_ptr.mut; } else if (is_slice(array_type)) { ptr_val->data.x_ptr.mut = parent_ptr->data.x_ptr.mut; } else if (array_type->id == ZigTypeIdPointer) { ptr_val->data.x_ptr.mut = parent_ptr->data.x_ptr.mut; } } else if (ptr_is_undef) { ptr_val->type = get_pointer_to_type(ira->codegen, parent_ptr->type->data.pointer.child_type, slice_is_const(return_type)); ptr_val->special = ConstValSpecialUndef; } else switch (parent_ptr->data.x_ptr.special) { case ConstPtrSpecialInvalid: case ConstPtrSpecialDiscard: zig_unreachable(); case ConstPtrSpecialRef: init_const_ptr_ref(ira->codegen, ptr_val, parent_ptr->data.x_ptr.data.ref.pointee, slice_is_const(return_type)); break; case ConstPtrSpecialBaseArray: zig_unreachable(); case ConstPtrSpecialBaseStruct: zig_panic("TODO"); case 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"); } ConstExprValue *len_val = &out_val->data.x_struct.fields[slice_len_index]; init_const_usize(ira->codegen, len_val, end_scalar - start_scalar); return return_type; } IrInstruction *new_instruction = ir_build_slice_from(&ira->new_irb, &instruction->base, ptr_ptr, casted_start, end, instruction->safety_check_on); ir_add_alloca(ira, new_instruction, return_type); return return_type; } static ZigType *ir_analyze_instruction_member_count(IrAnalyze *ira, IrInstructionMemberCount *instruction) { Error err; IrInstruction *container = instruction->container->other; if (type_is_invalid(container->value.type)) return ira->codegen->builtin_types.entry_invalid; ZigType *container_type = ir_resolve_type(ira, container); if ((err = ensure_complete_type(ira->codegen, container_type))) return ira->codegen->builtin_types.entry_invalid; uint64_t result; if (type_is_invalid(container_type)) { return ira->codegen->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); bigint_init_unsigned(&out_val->data.x_bigint, result); return ira->codegen->builtin_types.entry_num_lit_int; } static ZigType *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInstructionMemberType *instruction) { Error err; IrInstruction *container_type_value = instruction->container_type->other; ZigType *container_type = ir_resolve_type(ira, container_type_value); if (type_is_invalid(container_type)) return ira->codegen->builtin_types.entry_invalid; if ((err = ensure_complete_type(ira->codegen, container_type))) return ira->codegen->builtin_types.entry_invalid; uint64_t member_index; IrInstruction *index_value = instruction->member_index->other; if (!ir_resolve_usize(ira, index_value, &member_index)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } TypeStructField *field = &container_type->data.structure.fields[member_index]; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = field->type_entry; return ira->codegen->builtin_types.entry_type; } 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->builtin_types.entry_invalid; } TypeUnionField *field = &container_type->data.unionation.fields[member_index]; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = field->type_entry; return ira->codegen->builtin_types.entry_type; } else { ir_add_error(ira, container_type_value, buf_sprintf("type '%s' does not support @memberType", buf_ptr(&container_type->name))); return ira->codegen->builtin_types.entry_invalid; } } static ZigType *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInstructionMemberName *instruction) { Error err; IrInstruction *container_type_value = instruction->container_type->other; ZigType *container_type = ir_resolve_type(ira, container_type_value); if (type_is_invalid(container_type)) return ira->codegen->builtin_types.entry_invalid; if ((err = ensure_complete_type(ira->codegen, container_type))) return ira->codegen->builtin_types.entry_invalid; uint64_t member_index; IrInstruction *index_value = instruction->member_index->other; if (!ir_resolve_usize(ira, index_value, &member_index)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } TypeStructField *field = &container_type->data.structure.fields[member_index]; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); init_const_str_lit(ira->codegen, out_val, field->name); return out_val->type; } 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->builtin_types.entry_invalid; } TypeEnumField *field = &container_type->data.enumeration.fields[member_index]; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); init_const_str_lit(ira->codegen, out_val, field->name); return out_val->type; } 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->builtin_types.entry_invalid; } TypeUnionField *field = &container_type->data.unionation.fields[member_index]; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); init_const_str_lit(ira->codegen, out_val, field->name); return out_val->type; } else { ir_add_error(ira, container_type_value, buf_sprintf("type '%s' does not support @memberName", buf_ptr(&container_type->name))); return ira->codegen->builtin_types.entry_invalid; } } static ZigType *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstructionBreakpoint *instruction) { ir_build_breakpoint_from(&ira->new_irb, &instruction->base); return ira->codegen->builtin_types.entry_void; } static ZigType *ir_analyze_instruction_return_address(IrAnalyze *ira, IrInstructionReturnAddress *instruction) { ir_build_return_address_from(&ira->new_irb, &instruction->base); ZigType *u8 = ira->codegen->builtin_types.entry_u8; ZigType *u8_ptr_const = get_pointer_to_type(ira->codegen, u8, true); return u8_ptr_const; } static ZigType *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrInstructionFrameAddress *instruction) { ir_build_frame_address_from(&ira->new_irb, &instruction->base); ZigType *u8 = ira->codegen->builtin_types.entry_u8; ZigType *u8_ptr_const = get_pointer_to_type(ira->codegen, u8, true); return u8_ptr_const; } static ZigType *ir_analyze_instruction_handle(IrAnalyze *ira, IrInstructionHandle *instruction) { ir_build_handle_from(&ira->new_irb, &instruction->base); ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); assert(fn_entry != nullptr); return get_promise_type(ira->codegen, fn_entry->type_entry->data.fn.fn_type_id.return_type); } static ZigType *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) { Error err; IrInstruction *type_value = instruction->type_value->other; if (type_is_invalid(type_value->value.type)) return ira->codegen->builtin_types.entry_invalid; ZigType *type_entry = ir_resolve_type(ira, type_value); if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusAlignmentKnown))) return ira->codegen->builtin_types.entry_invalid; switch (type_entry->id) { case ZigTypeIdInvalid: zig_unreachable(); case ZigTypeIdMetaType: case ZigTypeIdUnreachable: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdNamespace: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdVoid: case ZigTypeIdOpaque: ir_add_error(ira, instruction->type_value, buf_sprintf("no align available for type '%s'", buf_ptr(&type_entry->name))); return ira->codegen->builtin_types.entry_invalid; case ZigTypeIdBool: case ZigTypeIdInt: case ZigTypeIdFloat: case ZigTypeIdPointer: case ZigTypeIdPromise: case ZigTypeIdArray: case ZigTypeIdStruct: case ZigTypeIdOptional: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdEnum: case ZigTypeIdUnion: case ZigTypeIdFn: { uint64_t align_in_bytes = get_abi_alignment(ira->codegen, type_entry); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); bigint_init_unsigned(&out_val->data.x_bigint, align_in_bytes); return ira->codegen->builtin_types.entry_num_lit_int; } } zig_unreachable(); } static ZigType *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstructionOverflowOp *instruction) { Error err; IrInstruction *type_value = instruction->type_value->other; if (type_is_invalid(type_value->value.type)) return ira->codegen->builtin_types.entry_invalid; ZigType *dest_type = ir_resolve_type(ira, type_value); if (type_is_invalid(dest_type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } IrInstruction *op1 = instruction->op1->other; if (type_is_invalid(op1->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, dest_type); if (type_is_invalid(casted_op1->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *op2 = instruction->op2->other; if (type_is_invalid(op2->value.type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; IrInstruction *result_ptr = instruction->result_ptr->other; if (type_is_invalid(result_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; 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); } 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->builtin_types.entry_invalid; if (casted_op1->value.special == ConstValSpecialStatic && casted_op2->value.special == ConstValSpecialStatic && casted_result_ptr->value.special == ConstValSpecialStatic) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); BigInt *op1_bigint = &casted_op1->value.data.x_bigint; BigInt *op2_bigint = &casted_op2->value.data.x_bigint; ConstExprValue *pointee_val = ir_const_ptr_pointee(ira, &casted_result_ptr->value, casted_result_ptr->source_node); if (pointee_val == nullptr) return ira->codegen->builtin_types.entry_invalid; 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; } if (!bigint_fits_in_bits(dest_bigint, dest_type->data.integral.bit_count, dest_type->data.integral.is_signed)) { out_val->data.x_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 ira->codegen->builtin_types.entry_bool; } ir_build_overflow_op_from(&ira->new_irb, &instruction->base, instruction->op, type_value, casted_op1, casted_op2, casted_result_ptr, dest_type); return ira->codegen->builtin_types.entry_bool; } static ZigType *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstructionTestErr *instruction) { IrInstruction *value = instruction->value->other; if (type_is_invalid(value->value.type)) return ira->codegen->builtin_types.entry_invalid; ZigType *type_entry = value->value.type; if (type_is_invalid(type_entry)) { return ira->codegen->builtin_types.entry_invalid; } else if (type_entry->id == ZigTypeIdErrorUnion) { if (instr_is_comptime(value)) { ConstExprValue *err_union_val = ir_resolve_const(ira, value, UndefBad); if (!err_union_val) return ira->codegen->builtin_types.entry_invalid; if (err_union_val->special != ConstValSpecialRuntime) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_bool = (err_union_val->data.x_err_union.err != nullptr); return ira->codegen->builtin_types.entry_bool; } } 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->builtin_types.entry_invalid; } if (!type_is_global_error_set(err_set_type) && err_set_type->data.error_set.err_count == 0) { assert(err_set_type->data.error_set.infer_fn == nullptr); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_bool = false; return ira->codegen->builtin_types.entry_bool; } ir_build_test_err_from(&ira->new_irb, &instruction->base, value); return ira->codegen->builtin_types.entry_bool; } else if (type_entry->id == ZigTypeIdErrorSet) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_bool = true; return ira->codegen->builtin_types.entry_bool; } else { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_bool = false; return ira->codegen->builtin_types.entry_bool; } } static ZigType *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, IrInstructionUnwrapErrCode *instruction) { IrInstruction *value = instruction->value->other; if (type_is_invalid(value->value.type)) return ira->codegen->builtin_types.entry_invalid; ZigType *ptr_type = value->value.type; // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing. assert(ptr_type->id == ZigTypeIdPointer); ZigType *type_entry = ptr_type->data.pointer.child_type; if (type_is_invalid(type_entry)) { return ira->codegen->builtin_types.entry_invalid; } else if (type_entry->id == ZigTypeIdErrorUnion) { if (instr_is_comptime(value)) { ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); if (!ptr_val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *err_union_val = ir_const_ptr_pointee(ira, ptr_val, instruction->base.source_node); if (err_union_val == nullptr) return ira->codegen->builtin_types.entry_invalid; if (err_union_val->special != ConstValSpecialRuntime) { ErrorTableEntry *err = err_union_val->data.x_err_union.err; assert(err); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_err_set = err; return type_entry->data.error_union.err_set_type; } } ir_build_unwrap_err_code_from(&ira->new_irb, &instruction->base, value); return type_entry->data.error_union.err_set_type; } else { ir_add_error(ira, value, buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } } static ZigType *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, IrInstructionUnwrapErrPayload *instruction) { assert(instruction->value->other); IrInstruction *value = instruction->value->other; if (type_is_invalid(value->value.type)) return ira->codegen->builtin_types.entry_invalid; ZigType *ptr_type = value->value.type; // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing. assert(ptr_type->id == ZigTypeIdPointer); ZigType *type_entry = ptr_type->data.pointer.child_type; if (type_is_invalid(type_entry)) { return ira->codegen->builtin_types.entry_invalid; } else if (type_entry->id == ZigTypeIdErrorUnion) { ZigType *payload_type = type_entry->data.error_union.payload_type; if (type_is_invalid(payload_type)) { return ira->codegen->builtin_types.entry_invalid; } 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); if (instr_is_comptime(value)) { ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); if (!ptr_val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *err_union_val = ir_const_ptr_pointee(ira, ptr_val, instruction->base.source_node); if (err_union_val == nullptr) return ira->codegen->builtin_types.entry_invalid; if (err_union_val->special != ConstValSpecialRuntime) { ErrorTableEntry *err = err_union_val->data.x_err_union.err; if (err != nullptr) { ir_add_error(ira, &instruction->base, buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.data.ref.pointee = err_union_val->data.x_err_union.payload; return result_type; } } ir_build_unwrap_err_payload_from(&ira->new_irb, &instruction->base, value, instruction->safety_check_on); return result_type; } else { ir_add_error(ira, value, buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } } static ZigType *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnProto *instruction) { Error err; AstNode *proto_node = instruction->base.source_node; assert(proto_node->type == NodeTypeFnProto); if (proto_node->data.fn_proto.auto_err_set) { ir_add_error(ira, &instruction->base, buf_sprintf("inferring error set of return type valid only for function definitions")); return ira->codegen->builtin_types.entry_invalid; } FnTypeId fn_type_id = {0}; init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length); for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) { AstNode *param_node = proto_node->data.fn_proto.params.at(fn_type_id.next_param_index); assert(param_node->type == NodeTypeParamDecl); bool param_is_var_args = param_node->data.param_decl.is_var_args; if (param_is_var_args) { if (fn_type_id.cc == CallingConventionC) { fn_type_id.param_count = fn_type_id.next_param_index; continue; } else if (fn_type_id.cc == CallingConventionUnspecified) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = get_generic_fn_type(ira->codegen, &fn_type_id); return ira->codegen->builtin_types.entry_type; } else { zig_unreachable(); } } FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index]; param_info->is_noalias = param_node->data.param_decl.is_noalias; if (instruction->param_types[fn_type_id.next_param_index] == nullptr) { param_info->type = nullptr; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = get_generic_fn_type(ira->codegen, &fn_type_id); return ira->codegen->builtin_types.entry_type; } else { IrInstruction *param_type_value = instruction->param_types[fn_type_id.next_param_index]->other; if (type_is_invalid(param_type_value->value.type)) return ira->codegen->builtin_types.entry_invalid; ZigType *param_type = ir_resolve_type(ira, param_type_value); if (type_is_invalid(param_type)) return ira->codegen->builtin_types.entry_invalid; if ((err = type_resolve(ira->codegen, param_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; if (type_requires_comptime(param_type)) { if (!calling_convention_allows_zig_types(fn_type_id.cc)) { ir_add_error(ira, param_type_value, buf_sprintf("parameter of type '%s' not allowed in function with calling convention '%s'", buf_ptr(¶m_type->name), calling_convention_name(fn_type_id.cc))); return ira->codegen->builtin_types.entry_invalid; } param_info->type = param_type; fn_type_id.next_param_index += 1; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = get_generic_fn_type(ira->codegen, &fn_type_id); return ira->codegen->builtin_types.entry_type; } if (!type_has_bits(param_type) && !calling_convention_allows_zig_types(fn_type_id.cc)) { ir_add_error(ira, param_type_value, buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'", buf_ptr(¶m_type->name), calling_convention_name(fn_type_id.cc))); return ira->codegen->builtin_types.entry_invalid; } param_info->type = param_type; } } if (instruction->align_value != nullptr) { if (!ir_resolve_align(ira, instruction->align_value->other, &fn_type_id.alignment)) return ira->codegen->builtin_types.entry_invalid; } IrInstruction *return_type_value = instruction->return_type->other; fn_type_id.return_type = ir_resolve_type(ira, return_type_value); if (type_is_invalid(fn_type_id.return_type)) return ira->codegen->builtin_types.entry_invalid; if (fn_type_id.return_type->id == ZigTypeIdOpaque) { ir_add_error(ira, instruction->return_type, buf_sprintf("return type cannot be opaque")); return ira->codegen->builtin_types.entry_invalid; } if (fn_type_id.cc == CallingConventionAsync) { if (instruction->async_allocator_type_value == nullptr) { ir_add_error(ira, &instruction->base, buf_sprintf("async fn proto missing allocator type")); return ira->codegen->builtin_types.entry_invalid; } IrInstruction *async_allocator_type_value = instruction->async_allocator_type_value->other; fn_type_id.async_allocator_type = ir_resolve_type(ira, async_allocator_type_value); if (type_is_invalid(fn_type_id.async_allocator_type)) return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = get_fn_type(ira->codegen, &fn_type_id); return ira->codegen->builtin_types.entry_type; } static ZigType *ir_analyze_instruction_test_comptime(IrAnalyze *ira, IrInstructionTestComptime *instruction) { IrInstruction *value = instruction->value->other; if (type_is_invalid(value->value.type)) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_bool = instr_is_comptime(value); return ira->codegen->builtin_types.entry_bool; } static ZigType *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, IrInstructionCheckSwitchProngs *instruction) { IrInstruction *target_value = instruction->target_value->other; ZigType *switch_type = target_value->value.type; if (type_is_invalid(switch_type)) return ira->codegen->builtin_types.entry_invalid; if (switch_type->id == ZigTypeIdEnum) { HashMap 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 = range->start->other; if (type_is_invalid(start_value->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *end_value = range->end->other; if (type_is_invalid(end_value->value.type)) return ira->codegen->builtin_types.entry_invalid; if (start_value->value.type->id != ZigTypeIdEnum) { ir_add_error(ira, range->start, buf_sprintf("not an enum type")); return ira->codegen->builtin_types.entry_invalid; } BigInt start_index; bigint_init_bigint(&start_index, &start_value->value.data.x_enum_tag); assert(end_value->value.type->id == ZigTypeIdEnum); BigInt end_index; bigint_init_bigint(&end_index, &end_value->value.data.x_enum_tag); BigInt field_index; bigint_init_bigint(&field_index, &start_index); for (;;) { Cmp cmp = bigint_cmp(&field_index, &end_index); if (cmp == CmpGT) { break; } auto entry = field_prev_uses.put_unique(field_index, start_value->source_node); if (entry) { AstNode *prev_node = entry->value; TypeEnumField *enum_field = find_enum_field_by_tag(switch_type, &field_index); assert(enum_field != nullptr); ErrorMsg *msg = ir_add_error(ira, start_value, buf_sprintf("duplicate switch value: '%s.%s'", buf_ptr(&switch_type->name), buf_ptr(enum_field->name))); add_error_note(ira->codegen, msg, prev_node, buf_sprintf("other value is here")); } bigint_incr(&field_index); } } if (!instruction->have_else_prong) { for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) { TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i]; auto entry = field_prev_uses.maybe_get(enum_field->value); if (!entry) { ir_add_error(ira, &instruction->base, buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&switch_type->name), buf_ptr(enum_field->name))); } } } } else if (switch_type->id == ZigTypeIdErrorSet) { if (!resolve_inferred_error_set(ira->codegen, switch_type, target_value->source_node)) { return ira->codegen->builtin_types.entry_invalid; } AstNode **field_prev_uses = allocate(ira->codegen->errors_by_index.length); for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i]; IrInstruction *start_value = range->start->other; if (type_is_invalid(start_value->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *end_value = range->end->other; if (type_is_invalid(end_value->value.type)) return ira->codegen->builtin_types.entry_invalid; assert(start_value->value.type->id == ZigTypeIdErrorSet); uint32_t start_index = start_value->value.data.x_err_set->value; assert(end_value->value.type->id == ZigTypeIdErrorSet); uint32_t end_index = end_value->value.data.x_err_set->value; if (start_index != end_index) { ir_add_error(ira, end_value, buf_sprintf("ranges not allowed when switching on errors")); return ira->codegen->builtin_types.entry_invalid; } 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 'error'")); return ira->codegen->builtin_types.entry_invalid; } else { for (uint32_t i = 0; i < switch_type->data.error_set.err_count; i += 1) { ErrorTableEntry *err_entry = switch_type->data.error_set.errors[i]; AstNode *prev_node = field_prev_uses[err_entry->value]; if (prev_node == nullptr) { ir_add_error(ira, &instruction->base, buf_sprintf("error.%s not handled in switch", buf_ptr(&err_entry->name))); } } } } free(field_prev_uses); } else if (switch_type->id == ZigTypeIdInt) { RangeSet rs = {0}; for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i]; IrInstruction *start_value = range->start->other; if (type_is_invalid(start_value->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_start_value = ir_implicit_cast(ira, start_value, switch_type); if (type_is_invalid(casted_start_value->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *end_value = range->end->other; if (type_is_invalid(end_value->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_end_value = ir_implicit_cast(ira, end_value, switch_type); if (type_is_invalid(casted_end_value->value.type)) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *start_val = ir_resolve_const(ira, casted_start_value, UndefBad); if (!start_val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *end_val = ir_resolve_const(ira, casted_end_value, UndefBad); if (!end_val) return ira->codegen->builtin_types.entry_invalid; assert(start_val->type->id == ZigTypeIdInt || start_val->type->id == ZigTypeIdComptimeInt); assert(end_val->type->id == ZigTypeIdInt || end_val->type->id == ZigTypeIdComptimeInt); AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bigint, &end_val->data.x_bigint, start_value->source_node); if (prev_node != nullptr) { ErrorMsg *msg = ir_add_error(ira, start_value, buf_sprintf("duplicate switch value")); add_error_note(ira->codegen, msg, prev_node, buf_sprintf("previous value is here")); return ira->codegen->builtin_types.entry_invalid; } } 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->builtin_types.entry_invalid; } } } 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->builtin_types.entry_invalid; } ir_build_const_from(ira, &instruction->base); return ira->codegen->builtin_types.entry_void; } static ZigType *ir_analyze_instruction_check_statement_is_void(IrAnalyze *ira, IrInstructionCheckStatementIsVoid *instruction) { IrInstruction *statement_value = instruction->statement_value->other; ZigType *statement_type = statement_value->value.type; if (type_is_invalid(statement_type)) return ira->codegen->builtin_types.entry_invalid; if (statement_type->id != ZigTypeIdVoid) { ir_add_error(ira, &instruction->base, buf_sprintf("expression value is ignored")); } ir_build_const_from(ira, &instruction->base); return ira->codegen->builtin_types.entry_void; } static ZigType *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstructionPanic *instruction) { IrInstruction *msg = instruction->msg->other; 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); 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); ir_link_new_instruction(new_instruction, &instruction->base); return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); } static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint32_t align_bytes, bool safety_check_on) { Error err; ZigType *target_type = target->value.type; assert(!type_is_invalid(target_type)); ZigType *result_type; uint32_t old_align_bytes; if (target_type->id == ZigTypeIdPointer) { result_type = adjust_ptr_align(ira->codegen, target_type, align_bytes); if ((err = resolve_ptr_align(ira, target_type, &old_align_bytes))) return ira->codegen->invalid_instruction; } else if (target_type->id == ZigTypeIdFn) { FnTypeId fn_type_id = target_type->data.fn.fn_type_id; old_align_bytes = fn_type_id.alignment; fn_type_id.alignment = align_bytes; result_type = get_fn_type(ira->codegen, &fn_type_id); } else if (target_type->id == ZigTypeIdOptional && target_type->data.maybe.child_type->id == ZigTypeIdPointer) { ZigType *ptr_type = target_type->data.maybe.child_type; if ((err = resolve_ptr_align(ira, ptr_type, &old_align_bytes))) return ira->codegen->invalid_instruction; ZigType *better_ptr_type = adjust_ptr_align(ira->codegen, ptr_type, align_bytes); result_type = get_optional_type(ira->codegen, better_ptr_type); } else if (target_type->id == ZigTypeIdOptional && target_type->data.maybe.child_type->id == ZigTypeIdFn) { FnTypeId fn_type_id = target_type->data.maybe.child_type->data.fn.fn_type_id; old_align_bytes = fn_type_id.alignment; fn_type_id.alignment = align_bytes; ZigType *fn_type = get_fn_type(ira->codegen, &fn_type_id); result_type = get_optional_type(ira->codegen, fn_type); } else if (is_slice(target_type)) { ZigType *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index].type_entry; if ((err = resolve_ptr_align(ira, slice_ptr_type, &old_align_bytes))) return ira->codegen->invalid_instruction; ZigType *result_ptr_type = adjust_ptr_align(ira->codegen, slice_ptr_type, align_bytes); result_type = get_slice_type(ira->codegen, result_ptr_type); } else { ir_add_error(ira, target, buf_sprintf("expected pointer or slice, found '%s'", buf_ptr(&target_type->name))); return ira->codegen->invalid_instruction; } if (instr_is_comptime(target)) { ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && val->data.x_ptr.data.hard_coded_addr.addr % align_bytes != 0) { ir_add_error(ira, target, buf_sprintf("pointer address 0x%" ZIG_PRI_x64 " is not aligned to %" PRIu32 " bytes", val->data.x_ptr.data.hard_coded_addr.addr, align_bytes)); return ira->codegen->invalid_instruction; } IrInstruction *result = ir_create_const(&ira->new_irb, target->scope, target->source_node, result_type); copy_const_val(&result->value, val, false); 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 ZigType *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCast *instruction) { Error err; IrInstruction *dest_type_value = instruction->dest_type->other; ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *ptr = instruction->ptr->other; ZigType *src_type = ptr->value.type; if (type_is_invalid(src_type)) return ira->codegen->builtin_types.entry_invalid; if (get_codegen_ptr_type(src_type) == nullptr) { ir_add_error(ira, ptr, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name))); return ira->codegen->builtin_types.entry_invalid; } if (get_codegen_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->builtin_types.entry_invalid; } if (get_ptr_const(src_type) && !get_ptr_const(dest_type)) { ir_add_error(ira, &instruction->base, buf_sprintf("cast discards const qualifier")); return ira->codegen->builtin_types.entry_invalid; } if (instr_is_comptime(ptr)) { ConstExprValue *val = ir_resolve_const(ira, ptr, UndefOk); if (!val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); copy_const_val(out_val, val, false); out_val->type = dest_type; return dest_type; } uint32_t src_align_bytes; if ((err = resolve_ptr_align(ira, src_type, &src_align_bytes))) return ira->codegen->builtin_types.entry_invalid; uint32_t dest_align_bytes; if ((err = resolve_ptr_align(ira, dest_type, &dest_align_bytes))) return ira->codegen->builtin_types.entry_invalid; if (dest_align_bytes > src_align_bytes) { ErrorMsg *msg = ir_add_error(ira, &instruction->base, 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_value->source_node, buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&dest_type->name), dest_align_bytes)); return ira->codegen->builtin_types.entry_invalid; } IrInstruction *casted_ptr = ir_build_ptr_cast(&ira->new_irb, instruction->base.scope, instruction->base.source_node, nullptr, ptr); casted_ptr->value.type = dest_type; if (type_has_bits(dest_type) && !type_has_bits(src_type)) { ErrorMsg *msg = ir_add_error(ira, &instruction->base, 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_value->source_node, buf_sprintf("'%s' has in-memory bits", buf_ptr(&dest_type->name))); return ira->codegen->builtin_types.entry_invalid; } // 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->builtin_types.entry_invalid; } else { result = casted_ptr; } ir_link_new_instruction(result, &instruction->base); return result->value.type; } static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val) { assert(val->special == ConstValSpecialStatic); switch (val->type->id) { case ZigTypeIdInvalid: case ZigTypeIdMetaType: case ZigTypeIdOpaque: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdNamespace: case ZigTypeIdUnreachable: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdPromise: zig_unreachable(); case ZigTypeIdVoid: return; case ZigTypeIdBool: buf[0] = val->data.x_bool ? 1 : 0; return; case ZigTypeIdInt: bigint_write_twos_complement(&val->data.x_bigint, buf, val->type->data.integral.bit_count, codegen->is_big_endian); return; case 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: { size_t buf_i = 0; expand_undef_array(codegen, val); for (size_t elem_i = 0; elem_i < val->type->data.array.len; elem_i += 1) { ConstExprValue *elem = &val->data.x_array.s_none.elements[elem_i]; buf_write_value_bytes(codegen, &buf[buf_i], elem); buf_i += type_size(codegen, elem->type); } } return; case ZigTypeIdStruct: zig_panic("TODO buf_write_value_bytes struct type"); case ZigTypeIdOptional: zig_panic("TODO buf_write_value_bytes maybe type"); case ZigTypeIdErrorUnion: zig_panic("TODO buf_write_value_bytes error union"); case ZigTypeIdErrorSet: zig_panic("TODO buf_write_value_bytes pure error type"); case ZigTypeIdEnum: zig_panic("TODO buf_write_value_bytes enum type"); case ZigTypeIdFn: zig_panic("TODO buf_write_value_bytes fn type"); case ZigTypeIdUnion: zig_panic("TODO buf_write_value_bytes union type"); } zig_unreachable(); } static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val) { assert(val->special == ConstValSpecialStatic); switch (val->type->id) { case ZigTypeIdInvalid: case ZigTypeIdMetaType: case ZigTypeIdOpaque: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdNamespace: case ZigTypeIdUnreachable: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdPromise: zig_unreachable(); case ZigTypeIdVoid: return; case ZigTypeIdBool: val->data.x_bool = (buf[0] != 0); return; 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; case ZigTypeIdFloat: float_read_ieee597(val, buf, codegen->is_big_endian); return; case ZigTypeIdPointer: { val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; BigInt bn; bigint_read_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count, codegen->is_big_endian, false); val->data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&bn); return; } case ZigTypeIdArray: zig_panic("TODO buf_read_value_bytes array type"); case ZigTypeIdStruct: zig_panic("TODO buf_read_value_bytes struct type"); 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 ZigTypeIdEnum: zig_panic("TODO buf_read_value_bytes enum type"); case ZigTypeIdFn: zig_panic("TODO buf_read_value_bytes fn type"); case ZigTypeIdUnion: zig_panic("TODO buf_read_value_bytes union type"); } zig_unreachable(); } static ZigType *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBitCast *instruction) { Error err; IrInstruction *dest_type_value = instruction->dest_type->other; ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *value = instruction->value->other; ZigType *src_type = value->value.type; if (type_is_invalid(src_type)) return ira->codegen->builtin_types.entry_invalid; if ((err = ensure_complete_type(ira->codegen, dest_type))) return ira->codegen->builtin_types.entry_invalid; if ((err = ensure_complete_type(ira->codegen, src_type))) return ira->codegen->builtin_types.entry_invalid; if (get_codegen_ptr_type(src_type) != nullptr) { ir_add_error(ira, value, buf_sprintf("unable to @bitCast from pointer type '%s'", buf_ptr(&src_type->name))); return ira->codegen->builtin_types.entry_invalid; } switch (src_type->id) { case ZigTypeIdInvalid: case ZigTypeIdMetaType: case ZigTypeIdOpaque: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdNamespace: case ZigTypeIdUnreachable: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdUndefined: case ZigTypeIdNull: ir_add_error(ira, dest_type_value, buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&src_type->name))); return ira->codegen->builtin_types.entry_invalid; default: break; } if (get_codegen_ptr_type(dest_type) != nullptr) { ir_add_error(ira, dest_type_value, buf_sprintf("unable to @bitCast to pointer type '%s'", buf_ptr(&dest_type->name))); return ira->codegen->builtin_types.entry_invalid; } switch (dest_type->id) { case ZigTypeIdInvalid: case ZigTypeIdMetaType: case ZigTypeIdOpaque: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdNamespace: case ZigTypeIdUnreachable: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdUndefined: case ZigTypeIdNull: ir_add_error(ira, dest_type_value, buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name))); return ira->codegen->builtin_types.entry_invalid; default: break; } 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, &instruction->base, 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->builtin_types.entry_invalid; } if (instr_is_comptime(value)) { ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); if (!val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->type = dest_type; uint8_t *buf = allocate_nonzero(src_size_bytes); buf_write_value_bytes(ira->codegen, buf, val); buf_read_value_bytes(ira->codegen, buf, out_val); return dest_type; } IrInstruction *result = ir_build_bit_cast(&ira->new_irb, instruction->base.scope, instruction->base.source_node, nullptr, value); ir_link_new_instruction(result, &instruction->base); result->value.type = dest_type; return dest_type; } static ZigType *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionIntToPtr *instruction) { Error err; IrInstruction *dest_type_value = instruction->dest_type->other; ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->builtin_types.entry_invalid; if (get_codegen_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->builtin_types.entry_invalid; } if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; if (!type_has_bits(dest_type)) { ir_add_error(ira, dest_type_value, buf_sprintf("type '%s' has 0 bits and cannot store information", buf_ptr(&dest_type->name))); return ira->codegen->builtin_types.entry_invalid; } IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; if (instr_is_comptime(casted_int)) { ConstExprValue *val = ir_resolve_const(ira, casted_int, UndefBad); if (!val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; out_val->data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&val->data.x_bigint); return dest_type; } IrInstruction *result = ir_build_int_to_ptr(&ira->new_irb, instruction->base.scope, instruction->base.source_node, nullptr, casted_int); ir_link_new_instruction(result, &instruction->base); return dest_type; } static ZigType *ir_analyze_instruction_decl_ref(IrAnalyze *ira, IrInstructionDeclRef *instruction) { Tld *tld = instruction->tld; LVal lval = instruction->lval; resolve_top_level_decl(ira->codegen, tld, lval == LValPtr, instruction->base.source_node); if (tld->resolution == TldResolutionInvalid) return ira->codegen->builtin_types.entry_invalid; switch (tld->id) { case TldIdContainer: case TldIdCompTime: zig_unreachable(); case TldIdVar: { TldVar *tld_var = (TldVar *)tld; ZigVar *var = tld_var->var; IrInstruction *var_ptr = ir_get_var_ptr(ira, &instruction->base, var); if (type_is_invalid(var_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; if (tld_var->extern_lib_name != nullptr) { add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, instruction->base.source_node); } if (lval == LValPtr) { ir_link_new_instruction(var_ptr, &instruction->base); return var_ptr->value.type; } else { IrInstruction *loaded_instr = ir_get_deref(ira, &instruction->base, var_ptr); ir_link_new_instruction(loaded_instr, &instruction->base); return loaded_instr->value.type; } } case TldIdFn: { TldFn *tld_fn = (TldFn *)tld; ZigFn *fn_entry = tld_fn->fn_entry; assert(fn_entry->type_entry); if (tld_fn->extern_lib_name != nullptr) { add_link_lib_symbol(ira, tld_fn->extern_lib_name, &fn_entry->symbol_name, instruction->base.source_node); } IrInstruction *ref_instruction = ir_create_const_fn(&ira->new_irb, instruction->base.scope, instruction->base.source_node, fn_entry); if (lval == LValPtr) { IrInstruction *ptr_instr = ir_get_ref(ira, &instruction->base, ref_instruction, true, false); ir_link_new_instruction(ptr_instr, &instruction->base); return ptr_instr->value.type; } else { ir_link_new_instruction(ref_instruction, &instruction->base); return ref_instruction->value.type; } } } zig_unreachable(); } static ZigType *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstructionPtrToInt *instruction) { IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; ZigType *usize = ira->codegen->builtin_types.entry_usize; if (get_codegen_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->builtin_types.entry_invalid; } if (!type_has_bits(target->value.type)) { ir_add_error(ira, target, buf_sprintf("pointer to size 0 type has no address")); return ira->codegen->builtin_types.entry_invalid; } if (instr_is_comptime(target)) { ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->builtin_types.entry_invalid; if (val->type->id == ZigTypeIdPointer && val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { IrInstruction *result = ir_create_const(&ira->new_irb, instruction->base.scope, instruction->base.source_node, usize); bigint_init_unsigned(&result->value.data.x_bigint, val->data.x_ptr.data.hard_coded_addr.addr); ir_link_new_instruction(result, &instruction->base); return usize; } } IrInstruction *result = ir_build_ptr_to_int(&ira->new_irb, instruction->base.scope, instruction->base.source_node, target); result->value.type = usize; ir_link_new_instruction(result, &instruction->base); return usize; } static ZigType *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstructionPtrType *instruction) { Error err; ZigType *child_type = ir_resolve_type(ira, instruction->child_type->other); if (type_is_invalid(child_type)) return ira->codegen->builtin_types.entry_invalid; if (child_type->id == ZigTypeIdUnreachable) { ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed")); return ira->codegen->builtin_types.entry_invalid; } else if (child_type->id == ZigTypeIdOpaque && instruction->ptr_len == PtrLenUnknown) { ir_add_error(ira, &instruction->base, buf_sprintf("unknown-length pointer to opaque")); return ira->codegen->builtin_types.entry_invalid; } uint32_t align_bytes; if (instruction->align_value != nullptr) { if (!ir_resolve_align(ira, instruction->align_value->other, &align_bytes)) return ira->codegen->builtin_types.entry_invalid; if ((err = type_resolve(ira->codegen, child_type, ResolveStatusAlignmentKnown))) return ira->codegen->builtin_types.entry_invalid; } else { if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; align_bytes = 0; } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = get_pointer_to_type_extra(ira->codegen, child_type, instruction->is_const, instruction->is_volatile, instruction->ptr_len, align_bytes, instruction->bit_offset_start, instruction->bit_offset_end - instruction->bit_offset_start); return ira->codegen->builtin_types.entry_type; } static ZigType *ir_analyze_instruction_align_cast(IrAnalyze *ira, IrInstructionAlignCast *instruction) { uint32_t align_bytes; IrInstruction *align_bytes_inst = instruction->align_bytes->other; if (!ir_resolve_align(ira, align_bytes_inst, &align_bytes)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *result = ir_align_cast(ira, target, align_bytes, true); if (type_is_invalid(result->value.type)) return ira->codegen->builtin_types.entry_invalid; ir_link_new_instruction(result, &instruction->base); return result->value.type; } static ZigType *ir_analyze_instruction_opaque_type(IrAnalyze *ira, IrInstructionOpaqueType *instruction) { Buf *name = get_anon_type_name(ira->codegen, ira->new_irb.exec, "opaque", instruction->base.source_node); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = get_opaque_type(ira->codegen, instruction->base.scope, instruction->base.source_node, buf_ptr(name)); return ira->codegen->builtin_types.entry_type; } static ZigType *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, IrInstructionSetAlignStack *instruction) { uint32_t align_bytes; IrInstruction *align_bytes_inst = instruction->align_bytes->other; if (!ir_resolve_align(ira, align_bytes_inst, &align_bytes)) return ira->codegen->builtin_types.entry_invalid; if (align_bytes > 256) { ir_add_error(ira, &instruction->base, buf_sprintf("attempt to @setAlignStack(%" PRIu32 "); maximum is 256", align_bytes)); return ira->codegen->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; } if (fn_entry->fn_inline == FnInlineAlways) { ir_add_error(ira, &instruction->base, buf_sprintf("@setAlignStack in inline function")); return ira->codegen->builtin_types.entry_invalid; } 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->builtin_types.entry_invalid; } fn_entry->set_alignstack_node = instruction->base.source_node; fn_entry->alignstack_value = align_bytes; ir_build_const_from(ira, &instruction->base); return ira->codegen->builtin_types.entry_void; } static ZigType *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstructionArgType *instruction) { IrInstruction *fn_type_inst = instruction->fn_type->other; ZigType *fn_type = ir_resolve_type(ira, fn_type_inst); if (type_is_invalid(fn_type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *arg_index_inst = instruction->arg_index->other; uint64_t arg_index; if (!ir_resolve_usize(ira, arg_index_inst, &arg_index)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; if (arg_index >= fn_type_id->param_count) { ir_add_error(ira, arg_index_inst, buf_sprintf("arg index %" ZIG_PRI_u64 " out of bounds; '%s' has %" ZIG_PRI_usize " arguments", arg_index, buf_ptr(&fn_type->name), fn_type_id->param_count)); return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = fn_type_id->param_info[arg_index].type; if (out_val->data.x_type == nullptr) { // Args are only unresolved if our function is generic. assert(fn_type->data.fn.is_generic); ir_add_error(ira, arg_index_inst, buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_u64 " because '%s' is generic", arg_index, buf_ptr(&fn_type->name))); return ira->codegen->builtin_types.entry_invalid; } return ira->codegen->builtin_types.entry_type; } static ZigType *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstructionTagType *instruction) { Error err; IrInstruction *target_inst = instruction->target->other; ZigType *enum_type = ir_resolve_type(ira, target_inst); if (type_is_invalid(enum_type)) return ira->codegen->builtin_types.entry_invalid; if (enum_type->id == ZigTypeIdEnum) { if ((err = ensure_complete_type(ira->codegen, enum_type))) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = enum_type->data.enumeration.tag_int_type; return ira->codegen->builtin_types.entry_type; } else if (enum_type->id == ZigTypeIdUnion) { if ((err = ensure_complete_type(ira->codegen, enum_type))) return ira->codegen->builtin_types.entry_invalid; AstNode *decl_node = enum_type->data.unionation.decl_node; if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) { assert(enum_type->data.unionation.tag_type != nullptr); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = enum_type->data.unionation.tag_type; return ira->codegen->builtin_types.entry_type; } else { ErrorMsg *msg = ir_add_error(ira, target_inst, buf_sprintf("union '%s' has no tag", buf_ptr(&enum_type->name))); add_error_note(ira->codegen, msg, decl_node, buf_sprintf("consider 'union(enum)' here")); return ira->codegen->builtin_types.entry_invalid; } } else { ir_add_error(ira, target_inst, buf_sprintf("expected enum or union, found '%s'", buf_ptr(&enum_type->name))); return ira->codegen->builtin_types.entry_invalid; } } static ZigType *ir_analyze_instruction_cancel(IrAnalyze *ira, IrInstructionCancel *instruction) { IrInstruction *target_inst = instruction->target->other; if (type_is_invalid(target_inst->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_target = ir_implicit_cast(ira, target_inst, ira->codegen->builtin_types.entry_promise); if (type_is_invalid(casted_target->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *result = ir_build_cancel(&ira->new_irb, instruction->base.scope, instruction->base.source_node, casted_target); result->value.type = ira->codegen->builtin_types.entry_void; result->value.special = ConstValSpecialStatic; ir_link_new_instruction(result, &instruction->base); return result->value.type; } static ZigType *ir_analyze_instruction_coro_id(IrAnalyze *ira, IrInstructionCoroId *instruction) { IrInstruction *promise_ptr = instruction->promise_ptr->other; if (type_is_invalid(promise_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *result = ir_build_coro_id(&ira->new_irb, instruction->base.scope, instruction->base.source_node, promise_ptr); ir_link_new_instruction(result, &instruction->base); result->value.type = ira->codegen->builtin_types.entry_usize; return result->value.type; } static ZigType *ir_analyze_instruction_coro_alloc(IrAnalyze *ira, IrInstructionCoroAlloc *instruction) { IrInstruction *coro_id = instruction->coro_id->other; if (type_is_invalid(coro_id->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *result = ir_build_coro_alloc(&ira->new_irb, instruction->base.scope, instruction->base.source_node, coro_id); ir_link_new_instruction(result, &instruction->base); result->value.type = ira->codegen->builtin_types.entry_bool; return result->value.type; } static ZigType *ir_analyze_instruction_coro_size(IrAnalyze *ira, IrInstructionCoroSize *instruction) { IrInstruction *result = ir_build_coro_size(&ira->new_irb, instruction->base.scope, instruction->base.source_node); ir_link_new_instruction(result, &instruction->base); result->value.type = ira->codegen->builtin_types.entry_usize; return result->value.type; } static ZigType *ir_analyze_instruction_coro_begin(IrAnalyze *ira, IrInstructionCoroBegin *instruction) { IrInstruction *coro_id = instruction->coro_id->other; if (type_is_invalid(coro_id->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *coro_mem_ptr = instruction->coro_mem_ptr->other; if (type_is_invalid(coro_mem_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); assert(fn_entry != nullptr); IrInstruction *result = ir_build_coro_begin(&ira->new_irb, instruction->base.scope, instruction->base.source_node, coro_id, coro_mem_ptr); ir_link_new_instruction(result, &instruction->base); result->value.type = get_promise_type(ira->codegen, fn_entry->type_entry->data.fn.fn_type_id.return_type); return result->value.type; } static ZigType *ir_analyze_instruction_get_implicit_allocator(IrAnalyze *ira, IrInstructionGetImplicitAllocator *instruction) { IrInstruction *result = ir_get_implicit_allocator(ira, &instruction->base, instruction->id); ir_link_new_instruction(result, &instruction->base); return result->value.type; } static ZigType *ir_analyze_instruction_coro_alloc_fail(IrAnalyze *ira, IrInstructionCoroAllocFail *instruction) { IrInstruction *err_val = instruction->err_val->other; if (type_is_invalid(err_val->value.type)) return ir_unreach_error(ira); IrInstruction *result = ir_build_coro_alloc_fail(&ira->new_irb, instruction->base.scope, instruction->base.source_node, err_val); ir_link_new_instruction(result, &instruction->base); result->value.type = ira->codegen->builtin_types.entry_unreachable; return ir_finish_anal(ira, result->value.type); } static ZigType *ir_analyze_instruction_coro_suspend(IrAnalyze *ira, IrInstructionCoroSuspend *instruction) { IrInstruction *save_point = nullptr; if (instruction->save_point != nullptr) { save_point = instruction->save_point->other; if (type_is_invalid(save_point->value.type)) return ira->codegen->builtin_types.entry_invalid; } IrInstruction *is_final = instruction->is_final->other; if (type_is_invalid(is_final->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *result = ir_build_coro_suspend(&ira->new_irb, instruction->base.scope, instruction->base.source_node, save_point, is_final); ir_link_new_instruction(result, &instruction->base); result->value.type = ira->codegen->builtin_types.entry_u8; return result->value.type; } static ZigType *ir_analyze_instruction_coro_end(IrAnalyze *ira, IrInstructionCoroEnd *instruction) { IrInstruction *result = ir_build_coro_end(&ira->new_irb, instruction->base.scope, instruction->base.source_node); ir_link_new_instruction(result, &instruction->base); result->value.type = ira->codegen->builtin_types.entry_void; return result->value.type; } static ZigType *ir_analyze_instruction_coro_free(IrAnalyze *ira, IrInstructionCoroFree *instruction) { IrInstruction *coro_id = instruction->coro_id->other; if (type_is_invalid(coro_id->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *coro_handle = instruction->coro_handle->other; if (type_is_invalid(coro_handle->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *result = ir_build_coro_free(&ira->new_irb, instruction->base.scope, instruction->base.source_node, coro_id, coro_handle); ir_link_new_instruction(result, &instruction->base); ZigType *ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, false); result->value.type = get_optional_type(ira->codegen, ptr_type); return result->value.type; } static ZigType *ir_analyze_instruction_coro_resume(IrAnalyze *ira, IrInstructionCoroResume *instruction) { IrInstruction *awaiter_handle = instruction->awaiter_handle->other; if (type_is_invalid(awaiter_handle->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_target = ir_implicit_cast(ira, awaiter_handle, ira->codegen->builtin_types.entry_promise); if (type_is_invalid(casted_target->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *result = ir_build_coro_resume(&ira->new_irb, instruction->base.scope, instruction->base.source_node, casted_target); ir_link_new_instruction(result, &instruction->base); result->value.type = ira->codegen->builtin_types.entry_void; return result->value.type; } static ZigType *ir_analyze_instruction_coro_save(IrAnalyze *ira, IrInstructionCoroSave *instruction) { IrInstruction *coro_handle = instruction->coro_handle->other; if (type_is_invalid(coro_handle->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *result = ir_build_coro_save(&ira->new_irb, instruction->base.scope, instruction->base.source_node, coro_handle); ir_link_new_instruction(result, &instruction->base); result->value.type = ira->codegen->builtin_types.entry_usize; return result->value.type; } static ZigType *ir_analyze_instruction_coro_promise(IrAnalyze *ira, IrInstructionCoroPromise *instruction) { IrInstruction *coro_handle = instruction->coro_handle->other; if (type_is_invalid(coro_handle->value.type)) return ira->codegen->builtin_types.entry_invalid; if (coro_handle->value.type->id != ZigTypeIdPromise || coro_handle->value.type->data.promise.result_type == nullptr) { ir_add_error(ira, &instruction->base, buf_sprintf("expected promise->T, found '%s'", buf_ptr(&coro_handle->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } ZigType *coro_frame_type = get_promise_frame_type(ira->codegen, coro_handle->value.type->data.promise.result_type); IrInstruction *result = ir_build_coro_promise(&ira->new_irb, instruction->base.scope, instruction->base.source_node, coro_handle); ir_link_new_instruction(result, &instruction->base); result->value.type = get_pointer_to_type(ira->codegen, coro_frame_type, false); return result->value.type; } static ZigType *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira, IrInstructionCoroAllocHelper *instruction) { IrInstruction *alloc_fn = instruction->alloc_fn->other; if (type_is_invalid(alloc_fn->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *coro_size = instruction->coro_size->other; if (type_is_invalid(coro_size->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *result = ir_build_coro_alloc_helper(&ira->new_irb, instruction->base.scope, instruction->base.source_node, alloc_fn, coro_size); ir_link_new_instruction(result, &instruction->base); ZigType *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, false); result->value.type = get_optional_type(ira->codegen, u8_ptr_type); return result->value.type; } static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op) { ZigType *operand_type = ir_resolve_type(ira, op); if (type_is_invalid(operand_type)) return ira->codegen->builtin_types.entry_invalid; if (operand_type->id == ZigTypeIdInt) { if (operand_type->data.integral.bit_count < 8) { ir_add_error(ira, op, buf_sprintf("expected integer type 8 bits or larger, found %" PRIu32 "-bit integer type", operand_type->data.integral.bit_count)); return ira->codegen->builtin_types.entry_invalid; } if (operand_type->data.integral.bit_count > ira->codegen->pointer_size_bytes * 8) { ir_add_error(ira, op, buf_sprintf("expected integer type pointer size or smaller, found %" PRIu32 "-bit integer type", operand_type->data.integral.bit_count)); return ira->codegen->builtin_types.entry_invalid; } if (!is_power_of_2(operand_type->data.integral.bit_count)) { ir_add_error(ira, op, buf_sprintf("%" PRIu32 "-bit integer type is not a power of 2", operand_type->data.integral.bit_count)); return ira->codegen->builtin_types.entry_invalid; } } else if (get_codegen_ptr_type(operand_type) == nullptr) { ir_add_error(ira, op, buf_sprintf("expected integer or pointer type, found '%s'", buf_ptr(&operand_type->name))); return ira->codegen->builtin_types.entry_invalid; } return operand_type; } static ZigType *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstructionAtomicRmw *instruction) { ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->other); if (type_is_invalid(operand_type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *ptr_inst = instruction->ptr->other; if (type_is_invalid(ptr_inst->value.type)) return ira->codegen->builtin_types.entry_invalid; // 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->builtin_types.entry_invalid; AtomicRmwOp op; if (instruction->op == nullptr) { op = instruction->resolved_op; } else { if (!ir_resolve_atomic_rmw_op(ira, instruction->op->other, &op)) { return ira->codegen->builtin_types.entry_invalid; } } IrInstruction *operand = instruction->operand->other; if (type_is_invalid(operand->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_operand = ir_implicit_cast(ira, operand, operand_type); if (type_is_invalid(casted_operand->value.type)) return ira->codegen->builtin_types.entry_invalid; AtomicOrder ordering; if (instruction->ordering == nullptr) { ordering = instruction->resolved_ordering; } else { if (!ir_resolve_atomic_order(ira, instruction->ordering->other, &ordering)) return ira->codegen->builtin_types.entry_invalid; if (ordering == AtomicOrderUnordered) { ir_add_error(ira, instruction->ordering, buf_sprintf("@atomicRmw atomic ordering must not be Unordered")); return ira->codegen->builtin_types.entry_invalid; } } 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); ir_link_new_instruction(result, &instruction->base); result->value.type = operand_type; return result->value.type; } static ZigType *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstructionAtomicLoad *instruction) { ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->other); if (type_is_invalid(operand_type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *ptr_inst = instruction->ptr->other; if (type_is_invalid(ptr_inst->value.type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; AtomicOrder ordering; if (instruction->ordering == nullptr) { ordering = instruction->resolved_ordering; } else { if (!ir_resolve_atomic_order(ira, instruction->ordering->other, &ordering)) return ira->codegen->builtin_types.entry_invalid; } if (ordering == AtomicOrderRelease || ordering == AtomicOrderAcqRel) { assert(instruction->ordering != nullptr); ir_add_error(ira, instruction->ordering, buf_sprintf("@atomicLoad atomic ordering must not be Release or AcqRel")); return ira->codegen->builtin_types.entry_invalid; } if (instr_is_comptime(casted_ptr)) { IrInstruction *result = ir_get_deref(ira, &instruction->base, casted_ptr); ir_link_new_instruction(result, &instruction->base); assert(result->value.type != nullptr); return result->value.type; } IrInstruction *result = ir_build_atomic_load(&ira->new_irb, instruction->base.scope, instruction->base.source_node, nullptr, casted_ptr, nullptr, ordering); ir_link_new_instruction(result, &instruction->base); result->value.type = operand_type; return result->value.type; } static ZigType *ir_analyze_instruction_promise_result_type(IrAnalyze *ira, IrInstructionPromiseResultType *instruction) { ZigType *promise_type = ir_resolve_type(ira, instruction->promise_type->other); if (type_is_invalid(promise_type)) return ira->codegen->builtin_types.entry_invalid; if (promise_type->id != ZigTypeIdPromise || promise_type->data.promise.result_type == nullptr) { ir_add_error(ira, &instruction->base, buf_sprintf("expected promise->T, found '%s'", buf_ptr(&promise_type->name))); return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = promise_type->data.promise.result_type; return ira->codegen->builtin_types.entry_type; } static ZigType *ir_analyze_instruction_await_bookkeeping(IrAnalyze *ira, IrInstructionAwaitBookkeeping *instruction) { ZigType *promise_result_type = ir_resolve_type(ira, instruction->promise_result_type->other); if (type_is_invalid(promise_result_type)) return ira->codegen->builtin_types.entry_invalid; ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); assert(fn_entry != nullptr); if (type_can_fail(promise_result_type)) { fn_entry->calls_or_awaits_errorable_fn = true; } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->type = ira->codegen->builtin_types.entry_void; return out_val->type; } static ZigType *ir_analyze_instruction_merge_err_ret_traces(IrAnalyze *ira, IrInstructionMergeErrRetTraces *instruction) { IrInstruction *coro_promise_ptr = instruction->coro_promise_ptr->other; if (type_is_invalid(coro_promise_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; assert(coro_promise_ptr->value.type->id == ZigTypeIdPointer); ZigType *promise_frame_type = coro_promise_ptr->value.type->data.pointer.child_type; assert(promise_frame_type->id == ZigTypeIdStruct); ZigType *promise_result_type = promise_frame_type->data.structure.fields[1].type_entry; if (!type_can_fail(promise_result_type)) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->type = ira->codegen->builtin_types.entry_void; return out_val->type; } IrInstruction *src_err_ret_trace_ptr = instruction->src_err_ret_trace_ptr->other; if (type_is_invalid(src_err_ret_trace_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *dest_err_ret_trace_ptr = instruction->dest_err_ret_trace_ptr->other; if (type_is_invalid(dest_err_ret_trace_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *result = ir_build_merge_err_ret_traces(&ira->new_irb, instruction->base.scope, instruction->base.source_node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr); ir_link_new_instruction(result, &instruction->base); result->value.type = ira->codegen->builtin_types.entry_void; return result->value.type; } static ZigType *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); ir_link_new_instruction(result, &instruction->base); result->value.type = ira->codegen->builtin_types.entry_void; return result->value.type; } static ZigType *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze *ira, IrInstructionMarkErrRetTracePtr *instruction) { IrInstruction *err_ret_trace_ptr = instruction->err_ret_trace_ptr->other; if (type_is_invalid(err_ret_trace_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *result = ir_build_mark_err_ret_trace_ptr(&ira->new_irb, instruction->base.scope, instruction->base.source_node, err_ret_trace_ptr); ir_link_new_instruction(result, &instruction->base); result->value.type = ira->codegen->builtin_types.entry_void; return result->value.type; } static ZigType *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionSqrt *instruction) { ZigType *float_type = ir_resolve_type(ira, instruction->type->other); if (type_is_invalid(float_type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *op = instruction->op->other; if (type_is_invalid(op->value.type)) return ira->codegen->builtin_types.entry_invalid; bool ok_type = float_type->id == ZigTypeIdComptimeFloat || float_type->id == ZigTypeIdFloat; if (!ok_type) { ir_add_error(ira, instruction->type, buf_sprintf("@sqrt does not support type '%s'", buf_ptr(&float_type->name))); return ira->codegen->builtin_types.entry_invalid; } IrInstruction *casted_op = ir_implicit_cast(ira, op, float_type); if (type_is_invalid(casted_op->value.type)) return ira->codegen->builtin_types.entry_invalid; if (instr_is_comptime(casted_op)) { ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad); if (!val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); if (float_type->id == ZigTypeIdComptimeFloat) { bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat); } else if (float_type->id == ZigTypeIdFloat) { switch (float_type->data.floating.bit_count) { case 16: out_val->data.x_f16 = f16_sqrt(val->data.x_f16); break; case 32: out_val->data.x_f32 = sqrtf(val->data.x_f32); break; case 64: out_val->data.x_f64 = sqrt(val->data.x_f64); break; case 128: f128M_sqrt(&val->data.x_f128, &out_val->data.x_f128); break; default: zig_unreachable(); } } else { zig_unreachable(); } return float_type; } assert(float_type->id == ZigTypeIdFloat); if (float_type->data.floating.bit_count != 16 && float_type->data.floating.bit_count != 32 && float_type->data.floating.bit_count != 64) { ir_add_error(ira, instruction->type, buf_sprintf("compiler TODO: add implementation of sqrt for '%s'", buf_ptr(&float_type->name))); return ira->codegen->builtin_types.entry_invalid; } IrInstruction *result = ir_build_sqrt(&ira->new_irb, instruction->base.scope, instruction->base.source_node, nullptr, casted_op); ir_link_new_instruction(result, &instruction->base); result->value.type = float_type; return result->value.type; } static ZigType *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstructionEnumToInt *instruction) { Error err; IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; if (target->value.type->id != ZigTypeIdEnum) { ir_add_error(ira, instruction->target, buf_sprintf("expected enum, found type '%s'", buf_ptr(&target->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } if ((err = type_resolve(ira->codegen, target->value.type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; ZigType *tag_type = target->value.type->data.enumeration.tag_int_type; IrInstruction *result = ir_analyze_enum_to_int(ira, &instruction->base, target, tag_type); ir_link_new_instruction(result, &instruction->base); return result->value.type; } static ZigType *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstructionIntToEnum *instruction) { Error err; IrInstruction *dest_type_value = instruction->dest_type->other; ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; ZigType *tag_type = dest_type->data.enumeration.tag_int_type; IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_target = ir_implicit_cast(ira, target, tag_type); if (type_is_invalid(casted_target->value.type)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *result = ir_analyze_int_to_enum(ira, &instruction->base, casted_target, dest_type); ir_link_new_instruction(result, &instruction->base); return result->value.type; } static ZigType *ir_analyze_instruction_check_runtime_scope(IrAnalyze *ira, IrInstructionCheckRuntimeScope *instruction) { IrInstruction *block_comptime_inst = instruction->scope_is_comptime->other; bool scope_is_comptime; if (!ir_resolve_bool(ira, block_comptime_inst, &scope_is_comptime)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *is_comptime_inst = instruction->is_comptime->other; bool is_comptime; if (!ir_resolve_bool(ira, is_comptime_inst, &is_comptime)) return ira->codegen->builtin_types.entry_invalid; 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->builtin_types.entry_invalid; } ir_build_const_from(ira, &instruction->base); return ira->codegen->builtin_types.entry_void; } static ZigType *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: case IrInstructionIdWidenOrShorten: case IrInstructionIdStructInit: case IrInstructionIdUnionInit: case IrInstructionIdStructFieldPtr: case IrInstructionIdUnionFieldPtr: case IrInstructionIdOptionalWrap: case IrInstructionIdErrWrapCode: case IrInstructionIdErrWrapPayload: case IrInstructionIdCast: zig_unreachable(); case IrInstructionIdReturn: return ir_analyze_instruction_return(ira, (IrInstructionReturn *)instruction); case IrInstructionIdConst: return ir_analyze_instruction_const(ira, (IrInstructionConst *)instruction); case IrInstructionIdUnOp: return ir_analyze_instruction_un_op(ira, (IrInstructionUnOp *)instruction); case IrInstructionIdBinOp: return ir_analyze_instruction_bin_op(ira, (IrInstructionBinOp *)instruction); case IrInstructionIdDeclVar: return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVar *)instruction); case IrInstructionIdLoadPtr: return ir_analyze_instruction_load_ptr(ira, (IrInstructionLoadPtr *)instruction); case IrInstructionIdStorePtr: return ir_analyze_instruction_store_ptr(ira, (IrInstructionStorePtr *)instruction); case IrInstructionIdElemPtr: return ir_analyze_instruction_elem_ptr(ira, (IrInstructionElemPtr *)instruction); case IrInstructionIdVarPtr: return ir_analyze_instruction_var_ptr(ira, (IrInstructionVarPtr *)instruction); case IrInstructionIdFieldPtr: return ir_analyze_instruction_field_ptr(ira, (IrInstructionFieldPtr *)instruction); case IrInstructionIdCall: return ir_analyze_instruction_call(ira, (IrInstructionCall *)instruction); case IrInstructionIdBr: return ir_analyze_instruction_br(ira, (IrInstructionBr *)instruction); case IrInstructionIdCondBr: return ir_analyze_instruction_cond_br(ira, (IrInstructionCondBr *)instruction); case IrInstructionIdUnreachable: return ir_analyze_instruction_unreachable(ira, (IrInstructionUnreachable *)instruction); case IrInstructionIdPhi: return ir_analyze_instruction_phi(ira, (IrInstructionPhi *)instruction); case IrInstructionIdTypeOf: return ir_analyze_instruction_typeof(ira, (IrInstructionTypeOf *)instruction); case IrInstructionIdToPtrType: return ir_analyze_instruction_to_ptr_type(ira, (IrInstructionToPtrType *)instruction); case IrInstructionIdPtrTypeChild: return ir_analyze_instruction_ptr_type_child(ira, (IrInstructionPtrTypeChild *)instruction); case IrInstructionIdSetCold: return ir_analyze_instruction_set_cold(ira, (IrInstructionSetCold *)instruction); case IrInstructionIdSetRuntimeSafety: return ir_analyze_instruction_set_runtime_safety(ira, (IrInstructionSetRuntimeSafety *)instruction); case IrInstructionIdSetFloatMode: return ir_analyze_instruction_set_float_mode(ira, (IrInstructionSetFloatMode *)instruction); case IrInstructionIdSliceType: return ir_analyze_instruction_slice_type(ira, (IrInstructionSliceType *)instruction); case IrInstructionIdAsm: return ir_analyze_instruction_asm(ira, (IrInstructionAsm *)instruction); case IrInstructionIdArrayType: return ir_analyze_instruction_array_type(ira, (IrInstructionArrayType *)instruction); case IrInstructionIdPromiseType: return ir_analyze_instruction_promise_type(ira, (IrInstructionPromiseType *)instruction); case IrInstructionIdSizeOf: return ir_analyze_instruction_size_of(ira, (IrInstructionSizeOf *)instruction); case IrInstructionIdTestNonNull: return ir_analyze_instruction_test_non_null(ira, (IrInstructionTestNonNull *)instruction); case IrInstructionIdUnwrapOptional: return ir_analyze_instruction_unwrap_maybe(ira, (IrInstructionUnwrapOptional *)instruction); case IrInstructionIdClz: return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction); case IrInstructionIdCtz: return ir_analyze_instruction_ctz(ira, (IrInstructionCtz *)instruction); case IrInstructionIdPopCount: return ir_analyze_instruction_pop_count(ira, (IrInstructionPopCount *)instruction); case IrInstructionIdSwitchBr: return ir_analyze_instruction_switch_br(ira, (IrInstructionSwitchBr *)instruction); case IrInstructionIdSwitchTarget: return ir_analyze_instruction_switch_target(ira, (IrInstructionSwitchTarget *)instruction); case IrInstructionIdSwitchVar: return ir_analyze_instruction_switch_var(ira, (IrInstructionSwitchVar *)instruction); case IrInstructionIdUnionTag: return ir_analyze_instruction_union_tag(ira, (IrInstructionUnionTag *)instruction); case IrInstructionIdImport: return ir_analyze_instruction_import(ira, (IrInstructionImport *)instruction); case IrInstructionIdArrayLen: return ir_analyze_instruction_array_len(ira, (IrInstructionArrayLen *)instruction); case IrInstructionIdRef: return ir_analyze_instruction_ref(ira, (IrInstructionRef *)instruction); case IrInstructionIdContainerInitList: return ir_analyze_instruction_container_init_list(ira, (IrInstructionContainerInitList *)instruction); case IrInstructionIdContainerInitFields: return ir_analyze_instruction_container_init_fields(ira, (IrInstructionContainerInitFields *)instruction); case IrInstructionIdMinValue: return ir_analyze_instruction_min_value(ira, (IrInstructionMinValue *)instruction); case IrInstructionIdMaxValue: return ir_analyze_instruction_max_value(ira, (IrInstructionMaxValue *)instruction); case IrInstructionIdCompileErr: return ir_analyze_instruction_compile_err(ira, (IrInstructionCompileErr *)instruction); case 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 IrInstructionIdCmpxchg: return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchg *)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 IrInstructionIdBoolNot: return ir_analyze_instruction_bool_not(ira, (IrInstructionBoolNot *)instruction); case IrInstructionIdMemset: return ir_analyze_instruction_memset(ira, (IrInstructionMemset *)instruction); case IrInstructionIdMemcpy: return ir_analyze_instruction_memcpy(ira, (IrInstructionMemcpy *)instruction); case IrInstructionIdSlice: return ir_analyze_instruction_slice(ira, (IrInstructionSlice *)instruction); case IrInstructionIdMemberCount: return ir_analyze_instruction_member_count(ira, (IrInstructionMemberCount *)instruction); case IrInstructionIdMemberType: return ir_analyze_instruction_member_type(ira, (IrInstructionMemberType *)instruction); case IrInstructionIdMemberName: return ir_analyze_instruction_member_name(ira, (IrInstructionMemberName *)instruction); case IrInstructionIdBreakpoint: return ir_analyze_instruction_breakpoint(ira, (IrInstructionBreakpoint *)instruction); case IrInstructionIdReturnAddress: return ir_analyze_instruction_return_address(ira, (IrInstructionReturnAddress *)instruction); case IrInstructionIdFrameAddress: return ir_analyze_instruction_frame_address(ira, (IrInstructionFrameAddress *)instruction); case IrInstructionIdHandle: return ir_analyze_instruction_handle(ira, (IrInstructionHandle *)instruction); case IrInstructionIdAlignOf: return ir_analyze_instruction_align_of(ira, (IrInstructionAlignOf *)instruction); case IrInstructionIdOverflowOp: return ir_analyze_instruction_overflow_op(ira, (IrInstructionOverflowOp *)instruction); case IrInstructionIdTestErr: return ir_analyze_instruction_test_err(ira, (IrInstructionTestErr *)instruction); case IrInstructionIdUnwrapErrCode: return ir_analyze_instruction_unwrap_err_code(ira, (IrInstructionUnwrapErrCode *)instruction); case IrInstructionIdUnwrapErrPayload: return ir_analyze_instruction_unwrap_err_payload(ira, (IrInstructionUnwrapErrPayload *)instruction); case IrInstructionIdFnProto: return ir_analyze_instruction_fn_proto(ira, (IrInstructionFnProto *)instruction); case IrInstructionIdTestComptime: return ir_analyze_instruction_test_comptime(ira, (IrInstructionTestComptime *)instruction); case IrInstructionIdCheckSwitchProngs: return ir_analyze_instruction_check_switch_prongs(ira, (IrInstructionCheckSwitchProngs *)instruction); case IrInstructionIdCheckStatementIsVoid: return ir_analyze_instruction_check_statement_is_void(ira, (IrInstructionCheckStatementIsVoid *)instruction); case IrInstructionIdDeclRef: return ir_analyze_instruction_decl_ref(ira, (IrInstructionDeclRef *)instruction); case IrInstructionIdPanic: return ir_analyze_instruction_panic(ira, (IrInstructionPanic *)instruction); case IrInstructionIdPtrCast: return ir_analyze_instruction_ptr_cast(ira, (IrInstructionPtrCast *)instruction); case IrInstructionIdBitCast: return ir_analyze_instruction_bit_cast(ira, (IrInstructionBitCast *)instruction); case IrInstructionIdIntToPtr: return ir_analyze_instruction_int_to_ptr(ira, (IrInstructionIntToPtr *)instruction); case IrInstructionIdPtrToInt: return ir_analyze_instruction_ptr_to_int(ira, (IrInstructionPtrToInt *)instruction); case IrInstructionIdTagName: return ir_analyze_instruction_enum_tag_name(ira, (IrInstructionTagName *)instruction); case IrInstructionIdFieldParentPtr: return ir_analyze_instruction_field_parent_ptr(ira, (IrInstructionFieldParentPtr *)instruction); case IrInstructionIdOffsetOf: return ir_analyze_instruction_offset_of(ira, (IrInstructionOffsetOf *)instruction); case IrInstructionIdTypeInfo: return ir_analyze_instruction_type_info(ira, (IrInstructionTypeInfo *) instruction); case IrInstructionIdTypeId: return ir_analyze_instruction_type_id(ira, (IrInstructionTypeId *)instruction); case IrInstructionIdSetEvalBranchQuota: return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstructionSetEvalBranchQuota *)instruction); case IrInstructionIdPtrType: return ir_analyze_instruction_ptr_type(ira, (IrInstructionPtrType *)instruction); case IrInstructionIdAlignCast: return ir_analyze_instruction_align_cast(ira, (IrInstructionAlignCast *)instruction); case IrInstructionIdOpaqueType: return ir_analyze_instruction_opaque_type(ira, (IrInstructionOpaqueType *)instruction); case IrInstructionIdSetAlignStack: return ir_analyze_instruction_set_align_stack(ira, (IrInstructionSetAlignStack *)instruction); case IrInstructionIdArgType: return ir_analyze_instruction_arg_type(ira, (IrInstructionArgType *)instruction); case IrInstructionIdTagType: return ir_analyze_instruction_tag_type(ira, (IrInstructionTagType *)instruction); case IrInstructionIdExport: return ir_analyze_instruction_export(ira, (IrInstructionExport *)instruction); case IrInstructionIdErrorReturnTrace: return ir_analyze_instruction_error_return_trace(ira, (IrInstructionErrorReturnTrace *)instruction); case IrInstructionIdErrorUnion: return ir_analyze_instruction_error_union(ira, (IrInstructionErrorUnion *)instruction); case IrInstructionIdCancel: return ir_analyze_instruction_cancel(ira, (IrInstructionCancel *)instruction); case IrInstructionIdCoroId: return ir_analyze_instruction_coro_id(ira, (IrInstructionCoroId *)instruction); case IrInstructionIdCoroAlloc: return ir_analyze_instruction_coro_alloc(ira, (IrInstructionCoroAlloc *)instruction); case IrInstructionIdCoroSize: return ir_analyze_instruction_coro_size(ira, (IrInstructionCoroSize *)instruction); case IrInstructionIdCoroBegin: return ir_analyze_instruction_coro_begin(ira, (IrInstructionCoroBegin *)instruction); case IrInstructionIdGetImplicitAllocator: return ir_analyze_instruction_get_implicit_allocator(ira, (IrInstructionGetImplicitAllocator *)instruction); case IrInstructionIdCoroAllocFail: return ir_analyze_instruction_coro_alloc_fail(ira, (IrInstructionCoroAllocFail *)instruction); case IrInstructionIdCoroSuspend: return ir_analyze_instruction_coro_suspend(ira, (IrInstructionCoroSuspend *)instruction); case IrInstructionIdCoroEnd: return ir_analyze_instruction_coro_end(ira, (IrInstructionCoroEnd *)instruction); case IrInstructionIdCoroFree: return ir_analyze_instruction_coro_free(ira, (IrInstructionCoroFree *)instruction); case IrInstructionIdCoroResume: return ir_analyze_instruction_coro_resume(ira, (IrInstructionCoroResume *)instruction); case IrInstructionIdCoroSave: return ir_analyze_instruction_coro_save(ira, (IrInstructionCoroSave *)instruction); case IrInstructionIdCoroPromise: return ir_analyze_instruction_coro_promise(ira, (IrInstructionCoroPromise *)instruction); case IrInstructionIdCoroAllocHelper: return ir_analyze_instruction_coro_alloc_helper(ira, (IrInstructionCoroAllocHelper *)instruction); case IrInstructionIdAtomicRmw: return ir_analyze_instruction_atomic_rmw(ira, (IrInstructionAtomicRmw *)instruction); case IrInstructionIdAtomicLoad: return ir_analyze_instruction_atomic_load(ira, (IrInstructionAtomicLoad *)instruction); case IrInstructionIdPromiseResultType: return ir_analyze_instruction_promise_result_type(ira, (IrInstructionPromiseResultType *)instruction); case IrInstructionIdAwaitBookkeeping: return ir_analyze_instruction_await_bookkeeping(ira, (IrInstructionAwaitBookkeeping *)instruction); case IrInstructionIdSaveErrRetAddr: return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstructionSaveErrRetAddr *)instruction); case IrInstructionIdAddImplicitReturnType: return ir_analyze_instruction_add_implicit_return_type(ira, (IrInstructionAddImplicitReturnType *)instruction); case IrInstructionIdMergeErrRetTraces: return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction); case IrInstructionIdMarkErrRetTracePtr: return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction); case IrInstructionIdSqrt: return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction); case IrInstructionIdIntToErr: return ir_analyze_instruction_int_to_err(ira, (IrInstructionIntToErr *)instruction); case IrInstructionIdErrToInt: return ir_analyze_instruction_err_to_int(ira, (IrInstructionErrToInt *)instruction); case IrInstructionIdIntToEnum: return ir_analyze_instruction_int_to_enum(ira, (IrInstructionIntToEnum *)instruction); case IrInstructionIdEnumToInt: return ir_analyze_instruction_enum_to_int(ira, (IrInstructionEnumToInt *)instruction); case IrInstructionIdCheckRuntimeScope: return ir_analyze_instruction_check_runtime_scope(ira, (IrInstructionCheckRuntimeScope *)instruction); } zig_unreachable(); } static ZigType *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction) { ZigType *instruction_type = ir_analyze_instruction_nocast(ira, instruction); instruction->value.type = instruction_type; if (instruction->other) { instruction->other->value.type = instruction_type; } else { assert(instruction_type->id == ZigTypeIdInvalid || instruction_type->id == ZigTypeIdUnreachable); instruction->other = instruction; } return instruction_type; } // This function attempts to evaluate IR code while doing type checking and other analysis. // It emits a new IrExecutable which is partially evaluated IR code. ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_exec, ZigType *expected_type, AstNode *expected_type_source_node) { assert(!old_exec->invalid); assert(expected_type == nullptr || !type_is_invalid(expected_type)); IrAnalyze *ira = allocate(1); old_exec->analysis = ira; ira->codegen = codegen; ZigFn *fn_entry = exec_fn_entry(old_exec); bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; ira->explicit_return_type = is_async ? get_promise_type(codegen, expected_type) : expected_type; ira->old_irb.codegen = codegen; ira->old_irb.exec = old_exec; ira->new_irb.codegen = codegen; ira->new_irb.exec = new_exec; ConstExprValue *vals = create_const_vals(ira->old_irb.exec->mem_slot_count); ira->exec_context.mem_slot_list.resize(ira->old_irb.exec->mem_slot_count); for (size_t i = 0; i < ira->exec_context.mem_slot_list.length; i += 1) { ira->exec_context.mem_slot_list.items[i] = &vals[i]; } IrBasicBlock *old_entry_bb = ira->old_irb.exec->basic_block_list.at(0); IrBasicBlock *new_entry_bb = ir_get_new_bb(ira, old_entry_bb, nullptr); ir_ref_bb(new_entry_bb); ira->new_irb.current_basic_block = new_entry_bb; ira->old_bb_index = 0; ir_start_bb(ira, old_entry_bb, nullptr); while (ira->old_bb_index < ira->old_irb.exec->basic_block_list.length) { IrInstruction *old_instruction = ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index); if (old_instruction->ref_count == 0 && !ir_has_side_effects(old_instruction)) { ira->instruction_index += 1; continue; } ZigType *return_type = ir_analyze_instruction(ira, old_instruction); if (type_is_invalid(return_type) && ir_should_inline(new_exec, old_instruction->scope)) { return ira->codegen->builtin_types.entry_invalid; } // unreachable instructions do their own control flow. if (return_type->id == ZigTypeIdUnreachable) continue; ira->instruction_index += 1; } if (new_exec->invalid) { return ira->codegen->builtin_types.entry_invalid; } else if (ira->src_implicit_return_type_list.length == 0) { return codegen->builtin_types.entry_unreachable; } else { return ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items, ira->src_implicit_return_type_list.length); } } bool ir_has_side_effects(IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: zig_unreachable(); case IrInstructionIdBr: case IrInstructionIdCondBr: case IrInstructionIdSwitchBr: case IrInstructionIdDeclVar: case IrInstructionIdStorePtr: case IrInstructionIdCall: case IrInstructionIdReturn: case IrInstructionIdUnreachable: case IrInstructionIdSetCold: case IrInstructionIdSetRuntimeSafety: case IrInstructionIdSetFloatMode: case IrInstructionIdImport: case IrInstructionIdCompileErr: case IrInstructionIdCompileLog: case IrInstructionIdCImport: case IrInstructionIdCInclude: case IrInstructionIdCDefine: case IrInstructionIdCUndef: case IrInstructionIdCmpxchg: case IrInstructionIdFence: case IrInstructionIdMemset: case IrInstructionIdMemcpy: case IrInstructionIdBreakpoint: case IrInstructionIdOverflowOp: // TODO when we support multiple returns this can be side effect free case IrInstructionIdCheckSwitchProngs: case IrInstructionIdCheckStatementIsVoid: case IrInstructionIdCheckRuntimeScope: case IrInstructionIdPanic: case IrInstructionIdSetEvalBranchQuota: case IrInstructionIdPtrType: case IrInstructionIdSetAlignStack: case IrInstructionIdExport: case IrInstructionIdCancel: case IrInstructionIdCoroId: case IrInstructionIdCoroBegin: case IrInstructionIdCoroAllocFail: case IrInstructionIdCoroEnd: case IrInstructionIdCoroResume: case IrInstructionIdCoroSave: case IrInstructionIdCoroAllocHelper: case IrInstructionIdAwaitBookkeeping: case IrInstructionIdSaveErrRetAddr: case IrInstructionIdAddImplicitReturnType: case IrInstructionIdMergeErrRetTraces: case IrInstructionIdMarkErrRetTracePtr: case IrInstructionIdAtomicRmw: return true; case IrInstructionIdPhi: case IrInstructionIdUnOp: case IrInstructionIdBinOp: case IrInstructionIdLoadPtr: case IrInstructionIdConst: case IrInstructionIdCast: case IrInstructionIdContainerInitList: case IrInstructionIdContainerInitFields: case IrInstructionIdStructInit: case IrInstructionIdUnionInit: case IrInstructionIdFieldPtr: case IrInstructionIdElemPtr: case IrInstructionIdVarPtr: case IrInstructionIdTypeOf: case IrInstructionIdToPtrType: case IrInstructionIdPtrTypeChild: case IrInstructionIdArrayLen: case IrInstructionIdStructFieldPtr: case IrInstructionIdUnionFieldPtr: case IrInstructionIdArrayType: case IrInstructionIdPromiseType: case IrInstructionIdSliceType: case IrInstructionIdSizeOf: case IrInstructionIdTestNonNull: case IrInstructionIdUnwrapOptional: case IrInstructionIdClz: case IrInstructionIdCtz: case IrInstructionIdPopCount: case IrInstructionIdSwitchVar: case IrInstructionIdSwitchTarget: case IrInstructionIdUnionTag: case IrInstructionIdRef: case IrInstructionIdMinValue: case IrInstructionIdMaxValue: case IrInstructionIdEmbedFile: case IrInstructionIdTruncate: case IrInstructionIdIntType: case IrInstructionIdBoolNot: case IrInstructionIdSlice: case IrInstructionIdMemberCount: case IrInstructionIdMemberType: case IrInstructionIdMemberName: case IrInstructionIdAlignOf: case IrInstructionIdReturnAddress: case IrInstructionIdFrameAddress: case IrInstructionIdHandle: case IrInstructionIdTestErr: case IrInstructionIdUnwrapErrCode: case IrInstructionIdOptionalWrap: case IrInstructionIdErrWrapCode: case IrInstructionIdErrWrapPayload: case IrInstructionIdFnProto: case IrInstructionIdTestComptime: case IrInstructionIdPtrCast: case IrInstructionIdBitCast: case IrInstructionIdWidenOrShorten: case IrInstructionIdPtrToInt: case IrInstructionIdIntToPtr: case IrInstructionIdIntToEnum: case IrInstructionIdIntToErr: case IrInstructionIdErrToInt: case IrInstructionIdDeclRef: case IrInstructionIdErrName: case IrInstructionIdTypeName: case IrInstructionIdTagName: case IrInstructionIdFieldParentPtr: case IrInstructionIdOffsetOf: case IrInstructionIdTypeInfo: case IrInstructionIdTypeId: case IrInstructionIdAlignCast: case IrInstructionIdOpaqueType: case IrInstructionIdArgType: case IrInstructionIdTagType: case IrInstructionIdErrorReturnTrace: case IrInstructionIdErrorUnion: case IrInstructionIdGetImplicitAllocator: case IrInstructionIdCoroAlloc: case IrInstructionIdCoroSize: case IrInstructionIdCoroSuspend: case IrInstructionIdCoroFree: case IrInstructionIdCoroPromise: case IrInstructionIdPromiseResultType: case IrInstructionIdSqrt: case IrInstructionIdAtomicLoad: case IrInstructionIdIntCast: case IrInstructionIdFloatCast: case IrInstructionIdErrSetCast: case IrInstructionIdIntToFloat: case IrInstructionIdFloatToInt: case IrInstructionIdBoolToInt: case IrInstructionIdFromBytes: case IrInstructionIdToBytes: case IrInstructionIdEnumToInt: return false; case IrInstructionIdAsm: { IrInstructionAsm *asm_instruction = (IrInstructionAsm *)instruction; return asm_instruction->has_side_effects; } case IrInstructionIdUnwrapErrPayload: { IrInstructionUnwrapErrPayload *unwrap_err_payload_instruction = (IrInstructionUnwrapErrPayload *)instruction; return unwrap_err_payload_instruction->safety_check_on; } } zig_unreachable(); }