/* * Copyright (c) 2016 Andrew Kelley * * This file is part of zig, which is MIT licensed. * See http://opensource.org/licenses/MIT */ #include "analyze.hpp" #include "ast_render.hpp" #include "error.hpp" #include "ir.hpp" #include "ir_print.hpp" #include "os.hpp" #include "range_set.hpp" #include "softfloat.hpp" #include "util.hpp" #include "mem_list.hpp" #include "all_types.hpp" #include struct IrBuilderSrc { CodeGen *codegen; IrExecutableSrc *exec; IrBasicBlockSrc *current_basic_block; AstNode *main_block_node; }; struct IrBuilderGen { CodeGen *codegen; IrExecutableGen *exec; IrBasicBlockGen *current_basic_block; // track for immediate post-analysis destruction mem::List constants; }; struct IrAnalyze { CodeGen *codegen; IrBuilderSrc old_irb; IrBuilderGen new_irb; size_t old_bb_index; size_t instruction_index; ZigType *explicit_return_type; AstNode *explicit_return_type_source_node; ZigList src_implicit_return_type_list; ZigList resume_stack; IrBasicBlockSrc *const_predecessor_bb; size_t ref_count; size_t break_debug_id; // for debugging purposes IrInstGen *return_ptr; // For the purpose of using in a debugger void dump(); }; enum ConstCastResultId { ConstCastResultIdOk, ConstCastResultIdInvalid, ConstCastResultIdErrSet, ConstCastResultIdErrSetGlobal, ConstCastResultIdPointerChild, ConstCastResultIdSliceChild, ConstCastResultIdOptionalChild, ConstCastResultIdErrorUnionPayload, ConstCastResultIdErrorUnionErrorSet, ConstCastResultIdFnAlign, ConstCastResultIdFnCC, ConstCastResultIdFnVarArgs, ConstCastResultIdFnIsGeneric, ConstCastResultIdFnReturnType, ConstCastResultIdFnArgCount, ConstCastResultIdFnGenericArgCount, ConstCastResultIdFnArg, ConstCastResultIdFnArgNoAlias, ConstCastResultIdType, ConstCastResultIdUnresolvedInferredErrSet, ConstCastResultIdAsyncAllocatorType, ConstCastResultIdBadAllowsZero, ConstCastResultIdArrayChild, ConstCastResultIdSentinelArrays, ConstCastResultIdPtrLens, ConstCastResultIdCV, ConstCastResultIdPtrSentinel, ConstCastResultIdIntShorten, }; struct ConstCastOnly; struct ConstCastArg { size_t arg_index; ZigType *actual_param_type; ZigType *expected_param_type; ConstCastOnly *child; }; struct ConstCastArgNoAlias { size_t arg_index; }; struct ConstCastOptionalMismatch; struct ConstCastPointerMismatch; struct ConstCastSliceMismatch; struct ConstCastErrUnionErrSetMismatch; struct ConstCastErrUnionPayloadMismatch; struct ConstCastErrSetMismatch; struct ConstCastTypeMismatch; struct ConstCastArrayMismatch; struct ConstCastBadAllowsZero; struct ConstCastBadNullTermArrays; struct ConstCastBadCV; struct ConstCastPtrSentinel; struct ConstCastIntShorten; struct ConstCastOnly { ConstCastResultId id; union { ConstCastErrSetMismatch *error_set_mismatch; ConstCastPointerMismatch *pointer_mismatch; ConstCastSliceMismatch *slice_mismatch; ConstCastOptionalMismatch *optional; ConstCastErrUnionPayloadMismatch *error_union_payload; ConstCastErrUnionErrSetMismatch *error_union_error_set; ConstCastTypeMismatch *type_mismatch; ConstCastArrayMismatch *array_mismatch; ConstCastOnly *return_type; ConstCastOnly *null_wrap_ptr_child; ConstCastArg fn_arg; ConstCastArgNoAlias arg_no_alias; ConstCastBadAllowsZero *bad_allows_zero; ConstCastBadNullTermArrays *sentinel_arrays; ConstCastBadCV *bad_cv; ConstCastPtrSentinel *bad_ptr_sentinel; ConstCastIntShorten *int_shorten; } data; }; struct ConstCastTypeMismatch { ZigType *wanted_type; ZigType *actual_type; }; struct ConstCastOptionalMismatch { ConstCastOnly child; ZigType *wanted_child; ZigType *actual_child; }; struct ConstCastPointerMismatch { ConstCastOnly child; ZigType *wanted_child; ZigType *actual_child; }; struct ConstCastSliceMismatch { ConstCastOnly child; ZigType *wanted_child; ZigType *actual_child; }; struct ConstCastArrayMismatch { ConstCastOnly child; ZigType *wanted_child; ZigType *actual_child; }; struct ConstCastErrUnionErrSetMismatch { ConstCastOnly child; ZigType *wanted_err_set; ZigType *actual_err_set; }; struct ConstCastErrUnionPayloadMismatch { ConstCastOnly child; ZigType *wanted_payload; ZigType *actual_payload; }; struct ConstCastErrSetMismatch { ZigList missing_errors; }; struct ConstCastBadAllowsZero { ZigType *wanted_type; ZigType *actual_type; }; struct ConstCastBadNullTermArrays { ConstCastOnly child; ZigType *wanted_type; ZigType *actual_type; }; struct ConstCastBadCV { ZigType *wanted_type; ZigType *actual_type; }; struct ConstCastPtrSentinel { ZigType *wanted_type; ZigType *actual_type; }; struct ConstCastIntShorten { ZigType *wanted_type; ZigType *actual_type; }; // for debugging purposes struct DbgIrBreakPoint { const char *src_file; uint32_t line; }; DbgIrBreakPoint dbg_ir_breakpoints_buf[20]; size_t dbg_ir_breakpoints_count = 0; static IrInstSrc *ir_gen_node(IrBuilderSrc *irb, AstNode *node, Scope *scope); static IrInstSrc *ir_gen_node_extra(IrBuilderSrc *irb, AstNode *node, Scope *scope, LVal lval, ResultLoc *result_loc); static IrInstGen *ir_implicit_cast(IrAnalyze *ira, IrInstGen *value, ZigType *expected_type); static IrInstGen *ir_implicit_cast2(IrAnalyze *ira, IrInst *value_source_instr, IrInstGen *value, ZigType *expected_type); static IrInstGen *ir_get_deref(IrAnalyze *ira, IrInst *source_instr, IrInstGen *ptr, ResultLoc *result_loc); static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutableSrc *exec, AstNode *source_node, Buf *msg); static IrInstGen *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name, IrInst* source_instr, IrInstGen *container_ptr, IrInst *container_ptr_src, ZigType *container_type, bool initializing); static void ir_assert_impl(bool ok, IrInst* source_instruction, const char *file, unsigned int line); static void ir_assert_gen_impl(bool ok, IrInstGen *source_instruction, const char *file, unsigned int line); static IrInstGen *ir_get_var_ptr(IrAnalyze *ira, IrInst *source_instr, ZigVar *var); static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstGen *op); static IrInstSrc *ir_lval_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *value, LVal lval, ResultLoc *result_loc); static IrInstSrc *ir_expr_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *inst, ResultLoc *result_loc); static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align); static ZigType *adjust_ptr_const(CodeGen *g, ZigType *ptr_type, bool is_const); static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align); static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ZigValue *val); static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ZigValue *val); static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, ZigValue *out_val, ZigValue *ptr_val); static IrInstGen *ir_analyze_ptr_cast(IrAnalyze *ira, IrInst* source_instr, IrInstGen *ptr, IrInst *ptr_src, ZigType *dest_type, IrInst *dest_type_src, bool safety_check_on, bool keep_bigger_alignment); static ZigValue *ir_resolve_const(IrAnalyze *ira, IrInstGen *value, UndefAllowed undef_allowed); static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align); static IrInstGen *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInst* source_instr, IrInstGen *target, ZigType *ptr_type); static IrInstGen *ir_analyze_bit_cast(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, ZigType *dest_type); static IrInstGen *ir_resolve_result_raw(IrAnalyze *ira, IrInst *suspend_source_instr, ResultLoc *result_loc, ZigType *value_type, IrInstGen *value, bool force_runtime, bool allow_discard); static IrInstGen *ir_resolve_result(IrAnalyze *ira, IrInst *suspend_source_instr, ResultLoc *result_loc, ZigType *value_type, IrInstGen *value, bool force_runtime, bool allow_discard); static IrInstGen *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInst* source_instr, IrInstGen *base_ptr, bool safety_check_on, bool initializing); static IrInstGen *ir_analyze_unwrap_error_payload(IrAnalyze *ira, IrInst* source_instr, IrInstGen *base_ptr, bool safety_check_on, bool initializing); static IrInstGen *ir_analyze_unwrap_err_code(IrAnalyze *ira, IrInst* source_instr, IrInstGen *base_ptr, bool initializing); static IrInstGen *ir_analyze_store_ptr(IrAnalyze *ira, IrInst* source_instr, IrInstGen *ptr, IrInstGen *uncasted_value, bool allow_write_through_const); static IrInstSrc *ir_gen_union_init_expr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *union_type, IrInstSrc *field_name, AstNode *expr_node, LVal lval, ResultLoc *parent_result_loc); static void ir_reset_result(ResultLoc *result_loc); static Buf *get_anon_type_name(CodeGen *codegen, IrExecutableSrc *exec, const char *kind_name, Scope *scope, AstNode *source_node, Buf *out_bare_name); static ResultLocCast *ir_build_cast_result_loc(IrBuilderSrc *irb, IrInstSrc *dest_type, ResultLoc *parent_result_loc); static IrInstGen *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInst* source_instr, TypeStructField *field, IrInstGen *struct_ptr, ZigType *struct_type, bool initializing); static IrInstGen *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_name, IrInst* source_instr, IrInstGen *container_ptr, ZigType *container_type); static ResultLoc *no_result_loc(void); static IrInstGen *ir_analyze_test_non_null(IrAnalyze *ira, IrInst *source_inst, IrInstGen *value); static IrInstGen *ir_error_dependency_loop(IrAnalyze *ira, IrInst *source_instr); static IrInstGen *ir_const_undef(IrAnalyze *ira, IrInst *source_instruction, ZigType *ty); static ZigVar *ir_create_var(IrBuilderSrc *irb, AstNode *node, Scope *scope, Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *is_comptime); static void build_decl_var_and_init(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var, IrInstSrc *init, const char *name_hint, IrInstSrc *is_comptime); static IrInstGen *ir_analyze_union_init(IrAnalyze *ira, IrInst* source_instruction, AstNode *field_source_node, ZigType *union_type, Buf *field_name, IrInstGen *field_result_loc, IrInstGen *result_loc); static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* source_instr, IrInstGen *struct_operand, TypeStructField *field); static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right); static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right); #define ir_assert(OK, SOURCE_INSTRUCTION) ir_assert_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__) #define ir_assert_gen(OK, SOURCE_INSTRUCTION) ir_assert_gen_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__) static void destroy_instruction_src(IrInstSrc *inst) { switch (inst->id) { case IrInstSrcIdInvalid: zig_unreachable(); case IrInstSrcIdReturn: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdConst: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdBinOp: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdMergeErrSets: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdDeclVar: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdCall: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdCallExtra: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdUnOp: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdCondBr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdBr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdPhi: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdContainerInitList: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdContainerInitFields: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdUnreachable: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdElemPtr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdVarPtr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdLoadPtr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdStorePtr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdTypeOf: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdFieldPtr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdSetCold: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdSetRuntimeSafety: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdSetFloatMode: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdArrayType: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdSliceType: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdAnyFrameType: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdAsm: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdSizeOf: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdTestNonNull: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdOptionalUnwrapPtr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdPopCount: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdClz: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdCtz: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdBswap: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdBitReverse: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdSwitchBr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdSwitchVar: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdSwitchElseVar: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdSwitchTarget: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdImport: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdRef: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdCompileErr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdCompileLog: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdErrName: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdCImport: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdCInclude: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdCDefine: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdCUndef: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdEmbedFile: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdCmpxchg: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdFence: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdTruncate: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdIntCast: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdFloatCast: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdErrSetCast: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdIntToFloat: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdFloatToInt: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdBoolToInt: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdVectorType: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdShuffleVector: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdSplat: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdBoolNot: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdMemset: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdMemcpy: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdSlice: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdBreakpoint: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdReturnAddress: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdFrameAddress: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdFrameHandle: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdFrameType: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdFrameSize: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdAlignOf: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdOverflowOp: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdTestErr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdUnwrapErrCode: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdUnwrapErrPayload: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdFnProto: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdTestComptime: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdPtrCast: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdBitCast: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdPtrToInt: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdIntToPtr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdIntToEnum: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdIntToErr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdErrToInt: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdCheckSwitchProngs: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdCheckStatementIsVoid: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdTypeName: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdTagName: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdPtrType: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdDeclRef: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdPanic: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdFieldParentPtr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdByteOffsetOf: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdBitOffsetOf: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdTypeInfo: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdType: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdHasField: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdSetEvalBranchQuota: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdAlignCast: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdImplicitCast: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdResolveResult: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdResetResult: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdOpaqueType: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdSetAlignStack: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdArgType: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdTagType: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdExport: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdErrorReturnTrace: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdErrorUnion: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdAtomicRmw: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdSaveErrRetAddr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdAddImplicitReturnType: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdFloatOp: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdMulAdd: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdAtomicLoad: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdAtomicStore: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdEnumToInt: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdCheckRuntimeScope: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdHasDecl: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdUndeclaredIdent: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdAlloca: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdEndExpr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdUnionInitNamedField: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdSuspendBegin: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdSuspendFinish: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdResume: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdAwait: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdSpillBegin: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdSpillEnd: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdCallArgs: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdWasmMemorySize: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdWasmMemoryGrow: return heap::c_allocator.destroy(reinterpret_cast(inst)); } zig_unreachable(); } void destroy_instruction_gen(IrInstGen *inst) { switch (inst->id) { case IrInstGenIdInvalid: zig_unreachable(); case IrInstGenIdReturn: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdConst: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdBinOp: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdCast: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdCall: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdCondBr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdBr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdPhi: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdUnreachable: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdElemPtr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdVarPtr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdReturnPtr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdLoadPtr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdStorePtr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdVectorStoreElem: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdStructFieldPtr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdUnionFieldPtr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdAsm: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdTestNonNull: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdOptionalUnwrapPtr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdPopCount: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdClz: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdCtz: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdBswap: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdBitReverse: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdSwitchBr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdUnionTag: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdRef: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdErrName: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdCmpxchg: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdFence: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdTruncate: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdShuffleVector: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdSplat: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdBoolNot: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdMemset: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdMemcpy: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdSlice: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdBreakpoint: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdReturnAddress: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdFrameAddress: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdFrameHandle: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdFrameSize: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdOverflowOp: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdTestErr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdUnwrapErrCode: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdUnwrapErrPayload: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdOptionalWrap: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdErrWrapCode: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdErrWrapPayload: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdPtrCast: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdBitCast: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdWidenOrShorten: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdPtrToInt: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdIntToPtr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdIntToEnum: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdIntToErr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdErrToInt: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdTagName: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdPanic: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdFieldParentPtr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdAlignCast: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdErrorReturnTrace: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdAtomicRmw: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdSaveErrRetAddr: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdFloatOp: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdMulAdd: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdAtomicLoad: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdAtomicStore: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdDeclVar: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdArrayToVector: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdVectorToArray: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdPtrOfArrayToSlice: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdAssertZero: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdAssertNonNull: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdAlloca: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdSuspendBegin: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdSuspendFinish: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdResume: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdAwait: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdSpillBegin: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdSpillEnd: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdVectorExtractElem: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdBinaryNot: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdNegation: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdNegationWrapping: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdWasmMemorySize: return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdWasmMemoryGrow: return heap::c_allocator.destroy(reinterpret_cast(inst)); } zig_unreachable(); } static void ira_ref(IrAnalyze *ira) { ira->ref_count += 1; } static void ira_deref(IrAnalyze *ira) { if (ira->ref_count > 1) { ira->ref_count -= 1; // immediate destruction of dangling IrInstGenConst is not possible // free tracking memory because it will never be used ira->new_irb.constants.deinit(&heap::c_allocator); return; } assert(ira->ref_count != 0); for (size_t bb_i = 0; bb_i < ira->old_irb.exec->basic_block_list.length; bb_i += 1) { IrBasicBlockSrc *pass1_bb = ira->old_irb.exec->basic_block_list.items[bb_i]; for (size_t inst_i = 0; inst_i < pass1_bb->instruction_list.length; inst_i += 1) { IrInstSrc *pass1_inst = pass1_bb->instruction_list.items[inst_i]; destroy_instruction_src(pass1_inst); } heap::c_allocator.destroy(pass1_bb); } ira->old_irb.exec->basic_block_list.deinit(); ira->old_irb.exec->tld_list.deinit(); heap::c_allocator.destroy(ira->old_irb.exec); ira->src_implicit_return_type_list.deinit(); ira->resume_stack.deinit(); // destroy dangling IrInstGenConst for (size_t i = 0; i < ira->new_irb.constants.length; i += 1) { auto constant = ira->new_irb.constants.items[i]; if (constant->base.base.ref_count == 0 && !ir_inst_gen_has_side_effects(&constant->base)) destroy_instruction_gen(&constant->base); } ira->new_irb.constants.deinit(&heap::c_allocator); heap::c_allocator.destroy(ira); } static ZigValue *const_ptr_pointee_unchecked_no_isf(CodeGen *g, ZigValue *const_val) { assert(get_src_ptr_type(const_val->type) != nullptr); assert(const_val->special == ConstValSpecialStatic); switch (type_has_one_possible_value(g, const_val->type->data.pointer.child_type)) { case OnePossibleValueInvalid: return nullptr; case OnePossibleValueYes: return get_the_one_possible_value(g, const_val->type->data.pointer.child_type); case OnePossibleValueNo: break; } ZigValue *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: { ZigValue *array_val = const_val->data.x_ptr.data.base_array.array_val; size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; if (elem_index == array_val->type->data.array.len) { result = array_val->type->data.array.sentinel; } else { expand_undef_array(g, array_val); result = &array_val->data.x_array.data.s_none.elements[elem_index]; } break; } case ConstPtrSpecialSubArray: { ZigValue *array_val = const_val->data.x_ptr.data.base_array.array_val; size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; // TODO handle sentinel terminated arrays expand_undef_array(g, array_val); result = g->pass1_arena->create(); result->special = array_val->special; result->type = get_array_type(g, array_val->type->data.array.child_type, array_val->type->data.array.len - elem_index, nullptr); result->data.x_array.special = ConstArraySpecialNone; result->data.x_array.data.s_none.elements = &array_val->data.x_array.data.s_none.elements[elem_index]; result->parent.id = ConstParentIdArray; result->parent.data.p_array.array_val = array_val; result->parent.data.p_array.elem_index = elem_index; break; } case ConstPtrSpecialBaseStruct: { ZigValue *struct_val = const_val->data.x_ptr.data.base_struct.struct_val; expand_undef_struct(g, struct_val); result = struct_val->data.x_struct.fields[const_val->data.x_ptr.data.base_struct.field_index]; break; } case ConstPtrSpecialBaseErrorUnionCode: result = const_val->data.x_ptr.data.base_err_union_code.err_union_val->data.x_err_union.error_set; break; case ConstPtrSpecialBaseErrorUnionPayload: result = const_val->data.x_ptr.data.base_err_union_payload.err_union_val->data.x_err_union.payload; break; case ConstPtrSpecialBaseOptionalPayload: result = const_val->data.x_ptr.data.base_optional_payload.optional_val->data.x_optional; break; case ConstPtrSpecialNull: result = const_val; break; case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialDiscard: zig_unreachable(); case ConstPtrSpecialFunction: zig_unreachable(); } assert(result != nullptr); return result; } static ZigValue *const_ptr_pointee_unchecked(CodeGen *g, ZigValue *const_val) { assert(get_src_ptr_type(const_val->type) != nullptr); assert(const_val->special == ConstValSpecialStatic); InferredStructField *isf = const_val->type->data.pointer.inferred_struct_field; if (isf != nullptr) { TypeStructField *field = find_struct_type_field(isf->inferred_struct_type, isf->field_name); assert(field != nullptr); if (field->is_comptime) { assert(field->init_val != nullptr); return field->init_val; } ZigValue *struct_val = const_ptr_pointee_unchecked_no_isf(g, const_val); assert(struct_val->type->id == ZigTypeIdStruct); return struct_val->data.x_struct.fields[field->src_index]; } return const_ptr_pointee_unchecked_no_isf(g, const_val); } static bool is_tuple(ZigType *type) { return type->id == ZigTypeIdStruct && type->data.structure.special == StructSpecialInferredTuple; } static bool is_slice(ZigType *type) { return type->id == ZigTypeIdStruct && type->data.structure.special == StructSpecialSlice; } // This function returns true when you can change the type of a ZigValue and the // value remains meaningful. static bool types_have_same_zig_comptime_repr(CodeGen *codegen, ZigType *expected, ZigType *actual) { if (expected == actual) return true; if (get_src_ptr_type(expected) != nullptr && get_src_ptr_type(actual) != nullptr) return true; if (is_opt_err_set(expected) && is_opt_err_set(actual)) return true; if (expected->id != actual->id) return false; switch (expected->id) { case ZigTypeIdInvalid: case ZigTypeIdUnreachable: zig_unreachable(); case ZigTypeIdMetaType: case ZigTypeIdVoid: case ZigTypeIdBool: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdEnumLiteral: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdBoundFn: case ZigTypeIdErrorSet: case ZigTypeIdOpaque: case ZigTypeIdAnyFrame: case ZigTypeIdFn: return true; case ZigTypeIdPointer: return expected->data.pointer.inferred_struct_field == actual->data.pointer.inferred_struct_field; case ZigTypeIdFloat: return expected->data.floating.bit_count == actual->data.floating.bit_count; case ZigTypeIdInt: return expected->data.integral.is_signed == actual->data.integral.is_signed; case ZigTypeIdStruct: return is_slice(expected) && is_slice(actual); case ZigTypeIdOptional: case ZigTypeIdErrorUnion: case ZigTypeIdEnum: case ZigTypeIdUnion: case ZigTypeIdVector: case ZigTypeIdFnFrame: return false; case ZigTypeIdArray: return expected->data.array.len == actual->data.array.len && expected->data.array.child_type == actual->data.array.child_type && (expected->data.array.sentinel == nullptr || (actual->data.array.sentinel != nullptr && const_values_equal(codegen, expected->data.array.sentinel, actual->data.array.sentinel))); } zig_unreachable(); } static bool ir_should_inline(IrExecutableSrc *exec, Scope *scope) { if (exec->is_inline) return true; while (scope != nullptr) { if (scope->id == ScopeIdCompTime) return true; if (scope->id == ScopeIdTypeOf) return false; if (scope->id == ScopeIdFnDef) break; scope = scope->parent; } return false; } static void ir_instruction_append(IrBasicBlockSrc *basic_block, IrInstSrc *instruction) { assert(basic_block); assert(instruction); basic_block->instruction_list.append(instruction); } static void ir_inst_gen_append(IrBasicBlockGen *basic_block, IrInstGen *instruction) { assert(basic_block); assert(instruction); basic_block->instruction_list.append(instruction); } static size_t exec_next_debug_id(IrExecutableSrc *exec) { size_t result = exec->next_debug_id; exec->next_debug_id += 1; return result; } static size_t exec_next_debug_id_gen(IrExecutableGen *exec) { size_t result = exec->next_debug_id; exec->next_debug_id += 1; return result; } static ZigFn *exec_fn_entry(IrExecutableSrc *exec) { return exec->fn_entry; } static Buf *exec_c_import_buf(IrExecutableSrc *exec) { return exec->c_import_buf; } static bool value_is_comptime(ZigValue *const_val) { return const_val->special != ConstValSpecialRuntime; } static bool instr_is_comptime(IrInstGen *instruction) { return value_is_comptime(instruction->value); } static bool instr_is_unreachable(IrInstSrc *instruction) { return instruction->is_noreturn; } static void ir_ref_bb(IrBasicBlockSrc *bb) { bb->ref_count += 1; } static void ir_ref_instruction(IrInstSrc *instruction, IrBasicBlockSrc *cur_bb) { assert(instruction->id != IrInstSrcIdInvalid); instruction->base.ref_count += 1; if (instruction->owner_bb != cur_bb && !instr_is_unreachable(instruction) && instruction->id != IrInstSrcIdConst) { ir_ref_bb(instruction->owner_bb); } } static void ir_ref_inst_gen(IrInstGen *instruction) { assert(instruction->id != IrInstGenIdInvalid); instruction->base.ref_count += 1; } static void ir_ref_var(ZigVar *var) { var->ref_count += 1; } static void create_result_ptr(CodeGen *codegen, ZigType *expected_type, ZigValue **out_result, ZigValue **out_result_ptr) { ZigValue *result = codegen->pass1_arena->create(); ZigValue *result_ptr = codegen->pass1_arena->create(); result->special = ConstValSpecialUndef; result->type = expected_type; result_ptr->special = ConstValSpecialStatic; result_ptr->type = get_pointer_to_type(codegen, result->type, false); result_ptr->data.x_ptr.mut = ConstPtrMutComptimeVar; result_ptr->data.x_ptr.special = ConstPtrSpecialRef; result_ptr->data.x_ptr.data.ref.pointee = result; *out_result = result; *out_result_ptr = result_ptr; } ZigType *ir_analyze_type_expr(IrAnalyze *ira, Scope *scope, AstNode *node) { Error err; ZigValue *result; ZigValue *result_ptr; create_result_ptr(ira->codegen, ira->codegen->builtin_types.entry_type, &result, &result_ptr); if ((err = ir_eval_const_value(ira->codegen, scope, node, result_ptr, ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, nullptr, node, nullptr, ira->new_irb.exec, nullptr, UndefBad))) { return ira->codegen->builtin_types.entry_invalid; } if (type_is_invalid(result->type)) return ira->codegen->builtin_types.entry_invalid; assert(result->special != ConstValSpecialRuntime); ZigType *res_type = result->data.x_type; return res_type; } static IrBasicBlockSrc *ir_create_basic_block(IrBuilderSrc *irb, Scope *scope, const char *name_hint) { IrBasicBlockSrc *result = heap::c_allocator.create(); result->scope = scope; result->name_hint = name_hint; result->debug_id = exec_next_debug_id(irb->exec); result->index = UINT32_MAX; // set later return result; } static IrBasicBlockGen *ir_create_basic_block_gen(IrAnalyze *ira, Scope *scope, const char *name_hint) { IrBasicBlockGen *result = heap::c_allocator.create(); result->scope = scope; result->name_hint = name_hint; result->debug_id = exec_next_debug_id_gen(ira->new_irb.exec); return result; } static IrBasicBlockGen *ir_build_bb_from(IrAnalyze *ira, IrBasicBlockSrc *other_bb) { IrBasicBlockGen *new_bb = ir_create_basic_block_gen(ira, other_bb->scope, other_bb->name_hint); other_bb->child = new_bb; return new_bb; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcDeclVar *) { return IrInstSrcIdDeclVar; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcBr *) { return IrInstSrcIdBr; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcCondBr *) { return IrInstSrcIdCondBr; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchBr *) { return IrInstSrcIdSwitchBr; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchVar *) { return IrInstSrcIdSwitchVar; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchElseVar *) { return IrInstSrcIdSwitchElseVar; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchTarget *) { return IrInstSrcIdSwitchTarget; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcPhi *) { return IrInstSrcIdPhi; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnOp *) { return IrInstSrcIdUnOp; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcBinOp *) { return IrInstSrcIdBinOp; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcMergeErrSets *) { return IrInstSrcIdMergeErrSets; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcLoadPtr *) { return IrInstSrcIdLoadPtr; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcStorePtr *) { return IrInstSrcIdStorePtr; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcFieldPtr *) { return IrInstSrcIdFieldPtr; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcElemPtr *) { return IrInstSrcIdElemPtr; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcVarPtr *) { return IrInstSrcIdVarPtr; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcCall *) { return IrInstSrcIdCall; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcCallArgs *) { return IrInstSrcIdCallArgs; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcCallExtra *) { return IrInstSrcIdCallExtra; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcConst *) { return IrInstSrcIdConst; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcReturn *) { return IrInstSrcIdReturn; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcContainerInitList *) { return IrInstSrcIdContainerInitList; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcContainerInitFields *) { return IrInstSrcIdContainerInitFields; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnreachable *) { return IrInstSrcIdUnreachable; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcTypeOf *) { return IrInstSrcIdTypeOf; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetCold *) { return IrInstSrcIdSetCold; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetRuntimeSafety *) { return IrInstSrcIdSetRuntimeSafety; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetFloatMode *) { return IrInstSrcIdSetFloatMode; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcArrayType *) { return IrInstSrcIdArrayType; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcAnyFrameType *) { return IrInstSrcIdAnyFrameType; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcSliceType *) { return IrInstSrcIdSliceType; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcAsm *) { return IrInstSrcIdAsm; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcSizeOf *) { return IrInstSrcIdSizeOf; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcTestNonNull *) { return IrInstSrcIdTestNonNull; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcOptionalUnwrapPtr *) { return IrInstSrcIdOptionalUnwrapPtr; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcClz *) { return IrInstSrcIdClz; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcCtz *) { return IrInstSrcIdCtz; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcPopCount *) { return IrInstSrcIdPopCount; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcBswap *) { return IrInstSrcIdBswap; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcBitReverse *) { return IrInstSrcIdBitReverse; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcImport *) { return IrInstSrcIdImport; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcCImport *) { return IrInstSrcIdCImport; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcCInclude *) { return IrInstSrcIdCInclude; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcCDefine *) { return IrInstSrcIdCDefine; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcCUndef *) { return IrInstSrcIdCUndef; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcRef *) { return IrInstSrcIdRef; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcCompileErr *) { return IrInstSrcIdCompileErr; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcCompileLog *) { return IrInstSrcIdCompileLog; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrName *) { return IrInstSrcIdErrName; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcEmbedFile *) { return IrInstSrcIdEmbedFile; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcCmpxchg *) { return IrInstSrcIdCmpxchg; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcFence *) { return IrInstSrcIdFence; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcTruncate *) { return IrInstSrcIdTruncate; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntCast *) { return IrInstSrcIdIntCast; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcFloatCast *) { return IrInstSrcIdFloatCast; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToFloat *) { return IrInstSrcIdIntToFloat; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcFloatToInt *) { return IrInstSrcIdFloatToInt; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcBoolToInt *) { return IrInstSrcIdBoolToInt; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcVectorType *) { return IrInstSrcIdVectorType; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcShuffleVector *) { return IrInstSrcIdShuffleVector; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcSplat *) { return IrInstSrcIdSplat; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcBoolNot *) { return IrInstSrcIdBoolNot; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcMemset *) { return IrInstSrcIdMemset; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcMemcpy *) { return IrInstSrcIdMemcpy; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcSlice *) { return IrInstSrcIdSlice; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcBreakpoint *) { return IrInstSrcIdBreakpoint; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcReturnAddress *) { return IrInstSrcIdReturnAddress; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameAddress *) { return IrInstSrcIdFrameAddress; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameHandle *) { return IrInstSrcIdFrameHandle; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameType *) { return IrInstSrcIdFrameType; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameSize *) { return IrInstSrcIdFrameSize; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcAlignOf *) { return IrInstSrcIdAlignOf; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcOverflowOp *) { return IrInstSrcIdOverflowOp; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcTestErr *) { return IrInstSrcIdTestErr; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcMulAdd *) { return IrInstSrcIdMulAdd; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcFloatOp *) { return IrInstSrcIdFloatOp; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnwrapErrCode *) { return IrInstSrcIdUnwrapErrCode; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnwrapErrPayload *) { return IrInstSrcIdUnwrapErrPayload; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcFnProto *) { return IrInstSrcIdFnProto; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcTestComptime *) { return IrInstSrcIdTestComptime; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcPtrCast *) { return IrInstSrcIdPtrCast; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcBitCast *) { return IrInstSrcIdBitCast; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToPtr *) { return IrInstSrcIdIntToPtr; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcPtrToInt *) { return IrInstSrcIdPtrToInt; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToEnum *) { return IrInstSrcIdIntToEnum; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcEnumToInt *) { return IrInstSrcIdEnumToInt; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToErr *) { return IrInstSrcIdIntToErr; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrToInt *) { return IrInstSrcIdErrToInt; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckSwitchProngs *) { return IrInstSrcIdCheckSwitchProngs; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckStatementIsVoid *) { return IrInstSrcIdCheckStatementIsVoid; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcTypeName *) { return IrInstSrcIdTypeName; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcDeclRef *) { return IrInstSrcIdDeclRef; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcPanic *) { return IrInstSrcIdPanic; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcTagName *) { return IrInstSrcIdTagName; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcTagType *) { return IrInstSrcIdTagType; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcFieldParentPtr *) { return IrInstSrcIdFieldParentPtr; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcByteOffsetOf *) { return IrInstSrcIdByteOffsetOf; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcBitOffsetOf *) { return IrInstSrcIdBitOffsetOf; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcTypeInfo *) { return IrInstSrcIdTypeInfo; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcType *) { return IrInstSrcIdType; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcHasField *) { return IrInstSrcIdHasField; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetEvalBranchQuota *) { return IrInstSrcIdSetEvalBranchQuota; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcPtrType *) { return IrInstSrcIdPtrType; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcAlignCast *) { return IrInstSrcIdAlignCast; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcImplicitCast *) { return IrInstSrcIdImplicitCast; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcResolveResult *) { return IrInstSrcIdResolveResult; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcResetResult *) { return IrInstSrcIdResetResult; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcOpaqueType *) { return IrInstSrcIdOpaqueType; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetAlignStack *) { return IrInstSrcIdSetAlignStack; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcArgType *) { return IrInstSrcIdArgType; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcExport *) { return IrInstSrcIdExport; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrorReturnTrace *) { return IrInstSrcIdErrorReturnTrace; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrorUnion *) { return IrInstSrcIdErrorUnion; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcAtomicRmw *) { return IrInstSrcIdAtomicRmw; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcAtomicLoad *) { return IrInstSrcIdAtomicLoad; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcAtomicStore *) { return IrInstSrcIdAtomicStore; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcSaveErrRetAddr *) { return IrInstSrcIdSaveErrRetAddr; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcAddImplicitReturnType *) { return IrInstSrcIdAddImplicitReturnType; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrSetCast *) { return IrInstSrcIdErrSetCast; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckRuntimeScope *) { return IrInstSrcIdCheckRuntimeScope; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcHasDecl *) { return IrInstSrcIdHasDecl; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcUndeclaredIdent *) { return IrInstSrcIdUndeclaredIdent; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcAlloca *) { return IrInstSrcIdAlloca; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcEndExpr *) { return IrInstSrcIdEndExpr; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnionInitNamedField *) { return IrInstSrcIdUnionInitNamedField; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcSuspendBegin *) { return IrInstSrcIdSuspendBegin; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcSuspendFinish *) { return IrInstSrcIdSuspendFinish; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcAwait *) { return IrInstSrcIdAwait; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcResume *) { return IrInstSrcIdResume; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcSpillBegin *) { return IrInstSrcIdSpillBegin; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcSpillEnd *) { return IrInstSrcIdSpillEnd; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemorySize *) { return IrInstSrcIdWasmMemorySize; } static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemoryGrow *) { return IrInstSrcIdWasmMemoryGrow; } static constexpr IrInstGenId ir_inst_id(IrInstGenDeclVar *) { return IrInstGenIdDeclVar; } static constexpr IrInstGenId ir_inst_id(IrInstGenBr *) { return IrInstGenIdBr; } static constexpr IrInstGenId ir_inst_id(IrInstGenCondBr *) { return IrInstGenIdCondBr; } static constexpr IrInstGenId ir_inst_id(IrInstGenSwitchBr *) { return IrInstGenIdSwitchBr; } static constexpr IrInstGenId ir_inst_id(IrInstGenPhi *) { return IrInstGenIdPhi; } static constexpr IrInstGenId ir_inst_id(IrInstGenBinaryNot *) { return IrInstGenIdBinaryNot; } static constexpr IrInstGenId ir_inst_id(IrInstGenNegation *) { return IrInstGenIdNegation; } static constexpr IrInstGenId ir_inst_id(IrInstGenNegationWrapping *) { return IrInstGenIdNegationWrapping; } static constexpr IrInstGenId ir_inst_id(IrInstGenBinOp *) { return IrInstGenIdBinOp; } static constexpr IrInstGenId ir_inst_id(IrInstGenLoadPtr *) { return IrInstGenIdLoadPtr; } static constexpr IrInstGenId ir_inst_id(IrInstGenStorePtr *) { return IrInstGenIdStorePtr; } static constexpr IrInstGenId ir_inst_id(IrInstGenVectorStoreElem *) { return IrInstGenIdVectorStoreElem; } static constexpr IrInstGenId ir_inst_id(IrInstGenStructFieldPtr *) { return IrInstGenIdStructFieldPtr; } static constexpr IrInstGenId ir_inst_id(IrInstGenUnionFieldPtr *) { return IrInstGenIdUnionFieldPtr; } static constexpr IrInstGenId ir_inst_id(IrInstGenElemPtr *) { return IrInstGenIdElemPtr; } static constexpr IrInstGenId ir_inst_id(IrInstGenVarPtr *) { return IrInstGenIdVarPtr; } static constexpr IrInstGenId ir_inst_id(IrInstGenReturnPtr *) { return IrInstGenIdReturnPtr; } static constexpr IrInstGenId ir_inst_id(IrInstGenCall *) { return IrInstGenIdCall; } static constexpr IrInstGenId ir_inst_id(IrInstGenReturn *) { return IrInstGenIdReturn; } static constexpr IrInstGenId ir_inst_id(IrInstGenCast *) { return IrInstGenIdCast; } static constexpr IrInstGenId ir_inst_id(IrInstGenUnreachable *) { return IrInstGenIdUnreachable; } static constexpr IrInstGenId ir_inst_id(IrInstGenAsm *) { return IrInstGenIdAsm; } static constexpr IrInstGenId ir_inst_id(IrInstGenTestNonNull *) { return IrInstGenIdTestNonNull; } static constexpr IrInstGenId ir_inst_id(IrInstGenOptionalUnwrapPtr *) { return IrInstGenIdOptionalUnwrapPtr; } static constexpr IrInstGenId ir_inst_id(IrInstGenOptionalWrap *) { return IrInstGenIdOptionalWrap; } static constexpr IrInstGenId ir_inst_id(IrInstGenUnionTag *) { return IrInstGenIdUnionTag; } static constexpr IrInstGenId ir_inst_id(IrInstGenClz *) { return IrInstGenIdClz; } static constexpr IrInstGenId ir_inst_id(IrInstGenCtz *) { return IrInstGenIdCtz; } static constexpr IrInstGenId ir_inst_id(IrInstGenPopCount *) { return IrInstGenIdPopCount; } static constexpr IrInstGenId ir_inst_id(IrInstGenBswap *) { return IrInstGenIdBswap; } static constexpr IrInstGenId ir_inst_id(IrInstGenBitReverse *) { return IrInstGenIdBitReverse; } static constexpr IrInstGenId ir_inst_id(IrInstGenRef *) { return IrInstGenIdRef; } static constexpr IrInstGenId ir_inst_id(IrInstGenErrName *) { return IrInstGenIdErrName; } static constexpr IrInstGenId ir_inst_id(IrInstGenCmpxchg *) { return IrInstGenIdCmpxchg; } static constexpr IrInstGenId ir_inst_id(IrInstGenFence *) { return IrInstGenIdFence; } static constexpr IrInstGenId ir_inst_id(IrInstGenTruncate *) { return IrInstGenIdTruncate; } static constexpr IrInstGenId ir_inst_id(IrInstGenShuffleVector *) { return IrInstGenIdShuffleVector; } static constexpr IrInstGenId ir_inst_id(IrInstGenSplat *) { return IrInstGenIdSplat; } static constexpr IrInstGenId ir_inst_id(IrInstGenBoolNot *) { return IrInstGenIdBoolNot; } static constexpr IrInstGenId ir_inst_id(IrInstGenMemset *) { return IrInstGenIdMemset; } static constexpr IrInstGenId ir_inst_id(IrInstGenMemcpy *) { return IrInstGenIdMemcpy; } static constexpr IrInstGenId ir_inst_id(IrInstGenSlice *) { return IrInstGenIdSlice; } static constexpr IrInstGenId ir_inst_id(IrInstGenBreakpoint *) { return IrInstGenIdBreakpoint; } static constexpr IrInstGenId ir_inst_id(IrInstGenReturnAddress *) { return IrInstGenIdReturnAddress; } static constexpr IrInstGenId ir_inst_id(IrInstGenFrameAddress *) { return IrInstGenIdFrameAddress; } static constexpr IrInstGenId ir_inst_id(IrInstGenFrameHandle *) { return IrInstGenIdFrameHandle; } static constexpr IrInstGenId ir_inst_id(IrInstGenFrameSize *) { return IrInstGenIdFrameSize; } static constexpr IrInstGenId ir_inst_id(IrInstGenOverflowOp *) { return IrInstGenIdOverflowOp; } static constexpr IrInstGenId ir_inst_id(IrInstGenTestErr *) { return IrInstGenIdTestErr; } static constexpr IrInstGenId ir_inst_id(IrInstGenMulAdd *) { return IrInstGenIdMulAdd; } static constexpr IrInstGenId ir_inst_id(IrInstGenFloatOp *) { return IrInstGenIdFloatOp; } static constexpr IrInstGenId ir_inst_id(IrInstGenUnwrapErrCode *) { return IrInstGenIdUnwrapErrCode; } static constexpr IrInstGenId ir_inst_id(IrInstGenUnwrapErrPayload *) { return IrInstGenIdUnwrapErrPayload; } static constexpr IrInstGenId ir_inst_id(IrInstGenErrWrapCode *) { return IrInstGenIdErrWrapCode; } static constexpr IrInstGenId ir_inst_id(IrInstGenErrWrapPayload *) { return IrInstGenIdErrWrapPayload; } static constexpr IrInstGenId ir_inst_id(IrInstGenPtrCast *) { return IrInstGenIdPtrCast; } static constexpr IrInstGenId ir_inst_id(IrInstGenBitCast *) { return IrInstGenIdBitCast; } static constexpr IrInstGenId ir_inst_id(IrInstGenWidenOrShorten *) { return IrInstGenIdWidenOrShorten; } static constexpr IrInstGenId ir_inst_id(IrInstGenIntToPtr *) { return IrInstGenIdIntToPtr; } static constexpr IrInstGenId ir_inst_id(IrInstGenPtrToInt *) { return IrInstGenIdPtrToInt; } static constexpr IrInstGenId ir_inst_id(IrInstGenIntToEnum *) { return IrInstGenIdIntToEnum; } static constexpr IrInstGenId ir_inst_id(IrInstGenIntToErr *) { return IrInstGenIdIntToErr; } static constexpr IrInstGenId ir_inst_id(IrInstGenErrToInt *) { return IrInstGenIdErrToInt; } static constexpr IrInstGenId ir_inst_id(IrInstGenPanic *) { return IrInstGenIdPanic; } static constexpr IrInstGenId ir_inst_id(IrInstGenTagName *) { return IrInstGenIdTagName; } static constexpr IrInstGenId ir_inst_id(IrInstGenFieldParentPtr *) { return IrInstGenIdFieldParentPtr; } static constexpr IrInstGenId ir_inst_id(IrInstGenAlignCast *) { return IrInstGenIdAlignCast; } static constexpr IrInstGenId ir_inst_id(IrInstGenErrorReturnTrace *) { return IrInstGenIdErrorReturnTrace; } static constexpr IrInstGenId ir_inst_id(IrInstGenAtomicRmw *) { return IrInstGenIdAtomicRmw; } static constexpr IrInstGenId ir_inst_id(IrInstGenAtomicLoad *) { return IrInstGenIdAtomicLoad; } static constexpr IrInstGenId ir_inst_id(IrInstGenAtomicStore *) { return IrInstGenIdAtomicStore; } static constexpr IrInstGenId ir_inst_id(IrInstGenSaveErrRetAddr *) { return IrInstGenIdSaveErrRetAddr; } static constexpr IrInstGenId ir_inst_id(IrInstGenVectorToArray *) { return IrInstGenIdVectorToArray; } static constexpr IrInstGenId ir_inst_id(IrInstGenArrayToVector *) { return IrInstGenIdArrayToVector; } static constexpr IrInstGenId ir_inst_id(IrInstGenAssertZero *) { return IrInstGenIdAssertZero; } static constexpr IrInstGenId ir_inst_id(IrInstGenAssertNonNull *) { return IrInstGenIdAssertNonNull; } static constexpr IrInstGenId ir_inst_id(IrInstGenPtrOfArrayToSlice *) { return IrInstGenIdPtrOfArrayToSlice; } static constexpr IrInstGenId ir_inst_id(IrInstGenSuspendBegin *) { return IrInstGenIdSuspendBegin; } static constexpr IrInstGenId ir_inst_id(IrInstGenSuspendFinish *) { return IrInstGenIdSuspendFinish; } static constexpr IrInstGenId ir_inst_id(IrInstGenAwait *) { return IrInstGenIdAwait; } static constexpr IrInstGenId ir_inst_id(IrInstGenResume *) { return IrInstGenIdResume; } static constexpr IrInstGenId ir_inst_id(IrInstGenSpillBegin *) { return IrInstGenIdSpillBegin; } static constexpr IrInstGenId ir_inst_id(IrInstGenSpillEnd *) { return IrInstGenIdSpillEnd; } static constexpr IrInstGenId ir_inst_id(IrInstGenVectorExtractElem *) { return IrInstGenIdVectorExtractElem; } static constexpr IrInstGenId ir_inst_id(IrInstGenAlloca *) { return IrInstGenIdAlloca; } static constexpr IrInstGenId ir_inst_id(IrInstGenConst *) { return IrInstGenIdConst; } static constexpr IrInstGenId ir_inst_id(IrInstGenWasmMemorySize *) { return IrInstGenIdWasmMemorySize; } static constexpr IrInstGenId ir_inst_id(IrInstGenWasmMemoryGrow *) { return IrInstGenIdWasmMemoryGrow; } template static T *ir_create_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { T *special_instruction = heap::c_allocator.create(); special_instruction->base.id = ir_inst_id(special_instruction); special_instruction->base.base.scope = scope; special_instruction->base.base.source_node = source_node; special_instruction->base.base.debug_id = exec_next_debug_id(irb->exec); special_instruction->base.owner_bb = irb->current_basic_block; return special_instruction; } template static T *ir_create_inst_gen(IrBuilderGen *irb, Scope *scope, AstNode *source_node) { T *special_instruction = heap::c_allocator.create(); special_instruction->base.id = ir_inst_id(special_instruction); special_instruction->base.base.scope = scope; special_instruction->base.base.source_node = source_node; special_instruction->base.base.debug_id = exec_next_debug_id_gen(irb->exec); special_instruction->base.owner_bb = irb->current_basic_block; special_instruction->base.value = irb->codegen->pass1_arena->create(); return special_instruction; } template static T *ir_create_inst_noval(IrBuilderGen *irb, Scope *scope, AstNode *source_node) { T *special_instruction = heap::c_allocator.create(); special_instruction->base.id = ir_inst_id(special_instruction); special_instruction->base.base.scope = scope; special_instruction->base.base.source_node = source_node; special_instruction->base.base.debug_id = exec_next_debug_id_gen(irb->exec); special_instruction->base.owner_bb = irb->current_basic_block; return special_instruction; } template static T *ir_build_instruction(IrBuilderSrc *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; } template static T *ir_build_inst_gen(IrBuilderGen *irb, Scope *scope, AstNode *source_node) { T *special_instruction = ir_create_inst_gen(irb, scope, source_node); ir_inst_gen_append(irb->current_basic_block, &special_instruction->base); return special_instruction; } template static T *ir_build_inst_noreturn(IrBuilderGen *irb, Scope *scope, AstNode *source_node) { T *special_instruction = ir_create_inst_noval(irb, scope, source_node); special_instruction->base.value = irb->codegen->intern.for_unreachable(); ir_inst_gen_append(irb->current_basic_block, &special_instruction->base); return special_instruction; } template static T *ir_build_inst_void(IrBuilderGen *irb, Scope *scope, AstNode *source_node) { T *special_instruction = ir_create_inst_noval(irb, scope, source_node); special_instruction->base.value = irb->codegen->intern.for_void(); ir_inst_gen_append(irb->current_basic_block, &special_instruction->base); return special_instruction; } IrInstGen *ir_create_alloca(CodeGen *g, Scope *scope, AstNode *source_node, ZigFn *fn, ZigType *var_type, const char *name_hint) { IrInstGenAlloca *alloca_gen = heap::c_allocator.create(); alloca_gen->base.id = IrInstGenIdAlloca; alloca_gen->base.base.source_node = source_node; alloca_gen->base.base.scope = scope; alloca_gen->base.value = g->pass1_arena->create(); alloca_gen->base.value->type = get_pointer_to_type(g, var_type, false); alloca_gen->base.base.ref_count = 1; alloca_gen->name_hint = name_hint; fn->alloca_gen_list.append(alloca_gen); return &alloca_gen->base; } static IrInstGen *ir_build_cast(IrAnalyze *ira, IrInst *source_instr,ZigType *dest_type, IrInstGen *value, CastOp cast_op) { IrInstGenCast *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); inst->base.value->type = dest_type; inst->value = value; inst->cast_op = cast_op; ir_ref_inst_gen(value); return &inst->base; } static IrInstSrc *ir_build_cond_br(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *condition, IrBasicBlockSrc *then_block, IrBasicBlockSrc *else_block, IrInstSrc *is_comptime) { IrInstSrcCondBr *inst = ir_build_instruction(irb, scope, source_node); inst->base.is_noreturn = true; inst->condition = condition; inst->then_block = then_block; inst->else_block = else_block; inst->is_comptime = is_comptime; ir_ref_instruction(condition, irb->current_basic_block); ir_ref_bb(then_block); ir_ref_bb(else_block); if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block); return &inst->base; } static IrInstGen *ir_build_cond_br_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *condition, IrBasicBlockGen *then_block, IrBasicBlockGen *else_block) { IrInstGenCondBr *inst = ir_build_inst_noreturn(&ira->new_irb, source_instr->scope, source_instr->source_node); inst->condition = condition; inst->then_block = then_block; inst->else_block = else_block; ir_ref_inst_gen(condition); return &inst->base; } static IrInstSrc *ir_build_return_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *operand) { IrInstSrcReturn *inst = ir_build_instruction(irb, scope, source_node); inst->base.is_noreturn = true; inst->operand = operand; if (operand != nullptr) ir_ref_instruction(operand, irb->current_basic_block); return &inst->base; } static IrInstGen *ir_build_return_gen(IrAnalyze *ira, IrInst *source_inst, IrInstGen *operand) { IrInstGenReturn *inst = ir_build_inst_noreturn(&ira->new_irb, source_inst->scope, source_inst->source_node); inst->operand = operand; if (operand != nullptr) ir_ref_inst_gen(operand); return &inst->base; } static IrInstSrc *ir_build_const_void(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { IrInstSrcConst *const_instruction = ir_create_instruction(irb, scope, source_node); ir_instruction_append(irb->current_basic_block, &const_instruction->base); const_instruction->value = irb->codegen->intern.for_void(); return &const_instruction->base; } static IrInstSrc *ir_build_const_undefined(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { IrInstSrcConst *const_instruction = ir_create_instruction(irb, scope, source_node); ir_instruction_append(irb->current_basic_block, &const_instruction->base); const_instruction->value = irb->codegen->intern.for_undefined(); return &const_instruction->base; } static IrInstSrc *ir_build_const_uint(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, uint64_t value) { IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->value = irb->codegen->pass1_arena->create(); const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_int; const_instruction->value->special = ConstValSpecialStatic; bigint_init_unsigned(&const_instruction->value->data.x_bigint, value); return &const_instruction->base; } static IrInstSrc *ir_build_const_bigint(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, BigInt *bigint) { IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->value = irb->codegen->pass1_arena->create(); const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_int; const_instruction->value->special = ConstValSpecialStatic; bigint_init_bigint(&const_instruction->value->data.x_bigint, bigint); return &const_instruction->base; } static IrInstSrc *ir_build_const_bigfloat(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, BigFloat *bigfloat) { IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->value = irb->codegen->pass1_arena->create(); const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_float; const_instruction->value->special = ConstValSpecialStatic; bigfloat_init_bigfloat(&const_instruction->value->data.x_bigfloat, bigfloat); return &const_instruction->base; } static IrInstSrc *ir_build_const_null(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { IrInstSrcConst *const_instruction = ir_create_instruction(irb, scope, source_node); ir_instruction_append(irb->current_basic_block, &const_instruction->base); const_instruction->value = irb->codegen->intern.for_null(); return &const_instruction->base; } static IrInstSrc *ir_build_const_usize(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, uint64_t value) { IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->value = irb->codegen->pass1_arena->create(); const_instruction->value->type = irb->codegen->builtin_types.entry_usize; const_instruction->value->special = ConstValSpecialStatic; bigint_init_unsigned(&const_instruction->value->data.x_bigint, value); return &const_instruction->base; } static IrInstSrc *ir_create_const_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigType *type_entry) { IrInstSrcConst *const_instruction = ir_create_instruction(irb, scope, source_node); const_instruction->value = irb->codegen->pass1_arena->create(); const_instruction->value->type = irb->codegen->builtin_types.entry_type; const_instruction->value->special = ConstValSpecialStatic; const_instruction->value->data.x_type = type_entry; return &const_instruction->base; } static IrInstSrc *ir_build_const_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigType *type_entry) { IrInstSrc *instruction = ir_create_const_type(irb, scope, source_node, type_entry); ir_instruction_append(irb->current_basic_block, instruction); return instruction; } static IrInstSrc *ir_build_const_import(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigType *import) { IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->value = irb->codegen->pass1_arena->create(); const_instruction->value->type = irb->codegen->builtin_types.entry_type; const_instruction->value->special = ConstValSpecialStatic; const_instruction->value->data.x_type = import; return &const_instruction->base; } static IrInstSrc *ir_build_const_bool(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, bool value) { IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->value = irb->codegen->pass1_arena->create(); const_instruction->value->type = irb->codegen->builtin_types.entry_bool; const_instruction->value->special = ConstValSpecialStatic; const_instruction->value->data.x_bool = value; return &const_instruction->base; } static IrInstSrc *ir_build_const_enum_literal(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *name) { IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->value = irb->codegen->pass1_arena->create(); const_instruction->value->type = irb->codegen->builtin_types.entry_enum_literal; const_instruction->value->special = ConstValSpecialStatic; const_instruction->value->data.x_enum_literal = name; return &const_instruction->base; } static IrInstSrc *ir_create_const_str_lit(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *str) { IrInstSrcConst *const_instruction = ir_create_instruction(irb, scope, source_node); const_instruction->value = irb->codegen->pass1_arena->create(); init_const_str_lit(irb->codegen, const_instruction->value, str); return &const_instruction->base; } static IrInstSrc *ir_build_const_str_lit(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *str) { IrInstSrc *instruction = ir_create_const_str_lit(irb, scope, source_node, str); ir_instruction_append(irb->current_basic_block, instruction); return instruction; } static IrInstSrc *ir_build_bin_op(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrBinOp op_id, IrInstSrc *op1, IrInstSrc *op2, bool safety_check_on) { IrInstSrcBinOp *inst = ir_build_instruction(irb, scope, source_node); inst->op_id = op_id; inst->op1 = op1; inst->op2 = op2; inst->safety_check_on = safety_check_on; ir_ref_instruction(op1, irb->current_basic_block); ir_ref_instruction(op2, irb->current_basic_block); return &inst->base; } static IrInstGen *ir_build_bin_op_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *res_type, IrBinOp op_id, IrInstGen *op1, IrInstGen *op2, bool safety_check_on) { IrInstGenBinOp *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); inst->base.value->type = res_type; inst->op_id = op_id; inst->op1 = op1; inst->op2 = op2; inst->safety_check_on = safety_check_on; ir_ref_inst_gen(op1); ir_ref_inst_gen(op2); return &inst->base; } static IrInstSrc *ir_build_merge_err_sets(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *op1, IrInstSrc *op2, Buf *type_name) { IrInstSrcMergeErrSets *inst = ir_build_instruction(irb, scope, source_node); inst->op1 = op1; inst->op2 = op2; inst->type_name = type_name; ir_ref_instruction(op1, irb->current_basic_block); ir_ref_instruction(op2, irb->current_basic_block); return &inst->base; } static IrInstSrc *ir_build_var_ptr_x(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var, ScopeFnDef *crossed_fndef_scope) { IrInstSrcVarPtr *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 IrInstSrc *ir_build_var_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var) { return ir_build_var_ptr_x(irb, scope, source_node, var, nullptr); } static IrInstGen *ir_build_var_ptr_gen(IrAnalyze *ira, IrInst *source_instr, ZigVar *var) { IrInstGenVarPtr *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->var = var; ir_ref_var(var); return &instruction->base; } static IrInstGen *ir_build_return_ptr(IrAnalyze *ira, Scope *scope, AstNode *source_node, ZigType *ty) { IrInstGenReturnPtr *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); instruction->base.value->type = ty; return &instruction->base; } static IrInstSrc *ir_build_elem_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *array_ptr, IrInstSrc *elem_index, bool safety_check_on, PtrLen ptr_len, AstNode *init_array_type_source_node) { IrInstSrcElemPtr *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; instruction->init_array_type_source_node = init_array_type_source_node; ir_ref_instruction(array_ptr, irb->current_basic_block); ir_ref_instruction(elem_index, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_elem_ptr_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *array_ptr, IrInstGen *elem_index, bool safety_check_on, ZigType *return_type) { IrInstGenElemPtr *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); instruction->base.value->type = return_type; instruction->array_ptr = array_ptr; instruction->elem_index = elem_index; instruction->safety_check_on = safety_check_on; ir_ref_inst_gen(array_ptr); ir_ref_inst_gen(elem_index); return &instruction->base; } static IrInstSrc *ir_build_field_ptr_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *container_ptr, IrInstSrc *field_name_expr, bool initializing) { IrInstSrcFieldPtr *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; instruction->initializing = initializing; ir_ref_instruction(container_ptr, irb->current_basic_block); ir_ref_instruction(field_name_expr, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_field_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *container_ptr, Buf *field_name, bool initializing) { IrInstSrcFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->container_ptr = container_ptr; instruction->field_name_buffer = field_name; instruction->field_name_expr = nullptr; instruction->initializing = initializing; ir_ref_instruction(container_ptr, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_has_field(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *container_type, IrInstSrc *field_name) { IrInstSrcHasField *instruction = ir_build_instruction(irb, scope, source_node); instruction->container_type = container_type; instruction->field_name = field_name; ir_ref_instruction(container_type, irb->current_basic_block); ir_ref_instruction(field_name, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_struct_field_ptr(IrAnalyze *ira, IrInst *source_instr, IrInstGen *struct_ptr, TypeStructField *field, ZigType *ptr_type) { IrInstGenStructFieldPtr *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); inst->base.value->type = ptr_type; inst->struct_ptr = struct_ptr; inst->field = field; ir_ref_inst_gen(struct_ptr); return &inst->base; } static IrInstGen *ir_build_union_field_ptr(IrAnalyze *ira, IrInst *source_instr, IrInstGen *union_ptr, TypeUnionField *field, bool safety_check_on, bool initializing, ZigType *ptr_type) { IrInstGenUnionFieldPtr *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); inst->base.value->type = ptr_type; inst->initializing = initializing; inst->safety_check_on = safety_check_on; inst->union_ptr = union_ptr; inst->field = field; ir_ref_inst_gen(union_ptr); return &inst->base; } static IrInstSrc *ir_build_call_extra(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *options, IrInstSrc *fn_ref, IrInstSrc *args, ResultLoc *result_loc) { IrInstSrcCallExtra *call_instruction = ir_build_instruction(irb, scope, source_node); call_instruction->options = options; call_instruction->fn_ref = fn_ref; call_instruction->args = args; call_instruction->result_loc = result_loc; ir_ref_instruction(options, irb->current_basic_block); ir_ref_instruction(fn_ref, irb->current_basic_block); ir_ref_instruction(args, irb->current_basic_block); return &call_instruction->base; } static IrInstSrc *ir_build_call_args(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *options, IrInstSrc *fn_ref, IrInstSrc **args_ptr, size_t args_len, ResultLoc *result_loc) { IrInstSrcCallArgs *call_instruction = ir_build_instruction(irb, scope, source_node); call_instruction->options = options; call_instruction->fn_ref = fn_ref; call_instruction->args_ptr = args_ptr; call_instruction->args_len = args_len; call_instruction->result_loc = result_loc; ir_ref_instruction(options, irb->current_basic_block); ir_ref_instruction(fn_ref, irb->current_basic_block); for (size_t i = 0; i < args_len; i += 1) ir_ref_instruction(args_ptr[i], irb->current_basic_block); return &call_instruction->base; } static IrInstSrc *ir_build_call_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigFn *fn_entry, IrInstSrc *fn_ref, size_t arg_count, IrInstSrc **args, IrInstSrc *ret_ptr, CallModifier modifier, bool is_async_call_builtin, IrInstSrc *new_stack, ResultLoc *result_loc) { IrInstSrcCall *call_instruction = ir_build_instruction(irb, scope, source_node); call_instruction->fn_entry = fn_entry; call_instruction->fn_ref = fn_ref; call_instruction->args = args; call_instruction->arg_count = arg_count; call_instruction->modifier = modifier; call_instruction->is_async_call_builtin = is_async_call_builtin; call_instruction->new_stack = new_stack; call_instruction->result_loc = result_loc; call_instruction->ret_ptr = ret_ptr; if (fn_ref != nullptr) ir_ref_instruction(fn_ref, irb->current_basic_block); for (size_t i = 0; i < arg_count; i += 1) ir_ref_instruction(args[i], irb->current_basic_block); if (ret_ptr != nullptr) ir_ref_instruction(ret_ptr, irb->current_basic_block); if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block); return &call_instruction->base; } static IrInstGenCall *ir_build_call_gen(IrAnalyze *ira, IrInst *source_instruction, ZigFn *fn_entry, IrInstGen *fn_ref, size_t arg_count, IrInstGen **args, CallModifier modifier, IrInstGen *new_stack, bool is_async_call_builtin, IrInstGen *result_loc, ZigType *return_type) { IrInstGenCall *call_instruction = ir_build_inst_gen(&ira->new_irb, source_instruction->scope, source_instruction->source_node); call_instruction->base.value->type = return_type; call_instruction->fn_entry = fn_entry; call_instruction->fn_ref = fn_ref; call_instruction->args = args; call_instruction->arg_count = arg_count; call_instruction->modifier = modifier; call_instruction->is_async_call_builtin = is_async_call_builtin; call_instruction->new_stack = new_stack; call_instruction->result_loc = result_loc; if (fn_ref != nullptr) ir_ref_inst_gen(fn_ref); for (size_t i = 0; i < arg_count; i += 1) ir_ref_inst_gen(args[i]); if (new_stack != nullptr) ir_ref_inst_gen(new_stack); if (result_loc != nullptr) ir_ref_inst_gen(result_loc); return call_instruction; } static IrInstSrc *ir_build_phi(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, size_t incoming_count, IrBasicBlockSrc **incoming_blocks, IrInstSrc **incoming_values, ResultLocPeerParent *peer_parent) { assert(incoming_count != 0); assert(incoming_count != SIZE_MAX); IrInstSrcPhi *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; phi_instruction->peer_parent = peer_parent; for (size_t i = 0; i < incoming_count; i += 1) { ir_ref_bb(incoming_blocks[i]); ir_ref_instruction(incoming_values[i], irb->current_basic_block); } return &phi_instruction->base; } static IrInstGen *ir_build_phi_gen(IrAnalyze *ira, IrInst *source_instr, size_t incoming_count, IrBasicBlockGen **incoming_blocks, IrInstGen **incoming_values, ZigType *result_type) { assert(incoming_count != 0); assert(incoming_count != SIZE_MAX); IrInstGenPhi *phi_instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); phi_instruction->base.value->type = result_type; 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_inst_gen(incoming_values[i]); } return &phi_instruction->base; } static IrInstSrc *ir_build_br(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrBasicBlockSrc *dest_block, IrInstSrc *is_comptime) { IrInstSrcBr *inst = ir_build_instruction(irb, scope, source_node); inst->base.is_noreturn = true; inst->dest_block = dest_block; inst->is_comptime = is_comptime; ir_ref_bb(dest_block); if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block); return &inst->base; } static IrInstGen *ir_build_br_gen(IrAnalyze *ira, IrInst *source_instr, IrBasicBlockGen *dest_block) { IrInstGenBr *inst = ir_build_inst_noreturn(&ira->new_irb, source_instr->scope, source_instr->source_node); inst->dest_block = dest_block; return &inst->base; } static IrInstSrc *ir_build_ptr_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *child_type, bool is_const, bool is_volatile, PtrLen ptr_len, IrInstSrc *sentinel, IrInstSrc *align_value, uint32_t bit_offset_start, uint32_t host_int_bytes, bool is_allow_zero) { IrInstSrcPtrType *inst = ir_build_instruction(irb, scope, source_node); inst->sentinel = sentinel; inst->align_value = align_value; inst->child_type = child_type; inst->is_const = is_const; inst->is_volatile = is_volatile; inst->ptr_len = ptr_len; inst->bit_offset_start = bit_offset_start; inst->host_int_bytes = host_int_bytes; inst->is_allow_zero = is_allow_zero; if (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block); if (align_value) ir_ref_instruction(align_value, irb->current_basic_block); ir_ref_instruction(child_type, irb->current_basic_block); return &inst->base; } static IrInstSrc *ir_build_un_op_lval(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrUnOp op_id, IrInstSrc *value, LVal lval, ResultLoc *result_loc) { IrInstSrcUnOp *instruction = ir_build_instruction(irb, scope, source_node); instruction->op_id = op_id; instruction->value = value; instruction->lval = lval; instruction->result_loc = result_loc; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_un_op(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrUnOp op_id, IrInstSrc *value) { return ir_build_un_op_lval(irb, scope, source_node, op_id, value, LValNone, nullptr); } static IrInstGen *ir_build_negation(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand, ZigType *expr_type) { IrInstGenNegation *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = expr_type; instruction->operand = operand; ir_ref_inst_gen(operand); return &instruction->base; } static IrInstGen *ir_build_negation_wrapping(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand, ZigType *expr_type) { IrInstGenNegationWrapping *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = expr_type; instruction->operand = operand; ir_ref_inst_gen(operand); return &instruction->base; } static IrInstGen *ir_build_binary_not(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand, ZigType *expr_type) { IrInstGenBinaryNot *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = expr_type; instruction->operand = operand; ir_ref_inst_gen(operand); return &instruction->base; } static IrInstSrc *ir_build_container_init_list(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, size_t item_count, IrInstSrc **elem_result_loc_list, IrInstSrc *result_loc, AstNode *init_array_type_source_node) { IrInstSrcContainerInitList *container_init_list_instruction = ir_build_instruction(irb, scope, source_node); container_init_list_instruction->item_count = item_count; container_init_list_instruction->elem_result_loc_list = elem_result_loc_list; container_init_list_instruction->result_loc = result_loc; container_init_list_instruction->init_array_type_source_node = init_array_type_source_node; for (size_t i = 0; i < item_count; i += 1) { ir_ref_instruction(elem_result_loc_list[i], irb->current_basic_block); } if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); return &container_init_list_instruction->base; } static IrInstSrc *ir_build_container_init_fields(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, size_t field_count, IrInstSrcContainerInitFieldsField *fields, IrInstSrc *result_loc) { IrInstSrcContainerInitFields *container_init_fields_instruction = ir_build_instruction(irb, scope, source_node); container_init_fields_instruction->field_count = field_count; container_init_fields_instruction->fields = fields; container_init_fields_instruction->result_loc = result_loc; for (size_t i = 0; i < field_count; i += 1) { ir_ref_instruction(fields[i].result_loc, irb->current_basic_block); } if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); return &container_init_fields_instruction->base; } static IrInstSrc *ir_build_unreachable(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { IrInstSrcUnreachable *inst = ir_build_instruction(irb, scope, source_node); inst->base.is_noreturn = true; return &inst->base; } static IrInstGen *ir_build_unreachable_gen(IrAnalyze *ira, IrInst *source_instr) { IrInstGenUnreachable *inst = ir_build_inst_noreturn(&ira->new_irb, source_instr->scope, source_instr->source_node); return &inst->base; } static IrInstSrcStorePtr *ir_build_store_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *ptr, IrInstSrc *value) { IrInstSrcStorePtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->ptr = ptr; instruction->value = value; ir_ref_instruction(ptr, irb->current_basic_block); ir_ref_instruction(value, irb->current_basic_block); return instruction; } static IrInstGen *ir_build_store_ptr_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *ptr, IrInstGen *value) { IrInstGenStorePtr *instruction = ir_build_inst_void(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->ptr = ptr; instruction->value = value; ir_ref_inst_gen(ptr); ir_ref_inst_gen(value); return &instruction->base; } static IrInstGen *ir_build_vector_store_elem(IrAnalyze *ira, IrInst *src_inst, IrInstGen *vector_ptr, IrInstGen *index, IrInstGen *value) { IrInstGenVectorStoreElem *inst = ir_build_inst_void( &ira->new_irb, src_inst->scope, src_inst->source_node); inst->vector_ptr = vector_ptr; inst->index = index; inst->value = value; ir_ref_inst_gen(vector_ptr); ir_ref_inst_gen(index); ir_ref_inst_gen(value); return &inst->base; } static IrInstSrc *ir_build_var_decl_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var, IrInstSrc *align_value, IrInstSrc *ptr) { IrInstSrcDeclVar *inst = ir_build_instruction(irb, scope, source_node); inst->var = var; inst->align_value = align_value; inst->ptr = ptr; if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); return &inst->base; } static IrInstGen *ir_build_var_decl_gen(IrAnalyze *ira, IrInst *source_instruction, ZigVar *var, IrInstGen *var_ptr) { IrInstGenDeclVar *inst = ir_build_inst_gen(&ira->new_irb, source_instruction->scope, source_instruction->source_node); inst->base.value->special = ConstValSpecialStatic; inst->base.value->type = ira->codegen->builtin_types.entry_void; inst->var = var; inst->var_ptr = var_ptr; ir_ref_inst_gen(var_ptr); return &inst->base; } static IrInstSrc *ir_build_export(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target, IrInstSrc *options) { IrInstSrcExport *export_instruction = ir_build_instruction( irb, scope, source_node); export_instruction->target = target; export_instruction->options = options; ir_ref_instruction(target, irb->current_basic_block); ir_ref_instruction(options, irb->current_basic_block); return &export_instruction->base; } static IrInstSrc *ir_build_load_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *ptr) { IrInstSrcLoadPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->ptr = ptr; ir_ref_instruction(ptr, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_load_ptr_gen(IrAnalyze *ira, IrInst *source_instruction, IrInstGen *ptr, ZigType *ty, IrInstGen *result_loc) { IrInstGenLoadPtr *instruction = ir_build_inst_gen( &ira->new_irb, source_instruction->scope, source_instruction->source_node); instruction->base.value->type = ty; instruction->ptr = ptr; instruction->result_loc = result_loc; ir_ref_inst_gen(ptr); if (result_loc != nullptr) ir_ref_inst_gen(result_loc); return &instruction->base; } static IrInstSrc *ir_build_typeof_n(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc **values, size_t value_count) { assert(value_count >= 2); IrInstSrcTypeOf *instruction = ir_build_instruction(irb, scope, source_node); instruction->value.list = values; instruction->value_count = value_count; for (size_t i = 0; i < value_count; i++) ir_ref_instruction(values[i], irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_typeof_1(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) { IrInstSrcTypeOf *instruction = ir_build_instruction(irb, scope, source_node); instruction->value.scalar = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_set_cold(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *is_cold) { IrInstSrcSetCold *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 IrInstSrc *ir_build_set_runtime_safety(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *safety_on) { IrInstSrcSetRuntimeSafety *inst = ir_build_instruction(irb, scope, source_node); inst->safety_on = safety_on; ir_ref_instruction(safety_on, irb->current_basic_block); return &inst->base; } static IrInstSrc *ir_build_set_float_mode(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *mode_value) { IrInstSrcSetFloatMode *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 IrInstSrc *ir_build_array_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *size, IrInstSrc *sentinel, IrInstSrc *child_type) { IrInstSrcArrayType *instruction = ir_build_instruction(irb, scope, source_node); instruction->size = size; instruction->sentinel = sentinel; instruction->child_type = child_type; ir_ref_instruction(size, irb->current_basic_block); if (sentinel != nullptr) ir_ref_instruction(sentinel, irb->current_basic_block); ir_ref_instruction(child_type, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_anyframe_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *payload_type) { IrInstSrcAnyFrameType *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 IrInstSrc *ir_build_slice_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *child_type, bool is_const, bool is_volatile, IrInstSrc *sentinel, IrInstSrc *align_value, bool is_allow_zero) { IrInstSrcSliceType *instruction = ir_build_instruction(irb, scope, source_node); instruction->is_const = is_const; instruction->is_volatile = is_volatile; instruction->child_type = child_type; instruction->sentinel = sentinel; instruction->align_value = align_value; instruction->is_allow_zero = is_allow_zero; if (sentinel != nullptr) ir_ref_instruction(sentinel, irb->current_basic_block); if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); ir_ref_instruction(child_type, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_asm_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *asm_template, IrInstSrc **input_list, IrInstSrc **output_types, ZigVar **output_vars, size_t return_count, bool has_side_effects, bool is_global) { IrInstSrcAsm *instruction = ir_build_instruction(irb, scope, source_node); instruction->asm_template = asm_template; instruction->input_list = input_list; instruction->output_types = output_types; instruction->output_vars = output_vars; instruction->return_count = return_count; instruction->has_side_effects = has_side_effects; instruction->is_global = is_global; assert(source_node->type == NodeTypeAsmExpr); for (size_t i = 0; i < source_node->data.asm_expr.output_list.length; i += 1) { IrInstSrc *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) { IrInstSrc *input_value = input_list[i]; ir_ref_instruction(input_value, irb->current_basic_block); } return &instruction->base; } static IrInstGen *ir_build_asm_gen(IrAnalyze *ira, IrInst *source_instr, Buf *asm_template, AsmToken *token_list, size_t token_list_len, IrInstGen **input_list, IrInstGen **output_types, ZigVar **output_vars, size_t return_count, bool has_side_effects, ZigType *return_type) { IrInstGenAsm *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = return_type; instruction->asm_template = asm_template; instruction->token_list = token_list; instruction->token_list_len = token_list_len; instruction->input_list = input_list; instruction->output_types = output_types; instruction->output_vars = output_vars; instruction->return_count = return_count; instruction->has_side_effects = has_side_effects; assert(source_instr->source_node->type == NodeTypeAsmExpr); for (size_t i = 0; i < source_instr->source_node->data.asm_expr.output_list.length; i += 1) { IrInstGen *output_type = output_types[i]; if (output_type) ir_ref_inst_gen(output_type); } for (size_t i = 0; i < source_instr->source_node->data.asm_expr.input_list.length; i += 1) { IrInstGen *input_value = input_list[i]; ir_ref_inst_gen(input_value); } return &instruction->base; } static IrInstSrc *ir_build_size_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value, bool bit_size) { IrInstSrcSizeOf *instruction = ir_build_instruction(irb, scope, source_node); instruction->type_value = type_value; instruction->bit_size = bit_size; ir_ref_instruction(type_value, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_test_non_null_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) { IrInstSrcTestNonNull *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_test_non_null_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value) { IrInstGenTestNonNull *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); inst->base.value->type = ira->codegen->builtin_types.entry_bool; inst->value = value; ir_ref_inst_gen(value); return &inst->base; } static IrInstSrc *ir_build_optional_unwrap_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *base_ptr, bool safety_check_on) { IrInstSrcOptionalUnwrapPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->base_ptr = base_ptr; instruction->safety_check_on = safety_check_on; ir_ref_instruction(base_ptr, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_optional_unwrap_ptr_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *base_ptr, bool safety_check_on, bool initializing, ZigType *result_type) { IrInstGenOptionalUnwrapPtr *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); inst->base.value->type = result_type; inst->base_ptr = base_ptr; inst->safety_check_on = safety_check_on; inst->initializing = initializing; ir_ref_inst_gen(base_ptr); return &inst->base; } static IrInstGen *ir_build_optional_wrap(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_ty, IrInstGen *operand, IrInstGen *result_loc) { IrInstGenOptionalWrap *instruction = ir_build_inst_gen( &ira->new_irb, source_instruction->scope, source_instruction->source_node); instruction->base.value->type = result_ty; instruction->operand = operand; instruction->result_loc = result_loc; ir_ref_inst_gen(operand); if (result_loc != nullptr) ir_ref_inst_gen(result_loc); return &instruction->base; } static IrInstGen *ir_build_err_wrap_payload(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_type, IrInstGen *operand, IrInstGen *result_loc) { IrInstGenErrWrapPayload *instruction = ir_build_inst_gen( &ira->new_irb, source_instruction->scope, source_instruction->source_node); instruction->base.value->type = result_type; instruction->operand = operand; instruction->result_loc = result_loc; ir_ref_inst_gen(operand); if (result_loc != nullptr) ir_ref_inst_gen(result_loc); return &instruction->base; } static IrInstGen *ir_build_err_wrap_code(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_type, IrInstGen *operand, IrInstGen *result_loc) { IrInstGenErrWrapCode *instruction = ir_build_inst_gen( &ira->new_irb, source_instruction->scope, source_instruction->source_node); instruction->base.value->type = result_type; instruction->operand = operand; instruction->result_loc = result_loc; ir_ref_inst_gen(operand); if (result_loc != nullptr) ir_ref_inst_gen(result_loc); return &instruction->base; } static IrInstSrc *ir_build_clz(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type, IrInstSrc *op) { IrInstSrcClz *instruction = ir_build_instruction(irb, scope, source_node); instruction->type = type; instruction->op = op; ir_ref_instruction(type, irb->current_basic_block); ir_ref_instruction(op, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_clz_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *result_type, IrInstGen *op) { IrInstGenClz *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = result_type; instruction->op = op; ir_ref_inst_gen(op); return &instruction->base; } static IrInstSrc *ir_build_ctz(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type, IrInstSrc *op) { IrInstSrcCtz *instruction = ir_build_instruction(irb, scope, source_node); instruction->type = type; instruction->op = op; ir_ref_instruction(type, irb->current_basic_block); ir_ref_instruction(op, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_ctz_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *result_type, IrInstGen *op) { IrInstGenCtz *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = result_type; instruction->op = op; ir_ref_inst_gen(op); return &instruction->base; } static IrInstSrc *ir_build_pop_count(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type, IrInstSrc *op) { IrInstSrcPopCount *instruction = ir_build_instruction(irb, scope, source_node); instruction->type = type; instruction->op = op; ir_ref_instruction(type, irb->current_basic_block); ir_ref_instruction(op, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_pop_count_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *result_type, IrInstGen *op) { IrInstGenPopCount *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = result_type; instruction->op = op; ir_ref_inst_gen(op); return &instruction->base; } static IrInstSrc *ir_build_bswap(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type, IrInstSrc *op) { IrInstSrcBswap *instruction = ir_build_instruction(irb, scope, source_node); instruction->type = type; instruction->op = op; ir_ref_instruction(type, irb->current_basic_block); ir_ref_instruction(op, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_bswap_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *op_type, IrInstGen *op) { IrInstGenBswap *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = op_type; instruction->op = op; ir_ref_inst_gen(op); return &instruction->base; } static IrInstSrc *ir_build_bit_reverse(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type, IrInstSrc *op) { IrInstSrcBitReverse *instruction = ir_build_instruction(irb, scope, source_node); instruction->type = type; instruction->op = op; ir_ref_instruction(type, irb->current_basic_block); ir_ref_instruction(op, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_bit_reverse_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *int_type, IrInstGen *op) { IrInstGenBitReverse *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = int_type; instruction->op = op; ir_ref_inst_gen(op); return &instruction->base; } static IrInstSrcSwitchBr *ir_build_switch_br_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target_value, IrBasicBlockSrc *else_block, size_t case_count, IrInstSrcSwitchBrCase *cases, IrInstSrc *is_comptime, IrInstSrc *switch_prongs_void) { IrInstSrcSwitchBr *instruction = ir_build_instruction(irb, scope, source_node); instruction->base.is_noreturn = true; 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); ir_ref_instruction(is_comptime, irb->current_basic_block); ir_ref_bb(else_block); ir_ref_instruction(switch_prongs_void, irb->current_basic_block); for (size_t i = 0; i < case_count; i += 1) { ir_ref_instruction(cases[i].value, irb->current_basic_block); ir_ref_bb(cases[i].block); } return instruction; } static IrInstGenSwitchBr *ir_build_switch_br_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *target_value, IrBasicBlockGen *else_block, size_t case_count, IrInstGenSwitchBrCase *cases) { IrInstGenSwitchBr *instruction = ir_build_inst_noreturn(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->target_value = target_value; instruction->else_block = else_block; instruction->case_count = case_count; instruction->cases = cases; ir_ref_inst_gen(target_value); for (size_t i = 0; i < case_count; i += 1) { ir_ref_inst_gen(cases[i].value); } return instruction; } static IrInstSrc *ir_build_switch_target(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target_value_ptr) { IrInstSrcSwitchTarget *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 IrInstSrc *ir_build_switch_var(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target_value_ptr, IrInstSrc **prongs_ptr, size_t prongs_len) { IrInstSrcSwitchVar *instruction = ir_build_instruction(irb, scope, source_node); instruction->target_value_ptr = target_value_ptr; instruction->prongs_ptr = prongs_ptr; instruction->prongs_len = prongs_len; ir_ref_instruction(target_value_ptr, irb->current_basic_block); for (size_t i = 0; i < prongs_len; i += 1) { ir_ref_instruction(prongs_ptr[i], irb->current_basic_block); } return &instruction->base; } // For this instruction the switch_br must be set later. static IrInstSrcSwitchElseVar *ir_build_switch_else_var(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target_value_ptr) { IrInstSrcSwitchElseVar *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; } static IrInstGen *ir_build_union_tag(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value, ZigType *tag_type) { IrInstGenUnionTag *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->value = value; instruction->base.value->type = tag_type; ir_ref_inst_gen(value); return &instruction->base; } static IrInstSrc *ir_build_import(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) { IrInstSrcImport *instruction = ir_build_instruction(irb, scope, source_node); instruction->name = name; ir_ref_instruction(name, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_ref_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) { IrInstSrcRef *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_ref_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_type, IrInstGen *operand, IrInstGen *result_loc) { IrInstGenRef *instruction = ir_build_inst_gen(&ira->new_irb, source_instruction->scope, source_instruction->source_node); instruction->base.value->type = result_type; instruction->operand = operand; instruction->result_loc = result_loc; ir_ref_inst_gen(operand); if (result_loc != nullptr) ir_ref_inst_gen(result_loc); return &instruction->base; } static IrInstSrc *ir_build_compile_err(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *msg) { IrInstSrcCompileErr *instruction = ir_build_instruction(irb, scope, source_node); instruction->msg = msg; ir_ref_instruction(msg, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_compile_log(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, size_t msg_count, IrInstSrc **msg_list) { IrInstSrcCompileLog *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 IrInstSrc *ir_build_err_name(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) { IrInstSrcErrName *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_err_name_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value, ZigType *str_type) { IrInstGenErrName *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = str_type; instruction->value = value; ir_ref_inst_gen(value); return &instruction->base; } static IrInstSrc *ir_build_c_import(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { IrInstSrcCImport *instruction = ir_build_instruction(irb, scope, source_node); return &instruction->base; } static IrInstSrc *ir_build_c_include(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) { IrInstSrcCInclude *instruction = ir_build_instruction(irb, scope, source_node); instruction->name = name; ir_ref_instruction(name, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_c_define(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name, IrInstSrc *value) { IrInstSrcCDefine *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 IrInstSrc *ir_build_c_undef(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) { IrInstSrcCUndef *instruction = ir_build_instruction(irb, scope, source_node); instruction->name = name; ir_ref_instruction(name, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_embed_file(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) { IrInstSrcEmbedFile *instruction = ir_build_instruction(irb, scope, source_node); instruction->name = name; ir_ref_instruction(name, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_cmpxchg_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value, IrInstSrc *ptr, IrInstSrc *cmp_value, IrInstSrc *new_value, IrInstSrc *success_order_value, IrInstSrc *failure_order_value, bool is_weak, ResultLoc *result_loc) { IrInstSrcCmpxchg *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->result_loc = result_loc; ir_ref_instruction(type_value, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); ir_ref_instruction(cmp_value, irb->current_basic_block); ir_ref_instruction(new_value, irb->current_basic_block); ir_ref_instruction(success_order_value, irb->current_basic_block); ir_ref_instruction(failure_order_value, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_cmpxchg_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_type, IrInstGen *ptr, IrInstGen *cmp_value, IrInstGen *new_value, AtomicOrder success_order, AtomicOrder failure_order, bool is_weak, IrInstGen *result_loc) { IrInstGenCmpxchg *instruction = ir_build_inst_gen(&ira->new_irb, source_instruction->scope, source_instruction->source_node); instruction->base.value->type = result_type; instruction->ptr = ptr; instruction->cmp_value = cmp_value; instruction->new_value = new_value; instruction->success_order = success_order; instruction->failure_order = failure_order; instruction->is_weak = is_weak; instruction->result_loc = result_loc; ir_ref_inst_gen(ptr); ir_ref_inst_gen(cmp_value); ir_ref_inst_gen(new_value); if (result_loc != nullptr) ir_ref_inst_gen(result_loc); return &instruction->base; } static IrInstSrc *ir_build_fence(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *order) { IrInstSrcFence *instruction = ir_build_instruction(irb, scope, source_node); instruction->order = order; ir_ref_instruction(order, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_fence_gen(IrAnalyze *ira, IrInst *source_instr, AtomicOrder order) { IrInstGenFence *instruction = ir_build_inst_void(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->order = order; return &instruction->base; } static IrInstSrc *ir_build_truncate(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_type, IrInstSrc *target) { IrInstSrcTruncate *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 IrInstGen *ir_build_truncate_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *dest_type, IrInstGen *target) { IrInstGenTruncate *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = dest_type; instruction->target = target; ir_ref_inst_gen(target); return &instruction->base; } static IrInstSrc *ir_build_int_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_type, IrInstSrc *target) { IrInstSrcIntCast *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 IrInstSrc *ir_build_float_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_type, IrInstSrc *target) { IrInstSrcFloatCast *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 IrInstSrc *ir_build_err_set_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_type, IrInstSrc *target) { IrInstSrcErrSetCast *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 IrInstSrc *ir_build_int_to_float(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_type, IrInstSrc *target) { IrInstSrcIntToFloat *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 IrInstSrc *ir_build_float_to_int(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_type, IrInstSrc *target) { IrInstSrcFloatToInt *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 IrInstSrc *ir_build_bool_to_int(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target) { IrInstSrcBoolToInt *instruction = ir_build_instruction(irb, scope, source_node); instruction->target = target; ir_ref_instruction(target, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_vector_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *len, IrInstSrc *elem_type) { IrInstSrcVectorType *instruction = ir_build_instruction(irb, scope, source_node); instruction->len = len; instruction->elem_type = elem_type; ir_ref_instruction(len, irb->current_basic_block); ir_ref_instruction(elem_type, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_shuffle_vector(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *scalar_type, IrInstSrc *a, IrInstSrc *b, IrInstSrc *mask) { IrInstSrcShuffleVector *instruction = ir_build_instruction(irb, scope, source_node); instruction->scalar_type = scalar_type; instruction->a = a; instruction->b = b; instruction->mask = mask; if (scalar_type != nullptr) ir_ref_instruction(scalar_type, irb->current_basic_block); ir_ref_instruction(a, irb->current_basic_block); ir_ref_instruction(b, irb->current_basic_block); ir_ref_instruction(mask, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_shuffle_vector_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, ZigType *result_type, IrInstGen *a, IrInstGen *b, IrInstGen *mask) { IrInstGenShuffleVector *inst = ir_build_inst_gen(&ira->new_irb, scope, source_node); inst->base.value->type = result_type; inst->a = a; inst->b = b; inst->mask = mask; ir_ref_inst_gen(a); ir_ref_inst_gen(b); ir_ref_inst_gen(mask); return &inst->base; } static IrInstSrc *ir_build_splat_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *len, IrInstSrc *scalar) { IrInstSrcSplat *instruction = ir_build_instruction(irb, scope, source_node); instruction->len = len; instruction->scalar = scalar; ir_ref_instruction(len, irb->current_basic_block); ir_ref_instruction(scalar, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_splat_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_type, IrInstGen *scalar) { IrInstGenSplat *instruction = ir_build_inst_gen( &ira->new_irb, source_instruction->scope, source_instruction->source_node); instruction->base.value->type = result_type; instruction->scalar = scalar; ir_ref_inst_gen(scalar); return &instruction->base; } static IrInstSrc *ir_build_bool_not(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) { IrInstSrcBoolNot *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_bool_not_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value) { IrInstGenBoolNot *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = ira->codegen->builtin_types.entry_bool; instruction->value = value; ir_ref_inst_gen(value); return &instruction->base; } static IrInstSrc *ir_build_memset_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_ptr, IrInstSrc *byte, IrInstSrc *count) { IrInstSrcMemset *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 IrInstGen *ir_build_memset_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *dest_ptr, IrInstGen *byte, IrInstGen *count) { IrInstGenMemset *instruction = ir_build_inst_void(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->dest_ptr = dest_ptr; instruction->byte = byte; instruction->count = count; ir_ref_inst_gen(dest_ptr); ir_ref_inst_gen(byte); ir_ref_inst_gen(count); return &instruction->base; } static IrInstSrc *ir_build_memcpy_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_ptr, IrInstSrc *src_ptr, IrInstSrc *count) { IrInstSrcMemcpy *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 IrInstGen *ir_build_memcpy_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *dest_ptr, IrInstGen *src_ptr, IrInstGen *count) { IrInstGenMemcpy *instruction = ir_build_inst_void(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->dest_ptr = dest_ptr; instruction->src_ptr = src_ptr; instruction->count = count; ir_ref_inst_gen(dest_ptr); ir_ref_inst_gen(src_ptr); ir_ref_inst_gen(count); return &instruction->base; } static IrInstSrc *ir_build_slice_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *ptr, IrInstSrc *start, IrInstSrc *end, IrInstSrc *sentinel, bool safety_check_on, ResultLoc *result_loc) { IrInstSrcSlice *instruction = ir_build_instruction(irb, scope, source_node); instruction->ptr = ptr; instruction->start = start; instruction->end = end; instruction->sentinel = sentinel; instruction->safety_check_on = safety_check_on; instruction->result_loc = result_loc; ir_ref_instruction(ptr, irb->current_basic_block); ir_ref_instruction(start, irb->current_basic_block); if (end) ir_ref_instruction(end, irb->current_basic_block); if (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_slice_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *slice_type, IrInstGen *ptr, IrInstGen *start, IrInstGen *end, bool safety_check_on, IrInstGen *result_loc, ZigValue *sentinel) { IrInstGenSlice *instruction = ir_build_inst_gen( &ira->new_irb, source_instruction->scope, source_instruction->source_node); instruction->base.value->type = slice_type; instruction->ptr = ptr; instruction->start = start; instruction->end = end; instruction->safety_check_on = safety_check_on; instruction->result_loc = result_loc; instruction->sentinel = sentinel; ir_ref_inst_gen(ptr); ir_ref_inst_gen(start); if (end != nullptr) ir_ref_inst_gen(end); if (result_loc != nullptr) ir_ref_inst_gen(result_loc); return &instruction->base; } static IrInstSrc *ir_build_breakpoint(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { IrInstSrcBreakpoint *instruction = ir_build_instruction(irb, scope, source_node); return &instruction->base; } static IrInstGen *ir_build_breakpoint_gen(IrAnalyze *ira, IrInst *source_instr) { IrInstGenBreakpoint *instruction = ir_build_inst_void(&ira->new_irb, source_instr->scope, source_instr->source_node); return &instruction->base; } static IrInstSrc *ir_build_return_address_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { IrInstSrcReturnAddress *instruction = ir_build_instruction(irb, scope, source_node); return &instruction->base; } static IrInstGen *ir_build_return_address_gen(IrAnalyze *ira, IrInst *source_instr) { IrInstGenReturnAddress *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); inst->base.value->type = ira->codegen->builtin_types.entry_usize; return &inst->base; } static IrInstSrc *ir_build_frame_address_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { IrInstSrcFrameAddress *inst = ir_build_instruction(irb, scope, source_node); return &inst->base; } static IrInstGen *ir_build_frame_address_gen(IrAnalyze *ira, IrInst *source_instr) { IrInstGenFrameAddress *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); inst->base.value->type = ira->codegen->builtin_types.entry_usize; return &inst->base; } static IrInstSrc *ir_build_handle_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { IrInstSrcFrameHandle *inst = ir_build_instruction(irb, scope, source_node); return &inst->base; } static IrInstGen *ir_build_handle_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *ty) { IrInstGenFrameHandle *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); inst->base.value->type = ty; return &inst->base; } static IrInstSrc *ir_build_frame_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *fn) { IrInstSrcFrameType *inst = ir_build_instruction(irb, scope, source_node); inst->fn = fn; ir_ref_instruction(fn, irb->current_basic_block); return &inst->base; } static IrInstSrc *ir_build_frame_size_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *fn) { IrInstSrcFrameSize *inst = ir_build_instruction(irb, scope, source_node); inst->fn = fn; ir_ref_instruction(fn, irb->current_basic_block); return &inst->base; } static IrInstGen *ir_build_frame_size_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *fn) { IrInstGenFrameSize *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); inst->base.value->type = ira->codegen->builtin_types.entry_usize; inst->fn = fn; ir_ref_inst_gen(fn); return &inst->base; } static IrInstSrc *ir_build_overflow_op_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrOverflowOp op, IrInstSrc *type_value, IrInstSrc *op1, IrInstSrc *op2, IrInstSrc *result_ptr) { IrInstSrcOverflowOp *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; 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 IrInstGen *ir_build_overflow_op_gen(IrAnalyze *ira, IrInst *source_instr, IrOverflowOp op, IrInstGen *op1, IrInstGen *op2, IrInstGen *result_ptr, ZigType *result_ptr_type) { IrInstGenOverflowOp *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = ira->codegen->builtin_types.entry_bool; instruction->op = op; instruction->op1 = op1; instruction->op2 = op2; instruction->result_ptr = result_ptr; instruction->result_ptr_type = result_ptr_type; ir_ref_inst_gen(op1); ir_ref_inst_gen(op2); ir_ref_inst_gen(result_ptr); return &instruction->base; } static IrInstSrc *ir_build_float_op_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *operand, BuiltinFnId fn_id) { IrInstSrcFloatOp *instruction = ir_build_instruction(irb, scope, source_node); instruction->operand = operand; instruction->fn_id = fn_id; ir_ref_instruction(operand, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_float_op_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand, BuiltinFnId fn_id, ZigType *operand_type) { IrInstGenFloatOp *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = operand_type; instruction->operand = operand; instruction->fn_id = fn_id; ir_ref_inst_gen(operand); return &instruction->base; } static IrInstSrc *ir_build_mul_add_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value, IrInstSrc *op1, IrInstSrc *op2, IrInstSrc *op3) { IrInstSrcMulAdd *instruction = ir_build_instruction(irb, scope, source_node); instruction->type_value = type_value; instruction->op1 = op1; instruction->op2 = op2; instruction->op3 = op3; ir_ref_instruction(type_value, irb->current_basic_block); ir_ref_instruction(op1, irb->current_basic_block); ir_ref_instruction(op2, irb->current_basic_block); ir_ref_instruction(op3, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_mul_add_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *op1, IrInstGen *op2, IrInstGen *op3, ZigType *expr_type) { IrInstGenMulAdd *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = expr_type; instruction->op1 = op1; instruction->op2 = op2; instruction->op3 = op3; ir_ref_inst_gen(op1); ir_ref_inst_gen(op2); ir_ref_inst_gen(op3); return &instruction->base; } static IrInstSrc *ir_build_align_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value) { IrInstSrcAlignOf *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 IrInstSrc *ir_build_test_err_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *base_ptr, bool resolve_err_set, bool base_ptr_is_payload) { IrInstSrcTestErr *instruction = ir_build_instruction(irb, scope, source_node); instruction->base_ptr = base_ptr; instruction->resolve_err_set = resolve_err_set; instruction->base_ptr_is_payload = base_ptr_is_payload; ir_ref_instruction(base_ptr, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_test_err_gen(IrAnalyze *ira, IrInst *source_instruction, IrInstGen *err_union) { IrInstGenTestErr *instruction = ir_build_inst_gen( &ira->new_irb, source_instruction->scope, source_instruction->source_node); instruction->base.value->type = ira->codegen->builtin_types.entry_bool; instruction->err_union = err_union; ir_ref_inst_gen(err_union); return &instruction->base; } static IrInstSrc *ir_build_unwrap_err_code_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *err_union_ptr) { IrInstSrcUnwrapErrCode *inst = ir_build_instruction(irb, scope, source_node); inst->err_union_ptr = err_union_ptr; ir_ref_instruction(err_union_ptr, irb->current_basic_block); return &inst->base; } static IrInstGen *ir_build_unwrap_err_code_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *err_union_ptr, ZigType *result_type) { IrInstGenUnwrapErrCode *inst = ir_build_inst_gen(&ira->new_irb, scope, source_node); inst->base.value->type = result_type; inst->err_union_ptr = err_union_ptr; ir_ref_inst_gen(err_union_ptr); return &inst->base; } static IrInstSrc *ir_build_unwrap_err_payload_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value, bool safety_check_on, bool initializing) { IrInstSrcUnwrapErrPayload *inst = ir_build_instruction(irb, scope, source_node); inst->value = value; inst->safety_check_on = safety_check_on; inst->initializing = initializing; ir_ref_instruction(value, irb->current_basic_block); return &inst->base; } static IrInstGen *ir_build_unwrap_err_payload_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *value, bool safety_check_on, bool initializing, ZigType *result_type) { IrInstGenUnwrapErrPayload *inst = ir_build_inst_gen(&ira->new_irb, scope, source_node); inst->base.value->type = result_type; inst->value = value; inst->safety_check_on = safety_check_on; inst->initializing = initializing; ir_ref_inst_gen(value); return &inst->base; } static IrInstSrc *ir_build_fn_proto(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc **param_types, IrInstSrc *align_value, IrInstSrc *callconv_value, IrInstSrc *return_type, bool is_var_args) { IrInstSrcFnProto *instruction = ir_build_instruction(irb, scope, source_node); instruction->param_types = param_types; instruction->align_value = align_value; instruction->callconv_value = callconv_value; instruction->return_type = return_type; instruction->is_var_args = is_var_args; assert(source_node->type == NodeTypeFnProto); size_t param_count = source_node->data.fn_proto.params.length; if (is_var_args) param_count -= 1; for (size_t i = 0; i < param_count; i += 1) { if (param_types[i] != nullptr) ir_ref_instruction(param_types[i], irb->current_basic_block); } if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); if (callconv_value != nullptr) ir_ref_instruction(callconv_value, irb->current_basic_block); ir_ref_instruction(return_type, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_test_comptime(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) { IrInstSrcTestComptime *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_ptr_cast_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_type, IrInstSrc *ptr, bool safety_check_on) { IrInstSrcPtrCast *instruction = ir_build_instruction( irb, scope, source_node); instruction->dest_type = dest_type; instruction->ptr = ptr; instruction->safety_check_on = safety_check_on; ir_ref_instruction(dest_type, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *ptr_type, IrInstGen *ptr, bool safety_check_on) { IrInstGenPtrCast *instruction = ir_build_inst_gen( &ira->new_irb, source_instruction->scope, source_instruction->source_node); instruction->base.value->type = ptr_type; instruction->ptr = ptr; instruction->safety_check_on = safety_check_on; ir_ref_inst_gen(ptr); return &instruction->base; } static IrInstSrc *ir_build_implicit_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *operand, ResultLocCast *result_loc_cast) { IrInstSrcImplicitCast *instruction = ir_build_instruction(irb, scope, source_node); instruction->operand = operand; instruction->result_loc_cast = result_loc_cast; ir_ref_instruction(operand, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_bit_cast_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *operand, ResultLocBitCast *result_loc_bit_cast) { IrInstSrcBitCast *instruction = ir_build_instruction(irb, scope, source_node); instruction->operand = operand; instruction->result_loc_bit_cast = result_loc_bit_cast; ir_ref_instruction(operand, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_bit_cast_gen(IrAnalyze *ira, IrInst *source_instruction, IrInstGen *operand, ZigType *ty) { IrInstGenBitCast *instruction = ir_build_inst_gen( &ira->new_irb, source_instruction->scope, source_instruction->source_node); instruction->base.value->type = ty; instruction->operand = operand; ir_ref_inst_gen(operand); return &instruction->base; } static IrInstGen *ir_build_widen_or_shorten(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target, ZigType *result_type) { IrInstGenWidenOrShorten *inst = ir_build_inst_gen(&ira->new_irb, scope, source_node); inst->base.value->type = result_type; inst->target = target; ir_ref_inst_gen(target); return &inst->base; } static IrInstSrc *ir_build_int_to_ptr_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_type, IrInstSrc *target) { IrInstSrcIntToPtr *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 IrInstGen *ir_build_int_to_ptr_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target, ZigType *ptr_type) { IrInstGenIntToPtr *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); instruction->base.value->type = ptr_type; instruction->target = target; ir_ref_inst_gen(target); return &instruction->base; } static IrInstSrc *ir_build_ptr_to_int_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target) { IrInstSrcPtrToInt *inst = ir_build_instruction(irb, scope, source_node); inst->target = target; ir_ref_instruction(target, irb->current_basic_block); return &inst->base; } static IrInstGen *ir_build_ptr_to_int_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *target) { IrInstGenPtrToInt *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); inst->base.value->type = ira->codegen->builtin_types.entry_usize; inst->target = target; ir_ref_inst_gen(target); return &inst->base; } static IrInstSrc *ir_build_int_to_enum_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_type, IrInstSrc *target) { IrInstSrcIntToEnum *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 IrInstGen *ir_build_int_to_enum_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, ZigType *dest_type, IrInstGen *target) { IrInstGenIntToEnum *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); instruction->base.value->type = dest_type; instruction->target = target; ir_ref_inst_gen(target); return &instruction->base; } static IrInstSrc *ir_build_enum_to_int(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target) { IrInstSrcEnumToInt *instruction = ir_build_instruction( irb, scope, source_node); instruction->target = target; ir_ref_instruction(target, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_int_to_err_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target) { IrInstSrcIntToErr *instruction = ir_build_instruction(irb, scope, source_node); instruction->target = target; ir_ref_instruction(target, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_int_to_err_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target, ZigType *wanted_type) { IrInstGenIntToErr *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); instruction->base.value->type = wanted_type; instruction->target = target; ir_ref_inst_gen(target); return &instruction->base; } static IrInstSrc *ir_build_err_to_int_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target) { IrInstSrcErrToInt *instruction = ir_build_instruction( irb, scope, source_node); instruction->target = target; ir_ref_instruction(target, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_err_to_int_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target, ZigType *wanted_type) { IrInstGenErrToInt *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); instruction->base.value->type = wanted_type; instruction->target = target; ir_ref_inst_gen(target); return &instruction->base; } static IrInstSrc *ir_build_check_switch_prongs(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target_value, IrInstSrcCheckSwitchProngsRange *ranges, size_t range_count, bool have_else_prong, bool have_underscore_prong) { IrInstSrcCheckSwitchProngs *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; instruction->have_underscore_prong = have_underscore_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 IrInstSrc *ir_build_check_statement_is_void(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc* statement_value) { IrInstSrcCheckStatementIsVoid *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 IrInstSrc *ir_build_type_name(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value) { IrInstSrcTypeName *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 IrInstSrc *ir_build_decl_ref(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Tld *tld, LVal lval) { IrInstSrcDeclRef *instruction = ir_build_instruction(irb, scope, source_node); instruction->tld = tld; instruction->lval = lval; return &instruction->base; } static IrInstSrc *ir_build_panic_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *msg) { IrInstSrcPanic *instruction = ir_build_instruction(irb, scope, source_node); instruction->base.is_noreturn = true; instruction->msg = msg; ir_ref_instruction(msg, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_panic_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *msg) { IrInstGenPanic *instruction = ir_build_inst_noreturn(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->msg = msg; ir_ref_inst_gen(msg); return &instruction->base; } static IrInstSrc *ir_build_tag_name_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target) { IrInstSrcTagName *instruction = ir_build_instruction(irb, scope, source_node); instruction->target = target; ir_ref_instruction(target, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_tag_name_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *target, ZigType *result_type) { IrInstGenTagName *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = result_type; instruction->target = target; ir_ref_inst_gen(target); return &instruction->base; } static IrInstSrc *ir_build_tag_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target) { IrInstSrcTagType *instruction = ir_build_instruction(irb, scope, source_node); instruction->target = target; ir_ref_instruction(target, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_field_parent_ptr_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value, IrInstSrc *field_name, IrInstSrc *field_ptr) { IrInstSrcFieldParentPtr *inst = ir_build_instruction( irb, scope, source_node); inst->type_value = type_value; inst->field_name = field_name; inst->field_ptr = field_ptr; 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 &inst->base; } static IrInstGen *ir_build_field_parent_ptr_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *field_ptr, TypeStructField *field, ZigType *result_type) { IrInstGenFieldParentPtr *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); inst->base.value->type = result_type; inst->field_ptr = field_ptr; inst->field = field; ir_ref_inst_gen(field_ptr); return &inst->base; } static IrInstSrc *ir_build_byte_offset_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value, IrInstSrc *field_name) { IrInstSrcByteOffsetOf *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 IrInstSrc *ir_build_bit_offset_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value, IrInstSrc *field_name) { IrInstSrcBitOffsetOf *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 IrInstSrc *ir_build_type_info(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value) { IrInstSrcTypeInfo *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 IrInstSrc *ir_build_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_info) { IrInstSrcType *instruction = ir_build_instruction(irb, scope, source_node); instruction->type_info = type_info; ir_ref_instruction(type_info, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_set_eval_branch_quota(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *new_quota) { IrInstSrcSetEvalBranchQuota *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 IrInstSrc *ir_build_align_cast_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *align_bytes, IrInstSrc *target) { IrInstSrcAlignCast *instruction = ir_build_instruction(irb, scope, source_node); instruction->align_bytes = align_bytes; instruction->target = target; ir_ref_instruction(align_bytes, irb->current_basic_block); ir_ref_instruction(target, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_align_cast_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target, ZigType *result_type) { IrInstGenAlignCast *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); instruction->base.value->type = result_type; instruction->target = target; ir_ref_inst_gen(target); return &instruction->base; } static IrInstSrc *ir_build_resolve_result(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ResultLoc *result_loc, IrInstSrc *ty) { IrInstSrcResolveResult *instruction = ir_build_instruction(irb, scope, source_node); instruction->result_loc = result_loc; instruction->ty = ty; if (ty != nullptr) ir_ref_instruction(ty, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_reset_result(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ResultLoc *result_loc) { IrInstSrcResetResult *instruction = ir_build_instruction(irb, scope, source_node); instruction->result_loc = result_loc; instruction->base.is_gen = true; return &instruction->base; } static IrInstSrc *ir_build_opaque_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { IrInstSrcOpaqueType *instruction = ir_build_instruction(irb, scope, source_node); return &instruction->base; } static IrInstSrc *ir_build_set_align_stack(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *align_bytes) { IrInstSrcSetAlignStack *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 IrInstSrc *ir_build_arg_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *fn_type, IrInstSrc *arg_index, bool allow_var) { IrInstSrcArgType *instruction = ir_build_instruction(irb, scope, source_node); instruction->fn_type = fn_type; instruction->arg_index = arg_index; instruction->allow_var = allow_var; ir_ref_instruction(fn_type, irb->current_basic_block); ir_ref_instruction(arg_index, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_error_return_trace_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstErrorReturnTraceOptional optional) { IrInstSrcErrorReturnTrace *inst = ir_build_instruction(irb, scope, source_node); inst->optional = optional; return &inst->base; } static IrInstGen *ir_build_error_return_trace_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstErrorReturnTraceOptional optional, ZigType *result_type) { IrInstGenErrorReturnTrace *inst = ir_build_inst_gen(&ira->new_irb, scope, source_node); inst->base.value->type = result_type; inst->optional = optional; return &inst->base; } static IrInstSrc *ir_build_error_union(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *err_set, IrInstSrc *payload) { IrInstSrcErrorUnion *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 IrInstSrc *ir_build_atomic_rmw_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *operand_type, IrInstSrc *ptr, IrInstSrc *op, IrInstSrc *operand, IrInstSrc *ordering) { IrInstSrcAtomicRmw *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; ir_ref_instruction(operand_type, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); ir_ref_instruction(op, irb->current_basic_block); ir_ref_instruction(operand, irb->current_basic_block); ir_ref_instruction(ordering, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_atomic_rmw_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *ptr, IrInstGen *operand, AtomicRmwOp op, AtomicOrder ordering, ZigType *operand_type) { IrInstGenAtomicRmw *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = operand_type; instruction->ptr = ptr; instruction->op = op; instruction->operand = operand; instruction->ordering = ordering; ir_ref_inst_gen(ptr); ir_ref_inst_gen(operand); return &instruction->base; } static IrInstSrc *ir_build_atomic_load_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *operand_type, IrInstSrc *ptr, IrInstSrc *ordering) { IrInstSrcAtomicLoad *instruction = ir_build_instruction(irb, scope, source_node); instruction->operand_type = operand_type; instruction->ptr = ptr; instruction->ordering = ordering; ir_ref_instruction(operand_type, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); ir_ref_instruction(ordering, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_atomic_load_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *ptr, AtomicOrder ordering, ZigType *operand_type) { IrInstGenAtomicLoad *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = operand_type; instruction->ptr = ptr; instruction->ordering = ordering; ir_ref_inst_gen(ptr); return &instruction->base; } static IrInstSrc *ir_build_atomic_store_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *operand_type, IrInstSrc *ptr, IrInstSrc *value, IrInstSrc *ordering) { IrInstSrcAtomicStore *instruction = ir_build_instruction(irb, scope, source_node); instruction->operand_type = operand_type; instruction->ptr = ptr; instruction->value = value; instruction->ordering = ordering; ir_ref_instruction(operand_type, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); ir_ref_instruction(value, irb->current_basic_block); ir_ref_instruction(ordering, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_atomic_store_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *ptr, IrInstGen *value, AtomicOrder ordering) { IrInstGenAtomicStore *instruction = ir_build_inst_void(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->ptr = ptr; instruction->value = value; instruction->ordering = ordering; ir_ref_inst_gen(ptr); ir_ref_inst_gen(value); return &instruction->base; } static IrInstSrc *ir_build_save_err_ret_addr_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { IrInstSrcSaveErrRetAddr *inst = ir_build_instruction(irb, scope, source_node); return &inst->base; } static IrInstGen *ir_build_save_err_ret_addr_gen(IrAnalyze *ira, IrInst *source_instr) { IrInstGenSaveErrRetAddr *inst = ir_build_inst_void(&ira->new_irb, source_instr->scope, source_instr->source_node); return &inst->base; } static IrInstSrc *ir_build_add_implicit_return_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value, ResultLocReturn *result_loc_ret) { IrInstSrcAddImplicitReturnType *inst = ir_build_instruction(irb, scope, source_node); inst->value = value; inst->result_loc_ret = result_loc_ret; ir_ref_instruction(value, irb->current_basic_block); return &inst->base; } static IrInstSrc *ir_build_has_decl(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *container, IrInstSrc *name) { IrInstSrcHasDecl *instruction = ir_build_instruction(irb, scope, source_node); instruction->container = container; instruction->name = name; ir_ref_instruction(container, irb->current_basic_block); ir_ref_instruction(name, irb->current_basic_block); return &instruction->base; } static IrInstSrc *ir_build_undeclared_identifier(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *name) { IrInstSrcUndeclaredIdent *instruction = ir_build_instruction(irb, scope, source_node); instruction->name = name; return &instruction->base; } static IrInstSrc *ir_build_check_runtime_scope(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *scope_is_comptime, IrInstSrc *is_comptime) { IrInstSrcCheckRuntimeScope *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 IrInstSrc *ir_build_union_init_named_field(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *union_type, IrInstSrc *field_name, IrInstSrc *field_result_loc, IrInstSrc *result_loc) { IrInstSrcUnionInitNamedField *instruction = ir_build_instruction(irb, scope, source_node); instruction->union_type = union_type; instruction->field_name = field_name; instruction->field_result_loc = field_result_loc; instruction->result_loc = result_loc; ir_ref_instruction(union_type, irb->current_basic_block); ir_ref_instruction(field_name, irb->current_basic_block); ir_ref_instruction(field_result_loc, irb->current_basic_block); if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_vector_to_array(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_type, IrInstGen *vector, IrInstGen *result_loc) { IrInstGenVectorToArray *instruction = ir_build_inst_gen(&ira->new_irb, source_instruction->scope, source_instruction->source_node); instruction->base.value->type = result_type; instruction->vector = vector; instruction->result_loc = result_loc; ir_ref_inst_gen(vector); ir_ref_inst_gen(result_loc); return &instruction->base; } static IrInstGen *ir_build_ptr_of_array_to_slice(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_type, IrInstGen *operand, IrInstGen *result_loc) { IrInstGenPtrOfArrayToSlice *instruction = ir_build_inst_gen(&ira->new_irb, source_instruction->scope, source_instruction->source_node); instruction->base.value->type = result_type; instruction->operand = operand; instruction->result_loc = result_loc; ir_ref_inst_gen(operand); ir_ref_inst_gen(result_loc); return &instruction->base; } static IrInstGen *ir_build_array_to_vector(IrAnalyze *ira, IrInst *source_instruction, IrInstGen *array, ZigType *result_type) { IrInstGenArrayToVector *instruction = ir_build_inst_gen(&ira->new_irb, source_instruction->scope, source_instruction->source_node); instruction->base.value->type = result_type; instruction->array = array; ir_ref_inst_gen(array); return &instruction->base; } static IrInstGen *ir_build_assert_zero(IrAnalyze *ira, IrInst *source_instruction, IrInstGen *target) { IrInstGenAssertZero *instruction = ir_build_inst_gen(&ira->new_irb, source_instruction->scope, source_instruction->source_node); instruction->base.value->type = ira->codegen->builtin_types.entry_void; instruction->target = target; ir_ref_inst_gen(target); return &instruction->base; } static IrInstGen *ir_build_assert_non_null(IrAnalyze *ira, IrInst *source_instruction, IrInstGen *target) { IrInstGenAssertNonNull *instruction = ir_build_inst_gen(&ira->new_irb, source_instruction->scope, source_instruction->source_node); instruction->base.value->type = ira->codegen->builtin_types.entry_void; instruction->target = target; ir_ref_inst_gen(target); return &instruction->base; } static IrInstSrc *ir_build_alloca_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *align, const char *name_hint, IrInstSrc *is_comptime) { IrInstSrcAlloca *instruction = ir_build_instruction(irb, scope, source_node); instruction->base.is_gen = true; instruction->align = align; instruction->name_hint = name_hint; instruction->is_comptime = is_comptime; if (align != nullptr) ir_ref_instruction(align, irb->current_basic_block); if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block); return &instruction->base; } static IrInstGenAlloca *ir_build_alloca_gen(IrAnalyze *ira, IrInst *source_instruction, uint32_t align, const char *name_hint) { IrInstGenAlloca *instruction = ir_create_inst_gen(&ira->new_irb, source_instruction->scope, source_instruction->source_node); instruction->align = align; instruction->name_hint = name_hint; return instruction; } static IrInstSrc *ir_build_end_expr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value, ResultLoc *result_loc) { IrInstSrcEndExpr *instruction = ir_build_instruction(irb, scope, source_node); instruction->base.is_gen = true; instruction->value = value; instruction->result_loc = result_loc; ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } static IrInstSrcSuspendBegin *ir_build_suspend_begin_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { return ir_build_instruction(irb, scope, source_node); } static IrInstGen *ir_build_suspend_begin_gen(IrAnalyze *ira, IrInst *source_instr) { IrInstGenSuspendBegin *inst = ir_build_inst_void(&ira->new_irb, source_instr->scope, source_instr->source_node); return &inst->base; } static IrInstSrc *ir_build_suspend_finish_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrcSuspendBegin *begin) { IrInstSrcSuspendFinish *inst = ir_build_instruction(irb, scope, source_node); inst->begin = begin; ir_ref_instruction(&begin->base, irb->current_basic_block); return &inst->base; } static IrInstGen *ir_build_suspend_finish_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGenSuspendBegin *begin) { IrInstGenSuspendFinish *inst = ir_build_inst_void(&ira->new_irb, source_instr->scope, source_instr->source_node); inst->begin = begin; ir_ref_inst_gen(&begin->base); return &inst->base; } static IrInstSrc *ir_build_await_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *frame, ResultLoc *result_loc, bool is_nosuspend) { IrInstSrcAwait *instruction = ir_build_instruction(irb, scope, source_node); instruction->frame = frame; instruction->result_loc = result_loc; instruction->is_nosuspend = is_nosuspend; ir_ref_instruction(frame, irb->current_basic_block); return &instruction->base; } static IrInstGenAwait *ir_build_await_gen(IrAnalyze *ira, IrInst *source_instruction, IrInstGen *frame, ZigType *result_type, IrInstGen *result_loc, bool is_nosuspend) { IrInstGenAwait *instruction = ir_build_inst_gen(&ira->new_irb, source_instruction->scope, source_instruction->source_node); instruction->base.value->type = result_type; instruction->frame = frame; instruction->result_loc = result_loc; instruction->is_nosuspend = is_nosuspend; ir_ref_inst_gen(frame); if (result_loc != nullptr) ir_ref_inst_gen(result_loc); return instruction; } static IrInstSrc *ir_build_resume_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *frame) { IrInstSrcResume *instruction = ir_build_instruction(irb, scope, source_node); instruction->frame = frame; ir_ref_instruction(frame, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_resume_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *frame) { IrInstGenResume *instruction = ir_build_inst_void(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->frame = frame; ir_ref_inst_gen(frame); return &instruction->base; } static IrInstSrcSpillBegin *ir_build_spill_begin_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *operand, SpillId spill_id) { IrInstSrcSpillBegin *instruction = ir_build_instruction(irb, scope, source_node); instruction->operand = operand; instruction->spill_id = spill_id; ir_ref_instruction(operand, irb->current_basic_block); return instruction; } static IrInstGen *ir_build_spill_begin_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand, SpillId spill_id) { IrInstGenSpillBegin *instruction = ir_build_inst_void(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->operand = operand; instruction->spill_id = spill_id; ir_ref_inst_gen(operand); return &instruction->base; } static IrInstSrc *ir_build_spill_end_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrcSpillBegin *begin) { IrInstSrcSpillEnd *instruction = ir_build_instruction(irb, scope, source_node); instruction->begin = begin; ir_ref_instruction(&begin->base, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_spill_end_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGenSpillBegin *begin, ZigType *result_type) { IrInstGenSpillEnd *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = result_type; instruction->begin = begin; ir_ref_inst_gen(&begin->base); return &instruction->base; } static IrInstGen *ir_build_vector_extract_elem(IrAnalyze *ira, IrInst *source_instruction, IrInstGen *vector, IrInstGen *index) { IrInstGenVectorExtractElem *instruction = ir_build_inst_gen( &ira->new_irb, source_instruction->scope, source_instruction->source_node); instruction->base.value->type = vector->value->type->data.vector.elem_type; instruction->vector = vector; instruction->index = index; ir_ref_inst_gen(vector); ir_ref_inst_gen(index); return &instruction->base; } static IrInstSrc *ir_build_wasm_memory_size_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { IrInstSrcWasmMemorySize *instruction = ir_build_instruction(irb, scope, source_node); return &instruction->base; } static IrInstGen *ir_build_wasm_memory_size_gen(IrAnalyze *ira, IrInst *source_instr) { IrInstGenWasmMemorySize *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = ira->codegen->builtin_types.entry_i32; return &instruction->base; } static IrInstSrc *ir_build_wasm_memory_grow_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *delta) { IrInstSrcWasmMemoryGrow *instruction = ir_build_instruction(irb, scope, source_node); instruction->delta = delta; ir_ref_instruction(delta, irb->current_basic_block); return &instruction->base; } static IrInstGen *ir_build_wasm_memory_grow_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *delta) { IrInstGenWasmMemoryGrow *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); instruction->base.value->type = ira->codegen->builtin_types.entry_i32; instruction->delta = delta; ir_ref_inst_gen(delta); return &instruction->base; } static void ir_count_defers(IrBuilderSrc *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 ScopeIdNoSuspend: case ScopeIdRuntime: case ScopeIdTypeOf: case ScopeIdExpr: scope = scope->parent; continue; case ScopeIdDeferExpr: case ScopeIdCImport: zig_unreachable(); } } } static IrInstSrc *ir_mark_gen(IrInstSrc *instruction) { instruction->is_gen = true; return instruction; } static bool ir_gen_defers_for_block(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, bool *is_noreturn, IrInstSrc *err_value) { Scope *scope = inner_scope; if (is_noreturn != nullptr) *is_noreturn = false; while (scope != outer_scope) { if (!scope) return true; switch (scope->id) { case ScopeIdDefer: { AstNode *defer_node = scope->source_node; assert(defer_node->type == NodeTypeDefer); ReturnKind defer_kind = defer_node->data.defer.kind; AstNode *defer_expr_node = defer_node->data.defer.expr; AstNode *defer_var_node = defer_node->data.defer.err_payload; if (defer_kind == ReturnKindError && err_value == nullptr) { // This is an `errdefer` but we're generating code for a // `return` that doesn't return an error, skip it scope = scope->parent; continue; } Scope *defer_expr_scope = defer_node->data.defer.expr_scope; if (defer_var_node != nullptr) { assert(defer_kind == ReturnKindError); assert(defer_var_node->type == NodeTypeSymbol); Buf *var_name = defer_var_node->data.symbol_expr.symbol; if (defer_expr_node->type == NodeTypeUnreachable) { add_node_error(irb->codegen, defer_var_node, buf_sprintf("unused variable: '%s'", buf_ptr(var_name))); return false; } IrInstSrc *is_comptime; if (ir_should_inline(irb->exec, defer_expr_scope)) { is_comptime = ir_build_const_bool(irb, defer_expr_scope, defer_expr_node, true); } else { is_comptime = ir_build_test_comptime(irb, defer_expr_scope, defer_expr_node, err_value); } ZigVar *err_var = ir_create_var(irb, defer_var_node, defer_expr_scope, var_name, true, true, false, is_comptime); build_decl_var_and_init(irb, defer_expr_scope, defer_var_node, err_var, err_value, buf_ptr(var_name), is_comptime); defer_expr_scope = err_var->child_scope; } IrInstSrc *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); if (defer_expr_value == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; if (defer_expr_value->is_noreturn) { if (is_noreturn != nullptr) *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 true; case ScopeIdBlock: case ScopeIdVarDecl: case ScopeIdLoop: case ScopeIdSuspend: case ScopeIdCompTime: case ScopeIdNoSuspend: case ScopeIdRuntime: case ScopeIdTypeOf: case ScopeIdExpr: scope = scope->parent; continue; case ScopeIdDeferExpr: case ScopeIdCImport: zig_unreachable(); } } return true; } static void ir_set_cursor_at_end_gen(IrBuilderGen *irb, IrBasicBlockGen *basic_block) { assert(basic_block); irb->current_basic_block = basic_block; } static void ir_set_cursor_at_end(IrBuilderSrc *irb, IrBasicBlockSrc *basic_block) { assert(basic_block); irb->current_basic_block = basic_block; } static void ir_append_basic_block_gen(IrBuilderGen *irb, IrBasicBlockGen *bb) { assert(!bb->already_appended); bb->already_appended = true; irb->exec->basic_block_list.append(bb); } static void ir_set_cursor_at_end_and_append_block_gen(IrBuilderGen *irb, IrBasicBlockGen *basic_block) { ir_append_basic_block_gen(irb, basic_block); ir_set_cursor_at_end_gen(irb, basic_block); } static void ir_set_cursor_at_end_and_append_block(IrBuilderSrc *irb, IrBasicBlockSrc *basic_block) { basic_block->index = irb->exec->basic_block_list.length; irb->exec->basic_block_list.append(basic_block); ir_set_cursor_at_end(irb, basic_block); } static ScopeSuspend *get_scope_suspend(Scope *scope) { while (scope) { if (scope->id == ScopeIdSuspend) return (ScopeSuspend *)scope; if (scope->id == ScopeIdFnDef) return nullptr; scope = scope->parent; } return nullptr; } static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) { while (scope) { if (scope->id == ScopeIdDeferExpr) return (ScopeDeferExpr *)scope; if (scope->id == ScopeIdFnDef) return nullptr; scope = scope->parent; } return nullptr; } static IrInstSrc *ir_gen_return(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { assert(node->type == NodeTypeReturnExpr); ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(scope); if (scope_defer_expr) { if (!scope_defer_expr->reported_err) { add_node_error(irb->codegen, node, buf_sprintf("cannot return from defer expression")); scope_defer_expr->reported_err = true; } return irb->codegen->invalid_inst_src; } Scope *outer_scope = irb->exec->begin_scope; AstNode *expr_node = node->data.return_expr.expr; switch (node->data.return_expr.kind) { case ReturnKindUnconditional: { ResultLocReturn *result_loc_ret = heap::c_allocator.create(); result_loc_ret->base.id = ResultLocIdReturn; ir_build_reset_result(irb, scope, node, &result_loc_ret->base); IrInstSrc *return_value; if (expr_node) { // Temporarily set this so that if we return a type it gets the name of the function ZigFn *prev_name_fn = irb->exec->name_fn; irb->exec->name_fn = exec_fn_entry(irb->exec); return_value = ir_gen_node_extra(irb, expr_node, scope, LValNone, &result_loc_ret->base); irb->exec->name_fn = prev_name_fn; if (return_value == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; } else { return_value = ir_build_const_void(irb, scope, node); ir_build_end_expr(irb, scope, node, return_value, &result_loc_ret->base); } ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, return_value, result_loc_ret)); size_t defer_counts[2]; ir_count_defers(irb, scope, outer_scope, defer_counts); bool have_err_defers = defer_counts[ReturnKindError] > 0; if (!have_err_defers && !irb->codegen->have_err_ret_tracing) { // only generate unconditional defers if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, nullptr)) return irb->codegen->invalid_inst_src; IrInstSrc *result = ir_build_return_src(irb, scope, node, nullptr); result_loc_ret->base.source_instruction = result; return result; } bool should_inline = ir_should_inline(irb->exec, scope); IrBasicBlockSrc *err_block = ir_create_basic_block(irb, scope, "ErrRetErr"); IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, scope, "ErrRetOk"); IrInstSrc *is_err = ir_build_test_err_src(irb, scope, node, return_value, false, true); IrInstSrc *is_comptime; if (should_inline) { is_comptime = ir_build_const_bool(irb, scope, node, should_inline); } else { is_comptime = ir_build_test_comptime(irb, scope, node, is_err); } ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err, err_block, ok_block, is_comptime)); IrBasicBlockSrc *ret_stmt_block = ir_create_basic_block(irb, scope, "RetStmt"); ir_set_cursor_at_end_and_append_block(irb, err_block); if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, return_value)) return irb->codegen->invalid_inst_src; if (irb->codegen->have_err_ret_tracing && !should_inline) { ir_build_save_err_ret_addr_src(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 (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, nullptr)) return irb->codegen->invalid_inst_src; ir_build_br(irb, scope, node, ret_stmt_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, ret_stmt_block); IrInstSrc *result = ir_build_return_src(irb, scope, node, nullptr); result_loc_ret->base.source_instruction = result; return result; } case ReturnKindError: { assert(expr_node); IrInstSrc *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr); if (err_union_ptr == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrc *is_err_val = ir_build_test_err_src(irb, scope, node, err_union_ptr, true, false); IrBasicBlockSrc *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn"); IrBasicBlockSrc *continue_block = ir_create_basic_block(irb, scope, "ErrRetContinue"); IrInstSrc *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); IrInstSrc *err_val_ptr = ir_build_unwrap_err_code_src(irb, scope, node, err_union_ptr); IrInstSrc *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr); ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, err_val, nullptr)); IrInstSrcSpillBegin *spill_begin = ir_build_spill_begin_src(irb, scope, node, err_val, SpillIdRetErrCode); ResultLocReturn *result_loc_ret = heap::c_allocator.create(); result_loc_ret->base.id = ResultLocIdReturn; ir_build_reset_result(irb, scope, node, &result_loc_ret->base); ir_build_end_expr(irb, scope, node, err_val, &result_loc_ret->base); bool is_noreturn = false; if (!ir_gen_defers_for_block(irb, scope, outer_scope, &is_noreturn, err_val)) { return irb->codegen->invalid_inst_src; } if (!is_noreturn) { if (irb->codegen->have_err_ret_tracing && !should_inline) { ir_build_save_err_ret_addr_src(irb, scope, node); } err_val = ir_build_spill_end_src(irb, scope, node, spill_begin); IrInstSrc *ret_inst = ir_build_return_src(irb, scope, node, err_val); result_loc_ret->base.source_instruction = ret_inst; } ir_set_cursor_at_end_and_append_block(irb, continue_block); IrInstSrc *unwrapped_ptr = ir_build_unwrap_err_payload_src(irb, scope, node, err_union_ptr, false, false); if (lval == LValPtr) return unwrapped_ptr; else return ir_expr_wrap(irb, scope, ir_build_load_ptr(irb, scope, node, unwrapped_ptr), result_loc); } } zig_unreachable(); } static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_scope, Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *is_comptime, bool skip_name_check) { ZigVar *variable_entry = heap::c_allocator.create(); variable_entry->parent_scope = parent_scope; variable_entry->shadowable = is_shadowable; variable_entry->is_comptime = is_comptime; variable_entry->src_arg_index = SIZE_MAX; variable_entry->const_value = codegen->pass1_arena->create(); if (is_comptime != nullptr) { is_comptime->base.ref_count += 1; } if (name) { variable_entry->name = strdup(buf_ptr(name)); if (!skip_name_check) { ZigVar *existing_var = find_variable(codegen, parent_scope, name, nullptr); if (existing_var && !existing_var->shadowable) { if (existing_var->var_type == nullptr || !type_is_invalid(existing_var->var_type)) { ErrorMsg *msg = add_node_error(codegen, node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); } variable_entry->var_type = codegen->builtin_types.entry_invalid; } else { ZigType *type; if (get_primitive_type(codegen, name, &type) != ErrorPrimitiveTypeNotFound) { add_node_error(codegen, node, buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name))); variable_entry->var_type = codegen->builtin_types.entry_invalid; } else { Tld *tld = find_decl(codegen, parent_scope, name); if (tld != nullptr) { bool want_err_msg = true; if (tld->id == TldIdVar) { ZigVar *var = reinterpret_cast(tld)->var; if (var != nullptr && var->var_type != nullptr && type_is_invalid(var->var_type)) { want_err_msg = false; } } if (want_err_msg) { ErrorMsg *msg = add_node_error(codegen, node, buf_sprintf("redefinition of '%s'", buf_ptr(name))); add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition is here")); } variable_entry->var_type = codegen->builtin_types.entry_invalid; } } } } } else { assert(is_shadowable); // TODO make this name not actually be in scope. user should be able to make a variable called "_anon" // might already be solved, let's just make sure it has test coverage // maybe we put a prefix on this so the debug info doesn't clobber user debug info for same named variables variable_entry->name = "_anon"; } variable_entry->src_is_const = src_is_const; variable_entry->gen_is_const = gen_is_const; variable_entry->decl_node = node; variable_entry->child_scope = create_var_scope(codegen, node, parent_scope, variable_entry); return variable_entry; } // Set name to nullptr to make the variable anonymous (not visible to programmer). // After you call this function var->child_scope has the variable in scope static ZigVar *ir_create_var(IrBuilderSrc *irb, AstNode *node, Scope *scope, Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *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); assert(var->child_scope); return var; } static ResultLocPeer *create_peer_result(ResultLocPeerParent *peer_parent) { ResultLocPeer *result = heap::c_allocator.create(); result->base.id = ResultLocIdPeer; result->base.source_instruction = peer_parent->base.source_instruction; result->parent = peer_parent; result->base.allow_write_through_const = peer_parent->parent->allow_write_through_const; return result; } static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *block_node, LVal lval, ResultLoc *result_loc) { assert(block_node->type == NodeTypeBlock); ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; ScopeBlock *scope_block = create_block_scope(irb->codegen, block_node, parent_scope); Scope *outer_block_scope = &scope_block->base; Scope *child_scope = outer_block_scope; ZigFn *fn_entry = scope_fn_entry(parent_scope); if (fn_entry && fn_entry->child_scope == parent_scope) { fn_entry->def_scope = scope_block; } if (block_node->data.block.statements.length == 0) { // {} return ir_lval_wrap(irb, parent_scope, ir_build_const_void(irb, child_scope, block_node), lval, result_loc); } if (block_node->data.block.name != nullptr) { scope_block->lval = lval; scope_block->incoming_blocks = &incoming_blocks; scope_block->incoming_values = &incoming_values; scope_block->end_block = ir_create_basic_block(irb, parent_scope, "BlockEnd"); scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node, ir_should_inline(irb->exec, parent_scope)); scope_block->peer_parent = heap::c_allocator.create(); scope_block->peer_parent->base.id = ResultLocIdPeerParent; scope_block->peer_parent->base.source_instruction = scope_block->is_comptime; scope_block->peer_parent->base.allow_write_through_const = result_loc->allow_write_through_const; scope_block->peer_parent->end_bb = scope_block->end_block; scope_block->peer_parent->is_comptime = scope_block->is_comptime; scope_block->peer_parent->parent = result_loc; ir_build_reset_result(irb, parent_scope, block_node, &scope_block->peer_parent->base); } bool is_continuation_unreachable = false; bool found_invalid_inst = false; IrInstSrc *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); IrInstSrc *statement_value = ir_gen_node(irb, statement_node, child_scope); if (statement_value == irb->codegen->invalid_inst_src) { // keep generating all the elements of the block in case of error, // we want to collect other compile errors found_invalid_inst = true; continue; } is_continuation_unreachable = instr_is_unreachable(statement_value); if (is_continuation_unreachable) { // keep the last noreturn statement value around in case we need to return it noreturn_return_value = statement_value; } // This logic must be kept in sync with // [STMT_EXPR_TEST_THING] <--- (search this token) if (statement_node->type == NodeTypeDefer) { // defer starts a new scope child_scope = statement_node->data.defer.child_scope; assert(child_scope); } else if (statement_value->id == IrInstSrcIdDeclVar) { // variable declarations start a new scope IrInstSrcDeclVar *decl_var_instruction = (IrInstSrcDeclVar *)statement_value; child_scope = decl_var_instruction->var->child_scope; } else if (!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 (found_invalid_inst) return irb->codegen->invalid_inst_src; if (is_continuation_unreachable) { assert(noreturn_return_value != nullptr); if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) { return noreturn_return_value; } if (scope_block->peer_parent != nullptr && scope_block->peer_parent->peers.length != 0) { scope_block->peer_parent->peers.last()->next_bb = scope_block->end_block; } ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block); IrInstSrc *phi = ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, incoming_blocks.items, incoming_values.items, scope_block->peer_parent); return ir_expr_wrap(irb, parent_scope, phi, result_loc); } else { incoming_blocks.append(irb->current_basic_block); IrInstSrc *else_expr_result = ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node)); if (scope_block->peer_parent != nullptr) { ResultLocPeer *peer_result = create_peer_result(scope_block->peer_parent); scope_block->peer_parent->peers.append(peer_result); ir_build_end_expr(irb, parent_scope, block_node, else_expr_result, &peer_result->base); if (scope_block->peer_parent->peers.length != 0) { scope_block->peer_parent->peers.last()->next_bb = scope_block->end_block; } } incoming_values.append(else_expr_result); } bool is_return_from_fn = block_node == irb->main_block_node; if (!is_return_from_fn) { if (!ir_gen_defers_for_block(irb, child_scope, outer_block_scope, nullptr, nullptr)) return irb->codegen->invalid_inst_src; } IrInstSrc *result; if (block_node->data.block.name != nullptr) { ir_mark_gen(ir_build_br(irb, parent_scope, block_node, scope_block->end_block, scope_block->is_comptime)); ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block); IrInstSrc *phi = ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, incoming_blocks.items, incoming_values.items, scope_block->peer_parent); result = ir_expr_wrap(irb, parent_scope, phi, result_loc); } else { IrInstSrc *void_inst = ir_mark_gen(ir_build_const_void(irb, child_scope, block_node)); result = ir_lval_wrap(irb, parent_scope, void_inst, lval, result_loc); } if (!is_return_from_fn) return result; // no need for save_err_ret_addr because this cannot return error // only generate unconditional defers ir_mark_gen(ir_build_add_implicit_return_type(irb, child_scope, block_node, result, nullptr)); ResultLocReturn *result_loc_ret = heap::c_allocator.create(); result_loc_ret->base.id = ResultLocIdReturn; ir_build_reset_result(irb, parent_scope, block_node, &result_loc_ret->base); ir_mark_gen(ir_build_end_expr(irb, parent_scope, block_node, result, &result_loc_ret->base)); if (!ir_gen_defers_for_block(irb, child_scope, outer_block_scope, nullptr, nullptr)) return irb->codegen->invalid_inst_src; return ir_mark_gen(ir_build_return_src(irb, child_scope, result->base.source_node, result)); } static IrInstSrc *ir_gen_bin_op_id(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrBinOp op_id) { Scope *inner_scope = scope; if (op_id == IrBinOpArrayCat || op_id == IrBinOpArrayMult) { inner_scope = create_comptime_scope(irb->codegen, node, scope); } IrInstSrc *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, inner_scope); IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, inner_scope); if (op1 == irb->codegen->invalid_inst_src || op2 == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; return ir_build_bin_op(irb, scope, node, op_id, op1, op2, true); } static IrInstSrc *ir_gen_merge_err_sets(IrBuilderSrc *irb, Scope *scope, AstNode *node) { IrInstSrc *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); if (op1 == irb->codegen->invalid_inst_src || op2 == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; // TODO only pass type_name when the || operator is the top level AST node in the var decl expr Buf bare_name = BUF_INIT; Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error", scope, node, &bare_name); return ir_build_merge_err_sets(irb, scope, node, op1, op2, type_name); } static IrInstSrc *ir_gen_assign(IrBuilderSrc *irb, Scope *scope, AstNode *node) { IrInstSrc *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValAssign, nullptr); if (lvalue == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; ResultLocInstruction *result_loc_inst = heap::c_allocator.create(); result_loc_inst->base.id = ResultLocIdInstruction; result_loc_inst->base.source_instruction = lvalue; ir_ref_instruction(lvalue, irb->current_basic_block); ir_build_reset_result(irb, scope, node, &result_loc_inst->base); IrInstSrc *rvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op2, scope, LValNone, &result_loc_inst->base); if (rvalue == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; return ir_build_const_void(irb, scope, node); } static IrInstSrc *ir_gen_assign_merge_err_sets(IrBuilderSrc *irb, Scope *scope, AstNode *node) { IrInstSrc *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValAssign, nullptr); if (lvalue == irb->codegen->invalid_inst_src) return lvalue; IrInstSrc *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue); IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); if (op2 == irb->codegen->invalid_inst_src) return op2; IrInstSrc *result = ir_build_merge_err_sets(irb, scope, node, op1, op2, nullptr); ir_build_store_ptr(irb, scope, node, lvalue, result); return ir_build_const_void(irb, scope, node); } static IrInstSrc *ir_gen_assign_op(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrBinOp op_id) { IrInstSrc *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValAssign, nullptr); if (lvalue == irb->codegen->invalid_inst_src) return lvalue; IrInstSrc *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue); IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); if (op2 == irb->codegen->invalid_inst_src) return op2; IrInstSrc *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 IrInstSrc *ir_gen_bool_or(IrBuilderSrc *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); IrInstSrc *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); if (val1 == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrBasicBlockSrc *post_val1_block = irb->current_basic_block; IrInstSrc *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 IrBasicBlockSrc *false_block = ir_create_basic_block(irb, scope, "BoolOrFalse"); // block for when val1 == true (don't even evaluate the second part) IrBasicBlockSrc *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); IrInstSrc *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); if (val2 == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrBasicBlockSrc *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); IrInstSrc **incoming_values = heap::c_allocator.allocate(2); incoming_values[0] = val1; incoming_values[1] = val2; IrBasicBlockSrc **incoming_blocks = heap::c_allocator.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, nullptr); } static IrInstSrc *ir_gen_bool_and(IrBuilderSrc *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); IrInstSrc *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); if (val1 == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrBasicBlockSrc *post_val1_block = irb->current_basic_block; IrInstSrc *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 IrBasicBlockSrc *true_block = ir_create_basic_block(irb, scope, "BoolAndTrue"); // block for when val1 == false (don't even evaluate the second part) IrBasicBlockSrc *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); IrInstSrc *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); if (val2 == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrBasicBlockSrc *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); IrInstSrc **incoming_values = heap::c_allocator.allocate(2); incoming_values[0] = val1; incoming_values[1] = val2; IrBasicBlockSrc **incoming_blocks = heap::c_allocator.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, nullptr); } static ResultLocPeerParent *ir_build_result_peers(IrBuilderSrc *irb, IrInstSrc *cond_br_inst, IrBasicBlockSrc *end_block, ResultLoc *parent, IrInstSrc *is_comptime) { ResultLocPeerParent *peer_parent = heap::c_allocator.create(); peer_parent->base.id = ResultLocIdPeerParent; peer_parent->base.source_instruction = cond_br_inst; peer_parent->base.allow_write_through_const = parent->allow_write_through_const; peer_parent->end_bb = end_block; peer_parent->is_comptime = is_comptime; peer_parent->parent = parent; IrInstSrc *popped_inst = irb->current_basic_block->instruction_list.pop(); ir_assert(popped_inst == cond_br_inst, &cond_br_inst->base); ir_build_reset_result(irb, cond_br_inst->base.scope, cond_br_inst->base.source_node, &peer_parent->base); irb->current_basic_block->instruction_list.append(popped_inst); return peer_parent; } static ResultLocPeerParent *ir_build_binary_result_peers(IrBuilderSrc *irb, IrInstSrc *cond_br_inst, IrBasicBlockSrc *else_block, IrBasicBlockSrc *end_block, ResultLoc *parent, IrInstSrc *is_comptime) { ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, parent, is_comptime); peer_parent->peers.append(create_peer_result(peer_parent)); peer_parent->peers.last()->next_bb = else_block; peer_parent->peers.append(create_peer_result(peer_parent)); peer_parent->peers.last()->next_bb = end_block; return peer_parent; } static IrInstSrc *ir_gen_orelse(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval, ResultLoc *result_loc) { assert(node->type == NodeTypeBinOpExpr); AstNode *op1_node = node->data.bin_op_expr.op1; AstNode *op2_node = node->data.bin_op_expr.op2; IrInstSrc *maybe_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr, nullptr); if (maybe_ptr == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrc *maybe_val = ir_build_load_ptr(irb, parent_scope, node, maybe_ptr); IrInstSrc *is_non_null = ir_build_test_non_null_src(irb, parent_scope, node, maybe_val); IrInstSrc *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); } IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, parent_scope, "OptionalNonNull"); IrBasicBlockSrc *null_block = ir_create_basic_block(irb, parent_scope, "OptionalNull"); IrBasicBlockSrc *end_block = ir_create_basic_block(irb, parent_scope, "OptionalEnd"); IrInstSrc *cond_br_inst = ir_build_cond_br(irb, parent_scope, node, is_non_null, ok_block, null_block, is_comptime); ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, ok_block, end_block, result_loc, is_comptime); ir_set_cursor_at_end_and_append_block(irb, null_block); IrInstSrc *null_result = ir_gen_node_extra(irb, op2_node, parent_scope, LValNone, &peer_parent->peers.at(0)->base); if (null_result == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrBasicBlockSrc *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); IrInstSrc *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, parent_scope, node, maybe_ptr, false); IrInstSrc *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr); ir_build_end_expr(irb, parent_scope, node, unwrapped_payload, &peer_parent->peers.at(1)->base); IrBasicBlockSrc *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); IrInstSrc **incoming_values = heap::c_allocator.allocate(2); incoming_values[0] = null_result; incoming_values[1] = unwrapped_payload; IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate(2); incoming_blocks[0] = after_null_block; incoming_blocks[1] = after_ok_block; IrInstSrc *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent); return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc); } static IrInstSrc *ir_gen_error_union(IrBuilderSrc *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; IrInstSrc *err_set = ir_gen_node(irb, op1_node, parent_scope); if (err_set == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrc *payload = ir_gen_node(irb, op2_node, parent_scope); if (payload == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; return ir_build_error_union(irb, parent_scope, node, err_set, payload); } static IrInstSrc *ir_gen_bin_op(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { assert(node->type == NodeTypeBinOpExpr); BinOpType bin_op_type = node->data.bin_op_expr.bin_op; switch (bin_op_type) { case BinOpTypeInvalid: zig_unreachable(); case BinOpTypeAssign: return ir_lval_wrap(irb, scope, ir_gen_assign(irb, scope, node), lval, result_loc); case BinOpTypeAssignTimes: return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMult), lval, result_loc); case BinOpTypeAssignTimesWrap: return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap), lval, result_loc); case BinOpTypeAssignDiv: return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpDivUnspecified), lval, result_loc); case BinOpTypeAssignMod: return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpRemUnspecified), lval, result_loc); case BinOpTypeAssignPlus: return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpAdd), lval, result_loc); case BinOpTypeAssignPlusWrap: return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpAddWrap), lval, result_loc); case BinOpTypeAssignMinus: return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpSub), lval, result_loc); case BinOpTypeAssignMinusWrap: return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap), lval, result_loc); case BinOpTypeAssignBitShiftLeft: return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc); case BinOpTypeAssignBitShiftRight: return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc); case BinOpTypeAssignBitAnd: return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd), lval, result_loc); case BinOpTypeAssignBitXor: return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinXor), lval, result_loc); case BinOpTypeAssignBitOr: return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinOr), lval, result_loc); case BinOpTypeAssignMergeErrorSets: return ir_lval_wrap(irb, scope, ir_gen_assign_merge_err_sets(irb, scope, node), lval, result_loc); case BinOpTypeBoolOr: return ir_lval_wrap(irb, scope, ir_gen_bool_or(irb, scope, node), lval, result_loc); case BinOpTypeBoolAnd: return ir_lval_wrap(irb, scope, ir_gen_bool_and(irb, scope, node), lval, result_loc); case BinOpTypeCmpEq: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpEq), lval, result_loc); case BinOpTypeCmpNotEq: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpNotEq), lval, result_loc); case BinOpTypeCmpLessThan: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessThan), lval, result_loc); case BinOpTypeCmpGreaterThan: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterThan), lval, result_loc); case BinOpTypeCmpLessOrEq: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessOrEq), lval, result_loc); case BinOpTypeCmpGreaterOrEq: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterOrEq), lval, result_loc); case BinOpTypeBinOr: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinOr), lval, result_loc); case BinOpTypeBinXor: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinXor), lval, result_loc); case BinOpTypeBinAnd: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd), lval, result_loc); case BinOpTypeBitShiftLeft: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc); case BinOpTypeBitShiftRight: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc); case BinOpTypeAdd: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd), lval, result_loc); case BinOpTypeAddWrap: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpAddWrap), lval, result_loc); case BinOpTypeSub: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpSub), lval, result_loc); case BinOpTypeSubWrap: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpSubWrap), lval, result_loc); case BinOpTypeMult: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMult), lval, result_loc); case BinOpTypeMultWrap: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap), lval, result_loc); case BinOpTypeDiv: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpDivUnspecified), lval, result_loc); case BinOpTypeMod: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpRemUnspecified), lval, result_loc); case BinOpTypeArrayCat: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat), lval, result_loc); case BinOpTypeArrayMult: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult), lval, result_loc); case BinOpTypeMergeErrorSets: return ir_lval_wrap(irb, scope, ir_gen_merge_err_sets(irb, scope, node), lval, result_loc); case BinOpTypeUnwrapOptional: return ir_gen_orelse(irb, scope, node, lval, result_loc); case BinOpTypeErrorUnion: return ir_lval_wrap(irb, scope, ir_gen_error_union(irb, scope, node), lval, result_loc); } zig_unreachable(); } static IrInstSrc *ir_gen_int_lit(IrBuilderSrc *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeIntLiteral); return ir_build_const_bigint(irb, scope, node, node->data.int_literal.bigint); } static IrInstSrc *ir_gen_float_lit(IrBuilderSrc *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_inst_src; } return ir_build_const_bigfloat(irb, scope, node, node->data.float_literal.bigfloat); } static IrInstSrc *ir_gen_char_lit(IrBuilderSrc *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeCharLiteral); return ir_build_const_uint(irb, scope, node, node->data.char_literal.value); } static IrInstSrc *ir_gen_null_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeNullLiteral); return ir_build_const_null(irb, scope, node); } static void populate_invalid_variable_in_scope(CodeGen *g, Scope *scope, AstNode *node, Buf *var_name) { ScopeDecls *scope_decls = nullptr; while (scope != nullptr) { if (scope->id == ScopeIdDecls) { scope_decls = reinterpret_cast(scope); } scope = scope->parent; } TldVar *tld_var = heap::c_allocator.create(); init_tld(&tld_var->base, TldIdVar, var_name, VisibModPub, node, &scope_decls->base); tld_var->base.resolution = TldResolutionInvalid; tld_var->var = add_variable(g, node, &scope_decls->base, var_name, false, g->invalid_inst_gen->value, &tld_var->base, g->builtin_types.entry_invalid); scope_decls->decl_table.put(var_name, &tld_var->base); } static IrInstSrc *ir_gen_symbol(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { Error err; assert(node->type == NodeTypeSymbol); Buf *variable_name = node->data.symbol_expr.symbol; if (buf_eql_str(variable_name, "_")) { if (lval == LValAssign) { IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, node); const_instruction->value = irb->codegen->pass1_arena->create(); const_instruction->value->type = get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_void, false); const_instruction->value->special = ConstValSpecialStatic; const_instruction->value->data.x_ptr.special = ConstPtrSpecialDiscard; return &const_instruction->base; } else { add_node_error(irb->codegen, node, buf_sprintf("`_` may only be used to assign things to")); return irb->codegen->invalid_inst_src; } } ZigType *primitive_type; if ((err = get_primitive_type(irb->codegen, variable_name, &primitive_type))) { if (err == ErrorOverflow) { add_node_error(irb->codegen, node, buf_sprintf("primitive integer type '%s' exceeds maximum bit width of 65535", buf_ptr(variable_name))); return irb->codegen->invalid_inst_src; } assert(err == ErrorPrimitiveTypeNotFound); } else { IrInstSrc *value = ir_build_const_type(irb, scope, node, primitive_type); if (lval == LValPtr || lval == LValAssign) { return ir_build_ref_src(irb, scope, node, value); } else { return ir_expr_wrap(irb, scope, value, result_loc); } } ScopeFnDef *crossed_fndef_scope; ZigVar *var = find_variable(irb->codegen, scope, variable_name, &crossed_fndef_scope); if (var) { IrInstSrc *var_ptr = ir_build_var_ptr_x(irb, scope, node, var, crossed_fndef_scope); if (lval == LValPtr || lval == LValAssign) { return var_ptr; } else { return ir_expr_wrap(irb, scope, ir_build_load_ptr(irb, scope, node, var_ptr), result_loc); } } Tld *tld = find_decl(irb->codegen, scope, variable_name); if (tld) { IrInstSrc *decl_ref = ir_build_decl_ref(irb, scope, node, tld, lval); if (lval == LValPtr || lval == LValAssign) { return decl_ref; } else { return ir_expr_wrap(irb, scope, decl_ref, result_loc); } } if (get_container_scope(node->owner)->any_imports_failed) { // skip the error message since we had a failing import in this file // if an import breaks we don't need redundant undeclared identifier errors return irb->codegen->invalid_inst_src; } return ir_build_undeclared_identifier(irb, scope, node, variable_name); } static IrInstSrc *ir_gen_array_access(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { assert(node->type == NodeTypeArrayAccessExpr); AstNode *array_ref_node = node->data.array_access_expr.array_ref_expr; IrInstSrc *array_ref_instruction = ir_gen_node_extra(irb, array_ref_node, scope, LValPtr, nullptr); if (array_ref_instruction == irb->codegen->invalid_inst_src) return array_ref_instruction; // Create an usize-typed result location to hold the subscript value, this // makes it possible for the compiler to infer the subscript expression type // if needed IrInstSrc *usize_type_inst = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, usize_type_inst, no_result_loc()); AstNode *subscript_node = node->data.array_access_expr.subscript; IrInstSrc *subscript_value = ir_gen_node_extra(irb, subscript_node, scope, LValNone, &result_loc_cast->base); if (subscript_value == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrc *subscript_instruction = ir_build_implicit_cast(irb, scope, subscript_node, subscript_value, result_loc_cast); IrInstSrc *ptr_instruction = ir_build_elem_ptr(irb, scope, node, array_ref_instruction, subscript_instruction, true, PtrLenSingle, nullptr); if (lval == LValPtr || lval == LValAssign) return ptr_instruction; IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction); return ir_expr_wrap(irb, scope, load_ptr, result_loc); } static IrInstSrc *ir_gen_field_access(IrBuilderSrc *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; IrInstSrc *container_ref_instruction = ir_gen_node_extra(irb, container_ref_node, scope, LValPtr, nullptr); if (container_ref_instruction == irb->codegen->invalid_inst_src) return container_ref_instruction; return ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name, false); } static IrInstSrc *ir_gen_overflow_op(IrBuilderSrc *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); IrInstSrc *type_value = ir_gen_node(irb, type_node, scope); if (type_value == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrc *op1 = ir_gen_node(irb, op1_node, scope); if (op1 == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrc *op2 = ir_gen_node(irb, op2_node, scope); if (op2 == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrc *result_ptr = ir_gen_node(irb, result_ptr_node, scope); if (result_ptr == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; return ir_build_overflow_op_src(irb, scope, node, op, type_value, op1, op2, result_ptr); } static IrInstSrc *ir_gen_mul_add(IrBuilderSrc *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); AstNode *type_node = node->data.fn_call_expr.params.at(0); AstNode *op1_node = node->data.fn_call_expr.params.at(1); AstNode *op2_node = node->data.fn_call_expr.params.at(2); AstNode *op3_node = node->data.fn_call_expr.params.at(3); IrInstSrc *type_value = ir_gen_node(irb, type_node, scope); if (type_value == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrc *op1 = ir_gen_node(irb, op1_node, scope); if (op1 == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrc *op2 = ir_gen_node(irb, op2_node, scope); if (op2 == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrc *op3 = ir_gen_node(irb, op3_node, scope); if (op3 == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; return ir_build_mul_add_src(irb, scope, node, type_value, op1, op2, op3); } static IrInstSrc *ir_gen_this(IrBuilderSrc *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 IrInstSrc *ir_gen_async_call(IrBuilderSrc *irb, Scope *scope, AstNode *await_node, AstNode *call_node, LVal lval, ResultLoc *result_loc) { size_t arg_offset = 3; if (call_node->data.fn_call_expr.params.length < arg_offset) { add_node_error(irb->codegen, call_node, buf_sprintf("expected at least %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize, arg_offset, call_node->data.fn_call_expr.params.length)); return irb->codegen->invalid_inst_src; } AstNode *bytes_node = call_node->data.fn_call_expr.params.at(0); IrInstSrc *bytes = ir_gen_node(irb, bytes_node, scope); if (bytes == irb->codegen->invalid_inst_src) return bytes; AstNode *ret_ptr_node = call_node->data.fn_call_expr.params.at(1); IrInstSrc *ret_ptr = ir_gen_node(irb, ret_ptr_node, scope); if (ret_ptr == irb->codegen->invalid_inst_src) return ret_ptr; AstNode *fn_ref_node = call_node->data.fn_call_expr.params.at(2); IrInstSrc *fn_ref = ir_gen_node(irb, fn_ref_node, scope); if (fn_ref == irb->codegen->invalid_inst_src) return fn_ref; size_t arg_count = call_node->data.fn_call_expr.params.length - arg_offset; IrInstSrc **args = heap::c_allocator.allocate(arg_count); for (size_t i = 0; i < arg_count; i += 1) { AstNode *arg_node = call_node->data.fn_call_expr.params.at(i + arg_offset); IrInstSrc *arg = ir_gen_node(irb, arg_node, scope); if (arg == irb->codegen->invalid_inst_src) return arg; args[i] = arg; } CallModifier modifier = (await_node == nullptr) ? CallModifierAsync : CallModifierNone; bool is_async_call_builtin = true; IrInstSrc *call = ir_build_call_src(irb, scope, call_node, nullptr, fn_ref, arg_count, args, ret_ptr, modifier, is_async_call_builtin, bytes, result_loc); return ir_lval_wrap(irb, scope, call, lval, result_loc); } static IrInstSrc *ir_gen_fn_call_with_args(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, AstNode *fn_ref_node, CallModifier modifier, IrInstSrc *options, AstNode **args_ptr, size_t args_len, LVal lval, ResultLoc *result_loc) { IrInstSrc *fn_ref = ir_gen_node(irb, fn_ref_node, scope); if (fn_ref == irb->codegen->invalid_inst_src) return fn_ref; IrInstSrc *fn_type = ir_build_typeof_1(irb, scope, source_node, fn_ref); IrInstSrc **args = heap::c_allocator.allocate(args_len); for (size_t i = 0; i < args_len; i += 1) { AstNode *arg_node = args_ptr[i]; IrInstSrc *arg_index = ir_build_const_usize(irb, scope, arg_node, i); IrInstSrc *arg_type = ir_build_arg_type(irb, scope, source_node, fn_type, arg_index, true); ResultLoc *no_result = no_result_loc(); ir_build_reset_result(irb, scope, source_node, no_result); ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, arg_type, no_result); IrInstSrc *arg = ir_gen_node_extra(irb, arg_node, scope, LValNone, &result_loc_cast->base); if (arg == irb->codegen->invalid_inst_src) return arg; args[i] = ir_build_implicit_cast(irb, scope, arg_node, arg, result_loc_cast); } IrInstSrc *fn_call; if (options != nullptr) { fn_call = ir_build_call_args(irb, scope, source_node, options, fn_ref, args, args_len, result_loc); } else { fn_call = ir_build_call_src(irb, scope, source_node, nullptr, fn_ref, args_len, args, nullptr, modifier, false, nullptr, result_loc); } return ir_lval_wrap(irb, scope, fn_call, lval, result_loc); } static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { assert(node->type == NodeTypeFnCallExpr); AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; Buf *name = fn_ref_expr->data.symbol_expr.symbol; auto entry = irb->codegen->builtin_fn_table.maybe_get(name); if (!entry) { add_node_error(irb->codegen, node, buf_sprintf("invalid builtin function: '%s'", buf_ptr(name))); return irb->codegen->invalid_inst_src; } 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_inst_src; } switch (builtin_fn->id) { case BuiltinFnIdInvalid: zig_unreachable(); case BuiltinFnIdTypeof: { Scope *sub_scope = create_typeof_scope(irb->codegen, node, scope); size_t arg_count = node->data.fn_call_expr.params.length; IrInstSrc *type_of; if (arg_count == 0) { add_node_error(irb->codegen, node, buf_sprintf("expected at least 1 argument, found 0")); return irb->codegen->invalid_inst_src; } else if (arg_count == 1) { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, sub_scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; type_of = ir_build_typeof_1(irb, scope, node, arg0_value); } else { IrInstSrc **args = heap::c_allocator.allocate(arg_count); for (size_t i = 0; i < arg_count; i += 1) { AstNode *arg_node = node->data.fn_call_expr.params.at(i); IrInstSrc *arg = ir_gen_node(irb, arg_node, sub_scope); if (arg == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; args[i] = arg; } type_of = ir_build_typeof_n(irb, scope, node, args, arg_count); } return ir_lval_wrap(irb, scope, type_of, lval, result_loc); } case BuiltinFnIdSetCold: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *set_cold = ir_build_set_cold(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, set_cold, lval, result_loc); } case BuiltinFnIdSetRuntimeSafety: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *set_safety = ir_build_set_runtime_safety(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, set_safety, lval, result_loc); } case BuiltinFnIdSetFloatMode: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *set_float_mode = ir_build_set_float_mode(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, set_float_mode, lval, result_loc); } case BuiltinFnIdSizeof: case BuiltinFnIdBitSizeof: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *size_of = ir_build_size_of(irb, scope, node, arg0_value, builtin_fn->id == BuiltinFnIdBitSizeof); return ir_lval_wrap(irb, scope, size_of, lval, result_loc); } case BuiltinFnIdImport: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *import = ir_build_import(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, import, lval, result_loc); } case BuiltinFnIdCImport: { IrInstSrc *c_import = ir_build_c_import(irb, scope, node); return ir_lval_wrap(irb, scope, c_import, lval, result_loc); } case BuiltinFnIdCInclude: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) 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_inst_src; } IrInstSrc *c_include = ir_build_c_include(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, c_include, lval, result_loc); } case BuiltinFnIdCDefine: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) 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_inst_src; } IrInstSrc *c_define = ir_build_c_define(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, c_define, lval, result_loc); } case BuiltinFnIdCUndef: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) 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_inst_src; } IrInstSrc *c_undef = ir_build_c_undef(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, c_undef, lval, result_loc); } case BuiltinFnIdCompileErr: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *compile_err = ir_build_compile_err(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, compile_err, lval, result_loc); } case BuiltinFnIdCompileLog: { IrInstSrc **args = heap::c_allocator.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_inst_src) return irb->codegen->invalid_inst_src; } IrInstSrc *compile_log = ir_build_compile_log(irb, scope, node, actual_param_count, args); return ir_lval_wrap(irb, scope, compile_log, lval, result_loc); } case BuiltinFnIdErrName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *err_name = ir_build_err_name(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, err_name, lval, result_loc); } case BuiltinFnIdEmbedFile: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *embed_file = ir_build_embed_file(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, embed_file, lval, result_loc); } case BuiltinFnIdCmpxchgWeak: case BuiltinFnIdCmpxchgStrong: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); if (arg2_value == irb->codegen->invalid_inst_src) return arg2_value; AstNode *arg3_node = node->data.fn_call_expr.params.at(3); IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope); if (arg3_value == irb->codegen->invalid_inst_src) return arg3_value; AstNode *arg4_node = node->data.fn_call_expr.params.at(4); IrInstSrc *arg4_value = ir_gen_node(irb, arg4_node, scope); if (arg4_value == irb->codegen->invalid_inst_src) return arg4_value; AstNode *arg5_node = node->data.fn_call_expr.params.at(5); IrInstSrc *arg5_value = ir_gen_node(irb, arg5_node, scope); if (arg5_value == irb->codegen->invalid_inst_src) return arg5_value; IrInstSrc *cmpxchg = ir_build_cmpxchg_src(irb, scope, node, arg0_value, arg1_value, arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak), result_loc); return ir_lval_wrap(irb, scope, cmpxchg, lval, result_loc); } case BuiltinFnIdFence: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *fence = ir_build_fence(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, fence, lval, result_loc); } case BuiltinFnIdDivExact: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivExact, arg0_value, arg1_value, true); return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); } case BuiltinFnIdDivTrunc: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivTrunc, arg0_value, arg1_value, true); return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); } case BuiltinFnIdDivFloor: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivFloor, arg0_value, arg1_value, true); return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); } case BuiltinFnIdRem: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemRem, arg0_value, arg1_value, true); return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); } case BuiltinFnIdMod: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true); return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); } case BuiltinFnIdSqrt: case BuiltinFnIdSin: case BuiltinFnIdCos: case BuiltinFnIdExp: case BuiltinFnIdExp2: case BuiltinFnIdLog: case BuiltinFnIdLog2: case BuiltinFnIdLog10: case BuiltinFnIdFabs: case BuiltinFnIdFloor: case BuiltinFnIdCeil: case BuiltinFnIdTrunc: case BuiltinFnIdNearbyInt: case BuiltinFnIdRound: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *inst = ir_build_float_op_src(irb, scope, node, arg0_value, builtin_fn->id); return ir_lval_wrap(irb, scope, inst, lval, result_loc); } case BuiltinFnIdTruncate: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *truncate = ir_build_truncate(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, truncate, lval, result_loc); } case BuiltinFnIdIntCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *result = ir_build_int_cast(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval, result_loc); } case BuiltinFnIdFloatCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *result = ir_build_float_cast(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval, result_loc); } case BuiltinFnIdErrSetCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *result = ir_build_err_set_cast(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval, result_loc); } case BuiltinFnIdIntToFloat: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *result = ir_build_int_to_float(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval, result_loc); } case BuiltinFnIdFloatToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *result = ir_build_float_to_int(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval, result_loc); } case BuiltinFnIdErrToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *result = ir_build_err_to_int_src(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, result, lval, result_loc); } case BuiltinFnIdIntToErr: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *result = ir_build_int_to_err_src(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, result, lval, result_loc); } case BuiltinFnIdBoolToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *result = ir_build_bool_to_int(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, result, lval, result_loc); } case BuiltinFnIdVectorType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *vector_type = ir_build_vector_type(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, vector_type, lval, result_loc); } case BuiltinFnIdShuffle: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); if (arg2_value == irb->codegen->invalid_inst_src) return arg2_value; AstNode *arg3_node = node->data.fn_call_expr.params.at(3); IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope); if (arg3_value == irb->codegen->invalid_inst_src) return arg3_value; IrInstSrc *shuffle_vector = ir_build_shuffle_vector(irb, scope, node, arg0_value, arg1_value, arg2_value, arg3_value); return ir_lval_wrap(irb, scope, shuffle_vector, lval, result_loc); } case BuiltinFnIdSplat: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *splat = ir_build_splat_src(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, splat, lval, result_loc); } case BuiltinFnIdMemcpy: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); if (arg2_value == irb->codegen->invalid_inst_src) return arg2_value; IrInstSrc *ir_memcpy = ir_build_memcpy_src(irb, scope, node, arg0_value, arg1_value, arg2_value); return ir_lval_wrap(irb, scope, ir_memcpy, lval, result_loc); } case BuiltinFnIdMemset: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); if (arg2_value == irb->codegen->invalid_inst_src) return arg2_value; IrInstSrc *ir_memset = ir_build_memset_src(irb, scope, node, arg0_value, arg1_value, arg2_value); return ir_lval_wrap(irb, scope, ir_memset, lval, result_loc); } case BuiltinFnIdWasmMemorySize: { IrInstSrc *ir_wasm_memory_size = ir_build_wasm_memory_size_src(irb, scope, node); return ir_lval_wrap(irb, scope, ir_wasm_memory_size, lval, result_loc); } case BuiltinFnIdWasmMemoryGrow: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *ir_wasm_memory_grow = ir_build_wasm_memory_grow_src(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, ir_wasm_memory_grow, lval, result_loc); } case BuiltinFnIdField: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LValPtr, nullptr); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node, arg0_value, arg1_value, false); if (lval == LValPtr || lval == LValAssign) return ptr_instruction; IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction); return ir_expr_wrap(irb, scope, load_ptr, result_loc); } case BuiltinFnIdHasField: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *type_info = ir_build_has_field(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, type_info, lval, result_loc); } case BuiltinFnIdTypeInfo: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *type_info = ir_build_type_info(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, type_info, lval, result_loc); } case BuiltinFnIdType: { AstNode *arg_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg = ir_gen_node(irb, arg_node, scope); if (arg == irb->codegen->invalid_inst_src) return arg; IrInstSrc *type = ir_build_type(irb, scope, node, arg); return ir_lval_wrap(irb, scope, type, lval, result_loc); } case BuiltinFnIdBreakpoint: return ir_lval_wrap(irb, scope, ir_build_breakpoint(irb, scope, node), lval, result_loc); case BuiltinFnIdReturnAddress: return ir_lval_wrap(irb, scope, ir_build_return_address_src(irb, scope, node), lval, result_loc); case BuiltinFnIdFrameAddress: return ir_lval_wrap(irb, scope, ir_build_frame_address_src(irb, scope, node), lval, result_loc); case BuiltinFnIdFrameHandle: if (!irb->exec->fn_entry) { add_node_error(irb->codegen, node, buf_sprintf("@frame() called outside of function definition")); return irb->codegen->invalid_inst_src; } return ir_lval_wrap(irb, scope, ir_build_handle_src(irb, scope, node), lval, result_loc); case BuiltinFnIdFrameType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *frame_type = ir_build_frame_type(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, frame_type, lval, result_loc); } case BuiltinFnIdFrameSize: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *frame_size = ir_build_frame_size_src(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, frame_size, lval, result_loc); } case BuiltinFnIdAlignOf: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *align_of = ir_build_align_of(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, align_of, lval, result_loc); } case BuiltinFnIdAddWithOverflow: return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd), lval, result_loc); case BuiltinFnIdSubWithOverflow: return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub), lval, result_loc); case BuiltinFnIdMulWithOverflow: return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval, result_loc); case BuiltinFnIdShlWithOverflow: return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval, result_loc); case BuiltinFnIdMulAdd: return ir_lval_wrap(irb, scope, ir_gen_mul_add(irb, scope, node), lval, result_loc); case BuiltinFnIdTypeName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *type_name = ir_build_type_name(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, type_name, lval, result_loc); } case BuiltinFnIdPanic: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *panic = ir_build_panic_src(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, panic, lval, result_loc); } case BuiltinFnIdPtrCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *ptr_cast = ir_build_ptr_cast_src(irb, scope, node, arg0_value, arg1_value, true); return ir_lval_wrap(irb, scope, ptr_cast, lval, result_loc); } case BuiltinFnIdBitCast: { AstNode *dest_type_node = node->data.fn_call_expr.params.at(0); IrInstSrc *dest_type = ir_gen_node(irb, dest_type_node, scope); if (dest_type == irb->codegen->invalid_inst_src) return dest_type; ResultLocBitCast *result_loc_bit_cast = heap::c_allocator.create(); result_loc_bit_cast->base.id = ResultLocIdBitCast; result_loc_bit_cast->base.source_instruction = dest_type; result_loc_bit_cast->base.allow_write_through_const = result_loc->allow_write_through_const; ir_ref_instruction(dest_type, irb->current_basic_block); result_loc_bit_cast->parent = result_loc; ir_build_reset_result(irb, scope, node, &result_loc_bit_cast->base); AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node_extra(irb, arg1_node, scope, LValNone, &result_loc_bit_cast->base); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *bitcast = ir_build_bit_cast_src(irb, scope, arg1_node, arg1_value, result_loc_bit_cast); return ir_lval_wrap(irb, scope, bitcast, lval, result_loc); } case BuiltinFnIdAs: { AstNode *dest_type_node = node->data.fn_call_expr.params.at(0); IrInstSrc *dest_type = ir_gen_node(irb, dest_type_node, scope); if (dest_type == irb->codegen->invalid_inst_src) return dest_type; ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, dest_type, result_loc); AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node_extra(irb, arg1_node, scope, LValNone, &result_loc_cast->base); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *result = ir_build_implicit_cast(irb, scope, node, arg1_value, result_loc_cast); return ir_lval_wrap(irb, scope, result, lval, result_loc); } case BuiltinFnIdIntToPtr: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *int_to_ptr = ir_build_int_to_ptr_src(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, int_to_ptr, lval, result_loc); } case BuiltinFnIdPtrToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *ptr_to_int = ir_build_ptr_to_int_src(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, ptr_to_int, lval, result_loc); } case BuiltinFnIdTagName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *tag_name = ir_build_tag_name_src(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, tag_name, lval, result_loc); } case BuiltinFnIdTagType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *tag_type = ir_build_tag_type(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, tag_type, lval, result_loc); } case BuiltinFnIdFieldParentPtr: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); if (arg2_value == irb->codegen->invalid_inst_src) return arg2_value; IrInstSrc *field_parent_ptr = ir_build_field_parent_ptr_src(irb, scope, node, arg0_value, arg1_value, arg2_value); return ir_lval_wrap(irb, scope, field_parent_ptr, lval, result_loc); } case BuiltinFnIdByteOffsetOf: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *offset_of = ir_build_byte_offset_of(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, offset_of, lval, result_loc); } case BuiltinFnIdBitOffsetOf: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *offset_of = ir_build_bit_offset_of(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, offset_of, lval, result_loc); } case BuiltinFnIdCall: { // Cast the options parameter to the options type ZigType *options_type = get_builtin_type(irb->codegen, "CallOptions"); IrInstSrc *options_type_inst = ir_build_const_type(irb, scope, node, options_type); ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc()); AstNode *options_node = node->data.fn_call_expr.params.at(0); IrInstSrc *options_inner = ir_gen_node_extra(irb, options_node, scope, LValNone, &result_loc_cast->base); if (options_inner == irb->codegen->invalid_inst_src) return options_inner; IrInstSrc *options = ir_build_implicit_cast(irb, scope, options_node, options_inner, result_loc_cast); AstNode *fn_ref_node = node->data.fn_call_expr.params.at(1); AstNode *args_node = node->data.fn_call_expr.params.at(2); if (args_node->type == NodeTypeContainerInitExpr) { if (args_node->data.container_init_expr.kind == ContainerInitKindArray || args_node->data.container_init_expr.entries.length == 0) { return ir_gen_fn_call_with_args(irb, scope, node, fn_ref_node, CallModifierNone, options, args_node->data.container_init_expr.entries.items, args_node->data.container_init_expr.entries.length, lval, result_loc); } else { exec_add_error_node(irb->codegen, irb->exec, args_node, buf_sprintf("TODO: @call with anon struct literal")); return irb->codegen->invalid_inst_src; } } else { IrInstSrc *fn_ref = ir_gen_node(irb, fn_ref_node, scope); if (fn_ref == irb->codegen->invalid_inst_src) return fn_ref; IrInstSrc *args = ir_gen_node(irb, args_node, scope); if (args == irb->codegen->invalid_inst_src) return args; IrInstSrc *call = ir_build_call_extra(irb, scope, node, options, fn_ref, args, result_loc); return ir_lval_wrap(irb, scope, call, lval, result_loc); } } case BuiltinFnIdAsyncCall: return ir_gen_async_call(irb, scope, nullptr, node, lval, result_loc); case BuiltinFnIdShlExact: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, arg0_value, arg1_value, true); return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); } case BuiltinFnIdShrExact: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, arg0_value, arg1_value, true); return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); } case BuiltinFnIdSetEvalBranchQuota: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *set_eval_branch_quota = ir_build_set_eval_branch_quota(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, set_eval_branch_quota, lval, result_loc); } case BuiltinFnIdAlignCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *align_cast = ir_build_align_cast_src(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, align_cast, lval, result_loc); } case BuiltinFnIdOpaqueType: { IrInstSrc *opaque_type = ir_build_opaque_type(irb, scope, node); return ir_lval_wrap(irb, scope, opaque_type, lval, result_loc); } case BuiltinFnIdThis: { IrInstSrc *this_inst = ir_gen_this(irb, scope, node); return ir_lval_wrap(irb, scope, this_inst, lval, result_loc); } case BuiltinFnIdSetAlignStack: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *set_align_stack = ir_build_set_align_stack(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, set_align_stack, lval, result_loc); } case BuiltinFnIdExport: { // Cast the options parameter to the options type ZigType *options_type = get_builtin_type(irb->codegen, "ExportOptions"); IrInstSrc *options_type_inst = ir_build_const_type(irb, scope, node, options_type); ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc()); AstNode *target_node = node->data.fn_call_expr.params.at(0); IrInstSrc *target_value = ir_gen_node(irb, target_node, scope); if (target_value == irb->codegen->invalid_inst_src) return target_value; AstNode *options_node = node->data.fn_call_expr.params.at(1); IrInstSrc *options_value = ir_gen_node_extra(irb, options_node, scope, LValNone, &result_loc_cast->base); if (options_value == irb->codegen->invalid_inst_src) return options_value; IrInstSrc *casted_options_value = ir_build_implicit_cast( irb, scope, options_node, options_value, result_loc_cast); IrInstSrc *ir_export = ir_build_export(irb, scope, node, target_value, casted_options_value); return ir_lval_wrap(irb, scope, ir_export, lval, result_loc); } case BuiltinFnIdErrorReturnTrace: { IrInstSrc *error_return_trace = ir_build_error_return_trace_src(irb, scope, node, IrInstErrorReturnTraceNull); return ir_lval_wrap(irb, scope, error_return_trace, lval, result_loc); } case BuiltinFnIdAtomicRmw: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); if (arg2_value == irb->codegen->invalid_inst_src) return arg2_value; AstNode *arg3_node = node->data.fn_call_expr.params.at(3); IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope); if (arg3_value == irb->codegen->invalid_inst_src) return arg3_value; AstNode *arg4_node = node->data.fn_call_expr.params.at(4); IrInstSrc *arg4_value = ir_gen_node(irb, arg4_node, scope); if (arg4_value == irb->codegen->invalid_inst_src) return arg4_value; IrInstSrc *inst = ir_build_atomic_rmw_src(irb, scope, node, arg0_value, arg1_value, arg2_value, arg3_value, arg4_value); return ir_lval_wrap(irb, scope, inst, lval, result_loc); } case BuiltinFnIdAtomicLoad: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); if (arg2_value == irb->codegen->invalid_inst_src) return arg2_value; IrInstSrc *inst = ir_build_atomic_load_src(irb, scope, node, arg0_value, arg1_value, arg2_value); return ir_lval_wrap(irb, scope, inst, lval, result_loc); } case BuiltinFnIdAtomicStore: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); if (arg2_value == irb->codegen->invalid_inst_src) return arg2_value; AstNode *arg3_node = node->data.fn_call_expr.params.at(3); IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope); if (arg3_value == irb->codegen->invalid_inst_src) return arg3_value; IrInstSrc *inst = ir_build_atomic_store_src(irb, scope, node, arg0_value, arg1_value, arg2_value, arg3_value); return ir_lval_wrap(irb, scope, inst, lval, result_loc); } case BuiltinFnIdIntToEnum: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *result = ir_build_int_to_enum_src(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval, result_loc); } case BuiltinFnIdEnumToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; IrInstSrc *result = ir_build_enum_to_int(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, result, lval, result_loc); } case BuiltinFnIdCtz: case BuiltinFnIdPopCount: case BuiltinFnIdClz: case BuiltinFnIdBswap: case BuiltinFnIdBitReverse: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *result; switch (builtin_fn->id) { case BuiltinFnIdCtz: result = ir_build_ctz(irb, scope, node, arg0_value, arg1_value); break; case BuiltinFnIdPopCount: result = ir_build_pop_count(irb, scope, node, arg0_value, arg1_value); break; case BuiltinFnIdClz: result = ir_build_clz(irb, scope, node, arg0_value, arg1_value); break; case BuiltinFnIdBswap: result = ir_build_bswap(irb, scope, node, arg0_value, arg1_value); break; case BuiltinFnIdBitReverse: result = ir_build_bit_reverse(irb, scope, node, arg0_value, arg1_value); break; default: zig_unreachable(); } return ir_lval_wrap(irb, scope, result, lval, result_loc); } case BuiltinFnIdHasDecl: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_inst_src) return arg1_value; IrInstSrc *has_decl = ir_build_has_decl(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, has_decl, lval, result_loc); } case BuiltinFnIdUnionInit: { AstNode *union_type_node = node->data.fn_call_expr.params.at(0); IrInstSrc *union_type_inst = ir_gen_node(irb, union_type_node, scope); if (union_type_inst == irb->codegen->invalid_inst_src) return union_type_inst; AstNode *name_node = node->data.fn_call_expr.params.at(1); IrInstSrc *name_inst = ir_gen_node(irb, name_node, scope); if (name_inst == irb->codegen->invalid_inst_src) return name_inst; AstNode *init_node = node->data.fn_call_expr.params.at(2); return ir_gen_union_init_expr(irb, scope, node, union_type_inst, name_inst, init_node, lval, result_loc); } } zig_unreachable(); } static ScopeNoSuspend *get_scope_nosuspend(Scope *scope) { while (scope) { if (scope->id == ScopeIdNoSuspend) return (ScopeNoSuspend *)scope; if (scope->id == ScopeIdFnDef) return nullptr; scope = scope->parent; } return nullptr; } static IrInstSrc *ir_gen_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { assert(node->type == NodeTypeFnCallExpr); if (node->data.fn_call_expr.modifier == CallModifierBuiltin) return ir_gen_builtin_fn_call(irb, scope, node, lval, result_loc); bool is_nosuspend = get_scope_nosuspend(scope) != nullptr; CallModifier modifier = node->data.fn_call_expr.modifier; if (is_nosuspend) { if (modifier == CallModifierAsync) { add_node_error(irb->codegen, node, buf_sprintf("async call in nosuspend scope")); return irb->codegen->invalid_inst_src; } modifier = CallModifierNoSuspend; } AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr; return ir_gen_fn_call_with_args(irb, scope, node, fn_ref_node, modifier, nullptr, node->data.fn_call_expr.params.items, node->data.fn_call_expr.params.length, lval, result_loc); } static IrInstSrc *ir_gen_if_bool_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { assert(node->type == NodeTypeIfBoolExpr); IrInstSrc *condition = ir_gen_node(irb, node->data.if_bool_expr.condition, scope); if (condition == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrc *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; IrBasicBlockSrc *then_block = ir_create_basic_block(irb, scope, "Then"); IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "Else"); IrBasicBlockSrc *endif_block = ir_create_basic_block(irb, scope, "EndIf"); IrInstSrc *cond_br_inst = ir_build_cond_br(irb, scope, node, condition, then_block, else_block, is_comptime); ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block, result_loc, is_comptime); ir_set_cursor_at_end_and_append_block(irb, then_block); Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); IrInstSrc *then_expr_result = ir_gen_node_extra(irb, then_node, subexpr_scope, lval, &peer_parent->peers.at(0)->base); if (then_expr_result == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrBasicBlockSrc *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); IrInstSrc *else_expr_result; if (else_node) { else_expr_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_parent->peers.at(1)->base); if (else_expr_result == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; } else { else_expr_result = ir_build_const_void(irb, scope, node); ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base); } IrBasicBlockSrc *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); IrInstSrc **incoming_values = heap::c_allocator.allocate(2); incoming_values[0] = then_expr_result; incoming_values[1] = else_expr_result; IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate(2); incoming_blocks[0] = after_then_block; incoming_blocks[1] = after_else_block; IrInstSrc *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent); return ir_expr_wrap(irb, scope, phi, result_loc); } static IrInstSrc *ir_gen_prefix_op_id_lval(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrUnOp op_id, LVal lval) { assert(node->type == NodeTypePrefixOpExpr); AstNode *expr_node = node->data.prefix_op_expr.primary_expr; IrInstSrc *value = ir_gen_node_extra(irb, expr_node, scope, lval, nullptr); if (value == irb->codegen->invalid_inst_src) return value; return ir_build_un_op(irb, scope, node, op_id, value); } static IrInstSrc *ir_gen_prefix_op_id(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrUnOp op_id) { return ir_gen_prefix_op_id_lval(irb, scope, node, op_id, LValNone); } static IrInstSrc *ir_expr_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *inst, ResultLoc *result_loc) { if (inst == irb->codegen->invalid_inst_src) return inst; ir_build_end_expr(irb, scope, inst->base.source_node, inst, result_loc); return inst; } static IrInstSrc *ir_lval_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *value, LVal lval, ResultLoc *result_loc) { // This logic must be kept in sync with // [STMT_EXPR_TEST_THING] <--- (search this token) if (value == irb->codegen->invalid_inst_src || instr_is_unreachable(value) || value->base.source_node->type == NodeTypeDefer || value->id == IrInstSrcIdDeclVar) { return value; } assert(lval != LValAssign); if (lval == LValPtr) { // We needed a pointer to a value, but we got a value. So we create // an instruction which just makes a pointer of it. return ir_build_ref_src(irb, scope, value->base.source_node, value); } else if (result_loc != nullptr) { return ir_expr_wrap(irb, scope, value, result_loc); } else { return value; } } static PtrLen star_token_to_ptr_len(TokenId token_id) { switch (token_id) { case TokenIdStar: case TokenIdStarStar: return PtrLenSingle; case TokenIdLBracket: return PtrLenUnknown; case TokenIdSymbol: return PtrLenC; default: zig_unreachable(); } } static IrInstSrc *ir_gen_pointer_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypePointerType); PtrLen ptr_len = star_token_to_ptr_len(node->data.pointer_type.star_token->id); bool is_const = node->data.pointer_type.is_const; bool is_volatile = node->data.pointer_type.is_volatile; bool is_allow_zero = node->data.pointer_type.allow_zero_token != nullptr; AstNode *sentinel_expr = node->data.pointer_type.sentinel; AstNode *expr_node = node->data.pointer_type.op_expr; AstNode *align_expr = node->data.pointer_type.align_expr; IrInstSrc *sentinel; if (sentinel_expr != nullptr) { sentinel = ir_gen_node(irb, sentinel_expr, scope); if (sentinel == irb->codegen->invalid_inst_src) return sentinel; } else { sentinel = nullptr; } IrInstSrc *align_value; if (align_expr != nullptr) { align_value = ir_gen_node(irb, align_expr, scope); if (align_value == irb->codegen->invalid_inst_src) return align_value; } else { align_value = nullptr; } IrInstSrc *child_type = ir_gen_node(irb, expr_node, scope); if (child_type == irb->codegen->invalid_inst_src) 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_inst_src; } bit_offset_start = bigint_as_u32(node->data.pointer_type.bit_offset_start); } uint32_t host_int_bytes = 0; if (node->data.pointer_type.host_int_bytes != nullptr) { if (!bigint_fits_in_bits(node->data.pointer_type.host_int_bytes, 32, false)) { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, node->data.pointer_type.host_int_bytes, 10); exec_add_error_node(irb->codegen, irb->exec, node, buf_sprintf("value %s too large for u32 byte count", buf_ptr(val_buf))); return irb->codegen->invalid_inst_src; } host_int_bytes = bigint_as_u32(node->data.pointer_type.host_int_bytes); } if (host_int_bytes != 0 && bit_offset_start >= host_int_bytes * 8) { exec_add_error_node(irb->codegen, irb->exec, node, buf_sprintf("bit offset starts after end of host integer")); return irb->codegen->invalid_inst_src; } return ir_build_ptr_type(irb, scope, node, child_type, is_const, is_volatile, ptr_len, sentinel, align_value, bit_offset_start, host_int_bytes, is_allow_zero); } static IrInstSrc *ir_gen_catch_unreachable(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, LVal lval, ResultLoc *result_loc) { IrInstSrc *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr); if (err_union_ptr == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrc *payload_ptr = ir_build_unwrap_err_payload_src(irb, scope, source_node, err_union_ptr, true, false); if (payload_ptr == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; if (lval == LValPtr) return payload_ptr; IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, source_node, payload_ptr); return ir_expr_wrap(irb, scope, load_ptr, result_loc); } static IrInstSrc *ir_gen_bool_not(IrBuilderSrc *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypePrefixOpExpr); AstNode *expr_node = node->data.prefix_op_expr.primary_expr; IrInstSrc *value = ir_gen_node(irb, expr_node, scope); if (value == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; return ir_build_bool_not(irb, scope, node, value); } static IrInstSrc *ir_gen_prefix_op_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { assert(node->type == NodeTypePrefixOpExpr); PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op; switch (prefix_op) { case PrefixOpInvalid: zig_unreachable(); case PrefixOpBoolNot: return ir_lval_wrap(irb, scope, ir_gen_bool_not(irb, scope, node), lval, result_loc); case PrefixOpBinNot: return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpBinNot), lval, result_loc); case PrefixOpNegation: return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation), lval, result_loc); case PrefixOpNegationWrap: return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval, result_loc); case PrefixOpOptional: return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval, result_loc); case PrefixOpAddrOf: { AstNode *expr_node = node->data.prefix_op_expr.primary_expr; return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr), lval, result_loc); } } zig_unreachable(); } static IrInstSrc *ir_gen_union_init_expr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *union_type, IrInstSrc *field_name, AstNode *expr_node, LVal lval, ResultLoc *parent_result_loc) { IrInstSrc *container_ptr = ir_build_resolve_result(irb, scope, source_node, parent_result_loc, union_type); IrInstSrc *field_ptr = ir_build_field_ptr_instruction(irb, scope, source_node, container_ptr, field_name, true); ResultLocInstruction *result_loc_inst = heap::c_allocator.create(); result_loc_inst->base.id = ResultLocIdInstruction; result_loc_inst->base.source_instruction = field_ptr; ir_ref_instruction(field_ptr, irb->current_basic_block); ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base); IrInstSrc *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone, &result_loc_inst->base); if (expr_value == irb->codegen->invalid_inst_src) return expr_value; IrInstSrc *init_union = ir_build_union_init_named_field(irb, scope, source_node, union_type, field_name, field_ptr, container_ptr); return ir_lval_wrap(irb, scope, init_union, lval, parent_result_loc); } static IrInstSrc *ir_gen_container_init_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *parent_result_loc) { assert(node->type == NodeTypeContainerInitExpr); AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr; ContainerInitKind kind = container_init_expr->kind; ResultLocCast *result_loc_cast = nullptr; ResultLoc *child_result_loc; AstNode *init_array_type_source_node; if (container_init_expr->type != nullptr) { IrInstSrc *container_type; if (container_init_expr->type->type == NodeTypeInferredArrayType) { if (kind == ContainerInitKindStruct) { add_node_error(irb->codegen, container_init_expr->type, buf_sprintf("initializing array with struct syntax")); return irb->codegen->invalid_inst_src; } IrInstSrc *sentinel; if (container_init_expr->type->data.inferred_array_type.sentinel != nullptr) { sentinel = ir_gen_node(irb, container_init_expr->type->data.inferred_array_type.sentinel, scope); if (sentinel == irb->codegen->invalid_inst_src) return sentinel; } else { sentinel = nullptr; } IrInstSrc *elem_type = ir_gen_node(irb, container_init_expr->type->data.inferred_array_type.child_type, scope); if (elem_type == irb->codegen->invalid_inst_src) return elem_type; size_t item_count = container_init_expr->entries.length; IrInstSrc *item_count_inst = ir_build_const_usize(irb, scope, node, item_count); container_type = ir_build_array_type(irb, scope, node, item_count_inst, sentinel, elem_type); } else { container_type = ir_gen_node(irb, container_init_expr->type, scope); if (container_type == irb->codegen->invalid_inst_src) return container_type; } result_loc_cast = ir_build_cast_result_loc(irb, container_type, parent_result_loc); child_result_loc = &result_loc_cast->base; init_array_type_source_node = container_type->base.source_node; } else { child_result_loc = parent_result_loc; if (parent_result_loc->source_instruction != nullptr) { init_array_type_source_node = parent_result_loc->source_instruction->base.source_node; } else { init_array_type_source_node = node; } } switch (kind) { case ContainerInitKindStruct: { IrInstSrc *container_ptr = ir_build_resolve_result(irb, scope, node, child_result_loc, nullptr); size_t field_count = container_init_expr->entries.length; IrInstSrcContainerInitFieldsField *fields = heap::c_allocator.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; IrInstSrc *field_ptr = ir_build_field_ptr(irb, scope, entry_node, container_ptr, name, true); ResultLocInstruction *result_loc_inst = heap::c_allocator.create(); result_loc_inst->base.id = ResultLocIdInstruction; result_loc_inst->base.source_instruction = field_ptr; result_loc_inst->base.allow_write_through_const = true; ir_ref_instruction(field_ptr, irb->current_basic_block); ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base); IrInstSrc *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone, &result_loc_inst->base); if (expr_value == irb->codegen->invalid_inst_src) return expr_value; fields[i].name = name; fields[i].source_node = entry_node; fields[i].result_loc = field_ptr; } IrInstSrc *result = ir_build_container_init_fields(irb, scope, node, field_count, fields, container_ptr); if (result_loc_cast != nullptr) { result = ir_build_implicit_cast(irb, scope, node, result, result_loc_cast); } return ir_lval_wrap(irb, scope, result, lval, parent_result_loc); } case ContainerInitKindArray: { size_t item_count = container_init_expr->entries.length; IrInstSrc *container_ptr = ir_build_resolve_result(irb, scope, node, child_result_loc, nullptr); IrInstSrc **result_locs = heap::c_allocator.allocate(item_count); for (size_t i = 0; i < item_count; i += 1) { AstNode *expr_node = container_init_expr->entries.at(i); IrInstSrc *elem_index = ir_build_const_usize(irb, scope, expr_node, i); IrInstSrc *elem_ptr = ir_build_elem_ptr(irb, scope, expr_node, container_ptr, elem_index, false, PtrLenSingle, init_array_type_source_node); ResultLocInstruction *result_loc_inst = heap::c_allocator.create(); result_loc_inst->base.id = ResultLocIdInstruction; result_loc_inst->base.source_instruction = elem_ptr; result_loc_inst->base.allow_write_through_const = true; ir_ref_instruction(elem_ptr, irb->current_basic_block); ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base); IrInstSrc *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone, &result_loc_inst->base); if (expr_value == irb->codegen->invalid_inst_src) return expr_value; result_locs[i] = elem_ptr; } IrInstSrc *result = ir_build_container_init_list(irb, scope, node, item_count, result_locs, container_ptr, init_array_type_source_node); if (result_loc_cast != nullptr) { result = ir_build_implicit_cast(irb, scope, node, result, result_loc_cast); } return ir_lval_wrap(irb, scope, result, lval, parent_result_loc); } } zig_unreachable(); } static ResultLocVar *ir_build_var_result_loc(IrBuilderSrc *irb, IrInstSrc *alloca, ZigVar *var) { ResultLocVar *result_loc_var = heap::c_allocator.create(); result_loc_var->base.id = ResultLocIdVar; result_loc_var->base.source_instruction = alloca; result_loc_var->base.allow_write_through_const = true; result_loc_var->var = var; ir_build_reset_result(irb, alloca->base.scope, alloca->base.source_node, &result_loc_var->base); return result_loc_var; } static ResultLocCast *ir_build_cast_result_loc(IrBuilderSrc *irb, IrInstSrc *dest_type, ResultLoc *parent_result_loc) { ResultLocCast *result_loc_cast = heap::c_allocator.create(); result_loc_cast->base.id = ResultLocIdCast; result_loc_cast->base.source_instruction = dest_type; result_loc_cast->base.allow_write_through_const = parent_result_loc->allow_write_through_const; ir_ref_instruction(dest_type, irb->current_basic_block); result_loc_cast->parent = parent_result_loc; ir_build_reset_result(irb, dest_type->base.scope, dest_type->base.source_node, &result_loc_cast->base); return result_loc_cast; } static void build_decl_var_and_init(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var, IrInstSrc *init, const char *name_hint, IrInstSrc *is_comptime) { IrInstSrc *alloca = ir_build_alloca_src(irb, scope, source_node, nullptr, name_hint, is_comptime); ResultLocVar *var_result_loc = ir_build_var_result_loc(irb, alloca, var); ir_build_end_expr(irb, scope, source_node, init, &var_result_loc->base); ir_build_var_decl_src(irb, scope, source_node, var, nullptr, alloca); } static IrInstSrc *ir_gen_var_decl(IrBuilderSrc *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_inst_src; } // Used for the type expr and the align expr Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope); IrInstSrc *type_instruction; if (variable_declaration->type != nullptr) { type_instruction = ir_gen_node(irb, variable_declaration->type, comptime_scope); if (type_instruction == irb->codegen->invalid_inst_src) return type_instruction; } else { type_instruction = nullptr; } bool is_shadowable = false; bool is_const = variable_declaration->is_const; bool is_extern = variable_declaration->is_extern; bool is_comptime_scalar = ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime; IrInstSrc *is_comptime = ir_build_const_bool(irb, scope, node, is_comptime_scalar); ZigVar *var = ir_create_var(irb, node, scope, variable_declaration->symbol, is_const, is_const, is_shadowable, is_comptime); // we detect IrInstSrcDeclVar in gen_block to make sure the next node // is inside var->child_scope if (!is_extern && !variable_declaration->expr) { var->var_type = irb->codegen->builtin_types.entry_invalid; add_node_error(irb->codegen, node, buf_sprintf("variables must be initialized")); return irb->codegen->invalid_inst_src; } IrInstSrc *align_value = nullptr; if (variable_declaration->align_expr != nullptr) { align_value = ir_gen_node(irb, variable_declaration->align_expr, comptime_scope); if (align_value == irb->codegen->invalid_inst_src) return align_value; } if (variable_declaration->section_expr != nullptr) { add_node_error(irb->codegen, variable_declaration->section_expr, buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol))); } // Parser should ensure that this never happens assert(variable_declaration->threadlocal_tok == nullptr); IrInstSrc *alloca = ir_build_alloca_src(irb, scope, node, align_value, buf_ptr(variable_declaration->symbol), is_comptime); // Create a result location for the initialization expression. ResultLocVar *result_loc_var = ir_build_var_result_loc(irb, alloca, var); ResultLoc *init_result_loc; ResultLocCast *result_loc_cast; if (type_instruction != nullptr) { result_loc_cast = ir_build_cast_result_loc(irb, type_instruction, &result_loc_var->base); init_result_loc = &result_loc_cast->base; } else { result_loc_cast = nullptr; init_result_loc = &result_loc_var->base; } Scope *init_scope = is_comptime_scalar ? create_comptime_scope(irb->codegen, variable_declaration->expr, scope) : scope; // Temporarily set the name of the IrExecutableSrc 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; IrInstSrc *init_value = ir_gen_node_extra(irb, variable_declaration->expr, init_scope, LValNone, init_result_loc); irb->exec->name = old_exec_name; if (init_value == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; if (result_loc_cast != nullptr) { IrInstSrc *implicit_cast = ir_build_implicit_cast(irb, scope, init_value->base.source_node, init_value, result_loc_cast); ir_build_end_expr(irb, scope, node, implicit_cast, &result_loc_var->base); } return ir_build_var_decl_src(irb, scope, node, var, align_value, alloca); } static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { assert(node->type == NodeTypeWhileExpr); AstNode *continue_expr_node = node->data.while_expr.continue_expr; AstNode *else_node = node->data.while_expr.else_node; IrBasicBlockSrc *cond_block = ir_create_basic_block(irb, scope, "WhileCond"); IrBasicBlockSrc *body_block = ir_create_basic_block(irb, scope, "WhileBody"); IrBasicBlockSrc *continue_block = continue_expr_node ? ir_create_basic_block(irb, scope, "WhileContinue") : cond_block; IrBasicBlockSrc *end_block = ir_create_basic_block(irb, scope, "WhileEnd"); IrBasicBlockSrc *else_block = else_node ? ir_create_basic_block(irb, scope, "WhileElse") : end_block; IrInstSrc *is_comptime = ir_build_const_bool(irb, scope, node, ir_should_inline(irb->exec, scope) || node->data.while_expr.is_inline); ir_build_br(irb, scope, node, cond_block, is_comptime); Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); Buf *var_symbol = node->data.while_expr.var_symbol; Buf *err_symbol = node->data.while_expr.err_symbol; if (err_symbol != nullptr) { ir_set_cursor_at_end_and_append_block(irb, cond_block); Scope *payload_scope; AstNode *symbol_node = node; // TODO make more accurate ZigVar *payload_var; if (var_symbol) { // TODO make it an error to write to payload variable payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol, true, false, false, is_comptime); payload_scope = payload_var->child_scope; } else { payload_scope = subexpr_scope; } ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, payload_scope); IrInstSrc *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope, LValPtr, nullptr); if (err_val_ptr == irb->codegen->invalid_inst_src) return err_val_ptr; IrInstSrc *is_err = ir_build_test_err_src(irb, scope, node->data.while_expr.condition, err_val_ptr, true, false); IrBasicBlockSrc *after_cond_block = irb->current_basic_block; IrInstSrc *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); IrInstSrc *cond_br_inst; if (!instr_is_unreachable(is_err)) { cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_err, else_block, body_block, is_comptime); cond_br_inst->is_gen = true; } else { // for the purposes of the source instruction to ir_build_result_peers cond_br_inst = irb->current_basic_block->instruction_list.last(); } ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc, is_comptime); ir_set_cursor_at_end_and_append_block(irb, body_block); if (var_symbol) { IrInstSrc *payload_ptr = ir_build_unwrap_err_payload_src(irb, &spill_scope->base, symbol_node, err_val_ptr, false, false); IrInstSrc *var_value = node->data.while_expr.var_is_ptr ? payload_ptr : ir_build_load_ptr(irb, &spill_scope->base, symbol_node, payload_ptr); build_decl_var_and_init(irb, payload_scope, symbol_node, payload_var, var_value, buf_ptr(var_symbol), is_comptime); } ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, payload_scope); loop_scope->break_block = end_block; loop_scope->continue_block = continue_block; loop_scope->is_comptime = is_comptime; loop_scope->incoming_blocks = &incoming_blocks; loop_scope->incoming_values = &incoming_values; loop_scope->lval = lval; loop_scope->peer_parent = peer_parent; loop_scope->spill_scope = spill_scope; // Note the body block of the loop is not the place that lval and result_loc are used - // it's actually in break statements, handled similarly to return statements. // That is why we set those values in loop_scope above and not in this ir_gen_node call. IrInstSrc *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base); if (body_result == irb->codegen->invalid_inst_src) 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); IrInstSrc *expr_result = ir_gen_node(irb, continue_expr_node, payload_scope); if (expr_result == irb->codegen->invalid_inst_src) return expr_result; if (!instr_is_unreachable(expr_result)) { ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, continue_expr_node, expr_result)); ir_mark_gen(ir_build_br(irb, payload_scope, node, cond_block, is_comptime)); } } ir_set_cursor_at_end_and_append_block(irb, else_block); assert(else_node != nullptr); // TODO make it an error to write to error variable AstNode *err_symbol_node = else_node; // TODO make more accurate ZigVar *err_var = ir_create_var(irb, err_symbol_node, scope, err_symbol, true, false, false, is_comptime); Scope *err_scope = err_var->child_scope; IrInstSrc *err_ptr = ir_build_unwrap_err_code_src(irb, err_scope, err_symbol_node, err_val_ptr); IrInstSrc *err_value = ir_build_load_ptr(irb, err_scope, err_symbol_node, err_ptr); build_decl_var_and_init(irb, err_scope, err_symbol_node, err_var, err_value, buf_ptr(err_symbol), is_comptime); if (peer_parent->peers.length != 0) { peer_parent->peers.last()->next_bb = else_block; } ResultLocPeer *peer_result = create_peer_result(peer_parent); peer_parent->peers.append(peer_result); IrInstSrc *else_result = ir_gen_node_extra(irb, else_node, err_scope, lval, &peer_result->base); if (else_result == irb->codegen->invalid_inst_src) return else_result; if (!instr_is_unreachable(else_result)) ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); IrBasicBlockSrc *after_else_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, end_block); if (else_result) { incoming_blocks.append(after_else_block); incoming_values.append(else_result); } else { incoming_blocks.append(after_cond_block); incoming_values.append(void_else_result); } if (peer_parent->peers.length != 0) { peer_parent->peers.last()->next_bb = end_block; } IrInstSrc *phi = ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items, peer_parent); return ir_expr_wrap(irb, scope, phi, result_loc); } else if (var_symbol != nullptr) { ir_set_cursor_at_end_and_append_block(irb, cond_block); Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); // TODO make it an error to write to payload variable AstNode *symbol_node = node; // TODO make more accurate ZigVar *payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol, true, false, false, is_comptime); Scope *child_scope = payload_var->child_scope; ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, child_scope); IrInstSrc *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope, LValPtr, nullptr); if (maybe_val_ptr == irb->codegen->invalid_inst_src) return maybe_val_ptr; IrInstSrc *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr); IrInstSrc *is_non_null = ir_build_test_non_null_src(irb, scope, node->data.while_expr.condition, maybe_val); IrBasicBlockSrc *after_cond_block = irb->current_basic_block; IrInstSrc *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); IrInstSrc *cond_br_inst; if (!instr_is_unreachable(is_non_null)) { cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_non_null, body_block, else_block, is_comptime); cond_br_inst->is_gen = true; } else { // for the purposes of the source instruction to ir_build_result_peers cond_br_inst = irb->current_basic_block->instruction_list.last(); } ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc, is_comptime); ir_set_cursor_at_end_and_append_block(irb, body_block); IrInstSrc *payload_ptr = ir_build_optional_unwrap_ptr(irb, &spill_scope->base, symbol_node, maybe_val_ptr, false); IrInstSrc *var_value = node->data.while_expr.var_is_ptr ? payload_ptr : ir_build_load_ptr(irb, &spill_scope->base, symbol_node, payload_ptr); build_decl_var_and_init(irb, child_scope, symbol_node, payload_var, var_value, buf_ptr(var_symbol), is_comptime); ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope); loop_scope->break_block = end_block; loop_scope->continue_block = continue_block; loop_scope->is_comptime = is_comptime; loop_scope->incoming_blocks = &incoming_blocks; loop_scope->incoming_values = &incoming_values; loop_scope->lval = lval; loop_scope->peer_parent = peer_parent; loop_scope->spill_scope = spill_scope; // Note the body block of the loop is not the place that lval and result_loc are used - // it's actually in break statements, handled similarly to return statements. // That is why we set those values in loop_scope above and not in this ir_gen_node call. IrInstSrc *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base); if (body_result == irb->codegen->invalid_inst_src) 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); IrInstSrc *expr_result = ir_gen_node(irb, continue_expr_node, child_scope); if (expr_result == irb->codegen->invalid_inst_src) return expr_result; if (!instr_is_unreachable(expr_result)) { ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, continue_expr_node, expr_result)); ir_mark_gen(ir_build_br(irb, child_scope, node, cond_block, is_comptime)); } } IrInstSrc *else_result = nullptr; if (else_node) { ir_set_cursor_at_end_and_append_block(irb, else_block); if (peer_parent->peers.length != 0) { peer_parent->peers.last()->next_bb = else_block; } ResultLocPeer *peer_result = create_peer_result(peer_parent); peer_parent->peers.append(peer_result); else_result = ir_gen_node_extra(irb, else_node, scope, lval, &peer_result->base); if (else_result == irb->codegen->invalid_inst_src) return else_result; if (!instr_is_unreachable(else_result)) ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); } IrBasicBlockSrc *after_else_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, end_block); if (else_result) { incoming_blocks.append(after_else_block); incoming_values.append(else_result); } else { incoming_blocks.append(after_cond_block); incoming_values.append(void_else_result); } if (peer_parent->peers.length != 0) { peer_parent->peers.last()->next_bb = end_block; } IrInstSrc *phi = ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items, peer_parent); return ir_expr_wrap(irb, scope, phi, result_loc); } else { ir_set_cursor_at_end_and_append_block(irb, cond_block); IrInstSrc *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope); if (cond_val == irb->codegen->invalid_inst_src) return cond_val; IrBasicBlockSrc *after_cond_block = irb->current_basic_block; IrInstSrc *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); IrInstSrc *cond_br_inst; if (!instr_is_unreachable(cond_val)) { cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val, body_block, else_block, is_comptime); cond_br_inst->is_gen = true; } else { // for the purposes of the source instruction to ir_build_result_peers cond_br_inst = irb->current_basic_block->instruction_list.last(); } ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc, is_comptime); ir_set_cursor_at_end_and_append_block(irb, body_block); ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, subexpr_scope); loop_scope->break_block = end_block; loop_scope->continue_block = continue_block; loop_scope->is_comptime = is_comptime; loop_scope->incoming_blocks = &incoming_blocks; loop_scope->incoming_values = &incoming_values; loop_scope->lval = lval; loop_scope->peer_parent = peer_parent; // Note the body block of the loop is not the place that lval and result_loc are used - // it's actually in break statements, handled similarly to return statements. // That is why we set those values in loop_scope above and not in this ir_gen_node call. IrInstSrc *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base); if (body_result == irb->codegen->invalid_inst_src) 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); IrInstSrc *expr_result = ir_gen_node(irb, continue_expr_node, subexpr_scope); if (expr_result == irb->codegen->invalid_inst_src) return expr_result; if (!instr_is_unreachable(expr_result)) { ir_mark_gen(ir_build_check_statement_is_void(irb, scope, continue_expr_node, expr_result)); ir_mark_gen(ir_build_br(irb, scope, node, cond_block, is_comptime)); } } IrInstSrc *else_result = nullptr; if (else_node) { ir_set_cursor_at_end_and_append_block(irb, else_block); if (peer_parent->peers.length != 0) { peer_parent->peers.last()->next_bb = else_block; } ResultLocPeer *peer_result = create_peer_result(peer_parent); peer_parent->peers.append(peer_result); else_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_result->base); if (else_result == irb->codegen->invalid_inst_src) return else_result; if (!instr_is_unreachable(else_result)) ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); } IrBasicBlockSrc *after_else_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, end_block); if (else_result) { incoming_blocks.append(after_else_block); incoming_values.append(else_result); } else { incoming_blocks.append(after_cond_block); incoming_values.append(void_else_result); } if (peer_parent->peers.length != 0) { peer_parent->peers.last()->next_bb = end_block; } IrInstSrc *phi = ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items, peer_parent); return ir_expr_wrap(irb, scope, phi, result_loc); } } static IrInstSrc *ir_gen_for_expr(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval, ResultLoc *result_loc) { assert(node->type == NodeTypeForExpr); AstNode *array_node = node->data.for_expr.array_expr; AstNode *elem_node = node->data.for_expr.elem_node; AstNode *index_node = node->data.for_expr.index_node; AstNode *body_node = node->data.for_expr.body; AstNode *else_node = node->data.for_expr.else_node; if (!elem_node) { add_node_error(irb->codegen, node, buf_sprintf("for loop expression missing element parameter")); return irb->codegen->invalid_inst_src; } assert(elem_node->type == NodeTypeSymbol); ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, parent_scope); IrInstSrc *array_val_ptr = ir_gen_node_extra(irb, array_node, &spill_scope->base, LValPtr, nullptr); if (array_val_ptr == irb->codegen->invalid_inst_src) return array_val_ptr; IrInstSrc *is_comptime = ir_build_const_bool(irb, parent_scope, node, ir_should_inline(irb->exec, parent_scope) || node->data.for_expr.is_inline); AstNode *index_var_source_node; ZigVar *index_var; const char *index_var_name; if (index_node) { index_var_source_node = index_node; Buf *index_var_name_buf = index_node->data.symbol_expr.symbol; index_var = ir_create_var(irb, index_node, parent_scope, index_var_name_buf, true, false, false, is_comptime); index_var_name = buf_ptr(index_var_name_buf); } else { index_var_source_node = node; index_var = ir_create_var(irb, node, parent_scope, nullptr, true, false, true, is_comptime); index_var_name = "i"; } IrInstSrc *zero = ir_build_const_usize(irb, parent_scope, node, 0); build_decl_var_and_init(irb, parent_scope, index_var_source_node, index_var, zero, index_var_name, is_comptime); parent_scope = index_var->child_scope; IrInstSrc *one = ir_build_const_usize(irb, parent_scope, node, 1); IrInstSrc *index_ptr = ir_build_var_ptr(irb, parent_scope, node, index_var); IrBasicBlockSrc *cond_block = ir_create_basic_block(irb, parent_scope, "ForCond"); IrBasicBlockSrc *body_block = ir_create_basic_block(irb, parent_scope, "ForBody"); IrBasicBlockSrc *end_block = ir_create_basic_block(irb, parent_scope, "ForEnd"); IrBasicBlockSrc *else_block = else_node ? ir_create_basic_block(irb, parent_scope, "ForElse") : end_block; IrBasicBlockSrc *continue_block = ir_create_basic_block(irb, parent_scope, "ForContinue"); Buf *len_field_name = buf_create_from_str("len"); IrInstSrc *len_ref = ir_build_field_ptr(irb, parent_scope, node, array_val_ptr, len_field_name, false); IrInstSrc *len_val = ir_build_load_ptr(irb, &spill_scope->base, node, len_ref); ir_build_br(irb, parent_scope, node, cond_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, cond_block); IrInstSrc *index_val = ir_build_load_ptr(irb, &spill_scope->base, node, index_ptr); IrInstSrc *cond = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpLessThan, index_val, len_val, false); IrBasicBlockSrc *after_cond_block = irb->current_basic_block; IrInstSrc *void_else_value = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); IrInstSrc *cond_br_inst = ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, cond, body_block, else_block, is_comptime)); ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc, is_comptime); ir_set_cursor_at_end_and_append_block(irb, body_block); IrInstSrc *elem_ptr = ir_build_elem_ptr(irb, &spill_scope->base, node, array_val_ptr, index_val, false, PtrLenSingle, nullptr); // TODO make it an error to write to element variable or i variable. Buf *elem_var_name = elem_node->data.symbol_expr.symbol; ZigVar *elem_var = ir_create_var(irb, elem_node, parent_scope, elem_var_name, true, false, false, is_comptime); Scope *child_scope = elem_var->child_scope; IrInstSrc *elem_value = node->data.for_expr.elem_is_ptr ? elem_ptr : ir_build_load_ptr(irb, &spill_scope->base, elem_node, elem_ptr); build_decl_var_and_init(irb, parent_scope, elem_node, elem_var, elem_value, buf_ptr(elem_var_name), is_comptime); ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope); loop_scope->break_block = end_block; loop_scope->continue_block = continue_block; loop_scope->is_comptime = is_comptime; loop_scope->incoming_blocks = &incoming_blocks; loop_scope->incoming_values = &incoming_values; loop_scope->lval = LValNone; loop_scope->peer_parent = peer_parent; loop_scope->spill_scope = spill_scope; // Note the body block of the loop is not the place that lval and result_loc are used - // it's actually in break statements, handled similarly to return statements. // That is why we set those values in loop_scope above and not in this ir_gen_node call. IrInstSrc *body_result = ir_gen_node(irb, body_node, &loop_scope->base); if (body_result == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; if (!instr_is_unreachable(body_result)) { ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.for_expr.body, body_result)); ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime)); } ir_set_cursor_at_end_and_append_block(irb, continue_block); IrInstSrc *new_index_val = ir_build_bin_op(irb, child_scope, node, IrBinOpAdd, index_val, one, false); ir_build_store_ptr(irb, child_scope, node, index_ptr, new_index_val)->allow_write_through_const = true; ir_build_br(irb, child_scope, node, cond_block, is_comptime); IrInstSrc *else_result = nullptr; if (else_node) { ir_set_cursor_at_end_and_append_block(irb, else_block); if (peer_parent->peers.length != 0) { peer_parent->peers.last()->next_bb = else_block; } ResultLocPeer *peer_result = create_peer_result(peer_parent); peer_parent->peers.append(peer_result); else_result = ir_gen_node_extra(irb, else_node, parent_scope, LValNone, &peer_result->base); if (else_result == irb->codegen->invalid_inst_src) return else_result; if (!instr_is_unreachable(else_result)) ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); } IrBasicBlockSrc *after_else_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, end_block); if (else_result) { incoming_blocks.append(after_else_block); incoming_values.append(else_result); } else { incoming_blocks.append(after_cond_block); incoming_values.append(void_else_value); } if (peer_parent->peers.length != 0) { peer_parent->peers.last()->next_bb = end_block; } IrInstSrc *phi = ir_build_phi(irb, parent_scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items, peer_parent); return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc); } static IrInstSrc *ir_gen_bool_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeBoolLiteral); return ir_build_const_bool(irb, scope, node, node->data.bool_literal.value); } static IrInstSrc *ir_gen_enum_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeEnumLiteral); Buf *name = &node->data.enum_literal.identifier->data.str_lit.str; return ir_build_const_enum_literal(irb, scope, node, name); } static IrInstSrc *ir_gen_string_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeStringLiteral); return ir_build_const_str_lit(irb, scope, node, node->data.string_literal.buf); } static IrInstSrc *ir_gen_array_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeArrayType); AstNode *size_node = node->data.array_type.size; AstNode *child_type_node = node->data.array_type.child_type; bool is_const = node->data.array_type.is_const; bool is_volatile = node->data.array_type.is_volatile; bool is_allow_zero = node->data.array_type.allow_zero_token != nullptr; AstNode *sentinel_expr = node->data.array_type.sentinel; AstNode *align_expr = node->data.array_type.align_expr; Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope); IrInstSrc *sentinel; if (sentinel_expr != nullptr) { sentinel = ir_gen_node(irb, sentinel_expr, comptime_scope); if (sentinel == irb->codegen->invalid_inst_src) return sentinel; } else { sentinel = nullptr; } if (size_node) { if (is_const) { add_node_error(irb->codegen, node, buf_create_from_str("const qualifier invalid on array type")); return irb->codegen->invalid_inst_src; } if (is_volatile) { add_node_error(irb->codegen, node, buf_create_from_str("volatile qualifier invalid on array type")); return irb->codegen->invalid_inst_src; } if (is_allow_zero) { add_node_error(irb->codegen, node, buf_create_from_str("allowzero qualifier invalid on array type")); return irb->codegen->invalid_inst_src; } if (align_expr != nullptr) { add_node_error(irb->codegen, node, buf_create_from_str("align qualifier invalid on array type")); return irb->codegen->invalid_inst_src; } IrInstSrc *size_value = ir_gen_node(irb, size_node, comptime_scope); if (size_value == irb->codegen->invalid_inst_src) return size_value; IrInstSrc *child_type = ir_gen_node(irb, child_type_node, comptime_scope); if (child_type == irb->codegen->invalid_inst_src) return child_type; return ir_build_array_type(irb, scope, node, size_value, sentinel, child_type); } else { IrInstSrc *align_value; if (align_expr != nullptr) { align_value = ir_gen_node(irb, align_expr, comptime_scope); if (align_value == irb->codegen->invalid_inst_src) return align_value; } else { align_value = nullptr; } IrInstSrc *child_type = ir_gen_node(irb, child_type_node, comptime_scope); if (child_type == irb->codegen->invalid_inst_src) return child_type; return ir_build_slice_type(irb, scope, node, child_type, is_const, is_volatile, sentinel, align_value, is_allow_zero); } } static IrInstSrc *ir_gen_anyframe_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeAnyFrameType); AstNode *payload_type_node = node->data.anyframe_type.payload_type; IrInstSrc *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_inst_src) return payload_type_value; } return ir_build_anyframe_type(irb, scope, node, payload_type_value); } static IrInstSrc *ir_gen_undefined_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeUndefinedLiteral); return ir_build_const_undefined(irb, scope, node); } static Error parse_asm_template(IrAnalyze *ira, AstNode *source_node, Buf *asm_template, ZigList *tok_list) { // TODO Connect the errors in this function back up to the actual source location // rather than just the token. https://github.com/ziglang/zig/issues/2080 enum State { StateStart, StatePercent, StateTemplate, StateVar, }; assert(tok_list->length == 0); AsmToken *cur_tok = nullptr; enum State state = StateStart; for (size_t i = 0; i < buf_len(asm_template); i += 1) { uint8_t c = *((uint8_t*)buf_ptr(asm_template) + i); switch (state) { case StateStart: if (c == '%') { tok_list->add_one(); cur_tok = &tok_list->last(); cur_tok->id = AsmTokenIdPercent; cur_tok->start = i; state = StatePercent; } else { tok_list->add_one(); cur_tok = &tok_list->last(); cur_tok->id = AsmTokenIdTemplate; cur_tok->start = i; state = StateTemplate; } break; case StatePercent: if (c == '%') { cur_tok->end = i; state = StateStart; } else if (c == '[') { cur_tok->id = AsmTokenIdVar; state = StateVar; } else if (c == '=') { cur_tok->id = AsmTokenIdUniqueId; cur_tok->end = i; state = StateStart; } else { add_node_error(ira->codegen, source_node, buf_create_from_str("expected a '%' or '['")); return ErrorSemanticAnalyzeFail; } break; case StateTemplate: if (c == '%') { cur_tok->end = i; i -= 1; cur_tok = nullptr; state = StateStart; } break; case StateVar: if (c == ']') { cur_tok->end = i; state = StateStart; } else if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c == '_')) { // do nothing } else { add_node_error(ira->codegen, source_node, buf_sprintf("invalid substitution character: '%c'", c)); return ErrorSemanticAnalyzeFail; } break; } } switch (state) { case StateStart: break; case StatePercent: case StateVar: add_node_error(ira->codegen, source_node, buf_sprintf("unexpected end of assembly template")); return ErrorSemanticAnalyzeFail; case StateTemplate: cur_tok->end = buf_len(asm_template); break; } return ErrorNone; } static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok, Buf *src_template) { const char *ptr = buf_ptr(src_template) + tok->start + 2; size_t len = tok->end - tok->start - 2; size_t result = 0; for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1, result += 1) { AsmOutput *asm_output = node->data.asm_expr.output_list.at(i); if (buf_eql_mem(asm_output->asm_symbolic_name, ptr, len)) { return result; } } for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1, result += 1) { AsmInput *asm_input = node->data.asm_expr.input_list.at(i); if (buf_eql_mem(asm_input->asm_symbolic_name, ptr, len)) { return result; } } return SIZE_MAX; } static IrInstSrc *ir_gen_asm_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeAsmExpr); AstNodeAsmExpr *asm_expr = &node->data.asm_expr; IrInstSrc *asm_template = ir_gen_node(irb, asm_expr->asm_template, scope); if (asm_template == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; bool is_volatile = asm_expr->volatile_token != nullptr; bool in_fn_scope = (scope_fn_entry(scope) != nullptr); if (!in_fn_scope) { if (is_volatile) { add_token_error(irb->codegen, node->owner, asm_expr->volatile_token, buf_sprintf("volatile is meaningless on global assembly")); return irb->codegen->invalid_inst_src; } if (asm_expr->output_list.length != 0 || asm_expr->input_list.length != 0 || asm_expr->clobber_list.length != 0) { add_node_error(irb->codegen, node, buf_sprintf("global assembly cannot have inputs, outputs, or clobbers")); return irb->codegen->invalid_inst_src; } return ir_build_asm_src(irb, scope, node, asm_template, nullptr, nullptr, nullptr, 0, is_volatile, true); } IrInstSrc **input_list = heap::c_allocator.allocate(asm_expr->input_list.length); IrInstSrc **output_types = heap::c_allocator.allocate(asm_expr->output_list.length); ZigVar **output_vars = heap::c_allocator.allocate(asm_expr->output_list.length); size_t return_count = 0; if (!is_volatile && asm_expr->output_list.length == 0) { add_node_error(irb->codegen, node, buf_sprintf("assembly expression with no output must be marked volatile")); return irb->codegen->invalid_inst_src; } for (size_t i = 0; i < asm_expr->output_list.length; i += 1) { AsmOutput *asm_output = asm_expr->output_list.at(i); if (asm_output->return_type) { return_count += 1; IrInstSrc *return_type = ir_gen_node(irb, asm_output->return_type, scope); if (return_type == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; if (return_count > 1) { add_node_error(irb->codegen, node, buf_sprintf("inline assembly allows up to one output value")); return irb->codegen->invalid_inst_src; } 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_inst_src; } } const char modifier = *buf_ptr(asm_output->constraint); if (modifier != '=') { add_node_error(irb->codegen, node, buf_sprintf("invalid modifier starting output constraint for '%s': '%c', only '=' is supported." " Compiler TODO: see https://github.com/ziglang/zig/issues/215", buf_ptr(asm_output->asm_symbolic_name), modifier)); return irb->codegen->invalid_inst_src; } } for (size_t i = 0; i < asm_expr->input_list.length; i += 1) { AsmInput *asm_input = asm_expr->input_list.at(i); IrInstSrc *input_value = ir_gen_node(irb, asm_input->expr, scope); if (input_value == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; input_list[i] = input_value; } return ir_build_asm_src(irb, scope, node, asm_template, input_list, output_types, output_vars, return_count, is_volatile, false); } static IrInstSrc *ir_gen_if_optional_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { assert(node->type == NodeTypeIfOptional); Buf *var_symbol = node->data.test_expr.var_symbol; AstNode *expr_node = node->data.test_expr.target_node; AstNode *then_node = node->data.test_expr.then_node; AstNode *else_node = node->data.test_expr.else_node; bool var_is_ptr = node->data.test_expr.var_is_ptr; ScopeExpr *spill_scope = create_expr_scope(irb->codegen, expr_node, scope); spill_scope->spill_harder = true; IrInstSrc *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, &spill_scope->base, LValPtr, nullptr); if (maybe_val_ptr == irb->codegen->invalid_inst_src) return maybe_val_ptr; IrInstSrc *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr); IrInstSrc *is_non_null = ir_build_test_non_null_src(irb, scope, node, maybe_val); IrBasicBlockSrc *then_block = ir_create_basic_block(irb, scope, "OptionalThen"); IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "OptionalElse"); IrBasicBlockSrc *endif_block = ir_create_basic_block(irb, scope, "OptionalEndIf"); IrInstSrc *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); } IrInstSrc *cond_br_inst = ir_build_cond_br(irb, scope, node, is_non_null, then_block, else_block, is_comptime); ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block, result_loc, is_comptime); ir_set_cursor_at_end_and_append_block(irb, then_block); Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, &spill_scope->base, is_comptime); Scope *var_scope; if (var_symbol) { bool is_shadowable = false; bool is_const = true; ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_symbol, is_const, is_const, is_shadowable, is_comptime); IrInstSrc *payload_ptr = ir_build_optional_unwrap_ptr(irb, subexpr_scope, node, maybe_val_ptr, false); IrInstSrc *var_value = var_is_ptr ? payload_ptr : ir_build_load_ptr(irb, &spill_scope->base, node, payload_ptr); build_decl_var_and_init(irb, subexpr_scope, node, var, var_value, buf_ptr(var_symbol), is_comptime); var_scope = var->child_scope; } else { var_scope = subexpr_scope; } IrInstSrc *then_expr_result = ir_gen_node_extra(irb, then_node, var_scope, lval, &peer_parent->peers.at(0)->base); if (then_expr_result == irb->codegen->invalid_inst_src) return then_expr_result; IrBasicBlockSrc *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); IrInstSrc *else_expr_result; if (else_node) { else_expr_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_parent->peers.at(1)->base); if (else_expr_result == irb->codegen->invalid_inst_src) return else_expr_result; } else { else_expr_result = ir_build_const_void(irb, scope, node); ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base); } IrBasicBlockSrc *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); IrInstSrc **incoming_values = heap::c_allocator.allocate(2); incoming_values[0] = then_expr_result; incoming_values[1] = else_expr_result; IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate(2); incoming_blocks[0] = after_then_block; incoming_blocks[1] = after_else_block; IrInstSrc *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent); return ir_expr_wrap(irb, scope, phi, result_loc); } static IrInstSrc *ir_gen_if_err_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { assert(node->type == NodeTypeIfErrorExpr); AstNode *target_node = node->data.if_err_expr.target_node; AstNode *then_node = node->data.if_err_expr.then_node; AstNode *else_node = node->data.if_err_expr.else_node; bool var_is_ptr = node->data.if_err_expr.var_is_ptr; bool var_is_const = true; Buf *var_symbol = node->data.if_err_expr.var_symbol; Buf *err_symbol = node->data.if_err_expr.err_symbol; IrInstSrc *err_val_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr, nullptr); if (err_val_ptr == irb->codegen->invalid_inst_src) return err_val_ptr; IrInstSrc *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr); IrInstSrc *is_err = ir_build_test_err_src(irb, scope, node, err_val_ptr, true, false); IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, scope, "TryOk"); IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "TryElse"); IrBasicBlockSrc *endif_block = ir_create_basic_block(irb, scope, "TryEnd"); bool force_comptime = ir_should_inline(irb->exec, scope); IrInstSrc *is_comptime = force_comptime ? ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, is_err); IrInstSrc *cond_br_inst = ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime); ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block, result_loc, is_comptime); ir_set_cursor_at_end_and_append_block(irb, ok_block); Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); Scope *var_scope; if (var_symbol) { bool is_shadowable = false; IrInstSrc *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); IrInstSrc *payload_ptr = ir_build_unwrap_err_payload_src(irb, subexpr_scope, node, err_val_ptr, false, false); IrInstSrc *var_value = var_is_ptr ? payload_ptr : ir_build_load_ptr(irb, subexpr_scope, node, payload_ptr); build_decl_var_and_init(irb, subexpr_scope, node, var, var_value, buf_ptr(var_symbol), var_is_comptime); var_scope = var->child_scope; } else { var_scope = subexpr_scope; } IrInstSrc *then_expr_result = ir_gen_node_extra(irb, then_node, var_scope, lval, &peer_parent->peers.at(0)->base); if (then_expr_result == irb->codegen->invalid_inst_src) return then_expr_result; IrBasicBlockSrc *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); IrInstSrc *else_expr_result; if (else_node) { Scope *err_var_scope; if (err_symbol) { bool is_shadowable = false; bool is_const = true; ZigVar *var = ir_create_var(irb, node, subexpr_scope, err_symbol, is_const, is_const, is_shadowable, is_comptime); IrInstSrc *err_ptr = ir_build_unwrap_err_code_src(irb, subexpr_scope, node, err_val_ptr); IrInstSrc *err_value = ir_build_load_ptr(irb, subexpr_scope, node, err_ptr); build_decl_var_and_init(irb, subexpr_scope, node, var, err_value, buf_ptr(err_symbol), is_comptime); err_var_scope = var->child_scope; } else { err_var_scope = subexpr_scope; } else_expr_result = ir_gen_node_extra(irb, else_node, err_var_scope, lval, &peer_parent->peers.at(1)->base); if (else_expr_result == irb->codegen->invalid_inst_src) return else_expr_result; } else { else_expr_result = ir_build_const_void(irb, scope, node); ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base); } IrBasicBlockSrc *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); IrInstSrc **incoming_values = heap::c_allocator.allocate(2); incoming_values[0] = then_expr_result; incoming_values[1] = else_expr_result; IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate(2); incoming_blocks[0] = after_then_block; incoming_blocks[1] = after_else_block; IrInstSrc *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent); return ir_expr_wrap(irb, scope, phi, result_loc); } static bool ir_gen_switch_prong_expr(IrBuilderSrc *irb, Scope *scope, AstNode *switch_node, AstNode *prong_node, IrBasicBlockSrc *end_block, IrInstSrc *is_comptime, IrInstSrc *var_is_comptime, IrInstSrc *target_value_ptr, IrInstSrc **prong_values, size_t prong_values_len, ZigList *incoming_blocks, ZigList *incoming_values, IrInstSrcSwitchElseVar **out_switch_else_var, LVal lval, ResultLoc *result_loc) { assert(switch_node->type == NodeTypeSwitchExpr); assert(prong_node->type == NodeTypeSwitchProng); AstNode *expr_node = prong_node->data.switch_prong.expr; AstNode *var_symbol_node = prong_node->data.switch_prong.var_symbol; Scope *child_scope; if (var_symbol_node) { assert(var_symbol_node->type == NodeTypeSymbol); Buf *var_name = var_symbol_node->data.symbol_expr.symbol; bool var_is_ptr = prong_node->data.switch_prong.var_is_ptr; bool is_shadowable = false; bool is_const = true; ZigVar *var = ir_create_var(irb, var_symbol_node, scope, var_name, is_const, is_const, is_shadowable, var_is_comptime); child_scope = var->child_scope; IrInstSrc *var_value; if (out_switch_else_var != nullptr) { IrInstSrcSwitchElseVar *switch_else_var = ir_build_switch_else_var(irb, scope, var_symbol_node, target_value_ptr); *out_switch_else_var = switch_else_var; IrInstSrc *payload_ptr = &switch_else_var->base; var_value = var_is_ptr ? payload_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, payload_ptr); } else if (prong_values != nullptr) { IrInstSrc *payload_ptr = ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr, prong_values, prong_values_len); var_value = var_is_ptr ? payload_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, payload_ptr); } else { var_value = var_is_ptr ? target_value_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, target_value_ptr); } build_decl_var_and_init(irb, scope, var_symbol_node, var, var_value, buf_ptr(var_name), var_is_comptime); } else { child_scope = scope; } IrInstSrc *expr_result = ir_gen_node_extra(irb, expr_node, child_scope, lval, result_loc); if (expr_result == irb->codegen->invalid_inst_src) 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 IrInstSrc *ir_gen_switch_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { assert(node->type == NodeTypeSwitchExpr); AstNode *target_node = node->data.switch_expr.expr; IrInstSrc *target_value_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr, nullptr); if (target_value_ptr == irb->codegen->invalid_inst_src) return target_value_ptr; IrInstSrc *target_value = ir_build_switch_target(irb, scope, node, target_value_ptr); IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "SwitchElse"); IrBasicBlockSrc *end_block = ir_create_basic_block(irb, scope, "SwitchEnd"); size_t prong_count = node->data.switch_expr.prongs.length; ZigList cases = {0}; IrInstSrc *is_comptime; IrInstSrc *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}; IrInstSrcSwitchElseVar *switch_else_var = nullptr; ResultLocPeerParent *peer_parent = heap::c_allocator.create(); peer_parent->base.id = ResultLocIdPeerParent; peer_parent->base.allow_write_through_const = result_loc->allow_write_through_const; peer_parent->end_bb = end_block; peer_parent->is_comptime = is_comptime; peer_parent->parent = result_loc; ir_build_reset_result(irb, scope, node, &peer_parent->base); // First do the else and the ranges Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope); AstNode *else_prong = nullptr; AstNode *underscore_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_node->data.switch_prong.any_items_are_range) { ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); IrInstSrc *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; IrInstSrc *start_value = ir_gen_node(irb, start_node, comptime_scope); if (start_value == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrc *end_value = ir_gen_node(irb, end_node, comptime_scope); if (end_value == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrcCheckSwitchProngsRange *check_range = check_ranges.add_one(); check_range->start = start_value; check_range->end = end_value; IrInstSrc *lower_range_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpGreaterOrEq, target_value, start_value, false); IrInstSrc *upper_range_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpLessOrEq, target_value, end_value, false); IrInstSrc *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 { IrInstSrc *item_value = ir_gen_node(irb, item_node, comptime_scope); if (item_value == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrcCheckSwitchProngsRange *check_range = check_ranges.add_one(); check_range->start = item_value; check_range->end = item_value; IrInstSrc *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; } } } IrBasicBlockSrc *range_block_yes = ir_create_basic_block(irb, scope, "SwitchRangeYes"); IrBasicBlockSrc *range_block_no = ir_create_basic_block(irb, scope, "SwitchRangeNo"); assert(ok_bit); assert(last_item_node); IrInstSrc *br_inst = ir_mark_gen(ir_build_cond_br(irb, scope, last_item_node, ok_bit, range_block_yes, range_block_no, is_comptime)); if (peer_parent->base.source_instruction == nullptr) { peer_parent->base.source_instruction = br_inst; } if (peer_parent->peers.length > 0) { peer_parent->peers.last()->next_bb = range_block_yes; } peer_parent->peers.append(this_peer_result_loc); ir_set_cursor_at_end_and_append_block(irb, range_block_yes); if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, is_comptime, var_is_comptime, target_value_ptr, nullptr, 0, &incoming_blocks, &incoming_values, nullptr, LValNone, &this_peer_result_loc->base)) { return irb->codegen->invalid_inst_src; } ir_set_cursor_at_end_and_append_block(irb, range_block_no); } else { 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_inst_src; } else_prong = prong_node; } else if (prong_item_count == 1 && prong_node->data.switch_prong.items.at(0)->type == NodeTypeSymbol && buf_eql_str(prong_node->data.switch_prong.items.at(0)->data.symbol_expr.symbol, "_")) { if (underscore_prong) { ErrorMsg *msg = add_node_error(irb->codegen, prong_node, buf_sprintf("multiple '_' prongs in switch expression")); add_error_note(irb->codegen, msg, underscore_prong, buf_sprintf("previous '_' prong is here")); return irb->codegen->invalid_inst_src; } underscore_prong = prong_node; } else { continue; } if (underscore_prong && else_prong) { ErrorMsg *msg = add_node_error(irb->codegen, prong_node, buf_sprintf("else and '_' prong in switch expression")); if (underscore_prong == prong_node) add_error_note(irb->codegen, msg, else_prong, buf_sprintf("else prong is here")); else add_error_note(irb->codegen, msg, underscore_prong, buf_sprintf("'_' prong is here")); return irb->codegen->invalid_inst_src; } ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); IrBasicBlockSrc *prev_block = irb->current_basic_block; if (peer_parent->peers.length > 0) { peer_parent->peers.last()->next_bb = else_block; } peer_parent->peers.append(this_peer_result_loc); ir_set_cursor_at_end_and_append_block(irb, else_block); if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, is_comptime, var_is_comptime, target_value_ptr, nullptr, 0, &incoming_blocks, &incoming_values, &switch_else_var, LValNone, &this_peer_result_loc->base)) { return irb->codegen->invalid_inst_src; } ir_set_cursor_at_end(irb, prev_block); } } // 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; if (underscore_prong == prong_node) continue; ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); IrBasicBlockSrc *prong_block = ir_create_basic_block(irb, scope, "SwitchProng"); IrInstSrc **items = heap::c_allocator.allocate(prong_item_count); for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) { AstNode *item_node = prong_node->data.switch_prong.items.at(item_i); assert(item_node->type != NodeTypeSwitchRange); IrInstSrc *item_value = ir_gen_node(irb, item_node, comptime_scope); if (item_value == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrcCheckSwitchProngsRange *check_range = check_ranges.add_one(); check_range->start = item_value; check_range->end = item_value; IrInstSrcSwitchBrCase *this_case = cases.add_one(); this_case->value = item_value; this_case->block = prong_block; items[item_i] = item_value; } IrBasicBlockSrc *prev_block = irb->current_basic_block; if (peer_parent->peers.length > 0) { peer_parent->peers.last()->next_bb = prong_block; } peer_parent->peers.append(this_peer_result_loc); ir_set_cursor_at_end_and_append_block(irb, prong_block); if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, is_comptime, var_is_comptime, target_value_ptr, items, prong_item_count, &incoming_blocks, &incoming_values, nullptr, LValNone, &this_peer_result_loc->base)) { return irb->codegen->invalid_inst_src; } ir_set_cursor_at_end(irb, prev_block); } IrInstSrc *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value, check_ranges.items, check_ranges.length, else_prong != nullptr, underscore_prong != nullptr); IrInstSrc *br_instruction; if (cases.length == 0) { br_instruction = ir_build_br(irb, scope, node, else_block, is_comptime); } else { IrInstSrcSwitchBr *switch_br = ir_build_switch_br_src(irb, scope, node, target_value, else_block, cases.length, cases.items, is_comptime, switch_prongs_void); if (switch_else_var != nullptr) { switch_else_var->switch_br = switch_br; } br_instruction = &switch_br->base; } if (peer_parent->base.source_instruction == nullptr) { peer_parent->base.source_instruction = br_instruction; } for (size_t i = 0; i < peer_parent->peers.length; i += 1) { peer_parent->peers.at(i)->base.source_instruction = peer_parent->base.source_instruction; } if (!else_prong && !underscore_prong) { if (peer_parent->peers.length != 0) { peer_parent->peers.last()->next_bb = else_block; } ir_set_cursor_at_end_and_append_block(irb, else_block); ir_build_unreachable(irb, scope, node); } else { if (peer_parent->peers.length != 0) { peer_parent->peers.last()->next_bb = end_block; } } ir_set_cursor_at_end_and_append_block(irb, end_block); assert(incoming_blocks.length == incoming_values.length); IrInstSrc *result_instruction; if (incoming_blocks.length == 0) { result_instruction = ir_build_const_void(irb, scope, node); } else { result_instruction = ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items, peer_parent); } return ir_lval_wrap(irb, scope, result_instruction, lval, result_loc); } static IrInstSrc *ir_gen_comptime(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval) { assert(node->type == NodeTypeCompTime); Scope *child_scope = create_comptime_scope(irb->codegen, node, parent_scope); // purposefully pass null for result_loc and let EndExpr handle it return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval, nullptr); } static IrInstSrc *ir_gen_nosuspend(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval) { assert(node->type == NodeTypeNoSuspend); Scope *child_scope = create_nosuspend_scope(irb->codegen, node, parent_scope); // purposefully pass null for result_loc and let EndExpr handle it return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval, nullptr); } static IrInstSrc *ir_gen_return_from_block(IrBuilderSrc *irb, Scope *break_scope, AstNode *node, ScopeBlock *block_scope) { IrInstSrc *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; } IrInstSrc *result_value; if (node->data.break_expr.expr) { ResultLocPeer *peer_result = create_peer_result(block_scope->peer_parent); block_scope->peer_parent->peers.append(peer_result); result_value = ir_gen_node_extra(irb, node->data.break_expr.expr, break_scope, block_scope->lval, &peer_result->base); if (result_value == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; } else { result_value = ir_build_const_void(irb, break_scope, node); } IrBasicBlockSrc *dest_block = block_scope->end_block; if (!ir_gen_defers_for_block(irb, break_scope, dest_block->scope, nullptr, nullptr)) return irb->codegen->invalid_inst_src; 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 IrInstSrc *ir_gen_break(IrBuilderSrc *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_inst_src; } else { add_node_error(irb->codegen, node, buf_sprintf("break expression outside loop")); return irb->codegen->invalid_inst_src; } } else if (search_scope->id == ScopeIdDeferExpr) { add_node_error(irb->codegen, node, buf_sprintf("cannot break out of defer expression")); return irb->codegen->invalid_inst_src; } 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_inst_src; } search_scope = search_scope->parent; } IrInstSrc *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; } IrInstSrc *result_value; if (node->data.break_expr.expr) { ResultLocPeer *peer_result = create_peer_result(loop_scope->peer_parent); loop_scope->peer_parent->peers.append(peer_result); result_value = ir_gen_node_extra(irb, node->data.break_expr.expr, break_scope, loop_scope->lval, &peer_result->base); if (result_value == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; } else { result_value = ir_build_const_void(irb, break_scope, node); } IrBasicBlockSrc *dest_block = loop_scope->break_block; if (!ir_gen_defers_for_block(irb, break_scope, dest_block->scope, nullptr, nullptr)) return irb->codegen->invalid_inst_src; 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 IrInstSrc *ir_gen_continue(IrBuilderSrc *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_inst_src; } else { add_node_error(irb->codegen, node, buf_sprintf("continue expression outside loop")); return irb->codegen->invalid_inst_src; } } else if (search_scope->id == ScopeIdDeferExpr) { add_node_error(irb->codegen, node, buf_sprintf("cannot continue out of defer expression")); return irb->codegen->invalid_inst_src; } 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; } IrInstSrc *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)); } IrBasicBlockSrc *dest_block = loop_scope->continue_block; if (!ir_gen_defers_for_block(irb, continue_scope, dest_block->scope, nullptr, nullptr)) return irb->codegen->invalid_inst_src; return ir_mark_gen(ir_build_br(irb, continue_scope, node, dest_block, is_comptime)); } static IrInstSrc *ir_gen_error_type(IrBuilderSrc *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 IrInstSrc *ir_gen_defer(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeDefer); ScopeDefer *defer_child_scope = create_defer_scope(irb->codegen, node, parent_scope); node->data.defer.child_scope = &defer_child_scope->base; ScopeDeferExpr *defer_expr_scope = create_defer_expr_scope(irb->codegen, node, parent_scope); node->data.defer.expr_scope = &defer_expr_scope->base; return ir_build_const_void(irb, parent_scope, node); } static IrInstSrc *ir_gen_slice(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { assert(node->type == NodeTypeSliceExpr); AstNodeSliceExpr *slice_expr = &node->data.slice_expr; AstNode *array_node = slice_expr->array_ref_expr; AstNode *start_node = slice_expr->start; AstNode *end_node = slice_expr->end; AstNode *sentinel_node = slice_expr->sentinel; IrInstSrc *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr, nullptr); if (ptr_value == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrc *start_value = ir_gen_node(irb, start_node, scope); if (start_value == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrc *end_value; if (end_node) { end_value = ir_gen_node(irb, end_node, scope); if (end_value == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; } else { end_value = nullptr; } IrInstSrc *sentinel_value; if (sentinel_node) { sentinel_value = ir_gen_node(irb, sentinel_node, scope); if (sentinel_value == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; } else { sentinel_value = nullptr; } IrInstSrc *slice = ir_build_slice_src(irb, scope, node, ptr_value, start_value, end_value, sentinel_value, true, result_loc); return ir_lval_wrap(irb, scope, slice, lval, result_loc); } static IrInstSrc *ir_gen_catch(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval, ResultLoc *result_loc) { assert(node->type == NodeTypeCatchExpr); AstNode *op1_node = node->data.unwrap_err_expr.op1; AstNode *op2_node = node->data.unwrap_err_expr.op2; AstNode *var_node = node->data.unwrap_err_expr.symbol; if (op2_node->type == NodeTypeUnreachable) { if (var_node != nullptr) { assert(var_node->type == NodeTypeSymbol); Buf *var_name = var_node->data.symbol_expr.symbol; add_node_error(irb->codegen, var_node, buf_sprintf("unused variable: '%s'", buf_ptr(var_name))); return irb->codegen->invalid_inst_src; } return ir_gen_catch_unreachable(irb, parent_scope, node, op1_node, lval, result_loc); } ScopeExpr *spill_scope = create_expr_scope(irb->codegen, op1_node, parent_scope); spill_scope->spill_harder = true; IrInstSrc *err_union_ptr = ir_gen_node_extra(irb, op1_node, &spill_scope->base, LValPtr, nullptr); if (err_union_ptr == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrc *is_err = ir_build_test_err_src(irb, parent_scope, node, err_union_ptr, true, false); IrInstSrc *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); } IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrOk"); IrBasicBlockSrc *err_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrError"); IrBasicBlockSrc *end_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrEnd"); IrInstSrc *cond_br_inst = ir_build_cond_br(irb, parent_scope, node, is_err, err_block, ok_block, is_comptime); ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, ok_block, end_block, result_loc, is_comptime); ir_set_cursor_at_end_and_append_block(irb, err_block); Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, &spill_scope->base, is_comptime); Scope *err_scope; if (var_node) { assert(var_node->type == NodeTypeSymbol); Buf *var_name = var_node->data.symbol_expr.symbol; bool is_const = true; bool is_shadowable = false; ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_name, is_const, is_const, is_shadowable, is_comptime); err_scope = var->child_scope; IrInstSrc *err_ptr = ir_build_unwrap_err_code_src(irb, err_scope, node, err_union_ptr); IrInstSrc *err_value = ir_build_load_ptr(irb, err_scope, var_node, err_ptr); build_decl_var_and_init(irb, err_scope, var_node, var, err_value, buf_ptr(var_name), is_comptime); } else { err_scope = subexpr_scope; } IrInstSrc *err_result = ir_gen_node_extra(irb, op2_node, err_scope, LValNone, &peer_parent->peers.at(0)->base); if (err_result == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrBasicBlockSrc *after_err_block = irb->current_basic_block; if (!instr_is_unreachable(err_result)) ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, ok_block); IrInstSrc *unwrapped_ptr = ir_build_unwrap_err_payload_src(irb, parent_scope, node, err_union_ptr, false, false); IrInstSrc *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr); ir_build_end_expr(irb, parent_scope, node, unwrapped_payload, &peer_parent->peers.at(1)->base); IrBasicBlockSrc *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); IrInstSrc **incoming_values = heap::c_allocator.allocate(2); incoming_values[0] = err_result; incoming_values[1] = unwrapped_payload; IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate(2); incoming_blocks[0] = after_err_block; incoming_blocks[1] = after_ok_block; IrInstSrc *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent); return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc); } static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *outer_scope, Scope *inner_scope) { if (inner_scope == nullptr || inner_scope == outer_scope) return false; bool need_comma = render_instance_name_recursive(codegen, name, outer_scope, inner_scope->parent); if (inner_scope->id != ScopeIdVarDecl) return need_comma; ScopeVarDecl *var_scope = (ScopeVarDecl *)inner_scope; if (need_comma) buf_append_char(name, ','); // TODO: const ptr reinterpret here to make the var type agree with the value? render_const_value(codegen, name, var_scope->var->const_value); return true; } static Buf *get_anon_type_name(CodeGen *codegen, IrExecutableSrc *exec, const char *kind_name, Scope *scope, AstNode *source_node, Buf *out_bare_name) { if (exec != nullptr && exec->name) { ZigType *import = get_scope_import(scope); Buf *namespace_name = buf_alloc(); append_namespace_qualification(codegen, namespace_name, import); buf_append_buf(namespace_name, exec->name); buf_init_from_buf(out_bare_name, exec->name); return namespace_name; } else if (exec != nullptr && exec->name_fn != nullptr) { Buf *name = buf_alloc(); buf_append_buf(name, &exec->name_fn->symbol_name); buf_appendf(name, "("); render_instance_name_recursive(codegen, name, &exec->name_fn->fndef_scope->base, exec->begin_scope); buf_appendf(name, ")"); buf_init_from_buf(out_bare_name, name); return name; } else { ZigType *import = get_scope_import(scope); Buf *namespace_name = buf_alloc(); append_namespace_qualification(codegen, namespace_name, import); buf_appendf(namespace_name, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize, kind_name, source_node->line + 1, source_node->column + 1); buf_init_from_buf(out_bare_name, namespace_name); return namespace_name; } } static IrInstSrc *ir_gen_container_decl(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeContainerDecl); ContainerKind kind = node->data.container_decl.kind; Buf *bare_name = buf_alloc(); Buf *name = get_anon_type_name(irb->codegen, irb->exec, container_string(kind), parent_scope, node, bare_name); ContainerLayout layout = node->data.container_decl.layout; ZigType *container_type = get_partial_container_type(irb->codegen, parent_scope, kind, node, buf_ptr(name), bare_name, layout); ScopeDecls *child_scope = get_container_scope(container_type); for (size_t i = 0; i < node->data.container_decl.decls.length; i += 1) { AstNode *child_node = node->data.container_decl.decls.at(i); scan_decls(irb->codegen, child_scope, child_node); } TldContainer *tld_container = heap::c_allocator.create(); init_tld(&tld_container->base, TldIdContainer, bare_name, VisibModPub, node, parent_scope); tld_container->type_entry = container_type; tld_container->decls_scope = child_scope; irb->codegen->resolve_queue.append(&tld_container->base); // Add this to the list to mark as invalid if analyzing this exec fails. irb->exec->tld_list.append(&tld_container->base); return ir_build_const_type(irb, parent_scope, node, container_type); } // errors should be populated with set1's values static ZigType *get_error_set_union(CodeGen *g, ErrorTableEntry **errors, ZigType *set1, ZigType *set2, Buf *type_name) { assert(set1->id == ZigTypeIdErrorSet); assert(set2->id == ZigTypeIdErrorSet); ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); err_set_type->size_in_bits = g->builtin_types.entry_global_error_set->size_in_bits; err_set_type->abi_align = g->builtin_types.entry_global_error_set->abi_align; err_set_type->abi_size = g->builtin_types.entry_global_error_set->abi_size; if (type_name == nullptr) { buf_resize(&err_set_type->name, 0); buf_appendf(&err_set_type->name, "error{"); } else { buf_init_from_buf(&err_set_type->name, type_name); } for (uint32_t i = 0, count = set1->data.error_set.err_count; i < count; i += 1) { assert(errors[set1->data.error_set.errors[i]->value] == set1->data.error_set.errors[i]); } uint32_t count = set1->data.error_set.err_count; for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = set2->data.error_set.errors[i]; if (errors[error_entry->value] == nullptr) { count += 1; } } err_set_type->data.error_set.err_count = count; err_set_type->data.error_set.errors = heap::c_allocator.allocate(count); bool need_comma = false; for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = set1->data.error_set.errors[i]; if (type_name == nullptr) { const char *comma = need_comma ? "," : ""; need_comma = true; buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&error_entry->name)); } err_set_type->data.error_set.errors[i] = error_entry; } uint32_t index = set1->data.error_set.err_count; for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = set2->data.error_set.errors[i]; if (errors[error_entry->value] == nullptr) { errors[error_entry->value] = error_entry; if (type_name == nullptr) { const char *comma = need_comma ? "," : ""; need_comma = true; buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&error_entry->name)); } err_set_type->data.error_set.errors[index] = error_entry; index += 1; } } assert(index == count); if (type_name == nullptr) { buf_appendf(&err_set_type->name, "}"); } return err_set_type; } static ZigType *make_err_set_with_one_item(CodeGen *g, Scope *parent_scope, AstNode *node, ErrorTableEntry *err_entry) { ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); buf_resize(&err_set_type->name, 0); buf_appendf(&err_set_type->name, "error{%s}", buf_ptr(&err_entry->name)); err_set_type->size_in_bits = g->builtin_types.entry_global_error_set->size_in_bits; err_set_type->abi_align = g->builtin_types.entry_global_error_set->abi_align; err_set_type->abi_size = g->builtin_types.entry_global_error_set->abi_size; err_set_type->data.error_set.err_count = 1; err_set_type->data.error_set.errors = heap::c_allocator.create(); err_set_type->data.error_set.errors[0] = err_entry; return err_set_type; } static AstNode *ast_field_to_symbol_node(AstNode *err_set_field_node) { if (err_set_field_node->type == NodeTypeSymbol) { return err_set_field_node; } else if (err_set_field_node->type == NodeTypeErrorSetField) { assert(err_set_field_node->data.err_set_field.field_name->type == NodeTypeSymbol); return err_set_field_node->data.err_set_field.field_name; } else { return err_set_field_node; } } static IrInstSrc *ir_gen_err_set_decl(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeErrorSetDecl); uint32_t err_count = node->data.err_set_decl.decls.length; Buf bare_name = BUF_INIT; Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error", parent_scope, node, &bare_name); ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); buf_init_from_buf(&err_set_type->name, type_name); err_set_type->data.error_set.err_count = err_count; err_set_type->size_in_bits = irb->codegen->builtin_types.entry_global_error_set->size_in_bits; err_set_type->abi_align = irb->codegen->builtin_types.entry_global_error_set->abi_align; err_set_type->abi_size = irb->codegen->builtin_types.entry_global_error_set->abi_size; err_set_type->data.error_set.errors = heap::c_allocator.allocate(err_count); size_t errors_count = irb->codegen->errors_by_index.length + err_count; ErrorTableEntry **errors = heap::c_allocator.allocate(errors_count); for (uint32_t i = 0; i < err_count; i += 1) { AstNode *field_node = node->data.err_set_decl.decls.at(i); AstNode *symbol_node = ast_field_to_symbol_node(field_node); Buf *err_name = symbol_node->data.symbol_expr.symbol; ErrorTableEntry *err = heap::c_allocator.create(); err->decl_node = field_node; buf_init_from_buf(&err->name, err_name); auto existing_entry = irb->codegen->error_table.put_unique(err_name, err); if (existing_entry) { err->value = existing_entry->value->value; } else { size_t error_value_count = irb->codegen->errors_by_index.length; assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)irb->codegen->err_tag_type->data.integral.bit_count)); err->value = error_value_count; irb->codegen->errors_by_index.append(err); } err_set_type->data.error_set.errors[i] = err; ErrorTableEntry *prev_err = errors[err->value]; if (prev_err != nullptr) { ErrorMsg *msg = add_node_error(irb->codegen, ast_field_to_symbol_node(err->decl_node), buf_sprintf("duplicate error: '%s'", buf_ptr(&err->name))); add_error_note(irb->codegen, msg, ast_field_to_symbol_node(prev_err->decl_node), buf_sprintf("other error here")); return irb->codegen->invalid_inst_src; } errors[err->value] = err; } heap::c_allocator.deallocate(errors, errors_count); return ir_build_const_type(irb, parent_scope, node, err_set_type); } static IrInstSrc *ir_gen_fn_proto(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeFnProto); size_t param_count = node->data.fn_proto.params.length; IrInstSrc **param_types = heap::c_allocator.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; IrInstSrc *type_value = ir_gen_node(irb, type_node, parent_scope); if (type_value == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; param_types[i] = type_value; } else { param_types[i] = nullptr; } } IrInstSrc *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_inst_src) return irb->codegen->invalid_inst_src; } IrInstSrc *callconv_value = nullptr; if (node->data.fn_proto.callconv_expr != nullptr) { callconv_value = ir_gen_node(irb, node->data.fn_proto.callconv_expr, parent_scope); if (callconv_value == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; } IrInstSrc *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_inst_src) return irb->codegen->invalid_inst_src; } } 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_inst_src; //return_type = nullptr; } return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, callconv_value, return_type, is_var_args); } static IrInstSrc *ir_gen_resume(IrBuilderSrc *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeResume); if (get_scope_nosuspend(scope) != nullptr) { add_node_error(irb->codegen, node, buf_sprintf("resume in nosuspend scope")); return irb->codegen->invalid_inst_src; } IrInstSrc *target_inst = ir_gen_node_extra(irb, node->data.resume_expr.expr, scope, LValPtr, nullptr); if (target_inst == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; return ir_build_resume_src(irb, scope, node, target_inst); } static IrInstSrc *ir_gen_await_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { assert(node->type == NodeTypeAwaitExpr); bool is_nosuspend = get_scope_nosuspend(scope) != nullptr; AstNode *expr_node = node->data.await_expr.expr; if (expr_node->type == NodeTypeFnCallExpr && expr_node->data.fn_call_expr.modifier == CallModifierBuiltin) { AstNode *fn_ref_expr = expr_node->data.fn_call_expr.fn_ref_expr; Buf *name = fn_ref_expr->data.symbol_expr.symbol; auto entry = irb->codegen->builtin_fn_table.maybe_get(name); if (entry != nullptr) { BuiltinFnEntry *builtin_fn = entry->value; if (builtin_fn->id == BuiltinFnIdAsyncCall) { return ir_gen_async_call(irb, scope, node, expr_node, lval, result_loc); } } } ZigFn *fn_entry = exec_fn_entry(irb->exec); if (!fn_entry) { add_node_error(irb->codegen, node, buf_sprintf("await outside function definition")); return irb->codegen->invalid_inst_src; } ScopeSuspend *existing_suspend_scope = get_scope_suspend(scope); if (existing_suspend_scope) { if (!existing_suspend_scope->reported_err) { ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot await inside suspend block")); add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("suspend block here")); existing_suspend_scope->reported_err = true; } return irb->codegen->invalid_inst_src; } IrInstSrc *target_inst = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr); if (target_inst == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrc *await_inst = ir_build_await_src(irb, scope, node, target_inst, result_loc, is_nosuspend); return ir_lval_wrap(irb, scope, await_inst, lval, result_loc); } static IrInstSrc *ir_gen_suspend(IrBuilderSrc *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_inst_src; } if (get_scope_nosuspend(parent_scope) != nullptr) { add_node_error(irb->codegen, node, buf_sprintf("suspend in nosuspend scope")); return irb->codegen->invalid_inst_src; } 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_inst_src; } IrInstSrcSuspendBegin *begin = ir_build_suspend_begin_src(irb, parent_scope, node); if (node->data.suspend.block != nullptr) { ScopeSuspend *suspend_scope = create_suspend_scope(irb->codegen, node, parent_scope); Scope *child_scope = &suspend_scope->base; IrInstSrc *susp_res = ir_gen_node(irb, node->data.suspend.block, child_scope); if (susp_res == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.suspend.block, susp_res)); } return ir_mark_gen(ir_build_suspend_finish_src(irb, parent_scope, node, begin)); } static IrInstSrc *ir_gen_node_raw(IrBuilderSrc *irb, AstNode *node, Scope *scope, LVal lval, ResultLoc *result_loc) { assert(scope); switch (node->type) { case NodeTypeStructValueField: case NodeTypeParamDecl: case NodeTypeUsingNamespace: case NodeTypeSwitchProng: case NodeTypeSwitchRange: case NodeTypeStructField: case NodeTypeErrorSetField: case NodeTypeFnDef: case NodeTypeTestDecl: zig_unreachable(); case NodeTypeBlock: return ir_gen_block(irb, scope, node, lval, result_loc); case NodeTypeGroupedExpr: return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval, result_loc); case NodeTypeBinOpExpr: return ir_gen_bin_op(irb, scope, node, lval, result_loc); case NodeTypeIntLiteral: return ir_lval_wrap(irb, scope, ir_gen_int_lit(irb, scope, node), lval, result_loc); case NodeTypeFloatLiteral: return ir_lval_wrap(irb, scope, ir_gen_float_lit(irb, scope, node), lval, result_loc); case NodeTypeCharLiteral: return ir_lval_wrap(irb, scope, ir_gen_char_lit(irb, scope, node), lval, result_loc); case NodeTypeSymbol: return ir_gen_symbol(irb, scope, node, lval, result_loc); case NodeTypeFnCallExpr: return ir_gen_fn_call(irb, scope, node, lval, result_loc); case NodeTypeIfBoolExpr: return ir_gen_if_bool_expr(irb, scope, node, lval, result_loc); case NodeTypePrefixOpExpr: return ir_gen_prefix_op_expr(irb, scope, node, lval, result_loc); case NodeTypeContainerInitExpr: return ir_gen_container_init_expr(irb, scope, node, lval, result_loc); case NodeTypeVariableDeclaration: return ir_gen_var_decl(irb, scope, node); case NodeTypeWhileExpr: return ir_gen_while_expr(irb, scope, node, lval, result_loc); case NodeTypeForExpr: return ir_gen_for_expr(irb, scope, node, lval, result_loc); case NodeTypeArrayAccessExpr: return ir_gen_array_access(irb, scope, node, lval, result_loc); case NodeTypeReturnExpr: return ir_gen_return(irb, scope, node, lval, result_loc); case NodeTypeFieldAccessExpr: { IrInstSrc *ptr_instruction = ir_gen_field_access(irb, scope, node); if (ptr_instruction == irb->codegen->invalid_inst_src) return ptr_instruction; if (lval == LValPtr || lval == LValAssign) return ptr_instruction; IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction); return ir_expr_wrap(irb, scope, load_ptr, result_loc); } case NodeTypePtrDeref: { AstNode *expr_node = node->data.ptr_deref_expr.target; LVal child_lval = lval; if (child_lval == LValAssign) child_lval = LValPtr; IrInstSrc *value = ir_gen_node_extra(irb, expr_node, scope, child_lval, nullptr); if (value == irb->codegen->invalid_inst_src) return value; // We essentially just converted any lvalue from &(x.*) to (&x).*; // this inhibits checking that x is a pointer later, so we directly // record whether the pointer check is needed IrInstSrc *un_op = ir_build_un_op_lval(irb, scope, node, IrUnOpDereference, value, lval, result_loc); return ir_expr_wrap(irb, scope, un_op, result_loc); } case NodeTypeUnwrapOptional: { AstNode *expr_node = node->data.unwrap_optional.expr; IrInstSrc *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr); if (maybe_ptr == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; IrInstSrc *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, maybe_ptr, true ); if (lval == LValPtr || lval == LValAssign) return unwrapped_ptr; IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, unwrapped_ptr); return ir_expr_wrap(irb, scope, load_ptr, result_loc); } case NodeTypeBoolLiteral: return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval, result_loc); case NodeTypeArrayType: return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval, result_loc); case NodeTypePointerType: return ir_lval_wrap(irb, scope, ir_gen_pointer_type(irb, scope, node), lval, result_loc); case NodeTypeAnyFrameType: return ir_lval_wrap(irb, scope, ir_gen_anyframe_type(irb, scope, node), lval, result_loc); case NodeTypeStringLiteral: return ir_lval_wrap(irb, scope, ir_gen_string_literal(irb, scope, node), lval, result_loc); case NodeTypeUndefinedLiteral: return ir_lval_wrap(irb, scope, ir_gen_undefined_literal(irb, scope, node), lval, result_loc); case NodeTypeAsmExpr: return ir_lval_wrap(irb, scope, ir_gen_asm_expr(irb, scope, node), lval, result_loc); case NodeTypeNullLiteral: return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval, result_loc); case NodeTypeIfErrorExpr: return ir_gen_if_err_expr(irb, scope, node, lval, result_loc); case NodeTypeIfOptional: return ir_gen_if_optional_expr(irb, scope, node, lval, result_loc); case NodeTypeSwitchExpr: return ir_gen_switch_expr(irb, scope, node, lval, result_loc); case NodeTypeCompTime: return ir_expr_wrap(irb, scope, ir_gen_comptime(irb, scope, node, lval), result_loc); case NodeTypeNoSuspend: return ir_expr_wrap(irb, scope, ir_gen_nosuspend(irb, scope, node, lval), result_loc); case NodeTypeErrorType: return ir_lval_wrap(irb, scope, ir_gen_error_type(irb, scope, node), lval, result_loc); case NodeTypeBreak: return ir_lval_wrap(irb, scope, ir_gen_break(irb, scope, node), lval, result_loc); case NodeTypeContinue: return ir_lval_wrap(irb, scope, ir_gen_continue(irb, scope, node), lval, result_loc); case NodeTypeUnreachable: return ir_build_unreachable(irb, scope, node); case NodeTypeDefer: return ir_lval_wrap(irb, scope, ir_gen_defer(irb, scope, node), lval, result_loc); case NodeTypeSliceExpr: return ir_gen_slice(irb, scope, node, lval, result_loc); case NodeTypeCatchExpr: return ir_gen_catch(irb, scope, node, lval, result_loc); case NodeTypeContainerDecl: return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval, result_loc); case NodeTypeFnProto: return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval, result_loc); case NodeTypeErrorSetDecl: return ir_lval_wrap(irb, scope, ir_gen_err_set_decl(irb, scope, node), lval, result_loc); case NodeTypeResume: return ir_lval_wrap(irb, scope, ir_gen_resume(irb, scope, node), lval, result_loc); case NodeTypeAwaitExpr: return ir_gen_await_expr(irb, scope, node, lval, result_loc); case NodeTypeSuspend: return ir_lval_wrap(irb, scope, ir_gen_suspend(irb, scope, node), lval, result_loc); case NodeTypeEnumLiteral: return ir_lval_wrap(irb, scope, ir_gen_enum_literal(irb, scope, node), lval, result_loc); case NodeTypeInferredArrayType: add_node_error(irb->codegen, node, buf_sprintf("inferred array size invalid here")); return irb->codegen->invalid_inst_src; case NodeTypeVarFieldType: return ir_lval_wrap(irb, scope, ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_var), lval, result_loc); } zig_unreachable(); } static ResultLoc *no_result_loc(void) { ResultLocNone *result_loc_none = heap::c_allocator.create(); result_loc_none->base.id = ResultLocIdNone; return &result_loc_none->base; } static IrInstSrc *ir_gen_node_extra(IrBuilderSrc *irb, AstNode *node, Scope *scope, LVal lval, ResultLoc *result_loc) { if (lval == LValAssign) { switch (node->type) { case NodeTypeStructValueField: case NodeTypeParamDecl: case NodeTypeUsingNamespace: case NodeTypeSwitchProng: case NodeTypeSwitchRange: case NodeTypeStructField: case NodeTypeErrorSetField: case NodeTypeFnDef: case NodeTypeTestDecl: zig_unreachable(); // cannot be assigned to case NodeTypeBlock: case NodeTypeGroupedExpr: case NodeTypeBinOpExpr: case NodeTypeIntLiteral: case NodeTypeFloatLiteral: case NodeTypeCharLiteral: case NodeTypeIfBoolExpr: case NodeTypeContainerInitExpr: case NodeTypeVariableDeclaration: case NodeTypeWhileExpr: case NodeTypeForExpr: case NodeTypeReturnExpr: case NodeTypeBoolLiteral: case NodeTypeArrayType: case NodeTypePointerType: case NodeTypeAnyFrameType: case NodeTypeStringLiteral: case NodeTypeUndefinedLiteral: case NodeTypeAsmExpr: case NodeTypeNullLiteral: case NodeTypeIfErrorExpr: case NodeTypeIfOptional: case NodeTypeSwitchExpr: case NodeTypeCompTime: case NodeTypeNoSuspend: case NodeTypeErrorType: case NodeTypeBreak: case NodeTypeContinue: case NodeTypeUnreachable: case NodeTypeDefer: case NodeTypeSliceExpr: case NodeTypeCatchExpr: case NodeTypeContainerDecl: case NodeTypeFnProto: case NodeTypeErrorSetDecl: case NodeTypeResume: case NodeTypeAwaitExpr: case NodeTypeSuspend: case NodeTypeEnumLiteral: case NodeTypeInferredArrayType: case NodeTypeVarFieldType: case NodeTypePrefixOpExpr: add_node_error(irb->codegen, node, buf_sprintf("invalid left-hand side to assignment")); return irb->codegen->invalid_inst_src; // @field can be assigned to case NodeTypeFnCallExpr: if (node->data.fn_call_expr.modifier == CallModifierBuiltin) { 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_inst_src; } if (entry->value->id == BuiltinFnIdField) { break; } } add_node_error(irb->codegen, node, buf_sprintf("invalid left-hand side to assignment")); return irb->codegen->invalid_inst_src; // can be assigned to case NodeTypeUnwrapOptional: case NodeTypePtrDeref: case NodeTypeFieldAccessExpr: case NodeTypeArrayAccessExpr: case NodeTypeSymbol: break; } } if (result_loc == nullptr) { // Create a result location indicating there is none - but if one gets created // it will be properly distributed. result_loc = no_result_loc(); ir_build_reset_result(irb, scope, node, result_loc); } Scope *child_scope; if (irb->exec->is_inline || (irb->exec->fn_entry != nullptr && irb->exec->fn_entry->child_scope == scope)) { child_scope = scope; } else { child_scope = &create_expr_scope(irb->codegen, node, scope)->base; } IrInstSrc *result = ir_gen_node_raw(irb, node, child_scope, lval, result_loc); if (result == irb->codegen->invalid_inst_src) { if (irb->exec->first_err_trace_msg == nullptr) { irb->exec->first_err_trace_msg = irb->codegen->trace_err; } } return result; } static IrInstSrc *ir_gen_node(IrBuilderSrc *irb, AstNode *node, Scope *scope) { return ir_gen_node_extra(irb, node, scope, LValNone, nullptr); } static void invalidate_exec(IrExecutableSrc *exec, ErrorMsg *msg) { if (exec->first_err_trace_msg != nullptr) return; exec->first_err_trace_msg = msg; for (size_t i = 0; i < exec->tld_list.length; i += 1) { exec->tld_list.items[i]->resolution = TldResolutionInvalid; } } static void invalidate_exec_gen(IrExecutableGen *exec, ErrorMsg *msg) { if (exec->first_err_trace_msg != nullptr) return; exec->first_err_trace_msg = msg; for (size_t i = 0; i < exec->tld_list.length; i += 1) { exec->tld_list.items[i]->resolution = TldResolutionInvalid; } if (exec->source_exec != nullptr) invalidate_exec(exec->source_exec, msg); } bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutableSrc *ir_executable) { assert(node->owner); IrBuilderSrc ir_builder = {0}; IrBuilderSrc *irb = &ir_builder; irb->codegen = codegen; irb->exec = ir_executable; irb->main_block_node = node; IrBasicBlockSrc *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); IrInstSrc *result = ir_gen_node_extra(irb, node, scope, LValNone, nullptr); if (result == irb->codegen->invalid_inst_src) return false; if (irb->exec->first_err_trace_msg != nullptr) { codegen->trace_err = irb->exec->first_err_trace_msg; return false; } if (!instr_is_unreachable(result)) { ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, result->base.source_node, result, nullptr)); // no need for save_err_ret_addr because this cannot return error ResultLocReturn *result_loc_ret = heap::c_allocator.create(); result_loc_ret->base.id = ResultLocIdReturn; ir_build_reset_result(irb, scope, node, &result_loc_ret->base); ir_mark_gen(ir_build_end_expr(irb, scope, node, result, &result_loc_ret->base)); ir_mark_gen(ir_build_return_src(irb, scope, result->base.source_node, result)); } return true; } bool ir_gen_fn(CodeGen *codegen, ZigFn *fn_entry) { assert(fn_entry); IrExecutableSrc *ir_executable = fn_entry->ir_executable; AstNode *body_node = fn_entry->body_node; assert(fn_entry->child_scope); return ir_gen(codegen, body_node, fn_entry->child_scope, ir_executable); } static void ir_add_call_stack_errors_gen(CodeGen *codegen, IrExecutableGen *exec, ErrorMsg *err_msg, int limit) { if (!exec || !exec->source_node || limit < 0) return; add_error_note(codegen, err_msg, exec->source_node, buf_sprintf("called from here")); ir_add_call_stack_errors_gen(codegen, exec->parent_exec, err_msg, limit - 1); } static void ir_add_call_stack_errors(CodeGen *codegen, IrExecutableSrc *exec, ErrorMsg *err_msg, int limit) { if (!exec || !exec->source_node || limit < 0) return; add_error_note(codegen, err_msg, exec->source_node, buf_sprintf("called from here")); ir_add_call_stack_errors_gen(codegen, exec->parent_exec, err_msg, limit - 1); } static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutableSrc *exec, AstNode *source_node, Buf *msg) { ErrorMsg *err_msg = add_node_error(codegen, source_node, msg); invalidate_exec(exec, err_msg); if (exec->parent_exec) { ir_add_call_stack_errors(codegen, exec, err_msg, 10); } return err_msg; } static ErrorMsg *exec_add_error_node_gen(CodeGen *codegen, IrExecutableGen *exec, AstNode *source_node, Buf *msg) { ErrorMsg *err_msg = add_node_error(codegen, source_node, msg); invalidate_exec_gen(exec, err_msg); if (exec->parent_exec) { ir_add_call_stack_errors_gen(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_gen(ira->codegen, ira->new_irb.exec, source_node, msg); } static ErrorMsg *opt_ir_add_error_node(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, Buf *msg) { if (ira != nullptr) return exec_add_error_node_gen(codegen, ira->new_irb.exec, source_node, msg); else return add_node_error(codegen, source_node, msg); } static ErrorMsg *ir_add_error(IrAnalyze *ira, IrInst *source_instruction, Buf *msg) { return ir_add_error_node(ira, source_instruction->source_node, msg); } static void ir_assert_impl(bool ok, IrInst *source_instruction, char const *file, unsigned int line) { if (ok) return; src_assert_impl(ok, source_instruction->source_node, file, line); } static void ir_assert_gen_impl(bool ok, IrInstGen *source_instruction, char const *file, unsigned int line) { if (ok) return; src_assert_impl(ok, source_instruction->base.source_node, file, line); } // This function takes a comptime ptr and makes the child const value conform to the type // described by the pointer. static Error eval_comptime_ptr_reinterpret(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, ZigValue *ptr_val) { Error err; assert(ptr_val->type->id == ZigTypeIdPointer); assert(ptr_val->special == ConstValSpecialStatic); ZigValue tmp = {}; tmp.special = ConstValSpecialStatic; tmp.type = ptr_val->type->data.pointer.child_type; if ((err = ir_read_const_ptr(ira, codegen, source_node, &tmp, ptr_val))) return err; ZigValue *child_val = const_ptr_pointee_unchecked(codegen, ptr_val); copy_const_val(codegen, child_val, &tmp); return ErrorNone; } ZigValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ZigValue *const_val, AstNode *source_node) { Error err; ZigValue *val = const_ptr_pointee_unchecked(codegen, const_val); if (val == nullptr) return nullptr; assert(const_val->type->id == ZigTypeIdPointer); ZigType *expected_type = const_val->type->data.pointer.child_type; if (expected_type == codegen->builtin_types.entry_var) { return val; } switch (type_has_one_possible_value(codegen, expected_type)) { case OnePossibleValueInvalid: return nullptr; case OnePossibleValueNo: break; case OnePossibleValueYes: return get_the_one_possible_value(codegen, expected_type); } if (!types_have_same_zig_comptime_repr(codegen, expected_type, val->type)) { if ((err = eval_comptime_ptr_reinterpret(ira, codegen, source_node, const_val))) return nullptr; return const_ptr_pointee_unchecked(codegen, const_val); } return val; } static Error ir_exec_scan_for_side_effects(CodeGen *codegen, IrExecutableGen *exec) { IrBasicBlockGen *bb = exec->basic_block_list.at(0); for (size_t i = 0; i < bb->instruction_list.length; i += 1) { IrInstGen *instruction = bb->instruction_list.at(i); if (instruction->id == IrInstGenIdReturn) { return ErrorNone; } else if (ir_inst_gen_has_side_effects(instruction)) { if (instr_is_comptime(instruction)) { switch (instruction->id) { case IrInstGenIdUnwrapErrPayload: case IrInstGenIdOptionalUnwrapPtr: case IrInstGenIdUnionFieldPtr: continue; default: break; } } if (get_scope_typeof(instruction->base.scope) != nullptr) { // doesn't count, it's inside a @TypeOf() continue; } exec_add_error_node_gen(codegen, exec, instruction->base.source_node, buf_sprintf("unable to evaluate constant expression")); return ErrorSemanticAnalyzeFail; } } zig_unreachable(); } static bool ir_emit_global_runtime_side_effect(IrAnalyze *ira, IrInst* source_instruction) { if (ir_should_inline(ira->old_irb.exec, source_instruction->scope)) { ir_add_error(ira, source_instruction, buf_sprintf("unable to evaluate constant expression")); return false; } return true; } static bool const_val_fits_in_num_lit(ZigValue *const_val, ZigType *num_lit_type) { return ((num_lit_type->id == ZigTypeIdComptimeFloat && (const_val->type->id == ZigTypeIdFloat || const_val->type->id == ZigTypeIdComptimeFloat)) || (num_lit_type->id == ZigTypeIdComptimeInt && (const_val->type->id == ZigTypeIdInt || const_val->type->id == ZigTypeIdComptimeInt))); } static bool float_has_fraction(ZigValue *const_val) { if (const_val->type->id == ZigTypeIdComptimeFloat) { return bigfloat_has_fraction(&const_val->data.x_bigfloat); } else if (const_val->type->id == ZigTypeIdFloat) { switch (const_val->type->data.floating.bit_count) { case 16: { float16_t floored = f16_roundToInt(const_val->data.x_f16, softfloat_round_minMag, false); return !f16_eq(floored, const_val->data.x_f16); } case 32: return floorf(const_val->data.x_f32) != const_val->data.x_f32; case 64: return floor(const_val->data.x_f64) != const_val->data.x_f64; case 128: { float128_t floored; f128M_roundToInt(&const_val->data.x_f128, softfloat_round_minMag, false, &floored); return !f128M_eq(&floored, &const_val->data.x_f128); } default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_append_buf(Buf *buf, ZigValue *const_val) { if (const_val->type->id == ZigTypeIdComptimeFloat) { bigfloat_append_buf(buf, &const_val->data.x_bigfloat); } else if (const_val->type->id == ZigTypeIdFloat) { switch (const_val->type->data.floating.bit_count) { case 16: buf_appendf(buf, "%f", zig_f16_to_double(const_val->data.x_f16)); break; case 32: buf_appendf(buf, "%f", const_val->data.x_f32); break; case 64: buf_appendf(buf, "%f", const_val->data.x_f64); break; case 128: { // TODO actual implementation const size_t extra_len = 100; size_t old_len = buf_len(buf); buf_resize(buf, old_len + extra_len); float64_t f64_value = f128M_to_f64(&const_val->data.x_f128); double double_value; memcpy(&double_value, &f64_value, sizeof(double)); int len = snprintf(buf_ptr(buf) + old_len, extra_len, "%f", double_value); assert(len > 0); buf_resize(buf, old_len + len); break; } default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_init_bigint(BigInt *bigint, ZigValue *const_val) { if (const_val->type->id == ZigTypeIdComptimeFloat) { bigint_init_bigfloat(bigint, &const_val->data.x_bigfloat); } else if (const_val->type->id == ZigTypeIdFloat) { switch (const_val->type->data.floating.bit_count) { case 16: { double x = zig_f16_to_double(const_val->data.x_f16); if (x >= 0) { bigint_init_unsigned(bigint, (uint64_t)x); } else { bigint_init_unsigned(bigint, (uint64_t)-x); bigint->is_negative = true; } break; } case 32: if (const_val->data.x_f32 >= 0) { bigint_init_unsigned(bigint, (uint64_t)(const_val->data.x_f32)); } else { bigint_init_unsigned(bigint, (uint64_t)(-const_val->data.x_f32)); bigint->is_negative = true; } break; case 64: if (const_val->data.x_f64 >= 0) { bigint_init_unsigned(bigint, (uint64_t)(const_val->data.x_f64)); } else { bigint_init_unsigned(bigint, (uint64_t)(-const_val->data.x_f64)); bigint->is_negative = true; } break; case 128: { BigFloat tmp_float; bigfloat_init_128(&tmp_float, const_val->data.x_f128); bigint_init_bigfloat(bigint, &tmp_float); } break; default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_init_bigfloat(ZigValue *dest_val, BigFloat *bigfloat) { if (dest_val->type->id == ZigTypeIdComptimeFloat) { bigfloat_init_bigfloat(&dest_val->data.x_bigfloat, bigfloat); } else if (dest_val->type->id == ZigTypeIdFloat) { switch (dest_val->type->data.floating.bit_count) { case 16: dest_val->data.x_f16 = bigfloat_to_f16(bigfloat); break; case 32: dest_val->data.x_f32 = bigfloat_to_f32(bigfloat); break; case 64: dest_val->data.x_f64 = bigfloat_to_f64(bigfloat); break; case 80: zig_panic("TODO"); case 128: dest_val->data.x_f128 = bigfloat_to_f128(bigfloat); break; default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_init_f16(ZigValue *dest_val, float16_t x) { if (dest_val->type->id == ZigTypeIdComptimeFloat) { bigfloat_init_16(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == ZigTypeIdFloat) { switch (dest_val->type->data.floating.bit_count) { case 16: dest_val->data.x_f16 = x; break; case 32: dest_val->data.x_f32 = zig_f16_to_double(x); break; case 64: dest_val->data.x_f64 = zig_f16_to_double(x); break; case 128: f16_to_f128M(x, &dest_val->data.x_f128); break; default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_init_f32(ZigValue *dest_val, float x) { if (dest_val->type->id == ZigTypeIdComptimeFloat) { bigfloat_init_32(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == ZigTypeIdFloat) { switch (dest_val->type->data.floating.bit_count) { case 16: dest_val->data.x_f16 = zig_double_to_f16(x); break; case 32: dest_val->data.x_f32 = x; break; case 64: dest_val->data.x_f64 = x; break; case 128: { float32_t x_f32; memcpy(&x_f32, &x, sizeof(float)); f32_to_f128M(x_f32, &dest_val->data.x_f128); break; } default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_init_f64(ZigValue *dest_val, double x) { if (dest_val->type->id == ZigTypeIdComptimeFloat) { bigfloat_init_64(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == ZigTypeIdFloat) { switch (dest_val->type->data.floating.bit_count) { case 16: dest_val->data.x_f16 = zig_double_to_f16(x); break; case 32: dest_val->data.x_f32 = x; break; case 64: dest_val->data.x_f64 = x; break; case 128: { float64_t x_f64; memcpy(&x_f64, &x, sizeof(double)); f64_to_f128M(x_f64, &dest_val->data.x_f128); break; } default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_init_f128(ZigValue *dest_val, float128_t x) { if (dest_val->type->id == ZigTypeIdComptimeFloat) { bigfloat_init_128(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == ZigTypeIdFloat) { switch (dest_val->type->data.floating.bit_count) { case 16: dest_val->data.x_f16 = f128M_to_f16(&x); break; case 32: { float32_t f32_val = f128M_to_f32(&x); memcpy(&dest_val->data.x_f32, &f32_val, sizeof(float)); break; } case 64: { float64_t f64_val = f128M_to_f64(&x); memcpy(&dest_val->data.x_f64, &f64_val, sizeof(double)); break; } case 128: { memcpy(&dest_val->data.x_f128, &x, sizeof(float128_t)); break; } default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_init_float(ZigValue *dest_val, ZigValue *src_val) { if (src_val->type->id == ZigTypeIdComptimeFloat) { float_init_bigfloat(dest_val, &src_val->data.x_bigfloat); } else if (src_val->type->id == ZigTypeIdFloat) { switch (src_val->type->data.floating.bit_count) { case 16: float_init_f16(dest_val, src_val->data.x_f16); break; case 32: float_init_f32(dest_val, src_val->data.x_f32); break; case 64: float_init_f64(dest_val, src_val->data.x_f64); break; case 128: float_init_f128(dest_val, src_val->data.x_f128); break; default: zig_unreachable(); } } else { zig_unreachable(); } } static bool float_is_nan(ZigValue *op) { if (op->type->id == ZigTypeIdComptimeFloat) { return bigfloat_is_nan(&op->data.x_bigfloat); } else if (op->type->id == ZigTypeIdFloat) { switch (op->type->data.floating.bit_count) { case 16: return f16_isSignalingNaN(op->data.x_f16); case 32: return op->data.x_f32 != op->data.x_f32; case 64: return op->data.x_f64 != op->data.x_f64; case 128: return f128M_isSignalingNaN(&op->data.x_f128); default: zig_unreachable(); } } else { zig_unreachable(); } } static Cmp float_cmp(ZigValue *op1, ZigValue *op2) { if (op1->type == op2->type) { if (op1->type->id == ZigTypeIdComptimeFloat) { return bigfloat_cmp(&op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == ZigTypeIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: if (f16_lt(op1->data.x_f16, op2->data.x_f16)) { return CmpLT; } else if (f16_lt(op2->data.x_f16, op1->data.x_f16)) { return CmpGT; } else { return CmpEQ; } case 32: if (op1->data.x_f32 > op2->data.x_f32) { return CmpGT; } else if (op1->data.x_f32 < op2->data.x_f32) { return CmpLT; } else { return CmpEQ; } case 64: if (op1->data.x_f64 > op2->data.x_f64) { return CmpGT; } else if (op1->data.x_f64 < op2->data.x_f64) { return CmpLT; } else { return CmpEQ; } case 128: if (f128M_lt(&op1->data.x_f128, &op2->data.x_f128)) { return CmpLT; } else if (f128M_eq(&op1->data.x_f128, &op2->data.x_f128)) { return CmpEQ; } else { return CmpGT; } default: zig_unreachable(); } } else { zig_unreachable(); } } BigFloat op1_big; BigFloat op2_big; float_init_bigfloat(op1, &op1_big); float_init_bigfloat(op2, &op2_big); return bigfloat_cmp(&op1_big, &op2_big); } // This function cannot handle NaN static Cmp float_cmp_zero(ZigValue *op) { if (op->type->id == ZigTypeIdComptimeFloat) { return bigfloat_cmp_zero(&op->data.x_bigfloat); } else if (op->type->id == ZigTypeIdFloat) { switch (op->type->data.floating.bit_count) { case 16: { const float16_t zero = zig_double_to_f16(0); if (f16_lt(op->data.x_f16, zero)) { return CmpLT; } else if (f16_lt(zero, op->data.x_f16)) { return CmpGT; } else { return CmpEQ; } } case 32: if (op->data.x_f32 < 0.0) { return CmpLT; } else if (op->data.x_f32 > 0.0) { return CmpGT; } else { return CmpEQ; } case 64: if (op->data.x_f64 < 0.0) { return CmpLT; } else if (op->data.x_f64 > 0.0) { return CmpGT; } else { return CmpEQ; } case 128: float128_t zero_float; ui32_to_f128M(0, &zero_float); if (f128M_lt(&op->data.x_f128, &zero_float)) { return CmpLT; } else if (f128M_eq(&op->data.x_f128, &zero_float)) { return CmpEQ; } else { return CmpGT; } default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_add(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { bigfloat_add(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == ZigTypeIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: out_val->data.x_f16 = f16_add(op1->data.x_f16, op2->data.x_f16); return; case 32: out_val->data.x_f32 = op1->data.x_f32 + op2->data.x_f32; return; case 64: out_val->data.x_f64 = op1->data.x_f64 + op2->data.x_f64; return; case 128: f128M_add(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); return; default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_sub(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { bigfloat_sub(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == ZigTypeIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: out_val->data.x_f16 = f16_sub(op1->data.x_f16, op2->data.x_f16); return; case 32: out_val->data.x_f32 = op1->data.x_f32 - op2->data.x_f32; return; case 64: out_val->data.x_f64 = op1->data.x_f64 - op2->data.x_f64; return; case 128: f128M_sub(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); return; default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_mul(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { bigfloat_mul(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == ZigTypeIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: out_val->data.x_f16 = f16_mul(op1->data.x_f16, op2->data.x_f16); return; case 32: out_val->data.x_f32 = op1->data.x_f32 * op2->data.x_f32; return; case 64: out_val->data.x_f64 = op1->data.x_f64 * op2->data.x_f64; return; case 128: f128M_mul(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); return; default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_div(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { bigfloat_div(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == ZigTypeIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16); return; case 32: out_val->data.x_f32 = op1->data.x_f32 / op2->data.x_f32; return; case 64: out_val->data.x_f64 = op1->data.x_f64 / op2->data.x_f64; return; case 128: f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); return; default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_div_trunc(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { bigfloat_div_trunc(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == ZigTypeIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16); out_val->data.x_f16 = f16_roundToInt(out_val->data.x_f16, softfloat_round_minMag, false); return; case 32: out_val->data.x_f32 = truncf(op1->data.x_f32 / op2->data.x_f32); return; case 64: out_val->data.x_f64 = trunc(op1->data.x_f64 / op2->data.x_f64); return; case 128: f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); f128M_roundToInt(&out_val->data.x_f128, softfloat_round_minMag, false, &out_val->data.x_f128); return; default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_div_floor(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { bigfloat_div_floor(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == ZigTypeIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16); out_val->data.x_f16 = f16_roundToInt(out_val->data.x_f16, softfloat_round_min, false); return; case 32: out_val->data.x_f32 = floorf(op1->data.x_f32 / op2->data.x_f32); return; case 64: out_val->data.x_f64 = floor(op1->data.x_f64 / op2->data.x_f64); return; case 128: f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); f128M_roundToInt(&out_val->data.x_f128, softfloat_round_min, false, &out_val->data.x_f128); return; default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_rem(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { bigfloat_rem(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == ZigTypeIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: out_val->data.x_f16 = f16_rem(op1->data.x_f16, op2->data.x_f16); return; case 32: out_val->data.x_f32 = fmodf(op1->data.x_f32, op2->data.x_f32); return; case 64: out_val->data.x_f64 = fmod(op1->data.x_f64, op2->data.x_f64); return; case 128: f128M_rem(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); return; default: zig_unreachable(); } } else { zig_unreachable(); } } // c = a - b * trunc(a / b) static float16_t zig_f16_mod(float16_t a, float16_t b) { float16_t c; c = f16_div(a, b); c = f16_roundToInt(c, softfloat_round_min, true); c = f16_mul(b, c); c = f16_sub(a, c); return c; } // c = a - b * trunc(a / b) static void zig_f128M_mod(const float128_t* a, const float128_t* b, float128_t* c) { f128M_div(a, b, c); f128M_roundToInt(c, softfloat_round_min, true, c); f128M_mul(b, c, c); f128M_sub(a, c, c); } static void float_mod(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { bigfloat_mod(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == ZigTypeIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: out_val->data.x_f16 = zig_f16_mod(op1->data.x_f16, op2->data.x_f16); return; case 32: out_val->data.x_f32 = fmodf(fmodf(op1->data.x_f32, op2->data.x_f32) + op2->data.x_f32, op2->data.x_f32); return; case 64: out_val->data.x_f64 = fmod(fmod(op1->data.x_f64, op2->data.x_f64) + op2->data.x_f64, op2->data.x_f64); return; case 128: zig_f128M_mod(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); return; default: zig_unreachable(); } } else { zig_unreachable(); } } static void float_negate(ZigValue *out_val, ZigValue *op) { out_val->type = op->type; if (op->type->id == ZigTypeIdComptimeFloat) { bigfloat_negate(&out_val->data.x_bigfloat, &op->data.x_bigfloat); } else if (op->type->id == ZigTypeIdFloat) { switch (op->type->data.floating.bit_count) { case 16: { const float16_t zero = zig_double_to_f16(0); out_val->data.x_f16 = f16_sub(zero, op->data.x_f16); return; } case 32: out_val->data.x_f32 = -op->data.x_f32; return; case 64: out_val->data.x_f64 = -op->data.x_f64; return; case 128: float128_t zero_f128; ui32_to_f128M(0, &zero_f128); f128M_sub(&zero_f128, &op->data.x_f128, &out_val->data.x_f128); return; default: zig_unreachable(); } } else { zig_unreachable(); } } void float_write_ieee597(ZigValue *op, uint8_t *buf, bool is_big_endian) { if (op->type->id != ZigTypeIdFloat) zig_unreachable(); const unsigned n = op->type->data.floating.bit_count / 8; assert(n <= 16); switch (op->type->data.floating.bit_count) { case 16: memcpy(buf, &op->data.x_f16, 2); break; case 32: memcpy(buf, &op->data.x_f32, 4); break; case 64: memcpy(buf, &op->data.x_f64, 8); break; case 128: memcpy(buf, &op->data.x_f128, 16); break; default: zig_unreachable(); } if (is_big_endian) { // Byteswap in place if needed for (size_t i = 0; i < n / 2; i++) { uint8_t u = buf[i]; buf[i] = buf[n - 1 - i]; buf[n - 1 - i] = u; } } } void float_read_ieee597(ZigValue *val, uint8_t *buf, bool is_big_endian) { if (val->type->id != ZigTypeIdFloat) zig_unreachable(); const unsigned n = val->type->data.floating.bit_count / 8; assert(n <= 16); uint8_t tmp[16]; uint8_t *ptr = buf; if (is_big_endian) { memcpy(tmp, buf, n); // Byteswap if needed for (size_t i = 0; i < n / 2; i++) { uint8_t u = tmp[i]; tmp[i] = tmp[n - 1 - i]; tmp[n - 1 - i] = u; } ptr = tmp; } switch (val->type->data.floating.bit_count) { case 16: memcpy(&val->data.x_f16, ptr, 2); return; case 32: memcpy(&val->data.x_f32, ptr, 4); return; case 64: memcpy(&val->data.x_f64, ptr, 8); return; case 128: memcpy(&val->data.x_f128, ptr, 16); return; default: zig_unreachable(); } } static void value_to_bigfloat(BigFloat *out, ZigValue *val) { switch (val->type->id) { case ZigTypeIdInt: case ZigTypeIdComptimeInt: bigfloat_init_bigint(out, &val->data.x_bigint); return; case ZigTypeIdComptimeFloat: *out = val->data.x_bigfloat; return; case ZigTypeIdFloat: switch (val->type->data.floating.bit_count) { case 16: bigfloat_init_16(out, val->data.x_f16); return; case 32: bigfloat_init_32(out, val->data.x_f32); return; case 64: bigfloat_init_64(out, val->data.x_f64); return; case 80: zig_panic("TODO"); case 128: bigfloat_init_128(out, val->data.x_f128); return; default: zig_unreachable(); } default: zig_unreachable(); } } static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstGen *instruction, ZigType *other_type, bool explicit_cast) { if (type_is_invalid(other_type)) { return false; } ZigValue *const_val = ir_resolve_const(ira, instruction, LazyOkNoUndef); if (const_val == nullptr) return false; if (const_val->special == ConstValSpecialLazy) { switch (const_val->data.x_lazy->id) { case LazyValueIdAlignOf: { // This is guaranteed to fit into a u29 if (other_type->id == ZigTypeIdComptimeInt) return true; size_t align_bits = get_align_amt_type(ira->codegen)->data.integral.bit_count; if (other_type->id == ZigTypeIdInt && !other_type->data.integral.is_signed && other_type->data.integral.bit_count >= align_bits) { return true; } break; } case LazyValueIdSizeOf: { // This is guaranteed to fit into a usize if (other_type->id == ZigTypeIdComptimeInt) return true; size_t usize_bits = ira->codegen->builtin_types.entry_usize->data.integral.bit_count; if (other_type->id == ZigTypeIdInt && !other_type->data.integral.is_signed && other_type->data.integral.bit_count >= usize_bits) { return true; } break; } default: break; } } const_val = ir_resolve_const(ira, instruction, UndefBad); if (const_val == nullptr) return false; bool const_val_is_int = (const_val->type->id == ZigTypeIdInt || const_val->type->id == ZigTypeIdComptimeInt); bool const_val_is_float = (const_val->type->id == ZigTypeIdFloat || const_val->type->id == ZigTypeIdComptimeFloat); assert(const_val_is_int || const_val_is_float); if (const_val_is_int && other_type->id == ZigTypeIdComptimeFloat) { return true; } if (other_type->id == ZigTypeIdFloat) { if (const_val->type->id == ZigTypeIdComptimeInt || const_val->type->id == ZigTypeIdComptimeFloat) { return true; } if (const_val->type->id == ZigTypeIdInt) { BigFloat tmp_bf; bigfloat_init_bigint(&tmp_bf, &const_val->data.x_bigint); BigFloat orig_bf; switch (other_type->data.floating.bit_count) { case 16: { float16_t tmp = bigfloat_to_f16(&tmp_bf); bigfloat_init_16(&orig_bf, tmp); break; } case 32: { float tmp = bigfloat_to_f32(&tmp_bf); bigfloat_init_32(&orig_bf, tmp); break; } case 64: { double tmp = bigfloat_to_f64(&tmp_bf); bigfloat_init_64(&orig_bf, tmp); break; } case 80: zig_panic("TODO"); case 128: { float128_t tmp = bigfloat_to_f128(&tmp_bf); bigfloat_init_128(&orig_bf, tmp); break; } default: zig_unreachable(); } BigInt orig_bi; bigint_init_bigfloat(&orig_bi, &orig_bf); if (bigint_cmp(&orig_bi, &const_val->data.x_bigint) == CmpEQ) { return true; } Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, &const_val->data.x_bigint, 10); ir_add_error_node(ira, instruction->base.source_node, buf_sprintf("type %s cannot represent integer value %s", buf_ptr(&other_type->name), buf_ptr(val_buf))); return false; } if (other_type->data.floating.bit_count >= const_val->type->data.floating.bit_count) { return true; } switch (other_type->data.floating.bit_count) { case 16: switch (const_val->type->data.floating.bit_count) { case 32: { float16_t tmp = zig_double_to_f16(const_val->data.x_f32); float orig = zig_f16_to_double(tmp); if (const_val->data.x_f32 == orig) { return true; } break; } case 64: { float16_t tmp = zig_double_to_f16(const_val->data.x_f64); double orig = zig_f16_to_double(tmp); if (const_val->data.x_f64 == orig) { return true; } break; } case 80: zig_panic("TODO"); case 128: { float16_t tmp = f128M_to_f16(&const_val->data.x_f128); float128_t orig; f16_to_f128M(tmp, &orig); if (f128M_eq(&orig, &const_val->data.x_f128)) { return true; } break; } default: zig_unreachable(); } break; case 32: switch (const_val->type->data.floating.bit_count) { case 64: { float tmp = const_val->data.x_f64; double orig = tmp; if (const_val->data.x_f64 == orig) { return true; } break; } case 80: zig_panic("TODO"); case 128: { float32_t tmp = f128M_to_f32(&const_val->data.x_f128); float128_t orig; f32_to_f128M(tmp, &orig); if (f128M_eq(&orig, &const_val->data.x_f128)) { return true; } break; } default: zig_unreachable(); } break; case 64: switch (const_val->type->data.floating.bit_count) { case 80: zig_panic("TODO"); case 128: { float64_t tmp = f128M_to_f64(&const_val->data.x_f128); float128_t orig; f64_to_f128M(tmp, &orig); if (f128M_eq(&orig, &const_val->data.x_f128)) { return true; } break; } default: zig_unreachable(); } break; case 80: assert(const_val->type->data.floating.bit_count == 128); zig_panic("TODO"); case 128: return true; default: zig_unreachable(); } Buf *val_buf = buf_alloc(); float_append_buf(val_buf, const_val); ir_add_error_node(ira, instruction->base.source_node, buf_sprintf("cast of value %s to type '%s' loses information", buf_ptr(val_buf), buf_ptr(&other_type->name))); return false; } else if (other_type->id == ZigTypeIdInt && const_val_is_int) { if (!other_type->data.integral.is_signed && const_val->data.x_bigint.is_negative) { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, &const_val->data.x_bigint, 10); ir_add_error_node(ira, instruction->base.source_node, 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_node(ira, instruction->base.source_node, 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_node(ira, instruction->base.source_node, 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_node(ira, instruction->base.source_node, buf_sprintf("%s value %s cannot be coerced to type '%s'", num_lit_str, buf_ptr(val_buf), buf_ptr(&other_type->name))); return false; } static bool is_tagged_union(ZigType *type) { if (type->id != ZigTypeIdUnion) return false; return (type->data.unionation.decl_node->data.container_decl.auto_enum || type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr); } static void populate_error_set_table(ErrorTableEntry **errors, ZigType *set) { assert(set->id == ZigTypeIdErrorSet); for (uint32_t i = 0; i < set->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = set->data.error_set.errors[i]; assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } } static ErrorTableEntry *better_documented_error(ErrorTableEntry *preferred, ErrorTableEntry *other) { if (preferred->decl_node->type == NodeTypeErrorSetField) return preferred; if (other->decl_node->type == NodeTypeErrorSetField) return other; return preferred; } static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigType *set2, AstNode *source_node) { assert(set1->id == ZigTypeIdErrorSet); assert(set2->id == ZigTypeIdErrorSet); if (!resolve_inferred_error_set(ira->codegen, set1, source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (!resolve_inferred_error_set(ira->codegen, set2, source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (type_is_global_error_set(set1)) { return set2; } if (type_is_global_error_set(set2)) { return set1; } size_t errors_count = ira->codegen->errors_by_index.length; ErrorTableEntry **errors = heap::c_allocator.allocate(errors_count); populate_error_set_table(errors, set1); 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{"); bool need_comma = false; for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = set2->data.error_set.errors[i]; ErrorTableEntry *existing_entry = errors[error_entry->value]; if (existing_entry != nullptr) { // prefer the one with docs const char *comma = need_comma ? "," : ""; need_comma = true; ErrorTableEntry *existing_entry_with_docs = better_documented_error(existing_entry, error_entry); intersection_list.append(existing_entry_with_docs); buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&existing_entry_with_docs->name)); } } heap::c_allocator.deallocate(errors, errors_count); err_set_type->data.error_set.err_count = intersection_list.length; err_set_type->data.error_set.errors = intersection_list.items; err_set_type->size_in_bits = ira->codegen->builtin_types.entry_global_error_set->size_in_bits; err_set_type->abi_align = ira->codegen->builtin_types.entry_global_error_set->abi_align; err_set_type->abi_size = ira->codegen->builtin_types.entry_global_error_set->abi_size; buf_appendf(&err_set_type->name, "}"); return err_set_type; } static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted_type, ZigType *actual_type, AstNode *source_node, bool wanted_is_mutable) { CodeGen *g = ira->codegen; ConstCastOnly result = {}; result.id = ConstCastResultIdOk; Error err; if (wanted_type == actual_type) return result; // If pointers have the same representation in memory, they can be "const-casted". // `const` attribute can be gained // `volatile` attribute can be gained // `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer) // but only if !wanted_is_mutable // alignment can be decreased // bit offset attributes must match exactly // PtrLenSingle/PtrLenUnknown must match exactly, but PtrLenC matches either one // sentinel-terminated pointers can coerce into PtrLenUnknown ZigType *wanted_ptr_type = get_src_ptr_type(wanted_type); ZigType *actual_ptr_type = get_src_ptr_type(actual_type); bool wanted_allows_zero = ptr_allows_addr_zero(wanted_type); bool actual_allows_zero = ptr_allows_addr_zero(actual_type); bool wanted_is_c_ptr = wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC; bool actual_is_c_ptr = actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenC; bool wanted_opt_or_ptr = wanted_ptr_type != nullptr && wanted_ptr_type->id == ZigTypeIdPointer; bool actual_opt_or_ptr = actual_ptr_type != nullptr && actual_ptr_type->id == ZigTypeIdPointer; if (wanted_opt_or_ptr && actual_opt_or_ptr) { bool ok_null_term_ptrs = wanted_ptr_type->data.pointer.sentinel == nullptr || (actual_ptr_type->data.pointer.sentinel != nullptr && const_values_equal(ira->codegen, wanted_ptr_type->data.pointer.sentinel, actual_ptr_type->data.pointer.sentinel)) || actual_ptr_type->data.pointer.ptr_len == PtrLenC; if (!ok_null_term_ptrs) { result.id = ConstCastResultIdPtrSentinel; result.data.bad_ptr_sentinel = heap::c_allocator.allocate_nonzero(1); result.data.bad_ptr_sentinel->wanted_type = wanted_ptr_type; result.data.bad_ptr_sentinel->actual_type = actual_ptr_type; return result; } bool ptr_lens_equal = actual_ptr_type->data.pointer.ptr_len == wanted_ptr_type->data.pointer.ptr_len; if (!(ptr_lens_equal || wanted_is_c_ptr || actual_is_c_ptr)) { result.id = ConstCastResultIdPtrLens; return result; } bool ok_cv_qualifiers = (!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) && (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile); if (!ok_cv_qualifiers) { result.id = ConstCastResultIdCV; result.data.bad_cv = heap::c_allocator.allocate_nonzero(1); result.data.bad_cv->wanted_type = wanted_ptr_type; result.data.bad_cv->actual_type = actual_ptr_type; return result; } ConstCastOnly child = types_match_const_cast_only(ira, wanted_ptr_type->data.pointer.child_type, actual_ptr_type->data.pointer.child_type, source_node, !wanted_ptr_type->data.pointer.is_const); if (child.id == ConstCastResultIdInvalid) return child; if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdPointerChild; result.data.pointer_mismatch = heap::c_allocator.allocate_nonzero(1); result.data.pointer_mismatch->child = child; result.data.pointer_mismatch->wanted_child = wanted_ptr_type->data.pointer.child_type; result.data.pointer_mismatch->actual_child = actual_ptr_type->data.pointer.child_type; return result; } bool ok_allows_zero = (wanted_allows_zero && (actual_allows_zero || !wanted_is_mutable)) || (!wanted_allows_zero && !actual_allows_zero); if (!ok_allows_zero) { result.id = ConstCastResultIdBadAllowsZero; result.data.bad_allows_zero = heap::c_allocator.allocate_nonzero(1); result.data.bad_allows_zero->wanted_type = wanted_type; result.data.bad_allows_zero->actual_type = actual_type; return result; } if ((err = type_resolve(g, actual_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { result.id = ConstCastResultIdInvalid; return result; } if ((err = type_resolve(g, wanted_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { result.id = ConstCastResultIdInvalid; return result; } if ((err = type_resolve(g, wanted_type, ResolveStatusZeroBitsKnown))) { result.id = ConstCastResultIdInvalid; return result; } if ((err = type_resolve(g, actual_type, ResolveStatusZeroBitsKnown))) { result.id = ConstCastResultIdInvalid; return result; } if (type_has_bits(g, wanted_type) == type_has_bits(g, actual_type) && actual_ptr_type->data.pointer.bit_offset_in_host == wanted_ptr_type->data.pointer.bit_offset_in_host && actual_ptr_type->data.pointer.host_int_bytes == wanted_ptr_type->data.pointer.host_int_bytes && get_ptr_align(ira->codegen, actual_ptr_type) >= get_ptr_align(ira->codegen, wanted_ptr_type)) { return result; } } // arrays if (wanted_type->id == ZigTypeIdArray && actual_type->id == ZigTypeIdArray && wanted_type->data.array.len == actual_type->data.array.len) { ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.array.child_type, actual_type->data.array.child_type, source_node, wanted_is_mutable); if (child.id == ConstCastResultIdInvalid) return child; if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdArrayChild; result.data.array_mismatch = heap::c_allocator.allocate_nonzero(1); result.data.array_mismatch->child = child; result.data.array_mismatch->wanted_child = wanted_type->data.array.child_type; result.data.array_mismatch->actual_child = actual_type->data.array.child_type; return result; } bool ok_null_terminated = (wanted_type->data.array.sentinel == nullptr) || (actual_type->data.array.sentinel != nullptr && const_values_equal(ira->codegen, wanted_type->data.array.sentinel, actual_type->data.array.sentinel)); if (!ok_null_terminated) { result.id = ConstCastResultIdSentinelArrays; result.data.sentinel_arrays = heap::c_allocator.allocate_nonzero(1); result.data.sentinel_arrays->child = child; result.data.sentinel_arrays->wanted_type = wanted_type; result.data.sentinel_arrays->actual_type = actual_type; return result; } return result; } // slice const if (is_slice(wanted_type) && is_slice(actual_type)) { ZigType *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index]->type_entry; ZigType *wanted_ptr_type = wanted_type->data.structure.fields[slice_ptr_index]->type_entry; if ((err = type_resolve(g, actual_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { result.id = ConstCastResultIdInvalid; return result; } if ((err = type_resolve(g, wanted_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { result.id = ConstCastResultIdInvalid; return result; } bool ok_sentinels = wanted_ptr_type->data.pointer.sentinel == nullptr || (actual_ptr_type->data.pointer.sentinel != nullptr && const_values_equal(ira->codegen, wanted_ptr_type->data.pointer.sentinel, actual_ptr_type->data.pointer.sentinel)); if (!ok_sentinels) { result.id = ConstCastResultIdPtrSentinel; result.data.bad_ptr_sentinel = heap::c_allocator.allocate_nonzero(1); result.data.bad_ptr_sentinel->wanted_type = wanted_ptr_type; result.data.bad_ptr_sentinel->actual_type = actual_ptr_type; return result; } if ((!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) && (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) && actual_ptr_type->data.pointer.bit_offset_in_host == wanted_ptr_type->data.pointer.bit_offset_in_host && actual_ptr_type->data.pointer.host_int_bytes == wanted_ptr_type->data.pointer.host_int_bytes && get_ptr_align(g, actual_ptr_type) >= get_ptr_align(g, wanted_ptr_type)) { ConstCastOnly child = types_match_const_cast_only(ira, wanted_ptr_type->data.pointer.child_type, actual_ptr_type->data.pointer.child_type, source_node, !wanted_ptr_type->data.pointer.is_const); if (child.id == ConstCastResultIdInvalid) return child; if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdSliceChild; result.data.slice_mismatch = heap::c_allocator.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 = heap::c_allocator.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 = heap::c_allocator.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 = heap::c_allocator.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 && container_set->data.error_set.incomplete) { return result; } // if the container set is the global one, it will always work. if (type_is_global_error_set(container_set)) { return result; } if (!resolve_inferred_error_set(ira->codegen, contained_set, source_node)) { result.id = ConstCastResultIdUnresolvedInferredErrSet; return result; } if (type_is_global_error_set(contained_set)) { result.id = ConstCastResultIdErrSetGlobal; return result; } size_t errors_count = g->errors_by_index.length; ErrorTableEntry **errors = heap::c_allocator.allocate(errors_count); 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 = heap::c_allocator.create(); } result.data.error_set_mismatch->missing_errors.append(contained_error_entry); } } heap::c_allocator.deallocate(errors, errors_count); return result; } // fn if (wanted_type->id == ZigTypeIdFn && actual_type->id == ZigTypeIdFn) { if (wanted_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) { result.id = ConstCastResultIdFnAlign; return result; } if (wanted_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) { result.id = ConstCastResultIdFnVarArgs; return result; } if (wanted_type->data.fn.is_generic != actual_type->data.fn.is_generic) { result.id = ConstCastResultIdFnIsGeneric; return result; } if (!wanted_type->data.fn.is_generic && actual_type->data.fn.fn_type_id.return_type->id != ZigTypeIdUnreachable) { ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.fn.fn_type_id.return_type, actual_type->data.fn.fn_type_id.return_type, source_node, false); if (child.id == ConstCastResultIdInvalid) return child; if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdFnReturnType; result.data.return_type = heap::c_allocator.allocate_nonzero(1); *result.data.return_type = child; return result; } } if (wanted_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) { result.id = ConstCastResultIdFnArgCount; return result; } if (wanted_type->data.fn.fn_type_id.next_param_index != actual_type->data.fn.fn_type_id.next_param_index) { result.id = ConstCastResultIdFnGenericArgCount; return result; } assert(wanted_type->data.fn.is_generic || wanted_type->data.fn.fn_type_id.next_param_index == wanted_type->data.fn.fn_type_id.param_count); for (size_t i = 0; i < wanted_type->data.fn.fn_type_id.param_count; i += 1) { // note it's reversed for parameters FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i]; FnTypeParamInfo *expected_param_info = &wanted_type->data.fn.fn_type_id.param_info[i]; ConstCastOnly arg_child = types_match_const_cast_only(ira, actual_param_info->type, expected_param_info->type, source_node, false); if (arg_child.id == ConstCastResultIdInvalid) return arg_child; if (arg_child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdFnArg; result.data.fn_arg.arg_index = i; result.data.fn_arg.actual_param_type = actual_param_info->type; result.data.fn_arg.expected_param_type = expected_param_info->type; result.data.fn_arg.child = heap::c_allocator.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; } } if (wanted_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) { // ConstCastResultIdFnCC is guaranteed to be the last one reported, meaning everything else is ok. result.id = ConstCastResultIdFnCC; return result; } return result; } if (wanted_type->id == ZigTypeIdInt && actual_type->id == ZigTypeIdInt) { if (wanted_type->data.integral.is_signed != actual_type->data.integral.is_signed || wanted_type->data.integral.bit_count != actual_type->data.integral.bit_count) { result.id = ConstCastResultIdIntShorten; result.data.int_shorten = heap::c_allocator.allocate_nonzero(1); result.data.int_shorten->wanted_type = wanted_type; result.data.int_shorten->actual_type = actual_type; return result; } return result; } result.id = ConstCastResultIdType; result.data.type_mismatch = heap::c_allocator.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 = heap::c_allocator.reallocate(*errors, old_errors_count, *errors_count); } static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigType *expected_type, IrInstGen **instructions, size_t instruction_count) { Error err; assert(instruction_count >= 1); IrInstGen *prev_inst; size_t i = 0; for (;;) { prev_inst = instructions[i]; if (type_is_invalid(prev_inst->value->type)) { return ira->codegen->builtin_types.entry_invalid; } if (prev_inst->value->type->id == ZigTypeIdUnreachable) { i += 1; if (i == instruction_count) { return prev_inst->value->type; } continue; } break; } ErrorTableEntry **errors = nullptr; size_t errors_count = 0; ZigType *err_set_type = nullptr; if (prev_inst->value->type->id == ZigTypeIdErrorSet) { if (!resolve_inferred_error_set(ira->codegen, prev_inst->value->type, prev_inst->base.source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (type_is_global_error_set(prev_inst->value->type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; } else { err_set_type = prev_inst->value->type; update_errors_helper(ira->codegen, &errors, &errors_count); for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } } } bool any_are_null = (prev_inst->value->type->id == ZigTypeIdNull); bool convert_to_const_slice = false; bool make_the_slice_const = false; bool make_the_pointer_const = false; for (; i < instruction_count; i += 1) { IrInstGen *cur_inst = instructions[i]; ZigType *cur_type = cur_inst->value->type; ZigType *prev_type = prev_inst->value->type; if (type_is_invalid(cur_type)) { return cur_type; } if (prev_type == cur_type) { continue; } if (prev_type->id == ZigTypeIdUnreachable) { prev_inst = cur_inst; continue; } if (cur_type->id == ZigTypeIdUnreachable) { continue; } if (prev_type->id == ZigTypeIdErrorSet) { ir_assert_gen(err_set_type != nullptr, prev_inst); if (cur_type->id == ZigTypeIdErrorSet) { if (type_is_global_error_set(err_set_type)) { continue; } bool allow_infer = cur_type->data.error_set.infer_fn != nullptr && cur_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; if (!allow_infer && !resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->base.source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (!allow_infer && type_is_global_error_set(cur_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; prev_inst = cur_inst; continue; } // number of declared errors might have increased now update_errors_helper(ira->codegen, &errors, &errors_count); // if err_set_type is a superset of cur_type, keep err_set_type. // if cur_type is a superset of err_set_type, switch err_set_type to cur_type bool prev_is_superset = true; for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) { ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i]; ErrorTableEntry *error_entry = errors[contained_error_entry->value]; if (error_entry == nullptr) { prev_is_superset = false; break; } } if (prev_is_superset) { continue; } // unset everything in errors for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; errors[error_entry->value] = nullptr; } for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) { assert(errors[i] == nullptr); } for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = cur_type->data.error_set.errors[i]; assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } bool cur_is_superset = true; for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *contained_error_entry = err_set_type->data.error_set.errors[i]; ErrorTableEntry *error_entry = errors[contained_error_entry->value]; if (error_entry == nullptr) { cur_is_superset = false; break; } } if (cur_is_superset) { err_set_type = cur_type; prev_inst = cur_inst; assert(errors != nullptr); continue; } // neither of them are supersets. so we invent a new error set type that is a union of both of them err_set_type = get_error_set_union(ira->codegen, errors, cur_type, err_set_type, nullptr); assert(errors != nullptr); continue; } else if (cur_type->id == ZigTypeIdErrorUnion) { if (type_is_global_error_set(err_set_type)) { prev_inst = cur_inst; continue; } ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type; bool allow_infer = cur_err_set_type->data.error_set.infer_fn != nullptr && cur_err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; if (!allow_infer && !resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->base.source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (!allow_infer && type_is_global_error_set(cur_err_set_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; prev_inst = cur_inst; continue; } update_errors_helper(ira->codegen, &errors, &errors_count); // test if err_set_type is a subset of cur_type's error set // unset everything in errors for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; errors[error_entry->value] = nullptr; } for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) { assert(errors[i] == nullptr); } for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i]; assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } bool cur_is_superset = true; for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *contained_error_entry = err_set_type->data.error_set.errors[i]; ErrorTableEntry *error_entry = errors[contained_error_entry->value]; if (error_entry == nullptr) { cur_is_superset = false; break; } } if (cur_is_superset) { err_set_type = cur_err_set_type; prev_inst = cur_inst; assert(errors != nullptr); continue; } // not a subset. invent new error set type, union of both of them err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, err_set_type, nullptr); prev_inst = cur_inst; assert(errors != nullptr); continue; } else { prev_inst = cur_inst; continue; } } if (cur_type->id == ZigTypeIdErrorSet) { bool allow_infer = cur_type->data.error_set.infer_fn != nullptr && cur_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; if (!allow_infer && !resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->base.source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (!allow_infer && type_is_global_error_set(cur_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; continue; } if (err_set_type != nullptr && type_is_global_error_set(err_set_type)) { continue; } update_errors_helper(ira->codegen, &errors, &errors_count); if (err_set_type == nullptr) { bool allow_infer = false; if (prev_type->id == ZigTypeIdErrorUnion) { err_set_type = prev_type->data.error_union.err_set_type; allow_infer = err_set_type->data.error_set.infer_fn != nullptr && err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; } else { err_set_type = cur_type; } if (!allow_infer && !resolve_inferred_error_set(ira->codegen, err_set_type, cur_inst->base.source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (!allow_infer && type_is_global_error_set(err_set_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; continue; } update_errors_helper(ira->codegen, &errors, &errors_count); for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } if (err_set_type == cur_type) { continue; } } // check if the cur type error set is a subset bool prev_is_superset = true; for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) { ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i]; ErrorTableEntry *error_entry = errors[contained_error_entry->value]; if (error_entry == nullptr) { prev_is_superset = false; break; } } if (prev_is_superset) { continue; } // not a subset. invent new error set type, union of both of them err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_type, nullptr); assert(errors != nullptr); continue; } if (prev_type->id == ZigTypeIdErrorUnion && cur_type->id == ZigTypeIdErrorUnion) { ZigType *prev_payload_type = prev_type->data.error_union.payload_type; ZigType *cur_payload_type = cur_type->data.error_union.payload_type; bool const_cast_prev = types_match_const_cast_only(ira, prev_payload_type, cur_payload_type, source_node, false).id == ConstCastResultIdOk; bool const_cast_cur = types_match_const_cast_only(ira, cur_payload_type, prev_payload_type, source_node, false).id == ConstCastResultIdOk; if (const_cast_prev || const_cast_cur) { if (const_cast_cur) { prev_inst = cur_inst; } ZigType *prev_err_set_type = (err_set_type == nullptr) ? prev_type->data.error_union.err_set_type : err_set_type; ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type; if (prev_err_set_type == cur_err_set_type) continue; bool allow_infer_prev = prev_err_set_type->data.error_set.infer_fn != nullptr && prev_err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; bool allow_infer_cur = cur_err_set_type->data.error_set.infer_fn != nullptr && cur_err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; if (!allow_infer_prev && !resolve_inferred_error_set(ira->codegen, prev_err_set_type, cur_inst->base.source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (!allow_infer_cur && !resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->base.source_node)) { return ira->codegen->builtin_types.entry_invalid; } if ((!allow_infer_prev && type_is_global_error_set(prev_err_set_type)) || (!allow_infer_cur && type_is_global_error_set(cur_err_set_type))) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; continue; } update_errors_helper(ira->codegen, &errors, &errors_count); if (err_set_type == nullptr) { err_set_type = prev_err_set_type; for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = prev_err_set_type->data.error_set.errors[i]; assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } } bool prev_is_superset = true; for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *contained_error_entry = cur_err_set_type->data.error_set.errors[i]; ErrorTableEntry *error_entry = errors[contained_error_entry->value]; if (error_entry == nullptr) { prev_is_superset = false; break; } } if (prev_is_superset) { continue; } // unset all the errors for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; errors[error_entry->value] = nullptr; } for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) { assert(errors[i] == nullptr); } for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i]; assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } bool cur_is_superset = true; for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *contained_error_entry = prev_err_set_type->data.error_set.errors[i]; ErrorTableEntry *error_entry = errors[contained_error_entry->value]; if (error_entry == nullptr) { cur_is_superset = false; break; } } if (cur_is_superset) { err_set_type = cur_err_set_type; continue; } err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, prev_err_set_type, nullptr); continue; } } if (prev_type->id == ZigTypeIdNull) { prev_inst = cur_inst; any_are_null = true; continue; } if (cur_type->id == ZigTypeIdNull) { any_are_null = true; continue; } if (prev_type->id == ZigTypeIdEnum && cur_type->id == ZigTypeIdEnumLiteral) { TypeEnumField *field = find_enum_type_field(prev_type, cur_inst->value->data.x_enum_literal); if (field != nullptr) { continue; } } if (is_tagged_union(prev_type) && cur_type->id == ZigTypeIdEnumLiteral) { TypeUnionField *field = find_union_type_field(prev_type, cur_inst->value->data.x_enum_literal); if (field != nullptr) { continue; } } if (cur_type->id == ZigTypeIdEnum && prev_type->id == ZigTypeIdEnumLiteral) { TypeEnumField *field = find_enum_type_field(cur_type, prev_inst->value->data.x_enum_literal); if (field != nullptr) { prev_inst = cur_inst; continue; } } if (is_tagged_union(cur_type) && prev_type->id == ZigTypeIdEnumLiteral) { TypeUnionField *field = find_union_type_field(cur_type, prev_inst->value->data.x_enum_literal); if (field != nullptr) { prev_inst = cur_inst; continue; } } if (prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenC && (cur_type->id == ZigTypeIdComptimeInt || cur_type->id == ZigTypeIdInt)) { continue; } if (cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenC && (prev_type->id == ZigTypeIdComptimeInt || prev_type->id == ZigTypeIdInt)) { prev_inst = cur_inst; continue; } if (prev_type->id == ZigTypeIdPointer && cur_type->id == ZigTypeIdPointer) { if (prev_type->data.pointer.ptr_len == PtrLenC && types_match_const_cast_only(ira, prev_type->data.pointer.child_type, cur_type->data.pointer.child_type, source_node, !prev_type->data.pointer.is_const).id == ConstCastResultIdOk) { continue; } if (cur_type->data.pointer.ptr_len == PtrLenC && types_match_const_cast_only(ira, cur_type->data.pointer.child_type, prev_type->data.pointer.child_type, source_node, !cur_type->data.pointer.is_const).id == ConstCastResultIdOk) { prev_inst = cur_inst; continue; } } if (types_match_const_cast_only(ira, prev_type, cur_type, source_node, false).id == ConstCastResultIdOk) { continue; } if (types_match_const_cast_only(ira, cur_type, prev_type, source_node, false).id == ConstCastResultIdOk) { prev_inst = cur_inst; continue; } if (prev_type->id == ZigTypeIdInt && cur_type->id == ZigTypeIdInt && prev_type->data.integral.is_signed == cur_type->data.integral.is_signed) { if (cur_type->data.integral.bit_count > prev_type->data.integral.bit_count) { prev_inst = cur_inst; } continue; } if (prev_type->id == ZigTypeIdFloat && cur_type->id == ZigTypeIdFloat) { if (cur_type->data.floating.bit_count > prev_type->data.floating.bit_count) { prev_inst = cur_inst; } continue; } if (prev_type->id == ZigTypeIdErrorUnion && types_match_const_cast_only(ira, prev_type->data.error_union.payload_type, cur_type, source_node, false).id == ConstCastResultIdOk) { continue; } if (cur_type->id == ZigTypeIdErrorUnion && types_match_const_cast_only(ira, cur_type->data.error_union.payload_type, prev_type, source_node, false).id == ConstCastResultIdOk) { if (err_set_type != nullptr) { ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type; bool allow_infer = cur_err_set_type->data.error_set.infer_fn != nullptr && cur_err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; if (!allow_infer && !resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->base.source_node)) { return ira->codegen->builtin_types.entry_invalid; } if ((!allow_infer && type_is_global_error_set(cur_err_set_type)) || type_is_global_error_set(err_set_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; prev_inst = cur_inst; continue; } update_errors_helper(ira->codegen, &errors, &errors_count); err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_err_set_type, nullptr); } prev_inst = cur_inst; continue; } if (prev_type->id == ZigTypeIdOptional && types_match_const_cast_only(ira, prev_type->data.maybe.child_type, cur_type, source_node, false).id == ConstCastResultIdOk) { continue; } if (cur_type->id == ZigTypeIdOptional && types_match_const_cast_only(ira, cur_type->data.maybe.child_type, prev_type, source_node, false).id == ConstCastResultIdOk) { prev_inst = cur_inst; continue; } if (prev_type->id == ZigTypeIdOptional && types_match_const_cast_only(ira, cur_type, prev_type->data.maybe.child_type, source_node, false).id == ConstCastResultIdOk) { prev_inst = cur_inst; any_are_null = true; continue; } if (cur_type->id == ZigTypeIdOptional && types_match_const_cast_only(ira, prev_type, cur_type->data.maybe.child_type, source_node, false).id == ConstCastResultIdOk) { any_are_null = true; continue; } if (cur_type->id == ZigTypeIdUndefined) { continue; } if (prev_type->id == ZigTypeIdUndefined) { prev_inst = cur_inst; continue; } if (prev_type->id == ZigTypeIdComptimeInt || prev_type->id == ZigTypeIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, prev_inst, cur_type, false)) { prev_inst = cur_inst; continue; } else { return ira->codegen->builtin_types.entry_invalid; } } if (cur_type->id == ZigTypeIdComptimeInt || cur_type->id == ZigTypeIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, cur_inst, prev_type, false)) { continue; } else { return ira->codegen->builtin_types.entry_invalid; } } // *[N]T to [*]T if (prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenSingle && prev_type->data.pointer.child_type->id == ZigTypeIdArray && ((cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenUnknown))) { prev_inst = cur_inst; if (prev_type->data.pointer.is_const && !cur_type->data.pointer.is_const) { // const array pointer and non-const unknown pointer make_the_pointer_const = true; } continue; } // *[N]T to [*]T if (cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenSingle && cur_type->data.pointer.child_type->id == ZigTypeIdArray && ((prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenUnknown))) { if (cur_type->data.pointer.is_const && !prev_type->data.pointer.is_const) { // const array pointer and non-const unknown pointer make_the_pointer_const = true; } continue; } // *[N]T to []T // *[N]T to E![]T if (cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenSingle && cur_type->data.pointer.child_type->id == ZigTypeIdArray && ((prev_type->id == ZigTypeIdErrorUnion && is_slice(prev_type->data.error_union.payload_type)) || is_slice(prev_type))) { ZigType *array_type = cur_type->data.pointer.child_type; ZigType *slice_type = (prev_type->id == ZigTypeIdErrorUnion) ? prev_type->data.error_union.payload_type : prev_type; ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, array_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) { bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 || !cur_type->data.pointer.is_const); if (!const_ok) make_the_slice_const = true; convert_to_const_slice = false; continue; } } // *[N]T to []T // *[N]T to E![]T if (prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.child_type->id == ZigTypeIdArray && prev_type->data.pointer.ptr_len == PtrLenSingle && ((cur_type->id == ZigTypeIdErrorUnion && is_slice(cur_type->data.error_union.payload_type)) || (cur_type->id == ZigTypeIdOptional && is_slice(cur_type->data.maybe.child_type)) || is_slice(cur_type))) { ZigType *array_type = prev_type->data.pointer.child_type; ZigType *slice_type; switch (cur_type->id) { case ZigTypeIdErrorUnion: slice_type = cur_type->data.error_union.payload_type; break; case ZigTypeIdOptional: slice_type = cur_type->data.maybe.child_type; break; default: slice_type = cur_type; break; } ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, array_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) { bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 || !prev_type->data.pointer.is_const); if (!const_ok) make_the_slice_const = true; prev_inst = cur_inst; convert_to_const_slice = false; continue; } } // *[N]T and *[M]T if (cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenSingle && cur_type->data.pointer.child_type->id == ZigTypeIdArray && prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenSingle && prev_type->data.pointer.child_type->id == ZigTypeIdArray && ( prev_type->data.pointer.child_type->data.array.sentinel == nullptr || (cur_type->data.pointer.child_type->data.array.sentinel != nullptr && const_values_equal(ira->codegen, prev_type->data.pointer.child_type->data.array.sentinel, cur_type->data.pointer.child_type->data.array.sentinel)) ) && types_match_const_cast_only(ira, cur_type->data.pointer.child_type->data.array.child_type, prev_type->data.pointer.child_type->data.array.child_type, source_node, !cur_type->data.pointer.is_const).id == ConstCastResultIdOk) { bool const_ok = (cur_type->data.pointer.is_const || !prev_type->data.pointer.is_const || prev_type->data.pointer.child_type->data.array.len == 0); if (!const_ok) make_the_slice_const = true; prev_inst = cur_inst; convert_to_const_slice = true; continue; } if (prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenSingle && prev_type->data.pointer.child_type->id == ZigTypeIdArray && cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenSingle && cur_type->data.pointer.child_type->id == ZigTypeIdArray && ( cur_type->data.pointer.child_type->data.array.sentinel == nullptr || (prev_type->data.pointer.child_type->data.array.sentinel != nullptr && const_values_equal(ira->codegen, cur_type->data.pointer.child_type->data.array.sentinel, prev_type->data.pointer.child_type->data.array.sentinel)) ) && types_match_const_cast_only(ira, prev_type->data.pointer.child_type->data.array.child_type, cur_type->data.pointer.child_type->data.array.child_type, source_node, !prev_type->data.pointer.is_const).id == ConstCastResultIdOk) { bool const_ok = (prev_type->data.pointer.is_const || !cur_type->data.pointer.is_const || cur_type->data.pointer.child_type->data.array.len == 0); if (!const_ok) make_the_slice_const = true; convert_to_const_slice = true; continue; } if (prev_type->id == ZigTypeIdEnum && cur_type->id == ZigTypeIdUnion && (cur_type->data.unionation.decl_node->data.container_decl.auto_enum || cur_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) { if ((err = type_resolve(ira->codegen, cur_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; if (cur_type->data.unionation.tag_type == prev_type) { continue; } } if (cur_type->id == ZigTypeIdEnum && prev_type->id == ZigTypeIdUnion && (prev_type->data.unionation.decl_node->data.container_decl.auto_enum || prev_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) { if ((err = type_resolve(ira->codegen, prev_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; if (prev_type->data.unionation.tag_type == cur_type) { prev_inst = cur_inst; continue; } } ErrorMsg *msg = ir_add_error_node(ira, source_node, buf_sprintf("incompatible types: '%s' and '%s'", buf_ptr(&prev_type->name), buf_ptr(&cur_type->name))); add_error_note(ira->codegen, msg, prev_inst->base.source_node, buf_sprintf("type '%s' here", buf_ptr(&prev_type->name))); add_error_note(ira->codegen, msg, cur_inst->base.source_node, buf_sprintf("type '%s' here", buf_ptr(&cur_type->name))); return ira->codegen->builtin_types.entry_invalid; } heap::c_allocator.deallocate(errors, errors_count); if (convert_to_const_slice) { if (prev_inst->value->type->id == ZigTypeIdPointer) { ZigType *array_type = prev_inst->value->type->data.pointer.child_type; src_assert(array_type->id == ZigTypeIdArray, source_node); ZigType *ptr_type = get_pointer_to_type_extra2( ira->codegen, array_type->data.array.child_type, prev_inst->value->type->data.pointer.is_const || make_the_slice_const, false, PtrLenUnknown, 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, array_type->data.array.sentinel); ZigType *slice_type = get_slice_type(ira->codegen, ptr_type); if (err_set_type != nullptr) { return get_error_union_type(ira->codegen, err_set_type, slice_type); } else { return slice_type; } } else { zig_unreachable(); } } else if (err_set_type != nullptr) { if (prev_inst->value->type->id == ZigTypeIdErrorSet) { return err_set_type; } else if (prev_inst->value->type->id == ZigTypeIdErrorUnion) { ZigType *payload_type = prev_inst->value->type->data.error_union.payload_type; if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown))) return ira->codegen->builtin_types.entry_invalid; return get_error_union_type(ira->codegen, err_set_type, payload_type); } else if (expected_type != nullptr && expected_type->id == ZigTypeIdErrorUnion) { ZigType *payload_type = expected_type->data.error_union.payload_type; if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown))) return ira->codegen->builtin_types.entry_invalid; return get_error_union_type(ira->codegen, err_set_type, payload_type); } else { if (prev_inst->value->type->id == ZigTypeIdComptimeInt || prev_inst->value->type->id == ZigTypeIdComptimeFloat) { ir_add_error_node(ira, source_node, buf_sprintf("unable to make error union out of number literal")); return ira->codegen->builtin_types.entry_invalid; } else if (prev_inst->value->type->id == ZigTypeIdNull) { ir_add_error_node(ira, source_node, buf_sprintf("unable to make error union out of null literal")); return ira->codegen->builtin_types.entry_invalid; } else { if ((err = type_resolve(ira->codegen, prev_inst->value->type, ResolveStatusSizeKnown))) return ira->codegen->builtin_types.entry_invalid; return get_error_union_type(ira->codegen, err_set_type, prev_inst->value->type); } } } else if (any_are_null && prev_inst->value->type->id != ZigTypeIdNull) { if (prev_inst->value->type->id == ZigTypeIdOptional) { return prev_inst->value->type; } else { if ((err = type_resolve(ira->codegen, prev_inst->value->type, ResolveStatusSizeKnown))) return ira->codegen->builtin_types.entry_invalid; return get_optional_type(ira->codegen, prev_inst->value->type); } } else if (make_the_slice_const) { ZigType *slice_type; if (prev_inst->value->type->id == ZigTypeIdErrorUnion) { slice_type = prev_inst->value->type->data.error_union.payload_type; } else if (is_slice(prev_inst->value->type)) { slice_type = prev_inst->value->type; } else { zig_unreachable(); } ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; ZigType *adjusted_ptr_type = adjust_ptr_const(ira->codegen, slice_ptr_type, make_the_slice_const); ZigType *adjusted_slice_type = get_slice_type(ira->codegen, adjusted_ptr_type); if (prev_inst->value->type->id == ZigTypeIdErrorUnion) { return get_error_union_type(ira->codegen, prev_inst->value->type->data.error_union.err_set_type, adjusted_slice_type); } else if (is_slice(prev_inst->value->type)) { return adjusted_slice_type; } else { zig_unreachable(); } } else if (make_the_pointer_const) { return adjust_ptr_const(ira->codegen, prev_inst->value->type, make_the_pointer_const); } else { return prev_inst->value->type; } } static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInst *source_instr, CastOp cast_op, ZigValue *other_val, ZigType *other_type, ZigValue *const_val, ZigType *new_type) { const_val->special = other_val->special; assert(other_val != const_val); switch (cast_op) { case CastOpNoCast: zig_unreachable(); case CastOpErrSet: case CastOpBitCast: zig_panic("TODO"); case CastOpNoop: { copy_const_val(ira->codegen, const_val, other_val); const_val->type = new_type; break; } case CastOpNumLitToConcrete: if (other_val->type->id == ZigTypeIdComptimeFloat) { assert(new_type->id == ZigTypeIdFloat); switch (new_type->data.floating.bit_count) { case 16: const_val->data.x_f16 = bigfloat_to_f16(&other_val->data.x_bigfloat); break; case 32: const_val->data.x_f32 = bigfloat_to_f32(&other_val->data.x_bigfloat); break; case 64: const_val->data.x_f64 = bigfloat_to_f64(&other_val->data.x_bigfloat); break; case 80: zig_panic("TODO"); case 128: const_val->data.x_f128 = bigfloat_to_f128(&other_val->data.x_bigfloat); break; default: zig_unreachable(); } } else if (other_val->type->id == ZigTypeIdComptimeInt) { bigint_init_bigint(&const_val->data.x_bigint, &other_val->data.x_bigint); } else { zig_unreachable(); } const_val->type = new_type; break; case CastOpIntToFloat: if (new_type->id == ZigTypeIdFloat) { BigFloat bigfloat; bigfloat_init_bigint(&bigfloat, &other_val->data.x_bigint); switch (new_type->data.floating.bit_count) { case 16: const_val->data.x_f16 = bigfloat_to_f16(&bigfloat); break; case 32: const_val->data.x_f32 = bigfloat_to_f32(&bigfloat); break; case 64: const_val->data.x_f64 = bigfloat_to_f64(&bigfloat); break; case 80: zig_panic("TODO"); case 128: const_val->data.x_f128 = bigfloat_to_f128(&bigfloat); break; default: zig_unreachable(); } } else if (new_type->id == ZigTypeIdComptimeFloat) { bigfloat_init_bigint(&const_val->data.x_bigfloat, &other_val->data.x_bigint); } else { 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 IrInstGen *ir_const(IrAnalyze *ira, IrInst *inst, ZigType *ty) { IrInstGenConst *const_instruction = ir_create_inst_gen(&ira->new_irb, inst->scope, inst->source_node); IrInstGen *new_instruction = &const_instruction->base; new_instruction->value->type = ty; new_instruction->value->special = ConstValSpecialStatic; ira->new_irb.constants.append(&heap::c_allocator, const_instruction); return new_instruction; } static IrInstGen *ir_const_noval(IrAnalyze *ira, IrInst *old_instruction) { IrInstGenConst *const_instruction = ir_create_inst_noval(&ira->new_irb, old_instruction->scope, old_instruction->source_node); ira->new_irb.constants.append(&heap::c_allocator, const_instruction); return &const_instruction->base; } // This function initializes the new IrInstGen with the provided ZigValue, // rather than creating a new one. static IrInstGen *ir_const_move(IrAnalyze *ira, IrInst *old_instruction, ZigValue *val) { IrInstGen *result = ir_const_noval(ira, old_instruction); result->value = val; return result; } static IrInstGen *ir_resolve_cast(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value, ZigType *wanted_type, CastOp cast_op) { if (instr_is_comptime(value) || !type_has_bits(ira->codegen, wanted_type)) { IrInstGen *result = ir_const(ira, source_instr, wanted_type); if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, value->value, value->value->type, result->value, wanted_type)) { return ira->codegen->invalid_inst_gen; } return result; } else { return ir_build_cast(ira, source_instr, wanted_type, value, cast_op); } } static IrInstGen *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, ZigType *wanted_type) { ir_assert(value->value->type->id == ZigTypeIdPointer, source_instr); Error err; if ((err = type_resolve(ira->codegen, value->value->type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { return ira->codegen->invalid_inst_gen; } wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value->type)); if (instr_is_comptime(value)) { ZigValue *val = ir_resolve_const(ira, value, UndefOk); if (val == nullptr) return ira->codegen->invalid_inst_gen; if (val->special == ConstValSpecialUndef) return ir_const_undef(ira, source_instr, wanted_type); ZigValue *pointee = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); if (pointee == nullptr) return ira->codegen->invalid_inst_gen; if (pointee->special != ConstValSpecialRuntime) { IrInstGen *result = ir_const(ira, source_instr, wanted_type); result->value->data.x_ptr.special = ConstPtrSpecialBaseArray; result->value->data.x_ptr.mut = val->data.x_ptr.mut; result->value->data.x_ptr.data.base_array.array_val = pointee; result->value->data.x_ptr.data.base_array.elem_index = 0; return result; } } return ir_build_cast(ira, source_instr, wanted_type, value, CastOpBitCast); } static IrInstGen *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInst* source_instr, IrInstGen *array_ptr, ZigType *wanted_type, ResultLoc *result_loc) { Error err; assert(array_ptr->value->type->id == ZigTypeIdPointer); assert(array_ptr->value->type->data.pointer.child_type->id == ZigTypeIdArray); ZigType *array_type = array_ptr->value->type->data.pointer.child_type; size_t array_len = array_type->data.array.len; // A zero-sized array can be casted regardless of the destination alignment, or // whether the pointer is undefined, and the result is always comptime known. // TODO However, this is exposing a result location bug that I failed to solve on the first try. // If you want to try to fix the bug, uncomment this block and get the tests passing. //if (array_len == 0 && array_type->data.array.sentinel == nullptr) { // ZigValue *undef_array = ira->codegen->pass1_arena->create(); // undef_array->special = ConstValSpecialUndef; // undef_array->type = array_type; // IrInstGen *result = ir_const(ira, source_instr, wanted_type); // init_const_slice(ira->codegen, result->value, undef_array, 0, 0, false); // result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = ConstPtrMutComptimeConst; // result->value->type = wanted_type; // return result; //} if ((err = type_resolve(ira->codegen, array_ptr->value->type, ResolveStatusAlignmentKnown))) { return ira->codegen->invalid_inst_gen; } if (array_len != 0) { wanted_type = adjust_slice_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, array_ptr->value->type)); } if (instr_is_comptime(array_ptr)) { UndefAllowed undef_allowed = (array_len == 0) ? UndefOk : UndefBad; ZigValue *array_ptr_val = ir_resolve_const(ira, array_ptr, undef_allowed); if (array_ptr_val == nullptr) return ira->codegen->invalid_inst_gen; ir_assert(is_slice(wanted_type), source_instr); if (array_ptr_val->special == ConstValSpecialUndef) { ZigValue *undef_array = ira->codegen->pass1_arena->create(); undef_array->special = ConstValSpecialUndef; undef_array->type = array_type; IrInstGen *result = ir_const(ira, source_instr, wanted_type); init_const_slice(ira->codegen, result->value, undef_array, 0, 0, false); result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = ConstPtrMutComptimeConst; result->value->type = wanted_type; return result; } bool wanted_const = wanted_type->data.structure.fields[slice_ptr_index]->type_entry->data.pointer.is_const; // Optimization to avoid creating unnecessary ZigValue in const_ptr_pointee if (array_ptr_val->data.x_ptr.special == ConstPtrSpecialSubArray) { ZigValue *array_val = array_ptr_val->data.x_ptr.data.base_array.array_val; if (array_val->special != ConstValSpecialRuntime) { IrInstGen *result = ir_const(ira, source_instr, wanted_type); init_const_slice(ira->codegen, result->value, array_val, array_ptr_val->data.x_ptr.data.base_array.elem_index, array_type->data.array.len, wanted_const); result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut; result->value->type = wanted_type; return result; } } else if (array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { ZigValue *pointee = const_ptr_pointee(ira, ira->codegen, array_ptr_val, source_instr->source_node); if (pointee == nullptr) return ira->codegen->invalid_inst_gen; if (pointee->special != ConstValSpecialRuntime) { assert(array_ptr_val->type->id == ZigTypeIdPointer); IrInstGen *result = ir_const(ira, source_instr, wanted_type); init_const_slice(ira->codegen, result->value, pointee, 0, array_type->data.array.len, wanted_const); result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut; result->value->type = wanted_type; return result; } } } if (result_loc == nullptr) result_loc = no_result_loc(); IrInstGen *result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, true); if (type_is_invalid(result_loc_inst->value->type) || result_loc_inst->value->type->id == ZigTypeIdUnreachable) { return result_loc_inst; } return ir_build_ptr_of_array_to_slice(ira, source_instr, wanted_type, array_ptr, result_loc_inst); } static IrBasicBlockGen *ir_get_new_bb(IrAnalyze *ira, IrBasicBlockSrc *old_bb, IrInst *ref_old_instruction) { assert(old_bb); if (old_bb->child) { if (ref_old_instruction == nullptr || old_bb->child->ref_instruction != ref_old_instruction) { return old_bb->child; } } IrBasicBlockGen *new_bb = ir_build_bb_from(ira, old_bb); new_bb->ref_instruction = ref_old_instruction; return new_bb; } static IrBasicBlockGen *ir_get_new_bb_runtime(IrAnalyze *ira, IrBasicBlockSrc *old_bb, IrInst *ref_old_instruction) { assert(ref_old_instruction != nullptr); IrBasicBlockGen *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, IrBasicBlockSrc *old_bb, IrBasicBlockSrc *const_predecessor_bb) { ir_assert(!old_bb->suspended, (old_bb->instruction_list.length != 0) ? &old_bb->instruction_list.at(0)->base : nullptr); ira->instruction_index = 0; ira->old_irb.current_basic_block = old_bb; ira->const_predecessor_bb = const_predecessor_bb; ira->old_bb_index = old_bb->index; } static IrInstGen *ira_suspend(IrAnalyze *ira, IrInst *old_instruction, IrBasicBlockSrc *next_bb, IrSuspendPosition *suspend_pos) { if (ira->codegen->verbose_ir) { fprintf(stderr, "suspend %s_%" PRIu32 " %s_%" PRIu32 " #%" PRIu32 " (%zu,%zu)\n", ira->old_irb.current_basic_block->name_hint, ira->old_irb.current_basic_block->debug_id, ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->name_hint, ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->debug_id, ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index)->base.debug_id, ira->old_bb_index, ira->instruction_index); } suspend_pos->basic_block_index = ira->old_bb_index; suspend_pos->instruction_index = ira->instruction_index; ira->old_irb.current_basic_block->suspended = true; // null next_bb means that the caller plans to call ira_resume before returning if (next_bb != nullptr) { ira->old_bb_index = next_bb->index; ira->old_irb.current_basic_block = ira->old_irb.exec->basic_block_list.at(ira->old_bb_index); assert(ira->old_irb.current_basic_block == next_bb); ira->instruction_index = 0; ira->const_predecessor_bb = nullptr; next_bb->child = ir_get_new_bb_runtime(ira, next_bb, old_instruction); ira->new_irb.current_basic_block = next_bb->child; } return ira->codegen->unreach_instruction; } static IrInstGen *ira_resume(IrAnalyze *ira) { IrSuspendPosition pos = ira->resume_stack.pop(); if (ira->codegen->verbose_ir) { fprintf(stderr, "resume (%zu,%zu) ", pos.basic_block_index, pos.instruction_index); } ira->old_bb_index = pos.basic_block_index; ira->old_irb.current_basic_block = ira->old_irb.exec->basic_block_list.at(ira->old_bb_index); assert(ira->old_irb.current_basic_block->in_resume_stack); ira->old_irb.current_basic_block->in_resume_stack = false; ira->old_irb.current_basic_block->suspended = false; ira->instruction_index = pos.instruction_index; assert(pos.instruction_index < ira->old_irb.current_basic_block->instruction_list.length); if (ira->codegen->verbose_ir) { fprintf(stderr, "%s_%" PRIu32 " #%" PRIu32 "\n", ira->old_irb.current_basic_block->name_hint, ira->old_irb.current_basic_block->debug_id, ira->old_irb.current_basic_block->instruction_list.at(pos.instruction_index)->base.debug_id); } ira->const_predecessor_bb = nullptr; ira->new_irb.current_basic_block = ira->old_irb.current_basic_block->child; assert(ira->new_irb.current_basic_block != nullptr); return ira->codegen->unreach_instruction; } static void ir_start_next_bb(IrAnalyze *ira) { ira->old_bb_index += 1; bool need_repeat = true; for (;;) { while (ira->old_bb_index < ira->old_irb.exec->basic_block_list.length) { IrBasicBlockSrc *old_bb = ira->old_irb.exec->basic_block_list.at(ira->old_bb_index); if (old_bb->child == nullptr && old_bb->suspend_instruction_ref == nullptr) { ira->old_bb_index += 1; continue; } // if it's already started, or // if it's a suspended block, // then skip it if (old_bb->suspended || (old_bb->child != nullptr && old_bb->child->instruction_list.length != 0) || (old_bb->child != nullptr && old_bb->child->already_appended)) { ira->old_bb_index += 1; continue; } // if there is a resume_stack, pop one from there rather than moving on. // the last item of the resume stack will be a basic block that will // move on to the next one below if (ira->resume_stack.length != 0) { ira_resume(ira); return; } if (old_bb->child == nullptr) { old_bb->child = ir_get_new_bb_runtime(ira, old_bb, old_bb->suspend_instruction_ref); } ira->new_irb.current_basic_block = old_bb->child; ir_start_bb(ira, old_bb, nullptr); return; } if (!need_repeat) { if (ira->resume_stack.length != 0) { ira_resume(ira); } return; } need_repeat = false; ira->old_bb_index = 0; continue; } } static void ir_finish_bb(IrAnalyze *ira) { if (!ira->new_irb.current_basic_block->already_appended) { ir_append_basic_block_gen(&ira->new_irb, ira->new_irb.current_basic_block); if (ira->codegen->verbose_ir) { fprintf(stderr, "append new bb %s_%" PRIu32 "\n", ira->new_irb.current_basic_block->name_hint, ira->new_irb.current_basic_block->debug_id); } } ira->instruction_index += 1; while (ira->instruction_index < ira->old_irb.current_basic_block->instruction_list.length) { IrInstSrc *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->base, buf_sprintf("unreachable code")); break; } ira->instruction_index += 1; } ir_start_next_bb(ira); } static IrInstGen *ir_unreach_error(IrAnalyze *ira) { ira->old_bb_index = SIZE_MAX; if (ira->new_irb.exec->first_err_trace_msg == nullptr) { ira->new_irb.exec->first_err_trace_msg = ira->codegen->trace_err; } return ira->codegen->unreach_instruction; } static bool ir_emit_backward_branch(IrAnalyze *ira, IrInst* source_instruction) { size_t *bbc = ira->new_irb.exec->backward_branch_count; size_t *quota = ira->new_irb.exec->backward_branch_quota; // If we're already over quota, we've already given an error message for this. if (*bbc > *quota) { assert(ira->codegen->errors.length > 0); return false; } *bbc += 1; if (*bbc > *quota) { ir_add_error(ira, source_instruction, buf_sprintf("evaluation exceeded %" ZIG_PRI_usize " backwards branches", *quota)); return false; } return true; } static IrInstGen *ir_inline_bb(IrAnalyze *ira, IrInst* source_instruction, IrBasicBlockSrc *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->child = ira->old_irb.current_basic_block->child; ir_start_bb(ira, old_bb, ira->old_irb.current_basic_block); return ira->codegen->unreach_instruction; } static IrInstGen *ir_finish_anal(IrAnalyze *ira, IrInstGen *instruction) { if (instruction->value->type->id == ZigTypeIdUnreachable) ir_finish_bb(ira); return instruction; } static IrInstGen *ir_const_fn(IrAnalyze *ira, IrInst *source_instr, ZigFn *fn_entry) { IrInstGen *result = ir_const(ira, source_instr, fn_entry->type_entry); result->value->special = ConstValSpecialStatic; result->value->data.x_ptr.data.fn.fn_entry = fn_entry; result->value->data.x_ptr.mut = ConstPtrMutComptimeConst; result->value->data.x_ptr.special = ConstPtrSpecialFunction; return result; } static IrInstGen *ir_const_bound_fn(IrAnalyze *ira, IrInst *src_inst, ZigFn *fn_entry, IrInstGen *first_arg, IrInst *first_arg_src) { // This is unfortunately required to avoid improperly freeing first_arg_src ira_ref(ira); IrInstGen *result = ir_const(ira, src_inst, get_bound_fn_type(ira->codegen, fn_entry)); result->value->data.x_bound_fn.fn = fn_entry; result->value->data.x_bound_fn.first_arg = first_arg; result->value->data.x_bound_fn.first_arg_src = first_arg_src; return result; } static IrInstGen *ir_const_type(IrAnalyze *ira, IrInst *source_instruction, ZigType *ty) { IrInstGen *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_type); result->value->data.x_type = ty; return result; } static IrInstGen *ir_const_bool(IrAnalyze *ira, IrInst *source_instruction, bool value) { IrInstGen *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_bool); result->value->data.x_bool = value; return result; } static IrInstGen *ir_const_undef(IrAnalyze *ira, IrInst *source_instruction, ZigType *ty) { IrInstGen *result = ir_const(ira, source_instruction, ty); result->value->special = ConstValSpecialUndef; return result; } static IrInstGen *ir_const_unreachable(IrAnalyze *ira, IrInst *source_instruction) { IrInstGen *result = ir_const_noval(ira, source_instruction); result->value = ira->codegen->intern.for_unreachable(); return result; } static IrInstGen *ir_const_void(IrAnalyze *ira, IrInst *source_instruction) { IrInstGen *result = ir_const_noval(ira, source_instruction); result->value = ira->codegen->intern.for_void(); return result; } static IrInstGen *ir_const_unsigned(IrAnalyze *ira, IrInst *source_instruction, uint64_t value) { IrInstGen *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_num_lit_int); bigint_init_unsigned(&result->value->data.x_bigint, value); return result; } static IrInstGen *ir_get_const_ptr(IrAnalyze *ira, IrInst *instruction, ZigValue *pointee, ZigType *pointee_type, ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile, uint32_t ptr_align) { ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, pointee_type, ptr_is_const, ptr_is_volatile, PtrLenSingle, ptr_align, 0, 0, false); IrInstGen *const_instr = ir_const(ira, instruction, ptr_type); ZigValue *const_val = const_instr->value; const_val->data.x_ptr.special = ConstPtrSpecialRef; const_val->data.x_ptr.mut = ptr_mut; const_val->data.x_ptr.data.ref.pointee = pointee; return const_instr; } static Error ir_resolve_const_val(CodeGen *codegen, IrExecutableGen *exec, AstNode *source_node, ZigValue *val, UndefAllowed undef_allowed) { Error err; for (;;) { switch (val->special) { case ConstValSpecialStatic: return ErrorNone; case ConstValSpecialRuntime: if (!type_has_bits(codegen, val->type)) return ErrorNone; exec_add_error_node_gen(codegen, exec, source_node, buf_sprintf("unable to evaluate constant expression")); return ErrorSemanticAnalyzeFail; case ConstValSpecialUndef: if (undef_allowed == UndefOk || undef_allowed == LazyOk) return ErrorNone; exec_add_error_node_gen(codegen, exec, source_node, buf_sprintf("use of undefined value here causes undefined behavior")); return ErrorSemanticAnalyzeFail; case ConstValSpecialLazy: if (undef_allowed == LazyOk || undef_allowed == LazyOkNoUndef) return ErrorNone; if ((err = ir_resolve_lazy(codegen, source_node, val))) return err; continue; } } } static ZigValue *ir_resolve_const(IrAnalyze *ira, IrInstGen *value, UndefAllowed undef_allowed) { Error err; if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, value->base.source_node, value->value, undef_allowed))) { return nullptr; } return value->value; } Error ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, ZigValue *return_ptr, size_t *backward_branch_count, size_t *backward_branch_quota, ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name, IrExecutableGen *parent_exec, AstNode *expected_type_source_node, UndefAllowed undef_allowed) { Error err; src_assert(return_ptr->type->id == ZigTypeIdPointer, source_node); if (type_is_invalid(return_ptr->type)) return ErrorSemanticAnalyzeFail; IrExecutableSrc *ir_executable = heap::c_allocator.create(); ir_executable->source_node = source_node; ir_executable->parent_exec = parent_exec; ir_executable->name = exec_name; ir_executable->is_inline = true; ir_executable->fn_entry = fn_entry; ir_executable->c_import_buf = c_import_buf; ir_executable->begin_scope = scope; if (!ir_gen(codegen, node, scope, ir_executable)) return ErrorSemanticAnalyzeFail; if (ir_executable->first_err_trace_msg != nullptr) { codegen->trace_err = ir_executable->first_err_trace_msg; return ErrorSemanticAnalyzeFail; } if (codegen->verbose_ir) { fprintf(stderr, "\nSource: "); ast_render(stderr, node, 4); fprintf(stderr, "\n{ // (IR)\n"); ir_print_src(codegen, stderr, ir_executable, 2); fprintf(stderr, "}\n"); } IrExecutableGen *analyzed_executable = heap::c_allocator.create(); analyzed_executable->source_node = source_node; analyzed_executable->parent_exec = parent_exec; analyzed_executable->source_exec = ir_executable; analyzed_executable->name = exec_name; analyzed_executable->is_inline = true; analyzed_executable->fn_entry = fn_entry; analyzed_executable->c_import_buf = c_import_buf; analyzed_executable->backward_branch_count = backward_branch_count; analyzed_executable->backward_branch_quota = backward_branch_quota; analyzed_executable->begin_scope = scope; ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, return_ptr->type->data.pointer.child_type, expected_type_source_node, return_ptr); if (type_is_invalid(result_type)) { return ErrorSemanticAnalyzeFail; } if (codegen->verbose_ir) { fprintf(stderr, "{ // (analyzed)\n"); ir_print_gen(codegen, stderr, analyzed_executable, 2); fprintf(stderr, "}\n"); } if ((err = ir_exec_scan_for_side_effects(codegen, analyzed_executable))) return err; ZigValue *result = const_ptr_pointee(nullptr, codegen, return_ptr, source_node); if (result == nullptr) return ErrorSemanticAnalyzeFail; if ((err = ir_resolve_const_val(codegen, analyzed_executable, node, result, undef_allowed))) return err; return ErrorNone; } static ErrorTableEntry *ir_resolve_error(IrAnalyze *ira, IrInstGen *err_value) { if (type_is_invalid(err_value->value->type)) return nullptr; if (err_value->value->type->id != ZigTypeIdErrorSet) { ir_add_error_node(ira, err_value->base.source_node, buf_sprintf("expected error, found '%s'", buf_ptr(&err_value->value->type->name))); return nullptr; } ZigValue *const_val = ir_resolve_const(ira, err_value, UndefBad); if (!const_val) return nullptr; assert(const_val->data.x_err_set != nullptr); return const_val->data.x_err_set; } static ZigType *ir_resolve_const_type(CodeGen *codegen, IrExecutableGen *exec, AstNode *source_node, ZigValue *val) { Error err; if ((err = ir_resolve_const_val(codegen, exec, source_node, val, UndefBad))) return codegen->builtin_types.entry_invalid; assert(val->data.x_type != nullptr); return val->data.x_type; } static ZigValue *ir_resolve_type_lazy(IrAnalyze *ira, IrInstGen *type_value) { if (type_is_invalid(type_value->value->type)) return nullptr; if (type_value->value->type->id != ZigTypeIdMetaType) { ir_add_error_node(ira, type_value->base.source_node, buf_sprintf("expected type 'type', found '%s'", buf_ptr(&type_value->value->type->name))); return nullptr; } Error err; if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, type_value->base.source_node, type_value->value, LazyOk))) { return nullptr; } return type_value->value; } static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstGen *type_value) { ZigValue *val = ir_resolve_type_lazy(ira, type_value); if (val == nullptr) return ira->codegen->builtin_types.entry_invalid; return ir_resolve_const_type(ira->codegen, ira->new_irb.exec, type_value->base.source_node, val); } static Error ir_validate_vector_elem_type(IrAnalyze *ira, AstNode *source_node, ZigType *elem_type) { Error err; bool is_valid; if ((err = is_valid_vector_elem_type(ira->codegen, elem_type, &is_valid))) return err; if (!is_valid) { ir_add_error_node(ira, source_node, buf_sprintf("vector element type must be integer, float, bool, or pointer; '%s' is invalid", buf_ptr(&elem_type->name))); return ErrorSemanticAnalyzeFail; } return ErrorNone; } static ZigType *ir_resolve_vector_elem_type(IrAnalyze *ira, IrInstGen *elem_type_value) { Error err; ZigType *elem_type = ir_resolve_type(ira, elem_type_value); if (type_is_invalid(elem_type)) return ira->codegen->builtin_types.entry_invalid; if ((err = ir_validate_vector_elem_type(ira, elem_type_value->base.source_node, elem_type))) return ira->codegen->builtin_types.entry_invalid; return elem_type; } static ZigType *ir_resolve_int_type(IrAnalyze *ira, IrInstGen *type_value) { ZigType *ty = ir_resolve_type(ira, type_value); if (type_is_invalid(ty)) return ira->codegen->builtin_types.entry_invalid; if (ty->id != ZigTypeIdInt) { ErrorMsg *msg = ir_add_error_node(ira, type_value->base.source_node, buf_sprintf("expected integer type, found '%s'", buf_ptr(&ty->name))); if (ty->id == ZigTypeIdVector && ty->data.vector.elem_type->id == ZigTypeIdInt) { add_error_note(ira->codegen, msg, type_value->base.source_node, buf_sprintf("represent vectors with their element types, i.e. '%s'", buf_ptr(&ty->data.vector.elem_type->name))); } return ira->codegen->builtin_types.entry_invalid; } return ty; } static ZigType *ir_resolve_error_set_type(IrAnalyze *ira, IrInst *op_source, IrInstGen *type_value) { if (type_is_invalid(type_value->value->type)) return ira->codegen->builtin_types.entry_invalid; if (type_value->value->type->id != ZigTypeIdMetaType) { ErrorMsg *msg = ir_add_error_node(ira, type_value->base.source_node, buf_sprintf("expected error set type, found '%s'", buf_ptr(&type_value->value->type->name))); add_error_note(ira->codegen, msg, op_source->source_node, buf_sprintf("`||` merges error sets; `or` performs boolean OR")); return ira->codegen->builtin_types.entry_invalid; } ZigValue *const_val = ir_resolve_const(ira, type_value, UndefBad); if (!const_val) return ira->codegen->builtin_types.entry_invalid; assert(const_val->data.x_type != nullptr); ZigType *result_type = const_val->data.x_type; if (result_type->id != ZigTypeIdErrorSet) { ErrorMsg *msg = ir_add_error_node(ira, type_value->base.source_node, buf_sprintf("expected error set type, found type '%s'", buf_ptr(&result_type->name))); add_error_note(ira->codegen, msg, op_source->source_node, buf_sprintf("`||` merges error sets; `or` performs boolean OR")); return ira->codegen->builtin_types.entry_invalid; } return result_type; } static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstGen *fn_value) { if (type_is_invalid(fn_value->value->type)) return nullptr; if (fn_value->value->type->id != ZigTypeIdFn) { ir_add_error_node(ira, fn_value->base.source_node, buf_sprintf("expected function type, found '%s'", buf_ptr(&fn_value->value->type->name))); return nullptr; } ZigValue *const_val = ir_resolve_const(ira, fn_value, UndefBad); if (!const_val) return nullptr; // May be a ConstPtrSpecialHardCodedAddr if (const_val->data.x_ptr.special != ConstPtrSpecialFunction) return nullptr; return const_val->data.x_ptr.data.fn.fn_entry; } static IrInstGen *ir_analyze_optional_wrap(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, ZigType *wanted_type, ResultLoc *result_loc) { assert(wanted_type->id == ZigTypeIdOptional); if (instr_is_comptime(value)) { ZigType *payload_type = wanted_type->data.maybe.child_type; IrInstGen *casted_payload = ir_implicit_cast(ira, value, payload_type); if (type_is_invalid(casted_payload->value->type)) return ira->codegen->invalid_inst_gen; ZigValue *val = ir_resolve_const(ira, casted_payload, UndefOk); if (!val) return ira->codegen->invalid_inst_gen; IrInstGenConst *const_instruction = ir_create_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value->special = ConstValSpecialStatic; if (types_have_same_zig_comptime_repr(ira->codegen, wanted_type, payload_type)) { copy_const_val(ira->codegen, const_instruction->base.value, val); } else { const_instruction->base.value->data.x_optional = val; } const_instruction->base.value->type = wanted_type; return &const_instruction->base; } if (result_loc == nullptr && handle_is_ptr(ira->codegen, wanted_type)) { result_loc = no_result_loc(); } IrInstGen *result_loc_inst = nullptr; if (result_loc != nullptr) { result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, true); if (type_is_invalid(result_loc_inst->value->type) || result_loc_inst->value->type->id == ZigTypeIdUnreachable) { return result_loc_inst; } } IrInstGen *result = ir_build_optional_wrap(ira, source_instr, wanted_type, value, result_loc_inst); result->value->data.rh_maybe = RuntimeHintOptionalNonNull; return result; } static IrInstGen *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, ZigType *wanted_type, ResultLoc *result_loc) { assert(wanted_type->id == ZigTypeIdErrorUnion); ZigType *payload_type = wanted_type->data.error_union.payload_type; ZigType *err_set_type = wanted_type->data.error_union.err_set_type; if (instr_is_comptime(value)) { IrInstGen *casted_payload = ir_implicit_cast(ira, value, payload_type); if (type_is_invalid(casted_payload->value->type)) return ira->codegen->invalid_inst_gen; ZigValue *val = ir_resolve_const(ira, casted_payload, UndefOk); if (val == nullptr) return ira->codegen->invalid_inst_gen; ZigValue *err_set_val = ira->codegen->pass1_arena->create(); err_set_val->type = err_set_type; err_set_val->special = ConstValSpecialStatic; err_set_val->data.x_err_set = nullptr; IrInstGenConst *const_instruction = ir_create_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value->type = wanted_type; const_instruction->base.value->special = ConstValSpecialStatic; const_instruction->base.value->data.x_err_union.error_set = err_set_val; const_instruction->base.value->data.x_err_union.payload = val; return &const_instruction->base; } IrInstGen *result_loc_inst; if (handle_is_ptr(ira->codegen, wanted_type)) { if (result_loc == nullptr) result_loc = no_result_loc(); result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, true); if (type_is_invalid(result_loc_inst->value->type) || result_loc_inst->value->type->id == ZigTypeIdUnreachable) { return result_loc_inst; } } else { result_loc_inst = nullptr; } IrInstGen *result = ir_build_err_wrap_payload(ira, source_instr, wanted_type, value, result_loc_inst); result->value->data.rh_error_union = RuntimeHintErrorUnionNonError; return result; } static IrInstGen *ir_analyze_err_set_cast(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, ZigType *wanted_type) { assert(value->value->type->id == ZigTypeIdErrorSet); assert(wanted_type->id == ZigTypeIdErrorSet); if (instr_is_comptime(value)) { ZigValue *val = ir_resolve_const(ira, value, UndefBad); if (!val) return ira->codegen->invalid_inst_gen; if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) { return ira->codegen->invalid_inst_gen; } 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_inst_gen; } } IrInstGenConst *const_instruction = ir_create_inst_gen(&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; } return ir_build_cast(ira, source_instr, wanted_type, value, CastOpErrSet); } static IrInstGen *ir_analyze_frame_ptr_to_anyframe(IrAnalyze *ira, IrInst* source_instr, IrInstGen *frame_ptr, ZigType *wanted_type) { if (instr_is_comptime(frame_ptr)) { ZigValue *ptr_val = ir_resolve_const(ira, frame_ptr, UndefBad); if (ptr_val == nullptr) return ira->codegen->invalid_inst_gen; ir_assert(ptr_val->type->id == ZigTypeIdPointer, source_instr); if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { zig_panic("TODO comptime frame pointer"); } } return ir_build_cast(ira, source_instr, wanted_type, frame_ptr, CastOpBitCast); } static IrInstGen *ir_analyze_anyframe_to_anyframe(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, ZigType *wanted_type) { if (instr_is_comptime(value)) { zig_panic("TODO comptime anyframe->T to anyframe"); } return ir_build_cast(ira, source_instr, wanted_type, value, CastOpBitCast); } static IrInstGen *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, ZigType *wanted_type, ResultLoc *result_loc) { assert(wanted_type->id == ZigTypeIdErrorUnion); IrInstGen *casted_value = ir_implicit_cast(ira, value, wanted_type->data.error_union.err_set_type); if (instr_is_comptime(casted_value)) { ZigValue *val = ir_resolve_const(ira, casted_value, UndefBad); if (!val) return ira->codegen->invalid_inst_gen; ZigValue *err_set_val = ira->codegen->pass1_arena->create(); err_set_val->special = ConstValSpecialStatic; err_set_val->type = wanted_type->data.error_union.err_set_type; err_set_val->data.x_err_set = val->data.x_err_set; IrInstGenConst *const_instruction = ir_create_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value->type = wanted_type; const_instruction->base.value->special = ConstValSpecialStatic; const_instruction->base.value->data.x_err_union.error_set = err_set_val; const_instruction->base.value->data.x_err_union.payload = nullptr; return &const_instruction->base; } IrInstGen *result_loc_inst; if (handle_is_ptr(ira->codegen, wanted_type)) { if (result_loc == nullptr) result_loc = no_result_loc(); result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, true); if (type_is_invalid(result_loc_inst->value->type) || result_loc_inst->value->type->id == ZigTypeIdUnreachable) { return result_loc_inst; } } else { result_loc_inst = nullptr; } IrInstGen *result = ir_build_err_wrap_code(ira, source_instr, wanted_type, value, result_loc_inst); result->value->data.rh_error_union = RuntimeHintErrorUnionError; return result; } static IrInstGen *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value, ZigType *wanted_type) { assert(wanted_type->id == ZigTypeIdOptional); assert(instr_is_comptime(value)); ZigValue *val = ir_resolve_const(ira, value, UndefBad); assert(val != nullptr); IrInstGen *result = ir_const(ira, source_instr, wanted_type); result->value->special = ConstValSpecialStatic; if (get_src_ptr_type(wanted_type) != nullptr) { result->value->data.x_ptr.special = ConstPtrSpecialNull; } else if (is_opt_err_set(wanted_type)) { result->value->data.x_err_set = nullptr; } else { result->value->data.x_optional = nullptr; } return result; } static IrInstGen *ir_analyze_null_to_c_pointer(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value, ZigType *wanted_type) { assert(wanted_type->id == ZigTypeIdPointer); assert(wanted_type->data.pointer.ptr_len == PtrLenC); assert(instr_is_comptime(value)); ZigValue *val = ir_resolve_const(ira, value, UndefBad); assert(val != nullptr); IrInstGen *result = ir_const(ira, source_instr, wanted_type); result->value->data.x_ptr.special = ConstPtrSpecialNull; result->value->data.x_ptr.mut = ConstPtrMutComptimeConst; return result; } static IrInstGen *ir_get_ref2(IrAnalyze *ira, IrInst* source_instruction, IrInstGen *value, ZigType *elem_type, bool is_const, bool is_volatile) { Error err; if (type_is_invalid(elem_type)) return ira->codegen->invalid_inst_gen; if (instr_is_comptime(value)) { ZigValue *val = ir_resolve_const(ira, value, LazyOk); if (!val) return ira->codegen->invalid_inst_gen; return ir_get_const_ptr(ira, source_instruction, val, elem_type, ConstPtrMutComptimeConst, is_const, is_volatile, 0); } ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, elem_type, is_const, is_volatile, PtrLenSingle, 0, 0, 0, false); if ((err = type_resolve(ira->codegen, ptr_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_inst_gen; IrInstGen *result_loc; if (type_has_bits(ira->codegen, ptr_type) && !handle_is_ptr(ira->codegen, elem_type)) { result_loc = ir_resolve_result(ira, source_instruction, no_result_loc(), elem_type, nullptr, true, true); } else { result_loc = nullptr; } IrInstGen *new_instruction = ir_build_ref_gen(ira, source_instruction, ptr_type, value, result_loc); new_instruction->value->data.rh_ptr = RuntimeHintPtrStack; return new_instruction; } static IrInstGen *ir_get_ref(IrAnalyze *ira, IrInst* source_instruction, IrInstGen *value, bool is_const, bool is_volatile) { return ir_get_ref2(ira, source_instruction, value, value->value->type, is_const, is_volatile); } static ZigType *ir_resolve_union_tag_type(IrAnalyze *ira, AstNode *source_node, ZigType *union_type) { assert(union_type->id == ZigTypeIdUnion); Error err; if ((err = type_resolve(ira->codegen, union_type, ResolveStatusSizeKnown))) return ira->codegen->builtin_types.entry_invalid; AstNode *decl_node = union_type->data.unionation.decl_node; if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) { assert(union_type->data.unionation.tag_type != nullptr); return union_type->data.unionation.tag_type; } else { ErrorMsg *msg = ir_add_error_node(ira, source_node, buf_sprintf("union '%s' has no tag", buf_ptr(&union_type->name))); add_error_note(ira->codegen, msg, decl_node, buf_sprintf("consider 'union(enum)' here")); return ira->codegen->builtin_types.entry_invalid; } } static IrInstGen *ir_analyze_enum_to_int(IrAnalyze *ira, IrInst *source_instr, IrInstGen *target) { Error err; IrInstGen *enum_target; ZigType *enum_type; if (target->value->type->id == ZigTypeIdUnion) { enum_type = ir_resolve_union_tag_type(ira, target->base.source_node, target->value->type); if (type_is_invalid(enum_type)) return ira->codegen->invalid_inst_gen; enum_target = ir_implicit_cast(ira, target, enum_type); if (type_is_invalid(enum_target->value->type)) return ira->codegen->invalid_inst_gen; } else if (target->value->type->id == ZigTypeIdEnum) { enum_target = target; enum_type = target->value->type; } else { ir_add_error_node(ira, target->base.source_node, buf_sprintf("expected enum, found type '%s'", buf_ptr(&target->value->type->name))); return ira->codegen->invalid_inst_gen; } if ((err = type_resolve(ira->codegen, enum_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_inst_gen; ZigType *tag_type = enum_type->data.enumeration.tag_int_type; assert(tag_type->id == ZigTypeIdInt || tag_type->id == ZigTypeIdComptimeInt); // If there is only one possible tag, then we know at comptime what it is. if (enum_type->data.enumeration.layout == ContainerLayoutAuto && enum_type->data.enumeration.src_field_count == 1) { IrInstGen *result = ir_const(ira, source_instr, tag_type); init_const_bigint(result->value, tag_type, &enum_type->data.enumeration.fields[0].value); return result; } if (instr_is_comptime(enum_target)) { ZigValue *val = ir_resolve_const(ira, enum_target, UndefBad); if (!val) return ira->codegen->invalid_inst_gen; IrInstGen *result = ir_const(ira, source_instr, tag_type); init_const_bigint(result->value, tag_type, &val->data.x_enum_tag); return result; } return ir_build_widen_or_shorten(ira, source_instr->scope, source_instr->source_node, enum_target, tag_type); } static IrInstGen *ir_analyze_union_to_tag(IrAnalyze *ira, IrInst* source_instr, IrInstGen *target, ZigType *wanted_type) { assert(target->value->type->id == ZigTypeIdUnion); assert(wanted_type->id == ZigTypeIdEnum); assert(wanted_type == target->value->type->data.unionation.tag_type); if (instr_is_comptime(target)) { ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_inst_gen; IrInstGen *result = ir_const(ira, source_instr, wanted_type); result->value->special = ConstValSpecialStatic; result->value->type = wanted_type; bigint_init_bigint(&result->value->data.x_enum_tag, &val->data.x_union.tag); return result; } // If there is only 1 possible tag, then we know at comptime what it is. if (wanted_type->data.enumeration.layout == ContainerLayoutAuto && wanted_type->data.enumeration.src_field_count == 1) { IrInstGen *result = ir_const(ira, source_instr, wanted_type); result->value->special = ConstValSpecialStatic; result->value->type = wanted_type; TypeEnumField *enum_field = target->value->type->data.unionation.fields[0].enum_field; bigint_init_bigint(&result->value->data.x_enum_tag, &enum_field->value); return result; } return ir_build_union_tag(ira, source_instr, target, wanted_type); } static IrInstGen *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInst* source_instr, IrInstGen *target, ZigType *wanted_type) { IrInstGen *result = ir_const(ira, source_instr, wanted_type); result->value->special = ConstValSpecialUndef; return result; } static IrInstGen *ir_analyze_enum_to_union(IrAnalyze *ira, IrInst* source_instr, IrInstGen *uncasted_target, ZigType *wanted_type) { Error err; assert(wanted_type->id == ZigTypeIdUnion); if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_inst_gen; IrInstGen *target = ir_implicit_cast(ira, uncasted_target, wanted_type->data.unionation.tag_type); if (type_is_invalid(target->value->type)) return ira->codegen->invalid_inst_gen; if (instr_is_comptime(target)) { ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_inst_gen; TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag); assert(union_field != nullptr); ZigType *field_type = resolve_union_field_type(ira->codegen, union_field); if (field_type == nullptr) return ira->codegen->invalid_inst_gen; if ((err = type_resolve(ira->codegen, field_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_inst_gen; switch (type_has_one_possible_value(ira->codegen, field_type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_inst_gen; case OnePossibleValueNo: { AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at( union_field->enum_field->decl_index); ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast to union '%s' must initialize '%s' field '%s'", buf_ptr(&wanted_type->name), buf_ptr(&field_type->name), buf_ptr(union_field->name))); add_error_note(ira->codegen, msg, field_node, buf_sprintf("field '%s' declared here", buf_ptr(union_field->name))); return ira->codegen->invalid_inst_gen; } case OnePossibleValueYes: break; } IrInstGen *result = ir_const(ira, source_instr, wanted_type); result->value->special = ConstValSpecialStatic; result->value->type = wanted_type; bigint_init_bigint(&result->value->data.x_union.tag, &val->data.x_enum_tag); result->value->data.x_union.payload = ira->codegen->pass1_arena->create(); result->value->data.x_union.payload->special = ConstValSpecialStatic; result->value->data.x_union.payload->type = field_type; return result; } // if the union has all fields 0 bits, we can do it // and in fact it's a noop cast because the union value is just the enum value if (wanted_type->data.unionation.gen_field_count == 0) { return ir_build_cast(ira, &target->base, wanted_type, target, CastOpNoop); } ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("runtime cast to union '%s' which has non-void fields", buf_ptr(&wanted_type->name))); for (uint32_t i = 0; i < wanted_type->data.unionation.src_field_count; i += 1) { TypeUnionField *union_field = &wanted_type->data.unionation.fields[i]; ZigType *field_type = resolve_union_field_type(ira->codegen, union_field); if (field_type == nullptr) return ira->codegen->invalid_inst_gen; bool has_bits; if ((err = type_has_bits2(ira->codegen, field_type, &has_bits))) return ira->codegen->invalid_inst_gen; if (has_bits) { AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(i); add_error_note(ira->codegen, msg, field_node, buf_sprintf("field '%s' has type '%s'", buf_ptr(union_field->name), buf_ptr(&field_type->name))); } } return ira->codegen->invalid_inst_gen; } static IrInstGen *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInst* source_instr, IrInstGen *target, ZigType *wanted_type) { assert(wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdFloat); if (instr_is_comptime(target)) { ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_inst_gen; 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_inst_gen; } 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_inst_gen; } } IrInstGen *result = ir_const(ira, source_instr, wanted_type); result->value->type = wanted_type; if (wanted_type->id == ZigTypeIdInt) { bigint_init_bigint(&result->value->data.x_bigint, &val->data.x_bigint); } else { float_init_float(result->value, val); } return result; } // If the destination integer type has no bits, then we can emit a comptime // zero. However, we still want to emit a runtime safety check to make sure // the target is zero. if (!type_has_bits(ira->codegen, wanted_type)) { assert(wanted_type->id == ZigTypeIdInt); assert(type_has_bits(ira->codegen, target->value->type)); ir_build_assert_zero(ira, source_instr, target); IrInstGen *result = ir_const_unsigned(ira, source_instr, 0); result->value->type = wanted_type; return result; } return ir_build_widen_or_shorten(ira, source_instr->scope, source_instr->source_node, target, wanted_type); } static IrInstGen *ir_analyze_int_to_enum(IrAnalyze *ira, IrInst* source_instr, IrInstGen *target, ZigType *wanted_type) { Error err; assert(wanted_type->id == ZigTypeIdEnum); ZigType *actual_type = target->value->type; if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_inst_gen; 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_inst_gen; } assert(actual_type->id == ZigTypeIdInt || actual_type->id == ZigTypeIdComptimeInt); if (instr_is_comptime(target)) { ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_inst_gen; TypeEnumField *field = find_enum_field_by_tag(wanted_type, &val->data.x_bigint); if (field == nullptr && !wanted_type->data.enumeration.non_exhaustive) { 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_inst_gen; } IrInstGen *result = ir_const(ira, source_instr, wanted_type); bigint_init_bigint(&result->value->data.x_enum_tag, &val->data.x_bigint); return result; } return ir_build_int_to_enum_gen(ira, source_instr->scope, source_instr->source_node, wanted_type, target); } static IrInstGen *ir_analyze_number_to_literal(IrAnalyze *ira, IrInst* source_instr, IrInstGen *target, ZigType *wanted_type) { ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_inst_gen; IrInstGen *result = ir_const(ira, source_instr, wanted_type); if (wanted_type->id == ZigTypeIdComptimeFloat) { float_init_float(result->value, val); } else if (wanted_type->id == ZigTypeIdComptimeInt) { bigint_init_bigint(&result->value->data.x_bigint, &val->data.x_bigint); } else { zig_unreachable(); } return result; } static IrInstGen *ir_analyze_int_to_err(IrAnalyze *ira, IrInst* source_instr, IrInstGen *target, ZigType *wanted_type) { assert(target->value->type->id == ZigTypeIdInt); assert(!target->value->type->data.integral.is_signed); assert(wanted_type->id == ZigTypeIdErrorSet); if (instr_is_comptime(target)) { ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_inst_gen; IrInstGen *result = ir_const(ira, source_instr, wanted_type); if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) { return ira->codegen->invalid_inst_gen; } 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_inst_gen; } size_t index = bigint_as_usize(&val->data.x_bigint); result->value->data.x_err_set = ira->codegen->errors_by_index.at(index); return result; } else { ErrorTableEntry *err = nullptr; BigInt err_int; for (uint32_t i = 0, count = wanted_type->data.error_set.err_count; i < count; i += 1) { ErrorTableEntry *this_err = wanted_type->data.error_set.errors[i]; bigint_init_unsigned(&err_int, this_err->value); if (bigint_cmp(&val->data.x_bigint, &err_int) == CmpEQ) { err = this_err; break; } } if (err == nullptr) { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, &val->data.x_bigint, 10); ir_add_error(ira, source_instr, buf_sprintf("integer value %s represents no error in '%s'", buf_ptr(val_buf), buf_ptr(&wanted_type->name))); return ira->codegen->invalid_inst_gen; } result->value->data.x_err_set = err; return result; } } return ir_build_int_to_err_gen(ira, source_instr->scope, source_instr->source_node, target, wanted_type); } static IrInstGen *ir_analyze_err_to_int(IrAnalyze *ira, IrInst* source_instr, IrInstGen *target, ZigType *wanted_type) { assert(wanted_type->id == ZigTypeIdInt); ZigType *err_type = target->value->type; if (instr_is_comptime(target)) { ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_inst_gen; IrInstGen *result = ir_const(ira, source_instr, wanted_type); ErrorTableEntry *err; if (err_type->id == ZigTypeIdErrorUnion) { err = val->data.x_err_union.error_set->data.x_err_set; } else if (err_type->id == ZigTypeIdErrorSet) { err = val->data.x_err_set; } else { zig_unreachable(); } result->value->type = wanted_type; uint64_t err_value = err ? err->value : 0; bigint_init_unsigned(&result->value->data.x_bigint, err_value); if (!bigint_fits_in_bits(&result->value->data.x_bigint, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) { ir_add_error_node(ira, source_instr->source_node, buf_sprintf("error code '%s' does not fit in '%s'", buf_ptr(&err->name), buf_ptr(&wanted_type->name))); return ira->codegen->invalid_inst_gen; } 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_inst_gen; } if (err_set_type->data.error_set.err_count == 0) { IrInstGen *result = ir_const(ira, source_instr, wanted_type); bigint_init_unsigned(&result->value->data.x_bigint, 0); return result; } else if (err_set_type->data.error_set.err_count == 1) { IrInstGen *result = ir_const(ira, source_instr, wanted_type); ErrorTableEntry *err = err_set_type->data.error_set.errors[0]; bigint_init_unsigned(&result->value->data.x_bigint, err->value); return result; } } BigInt bn; bigint_init_unsigned(&bn, ira->codegen->errors_by_index.length); if (!bigint_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) { ir_add_error_node(ira, source_instr->source_node, buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name))); return ira->codegen->invalid_inst_gen; } return ir_build_err_to_int_gen(ira, source_instr->scope, source_instr->source_node, target, wanted_type); } static IrInstGen *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInst* source_instr, IrInstGen *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_inst_gen; assert((wanted_type->data.pointer.is_const && target->value->type->data.pointer.is_const) || !target->value->type->data.pointer.is_const); wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, target->value->type)); ZigType *array_type = wanted_type->data.pointer.child_type; assert(array_type->id == ZigTypeIdArray); assert(array_type->data.array.len == 1); if (instr_is_comptime(target)) { ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_inst_gen; assert(val->type->id == ZigTypeIdPointer); ZigValue *pointee = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); if (pointee == nullptr) return ira->codegen->invalid_inst_gen; if (pointee->special != ConstValSpecialRuntime) { ZigValue *array_val = ira->codegen->pass1_arena->create(); array_val->special = ConstValSpecialStatic; array_val->type = array_type; array_val->data.x_array.special = ConstArraySpecialNone; array_val->data.x_array.data.s_none.elements = pointee; array_val->parent.id = ConstParentIdScalar; array_val->parent.data.p_scalar.scalar_val = pointee; IrInstGenConst *const_instruction = ir_create_inst_gen(&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 return ir_build_cast(ira, &target->base, wanted_type, target, CastOpBitCast); } 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, ast_field_to_symbol_node(error_entry->decl_node), buf_sprintf("'error.%s' not a member of destination error set", buf_ptr(&error_entry->name))); } break; } case ConstCastResultIdErrSetGlobal: { add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("cannot cast global error set into smaller set")); break; } case ConstCastResultIdPointerChild: { ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("pointer type child '%s' cannot cast into pointer type child '%s'", buf_ptr(&cast_result->data.pointer_mismatch->actual_child->name), buf_ptr(&cast_result->data.pointer_mismatch->wanted_child->name))); report_recursive_error(ira, source_node, &cast_result->data.pointer_mismatch->child, msg); break; } case ConstCastResultIdSliceChild: { ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("slice type child '%s' cannot cast into slice type child '%s'", buf_ptr(&cast_result->data.slice_mismatch->actual_child->name), buf_ptr(&cast_result->data.slice_mismatch->wanted_child->name))); report_recursive_error(ira, source_node, &cast_result->data.slice_mismatch->child, msg); break; } case ConstCastResultIdErrorUnionPayload: { ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("error union payload '%s' cannot cast into error union payload '%s'", buf_ptr(&cast_result->data.error_union_payload->actual_payload->name), buf_ptr(&cast_result->data.error_union_payload->wanted_payload->name))); report_recursive_error(ira, source_node, &cast_result->data.error_union_payload->child, msg); break; } case ConstCastResultIdType: { AstNode *wanted_decl_node = type_decl_node(cast_result->data.type_mismatch->wanted_type); AstNode *actual_decl_node = type_decl_node(cast_result->data.type_mismatch->actual_type); if (wanted_decl_node != nullptr) { add_error_note(ira->codegen, parent_msg, wanted_decl_node, buf_sprintf("%s declared here", buf_ptr(&cast_result->data.type_mismatch->wanted_type->name))); } if (actual_decl_node != nullptr) { add_error_note(ira->codegen, parent_msg, actual_decl_node, buf_sprintf("%s declared here", buf_ptr(&cast_result->data.type_mismatch->actual_type->name))); } break; } case ConstCastResultIdFnArg: { ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("parameter %" ZIG_PRI_usize ": '%s' cannot cast into '%s'", cast_result->data.fn_arg.arg_index, buf_ptr(&cast_result->data.fn_arg.actual_param_type->name), buf_ptr(&cast_result->data.fn_arg.expected_param_type->name))); report_recursive_error(ira, source_node, cast_result->data.fn_arg.child, msg); break; } case ConstCastResultIdBadAllowsZero: { ZigType *wanted_type = cast_result->data.bad_allows_zero->wanted_type; ZigType *actual_type = cast_result->data.bad_allows_zero->actual_type; bool wanted_allows_zero = ptr_allows_addr_zero(wanted_type); bool actual_allows_zero = ptr_allows_addr_zero(actual_type); if (actual_allows_zero && !wanted_allows_zero) { add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("'%s' could have null values which are illegal in type '%s'", buf_ptr(&actual_type->name), buf_ptr(&wanted_type->name))); } else { add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("mutable '%s' allows illegal null values stored to type '%s'", buf_ptr(&wanted_type->name), buf_ptr(&actual_type->name))); } break; } case ConstCastResultIdPtrLens: { add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("pointer length mismatch")); break; } case ConstCastResultIdPtrSentinel: { ZigType *actual_type = cast_result->data.bad_ptr_sentinel->actual_type; ZigType *wanted_type = cast_result->data.bad_ptr_sentinel->wanted_type; { Buf *txt_msg = buf_sprintf("destination pointer requires a terminating '"); render_const_value(ira->codegen, txt_msg, wanted_type->data.pointer.sentinel); buf_appendf(txt_msg, "' sentinel"); if (actual_type->data.pointer.sentinel != nullptr) { buf_appendf(txt_msg, ", but source pointer has a terminating '"); render_const_value(ira->codegen, txt_msg, actual_type->data.pointer.sentinel); buf_appendf(txt_msg, "' sentinel"); } add_error_note(ira->codegen, parent_msg, source_node, txt_msg); } break; } case ConstCastResultIdSentinelArrays: { ZigType *actual_type = cast_result->data.sentinel_arrays->actual_type; ZigType *wanted_type = cast_result->data.sentinel_arrays->wanted_type; Buf *txt_msg = buf_sprintf("destination array requires a terminating '"); render_const_value(ira->codegen, txt_msg, wanted_type->data.array.sentinel); buf_appendf(txt_msg, "' sentinel"); if (actual_type->data.array.sentinel != nullptr) { buf_appendf(txt_msg, ", but source array has a terminating '"); render_const_value(ira->codegen, txt_msg, actual_type->data.array.sentinel); buf_appendf(txt_msg, "' sentinel"); } add_error_note(ira->codegen, parent_msg, source_node, txt_msg); break; } case ConstCastResultIdCV: { ZigType *wanted_type = cast_result->data.bad_cv->wanted_type; ZigType *actual_type = cast_result->data.bad_cv->actual_type; bool ok_const = !actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const; bool ok_volatile = !actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile; if (!ok_const) { add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("cast discards const qualifier")); } else if (!ok_volatile) { add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("cast discards volatile qualifier")); } else { zig_unreachable(); } break; } case ConstCastResultIdFnIsGeneric: add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("only one of the functions is generic")); break; case ConstCastResultIdFnCC: add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("calling convention mismatch")); break; case ConstCastResultIdIntShorten: { ZigType *wanted_type = cast_result->data.int_shorten->wanted_type; ZigType *actual_type = cast_result->data.int_shorten->actual_type; const char *wanted_signed = wanted_type->data.integral.is_signed ? "signed" : "unsigned"; const char *actual_signed = actual_type->data.integral.is_signed ? "signed" : "unsigned"; add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("%s %" PRIu32 "-bit int cannot represent all possible %s %" PRIu32 "-bit values", wanted_signed, wanted_type->data.integral.bit_count, actual_signed, actual_type->data.integral.bit_count)); break; } case ConstCastResultIdFnAlign: // TODO case ConstCastResultIdFnVarArgs: // TODO case ConstCastResultIdFnReturnType: // TODO case ConstCastResultIdFnArgCount: // TODO case ConstCastResultIdFnGenericArgCount: // TODO case ConstCastResultIdFnArgNoAlias: // TODO case ConstCastResultIdUnresolvedInferredErrSet: // TODO case ConstCastResultIdAsyncAllocatorType: // TODO case ConstCastResultIdArrayChild: // TODO break; } } static IrInstGen *ir_analyze_array_to_vector(IrAnalyze *ira, IrInst* source_instr, IrInstGen *array, ZigType *vector_type) { if (instr_is_comptime(array)) { // arrays and vectors have the same ZigValue representation IrInstGen *result = ir_const(ira, source_instr, vector_type); copy_const_val(ira->codegen, result->value, array->value); result->value->type = vector_type; return result; } return ir_build_array_to_vector(ira, source_instr, array, vector_type); } static IrInstGen *ir_analyze_vector_to_array(IrAnalyze *ira, IrInst* source_instr, IrInstGen *vector, ZigType *array_type, ResultLoc *result_loc) { if (instr_is_comptime(vector)) { // arrays and vectors have the same ZigValue representation IrInstGen *result = ir_const(ira, source_instr, array_type); copy_const_val(ira->codegen, result->value, vector->value); result->value->type = array_type; return result; } if (result_loc == nullptr) { result_loc = no_result_loc(); } IrInstGen *result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, array_type, nullptr, true, true); if (type_is_invalid(result_loc_inst->value->type) || result_loc_inst->value->type->id == ZigTypeIdUnreachable) { return result_loc_inst; } return ir_build_vector_to_array(ira, source_instr, array_type, vector, result_loc_inst); } static IrInstGen *ir_analyze_int_to_c_ptr(IrAnalyze *ira, IrInst* source_instr, IrInstGen *integer, ZigType *dest_type) { IrInstGen *unsigned_integer; if (instr_is_comptime(integer)) { unsigned_integer = integer; } else { assert(integer->value->type->id == ZigTypeIdInt); if (integer->value->type->data.integral.bit_count > ira->codegen->builtin_types.entry_usize->data.integral.bit_count) { ir_add_error(ira, source_instr, buf_sprintf("integer type '%s' too big for implicit @intToPtr to type '%s'", buf_ptr(&integer->value->type->name), buf_ptr(&dest_type->name))); return ira->codegen->invalid_inst_gen; } if (integer->value->type->data.integral.is_signed) { ZigType *unsigned_int_type = get_int_type(ira->codegen, false, integer->value->type->data.integral.bit_count); unsigned_integer = ir_analyze_bit_cast(ira, source_instr, integer, unsigned_int_type); if (type_is_invalid(unsigned_integer->value->type)) return ira->codegen->invalid_inst_gen; } else { unsigned_integer = integer; } } return ir_analyze_int_to_ptr(ira, source_instr, unsigned_integer, dest_type); } static bool is_pointery_and_elem_is_not_pointery(ZigType *ty) { if (ty->id == ZigTypeIdPointer) return ty->data.pointer.child_type->id != ZigTypeIdPointer; if (ty->id == ZigTypeIdFn) return true; if (ty->id == ZigTypeIdOptional) { ZigType *ptr_ty = ty->data.maybe.child_type; if (ptr_ty->id == ZigTypeIdPointer) return ptr_ty->data.pointer.child_type->id != ZigTypeIdPointer; if (ptr_ty->id == ZigTypeIdFn) return true; } return false; } static IrInstGen *ir_analyze_enum_literal(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, ZigType *enum_type) { assert(enum_type->id == ZigTypeIdEnum); Error err; if ((err = type_resolve(ira->codegen, enum_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_inst_gen; TypeEnumField *field = find_enum_type_field(enum_type, value->value->data.x_enum_literal); if (field == nullptr) { ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("enum '%s' has no field named '%s'", buf_ptr(&enum_type->name), buf_ptr(value->value->data.x_enum_literal))); add_error_note(ira->codegen, msg, enum_type->data.enumeration.decl_node, buf_sprintf("'%s' declared here", buf_ptr(&enum_type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *result = ir_const(ira, source_instr, enum_type); bigint_init_bigint(&result->value->data.x_enum_tag, &field->value); return result; } static IrInstGen *ir_analyze_struct_literal_to_array(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, ZigType *wanted_type) { ir_add_error(ira, source_instr, buf_sprintf("TODO: type coercion of anon list literal to array")); return ira->codegen->invalid_inst_gen; } static IrInstGen *ir_analyze_struct_literal_to_struct(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, ZigType *wanted_type) { ir_add_error(ira, source_instr, buf_sprintf("TODO: type coercion of anon struct literal to struct")); return ira->codegen->invalid_inst_gen; } static IrInstGen *ir_analyze_struct_literal_to_union(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, ZigType *union_type) { Error err; ZigType *struct_type = value->value->type; assert(struct_type->id == ZigTypeIdStruct); assert(union_type->id == ZigTypeIdUnion); assert(struct_type->data.structure.src_field_count == 1); TypeStructField *only_field = struct_type->data.structure.fields[0]; if ((err = type_resolve(ira->codegen, union_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_inst_gen; TypeUnionField *union_field = find_union_type_field(union_type, only_field->name); if (union_field == nullptr) { ir_add_error_node(ira, only_field->decl_node, buf_sprintf("no member named '%s' in union '%s'", buf_ptr(only_field->name), buf_ptr(&union_type->name))); return ira->codegen->invalid_inst_gen; } ZigType *payload_type = resolve_union_field_type(ira->codegen, union_field); if (payload_type == nullptr) return ira->codegen->invalid_inst_gen; IrInstGen *field_value = ir_analyze_struct_value_field_value(ira, source_instr, value, only_field); if (type_is_invalid(field_value->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_value = ir_implicit_cast(ira, field_value, payload_type); if (type_is_invalid(casted_value->value->type)) return ira->codegen->invalid_inst_gen; if (instr_is_comptime(casted_value)) { ZigValue *val = ir_resolve_const(ira, casted_value, UndefBad); if (val == nullptr) return ira->codegen->invalid_inst_gen; IrInstGen *result = ir_const(ira, source_instr, union_type); bigint_init_bigint(&result->value->data.x_union.tag, &union_field->enum_field->value); result->value->data.x_union.payload = val; val->parent.id = ConstParentIdUnion; val->parent.data.p_union.union_val = result->value; return result; } IrInstGen *result_loc_inst = ir_resolve_result(ira, source_instr, no_result_loc(), union_type, nullptr, true, true); if (type_is_invalid(result_loc_inst->value->type) || result_loc_inst->value->type->id == ZigTypeIdUnreachable) { return ira->codegen->invalid_inst_gen; } IrInstGen *payload_ptr = ir_analyze_container_field_ptr(ira, only_field->name, source_instr, result_loc_inst, source_instr, union_type, true); if (type_is_invalid(payload_ptr->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *store_ptr_inst = ir_analyze_store_ptr(ira, source_instr, payload_ptr, casted_value, false); if (type_is_invalid(store_ptr_inst->value->type)) return ira->codegen->invalid_inst_gen; return ir_get_deref(ira, source_instr, result_loc_inst, nullptr); } // Add a compile error and return ErrorSemanticAnalyzeFail if the pointer alignment does not work, // otherwise return ErrorNone. Does not emit any instructions. // Assumes that the pointer types have element types with the same ABI alignment. Avoids resolving the // pointer types' alignments if both of the pointer types are ABI aligned. static Error ir_cast_ptr_align(IrAnalyze *ira, IrInst* source_instr, ZigType *dest_ptr_type, ZigType *src_ptr_type, AstNode *src_source_node) { Error err; ir_assert(dest_ptr_type->id == ZigTypeIdPointer, source_instr); ir_assert(src_ptr_type->id == ZigTypeIdPointer, source_instr); if (dest_ptr_type->data.pointer.explicit_alignment == 0 && src_ptr_type->data.pointer.explicit_alignment == 0) { return ErrorNone; } if ((err = type_resolve(ira->codegen, dest_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) return ErrorSemanticAnalyzeFail; if ((err = type_resolve(ira->codegen, src_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) return ErrorSemanticAnalyzeFail; uint32_t wanted_align = get_ptr_align(ira->codegen, dest_ptr_type); uint32_t actual_align = get_ptr_align(ira->codegen, src_ptr_type); if (wanted_align > actual_align) { ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment")); add_error_note(ira->codegen, msg, src_source_node, buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&src_ptr_type->name), actual_align)); add_error_note(ira->codegen, msg, source_instr->source_node, buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&dest_ptr_type->name), wanted_align)); return ErrorSemanticAnalyzeFail; } return ErrorNone; } static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* source_instr, IrInstGen *struct_operand, TypeStructField *field) { IrInstGen *struct_ptr = ir_get_ref(ira, source_instr, struct_operand, true, false); if (type_is_invalid(struct_ptr->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *field_ptr = ir_analyze_struct_field_ptr(ira, source_instr, field, struct_ptr, struct_operand->value->type, false); if (type_is_invalid(field_ptr->value->type)) return ira->codegen->invalid_inst_gen; return ir_get_deref(ira, source_instr, field_ptr, nullptr); } static IrInstGen *ir_analyze_optional_value_payload_value(IrAnalyze *ira, IrInst* source_instr, IrInstGen *optional_operand, bool safety_check_on) { IrInstGen *opt_ptr = ir_get_ref(ira, source_instr, optional_operand, true, false); IrInstGen *payload_ptr = ir_analyze_unwrap_optional_payload(ira, source_instr, opt_ptr, safety_check_on, false); return ir_get_deref(ira, source_instr, payload_ptr, nullptr); } static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr, ZigType *wanted_type, IrInstGen *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_inst_gen; } // This means the wanted type is anything. if (wanted_type == ira->codegen->builtin_types.entry_var) { return value; } // perfect match or non-const to const ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type, source_node, false); if (const_cast_result.id == ConstCastResultIdInvalid) return ira->codegen->invalid_inst_gen; if (const_cast_result.id == ConstCastResultIdOk) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop); } if (const_cast_result.id == ConstCastResultIdFnCC) { ir_assert(value->value->type->id == ZigTypeIdFn, source_instr); // ConstCastResultIdFnCC is guaranteed to be the last one reported, meaning everything else is ok. if (wanted_type->data.fn.fn_type_id.cc == CallingConventionAsync && actual_type->data.fn.fn_type_id.cc == CallingConventionUnspecified) { ir_assert(value->value->data.x_ptr.special == ConstPtrSpecialFunction, source_instr); ZigFn *fn = value->value->data.x_ptr.data.fn.fn_entry; if (fn->inferred_async_node == nullptr) { fn->inferred_async_node = source_instr->source_node; } return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop); } } // cast from T to ?T // note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism if (wanted_type->id == ZigTypeIdOptional) { ZigType *wanted_child_type = wanted_type->data.maybe.child_type; if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, false).id == ConstCastResultIdOk) { return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type, nullptr); } else if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) { return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type, nullptr); } else { return ira->codegen->invalid_inst_gen; } } 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_inst_gen; if ((err = type_resolve(ira->codegen, wanted_child_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) return ira->codegen->invalid_inst_gen; 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) { IrInstGen *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_inst_gen; return ir_analyze_optional_wrap(ira, source_instr, cast1, wanted_type, nullptr); } } } // T to E!T if (wanted_type->id == ZigTypeIdErrorUnion) { if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type, source_node, false).id == ConstCastResultIdOk) { return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type, nullptr); } else if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error_union.payload_type, true)) { return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type, nullptr); } else { return ira->codegen->invalid_inst_gen; } } } // 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) { IrInstGen *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_inst_gen; IrInstGen *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); if (type_is_invalid(cast2->value->type)) return ira->codegen->invalid_inst_gen; return cast2; } } // cast from comptime-known number to another number type if (instr_is_comptime(value) && (actual_type->id == ZigTypeIdInt || actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdFloat || actual_type->id == ZigTypeIdComptimeFloat) && (wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdComptimeInt || wanted_type->id == ZigTypeIdFloat || wanted_type->id == ZigTypeIdComptimeFloat)) { if (value->value->special == ConstValSpecialUndef) { IrInstGen *result = ir_const(ira, source_instr, wanted_type); result->value->special = ConstValSpecialUndef; return result; } if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) { if (wanted_type->id == ZigTypeIdComptimeInt || wanted_type->id == ZigTypeIdInt) { IrInstGen *result = ir_const(ira, source_instr, wanted_type); if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdInt) { copy_const_val(ira->codegen, result->value, value->value); result->value->type = wanted_type; } else { float_init_bigint(&result->value->data.x_bigint, value->value); } return result; } else if (wanted_type->id == ZigTypeIdComptimeFloat || wanted_type->id == ZigTypeIdFloat) { IrInstGen *result = ir_const(ira, source_instr, wanted_type); if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdInt) { BigFloat bf; bigfloat_init_bigint(&bf, &value->value->data.x_bigint); float_init_bigfloat(result->value, &bf); } else { float_init_float(result->value, value->value); } return result; } zig_unreachable(); } else { return ira->codegen->invalid_inst_gen; } } // widening conversion if (wanted_type->id == ZigTypeIdInt && actual_type->id == ZigTypeIdInt && wanted_type->data.integral.is_signed == actual_type->data.integral.is_signed && wanted_type->data.integral.bit_count >= actual_type->data.integral.bit_count) { return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); } // small enough unsigned ints can get casted to large enough signed ints if (wanted_type->id == ZigTypeIdInt && wanted_type->data.integral.is_signed && actual_type->id == ZigTypeIdInt && !actual_type->data.integral.is_signed && wanted_type->data.integral.bit_count > actual_type->data.integral.bit_count) { return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); } // float widening conversion if (wanted_type->id == ZigTypeIdFloat && actual_type->id == ZigTypeIdFloat && wanted_type->data.floating.bit_count >= actual_type->data.floating.bit_count) { return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); } // *[N]T to ?[]T if (wanted_type->id == ZigTypeIdOptional && is_slice(wanted_type->data.maybe.child_type) && actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle && actual_type->data.pointer.child_type->id == ZigTypeIdArray) { IrInstGen *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_inst_gen; IrInstGen *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); if (type_is_invalid(cast2->value->type)) return ira->codegen->invalid_inst_gen; return cast2; } // *[N]T to [*]T and [*c]T if (wanted_type->id == ZigTypeIdPointer && (wanted_type->data.pointer.ptr_len == PtrLenUnknown || wanted_type->data.pointer.ptr_len == PtrLenC) && actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle && actual_type->data.pointer.child_type->id == ZigTypeIdArray && (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile)) { ZigType *actual_array_type = actual_type->data.pointer.child_type; if (wanted_type->data.pointer.sentinel == nullptr || (actual_array_type->data.array.sentinel != nullptr && const_values_equal(ira->codegen, wanted_type->data.pointer.sentinel, actual_array_type->data.array.sentinel))) { if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) return ira->codegen->invalid_inst_gen; if ((err = type_resolve(ira->codegen, wanted_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) return ira->codegen->invalid_inst_gen; if (get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_type) && types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, actual_type->data.pointer.child_type->data.array.child_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) { return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type); } } } // *[N]T to []T // *[N]T to E![]T if ((is_slice(wanted_type) || (wanted_type->id == ZigTypeIdErrorUnion && is_slice(wanted_type->data.error_union.payload_type))) && actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle && actual_type->data.pointer.child_type->id == ZigTypeIdArray) { ZigType *slice_type = (wanted_type->id == ZigTypeIdErrorUnion) ? wanted_type->data.error_union.payload_type : wanted_type; ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; assert(slice_ptr_type->id == ZigTypeIdPointer); ZigType *array_type = actual_type->data.pointer.child_type; bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 || !actual_type->data.pointer.is_const); if (const_ok && types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, array_type->data.array.child_type, source_node, !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk) { // If the pointers both have ABI align, it works. // Or if the array length is 0, alignment doesn't matter. bool ok_align = array_type->data.array.len == 0 || (slice_ptr_type->data.pointer.explicit_alignment == 0 && actual_type->data.pointer.explicit_alignment == 0); if (!ok_align) { // If either one has non ABI align, we have to resolve them both if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { return ira->codegen->invalid_inst_gen; } if ((err = type_resolve(ira->codegen, slice_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { return ira->codegen->invalid_inst_gen; } ok_align = get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, slice_ptr_type); } if (ok_align) { if (wanted_type->id == ZigTypeIdErrorUnion) { IrInstGen *cast1 = ir_analyze_cast(ira, source_instr, slice_type, value); if (type_is_invalid(cast1->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); if (type_is_invalid(cast2->value->type)) return ira->codegen->invalid_inst_gen; return cast2; } else { return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, slice_type, nullptr); } } } } // *[N]T to E![]T if (wanted_type->id == ZigTypeIdErrorUnion && is_slice(wanted_type->data.error_union.payload_type) && actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle && actual_type->data.pointer.child_type->id == ZigTypeIdArray) { ZigType *slice_type = wanted_type->data.error_union.payload_type; ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; assert(slice_ptr_type->id == ZigTypeIdPointer); ZigType *array_type = actual_type->data.pointer.child_type; bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 || !actual_type->data.pointer.is_const); if (const_ok && types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, array_type->data.array.child_type, source_node, !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk) { // If the pointers both have ABI align, it works. bool ok_align = slice_ptr_type->data.pointer.explicit_alignment == 0 && actual_type->data.pointer.explicit_alignment == 0; if (!ok_align) { // If either one has non ABI align, we have to resolve them both if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { return ira->codegen->invalid_inst_gen; } if ((err = type_resolve(ira->codegen, slice_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { return ira->codegen->invalid_inst_gen; } ok_align = get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, slice_ptr_type); } if (ok_align) { return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, slice_type, nullptr); } } } // @Vector(N,T1) to @Vector(N,T2) if (actual_type->id == ZigTypeIdVector && wanted_type->id == ZigTypeIdVector) { if (actual_type->data.vector.len == wanted_type->data.vector.len && types_match_const_cast_only(ira, wanted_type->data.vector.elem_type, actual_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk) { return ir_analyze_bit_cast(ira, source_instr, value, wanted_type); } } // *@Frame(func) to anyframe->T or anyframe // *@Frame(func) to ?anyframe->T or ?anyframe // *@Frame(func) to E!anyframe->T or E!anyframe if (actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle && !actual_type->data.pointer.is_const && actual_type->data.pointer.child_type->id == ZigTypeIdFnFrame) { ZigType *anyframe_type; if (wanted_type->id == ZigTypeIdAnyFrame) { anyframe_type = wanted_type; } else if (wanted_type->id == ZigTypeIdOptional && wanted_type->data.maybe.child_type->id == ZigTypeIdAnyFrame) { anyframe_type = wanted_type->data.maybe.child_type; } else if (wanted_type->id == ZigTypeIdErrorUnion && wanted_type->data.error_union.payload_type->id == ZigTypeIdAnyFrame) { anyframe_type = wanted_type->data.error_union.payload_type; } else { anyframe_type = nullptr; } if (anyframe_type != nullptr) { bool ok = true; if (anyframe_type->data.any_frame.result_type != nullptr) { ZigFn *fn = actual_type->data.pointer.child_type->data.frame.fn; ZigType *fn_return_type = fn->type_entry->data.fn.fn_type_id.return_type; if (anyframe_type->data.any_frame.result_type != fn_return_type) { ok = false; } } if (ok) { IrInstGen *cast1 = ir_analyze_frame_ptr_to_anyframe(ira, source_instr, value, anyframe_type); if (anyframe_type == wanted_type) return cast1; return ir_analyze_cast(ira, source_instr, wanted_type, cast1); } } } // anyframe->T to anyframe if (actual_type->id == ZigTypeIdAnyFrame && actual_type->data.any_frame.result_type != nullptr && wanted_type->id == ZigTypeIdAnyFrame && wanted_type->data.any_frame.result_type == nullptr) { return ir_analyze_anyframe_to_anyframe(ira, source_instr, value, wanted_type); } // cast from null literal to maybe type if (wanted_type->id == ZigTypeIdOptional && actual_type->id == ZigTypeIdNull) { return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type); } // cast from null literal to C pointer if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC && actual_type->id == ZigTypeIdNull) { return ir_analyze_null_to_c_pointer(ira, source_instr, value, wanted_type); } // cast from E to E!T if (wanted_type->id == ZigTypeIdErrorUnion && actual_type->id == ZigTypeIdErrorSet) { return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type, nullptr); } // cast from typed number to integer or float literal. // works when the number is known at compile time if (instr_is_comptime(value) && ((actual_type->id == ZigTypeIdInt && wanted_type->id == ZigTypeIdComptimeInt) || (actual_type->id == ZigTypeIdFloat && wanted_type->id == ZigTypeIdComptimeFloat))) { return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type); } // cast from enum literal to enum with matching field name if (actual_type->id == ZigTypeIdEnumLiteral && wanted_type->id == ZigTypeIdEnum) { return ir_analyze_enum_literal(ira, source_instr, value, wanted_type); } // cast from enum literal to optional enum if (actual_type->id == ZigTypeIdEnumLiteral && (wanted_type->id == ZigTypeIdOptional && wanted_type->data.maybe.child_type->id == ZigTypeIdEnum)) { IrInstGen *result = ir_analyze_enum_literal(ira, source_instr, value, wanted_type->data.maybe.child_type); if (type_is_invalid(result->value->type)) return result; return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type, nullptr); } // cast from enum literal to error union when payload is an enum if (actual_type->id == ZigTypeIdEnumLiteral && (wanted_type->id == ZigTypeIdErrorUnion && wanted_type->data.error_union.payload_type->id == ZigTypeIdEnum)) { IrInstGen *result = ir_analyze_enum_literal(ira, source_instr, value, wanted_type->data.error_union.payload_type); if (type_is_invalid(result->value->type)) return result; return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type, nullptr); } // cast from union to the enum type of the union if (actual_type->id == ZigTypeIdUnion && wanted_type->id == ZigTypeIdEnum) { if ((err = type_resolve(ira->codegen, actual_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_inst_gen; if (actual_type->data.unionation.tag_type == wanted_type) { return ir_analyze_union_to_tag(ira, source_instr, value, wanted_type); } } // enum to union which has the enum as the tag type, or // enum literal to union which has a matching enum as the tag type if (is_tagged_union(wanted_type) && (actual_type->id == ZigTypeIdEnum || actual_type->id == ZigTypeIdEnumLiteral)) { return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type); } // cast from *T to *[1]T if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle) { ZigType *array_type = wanted_type->data.pointer.child_type; if (array_type->id == ZigTypeIdArray && array_type->data.array.len == 1 && types_match_const_cast_only(ira, array_type->data.array.child_type, actual_type->data.pointer.child_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk && // `types_match_const_cast_only` only gets info for child_types (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile)) { if ((err = ir_cast_ptr_align(ira, source_instr, wanted_type, actual_type, value->base.source_node))) return ira->codegen->invalid_inst_gen; return ir_analyze_ptr_to_array(ira, source_instr, value, wanted_type); } } // [:x]T to [*:x]T // [:x]T to [*c]T if (wanted_type->id == ZigTypeIdPointer && is_slice(actual_type) && ((wanted_type->data.pointer.ptr_len == PtrLenUnknown && wanted_type->data.pointer.sentinel != nullptr) || wanted_type->data.pointer.ptr_len == PtrLenC)) { ZigType *slice_ptr_type = resolve_struct_field_type(ira->codegen, actual_type->data.structure.fields[slice_ptr_index]); if (types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, slice_ptr_type->data.pointer.child_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk && (slice_ptr_type->data.pointer.sentinel != nullptr && (wanted_type->data.pointer.ptr_len == PtrLenC || const_values_equal(ira->codegen, wanted_type->data.pointer.sentinel, slice_ptr_type->data.pointer.sentinel)))) { TypeStructField *ptr_field = actual_type->data.structure.fields[slice_ptr_index]; IrInstGen *slice_ptr = ir_analyze_struct_value_field_value(ira, source_instr, value, ptr_field); return ir_implicit_cast2(ira, source_instr, slice_ptr, wanted_type); } } // cast from *T and [*]T to *c_void and ?*c_void // but don't do it if the actual type is a double pointer if (is_pointery_and_elem_is_not_pointery(actual_type)) { ZigType *dest_ptr_type = nullptr; if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.child_type == ira->codegen->builtin_types.entry_c_void) { dest_ptr_type = wanted_type; } else if (wanted_type->id == ZigTypeIdOptional && wanted_type->data.maybe.child_type->id == ZigTypeIdPointer && wanted_type->data.maybe.child_type->data.pointer.child_type == ira->codegen->builtin_types.entry_c_void) { dest_ptr_type = wanted_type->data.maybe.child_type; } if (dest_ptr_type != nullptr) { return ir_analyze_ptr_cast(ira, source_instr, value, source_instr, wanted_type, source_instr, true, false); } } // cast from T to *T where T is zero bits if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) { bool has_bits; if ((err = type_has_bits2(ira->codegen, actual_type, &has_bits))) return ira->codegen->invalid_inst_gen; if (!has_bits) { return ir_get_ref(ira, source_instr, value, false, false); } } // cast from @Vector(N, T) to [N]T if (wanted_type->id == ZigTypeIdArray && actual_type->id == ZigTypeIdVector && wanted_type->data.array.len == actual_type->data.vector.len && types_match_const_cast_only(ira, wanted_type->data.array.child_type, actual_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk) { return ir_analyze_vector_to_array(ira, source_instr, value, wanted_type, nullptr); } // cast from [N]T to @Vector(N, T) if (actual_type->id == ZigTypeIdArray && wanted_type->id == ZigTypeIdVector && actual_type->data.array.len == wanted_type->data.vector.len && types_match_const_cast_only(ira, actual_type->data.array.child_type, wanted_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk) { return ir_analyze_array_to_vector(ira, source_instr, value, wanted_type); } // casting between C pointers and normal pointers if (wanted_type->id == ZigTypeIdPointer && actual_type->id == ZigTypeIdPointer && (wanted_type->data.pointer.ptr_len == PtrLenC || actual_type->data.pointer.ptr_len == PtrLenC) && types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, actual_type->data.pointer.child_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) { return ir_analyze_ptr_cast(ira, source_instr, value, source_instr, wanted_type, source_instr, true, false); } // cast from integer to C pointer if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC && (actual_type->id == ZigTypeIdInt || actual_type->id == ZigTypeIdComptimeInt)) { return ir_analyze_int_to_c_ptr(ira, source_instr, value, wanted_type); } // cast from inferred struct type to array, union, or struct if (is_anon_container(actual_type)) { const bool is_array_init = actual_type->data.structure.special == StructSpecialInferredTuple; const uint32_t field_count = actual_type->data.structure.src_field_count; if (wanted_type->id == ZigTypeIdArray && (is_array_init || field_count == 0) && wanted_type->data.array.len == field_count) { return ir_analyze_struct_literal_to_array(ira, source_instr, value, wanted_type); } else if (wanted_type->id == ZigTypeIdStruct && (!is_array_init || field_count == 0)) { return ir_analyze_struct_literal_to_struct(ira, source_instr, value, wanted_type); } else if (wanted_type->id == ZigTypeIdUnion && !is_array_init && field_count == 1) { return ir_analyze_struct_literal_to_union(ira, source_instr, value, wanted_type); } } // cast from undefined to anything if (actual_type->id == ZigTypeIdUndefined) { return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); } // T to ?U, where T implicitly casts to U if (wanted_type->id == ZigTypeIdOptional && actual_type->id != ZigTypeIdOptional) { IrInstGen *cast1 = ir_implicit_cast2(ira, source_instr, value, wanted_type->data.maybe.child_type); if (type_is_invalid(cast1->value->type)) return ira->codegen->invalid_inst_gen; return ir_implicit_cast2(ira, source_instr, cast1, wanted_type); } // T to E!U, where T implicitly casts to U if (wanted_type->id == ZigTypeIdErrorUnion && actual_type->id != ZigTypeIdErrorUnion && actual_type->id != ZigTypeIdErrorSet) { IrInstGen *cast1 = ir_implicit_cast2(ira, source_instr, value, wanted_type->data.error_union.payload_type); if (type_is_invalid(cast1->value->type)) return ira->codegen->invalid_inst_gen; return ir_implicit_cast2(ira, source_instr, cast1, wanted_type); } ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node, buf_sprintf("expected type '%s', found '%s'", buf_ptr(&wanted_type->name), buf_ptr(&actual_type->name))); report_recursive_error(ira, source_instr->source_node, &const_cast_result, parent_msg); return ira->codegen->invalid_inst_gen; } static IrInstGen *ir_implicit_cast2(IrAnalyze *ira, IrInst *value_source_instr, IrInstGen *value, ZigType *expected_type) { assert(value); assert(!expected_type || !type_is_invalid(expected_type)); assert(value->value->type); assert(!type_is_invalid(value->value->type)); if (expected_type == nullptr) return value; // anything will do if (expected_type == value->value->type) return value; // match if (value->value->type->id == ZigTypeIdUnreachable) return value; return ir_analyze_cast(ira, value_source_instr, expected_type, value); } static IrInstGen *ir_implicit_cast(IrAnalyze *ira, IrInstGen *value, ZigType *expected_type) { return ir_implicit_cast2(ira, &value->base, value, expected_type); } static ZigType *get_ptr_elem_type(CodeGen *g, IrInstGen *ptr) { ir_assert_gen(ptr->value->type->id == ZigTypeIdPointer, ptr); ZigType *elem_type = ptr->value->type->data.pointer.child_type; if (elem_type != g->builtin_types.entry_var) return elem_type; if (ir_resolve_lazy(g, ptr->base.source_node, ptr->value)) return g->builtin_types.entry_invalid; assert(value_is_comptime(ptr->value)); ZigValue *pointee = const_ptr_pointee_unchecked(g, ptr->value); return pointee->type; } static IrInstGen *ir_get_deref(IrAnalyze *ira, IrInst* source_instruction, IrInstGen *ptr, ResultLoc *result_loc) { Error err; ZigType *ptr_type = ptr->value->type; if (type_is_invalid(ptr_type)) return ira->codegen->invalid_inst_gen; if (ptr_type->id != ZigTypeIdPointer) { ir_add_error_node(ira, source_instruction->source_node, buf_sprintf("attempt to dereference non-pointer type '%s'", buf_ptr(&ptr_type->name))); return ira->codegen->invalid_inst_gen; } ZigType *child_type = ptr_type->data.pointer.child_type; if (type_is_invalid(child_type)) return ira->codegen->invalid_inst_gen; // if the child type has one possible value, the deref is comptime switch (type_has_one_possible_value(ira->codegen, child_type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_inst_gen; case OnePossibleValueYes: return ir_const_move(ira, source_instruction, get_the_one_possible_value(ira->codegen, child_type)); case OnePossibleValueNo: break; } if (instr_is_comptime(ptr)) { if (ptr->value->special == ConstValSpecialUndef) { // If we are in a TypeOf call, we return an undefined value instead of erroring // since we know the type. if (get_scope_typeof(source_instruction->scope)) { return ir_const_undef(ira, source_instruction, child_type); } ir_add_error(ira, &ptr->base, buf_sprintf("attempt to dereference undefined value")); return ira->codegen->invalid_inst_gen; } if (ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { ZigValue *pointee = const_ptr_pointee_unchecked(ira->codegen, ptr->value); if (child_type == ira->codegen->builtin_types.entry_var) { child_type = pointee->type; } if (pointee->special != ConstValSpecialRuntime) { IrInstGen *result = ir_const(ira, source_instruction, child_type); if ((err = ir_read_const_ptr(ira, ira->codegen, source_instruction->source_node, result->value, ptr->value))) { return ira->codegen->invalid_inst_gen; } result->value->type = child_type; return result; } } } // if the instruction is a const ref instruction we can skip it if (ptr->id == IrInstGenIdRef) { IrInstGenRef *ref_inst = reinterpret_cast(ptr); return ref_inst->operand; } // If the instruction is a element pointer instruction to a vector, we emit // vector element extract instruction rather than load pointer. If the // pointer type has non-VECTOR_INDEX_RUNTIME value, it would have been // possible to implement this in the codegen for IrInstGenLoadPtr. // However if it has VECTOR_INDEX_RUNTIME then we must emit a compile error // if the vector index cannot be determined right here, right now, because // the type information does not contain enough information to actually // perform a dereference. if (ptr_type->data.pointer.vector_index == VECTOR_INDEX_RUNTIME) { if (ptr->id == IrInstGenIdElemPtr) { IrInstGenElemPtr *elem_ptr = (IrInstGenElemPtr *)ptr; IrInstGen *vector_loaded = ir_get_deref(ira, &elem_ptr->array_ptr->base, elem_ptr->array_ptr, nullptr); IrInstGen *elem_index = elem_ptr->elem_index; return ir_build_vector_extract_elem(ira, source_instruction, vector_loaded, elem_index); } ir_add_error(ira, &ptr->base, buf_sprintf("unable to determine vector element index of type '%s'", buf_ptr(&ptr_type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *result_loc_inst; if (ptr_type->data.pointer.host_int_bytes != 0 && handle_is_ptr(ira->codegen, child_type)) { if (result_loc == nullptr) result_loc = no_result_loc(); result_loc_inst = ir_resolve_result(ira, source_instruction, result_loc, child_type, nullptr, true, true); if (type_is_invalid(result_loc_inst->value->type) || result_loc_inst->value->type->id == ZigTypeIdUnreachable) { return result_loc_inst; } } else { result_loc_inst = nullptr; } return ir_build_load_ptr_gen(ira, source_instruction, ptr, child_type, result_loc_inst); } static bool ir_resolve_const_align(CodeGen *codegen, IrExecutableGen *exec, AstNode *source_node, ZigValue *const_val, uint32_t *out) { Error err; if ((err = ir_resolve_const_val(codegen, exec, source_node, const_val, UndefBad))) return false; uint32_t align_bytes = bigint_as_u32(&const_val->data.x_bigint); if (align_bytes == 0) { exec_add_error_node_gen(codegen, exec, source_node, buf_sprintf("alignment must be >= 1")); return false; } if (!is_power_of_2(align_bytes)) { exec_add_error_node_gen(codegen, exec, source_node, buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes)); return false; } *out = align_bytes; return true; } static bool ir_resolve_align(IrAnalyze *ira, IrInstGen *value, ZigType *elem_type, uint32_t *out) { if (type_is_invalid(value->value->type)) return false; // Look for this pattern: `*align(@alignOf(T)) T`. // This can be resolved to be `*out = 0` without resolving any alignment. if (elem_type != nullptr && value->value->special == ConstValSpecialLazy && value->value->data.x_lazy->id == LazyValueIdAlignOf) { LazyValueAlignOf *lazy_align_of = reinterpret_cast(value->value->data.x_lazy); ZigType *lazy_elem_type = ir_resolve_type(lazy_align_of->ira, lazy_align_of->target_type); if (type_is_invalid(lazy_elem_type)) return false; if (elem_type == lazy_elem_type) { *out = 0; return true; } } IrInstGen *casted_value = ir_implicit_cast(ira, value, get_align_amt_type(ira->codegen)); if (type_is_invalid(casted_value->value->type)) return false; return ir_resolve_const_align(ira->codegen, ira->new_irb.exec, value->base.source_node, casted_value->value, out); } static bool ir_resolve_unsigned(IrAnalyze *ira, IrInstGen *value, ZigType *int_type, uint64_t *out) { if (type_is_invalid(value->value->type)) return false; IrInstGen *casted_value = ir_implicit_cast(ira, value, int_type); if (type_is_invalid(casted_value->value->type)) return false; ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return false; *out = bigint_as_u64(&const_val->data.x_bigint); return true; } static bool ir_resolve_usize(IrAnalyze *ira, IrInstGen *value, uint64_t *out) { return ir_resolve_unsigned(ira, value, ira->codegen->builtin_types.entry_usize, out); } static bool ir_resolve_bool(IrAnalyze *ira, IrInstGen *value, bool *out) { if (type_is_invalid(value->value->type)) return false; IrInstGen *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_bool); if (type_is_invalid(casted_value->value->type)) return false; ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return false; *out = const_val->data.x_bool; return true; } static bool ir_resolve_comptime(IrAnalyze *ira, IrInstGen *value, bool *out) { if (!value) { *out = false; return true; } return ir_resolve_bool(ira, value, out); } static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstGen *value, AtomicOrder *out) { if (type_is_invalid(value->value->type)) return false; ZigType *atomic_order_type = get_builtin_type(ira->codegen, "AtomicOrder"); IrInstGen *casted_value = ir_implicit_cast(ira, value, atomic_order_type); if (type_is_invalid(casted_value->value->type)) return false; ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return false; *out = (AtomicOrder)bigint_as_u32(&const_val->data.x_enum_tag); return true; } static bool ir_resolve_atomic_rmw_op(IrAnalyze *ira, IrInstGen *value, AtomicRmwOp *out) { if (type_is_invalid(value->value->type)) return false; ZigType *atomic_rmw_op_type = get_builtin_type(ira->codegen, "AtomicRmwOp"); IrInstGen *casted_value = ir_implicit_cast(ira, value, atomic_rmw_op_type); if (type_is_invalid(casted_value->value->type)) return false; ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return false; *out = (AtomicRmwOp)bigint_as_u32(&const_val->data.x_enum_tag); return true; } static bool ir_resolve_global_linkage(IrAnalyze *ira, IrInstGen *value, GlobalLinkageId *out) { if (type_is_invalid(value->value->type)) return false; ZigType *global_linkage_type = get_builtin_type(ira->codegen, "GlobalLinkage"); IrInstGen *casted_value = ir_implicit_cast(ira, value, global_linkage_type); if (type_is_invalid(casted_value->value->type)) return false; ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return false; *out = (GlobalLinkageId)bigint_as_u32(&const_val->data.x_enum_tag); return true; } static bool ir_resolve_float_mode(IrAnalyze *ira, IrInstGen *value, FloatMode *out) { if (type_is_invalid(value->value->type)) return false; ZigType *float_mode_type = get_builtin_type(ira->codegen, "FloatMode"); IrInstGen *casted_value = ir_implicit_cast(ira, value, float_mode_type); if (type_is_invalid(casted_value->value->type)) return false; ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return false; *out = (FloatMode)bigint_as_u32(&const_val->data.x_enum_tag); return true; } static Buf *ir_resolve_str(IrAnalyze *ira, IrInstGen *value) { if (type_is_invalid(value->value->type)) return nullptr; ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, true, false, PtrLenUnknown, 0, 0, 0, false); ZigType *str_type = get_slice_type(ira->codegen, ptr_type); IrInstGen *casted_value = ir_implicit_cast(ira, value, str_type); if (type_is_invalid(casted_value->value->type)) return nullptr; ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return nullptr; ZigValue *ptr_field = const_val->data.x_struct.fields[slice_ptr_index]; ZigValue *len_field = const_val->data.x_struct.fields[slice_len_index]; assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray); ZigValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val; expand_undef_array(ira->codegen, array_val); size_t len = bigint_as_usize(&len_field->data.x_bigint); if (array_val->data.x_array.special == ConstArraySpecialBuf && len == buf_len(array_val->data.x_array.data.s_buf)) { return array_val->data.x_array.data.s_buf; } Buf *result = buf_alloc(); buf_resize(result, len); for (size_t i = 0; i < len; i += 1) { size_t new_index = ptr_field->data.x_ptr.data.base_array.elem_index + i; ZigValue *char_val = &array_val->data.x_array.data.s_none.elements[new_index]; if (char_val->special == ConstValSpecialUndef) { ir_add_error(ira, &casted_value->base, buf_sprintf("use of undefined value")); return nullptr; } uint64_t big_c = bigint_as_u64(&char_val->data.x_bigint); assert(big_c <= UINT8_MAX); uint8_t c = (uint8_t)big_c; buf_ptr(result)[i] = c; } return result; } static IrInstGen *ir_analyze_instruction_add_implicit_return_type(IrAnalyze *ira, IrInstSrcAddImplicitReturnType *instruction) { IrInstGen *value = instruction->value->child; if (type_is_invalid(value->value->type)) return ir_unreach_error(ira); if (instruction->result_loc_ret == nullptr || !instruction->result_loc_ret->implicit_return_type_done) { ira->src_implicit_return_type_list.append(value); } return ir_const_void(ira, &instruction->base.base); } static IrInstGen *ir_analyze_instruction_return(IrAnalyze *ira, IrInstSrcReturn *instruction) { if (instruction->operand == nullptr) { // result location mechanism took care of it. IrInstGen *result = ir_build_return_gen(ira, &instruction->base.base, nullptr); return ir_finish_anal(ira, result); } IrInstGen *operand = instruction->operand->child; if (type_is_invalid(operand->value->type)) return ir_unreach_error(ira); IrInstGen *casted_operand = ir_implicit_cast(ira, operand, ira->explicit_return_type); if (type_is_invalid(casted_operand->value->type)) { AstNode *source_node = ira->explicit_return_type_source_node; if (source_node != nullptr) { ErrorMsg *msg = ira->codegen->errors.last(); add_error_note(ira->codegen, msg, source_node, buf_sprintf("return type declared here")); } return ir_unreach_error(ira); } if (!instr_is_comptime(operand) && ira->explicit_return_type != nullptr && handle_is_ptr(ira->codegen, ira->explicit_return_type)) { // result location mechanism took care of it. IrInstGen *result = ir_build_return_gen(ira, &instruction->base.base, nullptr); return ir_finish_anal(ira, result); } if (casted_operand->value->special == ConstValSpecialRuntime && casted_operand->value->type->id == ZigTypeIdPointer && casted_operand->value->data.rh_ptr == RuntimeHintPtrStack) { ir_add_error(ira, &instruction->operand->base, buf_sprintf("function returns address of local variable")); return ir_unreach_error(ira); } IrInstGen *result = ir_build_return_gen(ira, &instruction->base.base, casted_operand); return ir_finish_anal(ira, result); } static IrInstGen *ir_analyze_instruction_const(IrAnalyze *ira, IrInstSrcConst *instruction) { return ir_const_move(ira, &instruction->base.base, instruction->value); } static IrInstGen *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstSrcBinOp *bin_op_instruction) { IrInstGen *op1 = bin_op_instruction->op1->child; if (type_is_invalid(op1->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *op2 = bin_op_instruction->op2->child; if (type_is_invalid(op2->value->type)) return ira->codegen->invalid_inst_gen; ZigType *bool_type = ira->codegen->builtin_types.entry_bool; IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, bool_type); if (type_is_invalid(casted_op1->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, bool_type); if (type_is_invalid(casted_op2->value->type)) return ira->codegen->invalid_inst_gen; if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); if (op1_val == nullptr) return ira->codegen->invalid_inst_gen; ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->invalid_inst_gen; assert(casted_op1->value->type->id == ZigTypeIdBool); assert(casted_op2->value->type->id == ZigTypeIdBool); bool result_bool; if (bin_op_instruction->op_id == IrBinOpBoolOr) { result_bool = op1_val->data.x_bool || op2_val->data.x_bool; } else if (bin_op_instruction->op_id == IrBinOpBoolAnd) { result_bool = op1_val->data.x_bool && op2_val->data.x_bool; } else { zig_unreachable(); } return ir_const_bool(ira, &bin_op_instruction->base.base, result_bool); } return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, bool_type, bin_op_instruction->op_id, casted_op1, casted_op2, bin_op_instruction->safety_check_on); } static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) { switch (op_id) { case IrBinOpCmpEq: return cmp == CmpEQ; case IrBinOpCmpNotEq: return cmp != CmpEQ; case IrBinOpCmpLessThan: return cmp == CmpLT; case IrBinOpCmpGreaterThan: return cmp == CmpGT; case IrBinOpCmpLessOrEq: return cmp != CmpGT; case IrBinOpCmpGreaterOrEq: return cmp != CmpLT; default: zig_unreachable(); } } static void set_optional_value_to_null(ZigValue *val) { assert(val->special == ConstValSpecialStatic); if (val->type->id == ZigTypeIdNull) return; // nothing to do assert(val->type->id == ZigTypeIdOptional); if (get_src_ptr_type(val->type) != nullptr) { val->data.x_ptr.special = ConstPtrSpecialNull; } else if (is_opt_err_set(val->type)) { val->data.x_err_set = nullptr; } else { val->data.x_optional = nullptr; } } static void set_optional_payload(ZigValue *opt_val, ZigValue *payload) { assert(opt_val->special == ConstValSpecialStatic); assert(opt_val->type->id == ZigTypeIdOptional); if (payload == nullptr) { set_optional_value_to_null(opt_val); } else if (is_opt_err_set(opt_val->type)) { assert(payload->type->id == ZigTypeIdErrorSet); opt_val->data.x_err_set = payload->data.x_err_set; } else { opt_val->data.x_optional = payload; } } static IrInstGen *ir_evaluate_bin_op_cmp(IrAnalyze *ira, ZigType *resolved_type, ZigValue *op1_val, ZigValue *op2_val, IrInst *source_instr, IrBinOp op_id, bool one_possible_value) { if (op1_val->special == ConstValSpecialUndef || op2_val->special == ConstValSpecialUndef) return ir_const_undef(ira, source_instr, resolved_type); if (resolved_type->id == ZigTypeIdPointer && op_id != IrBinOpCmpEq && op_id != IrBinOpCmpNotEq) { if ((op1_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr || op1_val->data.x_ptr.special == ConstPtrSpecialNull) && (op2_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr || op2_val->data.x_ptr.special == ConstPtrSpecialNull)) { uint64_t op1_addr = op1_val->data.x_ptr.special == ConstPtrSpecialNull ? 0 : op1_val->data.x_ptr.data.hard_coded_addr.addr; uint64_t op2_addr = op2_val->data.x_ptr.special == ConstPtrSpecialNull ? 0 : op2_val->data.x_ptr.data.hard_coded_addr.addr; Cmp cmp_result; if (op1_addr > op2_addr) { cmp_result = CmpGT; } else if (op1_addr < op2_addr) { cmp_result = CmpLT; } else { cmp_result = CmpEQ; } bool answer = resolve_cmp_op_id(op_id, cmp_result); return ir_const_bool(ira, source_instr, answer); } } else { bool are_equal = one_possible_value || const_values_equal(ira->codegen, op1_val, op2_val); bool answer; if (op_id == IrBinOpCmpEq) { answer = are_equal; } else if (op_id == IrBinOpCmpNotEq) { answer = !are_equal; } else { zig_unreachable(); } return ir_const_bool(ira, source_instr, answer); } zig_unreachable(); } static IrInstGen *ir_try_evaluate_bin_op_cmp_const(IrAnalyze *ira, IrInst *source_instr, IrInstGen *op1, IrInstGen *op2, ZigType *resolved_type, IrBinOp op_id) { assert(op1->value->type == resolved_type && op2->value->type == resolved_type); bool one_possible_value; switch (type_has_one_possible_value(ira->codegen, resolved_type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_inst_gen; case OnePossibleValueYes: one_possible_value = true; break; case OnePossibleValueNo: one_possible_value = false; break; } if (one_possible_value || (instr_is_comptime(op1) && instr_is_comptime(op2))) { ZigValue *op1_val = one_possible_value ? op1->value : ir_resolve_const(ira, op1, UndefBad); if (op1_val == nullptr) return ira->codegen->invalid_inst_gen; ZigValue *op2_val = one_possible_value ? op2->value : ir_resolve_const(ira, op2, UndefBad); if (op2_val == nullptr) return ira->codegen->invalid_inst_gen; if (resolved_type->id != ZigTypeIdVector) return ir_evaluate_bin_op_cmp(ira, resolved_type, op1_val, op2_val, source_instr, op_id, one_possible_value); IrInstGen *result = ir_const(ira, source_instr, get_vector_type(ira->codegen, resolved_type->data.vector.len, ira->codegen->builtin_types.entry_bool)); result->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(resolved_type->data.vector.len); expand_undef_array(ira->codegen, result->value); for (size_t i = 0;i < resolved_type->data.vector.len;i++) { IrInstGen *cur_res = ir_evaluate_bin_op_cmp(ira, resolved_type->data.vector.elem_type, &op1_val->data.x_array.data.s_none.elements[i], &op2_val->data.x_array.data.s_none.elements[i], source_instr, op_id, one_possible_value); copy_const_val(ira->codegen, &result->value->data.x_array.data.s_none.elements[i], cur_res->value); } return result; } else { return nullptr; } } // Returns ErrorNotLazy when the value cannot be determined static Error lazy_cmp_zero(CodeGen *codegen, AstNode *source_node, ZigValue *val, Cmp *result) { Error err; switch (type_has_one_possible_value(codegen, val->type)) { case OnePossibleValueInvalid: return ErrorSemanticAnalyzeFail; case OnePossibleValueNo: break; case OnePossibleValueYes: switch (val->type->id) { case ZigTypeIdInt: src_assert(val->type->data.integral.bit_count == 0, source_node); *result = CmpEQ; return ErrorNone; case ZigTypeIdUndefined: return ErrorNotLazy; default: zig_unreachable(); } } switch (val->special) { case ConstValSpecialRuntime: case ConstValSpecialUndef: return ErrorNotLazy; case ConstValSpecialStatic: switch (val->type->id) { case ZigTypeIdComptimeInt: case ZigTypeIdInt: *result = bigint_cmp_zero(&val->data.x_bigint); return ErrorNone; case ZigTypeIdComptimeFloat: case ZigTypeIdFloat: if (float_is_nan(val)) return ErrorNotLazy; *result = float_cmp_zero(val); return ErrorNone; default: return ErrorNotLazy; } case ConstValSpecialLazy: switch (val->data.x_lazy->id) { case LazyValueIdInvalid: zig_unreachable(); case LazyValueIdAlignOf: { LazyValueAlignOf *lazy_align_of = reinterpret_cast(val->data.x_lazy); IrAnalyze *ira = lazy_align_of->ira; bool is_zero_bits; if ((err = type_val_resolve_zero_bits(ira->codegen, lazy_align_of->target_type->value, nullptr, nullptr, &is_zero_bits))) { return err; } *result = is_zero_bits ? CmpEQ : CmpGT; return ErrorNone; } case LazyValueIdSizeOf: { LazyValueSizeOf *lazy_size_of = reinterpret_cast(val->data.x_lazy); IrAnalyze *ira = lazy_size_of->ira; bool is_zero_bits; if ((err = type_val_resolve_zero_bits(ira->codegen, lazy_size_of->target_type->value, nullptr, nullptr, &is_zero_bits))) { return err; } *result = is_zero_bits ? CmpEQ : CmpGT; return ErrorNone; } default: return ErrorNotLazy; } } zig_unreachable(); } static ErrorMsg *ir_eval_bin_op_cmp_scalar(IrAnalyze *ira, IrInst* source_instr, ZigValue *op1_val, IrBinOp op_id, ZigValue *op2_val, ZigValue *out_val) { Error err; { // Before resolving the values, we special case comparisons against zero. These can often // be done without resolving lazy values, preventing potential dependency loops. Cmp op1_cmp_zero; if ((err = lazy_cmp_zero(ira->codegen, source_instr->source_node, op1_val, &op1_cmp_zero))) { if (err == ErrorNotLazy) goto never_mind_just_calculate_it_normally; return ira->codegen->trace_err; } Cmp op2_cmp_zero; if ((err = lazy_cmp_zero(ira->codegen, source_instr->source_node, op2_val, &op2_cmp_zero))) { if (err == ErrorNotLazy) goto never_mind_just_calculate_it_normally; return ira->codegen->trace_err; } bool can_cmp_zero = false; Cmp cmp_result; if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpEQ) { can_cmp_zero = true; cmp_result = CmpEQ; } else if (op1_cmp_zero == CmpGT && op2_cmp_zero == CmpEQ) { can_cmp_zero = true; cmp_result = CmpGT; } else if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpGT) { can_cmp_zero = true; cmp_result = CmpLT; } else if (op1_cmp_zero == CmpLT && op2_cmp_zero == CmpEQ) { can_cmp_zero = true; cmp_result = CmpLT; } else if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpLT) { can_cmp_zero = true; cmp_result = CmpGT; } else if (op1_cmp_zero == CmpLT && op2_cmp_zero == CmpGT) { can_cmp_zero = true; cmp_result = CmpLT; } else if (op1_cmp_zero == CmpGT && op2_cmp_zero == CmpLT) { can_cmp_zero = true; cmp_result = CmpGT; } if (can_cmp_zero) { bool answer = resolve_cmp_op_id(op_id, cmp_result); out_val->special = ConstValSpecialStatic; out_val->data.x_bool = answer; return nullptr; } } never_mind_just_calculate_it_normally: if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, source_instr->source_node, op1_val, UndefOk))) { return ira->codegen->trace_err; } if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, source_instr->source_node, op2_val, UndefOk))) { return ira->codegen->trace_err; } if (op1_val->special == ConstValSpecialUndef || op2_val->special == ConstValSpecialUndef || op1_val->type->id == ZigTypeIdUndefined || op2_val->type->id == ZigTypeIdUndefined) { out_val->special = ConstValSpecialUndef; return nullptr; } bool op1_is_float = op1_val->type->id == ZigTypeIdFloat || op1_val->type->id == ZigTypeIdComptimeFloat; bool op2_is_float = op2_val->type->id == ZigTypeIdFloat || op2_val->type->id == ZigTypeIdComptimeFloat; if (op1_is_float && op2_is_float) { if (float_is_nan(op1_val) || float_is_nan(op2_val)) { out_val->special = ConstValSpecialStatic; out_val->data.x_bool = op_id == IrBinOpCmpNotEq; return nullptr; } if (op1_val->type->id == ZigTypeIdComptimeFloat) { IrInstGen *tmp = ir_const_noval(ira, source_instr); tmp->value = op1_val; IrInstGen *casted = ir_implicit_cast(ira, tmp, op2_val->type); op1_val = casted->value; } else if (op2_val->type->id == ZigTypeIdComptimeFloat) { IrInstGen *tmp = ir_const_noval(ira, source_instr); tmp->value = op2_val; IrInstGen *casted = ir_implicit_cast(ira, tmp, op1_val->type); op2_val = casted->value; } Cmp cmp_result = float_cmp(op1_val, op2_val); out_val->special = ConstValSpecialStatic; out_val->data.x_bool = resolve_cmp_op_id(op_id, cmp_result); return nullptr; } bool op1_is_int = op1_val->type->id == ZigTypeIdInt || op1_val->type->id == ZigTypeIdComptimeInt; bool op2_is_int = op2_val->type->id == ZigTypeIdInt || op2_val->type->id == ZigTypeIdComptimeInt; if (op1_is_int && op2_is_int) { Cmp cmp_result = bigint_cmp(&op1_val->data.x_bigint, &op2_val->data.x_bigint); out_val->special = ConstValSpecialStatic; out_val->data.x_bool = resolve_cmp_op_id(op_id, cmp_result); return nullptr; } // Handle the case where one of the two operands is a fp value and the other // is an integer value ZigValue *float_val; if (op1_is_int && op2_is_float) { float_val = op2_val; } else if (op1_is_float && op2_is_int) { float_val = op1_val; } else { zig_unreachable(); } // They can never be equal if the fp value has a non-zero decimal part if (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq) { if (float_has_fraction(float_val)) { out_val->special = ConstValSpecialStatic; out_val->data.x_bool = op_id == IrBinOpCmpNotEq; return nullptr; } } // Cast the integer operand into a fp value to perform the comparison BigFloat op1_bigfloat; BigFloat op2_bigfloat; value_to_bigfloat(&op1_bigfloat, op1_val); value_to_bigfloat(&op2_bigfloat, op2_val); Cmp cmp_result = bigfloat_cmp(&op1_bigfloat, &op2_bigfloat); out_val->special = ConstValSpecialStatic; out_val->data.x_bool = resolve_cmp_op_id(op_id, cmp_result); return nullptr; } static IrInstGen *ir_analyze_bin_op_cmp_numeric(IrAnalyze *ira, IrInst *source_instr, IrInstGen *op1, IrInstGen *op2, IrBinOp op_id) { Error err; ZigType *scalar_result_type = ira->codegen->builtin_types.entry_bool; ZigType *result_type = scalar_result_type; ZigType *op1_scalar_type = op1->value->type; ZigType *op2_scalar_type = op2->value->type; if (op1->value->type->id == ZigTypeIdVector && op2->value->type->id == ZigTypeIdVector) { if (op1->value->type->data.vector.len != op2->value->type->data.vector.len) { ir_add_error(ira, source_instr, buf_sprintf("vector length mismatch: %" PRIu64 " and %" PRIu64, op1->value->type->data.vector.len, op2->value->type->data.vector.len)); return ira->codegen->invalid_inst_gen; } result_type = get_vector_type(ira->codegen, op1->value->type->data.vector.len, scalar_result_type); op1_scalar_type = op1->value->type->data.vector.elem_type; op2_scalar_type = op2->value->type->data.vector.elem_type; } else if (op1->value->type->id == ZigTypeIdVector || op2->value->type->id == ZigTypeIdVector) { ir_add_error(ira, source_instr, buf_sprintf("mixed scalar and vector operands to comparison operator: '%s' and '%s'", buf_ptr(&op1->value->type->name), buf_ptr(&op2->value->type->name))); return ira->codegen->invalid_inst_gen; } bool opv_op1; switch (type_has_one_possible_value(ira->codegen, op1->value->type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_inst_gen; case OnePossibleValueYes: opv_op1 = true; break; case OnePossibleValueNo: opv_op1 = false; break; } bool opv_op2; switch (type_has_one_possible_value(ira->codegen, op2->value->type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_inst_gen; case OnePossibleValueYes: opv_op2 = true; break; case OnePossibleValueNo: opv_op2 = false; break; } Cmp op1_cmp_zero; bool have_op1_cmp_zero = false; if ((err = lazy_cmp_zero(ira->codegen, source_instr->source_node, op1->value, &op1_cmp_zero))) { if (err != ErrorNotLazy) return ira->codegen->invalid_inst_gen; } else { have_op1_cmp_zero = true; } Cmp op2_cmp_zero; bool have_op2_cmp_zero = false; if ((err = lazy_cmp_zero(ira->codegen, source_instr->source_node, op2->value, &op2_cmp_zero))) { if (err != ErrorNotLazy) return ira->codegen->invalid_inst_gen; } else { have_op2_cmp_zero = true; } if (((opv_op1 || instr_is_comptime(op1)) && (opv_op2 || instr_is_comptime(op2))) || (have_op1_cmp_zero && have_op2_cmp_zero)) { IrInstGen *result_instruction = ir_const(ira, source_instr, result_type); ZigValue *out_val = result_instruction->value; if (result_type->id == ZigTypeIdVector) { size_t len = result_type->data.vector.len; expand_undef_array(ira->codegen, op1->value); expand_undef_array(ira->codegen, op2->value); out_val->special = ConstValSpecialUndef; expand_undef_array(ira->codegen, out_val); for (size_t i = 0; i < len; i += 1) { ZigValue *scalar_op1_val = &op1->value->data.x_array.data.s_none.elements[i]; ZigValue *scalar_op2_val = &op2->value->data.x_array.data.s_none.elements[i]; ZigValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; assert(scalar_out_val->type == scalar_result_type); ErrorMsg *msg = ir_eval_bin_op_cmp_scalar(ira, source_instr, scalar_op1_val, op_id, scalar_op2_val, scalar_out_val); if (msg != nullptr) { add_error_note(ira->codegen, msg, source_instr->source_node, buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i)); return ira->codegen->invalid_inst_gen; } } out_val->type = result_type; out_val->special = ConstValSpecialStatic; } else { if (ir_eval_bin_op_cmp_scalar(ira, source_instr, op1->value, op_id, op2->value, out_val) != nullptr) { return ira->codegen->invalid_inst_gen; } } return result_instruction; } // If one operand has a comptime-known comparison with 0, and the other operand is unsigned, we might // know the answer, depending on the operator. // TODO make this work with vectors if (have_op1_cmp_zero && op2_scalar_type->id == ZigTypeIdInt && !op2_scalar_type->data.integral.is_signed) { if (op1_cmp_zero == CmpEQ) { // 0 <= unsigned_x // true // 0 > unsigned_x // false switch (op_id) { case IrBinOpCmpLessOrEq: return ir_const_bool(ira, source_instr, true); case IrBinOpCmpGreaterThan: return ir_const_bool(ira, source_instr, false); default: break; } } else if (op1_cmp_zero == CmpLT) { // -1 != unsigned_x // true // -1 <= unsigned_x // true // -1 < unsigned_x // true // -1 == unsigned_x // false // -1 >= unsigned_x // false // -1 > unsigned_x // false switch (op_id) { case IrBinOpCmpNotEq: case IrBinOpCmpLessOrEq: case IrBinOpCmpLessThan: return ir_const_bool(ira, source_instr, true); case IrBinOpCmpEq: case IrBinOpCmpGreaterOrEq: case IrBinOpCmpGreaterThan: return ir_const_bool(ira, source_instr, false); default: break; } } } if (have_op2_cmp_zero && op1_scalar_type->id == ZigTypeIdInt && !op1_scalar_type->data.integral.is_signed) { if (op2_cmp_zero == CmpEQ) { // unsigned_x < 0 // false // unsigned_x >= 0 // true switch (op_id) { case IrBinOpCmpLessThan: return ir_const_bool(ira, source_instr, false); case IrBinOpCmpGreaterOrEq: return ir_const_bool(ira, source_instr, true); default: break; } } else if (op2_cmp_zero == CmpLT) { // unsigned_x != -1 // true // unsigned_x >= -1 // true // unsigned_x > -1 // true // unsigned_x == -1 // false // unsigned_x < -1 // false // unsigned_x <= -1 // false switch (op_id) { case IrBinOpCmpNotEq: case IrBinOpCmpGreaterOrEq: case IrBinOpCmpGreaterThan: return ir_const_bool(ira, source_instr, true); case IrBinOpCmpEq: case IrBinOpCmpLessThan: case IrBinOpCmpLessOrEq: return ir_const_bool(ira, source_instr, false); default: break; } } } // It must be a runtime comparison. // For floats, emit a float comparison instruction. bool op1_is_float = op1_scalar_type->id == ZigTypeIdFloat || op1_scalar_type->id == ZigTypeIdComptimeFloat; bool op2_is_float = op2_scalar_type->id == ZigTypeIdFloat || op2_scalar_type->id == ZigTypeIdComptimeFloat; if (op1_is_float && op2_is_float) { // Implicit cast the smaller one to the larger one. ZigType *dest_scalar_type; if (op1_scalar_type->id == ZigTypeIdComptimeFloat) { dest_scalar_type = op2_scalar_type; } else if (op2_scalar_type->id == ZigTypeIdComptimeFloat) { dest_scalar_type = op1_scalar_type; } else if (op1_scalar_type->data.floating.bit_count >= op2_scalar_type->data.floating.bit_count) { dest_scalar_type = op1_scalar_type; } else { dest_scalar_type = op2_scalar_type; } ZigType *dest_type = (result_type->id == ZigTypeIdVector) ? get_vector_type(ira->codegen, result_type->data.vector.len, dest_scalar_type) : dest_scalar_type; IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, dest_type); IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, dest_type); if (type_is_invalid(casted_op1->value->type) || type_is_invalid(casted_op2->value->type)) return ira->codegen->invalid_inst_gen; return ir_build_bin_op_gen(ira, source_instr, result_type, op_id, casted_op1, casted_op2, true); } // For mixed unsigned integer sizes, implicit cast both operands to the larger integer. // For mixed signed and unsigned integers, implicit cast both operands to a signed // integer with + 1 bit. // For mixed floats and integers, extract the integer part from the float, cast that to // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float, // add/subtract 1. bool dest_int_is_signed = false; if (have_op1_cmp_zero) { if (op1_cmp_zero == CmpLT) dest_int_is_signed = true; } else if (op1_is_float) { dest_int_is_signed = true; } else if (op1_scalar_type->id == ZigTypeIdInt && op1_scalar_type->data.integral.is_signed) { dest_int_is_signed = true; } if (have_op2_cmp_zero) { if (op2_cmp_zero == CmpLT) dest_int_is_signed = true; } else if (op2_is_float) { dest_int_is_signed = true; } else if (op2->value->type->id == ZigTypeIdInt && op2->value->type->data.integral.is_signed) { dest_int_is_signed = true; } ZigType *dest_float_type = nullptr; uint32_t op1_bits; if (instr_is_comptime(op1)) { ZigValue *op1_val = ir_resolve_const(ira, op1, UndefOk); if (op1_val == nullptr) return ira->codegen->invalid_inst_gen; if (op1_val->special == ConstValSpecialUndef) return ir_const_undef(ira, source_instr, ira->codegen->builtin_types.entry_bool); if (result_type->id == ZigTypeIdVector) { ir_add_error(ira, &op1->base, buf_sprintf("compiler bug: TODO: support comptime vector here")); return ira->codegen->invalid_inst_gen; } bool is_unsigned; if (op1_is_float) { BigInt bigint = {}; float_init_bigint(&bigint, op1_val); Cmp zcmp = float_cmp_zero(op1_val); if (float_has_fraction(op1_val)) { if (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq) { return ir_const_bool(ira, source_instr, op_id == IrBinOpCmpNotEq); } if (zcmp == CmpLT) { bigint_decr(&bigint); } else { bigint_incr(&bigint); } } op1_bits = bigint_bits_needed(&bigint); is_unsigned = zcmp != CmpLT; } else { op1_bits = bigint_bits_needed(&op1_val->data.x_bigint); is_unsigned = bigint_cmp_zero(&op1_val->data.x_bigint) != CmpLT; } if (is_unsigned && dest_int_is_signed) { op1_bits += 1; } } else if (op1_is_float) { dest_float_type = op1_scalar_type; } else { ir_assert(op1_scalar_type->id == ZigTypeIdInt, source_instr); op1_bits = op1_scalar_type->data.integral.bit_count; if (!op1_scalar_type->data.integral.is_signed && dest_int_is_signed) { op1_bits += 1; } } uint32_t op2_bits; if (instr_is_comptime(op2)) { ZigValue *op2_val = ir_resolve_const(ira, op2, UndefOk); if (op2_val == nullptr) return ira->codegen->invalid_inst_gen; if (op2_val->special == ConstValSpecialUndef) return ir_const_undef(ira, source_instr, ira->codegen->builtin_types.entry_bool); if (result_type->id == ZigTypeIdVector) { ir_add_error(ira, &op2->base, buf_sprintf("compiler bug: TODO: support comptime vector here")); return ira->codegen->invalid_inst_gen; } bool is_unsigned; if (op2_is_float) { BigInt bigint = {}; float_init_bigint(&bigint, op2_val); Cmp zcmp = float_cmp_zero(op2_val); if (float_has_fraction(op2_val)) { if (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq) { return ir_const_bool(ira, source_instr, op_id == IrBinOpCmpNotEq); } if (zcmp == CmpLT) { bigint_decr(&bigint); } else { bigint_incr(&bigint); } } op2_bits = bigint_bits_needed(&bigint); is_unsigned = zcmp != CmpLT; } else { op2_bits = bigint_bits_needed(&op2_val->data.x_bigint); is_unsigned = bigint_cmp_zero(&op2_val->data.x_bigint) != CmpLT; } if (is_unsigned && dest_int_is_signed) { op2_bits += 1; } } else if (op2_is_float) { dest_float_type = op2_scalar_type; } else { ir_assert(op2_scalar_type->id == ZigTypeIdInt, source_instr); op2_bits = op2_scalar_type->data.integral.bit_count; if (!op2_scalar_type->data.integral.is_signed && dest_int_is_signed) { op2_bits += 1; } } ZigType *dest_scalar_type = (dest_float_type == nullptr) ? get_int_type(ira->codegen, dest_int_is_signed, (op1_bits > op2_bits) ? op1_bits : op2_bits) : dest_float_type; ZigType *dest_type = (result_type->id == ZigTypeIdVector) ? get_vector_type(ira->codegen, result_type->data.vector.len, dest_scalar_type) : dest_scalar_type; IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, dest_type); if (type_is_invalid(casted_op1->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, dest_type); if (type_is_invalid(casted_op2->value->type)) return ira->codegen->invalid_inst_gen; return ir_build_bin_op_gen(ira, source_instr, result_type, op_id, casted_op1, casted_op2, true); } static bool type_is_self_comparable(ZigType *ty, bool is_equality_cmp) { if (type_is_numeric(ty)) { return true; } switch (ty->id) { case ZigTypeIdInvalid: zig_unreachable(); case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdInt: case ZigTypeIdFloat: zig_unreachable(); // handled with the type_is_numeric check above case ZigTypeIdVector: // Not every case is handled by the type_is_numeric check above, // vectors of bool trigger this code path case ZigTypeIdBool: case ZigTypeIdMetaType: case ZigTypeIdVoid: case ZigTypeIdErrorSet: case ZigTypeIdFn: case ZigTypeIdOpaque: case ZigTypeIdBoundFn: case ZigTypeIdEnum: case ZigTypeIdEnumLiteral: case ZigTypeIdAnyFrame: return is_equality_cmp; case ZigTypeIdPointer: return is_equality_cmp || (ty->data.pointer.ptr_len == PtrLenC); case ZigTypeIdUnreachable: case ZigTypeIdArray: case ZigTypeIdStruct: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdErrorUnion: case ZigTypeIdUnion: case ZigTypeIdFnFrame: return false; case ZigTypeIdOptional: return is_equality_cmp && get_src_ptr_type(ty) != nullptr; } zig_unreachable(); } static IrInstGen *ir_try_evaluate_cmp_optional_non_optional_const(IrAnalyze *ira, IrInst *source_instr, ZigType *child_type, IrInstGen *optional, IrInstGen *non_optional, IrBinOp op_id) { assert(optional->value->type->id == ZigTypeIdOptional); assert(optional->value->type->data.maybe.child_type == non_optional->value->type); assert(non_optional->value->type == child_type); assert(op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); if (instr_is_comptime(optional) && instr_is_comptime(non_optional)) { ZigValue *optional_val = ir_resolve_const(ira, optional, UndefBad); if (!optional_val) { return ira->codegen->invalid_inst_gen; } ZigValue *non_optional_val = ir_resolve_const(ira, non_optional, UndefBad); if (!non_optional_val) { return ira->codegen->invalid_inst_gen; } if (!optional_value_is_null(optional_val)) { IrInstGen *optional_unwrapped = ir_analyze_optional_value_payload_value(ira, source_instr, optional, false); if (type_is_invalid(optional_unwrapped->value->type)) { return ira->codegen->invalid_inst_gen; } IrInstGen *ret = ir_try_evaluate_bin_op_cmp_const(ira, source_instr, optional_unwrapped, non_optional, child_type, op_id); assert(ret != nullptr); return ret; } return ir_const_bool(ira, source_instr, (op_id != IrBinOpCmpEq)); } else { return nullptr; } } static IrInstGen *ir_evaluate_cmp_optional_non_optional(IrAnalyze *ira, IrInst *source_instr, ZigType *child_type, IrInstGen *optional, IrInstGen *non_optional, IrBinOp op_id) { assert(optional->value->type->id == ZigTypeIdOptional); assert(optional->value->type->data.maybe.child_type == non_optional->value->type); assert(non_optional->value->type == child_type); assert(op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); ZigType *result_type = ira->codegen->builtin_types.entry_bool; ir_append_basic_block_gen(&ira->new_irb, ira->new_irb.current_basic_block); IrBasicBlockGen *null_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptionalOptionalNull"); IrBasicBlockGen *non_null_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptionalOptionalNotNull"); IrBasicBlockGen *end_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptionalEnd"); IrInstGen *is_non_null = ir_build_test_non_null_gen(ira, source_instr, optional); ir_build_cond_br_gen(ira, source_instr, is_non_null, non_null_block, null_block); ir_set_cursor_at_end_and_append_block_gen(&ira->new_irb, non_null_block); IrInstGen *optional_unwrapped = ir_analyze_optional_value_payload_value(ira, source_instr, optional, false); if (type_is_invalid(optional_unwrapped->value->type)) { return ira->codegen->invalid_inst_gen; } IrInstGen *non_null_cmp_result = ir_build_bin_op_gen(ira, source_instr, result_type, op_id, optional_unwrapped, non_optional, false); // safety check unnecessary for comparison operators ir_build_br_gen(ira, source_instr, end_block); ir_set_cursor_at_end_and_append_block_gen(&ira->new_irb, null_block); IrInstGen *null_result = ir_const_bool(ira, source_instr, (op_id != IrBinOpCmpEq)); ir_build_br_gen(ira, source_instr, end_block); ir_set_cursor_at_end_gen(&ira->new_irb, end_block); int incoming_count = 2; IrBasicBlockGen **incoming_blocks = heap::c_allocator.allocate_nonzero(incoming_count); incoming_blocks[0] = null_block; incoming_blocks[1] = non_null_block; IrInstGen **incoming_values = heap::c_allocator.allocate_nonzero(incoming_count); incoming_values[0] = null_result; incoming_values[1] = non_null_cmp_result; return ir_build_phi_gen(ira, source_instr, incoming_count, incoming_blocks, incoming_values, result_type); } static IrInstGen *ir_analyze_cmp_optional_non_optional(IrAnalyze *ira, IrInst *source_instr, IrInstGen *op1, IrInstGen *op2, IrInstGen *optional, IrBinOp op_id) { assert(op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); assert(optional->value->type->id == ZigTypeIdOptional); assert(get_src_ptr_type(optional->value->type) == nullptr); IrInstGen *non_optional; if (op1 == optional) { non_optional = op2; } else if (op2 == optional) { non_optional = op1; } else { zig_unreachable(); } ZigType *child_type = optional->value->type->data.maybe.child_type; bool child_type_matches = (child_type == non_optional->value->type); if (!child_type_matches || !type_is_self_comparable(child_type, true)) { ErrorMsg *msg = ir_add_error_node(ira, source_instr->source_node, buf_sprintf("cannot compare types '%s' and '%s'", buf_ptr(&op1->value->type->name), buf_ptr(&op2->value->type->name))); if (!child_type_matches) { if (non_optional->value->type->id == ZigTypeIdOptional) { add_error_note(ira->codegen, msg, source_instr->source_node, buf_sprintf( "optional to optional comparison is only supported for optional pointer types")); } else { add_error_note(ira->codegen, msg, source_instr->source_node, buf_sprintf("optional child type '%s' must be the same as non-optional type '%s'", buf_ptr(&child_type->name), buf_ptr(&non_optional->value->type->name))); } } else { add_error_note(ira->codegen, msg, source_instr->source_node, buf_sprintf("operator not supported for type '%s'", buf_ptr(&child_type->name))); } return ira->codegen->invalid_inst_gen; } if (child_type->id == ZigTypeIdVector) { ir_add_error_node(ira, source_instr->source_node, buf_sprintf("TODO add comparison of optional vector")); return ira->codegen->invalid_inst_gen; } if (IrInstGen *const_result = ir_try_evaluate_cmp_optional_non_optional_const(ira, source_instr, child_type, optional, non_optional, op_id)) { return const_result; } return ir_evaluate_cmp_optional_non_optional(ira, source_instr, child_type, optional, non_optional, op_id); } static IrInstGen *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstSrcBinOp *bin_op_instruction) { IrInstGen *op1 = bin_op_instruction->op1->child; if (type_is_invalid(op1->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *op2 = bin_op_instruction->op2->child; if (type_is_invalid(op2->value->type)) return ira->codegen->invalid_inst_gen; AstNode *source_node = bin_op_instruction->base.base.source_node; IrBinOp op_id = bin_op_instruction->op_id; bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); if (is_equality_cmp && op1->value->type->id == ZigTypeIdNull && op2->value->type->id == ZigTypeIdNull) { return ir_const_bool(ira, &bin_op_instruction->base.base, (op_id == IrBinOpCmpEq)); } else if (is_equality_cmp && ((op1->value->type->id == ZigTypeIdNull && op2->value->type->id == ZigTypeIdOptional) || (op2->value->type->id == ZigTypeIdNull && op1->value->type->id == ZigTypeIdOptional))) { IrInstGen *maybe_op; if (op1->value->type->id == ZigTypeIdNull) { maybe_op = op2; } else if (op2->value->type->id == ZigTypeIdNull) { maybe_op = op1; } else { zig_unreachable(); } if (instr_is_comptime(maybe_op)) { ZigValue *maybe_val = ir_resolve_const(ira, maybe_op, UndefBad); if (!maybe_val) return ira->codegen->invalid_inst_gen; bool is_null = optional_value_is_null(maybe_val); bool bool_result = (op_id == IrBinOpCmpEq) ? is_null : !is_null; return ir_const_bool(ira, &bin_op_instruction->base.base, bool_result); } IrInstGen *is_non_null = ir_build_test_non_null_gen(ira, &bin_op_instruction->base.base, maybe_op); if (op_id == IrBinOpCmpEq) { return ir_build_bool_not_gen(ira, &bin_op_instruction->base.base, is_non_null); } else { return is_non_null; } } else if (is_equality_cmp && ((op1->value->type->id == ZigTypeIdNull && op2->value->type->id == ZigTypeIdPointer && op2->value->type->data.pointer.ptr_len == PtrLenC) || (op2->value->type->id == ZigTypeIdNull && op1->value->type->id == ZigTypeIdPointer && op1->value->type->data.pointer.ptr_len == PtrLenC))) { IrInstGen *c_ptr_op; if (op1->value->type->id == ZigTypeIdNull) { c_ptr_op = op2; } else if (op2->value->type->id == ZigTypeIdNull) { c_ptr_op = op1; } else { zig_unreachable(); } if (instr_is_comptime(c_ptr_op)) { ZigValue *c_ptr_val = ir_resolve_const(ira, c_ptr_op, UndefOk); if (!c_ptr_val) return ira->codegen->invalid_inst_gen; if (c_ptr_val->special == ConstValSpecialUndef) return ir_const_undef(ira, &bin_op_instruction->base.base, ira->codegen->builtin_types.entry_bool); bool is_null = c_ptr_val->data.x_ptr.special == ConstPtrSpecialNull || (c_ptr_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && c_ptr_val->data.x_ptr.data.hard_coded_addr.addr == 0); bool bool_result = (op_id == IrBinOpCmpEq) ? is_null : !is_null; return ir_const_bool(ira, &bin_op_instruction->base.base, bool_result); } IrInstGen *is_non_null = ir_build_test_non_null_gen(ira, &bin_op_instruction->base.base, c_ptr_op); if (op_id == IrBinOpCmpEq) { return ir_build_bool_not_gen(ira, &bin_op_instruction->base.base, is_non_null); } else { return is_non_null; } } else if (is_equality_cmp && (op1->value->type->id == ZigTypeIdOptional && get_src_ptr_type(op1->value->type) == nullptr)) { return ir_analyze_cmp_optional_non_optional(ira, &bin_op_instruction->base.base, op1, op2, op1, op_id); } else if(is_equality_cmp && (op2->value->type->id == ZigTypeIdOptional && get_src_ptr_type(op2->value->type) == nullptr)) { return ir_analyze_cmp_optional_non_optional(ira, &bin_op_instruction->base.base, op1, op2, op2, op_id); } else if (op1->value->type->id == ZigTypeIdNull || op2->value->type->id == ZigTypeIdNull) { ZigType *non_null_type = (op1->value->type->id == ZigTypeIdNull) ? op2->value->type : op1->value->type; ir_add_error_node(ira, source_node, buf_sprintf("comparison of '%s' with null", buf_ptr(&non_null_type->name))); return ira->codegen->invalid_inst_gen; } else if (is_equality_cmp && ( (op1->value->type->id == ZigTypeIdEnumLiteral && op2->value->type->id == ZigTypeIdUnion) || (op2->value->type->id == ZigTypeIdEnumLiteral && op1->value->type->id == ZigTypeIdUnion))) { // Support equality comparison between a union's tag value and a enum literal IrInstGen *union_val = op1->value->type->id == ZigTypeIdUnion ? op1 : op2; IrInstGen *enum_val = op1->value->type->id == ZigTypeIdUnion ? op2 : op1; if (!is_tagged_union(union_val->value->type)) { ErrorMsg *msg = ir_add_error_node(ira, source_node, buf_sprintf("comparison of union and enum literal is only valid for tagged union types")); add_error_note(ira->codegen, msg, union_val->value->type->data.unionation.decl_node, buf_sprintf("type %s is not a tagged union", buf_ptr(&union_val->value->type->name))); return ira->codegen->invalid_inst_gen; } ZigType *tag_type = union_val->value->type->data.unionation.tag_type; assert(tag_type != nullptr); IrInstGen *casted_union = ir_implicit_cast(ira, union_val, tag_type); if (type_is_invalid(casted_union->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_val = ir_implicit_cast(ira, enum_val, tag_type); if (type_is_invalid(casted_val->value->type)) return ira->codegen->invalid_inst_gen; if (instr_is_comptime(casted_union)) { ZigValue *const_union_val = ir_resolve_const(ira, casted_union, UndefBad); if (!const_union_val) return ira->codegen->invalid_inst_gen; ZigValue *const_enum_val = ir_resolve_const(ira, casted_val, UndefBad); if (!const_enum_val) return ira->codegen->invalid_inst_gen; Cmp cmp_result = bigint_cmp(&const_union_val->data.x_union.tag, &const_enum_val->data.x_enum_tag); bool bool_result = (op_id == IrBinOpCmpEq) ? cmp_result == CmpEQ : cmp_result != CmpEQ; return ir_const_bool(ira, &bin_op_instruction->base.base, bool_result); } return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, ira->codegen->builtin_types.entry_bool, op_id, casted_union, casted_val, bin_op_instruction->safety_check_on); } if (op1->value->type->id == ZigTypeIdErrorSet && op2->value->type->id == ZigTypeIdErrorSet) { if (!is_equality_cmp) { ir_add_error_node(ira, source_node, buf_sprintf("operator not allowed for errors")); return ira->codegen->invalid_inst_gen; } ZigType *intersect_type = get_error_set_intersection(ira, op1->value->type, op2->value->type, source_node); if (type_is_invalid(intersect_type)) { return ira->codegen->invalid_inst_gen; } if (!resolve_inferred_error_set(ira->codegen, intersect_type, source_node)) { return ira->codegen->invalid_inst_gen; } // exception if one of the operators has the type of the empty error set, we allow the comparison // (and make it comptime known) // this is a function which is evaluated at comptime and returns an inferred error set will have an empty // error set. if (op1->value->type->data.error_set.err_count == 0 || op2->value->type->data.error_set.err_count == 0) { bool are_equal = false; bool answer; if (op_id == IrBinOpCmpEq) { answer = are_equal; } else if (op_id == IrBinOpCmpNotEq) { answer = !are_equal; } else { zig_unreachable(); } return ir_const_bool(ira, &bin_op_instruction->base.base, answer); } if (!type_is_global_error_set(intersect_type)) { if (intersect_type->data.error_set.err_count == 0) { ir_add_error_node(ira, source_node, buf_sprintf("error sets '%s' and '%s' have no common errors", buf_ptr(&op1->value->type->name), buf_ptr(&op2->value->type->name))); return ira->codegen->invalid_inst_gen; } if (op1->value->type->data.error_set.err_count == 1 && op2->value->type->data.error_set.err_count == 1) { bool are_equal = true; bool answer; if (op_id == IrBinOpCmpEq) { answer = are_equal; } else if (op_id == IrBinOpCmpNotEq) { answer = !are_equal; } else { zig_unreachable(); } return ir_const_bool(ira, &bin_op_instruction->base.base, answer); } } if (instr_is_comptime(op1) && instr_is_comptime(op2)) { ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad); if (op1_val == nullptr) return ira->codegen->invalid_inst_gen; ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad); if (op2_val == nullptr) return ira->codegen->invalid_inst_gen; bool answer; bool are_equal = op1_val->data.x_err_set->value == op2_val->data.x_err_set->value; if (op_id == IrBinOpCmpEq) { answer = are_equal; } else if (op_id == IrBinOpCmpNotEq) { answer = !are_equal; } else { zig_unreachable(); } return ir_const_bool(ira, &bin_op_instruction->base.base, answer); } return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, ira->codegen->builtin_types.entry_bool, op_id, op1, op2, bin_op_instruction->safety_check_on); } if (type_is_numeric(op1->value->type) && type_is_numeric(op2->value->type)) { // This operation allows any combination of integer and float types, regardless of the // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for // numeric types. return ir_analyze_bin_op_cmp_numeric(ira, &bin_op_instruction->base.base, op1, op2, op_id); } IrInstGen *instructions[] = {op1, op2}; ZigType *resolved_type = ir_resolve_peer_types(ira, source_node, nullptr, instructions, 2); if (type_is_invalid(resolved_type)) return ira->codegen->invalid_inst_gen; bool operator_allowed = type_is_self_comparable(resolved_type, is_equality_cmp); if (!operator_allowed) { ir_add_error_node(ira, source_node, buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, resolved_type); if (type_is_invalid(casted_op1->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, resolved_type); if (type_is_invalid(casted_op2->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *resolve_const_result = ir_try_evaluate_bin_op_cmp_const(ira, &bin_op_instruction->base.base, casted_op1, casted_op2, resolved_type, op_id); if (resolve_const_result != nullptr) { return resolve_const_result; } ZigType *res_type = (resolved_type->id == ZigTypeIdVector) ? get_vector_type(ira->codegen, resolved_type->data.vector.len, ira->codegen->builtin_types.entry_bool) : ira->codegen->builtin_types.entry_bool; return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, res_type, op_id, casted_op1, casted_op2, bin_op_instruction->safety_check_on); } static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, IrInst* source_instr, ZigType *type_entry, ZigValue *op1_val, IrBinOp op_id, ZigValue *op2_val, ZigValue *out_val) { bool is_int; bool is_float; Cmp op2_zcmp; if (type_entry->id == ZigTypeIdInt || type_entry->id == ZigTypeIdComptimeInt) { is_int = true; is_float = false; op2_zcmp = bigint_cmp_zero(&op2_val->data.x_bigint); } else if (type_entry->id == ZigTypeIdFloat || type_entry->id == ZigTypeIdComptimeFloat) { is_int = false; is_float = true; op2_zcmp = float_cmp_zero(op2_val); } else { zig_unreachable(); } if ((op_id == IrBinOpDivUnspecified || op_id == IrBinOpRemRem || op_id == IrBinOpRemMod || op_id == IrBinOpDivTrunc || op_id == IrBinOpDivFloor) && op2_zcmp == CmpEQ) { return ir_add_error(ira, source_instr, buf_sprintf("division by zero")); } if ((op_id == IrBinOpRemRem || op_id == IrBinOpRemMod) && op2_zcmp == CmpLT) { return ir_add_error(ira, source_instr, buf_sprintf("negative denominator")); } switch (op_id) { case IrBinOpInvalid: case IrBinOpBoolOr: case IrBinOpBoolAnd: case IrBinOpCmpEq: case IrBinOpCmpNotEq: case IrBinOpCmpLessThan: case IrBinOpCmpGreaterThan: case IrBinOpCmpLessOrEq: case IrBinOpCmpGreaterOrEq: case IrBinOpArrayCat: case IrBinOpArrayMult: case IrBinOpRemUnspecified: zig_unreachable(); case IrBinOpBinOr: assert(is_int); bigint_or(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); break; case IrBinOpBinXor: assert(is_int); bigint_xor(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); break; case IrBinOpBinAnd: assert(is_int); bigint_and(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); break; case IrBinOpBitShiftLeftExact: assert(is_int); bigint_shl(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); break; case IrBinOpBitShiftLeftLossy: assert(type_entry->id == ZigTypeIdInt); bigint_shl_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); break; case IrBinOpBitShiftRightExact: { assert(is_int); bigint_shr(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); BigInt orig_bigint; bigint_shl(&orig_bigint, &out_val->data.x_bigint, &op2_val->data.x_bigint); if (bigint_cmp(&op1_val->data.x_bigint, &orig_bigint) != CmpEQ) { return ir_add_error(ira, source_instr, buf_sprintf("exact shift shifted out 1 bits")); } break; } case IrBinOpBitShiftRightLossy: assert(is_int); bigint_shr(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); break; case IrBinOpAdd: if (is_int) { bigint_add(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); } else { float_add(out_val, op1_val, op2_val); } break; case IrBinOpAddWrap: assert(type_entry->id == ZigTypeIdInt); bigint_add_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); break; case IrBinOpSub: if (is_int) { bigint_sub(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); } else { float_sub(out_val, op1_val, op2_val); } break; case IrBinOpSubWrap: assert(type_entry->id == ZigTypeIdInt); bigint_sub_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); break; case IrBinOpMult: if (is_int) { bigint_mul(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); } else { float_mul(out_val, op1_val, op2_val); } break; case IrBinOpMultWrap: assert(type_entry->id == ZigTypeIdInt); bigint_mul_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); break; case IrBinOpDivUnspecified: assert(is_float); float_div(out_val, op1_val, op2_val); break; case IrBinOpDivTrunc: if (is_int) { bigint_div_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); } else { float_div_trunc(out_val, op1_val, op2_val); } break; case IrBinOpDivFloor: if (is_int) { bigint_div_floor(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); } else { float_div_floor(out_val, op1_val, op2_val); } break; case IrBinOpDivExact: if (is_int) { bigint_div_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); BigInt remainder; bigint_rem(&remainder, &op1_val->data.x_bigint, &op2_val->data.x_bigint); if (bigint_cmp_zero(&remainder) != CmpEQ) { return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder")); } } else { float_div_trunc(out_val, op1_val, op2_val); ZigValue remainder = {}; float_rem(&remainder, op1_val, op2_val); if (float_cmp_zero(&remainder) != CmpEQ) { return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder")); } } break; case IrBinOpRemRem: if (is_int) { bigint_rem(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); } else { float_rem(out_val, op1_val, op2_val); } break; case IrBinOpRemMod: if (is_int) { bigint_mod(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); } else { float_mod(out_val, op1_val, op2_val); } break; } if (type_entry->id == ZigTypeIdInt) { if (!bigint_fits_in_bits(&out_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed)) { return ir_add_error(ira, source_instr, buf_sprintf("operation caused overflow")); } } out_val->type = type_entry; out_val->special = ConstValSpecialStatic; return nullptr; } // This works on operands that have already been checked to be comptime known. static IrInstGen *ir_analyze_math_op(IrAnalyze *ira, IrInst* source_instr, ZigType *type_entry, ZigValue *op1_val, IrBinOp op_id, ZigValue *op2_val) { IrInstGen *result_instruction = ir_const(ira, source_instr, type_entry); ZigValue *out_val = result_instruction->value; if (type_entry->id == ZigTypeIdVector) { expand_undef_array(ira->codegen, op1_val); expand_undef_array(ira->codegen, op2_val); out_val->special = ConstValSpecialUndef; expand_undef_array(ira->codegen, out_val); size_t len = type_entry->data.vector.len; ZigType *scalar_type = type_entry->data.vector.elem_type; for (size_t i = 0; i < len; i += 1) { ZigValue *scalar_op1_val = &op1_val->data.x_array.data.s_none.elements[i]; ZigValue *scalar_op2_val = &op2_val->data.x_array.data.s_none.elements[i]; ZigValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; assert(scalar_op1_val->type == scalar_type); assert(scalar_out_val->type == scalar_type); ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type, scalar_op1_val, op_id, scalar_op2_val, scalar_out_val); if (msg != nullptr) { add_error_note(ira->codegen, msg, source_instr->source_node, buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i)); return ira->codegen->invalid_inst_gen; } } out_val->type = type_entry; out_val->special = ConstValSpecialStatic; } else { if (ir_eval_math_op_scalar(ira, source_instr, type_entry, op1_val, op_id, op2_val, out_val) != nullptr) { return ira->codegen->invalid_inst_gen; } } return ir_implicit_cast(ira, result_instruction, type_entry); } static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_instruction) { IrInstGen *op1 = bin_op_instruction->op1->child; if (type_is_invalid(op1->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *op2 = bin_op_instruction->op2->child; if (type_is_invalid(op2->value->type)) return ira->codegen->invalid_inst_gen; ZigType *op1_type = op1->value->type; ZigType *op2_type = op2->value->type; if (op1_type->id == ZigTypeIdVector && op2_type->id != ZigTypeIdVector) { ir_add_error(ira, &bin_op_instruction->op1->base, buf_sprintf("bit shifting operation expected vector type, found '%s'", buf_ptr(&op2_type->name))); return ira->codegen->invalid_inst_gen; } if (op1_type->id != ZigTypeIdVector && op2_type->id == ZigTypeIdVector) { ir_add_error(ira, &bin_op_instruction->op1->base, buf_sprintf("bit shifting operation expected vector type, found '%s'", buf_ptr(&op1_type->name))); return ira->codegen->invalid_inst_gen; } ZigType *op1_scalar_type = (op1_type->id == ZigTypeIdVector) ? op1_type->data.vector.elem_type : op1_type; ZigType *op2_scalar_type = (op2_type->id == ZigTypeIdVector) ? op2_type->data.vector.elem_type : op2_type; if (op1_scalar_type->id != ZigTypeIdInt && op1_scalar_type->id != ZigTypeIdComptimeInt) { ir_add_error(ira, &bin_op_instruction->op1->base, buf_sprintf("bit shifting operation expected integer type, found '%s'", buf_ptr(&op1_scalar_type->name))); return ira->codegen->invalid_inst_gen; } if (op2_scalar_type->id != ZigTypeIdInt && op2_scalar_type->id != ZigTypeIdComptimeInt) { ir_add_error(ira, &bin_op_instruction->op2->base, buf_sprintf("shift amount has to be an integer type, but found '%s'", buf_ptr(&op2_scalar_type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *casted_op2; IrBinOp op_id = bin_op_instruction->op_id; if (op1_scalar_type->id == ZigTypeIdComptimeInt) { // comptime_int has no finite bit width casted_op2 = op2; if (op_id == IrBinOpBitShiftLeftLossy) { op_id = IrBinOpBitShiftLeftExact; } if (!instr_is_comptime(op2)) { ir_add_error(ira, &bin_op_instruction->base.base, buf_sprintf("LHS of shift must be a fixed-width integer type, or RHS must be compile-time known")); return ira->codegen->invalid_inst_gen; } ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->invalid_inst_gen; if (op2_val->data.x_bigint.is_negative) { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, &op2_val->data.x_bigint, 10); ir_add_error(ira, &casted_op2->base, buf_sprintf("shift by negative value %s", buf_ptr(val_buf))); return ira->codegen->invalid_inst_gen; } } else { const unsigned bit_count = op1_scalar_type->data.integral.bit_count; ZigType *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen, bit_count > 0 ? bit_count - 1 : 0); if (op1_type->id == ZigTypeIdVector) { shift_amt_type = get_vector_type(ira->codegen, op1_type->data.vector.len, shift_amt_type); } casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type); if (type_is_invalid(casted_op2->value->type)) return ira->codegen->invalid_inst_gen; // This check is only valid iff op1 has at least one bit if (bit_count > 0 && instr_is_comptime(casted_op2)) { ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->invalid_inst_gen; ZigValue bit_count_value = {}; init_const_usize(ira->codegen, &bit_count_value, bit_count); if (!value_cmp_numeric_val_all(op2_val, CmpLT, &bit_count_value)) { ErrorMsg* msg = ir_add_error(ira, &bin_op_instruction->base.base, buf_sprintf("RHS of shift is too large for LHS type")); add_error_note(ira->codegen, msg, op1->base.source_node, buf_sprintf("type %s has only %u bits", buf_ptr(&op1->value->type->name), bit_count)); return ira->codegen->invalid_inst_gen; } } } // Fast path for zero RHS if (instr_is_comptime(casted_op2)) { ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->invalid_inst_gen; if (value_cmp_numeric_val_all(op2_val, CmpEQ, nullptr)) return ir_analyze_cast(ira, &bin_op_instruction->base.base, op1->value->type, op1); } if (instr_is_comptime(op1) && instr_is_comptime(casted_op2)) { ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad); if (op1_val == nullptr) return ira->codegen->invalid_inst_gen; ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->invalid_inst_gen; return ir_analyze_math_op(ira, &bin_op_instruction->base.base, op1_type, op1_val, op_id, op2_val); } return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, op1->value->type, op_id, op1, casted_op2, bin_op_instruction->safety_check_on); } static bool ok_float_op(IrBinOp op) { switch (op) { case IrBinOpInvalid: zig_unreachable(); case IrBinOpAdd: case IrBinOpSub: case IrBinOpMult: case IrBinOpDivUnspecified: case IrBinOpDivTrunc: case IrBinOpDivFloor: case IrBinOpDivExact: case IrBinOpRemRem: case IrBinOpRemMod: case IrBinOpRemUnspecified: return true; case IrBinOpBoolOr: case IrBinOpBoolAnd: case IrBinOpCmpEq: case IrBinOpCmpNotEq: case IrBinOpCmpLessThan: case IrBinOpCmpGreaterThan: case IrBinOpCmpLessOrEq: case IrBinOpCmpGreaterOrEq: case IrBinOpBinOr: case IrBinOpBinXor: case IrBinOpBinAnd: case IrBinOpBitShiftLeftLossy: case IrBinOpBitShiftLeftExact: case IrBinOpBitShiftRightLossy: case IrBinOpBitShiftRightExact: case IrBinOpAddWrap: case IrBinOpSubWrap: case IrBinOpMultWrap: case IrBinOpArrayCat: case IrBinOpArrayMult: return false; } zig_unreachable(); } static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) { switch (op) { case IrBinOpAdd: case IrBinOpSub: break; default: return false; } if (lhs_type->id != ZigTypeIdPointer) return false; switch (lhs_type->data.pointer.ptr_len) { case PtrLenSingle: return lhs_type->data.pointer.child_type->id == ZigTypeIdArray; case PtrLenUnknown: case PtrLenC: return true; } zig_unreachable(); } static bool value_cmp_numeric_val(ZigValue *left, Cmp predicate, ZigValue *right, bool any) { assert(left->special == ConstValSpecialStatic); assert(right == nullptr || right->special == ConstValSpecialStatic); switch (left->type->id) { case ZigTypeIdComptimeInt: case ZigTypeIdInt: { const Cmp result = right ? bigint_cmp(&left->data.x_bigint, &right->data.x_bigint) : bigint_cmp_zero(&left->data.x_bigint); return result == predicate; } case ZigTypeIdComptimeFloat: case ZigTypeIdFloat: { if (float_is_nan(left)) return false; if (right != nullptr && float_is_nan(right)) return false; const Cmp result = right ? float_cmp(left, right) : float_cmp_zero(left); return result == predicate; } case ZigTypeIdVector: { for (size_t i = 0; i < left->type->data.vector.len; i++) { ZigValue *scalar_val = &left->data.x_array.data.s_none.elements[i]; const bool result = value_cmp_numeric_val(scalar_val, predicate, right, any); if (any && result) return true; // This element satisfies the predicate else if (!any && !result) return false; // This element doesn't satisfy the predicate } return any ? false : true; } default: zig_unreachable(); } } static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right) { return value_cmp_numeric_val(left, predicate, right, true); } static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right) { return value_cmp_numeric_val(left, predicate, right, false); } static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruction) { Error err; IrInstGen *op1 = instruction->op1->child; if (type_is_invalid(op1->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *op2 = instruction->op2->child; if (type_is_invalid(op2->value->type)) return ira->codegen->invalid_inst_gen; IrBinOp op_id = instruction->op_id; // look for pointer math if (is_pointer_arithmetic_allowed(op1->value->type, op_id)) { IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, ira->codegen->builtin_types.entry_usize); if (type_is_invalid(casted_op2->value->type)) return ira->codegen->invalid_inst_gen; // If either operand is undef, result is undef. ZigValue *op1_val = nullptr; ZigValue *op2_val = nullptr; if (instr_is_comptime(op1)) { op1_val = ir_resolve_const(ira, op1, UndefOk); if (op1_val == nullptr) return ira->codegen->invalid_inst_gen; if (op1_val->special == ConstValSpecialUndef) return ir_const_undef(ira, &instruction->base.base, op1->value->type); } if (instr_is_comptime(casted_op2)) { op2_val = ir_resolve_const(ira, casted_op2, UndefOk); if (op2_val == nullptr) return ira->codegen->invalid_inst_gen; if (op2_val->special == ConstValSpecialUndef) return ir_const_undef(ira, &instruction->base.base, op1->value->type); } ZigType *elem_type = op1->value->type->data.pointer.child_type; if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_inst_gen; // NOTE: this variable is meaningful iff op2_val is not null! uint64_t byte_offset; if (op2_val != nullptr) { uint64_t elem_offset; if (!ir_resolve_usize(ira, casted_op2, &elem_offset)) return ira->codegen->invalid_inst_gen; byte_offset = type_size(ira->codegen, elem_type) * elem_offset; } // Fast path for cases where the RHS is zero if (op2_val != nullptr && byte_offset == 0) { return op1; } ZigType *result_type = op1->value->type; // Calculate the new alignment of the pointer { uint32_t align_bytes; if ((err = resolve_ptr_align(ira, op1->value->type, &align_bytes))) return ira->codegen->invalid_inst_gen; // If the addend is not a comptime-known value we can still count on // it being a multiple of the type size uint32_t addend = op2_val ? byte_offset : type_size(ira->codegen, elem_type); // The resulting pointer is aligned to the lcd between the // offset (an arbitrary number) and the alignment factor (always // a power of two, non zero) uint32_t new_align = 1 << ctzll(addend | align_bytes); // Rough guard to prevent overflows assert(new_align); result_type = adjust_ptr_align(ira->codegen, result_type, new_align); } if (op2_val != nullptr && op1_val != nullptr && (op1->value->data.x_ptr.special == ConstPtrSpecialHardCodedAddr || op1->value->data.x_ptr.special == ConstPtrSpecialNull)) { uint64_t start_addr = (op1_val->data.x_ptr.special == ConstPtrSpecialNull) ? 0 : op1_val->data.x_ptr.data.hard_coded_addr.addr; uint64_t new_addr; if (op_id == IrBinOpAdd) { new_addr = start_addr + byte_offset; } else if (op_id == IrBinOpSub) { new_addr = start_addr - byte_offset; } else { zig_unreachable(); } IrInstGen *result = ir_const(ira, &instruction->base.base, result_type); result->value->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; result->value->data.x_ptr.mut = ConstPtrMutRuntimeVar; result->value->data.x_ptr.data.hard_coded_addr.addr = new_addr; return result; } return ir_build_bin_op_gen(ira, &instruction->base.base, result_type, op_id, op1, casted_op2, true); } IrInstGen *instructions[] = {op1, op2}; ZigType *resolved_type = ir_resolve_peer_types(ira, instruction->base.base.source_node, nullptr, instructions, 2); if (type_is_invalid(resolved_type)) return ira->codegen->invalid_inst_gen; ZigType *scalar_type = (resolved_type->id == ZigTypeIdVector) ? resolved_type->data.vector.elem_type : resolved_type; bool is_int = scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdComptimeInt; bool is_float = scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat; if (!is_int && !(is_float && ok_float_op(op_id))) { AstNode *source_node = instruction->base.base.source_node; ir_add_error_node(ira, source_node, buf_sprintf("invalid operands to binary expression: '%s' and '%s'", buf_ptr(&op1->value->type->name), buf_ptr(&op2->value->type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, resolved_type); if (type_is_invalid(casted_op1->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, resolved_type); if (type_is_invalid(casted_op2->value->type)) return ira->codegen->invalid_inst_gen; // Comptime integers have no fixed size if (scalar_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; } } if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); if (op1_val == nullptr) return ira->codegen->invalid_inst_gen; ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->invalid_inst_gen; // Promote division with negative numbers to signed bool is_signed_div = value_cmp_numeric_val_any(op1_val, CmpLT, nullptr) || value_cmp_numeric_val_any(op2_val, CmpLT, nullptr); if (op_id == IrBinOpDivUnspecified && is_int) { // Default to truncating division and check if it's valid for the // given operands if signed op_id = IrBinOpDivTrunc; if (is_signed_div) { bool ok = false; if (value_cmp_numeric_val_any(op2_val, CmpEQ, nullptr)) { // the division by zero error will be caught later, but we don't have a // division function ambiguity problem. ok = true; } else { IrInstGen *trunc_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, op1_val, IrBinOpDivTrunc, op2_val); if (type_is_invalid(trunc_val->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *floor_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, op1_val, IrBinOpDivFloor, op2_val); if (type_is_invalid(floor_val->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *cmp_val = ir_analyze_bin_op_cmp_numeric(ira, &instruction->base.base, trunc_val, floor_val, IrBinOpCmpEq); if (type_is_invalid(cmp_val->value->type)) return ira->codegen->invalid_inst_gen; // We can "upgrade" the operator only if trunc(a/b) == floor(a/b) if (!ir_resolve_bool(ira, cmp_val, &ok)) return ira->codegen->invalid_inst_gen; } if (!ok) { ir_add_error(ira, &instruction->base.base, buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact", buf_ptr(&op1->value->type->name), buf_ptr(&op2->value->type->name))); return ira->codegen->invalid_inst_gen; } } } else if (op_id == IrBinOpRemUnspecified) { op_id = IrBinOpRemRem; if (is_signed_div) { bool ok = false; if (value_cmp_numeric_val_any(op2_val, CmpEQ, nullptr)) { // the division by zero error will be caught later, but we don't have a // division function ambiguity problem. ok = true; } else { IrInstGen *rem_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, op1_val, IrBinOpRemRem, op2_val); if (type_is_invalid(rem_val->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *mod_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, op1_val, IrBinOpRemMod, op2_val); if (type_is_invalid(mod_val->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *cmp_val = ir_analyze_bin_op_cmp_numeric(ira, &instruction->base.base, rem_val, mod_val, IrBinOpCmpEq); if (type_is_invalid(cmp_val->value->type)) return ira->codegen->invalid_inst_gen; // We can "upgrade" the operator only if mod(a,b) == rem(a,b) if (!ir_resolve_bool(ira, cmp_val, &ok)) return ira->codegen->invalid_inst_gen; } if (!ok) { ir_add_error(ira, &instruction->base.base, buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod", buf_ptr(&op1->value->type->name), buf_ptr(&op2->value->type->name))); return ira->codegen->invalid_inst_gen; } } } return ir_analyze_math_op(ira, &instruction->base.base, resolved_type, op1_val, op_id, op2_val); } const bool is_signed_div = (scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) || scalar_type->id == ZigTypeIdFloat; // Warn the user to use the proper operators here if (op_id == IrBinOpDivUnspecified && is_int) { op_id = IrBinOpDivTrunc; if (is_signed_div) { ir_add_error(ira, &instruction->base.base, buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact", buf_ptr(&op1->value->type->name), buf_ptr(&op2->value->type->name))); return ira->codegen->invalid_inst_gen; } } else if (op_id == IrBinOpRemUnspecified) { op_id = IrBinOpRemRem; if (is_signed_div) { ir_add_error(ira, &instruction->base.base, buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod", buf_ptr(&op1->value->type->name), buf_ptr(&op2->value->type->name))); return ira->codegen->invalid_inst_gen; } } return ir_build_bin_op_gen(ira, &instruction->base.base, resolved_type, op_id, casted_op1, casted_op2, instruction->safety_check_on); } static IrInstGen *ir_analyze_tuple_cat(IrAnalyze *ira, IrInst* source_instr, IrInstGen *op1, IrInstGen *op2) { Error err; ZigType *op1_type = op1->value->type; ZigType *op2_type = op2->value->type; uint32_t op1_field_count = op1_type->data.structure.src_field_count; uint32_t op2_field_count = op2_type->data.structure.src_field_count; Buf *bare_name = buf_alloc(); Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct), source_instr->scope, source_instr->source_node, bare_name); ZigType *new_type = get_partial_container_type(ira->codegen, source_instr->scope, ContainerKindStruct, source_instr->source_node, buf_ptr(name), bare_name, ContainerLayoutAuto); new_type->data.structure.special = StructSpecialInferredTuple; new_type->data.structure.resolve_status = ResolveStatusBeingInferred; uint32_t new_field_count = op1_field_count + op2_field_count; new_type->data.structure.src_field_count = new_field_count; new_type->data.structure.fields = realloc_type_struct_fields(new_type->data.structure.fields, 0, new_field_count); IrInstGen *new_struct_ptr = ir_resolve_result(ira, source_instr, no_result_loc(), new_type, nullptr, false, true); for (uint32_t i = 0; i < new_field_count; i += 1) { TypeStructField *src_field; if (i < op1_field_count) { src_field = op1_type->data.structure.fields[i]; } else { src_field = op2_type->data.structure.fields[i - op1_field_count]; } TypeStructField *new_field = new_type->data.structure.fields[i]; new_field->name = buf_sprintf("%" PRIu32, i); new_field->type_entry = src_field->type_entry; new_field->type_val = src_field->type_val; new_field->src_index = i; new_field->decl_node = src_field->decl_node; new_field->init_val = src_field->init_val; new_field->is_comptime = src_field->is_comptime; } if ((err = type_resolve(ira->codegen, new_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_inst_gen; ZigList const_ptrs = {}; for (uint32_t i = 0; i < new_field_count; i += 1) { TypeStructField *dst_field = new_type->data.structure.fields[i]; IrInstGen *src_struct_op; TypeStructField *src_field; if (i < op1_field_count) { src_field = op1_type->data.structure.fields[i]; src_struct_op = op1; } else { src_field = op2_type->data.structure.fields[i - op1_field_count]; src_struct_op = op2; } IrInstGen *field_value = ir_analyze_struct_value_field_value(ira, source_instr, src_struct_op, src_field); if (type_is_invalid(field_value->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *dest_ptr = ir_analyze_struct_field_ptr(ira, source_instr, dst_field, new_struct_ptr, new_type, true); if (type_is_invalid(dest_ptr->value->type)) return ira->codegen->invalid_inst_gen; if (instr_is_comptime(field_value)) { const_ptrs.append(dest_ptr); } IrInstGen *store_ptr_inst = ir_analyze_store_ptr(ira, source_instr, dest_ptr, field_value, true); if (type_is_invalid(store_ptr_inst->value->type)) return ira->codegen->invalid_inst_gen; } if (const_ptrs.length != new_field_count) { new_struct_ptr->value->special = ConstValSpecialRuntime; for (size_t i = 0; i < const_ptrs.length; i += 1) { IrInstGen *elem_result_loc = const_ptrs.at(i); assert(elem_result_loc->value->special == ConstValSpecialStatic); if (elem_result_loc->value->type->data.pointer.inferred_struct_field != nullptr) { // This field will be generated comptime; no need to do this. continue; } IrInstGen *deref = ir_get_deref(ira, &elem_result_loc->base, elem_result_loc, nullptr); if (!type_requires_comptime(ira->codegen, elem_result_loc->value->type->data.pointer.child_type)) { elem_result_loc->value->special = ConstValSpecialRuntime; } ir_analyze_store_ptr(ira, &elem_result_loc->base, elem_result_loc, deref, true); } } const_ptrs.deinit(); return ir_get_deref(ira, source_instr, new_struct_ptr, nullptr); } static IrInstGen *ir_analyze_array_cat(IrAnalyze *ira, IrInstSrcBinOp *instruction) { IrInstGen *op1 = instruction->op1->child; ZigType *op1_type = op1->value->type; if (type_is_invalid(op1_type)) return ira->codegen->invalid_inst_gen; IrInstGen *op2 = instruction->op2->child; ZigType *op2_type = op2->value->type; if (type_is_invalid(op2_type)) return ira->codegen->invalid_inst_gen; if (is_tuple(op1_type) && is_tuple(op2_type)) { return ir_analyze_tuple_cat(ira, &instruction->base.base, op1, op2); } ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad); if (!op1_val) return ira->codegen->invalid_inst_gen; ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad); if (!op2_val) return ira->codegen->invalid_inst_gen; ZigValue *sentinel1 = nullptr; ZigValue *op1_array_val; size_t op1_array_index; size_t op1_array_end; ZigType *child_type; if (op1_type->id == ZigTypeIdArray) { child_type = op1_type->data.array.child_type; op1_array_val = op1_val; op1_array_index = 0; op1_array_end = op1_type->data.array.len; sentinel1 = op1_type->data.array.sentinel; } else if (op1_type->id == ZigTypeIdPointer && op1_type->data.pointer.child_type == ira->codegen->builtin_types.entry_u8 && op1_type->data.pointer.sentinel != nullptr && op1_val->data.x_ptr.special == ConstPtrSpecialBaseArray) { child_type = op1_type->data.pointer.child_type; op1_array_val = op1_val->data.x_ptr.data.base_array.array_val; op1_array_index = op1_val->data.x_ptr.data.base_array.elem_index; op1_array_end = op1_array_val->type->data.array.len; sentinel1 = op1_type->data.pointer.sentinel; } else if (is_slice(op1_type)) { ZigType *ptr_type = op1_type->data.structure.fields[slice_ptr_index]->type_entry; child_type = ptr_type->data.pointer.child_type; ZigValue *ptr_val = op1_val->data.x_struct.fields[slice_ptr_index]; assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray); op1_array_val = ptr_val->data.x_ptr.data.base_array.array_val; op1_array_index = ptr_val->data.x_ptr.data.base_array.elem_index; ZigValue *len_val = op1_val->data.x_struct.fields[slice_len_index]; op1_array_end = op1_array_index + bigint_as_usize(&len_val->data.x_bigint); sentinel1 = ptr_type->data.pointer.sentinel; } else if (op1_type->id == ZigTypeIdPointer && op1_type->data.pointer.ptr_len == PtrLenSingle && op1_type->data.pointer.child_type->id == ZigTypeIdArray) { ZigType *array_type = op1_type->data.pointer.child_type; child_type = array_type->data.array.child_type; op1_array_val = const_ptr_pointee(ira, ira->codegen, op1_val, op1->base.source_node); if (op1_array_val == nullptr) return ira->codegen->invalid_inst_gen; op1_array_index = 0; op1_array_end = array_type->data.array.len; sentinel1 = array_type->data.array.sentinel; } else { ir_add_error(ira, &op1->base, buf_sprintf("expected array, found '%s'", buf_ptr(&op1->value->type->name))); return ira->codegen->invalid_inst_gen; } ZigValue *sentinel2 = nullptr; ZigValue *op2_array_val; size_t op2_array_index; size_t op2_array_end; bool op2_type_valid; if (op2_type->id == ZigTypeIdArray) { op2_type_valid = op2_type->data.array.child_type == child_type; op2_array_val = op2_val; op2_array_index = 0; op2_array_end = op2_array_val->type->data.array.len; sentinel2 = op2_type->data.array.sentinel; } else if (op2_type->id == ZigTypeIdPointer && op2_type->data.pointer.sentinel != nullptr && op2_val->data.x_ptr.special == ConstPtrSpecialBaseArray) { op2_type_valid = op2_type->data.pointer.child_type == child_type; op2_array_val = op2_val->data.x_ptr.data.base_array.array_val; op2_array_index = op2_val->data.x_ptr.data.base_array.elem_index; op2_array_end = op2_array_val->type->data.array.len; sentinel2 = op2_type->data.pointer.sentinel; } else if (is_slice(op2_type)) { ZigType *ptr_type = op2_type->data.structure.fields[slice_ptr_index]->type_entry; op2_type_valid = ptr_type->data.pointer.child_type == child_type; ZigValue *ptr_val = op2_val->data.x_struct.fields[slice_ptr_index]; assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray); op2_array_val = ptr_val->data.x_ptr.data.base_array.array_val; op2_array_index = ptr_val->data.x_ptr.data.base_array.elem_index; ZigValue *len_val = op2_val->data.x_struct.fields[slice_len_index]; op2_array_end = op2_array_index + bigint_as_usize(&len_val->data.x_bigint); sentinel2 = ptr_type->data.pointer.sentinel; } else if (op2_type->id == ZigTypeIdPointer && op2_type->data.pointer.ptr_len == PtrLenSingle && op2_type->data.pointer.child_type->id == ZigTypeIdArray) { ZigType *array_type = op2_type->data.pointer.child_type; op2_type_valid = array_type->data.array.child_type == child_type; op2_array_val = const_ptr_pointee(ira, ira->codegen, op2_val, op2->base.source_node); if (op2_array_val == nullptr) return ira->codegen->invalid_inst_gen; op2_array_index = 0; op2_array_end = array_type->data.array.len; sentinel2 = array_type->data.array.sentinel; } else { ir_add_error(ira, &op2->base, buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op2->value->type->name))); return ira->codegen->invalid_inst_gen; } if (!op2_type_valid) { ir_add_error(ira, &op2->base, buf_sprintf("expected array of type '%s', found '%s'", buf_ptr(&child_type->name), buf_ptr(&op2->value->type->name))); return ira->codegen->invalid_inst_gen; } ZigValue *sentinel; if (sentinel1 != nullptr && sentinel2 != nullptr) { // When there is a sentinel mismatch, no sentinel on the result. The type system // will catch this if it is a problem. sentinel = const_values_equal(ira->codegen, sentinel1, sentinel2) ? sentinel1 : nullptr; } else if (sentinel1 != nullptr) { sentinel = sentinel1; } else if (sentinel2 != nullptr) { sentinel = sentinel2; } else { sentinel = nullptr; } // The type of result is populated in the following if blocks IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); ZigValue *out_val = result->value; ZigValue *out_array_val; size_t new_len = (op1_array_end - op1_array_index) + (op2_array_end - op2_array_index); if (op1_type->id == ZigTypeIdPointer || op2_type->id == ZigTypeIdPointer) { out_array_val = ira->codegen->pass1_arena->create(); out_array_val->special = ConstValSpecialStatic; out_array_val->type = get_array_type(ira->codegen, child_type, new_len, sentinel); out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.data.ref.pointee = out_array_val; out_val->type = get_pointer_to_type(ira->codegen, out_array_val->type, true); } else if (is_slice(op1_type) || is_slice(op2_type)) { ZigType *ptr_type = get_pointer_to_type_extra2(ira->codegen, child_type, true, false, PtrLenUnknown, 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, sentinel); result->value->type = get_slice_type(ira->codegen, ptr_type); out_array_val = ira->codegen->pass1_arena->create(); out_array_val->special = ConstValSpecialStatic; out_array_val->type = get_array_type(ira->codegen, child_type, new_len, sentinel); out_val->data.x_struct.fields = alloc_const_vals_ptrs(ira->codegen, 2); out_val->data.x_struct.fields[slice_ptr_index]->type = ptr_type; out_val->data.x_struct.fields[slice_ptr_index]->special = ConstValSpecialStatic; out_val->data.x_struct.fields[slice_ptr_index]->data.x_ptr.special = ConstPtrSpecialBaseArray; out_val->data.x_struct.fields[slice_ptr_index]->data.x_ptr.data.base_array.array_val = out_array_val; out_val->data.x_struct.fields[slice_ptr_index]->data.x_ptr.data.base_array.elem_index = 0; out_val->data.x_struct.fields[slice_len_index]->type = ira->codegen->builtin_types.entry_usize; out_val->data.x_struct.fields[slice_len_index]->special = ConstValSpecialStatic; bigint_init_unsigned(&out_val->data.x_struct.fields[slice_len_index]->data.x_bigint, new_len); } else if (op1_type->id == ZigTypeIdArray || op2_type->id == ZigTypeIdArray) { result->value->type = get_array_type(ira->codegen, child_type, new_len, sentinel); out_array_val = out_val; } else { result->value->type = get_pointer_to_type_extra2(ira->codegen, child_type, true, false, PtrLenUnknown, 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, sentinel); out_array_val = ira->codegen->pass1_arena->create(); out_array_val->special = ConstValSpecialStatic; out_array_val->type = get_array_type(ira->codegen, child_type, new_len, sentinel); out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; out_val->data.x_ptr.data.base_array.array_val = out_array_val; out_val->data.x_ptr.data.base_array.elem_index = 0; } if (op1_array_val->data.x_array.special == ConstArraySpecialUndef && op2_array_val->data.x_array.special == ConstArraySpecialUndef) { out_array_val->data.x_array.special = ConstArraySpecialUndef; return result; } uint64_t full_len = new_len + ((sentinel != nullptr) ? 1 : 0); out_array_val->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(full_len); // TODO handle the buf case here for an optimization expand_undef_array(ira->codegen, op1_array_val); expand_undef_array(ira->codegen, op2_array_val); size_t next_index = 0; for (size_t i = op1_array_index; i < op1_array_end; i += 1, next_index += 1) { ZigValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index]; copy_const_val(ira->codegen, elem_dest_val, &op1_array_val->data.x_array.data.s_none.elements[i]); elem_dest_val->parent.id = ConstParentIdArray; elem_dest_val->parent.data.p_array.array_val = out_array_val; elem_dest_val->parent.data.p_array.elem_index = next_index; } for (size_t i = op2_array_index; i < op2_array_end; i += 1, next_index += 1) { ZigValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index]; copy_const_val(ira->codegen, elem_dest_val, &op2_array_val->data.x_array.data.s_none.elements[i]); elem_dest_val->parent.id = ConstParentIdArray; elem_dest_val->parent.data.p_array.array_val = out_array_val; elem_dest_val->parent.data.p_array.elem_index = next_index; } if (next_index < full_len) { ZigValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index]; copy_const_val(ira->codegen, elem_dest_val, sentinel); elem_dest_val->parent.id = ConstParentIdArray; elem_dest_val->parent.data.p_array.array_val = out_array_val; elem_dest_val->parent.data.p_array.elem_index = next_index; next_index += 1; } assert(next_index == full_len); return result; } static IrInstGen *ir_analyze_tuple_mult(IrAnalyze *ira, IrInst* source_instr, IrInstGen *op1, IrInstGen *op2) { Error err; ZigType *op1_type = op1->value->type; uint64_t op1_field_count = op1_type->data.structure.src_field_count; uint64_t mult_amt; if (!ir_resolve_usize(ira, op2, &mult_amt)) return ira->codegen->invalid_inst_gen; uint64_t new_field_count; if (mul_u64_overflow(op1_field_count, mult_amt, &new_field_count)) { ir_add_error(ira, source_instr, buf_sprintf("operation results in overflow")); return ira->codegen->invalid_inst_gen; } Buf *bare_name = buf_alloc(); Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct), source_instr->scope, source_instr->source_node, bare_name); ZigType *new_type = get_partial_container_type(ira->codegen, source_instr->scope, ContainerKindStruct, source_instr->source_node, buf_ptr(name), bare_name, ContainerLayoutAuto); new_type->data.structure.special = StructSpecialInferredTuple; new_type->data.structure.resolve_status = ResolveStatusBeingInferred; new_type->data.structure.src_field_count = new_field_count; new_type->data.structure.fields = realloc_type_struct_fields( new_type->data.structure.fields, 0, new_field_count); IrInstGen *new_struct_ptr = ir_resolve_result(ira, source_instr, no_result_loc(), new_type, nullptr, false, true); for (uint64_t i = 0; i < new_field_count; i += 1) { TypeStructField *src_field = op1_type->data.structure.fields[i % op1_field_count]; TypeStructField *new_field = new_type->data.structure.fields[i]; new_field->name = buf_sprintf("%" ZIG_PRI_u64, i); new_field->type_entry = src_field->type_entry; new_field->type_val = src_field->type_val; new_field->src_index = i; new_field->decl_node = src_field->decl_node; new_field->init_val = src_field->init_val; new_field->is_comptime = src_field->is_comptime; } if ((err = type_resolve(ira->codegen, new_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_inst_gen; ZigList const_ptrs = {}; for (uint64_t i = 0; i < new_field_count; i += 1) { TypeStructField *src_field = op1_type->data.structure.fields[i % op1_field_count]; TypeStructField *dst_field = new_type->data.structure.fields[i]; IrInstGen *field_value = ir_analyze_struct_value_field_value( ira, source_instr, op1, src_field); if (type_is_invalid(field_value->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *dest_ptr = ir_analyze_struct_field_ptr( ira, source_instr, dst_field, new_struct_ptr, new_type, true); if (type_is_invalid(dest_ptr->value->type)) return ira->codegen->invalid_inst_gen; if (instr_is_comptime(field_value)) { const_ptrs.append(dest_ptr); } IrInstGen *store_ptr_inst = ir_analyze_store_ptr( ira, source_instr, dest_ptr, field_value, true); if (type_is_invalid(store_ptr_inst->value->type)) return ira->codegen->invalid_inst_gen; } if (const_ptrs.length != new_field_count) { new_struct_ptr->value->special = ConstValSpecialRuntime; for (size_t i = 0; i < const_ptrs.length; i += 1) { IrInstGen *elem_result_loc = const_ptrs.at(i); assert(elem_result_loc->value->special == ConstValSpecialStatic); if (elem_result_loc->value->type->data.pointer.inferred_struct_field != nullptr) { // This field will be generated comptime; no need to do this. continue; } IrInstGen *deref = ir_get_deref(ira, &elem_result_loc->base, elem_result_loc, nullptr); if (!type_requires_comptime(ira->codegen, elem_result_loc->value->type->data.pointer.child_type)) { elem_result_loc->value->special = ConstValSpecialRuntime; } IrInstGen *store_ptr_inst = ir_analyze_store_ptr( ira, &elem_result_loc->base, elem_result_loc, deref, true); if (type_is_invalid(store_ptr_inst->value->type)) return ira->codegen->invalid_inst_gen; } } const_ptrs.deinit(); return ir_get_deref(ira, source_instr, new_struct_ptr, nullptr); } static IrInstGen *ir_analyze_array_mult(IrAnalyze *ira, IrInstSrcBinOp *instruction) { IrInstGen *op1 = instruction->op1->child; if (type_is_invalid(op1->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *op2 = instruction->op2->child; if (type_is_invalid(op2->value->type)) return ira->codegen->invalid_inst_gen; bool want_ptr_to_array = false; ZigType *array_type; ZigValue *array_val; if (op1->value->type->id == ZigTypeIdArray) { array_type = op1->value->type; array_val = ir_resolve_const(ira, op1, UndefOk); if (array_val == nullptr) return ira->codegen->invalid_inst_gen; } else if (op1->value->type->id == ZigTypeIdPointer && op1->value->type->data.pointer.ptr_len == PtrLenSingle && op1->value->type->data.pointer.child_type->id == ZigTypeIdArray) { array_type = op1->value->type->data.pointer.child_type; IrInstGen *array_inst = ir_get_deref(ira, &op1->base, op1, nullptr); if (type_is_invalid(array_inst->value->type)) return ira->codegen->invalid_inst_gen; array_val = ir_resolve_const(ira, array_inst, UndefOk); if (array_val == nullptr) return ira->codegen->invalid_inst_gen; want_ptr_to_array = true; } else if (is_tuple(op1->value->type)) { return ir_analyze_tuple_mult(ira, &instruction->base.base, op1, op2); } else { ir_add_error(ira, &op1->base, buf_sprintf("expected array type, found '%s'", buf_ptr(&op1->value->type->name))); return ira->codegen->invalid_inst_gen; } uint64_t mult_amt; if (!ir_resolve_usize(ira, op2, &mult_amt)) return ira->codegen->invalid_inst_gen; 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.base, buf_sprintf("operation results in overflow")); return ira->codegen->invalid_inst_gen; } ZigType *child_type = array_type->data.array.child_type; ZigType *result_array_type = get_array_type(ira->codegen, child_type, new_array_len, array_type->data.array.sentinel); IrInstGen *array_result; if (array_val->special == ConstValSpecialUndef || array_val->data.x_array.special == ConstArraySpecialUndef) { array_result = ir_const_undef(ira, &instruction->base.base, result_array_type); } else { array_result = ir_const(ira, &instruction->base.base, result_array_type); ZigValue *out_val = array_result->value; switch (type_has_one_possible_value(ira->codegen, result_array_type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_inst_gen; case OnePossibleValueYes: goto skip_computation; case OnePossibleValueNo: break; } // TODO optimize the buf case expand_undef_array(ira->codegen, array_val); size_t extra_null_term = (array_type->data.array.sentinel != nullptr) ? 1 : 0; out_val->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(new_array_len + extra_null_term); uint64_t i = 0; for (uint64_t x = 0; x < mult_amt; x += 1) { for (uint64_t y = 0; y < old_array_len; y += 1) { ZigValue *elem_dest_val = &out_val->data.x_array.data.s_none.elements[i]; copy_const_val(ira->codegen, elem_dest_val, &array_val->data.x_array.data.s_none.elements[y]); elem_dest_val->parent.id = ConstParentIdArray; elem_dest_val->parent.data.p_array.array_val = out_val; elem_dest_val->parent.data.p_array.elem_index = i; i += 1; } } assert(i == new_array_len); if (array_type->data.array.sentinel != nullptr) { ZigValue *elem_dest_val = &out_val->data.x_array.data.s_none.elements[i]; copy_const_val(ira->codegen, elem_dest_val, array_type->data.array.sentinel); elem_dest_val->parent.id = ConstParentIdArray; elem_dest_val->parent.data.p_array.array_val = out_val; elem_dest_val->parent.data.p_array.elem_index = i; i += 1; } } skip_computation: if (want_ptr_to_array) { return ir_get_ref(ira, &instruction->base.base, array_result, true, false); } else { return array_result; } } static IrInstGen *ir_analyze_instruction_merge_err_sets(IrAnalyze *ira, IrInstSrcMergeErrSets *instruction) { ZigType *op1_type = ir_resolve_error_set_type(ira, &instruction->base.base, instruction->op1->child); if (type_is_invalid(op1_type)) return ira->codegen->invalid_inst_gen; ZigType *op2_type = ir_resolve_error_set_type(ira, &instruction->base.base, instruction->op2->child); if (type_is_invalid(op2_type)) return ira->codegen->invalid_inst_gen; if (!resolve_inferred_error_set(ira->codegen, op1_type, instruction->op1->child->base.source_node)) { return ira->codegen->invalid_inst_gen; } if (!resolve_inferred_error_set(ira->codegen, op2_type, instruction->op2->child->base.source_node)) { return ira->codegen->invalid_inst_gen; } if (type_is_global_error_set(op1_type) || type_is_global_error_set(op2_type)) { return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_global_error_set); } size_t errors_count = ira->codegen->errors_by_index.length; ErrorTableEntry **errors = heap::c_allocator.allocate(errors_count); for (uint32_t i = 0, count = op1_type->data.error_set.err_count; i < count; i += 1) { ErrorTableEntry *error_entry = op1_type->data.error_set.errors[i]; assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } ZigType *result_type = get_error_set_union(ira->codegen, errors, op1_type, op2_type, instruction->type_name); heap::c_allocator.deallocate(errors, errors_count); return ir_const_type(ira, &instruction->base.base, result_type); } static IrInstGen *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstSrcBinOp *bin_op_instruction) { IrBinOp op_id = bin_op_instruction->op_id; switch (op_id) { case IrBinOpInvalid: zig_unreachable(); case IrBinOpBoolOr: case IrBinOpBoolAnd: return ir_analyze_bin_op_bool(ira, bin_op_instruction); case IrBinOpCmpEq: case IrBinOpCmpNotEq: case IrBinOpCmpLessThan: case IrBinOpCmpGreaterThan: case IrBinOpCmpLessOrEq: case IrBinOpCmpGreaterOrEq: return ir_analyze_bin_op_cmp(ira, bin_op_instruction); case IrBinOpBitShiftLeftLossy: case IrBinOpBitShiftLeftExact: case IrBinOpBitShiftRightLossy: case IrBinOpBitShiftRightExact: return ir_analyze_bit_shift(ira, bin_op_instruction); case IrBinOpBinOr: case IrBinOpBinXor: case IrBinOpBinAnd: case IrBinOpAdd: case IrBinOpAddWrap: case IrBinOpSub: case IrBinOpSubWrap: case IrBinOpMult: case IrBinOpMultWrap: case IrBinOpDivUnspecified: case IrBinOpDivTrunc: case IrBinOpDivFloor: case IrBinOpDivExact: case IrBinOpRemUnspecified: case IrBinOpRemRem: case IrBinOpRemMod: return ir_analyze_bin_op_math(ira, bin_op_instruction); case IrBinOpArrayCat: return ir_analyze_array_cat(ira, bin_op_instruction); case IrBinOpArrayMult: return ir_analyze_array_mult(ira, bin_op_instruction); } zig_unreachable(); } static IrInstGen *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstSrcDeclVar *decl_var_instruction) { Error err; ZigVar *var = decl_var_instruction->var; ZigType *explicit_type = nullptr; IrInstGen *var_type = nullptr; if (decl_var_instruction->var_type != nullptr) { var_type = decl_var_instruction->var_type->child; ZigType *proposed_type = ir_resolve_type(ira, var_type); explicit_type = validate_var_type(ira->codegen, var_type->base.source_node, proposed_type); if (type_is_invalid(explicit_type)) { var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_inst_gen; } } AstNode *source_node = decl_var_instruction->base.base.source_node; bool is_comptime_var = ir_get_var_is_comptime(var); bool var_class_requires_const = false; IrInstGen *var_ptr = decl_var_instruction->ptr->child; // if this is null, a compiler error happened and did not initialize the variable. // if there are no compile errors there may be a missing ir_expr_wrap in pass1 IR generation. if (var_ptr == nullptr || type_is_invalid(var_ptr->value->type)) { ir_assert(var_ptr != nullptr || ira->codegen->errors.length != 0, &decl_var_instruction->base.base); var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_inst_gen; } // The ir_build_var_decl_src call is supposed to pass a pointer to the allocation, not an initialization value. ir_assert(var_ptr->value->type->id == ZigTypeIdPointer, &decl_var_instruction->base.base); ZigType *result_type = var_ptr->value->type->data.pointer.child_type; if (type_is_invalid(result_type)) { result_type = ira->codegen->builtin_types.entry_invalid; } else if (result_type->id == ZigTypeIdUnreachable || result_type->id == ZigTypeIdOpaque) { zig_unreachable(); } ZigValue *init_val = nullptr; if (instr_is_comptime(var_ptr) && var_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { ZigValue *ptr_val = ir_resolve_const(ira, var_ptr, UndefBad); if (ptr_val == nullptr) return ira->codegen->invalid_inst_gen; init_val = const_ptr_pointee(ira, ira->codegen, ptr_val, decl_var_instruction->base.base.source_node); if (init_val == nullptr) return ira->codegen->invalid_inst_gen; if (is_comptime_var) { if (var->gen_is_const) { var->const_value = init_val; } else { var->const_value = ira->codegen->pass1_arena->create(); copy_const_val(ira->codegen, var->const_value, init_val); } } } switch (type_requires_comptime(ira->codegen, result_type)) { case ReqCompTimeInvalid: result_type = ira->codegen->builtin_types.entry_invalid; break; case ReqCompTimeYes: var_class_requires_const = true; if (!var->gen_is_const && !is_comptime_var) { ir_add_error_node(ira, source_node, buf_sprintf("variable of type '%s' must be const or comptime", buf_ptr(&result_type->name))); result_type = ira->codegen->builtin_types.entry_invalid; } break; case ReqCompTimeNo: if (init_val != nullptr && value_is_comptime(init_val)) { if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, decl_var_instruction->base.base.source_node, init_val, UndefOk))) { result_type = ira->codegen->builtin_types.entry_invalid; } else if (init_val->type->id == ZigTypeIdFn && init_val->special != ConstValSpecialUndef && init_val->data.x_ptr.special == ConstPtrSpecialFunction && init_val->data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways) { var_class_requires_const = true; if (!var->src_is_const && !is_comptime_var) { ErrorMsg *msg = ir_add_error_node(ira, source_node, buf_sprintf("functions marked inline must be stored in const or comptime var")); AstNode *proto_node = init_val->data.x_ptr.data.fn.fn_entry->proto_node; add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here")); result_type = ira->codegen->builtin_types.entry_invalid; } } } break; } while (var->next_var != nullptr) { var = var->next_var; } // This must be done after possibly creating a new variable above var->ref_count = 0; var->ptr_instruction = var_ptr; var->var_type = result_type; assert(var->var_type); if (type_is_invalid(result_type)) { return ir_const_void(ira, &decl_var_instruction->base.base); } if (decl_var_instruction->align_value == nullptr) { if ((err = type_resolve(ira->codegen, result_type, ResolveStatusAlignmentKnown))) { var->var_type = ira->codegen->builtin_types.entry_invalid; return ir_const_void(ira, &decl_var_instruction->base.base); } var->align_bytes = get_ptr_align(ira->codegen, var_ptr->value->type); } else { if (!ir_resolve_align(ira, decl_var_instruction->align_value->child, nullptr, &var->align_bytes)) { var->var_type = ira->codegen->builtin_types.entry_invalid; } } if (init_val != nullptr && value_is_comptime(init_val)) { // Resolve ConstPtrMutInfer if (var->gen_is_const) { var_ptr->value->data.x_ptr.mut = ConstPtrMutComptimeConst; } else if (is_comptime_var) { var_ptr->value->data.x_ptr.mut = ConstPtrMutComptimeVar; } else { // we need a runtime ptr but we have a comptime val. // since it's a comptime val there are no instructions for it. // we memcpy the init value here IrInstGen *deref = ir_get_deref(ira, &var_ptr->base, var_ptr, nullptr); if (type_is_invalid(deref->value->type)) { var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_inst_gen; } // If this assertion trips, something is wrong with the IR instructions, because // we expected the above deref to return a constant value, but it created a runtime // instruction. assert(deref->value->special != ConstValSpecialRuntime); var_ptr->value->special = ConstValSpecialRuntime; ir_analyze_store_ptr(ira, &var_ptr->base, var_ptr, deref, false); } if (instr_is_comptime(var_ptr) && (is_comptime_var || (var_class_requires_const && var->gen_is_const))) { return ir_const_void(ira, &decl_var_instruction->base.base); } } else if (is_comptime_var) { ir_add_error(ira, &decl_var_instruction->base.base, buf_sprintf("cannot store runtime value in compile time variable")); var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_inst_gen; } ZigFn *fn_entry = ira->new_irb.exec->fn_entry; if (fn_entry) fn_entry->variable_list.append(var); return ir_build_var_decl_gen(ira, &decl_var_instruction->base.base, var, var_ptr); } static IrInstGen *ir_analyze_instruction_export(IrAnalyze *ira, IrInstSrcExport *instruction) { IrInstGen *target = instruction->target->child; if (type_is_invalid(target->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *options = instruction->options->child; if (type_is_invalid(options->value->type)) return ira->codegen->invalid_inst_gen; ZigType *options_type = options->value->type; assert(options_type->id == ZigTypeIdStruct); TypeStructField *name_field = find_struct_type_field(options_type, buf_create_from_str("name")); ir_assert(name_field != nullptr, &instruction->base.base); IrInstGen *name_inst = ir_analyze_struct_value_field_value(ira, &instruction->base.base, options, name_field); if (type_is_invalid(name_inst->value->type)) return ira->codegen->invalid_inst_gen; TypeStructField *linkage_field = find_struct_type_field(options_type, buf_create_from_str("linkage")); ir_assert(linkage_field != nullptr, &instruction->base.base); IrInstGen *linkage_inst = ir_analyze_struct_value_field_value(ira, &instruction->base.base, options, linkage_field); if (type_is_invalid(linkage_inst->value->type)) return ira->codegen->invalid_inst_gen; TypeStructField *section_field = find_struct_type_field(options_type, buf_create_from_str("section")); ir_assert(section_field != nullptr, &instruction->base.base); IrInstGen *section_inst = ir_analyze_struct_value_field_value(ira, &instruction->base.base, options, section_field); if (type_is_invalid(section_inst->value->type)) return ira->codegen->invalid_inst_gen; // The `section` field is optional, we have to unwrap it first IrInstGen *non_null_check = ir_analyze_test_non_null(ira, &instruction->base.base, section_inst); bool is_non_null; if (!ir_resolve_bool(ira, non_null_check, &is_non_null)) return ira->codegen->invalid_inst_gen; IrInstGen *section_str_inst = nullptr; if (is_non_null) { section_str_inst = ir_analyze_optional_value_payload_value(ira, &instruction->base.base, section_inst, false); if (type_is_invalid(section_str_inst->value->type)) return ira->codegen->invalid_inst_gen; } // Resolve all the comptime values Buf *symbol_name = ir_resolve_str(ira, name_inst); if (!symbol_name) return ira->codegen->invalid_inst_gen; if (buf_len(symbol_name) < 1) { ir_add_error(ira, &name_inst->base, buf_sprintf("exported symbol name cannot be empty")); return ira->codegen->invalid_inst_gen; } GlobalLinkageId global_linkage_id; if (!ir_resolve_global_linkage(ira, linkage_inst, &global_linkage_id)) return ira->codegen->invalid_inst_gen; Buf *section_name = nullptr; if (section_str_inst != nullptr && !(section_name = ir_resolve_str(ira, section_str_inst))) return ira->codegen->invalid_inst_gen; // TODO: This function needs to be audited. // It's not clear how all the different types are supposed to be handled. // Need comprehensive tests for exporting one thing in one file and declaring an extern var // in another file. TldFn *tld_fn = heap::c_allocator.create(); tld_fn->base.id = TldIdFn; tld_fn->base.source_node = instruction->base.base.source_node; auto entry = ira->codegen->exported_symbol_names.put_unique(symbol_name, &tld_fn->base); if (entry) { AstNode *other_export_node = entry->value->source_node; ErrorMsg *msg = ir_add_error(ira, &instruction->base.base, buf_sprintf("exported symbol collision: '%s'", buf_ptr(symbol_name))); add_error_note(ira->codegen, msg, other_export_node, buf_sprintf("other symbol is here")); return ira->codegen->invalid_inst_gen; } Error err; bool want_var_export = false; switch (target->value->type->id) { case ZigTypeIdInvalid: case ZigTypeIdUnreachable: zig_unreachable(); case ZigTypeIdFn: { assert(target->value->data.x_ptr.special == ConstPtrSpecialFunction); ZigFn *fn_entry = target->value->data.x_ptr.data.fn.fn_entry; tld_fn->fn_entry = fn_entry; CallingConvention cc = fn_entry->type_entry->data.fn.fn_type_id.cc; switch (cc) { case CallingConventionUnspecified: { ErrorMsg *msg = ir_add_error(ira, &target->base, 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->base, buf_sprintf("exported function cannot be async")); add_error_note(ira->codegen, msg, fn_entry->proto_node, buf_sprintf("declared here")); } break; case CallingConventionC: case CallingConventionCold: case CallingConventionNaked: case CallingConventionInterrupt: case CallingConventionSignal: case CallingConventionStdcall: case CallingConventionFastcall: case CallingConventionVectorcall: case CallingConventionThiscall: case CallingConventionAPCS: case CallingConventionAAPCS: case CallingConventionAAPCSVFP: add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, cc); fn_entry->section_name = section_name; break; } } break; case ZigTypeIdStruct: if (is_slice(target->value->type)) { ir_add_error(ira, &target->base, 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->base, buf_sprintf("exported struct value must be declared extern")); add_error_note(ira->codegen, msg, target->value->type->data.structure.decl_node, buf_sprintf("declared here")); } else { want_var_export = true; } break; case ZigTypeIdUnion: if (target->value->type->data.unionation.layout != ContainerLayoutExtern) { ErrorMsg *msg = ir_add_error(ira, &target->base, buf_sprintf("exported union value must be declared extern")); add_error_note(ira->codegen, msg, target->value->type->data.unionation.decl_node, buf_sprintf("declared here")); } else { want_var_export = true; } break; case ZigTypeIdEnum: if (target->value->type->data.enumeration.layout != ContainerLayoutExtern) { ErrorMsg *msg = ir_add_error(ira, &target->base, buf_sprintf("exported enum value must be declared extern")); add_error_note(ira->codegen, msg, target->value->type->data.enumeration.decl_node, buf_sprintf("declared here")); } else { want_var_export = true; } break; case ZigTypeIdArray: { bool ok_type; if ((err = type_allowed_in_extern(ira->codegen, target->value->type->data.array.child_type, &ok_type))) return ira->codegen->invalid_inst_gen; if (!ok_type) { ir_add_error(ira, &target->base, buf_sprintf("array element type '%s' not extern-compatible", buf_ptr(&target->value->type->data.array.child_type->name))); } else { want_var_export = true; } break; } case ZigTypeIdMetaType: { ZigType *type_value = target->value->data.x_type; switch (type_value->id) { case ZigTypeIdInvalid: zig_unreachable(); case ZigTypeIdStruct: if (is_slice(type_value)) { ir_add_error(ira, &target->base, 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->base, 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->base, 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->base, 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->base, buf_sprintf("exported function type must specify calling convention")); } } break; case ZigTypeIdInt: case ZigTypeIdFloat: case ZigTypeIdPointer: case ZigTypeIdArray: case ZigTypeIdBool: case ZigTypeIdVector: break; case ZigTypeIdMetaType: case ZigTypeIdVoid: case ZigTypeIdUnreachable: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdEnumLiteral: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdOptional: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdBoundFn: case ZigTypeIdOpaque: case ZigTypeIdFnFrame: case ZigTypeIdAnyFrame: ir_add_error(ira, &target->base, buf_sprintf("invalid export target '%s'", buf_ptr(&type_value->name))); break; } } break; case ZigTypeIdInt: want_var_export = true; break; case ZigTypeIdVoid: case ZigTypeIdBool: case ZigTypeIdFloat: case ZigTypeIdPointer: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdOptional: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdVector: zig_panic("TODO export const value of type %s", buf_ptr(&target->value->type->name)); case ZigTypeIdBoundFn: case ZigTypeIdOpaque: case ZigTypeIdEnumLiteral: case ZigTypeIdFnFrame: case ZigTypeIdAnyFrame: ir_add_error(ira, &target->base, buf_sprintf("invalid export target type '%s'", buf_ptr(&target->value->type->name))); break; } // TODO audit the various ways to use @export if (want_var_export && target->id == IrInstGenIdLoadPtr) { IrInstGenLoadPtr *load_ptr = reinterpret_cast(target); if (load_ptr->ptr->id == IrInstGenIdVarPtr) { IrInstGenVarPtr *var_ptr = reinterpret_cast(load_ptr->ptr); ZigVar *var = var_ptr->var; add_var_export(ira->codegen, var, buf_ptr(symbol_name), global_linkage_id); var->section_name = section_name; } } return ir_const_void(ira, &instruction->base.base); } static bool exec_has_err_ret_trace(CodeGen *g, IrExecutableSrc *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 IrInstGen *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, IrInstSrcErrorReturnTrace *instruction) { ZigType *ptr_to_stack_trace_type = get_pointer_to_type(ira->codegen, get_stack_trace_type(ira->codegen), false); if (instruction->optional == IrInstErrorReturnTraceNull) { ZigType *optional_type = get_optional_type(ira->codegen, ptr_to_stack_trace_type); if (!exec_has_err_ret_trace(ira->codegen, ira->old_irb.exec)) { IrInstGen *result = ir_const(ira, &instruction->base.base, optional_type); ZigValue *out_val = result->value; assert(get_src_ptr_type(optional_type) != nullptr); out_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; out_val->data.x_ptr.data.hard_coded_addr.addr = 0; return result; } return ir_build_error_return_trace_gen(ira, instruction->base.base.scope, instruction->base.base.source_node, instruction->optional, optional_type); } else { assert(ira->codegen->have_err_ret_tracing); return ir_build_error_return_trace_gen(ira, instruction->base.base.scope, instruction->base.base.source_node, instruction->optional, ptr_to_stack_trace_type); } } static IrInstGen *ir_analyze_instruction_error_union(IrAnalyze *ira, IrInstSrcErrorUnion *instruction) { IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type); result->value->special = ConstValSpecialLazy; LazyValueErrUnionType *lazy_err_union_type = heap::c_allocator.create(); lazy_err_union_type->ira = ira; ira_ref(ira); result->value->data.x_lazy = &lazy_err_union_type->base; lazy_err_union_type->base.id = LazyValueIdErrUnionType; lazy_err_union_type->err_set_type = instruction->err_set->child; if (ir_resolve_type_lazy(ira, lazy_err_union_type->err_set_type) == nullptr) return ira->codegen->invalid_inst_gen; lazy_err_union_type->payload_type = instruction->payload->child; if (ir_resolve_type_lazy(ira, lazy_err_union_type->payload_type) == nullptr) return ira->codegen->invalid_inst_gen; return result; } static IrInstGen *ir_analyze_alloca(IrAnalyze *ira, IrInst *source_inst, ZigType *var_type, uint32_t align, const char *name_hint, bool force_comptime) { Error err; ZigValue *pointee = ira->codegen->pass1_arena->create(); pointee->special = ConstValSpecialUndef; pointee->llvm_align = align; IrInstGenAlloca *result = ir_build_alloca_gen(ira, source_inst, align, name_hint); result->base.value->special = ConstValSpecialStatic; result->base.value->data.x_ptr.special = ConstPtrSpecialRef; result->base.value->data.x_ptr.mut = force_comptime ? ConstPtrMutComptimeVar : ConstPtrMutInfer; result->base.value->data.x_ptr.data.ref.pointee = pointee; bool var_type_has_bits; if ((err = type_has_bits2(ira->codegen, var_type, &var_type_has_bits))) return ira->codegen->invalid_inst_gen; if (align != 0) { if ((err = type_resolve(ira->codegen, var_type, ResolveStatusAlignmentKnown))) return ira->codegen->invalid_inst_gen; if (!var_type_has_bits) { ir_add_error(ira, source_inst, buf_sprintf("variable '%s' of zero-bit type '%s' has no in-memory representation, it cannot be aligned", name_hint, buf_ptr(&var_type->name))); return ira->codegen->invalid_inst_gen; } } assert(result->base.value->data.x_ptr.special != ConstPtrSpecialInvalid); pointee->type = var_type; result->base.value->type = get_pointer_to_type_extra(ira->codegen, var_type, false, false, PtrLenSingle, align, 0, 0, false); if (!force_comptime) { ZigFn *fn_entry = ira->new_irb.exec->fn_entry; if (fn_entry != nullptr) { fn_entry->alloca_gen_list.append(result); } } return &result->base; } static ZigType *ir_result_loc_expected_type(IrAnalyze *ira, IrInst *suspend_source_instr, ResultLoc *result_loc) { switch (result_loc->id) { case ResultLocIdInvalid: case ResultLocIdPeerParent: zig_unreachable(); case ResultLocIdNone: case ResultLocIdVar: case ResultLocIdBitCast: case ResultLocIdCast: return nullptr; case ResultLocIdInstruction: return result_loc->source_instruction->child->value->type; case ResultLocIdReturn: return ira->explicit_return_type; case ResultLocIdPeer: return reinterpret_cast(result_loc)->parent->resolved_type; } zig_unreachable(); } static bool type_can_bit_cast(ZigType *t) { switch (t->id) { case ZigTypeIdInvalid: zig_unreachable(); case ZigTypeIdMetaType: case ZigTypeIdOpaque: case ZigTypeIdBoundFn: case ZigTypeIdUnreachable: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdEnumLiteral: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdPointer: return false; default: // TODO list these types out explicitly, there are probably some other invalid ones here return true; } } static void set_up_result_loc_for_inferred_comptime(IrAnalyze *ira, IrInstGen *ptr) { ZigValue *undef_child = ira->codegen->pass1_arena->create(); undef_child->type = ptr->value->type->data.pointer.child_type; undef_child->special = ConstValSpecialUndef; ptr->value->special = ConstValSpecialStatic; ptr->value->data.x_ptr.mut = ConstPtrMutInfer; ptr->value->data.x_ptr.special = ConstPtrSpecialRef; ptr->value->data.x_ptr.data.ref.pointee = undef_child; } static Error ir_result_has_type(IrAnalyze *ira, ResultLoc *result_loc, bool *out) { switch (result_loc->id) { case ResultLocIdInvalid: case ResultLocIdPeerParent: zig_unreachable(); case ResultLocIdNone: case ResultLocIdPeer: *out = false; return ErrorNone; case ResultLocIdReturn: case ResultLocIdInstruction: case ResultLocIdBitCast: *out = true; return ErrorNone; case ResultLocIdCast: { ResultLocCast *result_cast = reinterpret_cast(result_loc); ZigType *dest_type = ir_resolve_type(ira, result_cast->base.source_instruction->child); if (type_is_invalid(dest_type)) return ErrorSemanticAnalyzeFail; *out = (dest_type != ira->codegen->builtin_types.entry_var); return ErrorNone; } case ResultLocIdVar: *out = reinterpret_cast(result_loc)->var->decl_node->data.variable_declaration.type != nullptr; return ErrorNone; } zig_unreachable(); } static IrInstGen *ir_resolve_no_result_loc(IrAnalyze *ira, IrInst *suspend_source_instr, ResultLoc *result_loc, ZigType *value_type) { if (type_is_invalid(value_type)) return ira->codegen->invalid_inst_gen; IrInstGenAlloca *alloca_gen = ir_build_alloca_gen(ira, suspend_source_instr, 0, ""); alloca_gen->base.value->type = get_pointer_to_type_extra(ira->codegen, value_type, false, false, PtrLenSingle, 0, 0, 0, false); set_up_result_loc_for_inferred_comptime(ira, &alloca_gen->base); ZigFn *fn_entry = ira->new_irb.exec->fn_entry; if (fn_entry != nullptr && get_scope_typeof(suspend_source_instr->scope) == nullptr) { fn_entry->alloca_gen_list.append(alloca_gen); } result_loc->written = true; result_loc->resolved_loc = &alloca_gen->base; return result_loc->resolved_loc; } static bool result_loc_is_discard(ResultLoc *result_loc_pass1) { if (result_loc_pass1->id == ResultLocIdInstruction && result_loc_pass1->source_instruction->id == IrInstSrcIdConst) { IrInstSrcConst *const_inst = reinterpret_cast(result_loc_pass1->source_instruction); if (value_is_comptime(const_inst->value) && const_inst->value->type->id == ZigTypeIdPointer && const_inst->value->data.x_ptr.special == ConstPtrSpecialDiscard) { return true; } } return false; } // when calling this function, at the callsite must check for result type noreturn and propagate it up static IrInstGen *ir_resolve_result_raw(IrAnalyze *ira, IrInst *suspend_source_instr, ResultLoc *result_loc, ZigType *value_type, IrInstGen *value, bool force_runtime, bool allow_discard) { Error err; if (result_loc->resolved_loc != nullptr) { // allow to redo the result location if the value is known and comptime and the previous one isn't if (value == nullptr || !instr_is_comptime(value) || instr_is_comptime(result_loc->resolved_loc)) { return result_loc->resolved_loc; } } result_loc->gen_instruction = value; result_loc->implicit_elem_type = value_type; switch (result_loc->id) { case ResultLocIdInvalid: case ResultLocIdPeerParent: zig_unreachable(); case ResultLocIdNone: { if (value != nullptr) { return nullptr; } // need to return a result location and don't have one. use a stack allocation return ir_resolve_no_result_loc(ira, suspend_source_instr, result_loc, value_type); } case ResultLocIdVar: { ResultLocVar *result_loc_var = reinterpret_cast(result_loc); assert(result_loc->source_instruction->id == IrInstSrcIdAlloca); IrInstSrcAlloca *alloca_src = reinterpret_cast(result_loc->source_instruction); ZigVar *var = result_loc_var->var; if (var->var_type != nullptr && !ir_get_var_is_comptime(var)) { // This is at least the second time we've seen this variable declaration during analysis. // This means that this is actually a different variable due to, e.g. an inline while loop. // We make a new variable so that it can hold a different type, and so the debug info can // be distinct. ZigVar *new_var = create_local_var(ira->codegen, var->decl_node, var->child_scope, buf_create_from_str(var->name), var->src_is_const, var->gen_is_const, var->shadowable, var->is_comptime, true); new_var->align_bytes = var->align_bytes; var->next_var = new_var; var = new_var; } if (value_type->id == ZigTypeIdUnreachable || value_type->id == ZigTypeIdOpaque) { ir_add_error(ira, &result_loc->source_instruction->base, buf_sprintf("variable of type '%s' not allowed", buf_ptr(&value_type->name))); return ira->codegen->invalid_inst_gen; } if (alloca_src->base.child == nullptr || var->ptr_instruction == nullptr) { bool force_comptime; if (!ir_resolve_comptime(ira, alloca_src->is_comptime->child, &force_comptime)) return ira->codegen->invalid_inst_gen; uint32_t align = 0; if (alloca_src->align != nullptr && !ir_resolve_align(ira, alloca_src->align->child, nullptr, &align)) { return ira->codegen->invalid_inst_gen; } IrInstGen *alloca_gen = ir_analyze_alloca(ira, &result_loc->source_instruction->base, value_type, align, alloca_src->name_hint, force_comptime); if (force_runtime) { alloca_gen->value->data.x_ptr.mut = ConstPtrMutRuntimeVar; alloca_gen->value->special = ConstValSpecialRuntime; } if (alloca_src->base.child != nullptr && !result_loc->written) { alloca_src->base.child->base.ref_count = 0; } alloca_src->base.child = alloca_gen; var->ptr_instruction = alloca_gen; } result_loc->written = true; result_loc->resolved_loc = alloca_src->base.child; return alloca_src->base.child; } case ResultLocIdInstruction: { result_loc->written = true; result_loc->resolved_loc = result_loc->source_instruction->child; return result_loc->resolved_loc; } case ResultLocIdReturn: { if (value != nullptr) { reinterpret_cast(result_loc)->implicit_return_type_done = true; ira->src_implicit_return_type_list.append(value); } result_loc->written = true; result_loc->resolved_loc = ira->return_ptr; return result_loc->resolved_loc; } case ResultLocIdPeer: { ResultLocPeer *result_peer = reinterpret_cast(result_loc); ResultLocPeerParent *peer_parent = result_peer->parent; if (peer_parent->peers.length == 1) { IrInstGen *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, peer_parent->parent, value_type, value, force_runtime, true); result_peer->suspend_pos.basic_block_index = SIZE_MAX; result_peer->suspend_pos.instruction_index = SIZE_MAX; if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) || parent_result_loc->value->type->id == ZigTypeIdUnreachable) { return parent_result_loc; } result_loc->written = true; result_loc->resolved_loc = parent_result_loc; return result_loc->resolved_loc; } bool is_condition_comptime; if (!ir_resolve_comptime(ira, peer_parent->is_comptime->child, &is_condition_comptime)) return ira->codegen->invalid_inst_gen; if (is_condition_comptime) { peer_parent->skipped = true; return ir_resolve_result(ira, suspend_source_instr, peer_parent->parent, value_type, value, force_runtime, true); } bool peer_parent_has_type; if ((err = ir_result_has_type(ira, peer_parent->parent, &peer_parent_has_type))) return ira->codegen->invalid_inst_gen; if (peer_parent_has_type) { peer_parent->skipped = true; IrInstGen *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, peer_parent->parent, value_type, value, force_runtime || !is_condition_comptime, true); if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) || parent_result_loc->value->type->id == ZigTypeIdUnreachable) { return parent_result_loc; } peer_parent->parent->written = true; result_loc->written = true; result_loc->resolved_loc = parent_result_loc; return result_loc->resolved_loc; } if (peer_parent->resolved_type == nullptr) { if (peer_parent->end_bb->suspend_instruction_ref == nullptr) { peer_parent->end_bb->suspend_instruction_ref = suspend_source_instr; } IrInstGen *unreach_inst = ira_suspend(ira, suspend_source_instr, result_peer->next_bb, &result_peer->suspend_pos); if (result_peer->next_bb == nullptr) { ir_start_next_bb(ira); } return unreach_inst; } IrInstGen *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, peer_parent->parent, peer_parent->resolved_type, nullptr, force_runtime, true); if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) || parent_result_loc->value->type->id == ZigTypeIdUnreachable) { return parent_result_loc; } // because is_condition_comptime is false, we mark this a runtime pointer parent_result_loc->value->special = ConstValSpecialRuntime; result_loc->written = true; result_loc->resolved_loc = parent_result_loc; return result_loc->resolved_loc; } case ResultLocIdCast: { ResultLocCast *result_cast = reinterpret_cast(result_loc); ZigType *dest_type = ir_resolve_type(ira, result_cast->base.source_instruction->child); if (type_is_invalid(dest_type)) return ira->codegen->invalid_inst_gen; if (dest_type == ira->codegen->builtin_types.entry_var) { return ir_resolve_no_result_loc(ira, suspend_source_instr, result_loc, value_type); } IrInstGen *casted_value; if (value != nullptr) { casted_value = ir_implicit_cast2(ira, suspend_source_instr, value, dest_type); if (type_is_invalid(casted_value->value->type)) return ira->codegen->invalid_inst_gen; dest_type = casted_value->value->type; } else { casted_value = nullptr; } IrInstGen *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, result_cast->parent, dest_type, casted_value, force_runtime, true); if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) || parent_result_loc->value->type->id == ZigTypeIdUnreachable) { return parent_result_loc; } ZigType *parent_ptr_type = parent_result_loc->value->type; assert(parent_ptr_type->id == ZigTypeIdPointer); if ((err = type_resolve(ira->codegen, parent_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { return ira->codegen->invalid_inst_gen; } uint64_t parent_ptr_align = get_ptr_align(ira->codegen, parent_ptr_type); if ((err = type_resolve(ira->codegen, value_type, ResolveStatusAlignmentKnown))) { return ira->codegen->invalid_inst_gen; } if (!type_has_bits(ira->codegen, value_type)) { parent_ptr_align = 0; } // If we're casting from a sentinel-terminated array to a non-sentinel-terminated array, // we actually need the result location pointer to *not* have a sentinel. Otherwise the generated // memcpy will write an extra byte to the destination, and THAT'S NO GOOD. ZigType *ptr_elem_type; if (value_type->id == ZigTypeIdArray && value_type->data.array.sentinel != nullptr && dest_type->id == ZigTypeIdArray && dest_type->data.array.sentinel == nullptr) { ptr_elem_type = get_array_type(ira->codegen, value_type->data.array.child_type, value_type->data.array.len, nullptr); } else { ptr_elem_type = value_type; } ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, ptr_elem_type, parent_ptr_type->data.pointer.is_const, parent_ptr_type->data.pointer.is_volatile, PtrLenSingle, parent_ptr_align, 0, 0, parent_ptr_type->data.pointer.allow_zero); ConstCastOnly const_cast_result = types_match_const_cast_only(ira, parent_result_loc->value->type, ptr_type, result_cast->base.source_instruction->base.source_node, false); if (const_cast_result.id == ConstCastResultIdInvalid) return ira->codegen->invalid_inst_gen; if (const_cast_result.id != ConstCastResultIdOk) { if (allow_discard) { return parent_result_loc; } // We will not be able to provide a result location for this value. Create // a new result location. result_cast->parent->written = false; return ir_resolve_no_result_loc(ira, suspend_source_instr, result_loc, value_type); } result_loc->written = true; result_loc->resolved_loc = ir_analyze_ptr_cast(ira, suspend_source_instr, parent_result_loc, &parent_result_loc->base, ptr_type, &result_cast->base.source_instruction->base, false, false); return result_loc->resolved_loc; } case ResultLocIdBitCast: { ResultLocBitCast *result_bit_cast = reinterpret_cast(result_loc); ZigType *dest_type = ir_resolve_type(ira, result_bit_cast->base.source_instruction->child); if (type_is_invalid(dest_type)) return ira->codegen->invalid_inst_gen; ZigType *dest_cg_ptr_type; if ((err = get_codegen_ptr_type(ira->codegen, dest_type, &dest_cg_ptr_type))) return ira->codegen->invalid_inst_gen; if (dest_cg_ptr_type != nullptr) { ir_add_error(ira, &result_loc->source_instruction->base, buf_sprintf("unable to @bitCast to pointer type '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_inst_gen; } if (!type_can_bit_cast(dest_type)) { ir_add_error(ira, &result_loc->source_instruction->base, buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_inst_gen; } ZigType *value_cg_ptr_type; if ((err = get_codegen_ptr_type(ira->codegen, value_type, &value_cg_ptr_type))) return ira->codegen->invalid_inst_gen; if (value_cg_ptr_type != nullptr) { ir_add_error(ira, suspend_source_instr, buf_sprintf("unable to @bitCast from pointer type '%s'", buf_ptr(&value_type->name))); return ira->codegen->invalid_inst_gen; } if (!type_can_bit_cast(value_type)) { ir_add_error(ira, suspend_source_instr, buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&value_type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *bitcasted_value; if (value != nullptr) { bitcasted_value = ir_analyze_bit_cast(ira, &result_loc->source_instruction->base, value, dest_type); dest_type = bitcasted_value->value->type; } else { bitcasted_value = nullptr; } if (bitcasted_value != nullptr && type_is_invalid(bitcasted_value->value->type)) { return bitcasted_value; } bool parent_was_written = result_bit_cast->parent->written; IrInstGen *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, result_bit_cast->parent, dest_type, bitcasted_value, force_runtime, true); if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) || parent_result_loc->value->type->id == ZigTypeIdUnreachable) { return parent_result_loc; } ZigType *parent_ptr_type = parent_result_loc->value->type; assert(parent_ptr_type->id == ZigTypeIdPointer); ZigType *child_type = parent_ptr_type->data.pointer.child_type; if (result_loc_is_discard(result_bit_cast->parent)) { assert(allow_discard); return parent_result_loc; } if ((err = type_resolve(ira->codegen, child_type, ResolveStatusSizeKnown))) { return ira->codegen->invalid_inst_gen; } if ((err = type_resolve(ira->codegen, value_type, ResolveStatusSizeKnown))) { return ira->codegen->invalid_inst_gen; } if (child_type != ira->codegen->builtin_types.entry_var) { if (type_size(ira->codegen, child_type) != type_size(ira->codegen, value_type)) { // pointer cast won't work; we need a temporary location. result_bit_cast->parent->written = parent_was_written; result_loc->written = true; result_loc->resolved_loc = ir_resolve_result(ira, suspend_source_instr, no_result_loc(), value_type, bitcasted_value, force_runtime, true); return result_loc->resolved_loc; } } uint64_t parent_ptr_align = 0; if (type_has_bits(ira->codegen, value_type)) parent_ptr_align = get_ptr_align(ira->codegen, parent_ptr_type); ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value_type, parent_ptr_type->data.pointer.is_const, parent_ptr_type->data.pointer.is_volatile, PtrLenSingle, parent_ptr_align, 0, 0, parent_ptr_type->data.pointer.allow_zero); result_loc->written = true; result_loc->resolved_loc = ir_analyze_ptr_cast(ira, suspend_source_instr, parent_result_loc, &parent_result_loc->base, ptr_type, &result_bit_cast->base.source_instruction->base, false, false); return result_loc->resolved_loc; } } zig_unreachable(); } static IrInstGen *ir_resolve_result(IrAnalyze *ira, IrInst *suspend_source_instr, ResultLoc *result_loc_pass1, ZigType *value_type, IrInstGen *value, bool force_runtime, bool allow_discard) { if (!allow_discard && result_loc_is_discard(result_loc_pass1)) { result_loc_pass1 = no_result_loc(); } bool was_written = result_loc_pass1->written; IrInstGen *result_loc = ir_resolve_result_raw(ira, suspend_source_instr, result_loc_pass1, value_type, value, force_runtime, allow_discard); if (result_loc == nullptr || result_loc->value->type->id == ZigTypeIdUnreachable || type_is_invalid(result_loc->value->type)) { return result_loc; } if ((force_runtime || (value != nullptr && !instr_is_comptime(value))) && result_loc_pass1->written && result_loc->value->data.x_ptr.mut == ConstPtrMutInfer) { result_loc->value->special = ConstValSpecialRuntime; } InferredStructField *isf = result_loc->value->type->data.pointer.inferred_struct_field; if (isf != nullptr) { TypeStructField *field; IrInstGen *casted_ptr; if (isf->already_resolved) { field = find_struct_type_field(isf->inferred_struct_type, isf->field_name); casted_ptr = result_loc; } else { isf->already_resolved = true; // Now it's time to add the field to the struct type. uint32_t old_field_count = isf->inferred_struct_type->data.structure.src_field_count; uint32_t new_field_count = old_field_count + 1; isf->inferred_struct_type->data.structure.src_field_count = new_field_count; isf->inferred_struct_type->data.structure.fields = realloc_type_struct_fields( isf->inferred_struct_type->data.structure.fields, old_field_count, new_field_count); field = isf->inferred_struct_type->data.structure.fields[old_field_count]; field->name = isf->field_name; field->type_entry = value_type; field->type_val = create_const_type(ira->codegen, field->type_entry); field->src_index = old_field_count; field->decl_node = value ? value->base.source_node : suspend_source_instr->source_node; if (value && instr_is_comptime(value)) { ZigValue *val = ir_resolve_const(ira, value, UndefOk); if (!val) return ira->codegen->invalid_inst_gen; field->is_comptime = true; field->init_val = ira->codegen->pass1_arena->create(); copy_const_val(ira->codegen, field->init_val, val); return result_loc; } ZigType *struct_ptr_type = get_pointer_to_type(ira->codegen, isf->inferred_struct_type, false); if (instr_is_comptime(result_loc)) { casted_ptr = ir_const(ira, suspend_source_instr, struct_ptr_type); copy_const_val(ira->codegen, casted_ptr->value, result_loc->value); casted_ptr->value->type = struct_ptr_type; } else { casted_ptr = result_loc; } if (instr_is_comptime(casted_ptr)) { ZigValue *ptr_val = ir_resolve_const(ira, casted_ptr, UndefBad); if (!ptr_val) return ira->codegen->invalid_inst_gen; if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { ZigValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val, suspend_source_instr->source_node); struct_val->special = ConstValSpecialStatic; struct_val->data.x_struct.fields = realloc_const_vals_ptrs(ira->codegen, struct_val->data.x_struct.fields, old_field_count, new_field_count); ZigValue *field_val = struct_val->data.x_struct.fields[old_field_count]; field_val->special = ConstValSpecialUndef; field_val->type = field->type_entry; field_val->parent.id = ConstParentIdStruct; field_val->parent.data.p_struct.struct_val = struct_val; field_val->parent.data.p_struct.field_index = old_field_count; } } } result_loc = ir_analyze_struct_field_ptr(ira, suspend_source_instr, field, casted_ptr, isf->inferred_struct_type, true); result_loc_pass1->resolved_loc = result_loc; } if (was_written) { return result_loc; } ir_assert(result_loc->value->type->id == ZigTypeIdPointer, suspend_source_instr); ZigType *actual_elem_type = result_loc->value->type->data.pointer.child_type; if (actual_elem_type->id == ZigTypeIdOptional && value_type->id != ZigTypeIdOptional && value_type->id != ZigTypeIdNull && value_type->id != ZigTypeIdUndefined) { bool same_comptime_repr = types_have_same_zig_comptime_repr(ira->codegen, actual_elem_type, value_type); if (!same_comptime_repr) { result_loc_pass1->written = was_written; return ir_analyze_unwrap_optional_payload(ira, suspend_source_instr, result_loc, false, true); } } else if (actual_elem_type->id == ZigTypeIdErrorUnion && value_type->id != ZigTypeIdErrorUnion && value_type->id != ZigTypeIdUndefined) { if (value_type->id == ZigTypeIdErrorSet) { return ir_analyze_unwrap_err_code(ira, suspend_source_instr, result_loc, true); } else { IrInstGen *unwrapped_err_ptr = ir_analyze_unwrap_error_payload(ira, suspend_source_instr, result_loc, false, true); ZigType *actual_payload_type = actual_elem_type->data.error_union.payload_type; if (actual_payload_type->id == ZigTypeIdOptional && value_type->id != ZigTypeIdOptional && value_type->id != ZigTypeIdNull && value_type->id != ZigTypeIdUndefined) { return ir_analyze_unwrap_optional_payload(ira, suspend_source_instr, unwrapped_err_ptr, false, true); } else { return unwrapped_err_ptr; } } } return result_loc; } static IrInstGen *ir_analyze_instruction_resolve_result(IrAnalyze *ira, IrInstSrcResolveResult *instruction) { ZigType *implicit_elem_type; if (instruction->ty == nullptr) { if (instruction->result_loc->id == ResultLocIdCast) { implicit_elem_type = ir_resolve_type(ira, instruction->result_loc->source_instruction->child); if (type_is_invalid(implicit_elem_type)) return ira->codegen->invalid_inst_gen; } else if (instruction->result_loc->id == ResultLocIdReturn) { implicit_elem_type = ira->explicit_return_type; if (type_is_invalid(implicit_elem_type)) return ira->codegen->invalid_inst_gen; } else { implicit_elem_type = ira->codegen->builtin_types.entry_var; } if (implicit_elem_type == ira->codegen->builtin_types.entry_var) { Buf *bare_name = buf_alloc(); Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct), instruction->base.base.scope, instruction->base.base.source_node, bare_name); StructSpecial struct_special = StructSpecialInferredStruct; if (instruction->base.base.source_node->type == NodeTypeContainerInitExpr && instruction->base.base.source_node->data.container_init_expr.kind == ContainerInitKindArray) { struct_special = StructSpecialInferredTuple; } ZigType *inferred_struct_type = get_partial_container_type(ira->codegen, instruction->base.base.scope, ContainerKindStruct, instruction->base.base.source_node, buf_ptr(name), bare_name, ContainerLayoutAuto); inferred_struct_type->data.structure.special = struct_special; inferred_struct_type->data.structure.resolve_status = ResolveStatusBeingInferred; implicit_elem_type = inferred_struct_type; } } else { implicit_elem_type = ir_resolve_type(ira, instruction->ty->child); if (type_is_invalid(implicit_elem_type)) return ira->codegen->invalid_inst_gen; } IrInstGen *result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, implicit_elem_type, nullptr, false, true); if (result_loc != nullptr) return result_loc; ZigFn *fn = ira->new_irb.exec->fn_entry; if (fn != nullptr && fn->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync && instruction->result_loc->id == ResultLocIdReturn) { result_loc = ir_resolve_result(ira, &instruction->base.base, no_result_loc(), implicit_elem_type, nullptr, false, true); if (result_loc != nullptr && (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable)) { return result_loc; } result_loc->value->special = ConstValSpecialRuntime; return result_loc; } IrInstGen *result = ir_const(ira, &instruction->base.base, implicit_elem_type); result->value->special = ConstValSpecialUndef; IrInstGen *ptr = ir_get_ref(ira, &instruction->base.base, result, false, false); ptr->value->data.x_ptr.mut = ConstPtrMutComptimeVar; return ptr; } static void ir_reset_result(ResultLoc *result_loc) { result_loc->written = false; result_loc->resolved_loc = nullptr; result_loc->gen_instruction = nullptr; result_loc->implicit_elem_type = nullptr; switch (result_loc->id) { case ResultLocIdInvalid: zig_unreachable(); case ResultLocIdPeerParent: { ResultLocPeerParent *peer_parent = reinterpret_cast(result_loc); peer_parent->skipped = false; peer_parent->done_resuming = false; peer_parent->resolved_type = nullptr; for (size_t i = 0; i < peer_parent->peers.length; i += 1) { ir_reset_result(&peer_parent->peers.at(i)->base); } break; } case ResultLocIdVar: { IrInstSrcAlloca *alloca_src = reinterpret_cast(result_loc->source_instruction); alloca_src->base.child = nullptr; break; } case ResultLocIdReturn: reinterpret_cast(result_loc)->implicit_return_type_done = false; break; case ResultLocIdPeer: case ResultLocIdNone: case ResultLocIdInstruction: case ResultLocIdBitCast: case ResultLocIdCast: break; } } static IrInstGen *ir_analyze_instruction_reset_result(IrAnalyze *ira, IrInstSrcResetResult *instruction) { ir_reset_result(instruction->result_loc); return ir_const_void(ira, &instruction->base.base); } static IrInstGen *get_async_call_result_loc(IrAnalyze *ira, IrInst* source_instr, ZigType *fn_ret_type, bool is_async_call_builtin, IrInstGen **args_ptr, size_t args_len, IrInstGen *ret_ptr_uncasted) { ir_assert(is_async_call_builtin, source_instr); if (type_is_invalid(ret_ptr_uncasted->value->type)) return ira->codegen->invalid_inst_gen; if (ret_ptr_uncasted->value->type->id == ZigTypeIdVoid) { // Result location will be inside the async frame. return nullptr; } return ir_implicit_cast(ira, ret_ptr_uncasted, get_pointer_to_type(ira->codegen, fn_ret_type, false)); } static IrInstGen *ir_analyze_async_call(IrAnalyze *ira, IrInst* source_instr, ZigFn *fn_entry, ZigType *fn_type, IrInstGen *fn_ref, IrInstGen **casted_args, size_t arg_count, IrInstGen *casted_new_stack, bool is_async_call_builtin, IrInstGen *ret_ptr_uncasted, ResultLoc *call_result_loc) { if (fn_entry == nullptr) { if (fn_type->data.fn.fn_type_id.cc != CallingConventionAsync) { ir_add_error(ira, &fn_ref->base, buf_sprintf("expected async function, found '%s'", buf_ptr(&fn_type->name))); return ira->codegen->invalid_inst_gen; } if (casted_new_stack == nullptr) { ir_add_error(ira, &fn_ref->base, buf_sprintf("function is not comptime-known; @asyncCall required")); return ira->codegen->invalid_inst_gen; } } if (casted_new_stack != nullptr) { ZigType *fn_ret_type = fn_type->data.fn.fn_type_id.return_type; IrInstGen *ret_ptr = get_async_call_result_loc(ira, source_instr, fn_ret_type, is_async_call_builtin, casted_args, arg_count, ret_ptr_uncasted); if (ret_ptr != nullptr && type_is_invalid(ret_ptr->value->type)) return ira->codegen->invalid_inst_gen; ZigType *anyframe_type = get_any_frame_type(ira->codegen, fn_ret_type); IrInstGenCall *call_gen = ir_build_call_gen(ira, source_instr, fn_entry, fn_ref, arg_count, casted_args, CallModifierAsync, casted_new_stack, is_async_call_builtin, ret_ptr, anyframe_type); return &call_gen->base; } else { ZigType *frame_type = get_fn_frame_type(ira->codegen, fn_entry); IrInstGen *result_loc = ir_resolve_result(ira, source_instr, call_result_loc, frame_type, nullptr, true, false); if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) { return result_loc; } result_loc = ir_implicit_cast2(ira, source_instr, result_loc, get_pointer_to_type(ira->codegen, frame_type, false)); if (type_is_invalid(result_loc->value->type)) return ira->codegen->invalid_inst_gen; return &ir_build_call_gen(ira, source_instr, fn_entry, fn_ref, arg_count, casted_args, CallModifierAsync, casted_new_stack, is_async_call_builtin, result_loc, frame_type)->base; } } static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node, IrInstGen *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); IrInstGen *casted_arg; if (param_decl_node->data.param_decl.var_token == nullptr) { AstNode *param_type_node = param_decl_node->data.param_decl.type; ZigType *param_type = ir_analyze_type_expr(ira, *exec_scope, param_type_node); if (type_is_invalid(param_type)) return false; casted_arg = ir_implicit_cast(ira, arg, param_type); if (type_is_invalid(casted_arg->value->type)) return false; } else { casted_arg = arg; } ZigValue *arg_val = ir_resolve_const(ira, casted_arg, UndefOk); if (!arg_val) return false; Buf *param_name = param_decl_node->data.param_decl.name; ZigVar *var = add_variable(ira->codegen, param_decl_node, *exec_scope, param_name, true, arg_val, nullptr, arg_val->type); *exec_scope = var->child_scope; *next_proto_i += 1; return true; } static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_node, IrInstGen *arg, IrInst *arg_src, Scope **child_scope, size_t *next_proto_i, GenericFnTypeId *generic_id, FnTypeId *fn_type_id, IrInstGen **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; IrInstGen *casted_arg; if (is_var_args) { arg_part_of_generic_id = true; casted_arg = arg; } else { if (param_decl_node->data.param_decl.var_token == nullptr) { AstNode *param_type_node = param_decl_node->data.param_decl.type; ZigType *param_type = ir_analyze_type_expr(ira, *child_scope, param_type_node); if (type_is_invalid(param_type)) return false; casted_arg = ir_implicit_cast2(ira, arg_src, arg, param_type); if (type_is_invalid(casted_arg->value->type)) return false; } else { arg_part_of_generic_id = true; casted_arg = arg; } } bool comptime_arg = param_decl_node->data.param_decl.is_comptime; if (!comptime_arg) { switch (type_requires_comptime(ira->codegen, casted_arg->value->type)) { case ReqCompTimeInvalid: return false; case ReqCompTimeYes: comptime_arg = true; break; case ReqCompTimeNo: break; } } ZigValue *arg_val; if (comptime_arg) { arg_part_of_generic_id = true; arg_val = ir_resolve_const(ira, casted_arg, UndefBad); if (!arg_val) return false; } else { arg_val = create_const_runtime(ira->codegen, casted_arg->value->type); } if (arg_part_of_generic_id) { copy_const_val(ira->codegen, &generic_id->params[generic_id->param_count], arg_val); generic_id->param_count += 1; } Buf *param_name = param_decl_node->data.param_decl.name; if (!param_name) return false; if (!is_var_args) { ZigVar *var = add_variable(ira->codegen, param_decl_node, *child_scope, param_name, true, arg_val, nullptr, arg_val->type); *child_scope = var->child_scope; var->shadowable = !comptime_arg; *next_proto_i += 1; } else if (casted_arg->value->type->id == ZigTypeIdComptimeInt || casted_arg->value->type->id == ZigTypeIdComptimeFloat) { ir_add_error(ira, &casted_arg->base, buf_sprintf("compiler bug: integer and float literals in var args function must be casted. https://github.com/ziglang/zig/issues/557")); return false; } if (!comptime_arg) { casted_args[fn_type_id->param_count] = casted_arg; FnTypeParamInfo *param_info = &fn_type_id->param_info[fn_type_id->param_count]; param_info->type = casted_arg->value->type; param_info->is_noalias = param_decl_node->data.param_decl.is_noalias; impl_fn->param_source_nodes[fn_type_id->param_count] = param_decl_node; fn_type_id->param_count += 1; } return true; } static IrInstGen *ir_get_var_ptr(IrAnalyze *ira, IrInst *source_instr, ZigVar *var) { while (var->next_var != nullptr) { var = var->next_var; } if (var->var_type == nullptr || type_is_invalid(var->var_type)) return ira->codegen->invalid_inst_gen; bool is_volatile = false; ZigType *var_ptr_type = get_pointer_to_type_extra(ira->codegen, var->var_type, var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0, false); if (var->ptr_instruction != nullptr) { return ir_implicit_cast(ira, var->ptr_instruction, var_ptr_type); } bool comptime_var_mem = ir_get_var_is_comptime(var); bool linkage_makes_it_runtime = var->decl_node->data.variable_declaration.is_extern; IrInstGen *result = ir_build_var_ptr_gen(ira, source_instr, var); result->value->type = var_ptr_type; if (!linkage_makes_it_runtime && !var->is_thread_local && value_is_comptime(var->const_value)) { ZigValue *val = var->const_value; switch (val->special) { case ConstValSpecialRuntime: break; case ConstValSpecialStatic: // fallthrough case ConstValSpecialLazy: // fallthrough case ConstValSpecialUndef: { ConstPtrMut ptr_mut; if (comptime_var_mem) { ptr_mut = ConstPtrMutComptimeVar; } else if (var->gen_is_const) { ptr_mut = ConstPtrMutComptimeConst; } else { assert(!comptime_var_mem); ptr_mut = ConstPtrMutRuntimeVar; } result->value->special = ConstValSpecialStatic; result->value->data.x_ptr.mut = ptr_mut; result->value->data.x_ptr.special = ConstPtrSpecialRef; result->value->data.x_ptr.data.ref.pointee = val; return result; } } } bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr); result->value->data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack; return result; } // This function is called when a comptime value becomes accessible at runtime. static void mark_comptime_value_escape(IrAnalyze *ira, IrInst* source_instr, ZigValue *val) { ir_assert(value_is_comptime(val), source_instr); if (val->special == ConstValSpecialUndef) return; if (val->type->id == ZigTypeIdFn && val->type->data.fn.fn_type_id.cc == CallingConventionUnspecified) { ir_assert(val->data.x_ptr.special == ConstPtrSpecialFunction, source_instr); if (val->data.x_ptr.data.fn.fn_entry->non_async_node == nullptr) { val->data.x_ptr.data.fn.fn_entry->non_async_node = source_instr->source_node; } } } static IrInstGen *ir_analyze_store_ptr(IrAnalyze *ira, IrInst* source_instr, IrInstGen *ptr, IrInstGen *uncasted_value, bool allow_write_through_const) { assert(ptr->value->type->id == ZigTypeIdPointer); if (ptr->value->data.x_ptr.special == ConstPtrSpecialDiscard) { if (uncasted_value->value->type->id == ZigTypeIdErrorUnion || uncasted_value->value->type->id == ZigTypeIdErrorSet) { ir_add_error(ira, source_instr, buf_sprintf("error is discarded")); return ira->codegen->invalid_inst_gen; } return ir_const_void(ira, source_instr); } if (ptr->value->type->data.pointer.is_const && !allow_write_through_const) { ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); return ira->codegen->invalid_inst_gen; } ZigType *child_type = ptr->value->type->data.pointer.child_type; IrInstGen *value = ir_implicit_cast(ira, uncasted_value, child_type); if (type_is_invalid(value->value->type)) return ira->codegen->invalid_inst_gen; switch (type_has_one_possible_value(ira->codegen, child_type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_inst_gen; case OnePossibleValueYes: return ir_const_void(ira, source_instr); case OnePossibleValueNo: break; } if (instr_is_comptime(ptr) && ptr->value->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { if (!allow_write_through_const && ptr->value->data.x_ptr.mut == ConstPtrMutComptimeConst) { ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); return ira->codegen->invalid_inst_gen; } if ((allow_write_through_const && ptr->value->data.x_ptr.mut == ConstPtrMutComptimeConst) || ptr->value->data.x_ptr.mut == ConstPtrMutComptimeVar || ptr->value->data.x_ptr.mut == ConstPtrMutInfer) { if (instr_is_comptime(value)) { ZigValue *dest_val = const_ptr_pointee(ira, ira->codegen, ptr->value, source_instr->source_node); if (dest_val == nullptr) return ira->codegen->invalid_inst_gen; if (dest_val->special != ConstValSpecialRuntime) { copy_const_val(ira->codegen, dest_val, value->value); if (ptr->value->data.x_ptr.mut == ConstPtrMutComptimeVar && !ira->new_irb.current_basic_block->must_be_comptime_source_instr) { ira->new_irb.current_basic_block->must_be_comptime_source_instr = source_instr; } return ir_const_void(ira, source_instr); } } if (ptr->value->data.x_ptr.mut == ConstPtrMutInfer) { ptr->value->special = ConstValSpecialRuntime; } else { ir_add_error(ira, source_instr, buf_sprintf("cannot store runtime value in compile time variable")); ZigValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, ptr->value); dest_val->type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_inst_gen; } } } if (ptr->value->type->data.pointer.inferred_struct_field != nullptr && child_type == ira->codegen->builtin_types.entry_var) { child_type = ptr->value->type->data.pointer.inferred_struct_field->inferred_struct_type; } switch (type_requires_comptime(ira->codegen, child_type)) { case ReqCompTimeInvalid: return ira->codegen->invalid_inst_gen; case ReqCompTimeYes: switch (type_has_one_possible_value(ira->codegen, ptr->value->type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_inst_gen; case OnePossibleValueNo: ir_add_error(ira, source_instr, buf_sprintf("cannot store runtime value in type '%s'", buf_ptr(&child_type->name))); return ira->codegen->invalid_inst_gen; case OnePossibleValueYes: return ir_const_void(ira, source_instr); } zig_unreachable(); case ReqCompTimeNo: break; } if (instr_is_comptime(value)) { mark_comptime_value_escape(ira, source_instr, value->value); } // If this is a store to a pointer with a runtime-known vector index, // we have to figure out the IrInstGen which represents the index and // emit a IrInstGenVectorStoreElem, or emit a compile error // explaining why it is impossible for this store to work. Which is that // the pointer address is of the vector; without the element index being known // we cannot properly perform the insertion. if (ptr->value->type->data.pointer.vector_index == VECTOR_INDEX_RUNTIME) { if (ptr->id == IrInstGenIdElemPtr) { IrInstGenElemPtr *elem_ptr = (IrInstGenElemPtr *)ptr; return ir_build_vector_store_elem(ira, source_instr, elem_ptr->array_ptr, elem_ptr->elem_index, value); } ir_add_error(ira, &ptr->base, buf_sprintf("unable to determine vector element index of type '%s'", buf_ptr(&ptr->value->type->name))); return ira->codegen->invalid_inst_gen; } return ir_build_store_ptr_gen(ira, source_instr, ptr, value); } static IrInstGen *analyze_casted_new_stack(IrAnalyze *ira, IrInst* source_instr, IrInstGen *new_stack, IrInst *new_stack_src, bool is_async_call_builtin, ZigFn *fn_entry) { if (new_stack == nullptr) return nullptr; if (!is_async_call_builtin && arch_stack_pointer_register_name(ira->codegen->zig_target->arch) == nullptr) { ir_add_error(ira, source_instr, buf_sprintf("target arch '%s' does not support calling with a new stack", target_arch_name(ira->codegen->zig_target->arch))); } if (is_async_call_builtin && fn_entry != nullptr && new_stack->value->type->id == ZigTypeIdPointer && new_stack->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame) { ZigType *needed_frame_type = get_pointer_to_type(ira->codegen, get_fn_frame_type(ira->codegen, fn_entry), false); return ir_implicit_cast(ira, new_stack, needed_frame_type); } else { ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, false, false, PtrLenUnknown, target_fn_align(ira->codegen->zig_target), 0, 0, false); ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr); ira->codegen->need_frame_size_prefix_data = true; return ir_implicit_cast2(ira, new_stack_src, new_stack, u8_slice); } } static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr, ZigFn *fn_entry, ZigType *fn_type, IrInstGen *fn_ref, IrInstGen *first_arg_ptr, IrInst *first_arg_ptr_src, CallModifier modifier, IrInstGen *new_stack, IrInst *new_stack_src, bool is_async_call_builtin, IrInstGen **args_ptr, size_t args_len, IrInstGen *ret_ptr, ResultLoc *call_result_loc) { Error err; FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; size_t first_arg_1_or_0 = first_arg_ptr ? 1 : 0; // for extern functions, the var args argument is not counted. // for zig functions, it is. size_t var_args_1_or_0; if (fn_type_id->cc == CallingConventionC) { var_args_1_or_0 = 0; } else { var_args_1_or_0 = fn_type_id->is_var_args ? 1 : 0; } size_t src_param_count = fn_type_id->param_count - var_args_1_or_0; size_t call_param_count = args_len + first_arg_1_or_0; AstNode *source_node = source_instr->source_node; AstNode *fn_proto_node = fn_entry ? fn_entry->proto_node : nullptr;; if (fn_type_id->cc == CallingConventionNaked) { ErrorMsg *msg = ir_add_error(ira, &fn_ref->base, buf_sprintf("unable to call function with naked calling convention")); if (fn_proto_node) { add_error_note(ira->codegen, msg, fn_proto_node, buf_sprintf("declared here")); } return ira->codegen->invalid_inst_gen; } if (fn_type_id->is_var_args) { if (call_param_count < src_param_count) { ErrorMsg *msg = ir_add_error_node(ira, source_node, buf_sprintf("expected at least %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize "", src_param_count, call_param_count)); if (fn_proto_node) { add_error_note(ira->codegen, msg, fn_proto_node, buf_sprintf("declared here")); } return ira->codegen->invalid_inst_gen; } } else if (src_param_count != call_param_count) { ErrorMsg *msg = ir_add_error_node(ira, source_node, buf_sprintf("expected %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize "", src_param_count, call_param_count)); if (fn_proto_node) { add_error_note(ira->codegen, msg, fn_proto_node, buf_sprintf("declared here")); } return ira->codegen->invalid_inst_gen; } if (modifier == CallModifierCompileTime) { // If we are evaluating an extern function in a TypeOf call, we can return an undefined value // of its return type. if (fn_entry != nullptr && get_scope_typeof(source_instr->scope) != nullptr && fn_proto_node->data.fn_proto.is_extern) { assert(fn_entry->body_node == nullptr); AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type; ZigType *return_type = ir_analyze_type_expr(ira, source_instr->scope, return_type_node); if (type_is_invalid(return_type)) return ira->codegen->invalid_inst_gen; return ir_const_undef(ira, source_instr, return_type); } // 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->base, buf_sprintf("unable to evaluate constant expression")); return ira->codegen->invalid_inst_gen; } if (!ir_emit_backward_branch(ira, source_instr)) return ira->codegen->invalid_inst_gen; // Fork a scope of the function with known values for the parameters. Scope *exec_scope = &fn_entry->fndef_scope->base; size_t next_proto_i = 0; if (first_arg_ptr) { assert(first_arg_ptr->value->type->id == ZigTypeIdPointer); bool first_arg_known_bare = false; if (fn_type_id->next_param_index >= 1) { ZigType *param_type = fn_type_id->param_info[next_proto_i].type; if (type_is_invalid(param_type)) return ira->codegen->invalid_inst_gen; first_arg_known_bare = param_type->id != ZigTypeIdPointer; } IrInstGen *first_arg; if (!first_arg_known_bare && handle_is_ptr(ira->codegen, first_arg_ptr->value->type->data.pointer.child_type)) { first_arg = first_arg_ptr; } else { first_arg = ir_get_deref(ira, &first_arg_ptr->base, first_arg_ptr, nullptr); if (type_is_invalid(first_arg->value->type)) return ira->codegen->invalid_inst_gen; } if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, first_arg, &exec_scope, &next_proto_i)) return ira->codegen->invalid_inst_gen; } for (size_t call_i = 0; call_i < args_len; call_i += 1) { IrInstGen *old_arg = args_ptr[call_i]; if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, old_arg, &exec_scope, &next_proto_i)) return ira->codegen->invalid_inst_gen; } AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type; ZigType *specified_return_type = ir_analyze_type_expr(ira, exec_scope, return_type_node); if (type_is_invalid(specified_return_type)) return ira->codegen->invalid_inst_gen; ZigType *return_type; ZigType *inferred_err_set_type = nullptr; if (fn_proto_node->data.fn_proto.auto_err_set) { inferred_err_set_type = get_auto_err_set_type(ira->codegen, fn_entry); if ((err = type_resolve(ira->codegen, specified_return_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_inst_gen; return_type = get_error_union_type(ira->codegen, inferred_err_set_type, specified_return_type); } else { return_type = specified_return_type; } bool cacheable = fn_eval_cacheable(exec_scope, return_type); ZigValue *result = nullptr; if (cacheable) { auto entry = ira->codegen->memoized_fn_eval_table.maybe_get(exec_scope); if (entry) result = entry->value; } if (result == nullptr) { // Analyze the fn body block like any other constant expression. AstNode *body_node = fn_entry->body_node; ZigValue *result_ptr; create_result_ptr(ira->codegen, return_type, &result, &result_ptr); if ((err = ir_eval_const_value(ira->codegen, exec_scope, body_node, result_ptr, ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry, nullptr, source_instr->source_node, nullptr, ira->new_irb.exec, return_type_node, UndefOk))) { return ira->codegen->invalid_inst_gen; } if (inferred_err_set_type != nullptr) { inferred_err_set_type->data.error_set.incomplete = false; if (result->type->id == ZigTypeIdErrorUnion) { ErrorTableEntry *err = result->data.x_err_union.error_set->data.x_err_set; if (err != nullptr) { inferred_err_set_type->data.error_set.err_count = 1; inferred_err_set_type->data.error_set.errors = heap::c_allocator.create(); inferred_err_set_type->data.error_set.errors[0] = err; } ZigType *fn_inferred_err_set_type = result->type->data.error_union.err_set_type; inferred_err_set_type->data.error_set.err_count = fn_inferred_err_set_type->data.error_set.err_count; inferred_err_set_type->data.error_set.errors = fn_inferred_err_set_type->data.error_set.errors; } else if (result->type->id == ZigTypeIdErrorSet) { inferred_err_set_type->data.error_set.err_count = result->type->data.error_set.err_count; inferred_err_set_type->data.error_set.errors = result->type->data.error_set.errors; } } if (cacheable) { ira->codegen->memoized_fn_eval_table.put(exec_scope, result); } if (type_is_invalid(result->type)) { return ira->codegen->invalid_inst_gen; } } IrInstGen *new_instruction = ir_const_move(ira, source_instr, result); return ir_finish_anal(ira, new_instruction); } if (fn_type->data.fn.is_generic) { if (!fn_entry) { ir_add_error(ira, &fn_ref->base, buf_sprintf("calling a generic function requires compile-time known function value")); return ira->codegen->invalid_inst_gen; } size_t new_fn_arg_count = first_arg_1_or_0 + args_len; IrInstGen **casted_args = heap::c_allocator.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(ira->codegen, fn_proto_node); impl_fn->param_source_nodes = heap::c_allocator.allocate(new_fn_arg_count); buf_init_from_buf(&impl_fn->symbol_name, &fn_entry->symbol_name); impl_fn->fndef_scope = create_fndef_scope(ira->codegen, impl_fn->body_node, parent_scope, impl_fn); impl_fn->child_scope = &impl_fn->fndef_scope->base; FnTypeId inst_fn_type_id = {0}; init_fn_type_id(&inst_fn_type_id, fn_proto_node, fn_type_id->cc, new_fn_arg_count); inst_fn_type_id.param_count = 0; inst_fn_type_id.is_var_args = false; // TODO maybe GenericFnTypeId can be replaced with using the child_scope directly // as the key in generic_table GenericFnTypeId *generic_id = heap::c_allocator.create(); generic_id->fn_entry = fn_entry; generic_id->param_count = 0; generic_id->params = ira->codegen->pass1_arena->allocate(new_fn_arg_count); size_t next_proto_i = 0; if (first_arg_ptr) { assert(first_arg_ptr->value->type->id == ZigTypeIdPointer); bool first_arg_known_bare = false; if (fn_type_id->next_param_index >= 1) { ZigType *param_type = fn_type_id->param_info[next_proto_i].type; if (type_is_invalid(param_type)) return ira->codegen->invalid_inst_gen; first_arg_known_bare = param_type->id != ZigTypeIdPointer; } IrInstGen *first_arg; if (!first_arg_known_bare && handle_is_ptr(ira->codegen, first_arg_ptr->value->type->data.pointer.child_type)) { first_arg = first_arg_ptr; } else { first_arg = ir_get_deref(ira, &first_arg_ptr->base, first_arg_ptr, nullptr); if (type_is_invalid(first_arg->value->type)) return ira->codegen->invalid_inst_gen; } if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, first_arg, first_arg_ptr_src, &impl_fn->child_scope, &next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn)) { return ira->codegen->invalid_inst_gen; } } ZigFn *parent_fn_entry = ira->new_irb.exec->fn_entry; assert(parent_fn_entry); for (size_t call_i = 0; call_i < args_len; call_i += 1) { IrInstGen *arg = args_ptr[call_i]; AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i); assert(param_decl_node->type == NodeTypeParamDecl); if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg, &arg->base, &impl_fn->child_scope, &next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn)) { return ira->codegen->invalid_inst_gen; } } if (fn_proto_node->data.fn_proto.align_expr != nullptr) { ZigValue *align_result; ZigValue *result_ptr; create_result_ptr(ira->codegen, get_align_amt_type(ira->codegen), &align_result, &result_ptr); if ((err = ir_eval_const_value(ira->codegen, impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr, result_ptr, ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec, nullptr, UndefBad))) { return ira->codegen->invalid_inst_gen; } IrInstGenConst *const_instruction = ir_create_inst_noval(&ira->new_irb, impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr); const_instruction->base.value = align_result; uint32_t align_bytes = 0; ir_resolve_align(ira, &const_instruction->base, nullptr, &align_bytes); impl_fn->align_bytes = align_bytes; inst_fn_type_id.alignment = align_bytes; } if (fn_proto_node->data.fn_proto.return_var_token == nullptr) { AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type; ZigType *specified_return_type = ir_analyze_type_expr(ira, impl_fn->child_scope, return_type_node); if (type_is_invalid(specified_return_type)) return ira->codegen->invalid_inst_gen; if(!is_valid_return_type(specified_return_type)){ ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("call to generic function with %s return type '%s' not allowed", type_id_name(specified_return_type->id), buf_ptr(&specified_return_type->name))); add_error_note(ira->codegen, msg, fn_proto_node, buf_sprintf("function declared here")); Tld *tld = find_decl(ira->codegen, &fn_entry->fndef_scope->base, &specified_return_type->name); if (tld != nullptr) { add_error_note(ira->codegen, msg, tld->source_node, buf_sprintf("type declared here")); } return ira->codegen->invalid_inst_gen; } if (fn_proto_node->data.fn_proto.auto_err_set) { ZigType *inferred_err_set_type = get_auto_err_set_type(ira->codegen, impl_fn); if ((err = type_resolve(ira->codegen, specified_return_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_inst_gen; inst_fn_type_id.return_type = get_error_union_type(ira->codegen, inferred_err_set_type, specified_return_type); } else { inst_fn_type_id.return_type = specified_return_type; } switch (type_requires_comptime(ira->codegen, specified_return_type)) { case ReqCompTimeYes: // Throw out our work and call the function as if it were comptime. return ir_analyze_fn_call(ira, source_instr, fn_entry, fn_type, fn_ref, first_arg_ptr, first_arg_ptr_src, CallModifierCompileTime, new_stack, new_stack_src, is_async_call_builtin, args_ptr, args_len, ret_ptr, call_result_loc); case ReqCompTimeInvalid: return ira->codegen->invalid_inst_gen; case ReqCompTimeNo: break; } } auto existing_entry = ira->codegen->generic_table.put_unique(generic_id, impl_fn); if (existing_entry) { // throw away all our work and use the existing function impl_fn = existing_entry->value; } else { // finish instantiating the function impl_fn->type_entry = get_fn_type(ira->codegen, &inst_fn_type_id); if (type_is_invalid(impl_fn->type_entry)) return ira->codegen->invalid_inst_gen; impl_fn->ir_executable->source_node = source_instr->source_node; impl_fn->ir_executable->parent_exec = ira->new_irb.exec; impl_fn->analyzed_executable.source_node = source_instr->source_node; impl_fn->analyzed_executable.parent_exec = ira->new_irb.exec; impl_fn->analyzed_executable.backward_branch_quota = ira->new_irb.exec->backward_branch_quota; impl_fn->analyzed_executable.is_generic_instantiation = true; ira->codegen->fn_defs.append(impl_fn); } FnTypeId *impl_fn_type_id = &impl_fn->type_entry->data.fn.fn_type_id; if (fn_type_can_fail(impl_fn_type_id)) { parent_fn_entry->calls_or_awaits_errorable_fn = true; } IrInstGen *casted_new_stack = analyze_casted_new_stack(ira, source_instr, new_stack, new_stack_src, is_async_call_builtin, impl_fn); if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value->type)) return ira->codegen->invalid_inst_gen; size_t impl_param_count = impl_fn_type_id->param_count; if (modifier == CallModifierAsync) { IrInstGen *result = ir_analyze_async_call(ira, source_instr, impl_fn, impl_fn->type_entry, nullptr, casted_args, impl_param_count, casted_new_stack, is_async_call_builtin, ret_ptr, call_result_loc); return ir_finish_anal(ira, result); } IrInstGen *result_loc; if (handle_is_ptr(ira->codegen, impl_fn_type_id->return_type)) { result_loc = ir_resolve_result(ira, source_instr, call_result_loc, impl_fn_type_id->return_type, nullptr, true, false); if (result_loc != nullptr) { if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) { return result_loc; } if (result_loc->value->type->data.pointer.is_const) { ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); return ira->codegen->invalid_inst_gen; } IrInstGen *dummy_value = ir_const(ira, source_instr, impl_fn_type_id->return_type); dummy_value->value->special = ConstValSpecialRuntime; IrInstGen *dummy_result = ir_implicit_cast2(ira, source_instr, dummy_value, result_loc->value->type->data.pointer.child_type); if (type_is_invalid(dummy_result->value->type)) return ira->codegen->invalid_inst_gen; ZigType *res_child_type = result_loc->value->type->data.pointer.child_type; if (res_child_type == ira->codegen->builtin_types.entry_var) { res_child_type = impl_fn_type_id->return_type; } if (!handle_is_ptr(ira->codegen, res_child_type)) { ir_reset_result(call_result_loc); result_loc = nullptr; } } } else if (is_async_call_builtin) { result_loc = get_async_call_result_loc(ira, source_instr, impl_fn_type_id->return_type, is_async_call_builtin, args_ptr, args_len, ret_ptr); if (result_loc != nullptr && type_is_invalid(result_loc->value->type)) return ira->codegen->invalid_inst_gen; } else { result_loc = nullptr; } if (impl_fn_type_id->cc == CallingConventionAsync && parent_fn_entry->inferred_async_node == nullptr && modifier != CallModifierNoSuspend) { parent_fn_entry->inferred_async_node = fn_ref->base.source_node; parent_fn_entry->inferred_async_fn = impl_fn; } IrInstGenCall *new_call_instruction = ir_build_call_gen(ira, source_instr, impl_fn, nullptr, impl_param_count, casted_args, modifier, casted_new_stack, is_async_call_builtin, result_loc, impl_fn_type_id->return_type); if (get_scope_typeof(source_instr->scope) == nullptr) { parent_fn_entry->call_list.append(new_call_instruction); } return ir_finish_anal(ira, &new_call_instruction->base); } ZigFn *parent_fn_entry = ira->new_irb.exec->fn_entry; 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; } IrInstGen **casted_args = heap::c_allocator.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->invalid_inst_gen; IrInstGen *first_arg; if (param_type->id == ZigTypeIdPointer && handle_is_ptr(ira->codegen, first_arg_ptr->value->type->data.pointer.child_type)) { first_arg = first_arg_ptr; } else { first_arg = ir_get_deref(ira, &first_arg_ptr->base, first_arg_ptr, nullptr); if (type_is_invalid(first_arg->value->type)) return ira->codegen->invalid_inst_gen; } IrInstGen *casted_arg = ir_implicit_cast2(ira, first_arg_ptr_src, first_arg, param_type); if (type_is_invalid(casted_arg->value->type)) return ira->codegen->invalid_inst_gen; casted_args[next_arg_index] = casted_arg; next_arg_index += 1; } for (size_t call_i = 0; call_i < args_len; call_i += 1) { IrInstGen *old_arg = args_ptr[call_i]; if (type_is_invalid(old_arg->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_arg; if (next_arg_index < src_param_count) { ZigType *param_type = fn_type_id->param_info[next_arg_index].type; if (type_is_invalid(param_type)) return ira->codegen->invalid_inst_gen; casted_arg = ir_implicit_cast(ira, old_arg, param_type); if (type_is_invalid(casted_arg->value->type)) return ira->codegen->invalid_inst_gen; } else { casted_arg = old_arg; } casted_args[next_arg_index] = casted_arg; next_arg_index += 1; } assert(next_arg_index == call_param_count); ZigType *return_type = fn_type_id->return_type; if (type_is_invalid(return_type)) return ira->codegen->invalid_inst_gen; if (fn_entry != nullptr && fn_entry->fn_inline == FnInlineAlways && modifier == CallModifierNeverInline) { ir_add_error(ira, source_instr, buf_sprintf("no-inline call of inline function")); return ira->codegen->invalid_inst_gen; } IrInstGen *casted_new_stack = analyze_casted_new_stack(ira, source_instr, new_stack, new_stack_src, is_async_call_builtin, fn_entry); if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value->type)) return ira->codegen->invalid_inst_gen; if (modifier == CallModifierAsync) { IrInstGen *result = ir_analyze_async_call(ira, source_instr, fn_entry, fn_type, fn_ref, casted_args, call_param_count, casted_new_stack, is_async_call_builtin, ret_ptr, call_result_loc); return ir_finish_anal(ira, result); } if (fn_type_id->cc == CallingConventionAsync && parent_fn_entry->inferred_async_node == nullptr && modifier != CallModifierNoSuspend) { parent_fn_entry->inferred_async_node = fn_ref->base.source_node; parent_fn_entry->inferred_async_fn = fn_entry; } IrInstGen *result_loc; if (handle_is_ptr(ira->codegen, return_type)) { result_loc = ir_resolve_result(ira, source_instr, call_result_loc, return_type, nullptr, true, false); if (result_loc != nullptr) { if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) { return result_loc; } if (result_loc->value->type->data.pointer.is_const) { ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); return ira->codegen->invalid_inst_gen; } IrInstGen *dummy_value = ir_const(ira, source_instr, return_type); dummy_value->value->special = ConstValSpecialRuntime; IrInstGen *dummy_result = ir_implicit_cast2(ira, source_instr, dummy_value, result_loc->value->type->data.pointer.child_type); if (type_is_invalid(dummy_result->value->type)) return ira->codegen->invalid_inst_gen; ZigType *res_child_type = result_loc->value->type->data.pointer.child_type; if (res_child_type == ira->codegen->builtin_types.entry_var) { res_child_type = return_type; } if (!handle_is_ptr(ira->codegen, res_child_type)) { ir_reset_result(call_result_loc); result_loc = nullptr; } } } else if (is_async_call_builtin) { result_loc = get_async_call_result_loc(ira, source_instr, return_type, is_async_call_builtin, args_ptr, args_len, ret_ptr); if (result_loc != nullptr && type_is_invalid(result_loc->value->type)) return ira->codegen->invalid_inst_gen; } else { result_loc = nullptr; } IrInstGenCall *new_call_instruction = ir_build_call_gen(ira, source_instr, fn_entry, fn_ref, call_param_count, casted_args, modifier, casted_new_stack, is_async_call_builtin, result_loc, return_type); if (get_scope_typeof(source_instr->scope) == nullptr) { parent_fn_entry->call_list.append(new_call_instruction); } return ir_finish_anal(ira, &new_call_instruction->base); } static IrInstGen *ir_analyze_fn_call_src(IrAnalyze *ira, IrInstSrcCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type, IrInstGen *fn_ref, IrInstGen *first_arg_ptr, IrInst *first_arg_ptr_src, CallModifier modifier) { IrInstGen *new_stack = nullptr; IrInst *new_stack_src = nullptr; if (call_instruction->new_stack) { new_stack = call_instruction->new_stack->child; if (type_is_invalid(new_stack->value->type)) return ira->codegen->invalid_inst_gen; new_stack_src = &call_instruction->new_stack->base; } IrInstGen **args_ptr = heap::c_allocator.allocate(call_instruction->arg_count); for (size_t i = 0; i < call_instruction->arg_count; i += 1) { args_ptr[i] = call_instruction->args[i]->child; if (type_is_invalid(args_ptr[i]->value->type)) return ira->codegen->invalid_inst_gen; } IrInstGen *ret_ptr = nullptr; if (call_instruction->ret_ptr != nullptr) { ret_ptr = call_instruction->ret_ptr->child; if (type_is_invalid(ret_ptr->value->type)) return ira->codegen->invalid_inst_gen; } IrInstGen *result = ir_analyze_fn_call(ira, &call_instruction->base.base, fn_entry, fn_type, fn_ref, first_arg_ptr, first_arg_ptr_src, modifier, new_stack, new_stack_src, call_instruction->is_async_call_builtin, args_ptr, call_instruction->arg_count, ret_ptr, call_instruction->result_loc); heap::c_allocator.deallocate(args_ptr, call_instruction->arg_count); return result; } static IrInstGen *ir_analyze_call_extra(IrAnalyze *ira, IrInst* source_instr, IrInstSrc *pass1_options, IrInstSrc *pass1_fn_ref, IrInstGen **args_ptr, size_t args_len, ResultLoc *result_loc) { IrInstGen *options = pass1_options->child; if (type_is_invalid(options->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *fn_ref = pass1_fn_ref->child; if (type_is_invalid(fn_ref->value->type)) return ira->codegen->invalid_inst_gen; TypeStructField *modifier_field = find_struct_type_field(options->value->type, buf_create_from_str("modifier")); ir_assert(modifier_field != nullptr, source_instr); IrInstGen *modifier_inst = ir_analyze_struct_value_field_value(ira, source_instr, options, modifier_field); ZigValue *modifier_val = ir_resolve_const(ira, modifier_inst, UndefBad); if (modifier_val == nullptr) return ira->codegen->invalid_inst_gen; CallModifier modifier = (CallModifier)bigint_as_u32(&modifier_val->data.x_enum_tag); if (ir_should_inline(ira->old_irb.exec, source_instr->scope)) { switch (modifier) { case CallModifierBuiltin: zig_unreachable(); case CallModifierAsync: ir_add_error(ira, source_instr, buf_sprintf("TODO: comptime @call with async modifier")); return ira->codegen->invalid_inst_gen; case CallModifierCompileTime: case CallModifierNone: case CallModifierAlwaysInline: case CallModifierAlwaysTail: case CallModifierNoSuspend: modifier = CallModifierCompileTime; break; case CallModifierNeverInline: ir_add_error(ira, source_instr, buf_sprintf("unable to perform 'never_inline' call at compile-time")); return ira->codegen->invalid_inst_gen; case CallModifierNeverTail: ir_add_error(ira, source_instr, buf_sprintf("unable to perform 'never_tail' call at compile-time")); return ira->codegen->invalid_inst_gen; } } IrInstGen *first_arg_ptr = nullptr; IrInst *first_arg_ptr_src = nullptr; ZigFn *fn = nullptr; if (instr_is_comptime(fn_ref)) { if (fn_ref->value->type->id == ZigTypeIdBoundFn) { assert(fn_ref->value->special == ConstValSpecialStatic); fn = fn_ref->value->data.x_bound_fn.fn; first_arg_ptr = fn_ref->value->data.x_bound_fn.first_arg; first_arg_ptr_src = fn_ref->value->data.x_bound_fn.first_arg_src; if (type_is_invalid(first_arg_ptr->value->type)) return ira->codegen->invalid_inst_gen; } else { fn = ir_resolve_fn(ira, fn_ref); } } // Some modifiers require the callee to be comptime-known switch (modifier) { case CallModifierCompileTime: case CallModifierAlwaysInline: case CallModifierAsync: if (fn == nullptr) { ir_add_error(ira, &modifier_inst->base, buf_sprintf("the specified modifier requires a comptime-known function")); return ira->codegen->invalid_inst_gen; } ZIG_FALLTHROUGH; default: break; } ZigType *fn_type = (fn != nullptr) ? fn->type_entry : fn_ref->value->type; TypeStructField *stack_field = find_struct_type_field(options->value->type, buf_create_from_str("stack")); ir_assert(stack_field != nullptr, source_instr); IrInstGen *opt_stack = ir_analyze_struct_value_field_value(ira, source_instr, options, stack_field); if (type_is_invalid(opt_stack->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *stack_is_non_null_inst = ir_analyze_test_non_null(ira, source_instr, opt_stack); bool stack_is_non_null; if (!ir_resolve_bool(ira, stack_is_non_null_inst, &stack_is_non_null)) return ira->codegen->invalid_inst_gen; IrInstGen *stack = nullptr; IrInst *stack_src = nullptr; if (stack_is_non_null) { stack = ir_analyze_optional_value_payload_value(ira, source_instr, opt_stack, false); if (type_is_invalid(stack->value->type)) return ira->codegen->invalid_inst_gen; stack_src = &stack->base; } return ir_analyze_fn_call(ira, source_instr, fn, fn_type, fn_ref, first_arg_ptr, first_arg_ptr_src, modifier, stack, stack_src, false, args_ptr, args_len, nullptr, result_loc); } static IrInstGen *ir_analyze_instruction_call_extra(IrAnalyze *ira, IrInstSrcCallExtra *instruction) { IrInstGen *args = instruction->args->child; ZigType *args_type = args->value->type; if (type_is_invalid(args_type)) return ira->codegen->invalid_inst_gen; if (args_type->id != ZigTypeIdStruct) { ir_add_error(ira, &args->base, buf_sprintf("expected tuple or struct, found '%s'", buf_ptr(&args_type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen **args_ptr = nullptr; size_t args_len = 0; if (is_tuple(args_type)) { args_len = args_type->data.structure.src_field_count; args_ptr = heap::c_allocator.allocate(args_len); for (size_t i = 0; i < args_len; i += 1) { TypeStructField *arg_field = args_type->data.structure.fields[i]; args_ptr[i] = ir_analyze_struct_value_field_value(ira, &instruction->base.base, args, arg_field); if (type_is_invalid(args_ptr[i]->value->type)) return ira->codegen->invalid_inst_gen; } } else { ir_add_error(ira, &args->base, buf_sprintf("TODO: struct args")); return ira->codegen->invalid_inst_gen; } IrInstGen *result = ir_analyze_call_extra(ira, &instruction->base.base, instruction->options, instruction->fn_ref, args_ptr, args_len, instruction->result_loc); heap::c_allocator.deallocate(args_ptr, args_len); return result; } static IrInstGen *ir_analyze_instruction_call_args(IrAnalyze *ira, IrInstSrcCallArgs *instruction) { IrInstGen **args_ptr = heap::c_allocator.allocate(instruction->args_len); for (size_t i = 0; i < instruction->args_len; i += 1) { args_ptr[i] = instruction->args_ptr[i]->child; if (type_is_invalid(args_ptr[i]->value->type)) return ira->codegen->invalid_inst_gen; } IrInstGen *result = ir_analyze_call_extra(ira, &instruction->base.base, instruction->options, instruction->fn_ref, args_ptr, instruction->args_len, instruction->result_loc); heap::c_allocator.deallocate(args_ptr, instruction->args_len); return result; } static IrInstGen *ir_analyze_instruction_call(IrAnalyze *ira, IrInstSrcCall *call_instruction) { IrInstGen *fn_ref = call_instruction->fn_ref->child; if (type_is_invalid(fn_ref->value->type)) return ira->codegen->invalid_inst_gen; bool is_comptime = (call_instruction->modifier == CallModifierCompileTime) || ir_should_inline(ira->old_irb.exec, call_instruction->base.base.scope); CallModifier modifier = is_comptime ? CallModifierCompileTime : call_instruction->modifier; if (is_comptime || instr_is_comptime(fn_ref)) { if (fn_ref->value->type->id == ZigTypeIdMetaType) { ZigType *ty = ir_resolve_type(ira, fn_ref); if (ty == nullptr) return ira->codegen->invalid_inst_gen; ErrorMsg *msg = ir_add_error(ira, &fn_ref->base, buf_sprintf("type '%s' not a function", buf_ptr(&ty->name))); add_error_note(ira->codegen, msg, call_instruction->base.base.source_node, buf_sprintf("use @as builtin for type coercion")); return ira->codegen->invalid_inst_gen; } else if (fn_ref->value->type->id == ZigTypeIdFn) { ZigFn *fn_table_entry = ir_resolve_fn(ira, fn_ref); ZigType *fn_type = fn_table_entry ? fn_table_entry->type_entry : fn_ref->value->type; CallModifier modifier = is_comptime ? CallModifierCompileTime : call_instruction->modifier; return ir_analyze_fn_call_src(ira, call_instruction, fn_table_entry, fn_type, fn_ref, nullptr, nullptr, modifier); } else if (fn_ref->value->type->id == ZigTypeIdBoundFn) { assert(fn_ref->value->special == ConstValSpecialStatic); ZigFn *fn_table_entry = fn_ref->value->data.x_bound_fn.fn; IrInstGen *first_arg_ptr = fn_ref->value->data.x_bound_fn.first_arg; IrInst *first_arg_ptr_src = fn_ref->value->data.x_bound_fn.first_arg_src; CallModifier modifier = is_comptime ? CallModifierCompileTime : call_instruction->modifier; return ir_analyze_fn_call_src(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry, fn_ref, first_arg_ptr, first_arg_ptr_src, modifier); } else { ir_add_error(ira, &fn_ref->base, buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value->type->name))); return ira->codegen->invalid_inst_gen; } } if (fn_ref->value->type->id == ZigTypeIdFn) { return ir_analyze_fn_call_src(ira, call_instruction, nullptr, fn_ref->value->type, fn_ref, nullptr, nullptr, modifier); } else { ir_add_error(ira, &fn_ref->base, buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value->type->name))); return ira->codegen->invalid_inst_gen; } } // out_val->type must be the type to read the pointer as // if the type is different than the actual type then it does a comptime byte reinterpretation static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, ZigValue *out_val, ZigValue *ptr_val) { Error err; assert(out_val->type != nullptr); ZigValue *pointee = const_ptr_pointee_unchecked(codegen, ptr_val); src_assert(pointee->type != nullptr, source_node); if ((err = type_resolve(codegen, pointee->type, ResolveStatusSizeKnown))) return ErrorSemanticAnalyzeFail; if ((err = type_resolve(codegen, out_val->type, ResolveStatusSizeKnown))) return ErrorSemanticAnalyzeFail; size_t src_size = type_size(codegen, pointee->type); size_t dst_size = type_size(codegen, out_val->type); if (dst_size <= src_size) { if (src_size == dst_size && types_have_same_zig_comptime_repr(codegen, out_val->type, pointee->type)) { copy_const_val(codegen, out_val, pointee); return ErrorNone; } Buf buf = BUF_INIT; buf_resize(&buf, src_size); buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf), pointee); if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val))) return err; buf_deinit(&buf); return ErrorNone; } switch (ptr_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: zig_unreachable(); case ConstPtrSpecialNull: if (dst_size == 0) return ErrorNone; opt_ir_add_error_node(ira, codegen, source_node, buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from null pointer", dst_size)); return ErrorSemanticAnalyzeFail; case ConstPtrSpecialRef: { opt_ir_add_error_node(ira, codegen, source_node, buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from pointer to %s which is %" ZIG_PRI_usize " bytes", dst_size, buf_ptr(&pointee->type->name), src_size)); return ErrorSemanticAnalyzeFail; } case ConstPtrSpecialSubArray: { ZigValue *array_val = ptr_val->data.x_ptr.data.base_array.array_val; assert(array_val->type->id == ZigTypeIdArray); if (array_val->data.x_array.special != ConstArraySpecialNone) zig_panic("TODO"); if (dst_size > src_size) { size_t elem_index = ptr_val->data.x_ptr.data.base_array.elem_index; opt_ir_add_error_node(ira, codegen, source_node, buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from %s at index %" ZIG_PRI_usize " which is %" ZIG_PRI_usize " bytes", dst_size, buf_ptr(&array_val->type->name), elem_index, src_size)); return ErrorSemanticAnalyzeFail; } size_t elem_size = src_size; size_t elem_count = (dst_size % elem_size == 0) ? (dst_size / elem_size) : (dst_size / elem_size + 1); Buf buf = BUF_INIT; buf_resize(&buf, elem_count * elem_size); for (size_t i = 0; i < elem_count; i += 1) { ZigValue *elem_val = &array_val->data.x_array.data.s_none.elements[i]; buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf) + (i * elem_size), elem_val); } if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val))) return err; buf_deinit(&buf); return ErrorNone; } case ConstPtrSpecialBaseArray: { ZigValue *array_val = ptr_val->data.x_ptr.data.base_array.array_val; assert(array_val->type->id == ZigTypeIdArray); if (array_val->data.x_array.special != ConstArraySpecialNone) zig_panic("TODO"); size_t elem_size = src_size; size_t elem_index = ptr_val->data.x_ptr.data.base_array.elem_index; src_size = elem_size * (array_val->type->data.array.len - elem_index); if (dst_size > src_size) { opt_ir_add_error_node(ira, codegen, source_node, buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from %s at index %" ZIG_PRI_usize " which is %" ZIG_PRI_usize " bytes", dst_size, buf_ptr(&array_val->type->name), elem_index, src_size)); return ErrorSemanticAnalyzeFail; } size_t elem_count = (dst_size % elem_size == 0) ? (dst_size / elem_size) : (dst_size / elem_size + 1); Buf buf = BUF_INIT; buf_resize(&buf, elem_count * elem_size); for (size_t i = 0; i < elem_count; i += 1) { ZigValue *elem_val = &array_val->data.x_array.data.s_none.elements[elem_index + i]; buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf) + (i * elem_size), elem_val); } if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val))) return err; buf_deinit(&buf); return ErrorNone; } case ConstPtrSpecialBaseStruct: case ConstPtrSpecialBaseErrorUnionCode: case ConstPtrSpecialBaseErrorUnionPayload: case ConstPtrSpecialBaseOptionalPayload: case ConstPtrSpecialDiscard: case ConstPtrSpecialHardCodedAddr: case ConstPtrSpecialFunction: zig_panic("TODO"); } zig_unreachable(); } static IrInstGen *ir_analyze_optional_type(IrAnalyze *ira, IrInstSrcUnOp *instruction) { IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type); result->value->special = ConstValSpecialLazy; LazyValueOptType *lazy_opt_type = heap::c_allocator.create(); lazy_opt_type->ira = ira; ira_ref(ira); result->value->data.x_lazy = &lazy_opt_type->base; lazy_opt_type->base.id = LazyValueIdOptType; lazy_opt_type->payload_type = instruction->value->child; if (ir_resolve_type_lazy(ira, lazy_opt_type->payload_type) == nullptr) return ira->codegen->invalid_inst_gen; return result; } static ErrorMsg *ir_eval_negation_scalar(IrAnalyze *ira, IrInst* source_instr, ZigType *scalar_type, ZigValue *operand_val, ZigValue *scalar_out_val, bool is_wrap_op) { bool is_float = (scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat); bool ok_type = ((scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) || scalar_type->id == ZigTypeIdComptimeInt || (is_float && !is_wrap_op)); if (!ok_type) { const char *fmt = is_wrap_op ? "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'"; return ir_add_error(ira, source_instr, buf_sprintf(fmt, buf_ptr(&scalar_type->name))); } if (is_float) { float_negate(scalar_out_val, operand_val); } else if (is_wrap_op) { bigint_negate_wrap(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint, scalar_type->data.integral.bit_count); } else { bigint_negate(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint); } scalar_out_val->type = scalar_type; scalar_out_val->special = ConstValSpecialStatic; if (is_wrap_op || is_float || scalar_type->id == ZigTypeIdComptimeInt) { return nullptr; } if (!bigint_fits_in_bits(&scalar_out_val->data.x_bigint, scalar_type->data.integral.bit_count, true)) { return ir_add_error(ira, source_instr, buf_sprintf("negation caused overflow")); } return nullptr; } static IrInstGen *ir_analyze_negation(IrAnalyze *ira, IrInstSrcUnOp *instruction) { IrInstGen *value = instruction->value->child; ZigType *expr_type = value->value->type; if (type_is_invalid(expr_type)) return ira->codegen->invalid_inst_gen; if (!(expr_type->id == ZigTypeIdInt || expr_type->id == ZigTypeIdComptimeInt || expr_type->id == ZigTypeIdFloat || expr_type->id == ZigTypeIdComptimeFloat || expr_type->id == ZigTypeIdVector)) { ir_add_error(ira, &instruction->base.base, buf_sprintf("negation of type '%s'", buf_ptr(&expr_type->name))); return ira->codegen->invalid_inst_gen; } bool is_wrap_op = (instruction->op_id == IrUnOpNegationWrap); ZigType *scalar_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type; if (instr_is_comptime(value)) { ZigValue *operand_val = ir_resolve_const(ira, value, UndefBad); if (!operand_val) return ira->codegen->invalid_inst_gen; IrInstGen *result_instruction = ir_const(ira, &instruction->base.base, expr_type); ZigValue *out_val = result_instruction->value; if (expr_type->id == ZigTypeIdVector) { expand_undef_array(ira->codegen, operand_val); out_val->special = ConstValSpecialUndef; expand_undef_array(ira->codegen, out_val); size_t len = expr_type->data.vector.len; for (size_t i = 0; i < len; i += 1) { ZigValue *scalar_operand_val = &operand_val->data.x_array.data.s_none.elements[i]; ZigValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; assert(scalar_operand_val->type == scalar_type); assert(scalar_out_val->type == scalar_type); ErrorMsg *msg = ir_eval_negation_scalar(ira, &instruction->base.base, scalar_type, scalar_operand_val, scalar_out_val, is_wrap_op); if (msg != nullptr) { add_error_note(ira->codegen, msg, instruction->base.base.source_node, buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i)); return ira->codegen->invalid_inst_gen; } } out_val->type = expr_type; out_val->special = ConstValSpecialStatic; } else { if (ir_eval_negation_scalar(ira, &instruction->base.base, scalar_type, operand_val, out_val, is_wrap_op) != nullptr) { return ira->codegen->invalid_inst_gen; } } return result_instruction; } if (is_wrap_op) { return ir_build_negation_wrapping(ira, &instruction->base.base, value, expr_type); } else { return ir_build_negation(ira, &instruction->base.base, value, expr_type); } } static IrInstGen *ir_analyze_bin_not(IrAnalyze *ira, IrInstSrcUnOp *instruction) { IrInstGen *value = instruction->value->child; ZigType *expr_type = value->value->type; if (type_is_invalid(expr_type)) return ira->codegen->invalid_inst_gen; ZigType *scalar_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type; if (scalar_type->id != ZigTypeIdInt) { ir_add_error(ira, &instruction->base.base, buf_sprintf("unable to perform binary not operation on type '%s'", buf_ptr(&expr_type->name))); return ira->codegen->invalid_inst_gen; } if (instr_is_comptime(value)) { ZigValue *expr_val = ir_resolve_const(ira, value, UndefBad); if (expr_val == nullptr) return ira->codegen->invalid_inst_gen; IrInstGen *result = ir_const(ira, &instruction->base.base, expr_type); if (expr_type->id == ZigTypeIdVector) { expand_undef_array(ira->codegen, expr_val); result->value->special = ConstValSpecialUndef; expand_undef_array(ira->codegen, result->value); for (size_t i = 0; i < expr_type->data.vector.len; i++) { ZigValue *src_val = &expr_val->data.x_array.data.s_none.elements[i]; ZigValue *dst_val = &result->value->data.x_array.data.s_none.elements[i]; dst_val->type = scalar_type; dst_val->special = ConstValSpecialStatic; bigint_not(&dst_val->data.x_bigint, &src_val->data.x_bigint, scalar_type->data.integral.bit_count, scalar_type->data.integral.is_signed); } } else { bigint_not(&result->value->data.x_bigint, &expr_val->data.x_bigint, scalar_type->data.integral.bit_count, scalar_type->data.integral.is_signed); } return result; } return ir_build_binary_not(ira, &instruction->base.base, value, expr_type); } static IrInstGen *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstSrcUnOp *instruction) { IrUnOp op_id = instruction->op_id; switch (op_id) { case IrUnOpInvalid: zig_unreachable(); case IrUnOpBinNot: return ir_analyze_bin_not(ira, instruction); case IrUnOpNegation: case IrUnOpNegationWrap: return ir_analyze_negation(ira, instruction); case IrUnOpDereference: { IrInstGen *ptr = instruction->value->child; if (type_is_invalid(ptr->value->type)) return ira->codegen->invalid_inst_gen; ZigType *ptr_type = ptr->value->type; if (ptr_type->id == ZigTypeIdPointer && ptr_type->data.pointer.ptr_len == PtrLenUnknown) { ir_add_error_node(ira, instruction->base.base.source_node, buf_sprintf("index syntax required for unknown-length pointer type '%s'", buf_ptr(&ptr_type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *result = ir_get_deref(ira, &instruction->base.base, ptr, instruction->result_loc); if (type_is_invalid(result->value->type)) return ira->codegen->invalid_inst_gen; // If the result needs to be an lvalue, type check it if (instruction->lval != LValNone && result->value->type->id != ZigTypeIdPointer) { ir_add_error(ira, &instruction->base.base, buf_sprintf("attempt to dereference non-pointer type '%s'", buf_ptr(&result->value->type->name))); return ira->codegen->invalid_inst_gen; } return result; } case IrUnOpOptional: return ir_analyze_optional_type(ira, instruction); } zig_unreachable(); } static void ir_push_resume(IrAnalyze *ira, IrSuspendPosition pos) { IrBasicBlockSrc *old_bb = ira->old_irb.exec->basic_block_list.at(pos.basic_block_index); if (old_bb->in_resume_stack) return; ira->resume_stack.append(pos); old_bb->in_resume_stack = true; } static void ir_push_resume_block(IrAnalyze *ira, IrBasicBlockSrc *old_bb) { if (ira->resume_stack.length != 0) { ir_push_resume(ira, {old_bb->index, 0}); } } static IrInstGen *ir_analyze_instruction_br(IrAnalyze *ira, IrInstSrcBr *br_instruction) { IrBasicBlockSrc *old_dest_block = br_instruction->dest_block; bool is_comptime; if (!ir_resolve_comptime(ira, br_instruction->is_comptime->child, &is_comptime)) return ir_unreach_error(ira); if (is_comptime || (old_dest_block->ref_count == 1 && old_dest_block->suspend_instruction_ref == nullptr)) return ir_inline_bb(ira, &br_instruction->base.base, old_dest_block); IrBasicBlockGen *new_bb = ir_get_new_bb_runtime(ira, old_dest_block, &br_instruction->base.base); if (new_bb == nullptr) return ir_unreach_error(ira); ir_push_resume_block(ira, old_dest_block); IrInstGen *result = ir_build_br_gen(ira, &br_instruction->base.base, new_bb); return ir_finish_anal(ira, result); } static IrInstGen *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstSrcCondBr *cond_br_instruction) { IrInstGen *condition = cond_br_instruction->condition->child; if (type_is_invalid(condition->value->type)) return ir_unreach_error(ira); bool is_comptime; if (!ir_resolve_comptime(ira, cond_br_instruction->is_comptime->child, &is_comptime)) return ir_unreach_error(ira); ZigType *bool_type = ira->codegen->builtin_types.entry_bool; IrInstGen *casted_condition = ir_implicit_cast(ira, condition, bool_type); if (type_is_invalid(casted_condition->value->type)) return ir_unreach_error(ira); if (is_comptime || instr_is_comptime(casted_condition)) { bool cond_is_true; if (!ir_resolve_bool(ira, casted_condition, &cond_is_true)) return ir_unreach_error(ira); IrBasicBlockSrc *old_dest_block = cond_is_true ? cond_br_instruction->then_block : cond_br_instruction->else_block; if (is_comptime || (old_dest_block->ref_count == 1 && old_dest_block->suspend_instruction_ref == nullptr)) return ir_inline_bb(ira, &cond_br_instruction->base.base, old_dest_block); IrBasicBlockGen *new_dest_block = ir_get_new_bb_runtime(ira, old_dest_block, &cond_br_instruction->base.base); if (new_dest_block == nullptr) return ir_unreach_error(ira); ir_push_resume_block(ira, old_dest_block); IrInstGen *result = ir_build_br_gen(ira, &cond_br_instruction->base.base, new_dest_block); return ir_finish_anal(ira, result); } assert(cond_br_instruction->then_block != cond_br_instruction->else_block); IrBasicBlockGen *new_then_block = ir_get_new_bb_runtime(ira, cond_br_instruction->then_block, &cond_br_instruction->base.base); if (new_then_block == nullptr) return ir_unreach_error(ira); IrBasicBlockGen *new_else_block = ir_get_new_bb_runtime(ira, cond_br_instruction->else_block, &cond_br_instruction->base.base); if (new_else_block == nullptr) return ir_unreach_error(ira); ir_push_resume_block(ira, cond_br_instruction->else_block); ir_push_resume_block(ira, cond_br_instruction->then_block); IrInstGen *result = ir_build_cond_br_gen(ira, &cond_br_instruction->base.base, casted_condition, new_then_block, new_else_block); return ir_finish_anal(ira, result); } static IrInstGen *ir_analyze_instruction_unreachable(IrAnalyze *ira, IrInstSrcUnreachable *unreachable_instruction) { IrInstGen *result = ir_build_unreachable_gen(ira, &unreachable_instruction->base.base); return ir_finish_anal(ira, result); } static IrInstGen *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstSrcPhi *phi_instruction) { Error err; if (ira->const_predecessor_bb) { for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { IrBasicBlockSrc *predecessor = phi_instruction->incoming_blocks[i]; if (predecessor != ira->const_predecessor_bb) continue; IrInstGen *value = phi_instruction->incoming_values[i]->child; assert(value->value->type); if (type_is_invalid(value->value->type)) return ira->codegen->invalid_inst_gen; if (value->value->special != ConstValSpecialRuntime) { IrInstGen *result = ir_const(ira, &phi_instruction->base.base, nullptr); copy_const_val(ira->codegen, result->value, value->value); return result; } else { return value; } } zig_unreachable(); } ResultLocPeerParent *peer_parent = phi_instruction->peer_parent; if (peer_parent != nullptr && !peer_parent->skipped && !peer_parent->done_resuming && peer_parent->peers.length >= 2) { if (peer_parent->resolved_type == nullptr) { IrInstGen **instructions = heap::c_allocator.allocate(peer_parent->peers.length); for (size_t i = 0; i < peer_parent->peers.length; i += 1) { ResultLocPeer *this_peer = peer_parent->peers.at(i); IrInstGen *gen_instruction = this_peer->base.gen_instruction; if (gen_instruction == nullptr) { // unreachable instructions will cause implicit_elem_type to be null if (this_peer->base.implicit_elem_type == nullptr) { instructions[i] = ir_const_unreachable(ira, &this_peer->base.source_instruction->base); } else { instructions[i] = ir_const(ira, &this_peer->base.source_instruction->base, this_peer->base.implicit_elem_type); instructions[i]->value->special = ConstValSpecialRuntime; } } else { instructions[i] = gen_instruction; } } ZigType *expected_type = ir_result_loc_expected_type(ira, &phi_instruction->base.base, peer_parent->parent); peer_parent->resolved_type = ir_resolve_peer_types(ira, peer_parent->base.source_instruction->base.source_node, expected_type, instructions, peer_parent->peers.length); if (type_is_invalid(peer_parent->resolved_type)) return ira->codegen->invalid_inst_gen; // the logic below assumes there are no instructions in the new current basic block yet ir_assert(ira->new_irb.current_basic_block->instruction_list.length == 0, &phi_instruction->base.base); // In case resolving the parent activates a suspend, do it now IrInstGen *parent_result_loc = ir_resolve_result(ira, &phi_instruction->base.base, peer_parent->parent, peer_parent->resolved_type, nullptr, false, true); if (parent_result_loc != nullptr && (type_is_invalid(parent_result_loc->value->type) || parent_result_loc->value->type->id == ZigTypeIdUnreachable)) { return parent_result_loc; } // If the above code generated any instructions in the current basic block, we need // to move them to the peer parent predecessor. ZigList instrs_to_move = {}; while (ira->new_irb.current_basic_block->instruction_list.length != 0) { instrs_to_move.append(ira->new_irb.current_basic_block->instruction_list.pop()); } if (instrs_to_move.length != 0) { IrBasicBlockGen *predecessor = peer_parent->base.source_instruction->child->owner_bb; IrInstGen *branch_instruction = predecessor->instruction_list.pop(); ir_assert(branch_instruction->value->type->id == ZigTypeIdUnreachable, &phi_instruction->base.base); while (instrs_to_move.length != 0) { predecessor->instruction_list.append(instrs_to_move.pop()); } predecessor->instruction_list.append(branch_instruction); } } IrSuspendPosition suspend_pos; ira_suspend(ira, &phi_instruction->base.base, nullptr, &suspend_pos); ir_push_resume(ira, suspend_pos); for (size_t i = 0; i < peer_parent->peers.length; i += 1) { ResultLocPeer *opposite_peer = peer_parent->peers.at(peer_parent->peers.length - i - 1); if (opposite_peer->base.implicit_elem_type != nullptr && opposite_peer->base.implicit_elem_type->id != ZigTypeIdUnreachable) { ir_push_resume(ira, opposite_peer->suspend_pos); } } peer_parent->done_resuming = true; return ira_resume(ira); } ZigList new_incoming_blocks = {0}; ZigList new_incoming_values = {0}; for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { IrBasicBlockSrc *predecessor = phi_instruction->incoming_blocks[i]; if (predecessor->ref_count == 0) continue; IrInstSrc *old_value = phi_instruction->incoming_values[i]; assert(old_value); IrInstGen *new_value = old_value->child; if (!new_value || new_value->value->type->id == ZigTypeIdUnreachable || predecessor->child == nullptr) continue; if (type_is_invalid(new_value->value->type)) return ira->codegen->invalid_inst_gen; assert(predecessor->child); new_incoming_blocks.append(predecessor->child); new_incoming_values.append(new_value); } if (new_incoming_blocks.length == 0) { IrInstGen *result = ir_build_unreachable_gen(ira, &phi_instruction->base.base); return ir_finish_anal(ira, result); } if (new_incoming_blocks.length == 1) { return new_incoming_values.at(0); } ZigType *resolved_type = nullptr; if (peer_parent != nullptr) { bool peer_parent_has_type; if ((err = ir_result_has_type(ira, peer_parent->parent, &peer_parent_has_type))) return ira->codegen->invalid_inst_gen; if (peer_parent_has_type) { if (peer_parent->parent->id == ResultLocIdReturn) { resolved_type = ira->explicit_return_type; } else if (peer_parent->parent->id == ResultLocIdCast) { resolved_type = ir_resolve_type(ira, peer_parent->parent->source_instruction->child); } else if (peer_parent->parent->resolved_loc) { ZigType *resolved_loc_ptr_type = peer_parent->parent->resolved_loc->value->type; ir_assert(resolved_loc_ptr_type->id == ZigTypeIdPointer, &phi_instruction->base.base); resolved_type = resolved_loc_ptr_type->data.pointer.child_type; } if (resolved_type != nullptr && type_is_invalid(resolved_type)) return ira->codegen->invalid_inst_gen; } } if (resolved_type == nullptr) { resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.base.source_node, nullptr, new_incoming_values.items, new_incoming_values.length); if (type_is_invalid(resolved_type)) return ira->codegen->invalid_inst_gen; } switch (type_has_one_possible_value(ira->codegen, resolved_type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_inst_gen; case OnePossibleValueYes: return ir_const_move(ira, &phi_instruction->base.base, get_the_one_possible_value(ira->codegen, resolved_type)); case OnePossibleValueNo: break; } switch (type_requires_comptime(ira->codegen, resolved_type)) { case ReqCompTimeInvalid: return ira->codegen->invalid_inst_gen; case ReqCompTimeYes: ir_add_error(ira, &phi_instruction->base.base, buf_sprintf("values of type '%s' must be comptime known", buf_ptr(&resolved_type->name))); return ira->codegen->invalid_inst_gen; case ReqCompTimeNo: break; } bool all_stack_ptrs = (resolved_type->id == ZigTypeIdPointer); // cast all values to the resolved type. however we can't put cast instructions in front of the phi instruction. // so we go back and insert the casts as the last instruction in the corresponding predecessor blocks, and // then make sure the branch instruction is preserved. IrBasicBlockGen *cur_bb = ira->new_irb.current_basic_block; for (size_t i = 0; i < new_incoming_values.length; i += 1) { IrInstGen *new_value = new_incoming_values.at(i); IrBasicBlockGen *predecessor = new_incoming_blocks.at(i); ir_assert(predecessor->instruction_list.length != 0, &phi_instruction->base.base); IrInstGen *branch_instruction = predecessor->instruction_list.pop(); ir_set_cursor_at_end_gen(&ira->new_irb, predecessor); IrInstGen *casted_value = ir_implicit_cast(ira, new_value, resolved_type); if (type_is_invalid(casted_value->value->type)) { return ira->codegen->invalid_inst_gen; } 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_gen(&ira->new_irb, cur_bb); IrInstGen *result = ir_build_phi_gen(ira, &phi_instruction->base.base, new_incoming_blocks.length, new_incoming_blocks.items, new_incoming_values.items, resolved_type); if (all_stack_ptrs) { assert(result->value->special == ConstValSpecialRuntime); result->value->data.rh_ptr = RuntimeHintPtrStack; } return result; } static IrInstGen *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstSrcVarPtr *instruction) { ZigVar *var = instruction->var; IrInstGen *result = ir_get_var_ptr(ira, &instruction->base.base, var); if (instruction->crossed_fndef_scope != nullptr && !instr_is_comptime(result)) { ErrorMsg *msg = ir_add_error(ira, &instruction->base.base, buf_sprintf("'%s' not accessible from inner function", var->name)); add_error_note(ira->codegen, msg, instruction->crossed_fndef_scope->base.source_node, buf_sprintf("crossed function definition here")); add_error_note(ira->codegen, msg, var->decl_node, buf_sprintf("declared here")); return ira->codegen->invalid_inst_gen; } return result; } static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align) { assert(ptr_type->id == ZigTypeIdPointer); return get_pointer_to_type_extra2(g, ptr_type->data.pointer.child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, ptr_type->data.pointer.ptr_len, new_align, ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, ptr_type->data.pointer.allow_zero, ptr_type->data.pointer.vector_index, ptr_type->data.pointer.inferred_struct_field, ptr_type->data.pointer.sentinel); } static ZigType *adjust_ptr_sentinel(CodeGen *g, ZigType *ptr_type, ZigValue *new_sentinel) { assert(ptr_type->id == ZigTypeIdPointer); return get_pointer_to_type_extra2(g, ptr_type->data.pointer.child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, ptr_type->data.pointer.ptr_len, ptr_type->data.pointer.explicit_alignment, ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, ptr_type->data.pointer.allow_zero, ptr_type->data.pointer.vector_index, ptr_type->data.pointer.inferred_struct_field, new_sentinel); } static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align) { assert(is_slice(slice_type)); ZigType *ptr_type = adjust_ptr_align(g, slice_type->data.structure.fields[slice_ptr_index]->type_entry, new_align); return get_slice_type(g, ptr_type); } static ZigType *adjust_ptr_len(CodeGen *g, ZigType *ptr_type, PtrLen ptr_len) { assert(ptr_type->id == ZigTypeIdPointer); return get_pointer_to_type_extra2(g, ptr_type->data.pointer.child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, ptr_len, ptr_type->data.pointer.explicit_alignment, ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, ptr_type->data.pointer.allow_zero, ptr_type->data.pointer.vector_index, ptr_type->data.pointer.inferred_struct_field, (ptr_len != PtrLenUnknown) ? nullptr : ptr_type->data.pointer.sentinel); } static ZigType *adjust_ptr_allow_zero(CodeGen *g, ZigType *ptr_type, bool allow_zero) { assert(ptr_type->id == ZigTypeIdPointer); return get_pointer_to_type_extra2(g, ptr_type->data.pointer.child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, ptr_type->data.pointer.ptr_len, ptr_type->data.pointer.explicit_alignment, ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, allow_zero, ptr_type->data.pointer.vector_index, ptr_type->data.pointer.inferred_struct_field, ptr_type->data.pointer.sentinel); } static ZigType *adjust_ptr_const(CodeGen *g, ZigType *ptr_type, bool is_const) { assert(ptr_type->id == ZigTypeIdPointer); return get_pointer_to_type_extra2(g, ptr_type->data.pointer.child_type, is_const, ptr_type->data.pointer.is_volatile, ptr_type->data.pointer.ptr_len, ptr_type->data.pointer.explicit_alignment, ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, ptr_type->data.pointer.allow_zero, ptr_type->data.pointer.vector_index, ptr_type->data.pointer.inferred_struct_field, ptr_type->data.pointer.sentinel); } static Error compute_elem_align(IrAnalyze *ira, ZigType *elem_type, uint32_t base_ptr_align, uint64_t elem_index, uint32_t *result) { Error err; if (base_ptr_align == 0) { *result = 0; return ErrorNone; } // figure out the largest alignment possible if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) return err; uint64_t elem_size = type_size(ira->codegen, elem_type); uint64_t abi_align = get_abi_alignment(ira->codegen, elem_type); uint64_t ptr_align = base_ptr_align; uint64_t chosen_align = abi_align; if (ptr_align >= abi_align) { while (ptr_align > abi_align) { if ((elem_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(); } *result = chosen_align; return ErrorNone; } static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemPtr *elem_ptr_instruction) { Error err; IrInstGen *array_ptr = elem_ptr_instruction->array_ptr->child; if (type_is_invalid(array_ptr->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *elem_index = elem_ptr_instruction->elem_index->child; if (type_is_invalid(elem_index->value->type)) return ira->codegen->invalid_inst_gen; ZigValue *orig_array_ptr_val = array_ptr->value; ZigType *ptr_type = orig_array_ptr_val->type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *array_type = ptr_type->data.pointer.child_type; // At first return_type will be the pointer type we want to return, except with an optimistic alignment. // We will adjust return_type's alignment before returning it. ZigType *return_type; if (type_is_invalid(array_type)) return ira->codegen->invalid_inst_gen; if (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle && array_type->data.pointer.child_type->id == ZigTypeIdArray) { IrInstGen *ptr_value = ir_get_deref(ira, &elem_ptr_instruction->base.base, array_ptr, nullptr); if (type_is_invalid(ptr_value->value->type)) return ira->codegen->invalid_inst_gen; array_type = array_type->data.pointer.child_type; ptr_type = ptr_type->data.pointer.child_type; orig_array_ptr_val = ptr_value->value; } if (array_type->id == ZigTypeIdArray) { if(array_type->data.array.len == 0 && array_type->data.array.sentinel == nullptr){ ir_add_error(ira, &elem_ptr_instruction->base.base, buf_sprintf("accessing a zero length array is not allowed")); return ira->codegen->invalid_inst_gen; } ZigType *child_type = array_type->data.array.child_type; if (ptr_type->data.pointer.host_int_bytes == 0) { return_type = get_pointer_to_type_extra(ira->codegen, child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, elem_ptr_instruction->ptr_len, ptr_type->data.pointer.explicit_alignment, 0, 0, false); } else { uint64_t elem_val_scalar; if (!ir_resolve_usize(ira, elem_index, &elem_val_scalar)) return ira->codegen->invalid_inst_gen; size_t bit_width = type_size_bits(ira->codegen, child_type); size_t bit_offset = bit_width * elem_val_scalar; return_type = get_pointer_to_type_extra(ira->codegen, child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, elem_ptr_instruction->ptr_len, 1, (uint32_t)bit_offset, ptr_type->data.pointer.host_int_bytes, false); } } else if (array_type->id == ZigTypeIdPointer) { if (array_type->data.pointer.ptr_len == PtrLenSingle) { ir_add_error_node(ira, elem_ptr_instruction->base.base.source_node, buf_sprintf("index of single-item pointer")); return ira->codegen->invalid_inst_gen; } return_type = adjust_ptr_len(ira->codegen, array_type, elem_ptr_instruction->ptr_len); } else if (is_slice(array_type)) { return_type = adjust_ptr_len(ira->codegen, array_type->data.structure.fields[slice_ptr_index]->type_entry, elem_ptr_instruction->ptr_len); } else if (array_type->id == ZigTypeIdVector) { // This depends on whether the element index is comptime, so it is computed later. return_type = nullptr; } else if (elem_ptr_instruction->init_array_type_source_node != nullptr && array_type->id == ZigTypeIdStruct && array_type->data.structure.resolve_status == ResolveStatusBeingInferred) { ZigType *usize = ira->codegen->builtin_types.entry_usize; IrInstGen *casted_elem_index = ir_implicit_cast(ira, elem_index, usize); if (type_is_invalid(casted_elem_index->value->type)) return ira->codegen->invalid_inst_gen; ir_assert(instr_is_comptime(casted_elem_index), &elem_ptr_instruction->base.base); Buf *field_name = buf_alloc(); bigint_append_buf(field_name, &casted_elem_index->value->data.x_bigint, 10); return ir_analyze_inferred_field_ptr(ira, field_name, &elem_ptr_instruction->base.base, array_ptr, array_type); } else if (is_tuple(array_type)) { uint64_t elem_index_scalar; if (!ir_resolve_usize(ira, elem_index, &elem_index_scalar)) return ira->codegen->invalid_inst_gen; if (elem_index_scalar >= array_type->data.structure.src_field_count) { ir_add_error(ira, &elem_ptr_instruction->base.base, buf_sprintf( "field index %" ZIG_PRI_u64 " outside tuple '%s' which has %" PRIu32 " fields", elem_index_scalar, buf_ptr(&array_type->name), array_type->data.structure.src_field_count)); return ira->codegen->invalid_inst_gen; } TypeStructField *field = array_type->data.structure.fields[elem_index_scalar]; return ir_analyze_struct_field_ptr(ira, &elem_ptr_instruction->base.base, field, array_ptr, array_type, false); } else { ir_add_error_node(ira, elem_ptr_instruction->base.base.source_node, buf_sprintf("array access of non-array type '%s'", buf_ptr(&array_type->name))); return ira->codegen->invalid_inst_gen; } ZigType *usize = ira->codegen->builtin_types.entry_usize; IrInstGen *casted_elem_index = ir_implicit_cast(ira, elem_index, usize); if (type_is_invalid(casted_elem_index->value->type)) return ira->codegen->invalid_inst_gen; bool safety_check_on = elem_ptr_instruction->safety_check_on; if (instr_is_comptime(casted_elem_index)) { ZigValue *index_val = ir_resolve_const(ira, casted_elem_index, UndefBad); if (index_val == nullptr) return ira->codegen->invalid_inst_gen; uint64_t index = bigint_as_u64(&index_val->data.x_bigint); if (array_type->id == ZigTypeIdArray) { uint64_t array_len = array_type->data.array.len + (array_type->data.array.sentinel != nullptr); if (index >= array_len) { ir_add_error_node(ira, elem_ptr_instruction->base.base.source_node, buf_sprintf("index %" ZIG_PRI_u64 " outside array of size %" ZIG_PRI_u64, index, array_len)); return ira->codegen->invalid_inst_gen; } safety_check_on = false; } if (array_type->id == ZigTypeIdVector) { ZigType *elem_type = array_type->data.vector.elem_type; uint32_t host_vec_len = array_type->data.vector.len; return_type = get_pointer_to_type_extra2(ira->codegen, elem_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, elem_ptr_instruction->ptr_len, get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, (uint32_t)index, nullptr, nullptr); } else if (return_type->data.pointer.explicit_alignment != 0) { uint32_t chosen_align; if ((err = compute_elem_align(ira, return_type->data.pointer.child_type, return_type->data.pointer.explicit_alignment, index, &chosen_align))) { return ira->codegen->invalid_inst_gen; } return_type = adjust_ptr_align(ira->codegen, return_type, chosen_align); } // TODO The `array_type->id == ZigTypeIdArray` exception here should not be an exception; // the `orig_array_ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar` clause should be omitted completely. // However there are bugs to fix before this improvement can be made. if (orig_array_ptr_val->special != ConstValSpecialRuntime && orig_array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr && (orig_array_ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar || array_type->id == ZigTypeIdArray)) { if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, elem_ptr_instruction->base.base.source_node, orig_array_ptr_val, UndefBad))) { return ira->codegen->invalid_inst_gen; } ZigValue *array_ptr_val = const_ptr_pointee(ira, ira->codegen, orig_array_ptr_val, elem_ptr_instruction->base.base.source_node); if (array_ptr_val == nullptr) return ira->codegen->invalid_inst_gen; if (array_ptr_val->special == ConstValSpecialUndef && elem_ptr_instruction->init_array_type_source_node != nullptr) { if (array_type->id == ZigTypeIdArray || array_type->id == ZigTypeIdVector) { array_ptr_val->data.x_array.special = ConstArraySpecialNone; array_ptr_val->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(array_type->data.array.len); array_ptr_val->special = ConstValSpecialStatic; for (size_t i = 0; i < array_type->data.array.len; i += 1) { ZigValue *elem_val = &array_ptr_val->data.x_array.data.s_none.elements[i]; elem_val->special = ConstValSpecialUndef; elem_val->type = array_type->data.array.child_type; elem_val->parent.id = ConstParentIdArray; elem_val->parent.data.p_array.array_val = array_ptr_val; elem_val->parent.data.p_array.elem_index = i; } } else if (is_slice(array_type)) { ir_assert(array_ptr->value->type->id == ZigTypeIdPointer, &elem_ptr_instruction->base.base); ZigType *actual_array_type = array_ptr->value->type->data.pointer.child_type; if (type_is_invalid(actual_array_type)) return ira->codegen->invalid_inst_gen; if (actual_array_type->id != ZigTypeIdArray) { ir_add_error_node(ira, elem_ptr_instruction->init_array_type_source_node, buf_sprintf("array literal requires address-of operator to coerce to slice type '%s'", buf_ptr(&actual_array_type->name))); return ira->codegen->invalid_inst_gen; } ZigValue *array_init_val = ira->codegen->pass1_arena->create(); array_init_val->special = ConstValSpecialStatic; array_init_val->type = actual_array_type; array_init_val->data.x_array.special = ConstArraySpecialNone; array_init_val->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(actual_array_type->data.array.len); array_init_val->special = ConstValSpecialStatic; for (size_t i = 0; i < actual_array_type->data.array.len; i += 1) { ZigValue *elem_val = &array_init_val->data.x_array.data.s_none.elements[i]; elem_val->special = ConstValSpecialUndef; elem_val->type = actual_array_type->data.array.child_type; elem_val->parent.id = ConstParentIdArray; elem_val->parent.data.p_array.array_val = array_init_val; elem_val->parent.data.p_array.elem_index = i; } init_const_slice(ira->codegen, array_ptr_val, array_init_val, 0, actual_array_type->data.array.len, false); array_ptr_val->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = ConstPtrMutInfer; } else { ir_add_error_node(ira, elem_ptr_instruction->init_array_type_source_node, buf_sprintf("expected array type or [_], found '%s'", buf_ptr(&array_type->name))); return ira->codegen->invalid_inst_gen; } } if (array_ptr_val->special != ConstValSpecialRuntime && (array_type->id != ZigTypeIdPointer || array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr)) { if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, elem_ptr_instruction->base.base.source_node, array_ptr_val, UndefOk))) { return ira->codegen->invalid_inst_gen; } if (array_type->id == ZigTypeIdPointer) { IrInstGen *result = ir_const(ira, &elem_ptr_instruction->base.base, return_type); ZigValue *out_val = result->value; out_val->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut; size_t new_index; size_t mem_size; size_t old_size; switch (array_ptr_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: case ConstPtrSpecialDiscard: zig_unreachable(); case ConstPtrSpecialRef: if (array_ptr_val->data.x_ptr.data.ref.pointee->type->id == ZigTypeIdArray) { ZigValue *array_val = array_ptr_val->data.x_ptr.data.ref.pointee; new_index = index; ZigType *array_type = array_val->type; mem_size = array_type->data.array.len; if (array_type->data.array.sentinel != nullptr) { mem_size += 1; } old_size = mem_size; out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; out_val->data.x_ptr.data.base_array.array_val = array_val; out_val->data.x_ptr.data.base_array.elem_index = new_index; } else { mem_size = 1; old_size = 1; new_index = index; out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.data.ref.pointee = array_ptr_val->data.x_ptr.data.ref.pointee; } break; case ConstPtrSpecialBaseArray: case ConstPtrSpecialSubArray: { size_t offset = array_ptr_val->data.x_ptr.data.base_array.elem_index; new_index = offset + index; ZigType *array_type = array_ptr_val->data.x_ptr.data.base_array.array_val->type; mem_size = array_type->data.array.len; if (array_type->data.array.sentinel != nullptr) { mem_size += 1; } old_size = mem_size - offset; assert(array_ptr_val->data.x_ptr.data.base_array.array_val); out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; out_val->data.x_ptr.data.base_array.array_val = array_ptr_val->data.x_ptr.data.base_array.array_val; out_val->data.x_ptr.data.base_array.elem_index = new_index; break; } case ConstPtrSpecialBaseStruct: zig_panic("TODO elem ptr on a const inner struct"); case ConstPtrSpecialBaseErrorUnionCode: zig_panic("TODO elem ptr on a const inner error union code"); case ConstPtrSpecialBaseErrorUnionPayload: zig_panic("TODO elem ptr on a const inner error union payload"); case ConstPtrSpecialBaseOptionalPayload: zig_panic("TODO elem ptr on a const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO element ptr of a function casted to a ptr"); case ConstPtrSpecialNull: zig_panic("TODO elem ptr on a null pointer"); } if (new_index >= mem_size) { ir_add_error_node(ira, elem_ptr_instruction->base.base.source_node, buf_sprintf("index %" ZIG_PRI_u64 " outside pointer of size %" ZIG_PRI_usize "", index, old_size)); return ira->codegen->invalid_inst_gen; } return result; } else if (is_slice(array_type)) { ZigValue *ptr_field = array_ptr_val->data.x_struct.fields[slice_ptr_index]; ir_assert(ptr_field != nullptr, &elem_ptr_instruction->base.base); if (ptr_field->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { return ir_build_elem_ptr_gen(ira, elem_ptr_instruction->base.base.scope, elem_ptr_instruction->base.base.source_node, array_ptr, casted_elem_index, false, return_type); } ZigValue *len_field = array_ptr_val->data.x_struct.fields[slice_len_index]; IrInstGen *result = ir_const(ira, &elem_ptr_instruction->base.base, return_type); ZigValue *out_val = result->value; ZigType *slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; uint64_t slice_len = bigint_as_u64(&len_field->data.x_bigint); uint64_t full_slice_len = slice_len + ((slice_ptr_type->data.pointer.sentinel != nullptr) ? 1 : 0); if (index >= full_slice_len) { ir_add_error_node(ira, elem_ptr_instruction->base.base.source_node, buf_sprintf("index %" ZIG_PRI_u64 " outside slice of size %" ZIG_PRI_u64, index, slice_len)); return ira->codegen->invalid_inst_gen; } 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 ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: { size_t offset = ptr_field->data.x_ptr.data.base_array.elem_index; uint64_t new_index = offset + index; if (ptr_field->data.x_ptr.data.base_array.array_val->data.x_array.special != ConstArraySpecialBuf) { ir_assert(new_index < ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len, &elem_ptr_instruction->base.base); } out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; out_val->data.x_ptr.data.base_array.array_val = ptr_field->data.x_ptr.data.base_array.array_val; out_val->data.x_ptr.data.base_array.elem_index = new_index; break; } case ConstPtrSpecialBaseStruct: zig_panic("TODO elem ptr on a slice backed by const inner struct"); case ConstPtrSpecialBaseErrorUnionCode: zig_panic("TODO elem ptr on a slice backed by const inner error union code"); case ConstPtrSpecialBaseErrorUnionPayload: zig_panic("TODO elem ptr on a slice backed by const inner error union payload"); case ConstPtrSpecialBaseOptionalPayload: zig_panic("TODO elem ptr on a slice backed by const optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO elem ptr on a slice that was ptrcast from a function"); case ConstPtrSpecialNull: zig_panic("TODO elem ptr on a slice has a null pointer"); } return result; } else if (array_type->id == ZigTypeIdArray || array_type->id == ZigTypeIdVector) { IrInstGen *result; if (orig_array_ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { result = ir_build_elem_ptr_gen(ira, elem_ptr_instruction->base.base.scope, elem_ptr_instruction->base.base.source_node, array_ptr, casted_elem_index, false, return_type); result->value->special = ConstValSpecialStatic; } else { result = ir_const(ira, &elem_ptr_instruction->base.base, return_type); } ZigValue *out_val = result->value; out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; out_val->data.x_ptr.mut = orig_array_ptr_val->data.x_ptr.mut; out_val->data.x_ptr.data.base_array.array_val = array_ptr_val; out_val->data.x_ptr.data.base_array.elem_index = index; return result; } else { zig_unreachable(); } } } } else if (array_type->id == ZigTypeIdVector) { // runtime known element index ZigType *elem_type = array_type->data.vector.elem_type; uint32_t host_vec_len = array_type->data.vector.len; return_type = get_pointer_to_type_extra2(ira->codegen, elem_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, elem_ptr_instruction->ptr_len, get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, VECTOR_INDEX_RUNTIME, nullptr, nullptr); } else { // runtime known element index switch (type_requires_comptime(ira->codegen, return_type)) { case ReqCompTimeYes: ir_add_error(ira, &elem_index->base, buf_sprintf("values of type '%s' must be comptime known, but index value is runtime known", buf_ptr(&return_type->data.pointer.child_type->name))); return ira->codegen->invalid_inst_gen; case ReqCompTimeInvalid: return ira->codegen->invalid_inst_gen; case ReqCompTimeNo: break; } if (return_type->data.pointer.explicit_alignment != 0) { if ((err = type_resolve(ira->codegen, return_type->data.pointer.child_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_inst_gen; uint64_t elem_size = type_size(ira->codegen, return_type->data.pointer.child_type); uint64_t abi_align = get_abi_alignment(ira->codegen, return_type->data.pointer.child_type); uint64_t ptr_align = get_ptr_align(ira->codegen, return_type); if (ptr_align < abi_align) { if (elem_size >= ptr_align && elem_size % ptr_align == 0) { return_type = adjust_ptr_align(ira->codegen, return_type, ptr_align); } else { // can't get here because guaranteed elem_size >= abi_align zig_unreachable(); } } else { return_type = adjust_ptr_align(ira->codegen, return_type, abi_align); } } } return ir_build_elem_ptr_gen(ira, elem_ptr_instruction->base.base.scope, elem_ptr_instruction->base.base.source_node, array_ptr, casted_elem_index, safety_check_on, return_type); } static IrInstGen *ir_analyze_container_member_access_inner(IrAnalyze *ira, ZigType *bare_struct_type, Buf *field_name, IrInst* source_instr, IrInstGen *container_ptr, IrInst *container_ptr_src, ZigType *container_type) { if (!is_slice(bare_struct_type)) { ScopeDecls *container_scope = get_container_scope(bare_struct_type); assert(container_scope != nullptr); auto tld = find_container_decl(ira->codegen, container_scope, field_name); if (tld) { if (tld->id == TldIdFn) { resolve_top_level_decl(ira->codegen, tld, source_instr->source_node, false); if (tld->resolution == TldResolutionInvalid) return ira->codegen->invalid_inst_gen; if (tld->resolution == TldResolutionResolving) return ir_error_dependency_loop(ira, source_instr); if (tld->visib_mod == VisibModPrivate && tld->import != get_scope_import(source_instr->scope)) { ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("'%s' is private", buf_ptr(field_name))); add_error_note(ira->codegen, msg, tld->source_node, buf_sprintf("declared here")); return ira->codegen->invalid_inst_gen; } TldFn *tld_fn = (TldFn *)tld; ZigFn *fn_entry = tld_fn->fn_entry; assert(fn_entry != nullptr); if (type_is_invalid(fn_entry->type_entry)) return ira->codegen->invalid_inst_gen; IrInstGen *bound_fn_value = ir_const_bound_fn(ira, source_instr, fn_entry, container_ptr, container_ptr_src); return ir_get_ref(ira, source_instr, bound_fn_value, true, false); } else if (tld->id == TldIdVar) { resolve_top_level_decl(ira->codegen, tld, source_instr->source_node, false); if (tld->resolution == TldResolutionInvalid) return ira->codegen->invalid_inst_gen; if (tld->resolution == TldResolutionResolving) return ir_error_dependency_loop(ira, source_instr); TldVar *tld_var = (TldVar *)tld; ZigVar *var = tld_var->var; assert(var != nullptr); if (type_is_invalid(var->var_type)) return ira->codegen->invalid_inst_gen; if (var->const_value->type->id == ZigTypeIdFn) { ir_assert(var->const_value->data.x_ptr.special == ConstPtrSpecialFunction, source_instr); ZigFn *fn = var->const_value->data.x_ptr.data.fn.fn_entry; IrInstGen *bound_fn_value = ir_const_bound_fn(ira, source_instr, fn, container_ptr, container_ptr_src); 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_inst_gen; } static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field) { if (field->init_val != nullptr) return; if (field->decl_node->type != NodeTypeStructField) return; AstNode *init_node = field->decl_node->data.struct_field.value; if (init_node == nullptr) return; // scope is not the scope of the struct init, it's the scope of the struct type decl Scope *analyze_scope = &get_container_scope(container_type)->base; // memoize it field->init_val = analyze_const_value(codegen, analyze_scope, init_node, field->type_entry, nullptr, UndefOk); } static IrInstGen *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInst* source_instr, TypeStructField *field, IrInstGen *struct_ptr, ZigType *struct_type, bool initializing) { Error err; ZigType *field_type = resolve_struct_field_type(ira->codegen, field); if (field_type == nullptr) return ira->codegen->invalid_inst_gen; if (field->is_comptime) { IrInstGen *elem = ir_const(ira, source_instr, field_type); memoize_field_init_val(ira->codegen, struct_type, field); if(field->init_val != nullptr && type_is_invalid(field->init_val->type)){ return ira->codegen->invalid_inst_gen; } copy_const_val(ira->codegen, elem->value, field->init_val); return ir_get_ref2(ira, source_instr, elem, field_type, true, false); } switch (type_has_one_possible_value(ira->codegen, field_type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_inst_gen; case OnePossibleValueYes: { IrInstGen *elem = ir_const_move(ira, source_instr, get_the_one_possible_value(ira->codegen, field_type)); return ir_get_ref(ira, source_instr, elem, struct_ptr->value->type->data.pointer.is_const, struct_ptr->value->type->data.pointer.is_volatile); } case OnePossibleValueNo: break; } bool is_const = struct_ptr->value->type->data.pointer.is_const; bool is_volatile = struct_ptr->value->type->data.pointer.is_volatile; ZigType *ptr_type; if (is_anon_container(struct_type)) { ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, is_const, is_volatile, PtrLenSingle, 0, 0, 0, false); } else { ResolveStatus needed_resolve_status = (struct_type->data.structure.layout == ContainerLayoutAuto) ? ResolveStatusZeroBitsKnown : ResolveStatusSizeKnown; if ((err = type_resolve(ira->codegen, struct_type, needed_resolve_status))) return ira->codegen->invalid_inst_gen; assert(struct_ptr->value->type->id == ZigTypeIdPointer); uint32_t ptr_bit_offset = struct_ptr->value->type->data.pointer.bit_offset_in_host; uint32_t ptr_host_int_bytes = struct_ptr->value->type->data.pointer.host_int_bytes; uint32_t host_int_bytes_for_result_type = (ptr_host_int_bytes == 0) ? get_host_int_bytes(ira->codegen, struct_type, field) : ptr_host_int_bytes; ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, is_const, is_volatile, PtrLenSingle, field->align, (uint32_t)(ptr_bit_offset + field->bit_offset_in_host), (uint32_t)host_int_bytes_for_result_type, false); } if (instr_is_comptime(struct_ptr)) { ZigValue *ptr_val = ir_resolve_const(ira, struct_ptr, UndefBad); if (!ptr_val) return ira->codegen->invalid_inst_gen; if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { ZigValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); if (struct_val == nullptr) return ira->codegen->invalid_inst_gen; if (type_is_invalid(struct_val->type)) return ira->codegen->invalid_inst_gen; // This to allow lazy values to be resolved. if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, source_instr->source_node, struct_val, UndefOk))) { return ira->codegen->invalid_inst_gen; } if (initializing && struct_val->special == ConstValSpecialUndef) { struct_val->data.x_struct.fields = alloc_const_vals_ptrs(ira->codegen, struct_type->data.structure.src_field_count); struct_val->special = ConstValSpecialStatic; for (size_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) { ZigValue *field_val = struct_val->data.x_struct.fields[i]; field_val->special = ConstValSpecialUndef; field_val->type = resolve_struct_field_type(ira->codegen, struct_type->data.structure.fields[i]); field_val->parent.id = ConstParentIdStruct; field_val->parent.data.p_struct.struct_val = struct_val; field_val->parent.data.p_struct.field_index = i; } } IrInstGen *result; if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { result = ir_build_struct_field_ptr(ira, source_instr, struct_ptr, field, ptr_type); result->value->special = ConstValSpecialStatic; } else { result = ir_const(ira, source_instr, ptr_type); } ZigValue *const_val = result->value; const_val->data.x_ptr.special = ConstPtrSpecialBaseStruct; const_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; const_val->data.x_ptr.data.base_struct.struct_val = struct_val; const_val->data.x_ptr.data.base_struct.field_index = field->src_index; return result; } } return ir_build_struct_field_ptr(ira, source_instr, struct_ptr, field, ptr_type); } static IrInstGen *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_name, IrInst* source_instr, IrInstGen *container_ptr, ZigType *container_type) { // The type of the field is not available until a store using this pointer happens. // So, here we create a special pointer type which has the inferred struct type and // field name encoded in the type. Later, when there is a store via this pointer, // the field type will then be available, and the field will be added to the inferred // struct. ZigType *container_ptr_type = container_ptr->value->type; ir_assert(container_ptr_type->id == ZigTypeIdPointer, source_instr); InferredStructField *inferred_struct_field = heap::c_allocator.create(); inferred_struct_field->inferred_struct_type = container_type; inferred_struct_field->field_name = field_name; ZigType *elem_type = ira->codegen->builtin_types.entry_var; ZigType *field_ptr_type = get_pointer_to_type_extra2(ira->codegen, elem_type, container_ptr_type->data.pointer.is_const, container_ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, inferred_struct_field, nullptr); if (instr_is_comptime(container_ptr)) { ZigValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); if (ptr_val == nullptr) return ira->codegen->invalid_inst_gen; IrInstGen *result; if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { result = ir_build_cast(ira, source_instr, container_ptr_type, container_ptr, CastOpNoop); } else { result = ir_const(ira, source_instr, field_ptr_type); } copy_const_val(ira->codegen, result->value, ptr_val); result->value->type = field_ptr_type; return result; } return ir_build_cast(ira, source_instr, field_ptr_type, container_ptr, CastOpNoop); } static IrInstGen *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name, IrInst* source_instr, IrInstGen *container_ptr, IrInst *container_ptr_src, ZigType *container_type, bool initializing) { Error err; ZigType *bare_type = container_ref_type(container_type); if (initializing && bare_type->id == ZigTypeIdStruct && bare_type->data.structure.resolve_status == ResolveStatusBeingInferred) { return ir_analyze_inferred_field_ptr(ira, field_name, source_instr, container_ptr, bare_type); } // Tracks wether we should return an undefined value of the correct type. // We do this if the container pointer is undefined and we are in a TypeOf call. bool return_undef = container_ptr->value->special == ConstValSpecialUndef && \ get_scope_typeof(source_instr->scope) != nullptr; if ((err = type_resolve(ira->codegen, bare_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_inst_gen; assert(container_ptr->value->type->id == ZigTypeIdPointer); if (bare_type->id == ZigTypeIdStruct) { TypeStructField *field = find_struct_type_field(bare_type, field_name); if (field != nullptr) { if (return_undef) { ZigType *field_ptr_type = get_pointer_to_type(ira->codegen, resolve_struct_field_type(ira->codegen, field), container_ptr->value->type->data.pointer.is_const); return ir_const_undef(ira, source_instr, field_ptr_type); } return ir_analyze_struct_field_ptr(ira, source_instr, field, container_ptr, bare_type, initializing); } else { return ir_analyze_container_member_access_inner(ira, bare_type, field_name, source_instr, container_ptr, container_ptr_src, container_type); } } if (bare_type->id == ZigTypeIdEnum) { return ir_analyze_container_member_access_inner(ira, bare_type, field_name, source_instr, container_ptr, container_ptr_src, container_type); } if (bare_type->id == ZigTypeIdUnion) { bool is_const = container_ptr->value->type->data.pointer.is_const; bool is_volatile = container_ptr->value->type->data.pointer.is_volatile; TypeUnionField *field = find_union_type_field(bare_type, field_name); if (field == nullptr) { return ir_analyze_container_member_access_inner(ira, bare_type, field_name, source_instr, container_ptr, container_ptr_src, container_type); } ZigType *field_type = resolve_union_field_type(ira->codegen, field); if (field_type == nullptr) return ira->codegen->invalid_inst_gen; ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, is_const, is_volatile, PtrLenSingle, 0, 0, 0, false); if (instr_is_comptime(container_ptr)) { ZigValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); if (!ptr_val) return ira->codegen->invalid_inst_gen; if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar && ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { ZigValue *union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); if (union_val == nullptr) return ira->codegen->invalid_inst_gen; if (type_is_invalid(union_val->type)) return ira->codegen->invalid_inst_gen; if (initializing) { ZigValue *payload_val = ira->codegen->pass1_arena->create(); payload_val->special = ConstValSpecialUndef; payload_val->type = field_type; payload_val->parent.id = ConstParentIdUnion; payload_val->parent.data.p_union.union_val = union_val; union_val->special = ConstValSpecialStatic; bigint_init_bigint(&union_val->data.x_union.tag, &field->enum_field->value); union_val->data.x_union.payload = payload_val; } else if (bare_type->data.unionation.layout != ContainerLayoutExtern) { TypeUnionField *actual_field = find_union_field_by_tag(bare_type, &union_val->data.x_union.tag); if (actual_field == nullptr) zig_unreachable(); if (field != actual_field) { ir_add_error_node(ira, source_instr->source_node, buf_sprintf("accessing union field '%s' while field '%s' is set", buf_ptr(field_name), buf_ptr(actual_field->name))); return ira->codegen->invalid_inst_gen; } } ZigValue *payload_val = union_val->data.x_union.payload; IrInstGen *result; if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { result = ir_build_union_field_ptr(ira, source_instr, container_ptr, field, true, initializing, ptr_type); result->value->special = ConstValSpecialStatic; } else { result = ir_const(ira, source_instr, ptr_type); } ZigValue *const_val = result->value; const_val->data.x_ptr.special = ConstPtrSpecialRef; const_val->data.x_ptr.mut = container_ptr->value->data.x_ptr.mut; const_val->data.x_ptr.data.ref.pointee = payload_val; return result; } } return ir_build_union_field_ptr(ira, source_instr, container_ptr, field, true, initializing, ptr_type); } zig_unreachable(); } static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name, AstNode *source_node) { bool is_libc = target_is_libc_lib_name(ira->codegen->zig_target, buf_ptr(lib_name)); if (is_libc && ira->codegen->libc_link_lib == nullptr && !ira->codegen->reported_bad_link_libc_error) { ir_add_error_node(ira, source_node, buf_sprintf("dependency on library c must be explicitly specified in the build command")); ira->codegen->reported_bad_link_libc_error = true; } LinkLib *link_lib = add_link_lib(ira->codegen, lib_name); for (size_t i = 0; i < link_lib->symbols.length; i += 1) { Buf *existing_symbol_name = link_lib->symbols.at(i); if (buf_eql_buf(existing_symbol_name, symbol_name)) { return; } } if (!is_libc && !target_is_wasm(ira->codegen->zig_target) && !ira->codegen->have_pic && !ira->codegen->reported_bad_link_libc_error) { ErrorMsg *msg = ir_add_error_node(ira, source_node, buf_sprintf("dependency on dynamic library '%s' requires enabling Position Independent Code", buf_ptr(lib_name))); add_error_note(ira->codegen, msg, source_node, buf_sprintf("fixed by `--library %s` or `-fPIC`", buf_ptr(lib_name))); ira->codegen->reported_bad_link_libc_error = true; } for (size_t i = 0; i < ira->codegen->forbidden_libs.length; i += 1) { Buf *forbidden_lib_name = ira->codegen->forbidden_libs.at(i); if (buf_eql_buf(lib_name, forbidden_lib_name)) { ir_add_error_node(ira, source_node, buf_sprintf("linking against forbidden library '%s'", buf_ptr(symbol_name))); } } link_lib->symbols.append(symbol_name); } static IrInstGen *ir_error_dependency_loop(IrAnalyze *ira, IrInst* source_instr) { ir_add_error(ira, source_instr, buf_sprintf("dependency loop detected")); return ira->codegen->invalid_inst_gen; } static IrInstGen *ir_analyze_decl_ref(IrAnalyze *ira, IrInst* source_instruction, Tld *tld) { resolve_top_level_decl(ira->codegen, tld, source_instruction->source_node, true); if (tld->resolution == TldResolutionInvalid) { return ira->codegen->invalid_inst_gen; } if (tld->resolution == TldResolutionResolving) return ir_error_dependency_loop(ira, source_instruction); switch (tld->id) { case TldIdContainer: case TldIdCompTime: case TldIdUsingNamespace: zig_unreachable(); case TldIdVar: { TldVar *tld_var = (TldVar *)tld; ZigVar *var = tld_var->var; assert(var != nullptr); if (tld_var->extern_lib_name != nullptr) { add_link_lib_symbol(ira, tld_var->extern_lib_name, buf_create_from_str(var->name), source_instruction->source_node); } return ir_get_var_ptr(ira, source_instruction, var); } case TldIdFn: { TldFn *tld_fn = (TldFn *)tld; ZigFn *fn_entry = tld_fn->fn_entry; assert(fn_entry->type_entry != nullptr); if (type_is_invalid(fn_entry->type_entry)) return ira->codegen->invalid_inst_gen; 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); } IrInstGen *fn_inst = ir_const_fn(ira, source_instruction, fn_entry); return ir_get_ref(ira, source_instruction, fn_inst, true, false); } } zig_unreachable(); } static ErrorTableEntry *find_err_table_entry(ZigType *err_set_type, Buf *field_name) { assert(err_set_type->id == ZigTypeIdErrorSet); for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *err_table_entry = err_set_type->data.error_set.errors[i]; if (buf_eql_buf(&err_table_entry->name, field_name)) { return err_table_entry; } } return nullptr; } static IrInstGen *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstSrcFieldPtr *field_ptr_instruction) { Error err; IrInstGen *container_ptr = field_ptr_instruction->container_ptr->child; if (type_is_invalid(container_ptr->value->type)) return ira->codegen->invalid_inst_gen; ZigType *container_type = container_ptr->value->type->data.pointer.child_type; Buf *field_name = field_ptr_instruction->field_name_buffer; if (!field_name) { IrInstGen *field_name_expr = field_ptr_instruction->field_name_expr->child; field_name = ir_resolve_str(ira, field_name_expr); if (!field_name) return ira->codegen->invalid_inst_gen; } AstNode *source_node = field_ptr_instruction->base.base.source_node; if (type_is_invalid(container_type)) { return ira->codegen->invalid_inst_gen; } else if (is_tuple(container_type) && !field_ptr_instruction->initializing && buf_eql_str(field_name, "len")) { IrInstGen *len_inst = ir_const_unsigned(ira, &field_ptr_instruction->base.base, container_type->data.structure.src_field_count); return ir_get_ref(ira, &field_ptr_instruction->base.base, len_inst, true, false); } else if (is_slice(container_type) || is_container_ref(container_type)) { assert(container_ptr->value->type->id == ZigTypeIdPointer); if (container_type->id == ZigTypeIdPointer) { ZigType *bare_type = container_ref_type(container_type); IrInstGen *container_child = ir_get_deref(ira, &field_ptr_instruction->base.base, container_ptr, nullptr); IrInstGen *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base.base, container_child, &field_ptr_instruction->container_ptr->base, bare_type, field_ptr_instruction->initializing); return result; } else { IrInstGen *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base.base, container_ptr, &field_ptr_instruction->container_ptr->base, container_type, field_ptr_instruction->initializing); return result; } } else if (is_array_ref(container_type) && !field_ptr_instruction->initializing) { if (buf_eql_str(field_name, "len")) { ZigValue *len_val = ira->codegen->pass1_arena->create(); if (container_type->id == ZigTypeIdPointer) { init_const_usize(ira->codegen, len_val, container_type->data.pointer.child_type->data.array.len); } else { init_const_usize(ira->codegen, len_val, container_type->data.array.len); } ZigType *usize = ira->codegen->builtin_types.entry_usize; bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, len_val, usize, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); } else { ir_add_error_node(ira, source_node, buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&container_type->name))); return ira->codegen->invalid_inst_gen; } } else if (container_type->id == ZigTypeIdMetaType) { ZigValue *container_ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); if (!container_ptr_val) return ira->codegen->invalid_inst_gen; assert(container_ptr->value->type->id == ZigTypeIdPointer); ZigValue *child_val = const_ptr_pointee(ira, ira->codegen, container_ptr_val, source_node); if (child_val == nullptr) return ira->codegen->invalid_inst_gen; if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, field_ptr_instruction->base.base.source_node, child_val, UndefBad))) { return ira->codegen->invalid_inst_gen; } ZigType *child_type = child_val->data.x_type; if (type_is_invalid(child_type)) { return ira->codegen->invalid_inst_gen; } else if (is_container(child_type)) { if (child_type->id == ZigTypeIdEnum) { if ((err = type_resolve(ira->codegen, child_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_inst_gen; TypeEnumField *field = find_enum_type_field(child_type, field_name); if (field) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, create_const_enum(ira->codegen, child_type, &field->value), child_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); } } ScopeDecls *container_scope = get_container_scope(child_type); Tld *tld = find_container_decl(ira->codegen, container_scope, field_name); if (tld) { if (tld->visib_mod == VisibModPrivate && tld->import != get_scope_import(field_ptr_instruction->base.base.scope)) { ErrorMsg *msg = ir_add_error(ira, &field_ptr_instruction->base.base, buf_sprintf("'%s' is private", buf_ptr(field_name))); add_error_note(ira->codegen, msg, tld->source_node, buf_sprintf("declared here")); return ira->codegen->invalid_inst_gen; } return ir_analyze_decl_ref(ira, &field_ptr_instruction->base.base, tld); } if (child_type->id == ZigTypeIdUnion && (child_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr || child_type->data.unionation.decl_node->data.container_decl.auto_enum)) { if ((err = type_resolve(ira->codegen, child_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_inst_gen; TypeUnionField *field = find_union_type_field(child_type, field_name); if (field) { ZigType *enum_type = child_type->data.unionation.tag_type; bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, create_const_enum(ira->codegen, enum_type, &field->enum_field->value), enum_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); } } const char *container_name = (child_type == ira->codegen->root_import) ? "root source file" : buf_ptr(buf_sprintf("container '%s'", buf_ptr(&child_type->name))); ir_add_error(ira, &field_ptr_instruction->base.base, buf_sprintf("%s has no member called '%s'", container_name, buf_ptr(field_name))); return ira->codegen->invalid_inst_gen; } 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 = heap::c_allocator.create(); err_entry->decl_node = field_ptr_instruction->base.base.source_node; buf_init_from_buf(&err_entry->name, field_name); size_t error_value_count = ira->codegen->errors_by_index.length; assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)ira->codegen->err_tag_type->data.integral.bit_count)); err_entry->value = error_value_count; ira->codegen->errors_by_index.append(err_entry); ira->codegen->error_table.put(field_name, err_entry); } if (err_entry->set_with_only_this_in_it == nullptr) { err_entry->set_with_only_this_in_it = make_err_set_with_one_item(ira->codegen, field_ptr_instruction->base.base.scope, field_ptr_instruction->base.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.base.source_node)) { return ira->codegen->invalid_inst_gen; } err_entry = find_err_table_entry(child_type, field_name); if (err_entry == nullptr) { ir_add_error(ira, &field_ptr_instruction->base.base, buf_sprintf("no error named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&child_type->name))); return ira->codegen->invalid_inst_gen; } err_set_type = child_type; } ZigValue *const_val = ira->codegen->pass1_arena->create(); const_val->special = ConstValSpecialStatic; const_val->type = err_set_type; const_val->data.x_err_set = err_entry; bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, const_val, err_set_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); } else if (child_type->id == ZigTypeIdInt) { if (buf_eql_str(field_name, "bit_count")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, create_const_unsigned_negative(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int, child_type->data.integral.bit_count, false), ira->codegen->builtin_types.entry_num_lit_int, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); } else if (buf_eql_str(field_name, "is_signed")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, create_const_bool(ira->codegen, child_type->data.integral.is_signed), ira->codegen->builtin_types.entry_bool, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); } else { ir_add_error(ira, &field_ptr_instruction->base.base, buf_sprintf("type '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->invalid_inst_gen; } } else if (child_type->id == ZigTypeIdFloat) { if (buf_eql_str(field_name, "bit_count")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, create_const_unsigned_negative(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int, child_type->data.floating.bit_count, false), ira->codegen->builtin_types.entry_num_lit_int, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); } else { ir_add_error(ira, &field_ptr_instruction->base.base, buf_sprintf("type '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->invalid_inst_gen; } } else if (child_type->id == ZigTypeIdPointer) { if (buf_eql_str(field_name, "Child")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, create_const_type(ira->codegen, child_type->data.pointer.child_type), ira->codegen->builtin_types.entry_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); } else if (buf_eql_str(field_name, "alignment")) { bool ptr_is_const = true; bool ptr_is_volatile = false; if ((err = type_resolve(ira->codegen, child_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { return ira->codegen->invalid_inst_gen; } return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, create_const_unsigned_negative(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int, get_ptr_align(ira->codegen, child_type), false), ira->codegen->builtin_types.entry_num_lit_int, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); } else { ir_add_error(ira, &field_ptr_instruction->base.base, buf_sprintf("type '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->invalid_inst_gen; } } else if (child_type->id == ZigTypeIdArray) { if (buf_eql_str(field_name, "Child")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, create_const_type(ira->codegen, child_type->data.array.child_type), ira->codegen->builtin_types.entry_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); } else if (buf_eql_str(field_name, "len")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, create_const_unsigned_negative(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int, child_type->data.array.len, false), ira->codegen->builtin_types.entry_num_lit_int, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); } else { ir_add_error(ira, &field_ptr_instruction->base.base, buf_sprintf("type '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->invalid_inst_gen; } } else if (child_type->id == ZigTypeIdErrorUnion) { if (buf_eql_str(field_name, "Payload")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, create_const_type(ira->codegen, child_type->data.error_union.payload_type), ira->codegen->builtin_types.entry_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); } else if (buf_eql_str(field_name, "ErrorSet")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, create_const_type(ira->codegen, child_type->data.error_union.err_set_type), ira->codegen->builtin_types.entry_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); } else { ir_add_error(ira, &field_ptr_instruction->base.base, buf_sprintf("type '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->invalid_inst_gen; } } else if (child_type->id == ZigTypeIdOptional) { if (buf_eql_str(field_name, "Child")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, create_const_type(ira->codegen, child_type->data.maybe.child_type), ira->codegen->builtin_types.entry_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); } else { ir_add_error(ira, &field_ptr_instruction->base.base, buf_sprintf("type '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->invalid_inst_gen; } } 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.base, buf_sprintf("ReturnType has not been resolved because '%s' is generic", buf_ptr(&child_type->name))); return ira->codegen->invalid_inst_gen; } bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, create_const_type(ira->codegen, child_type->data.fn.fn_type_id.return_type), ira->codegen->builtin_types.entry_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); } else if (buf_eql_str(field_name, "is_var_args")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, create_const_bool(ira->codegen, child_type->data.fn.fn_type_id.is_var_args), ira->codegen->builtin_types.entry_bool, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); } else if (buf_eql_str(field_name, "arg_count")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, create_const_usize(ira->codegen, child_type->data.fn.fn_type_id.param_count), ira->codegen->builtin_types.entry_usize, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); } else { ir_add_error(ira, &field_ptr_instruction->base.base, buf_sprintf("type '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->invalid_inst_gen; } } else { ir_add_error(ira, &field_ptr_instruction->base.base, buf_sprintf("type '%s' does not support field access", buf_ptr(&child_type->name))); return ira->codegen->invalid_inst_gen; } } else if (field_ptr_instruction->initializing) { ir_add_error(ira, &field_ptr_instruction->base.base, buf_sprintf("type '%s' does not support struct initialization syntax", buf_ptr(&container_type->name))); return ira->codegen->invalid_inst_gen; } else { ir_add_error_node(ira, field_ptr_instruction->base.base.source_node, buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); return ira->codegen->invalid_inst_gen; } } static IrInstGen *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstSrcStorePtr *instruction) { IrInstGen *ptr = instruction->ptr->child; if (type_is_invalid(ptr->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *value = instruction->value->child; if (type_is_invalid(value->value->type)) return ira->codegen->invalid_inst_gen; return ir_analyze_store_ptr(ira, &instruction->base.base, ptr, value, instruction->allow_write_through_const); } static IrInstGen *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstSrcLoadPtr *instruction) { IrInstGen *ptr = instruction->ptr->child; if (type_is_invalid(ptr->value->type)) return ira->codegen->invalid_inst_gen; return ir_get_deref(ira, &instruction->base.base, ptr, nullptr); } static IrInstGen *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstSrcTypeOf *typeof_instruction) { ZigType *type_entry; const size_t value_count = typeof_instruction->value_count; // Fast path for the common case of TypeOf with a single argument if (value_count < 2) { type_entry = typeof_instruction->value.scalar->child->value->type; } else { IrInstGen **args = heap::c_allocator.allocate(value_count); for (size_t i = 0; i < value_count; i += 1) { IrInstGen *value = typeof_instruction->value.list[i]->child; if (type_is_invalid(value->value->type)) return ira->codegen->invalid_inst_gen; args[i] = value; } type_entry = ir_resolve_peer_types(ira, typeof_instruction->base.base.source_node, nullptr, args, value_count); heap::c_allocator.deallocate(args, value_count); } if (type_is_invalid(type_entry)) return ira->codegen->invalid_inst_gen; return ir_const_type(ira, &typeof_instruction->base.base, type_entry); } static IrInstGen *ir_analyze_instruction_set_cold(IrAnalyze *ira, IrInstSrcSetCold *instruction) { if (ira->new_irb.exec->is_inline) { // ignore setCold when running functions at compile time return ir_const_void(ira, &instruction->base.base); } IrInstGen *is_cold_value = instruction->is_cold->child; bool want_cold; if (!ir_resolve_bool(ira, is_cold_value, &want_cold)) return ira->codegen->invalid_inst_gen; ZigFn *fn_entry = scope_fn_entry(instruction->base.base.scope); if (fn_entry == nullptr) { ir_add_error(ira, &instruction->base.base, buf_sprintf("@setCold outside function")); return ira->codegen->invalid_inst_gen; } if (fn_entry->set_cold_node != nullptr) { ErrorMsg *msg = ir_add_error(ira, &instruction->base.base, buf_sprintf("cold set twice in same function")); add_error_note(ira->codegen, msg, fn_entry->set_cold_node, buf_sprintf("first set here")); return ira->codegen->invalid_inst_gen; } fn_entry->set_cold_node = instruction->base.base.source_node; fn_entry->is_cold = want_cold; return ir_const_void(ira, &instruction->base.base); } static IrInstGen *ir_analyze_instruction_set_runtime_safety(IrAnalyze *ira, IrInstSrcSetRuntimeSafety *set_runtime_safety_instruction) { if (ira->new_irb.exec->is_inline) { // ignore setRuntimeSafety when running functions at compile time return ir_const_void(ira, &set_runtime_safety_instruction->base.base); } bool *safety_off_ptr; AstNode **safety_set_node_ptr; Scope *scope = set_runtime_safety_instruction->base.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); IrInstGen *safety_on_value = set_runtime_safety_instruction->safety_on->child; bool want_runtime_safety; if (!ir_resolve_bool(ira, safety_on_value, &want_runtime_safety)) return ira->codegen->invalid_inst_gen; AstNode *source_node = set_runtime_safety_instruction->base.base.source_node; if (*safety_set_node_ptr) { ErrorMsg *msg = ir_add_error_node(ira, source_node, buf_sprintf("runtime safety set twice for same scope")); add_error_note(ira->codegen, msg, *safety_set_node_ptr, buf_sprintf("first set here")); return ira->codegen->invalid_inst_gen; } *safety_set_node_ptr = source_node; *safety_off_ptr = !want_runtime_safety; return ir_const_void(ira, &set_runtime_safety_instruction->base.base); } static IrInstGen *ir_analyze_instruction_set_float_mode(IrAnalyze *ira, IrInstSrcSetFloatMode *instruction) { if (ira->new_irb.exec->is_inline) { // ignore setFloatMode when running functions at compile time return ir_const_void(ira, &instruction->base.base); } bool *fast_math_on_ptr; AstNode **fast_math_set_node_ptr; Scope *scope = instruction->base.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); IrInstGen *float_mode_value = instruction->mode_value->child; FloatMode float_mode_scalar; if (!ir_resolve_float_mode(ira, float_mode_value, &float_mode_scalar)) return ira->codegen->invalid_inst_gen; AstNode *source_node = instruction->base.base.source_node; if (*fast_math_set_node_ptr) { ErrorMsg *msg = ir_add_error_node(ira, source_node, buf_sprintf("float mode set twice for same scope")); add_error_note(ira->codegen, msg, *fast_math_set_node_ptr, buf_sprintf("first set here")); return ira->codegen->invalid_inst_gen; } *fast_math_set_node_ptr = source_node; *fast_math_on_ptr = (float_mode_scalar == FloatModeOptimized); return ir_const_void(ira, &instruction->base.base); } static IrInstGen *ir_analyze_instruction_any_frame_type(IrAnalyze *ira, IrInstSrcAnyFrameType *instruction) { ZigType *payload_type = nullptr; if (instruction->payload_type != nullptr) { payload_type = ir_resolve_type(ira, instruction->payload_type->child); if (type_is_invalid(payload_type)) return ira->codegen->invalid_inst_gen; } ZigType *any_frame_type = get_any_frame_type(ira->codegen, payload_type); return ir_const_type(ira, &instruction->base.base, any_frame_type); } static IrInstGen *ir_analyze_instruction_slice_type(IrAnalyze *ira, IrInstSrcSliceType *slice_type_instruction) { IrInstGen *result = ir_const(ira, &slice_type_instruction->base.base, ira->codegen->builtin_types.entry_type); result->value->special = ConstValSpecialLazy; LazyValueSliceType *lazy_slice_type = heap::c_allocator.create(); lazy_slice_type->ira = ira; ira_ref(ira); result->value->data.x_lazy = &lazy_slice_type->base; lazy_slice_type->base.id = LazyValueIdSliceType; if (slice_type_instruction->align_value != nullptr) { lazy_slice_type->align_inst = slice_type_instruction->align_value->child; if (ir_resolve_const(ira, lazy_slice_type->align_inst, LazyOk) == nullptr) return ira->codegen->invalid_inst_gen; } if (slice_type_instruction->sentinel != nullptr) { lazy_slice_type->sentinel = slice_type_instruction->sentinel->child; if (ir_resolve_const(ira, lazy_slice_type->sentinel, LazyOk) == nullptr) return ira->codegen->invalid_inst_gen; } lazy_slice_type->elem_type = slice_type_instruction->child_type->child; if (ir_resolve_type_lazy(ira, lazy_slice_type->elem_type) == nullptr) return ira->codegen->invalid_inst_gen; lazy_slice_type->is_const = slice_type_instruction->is_const; lazy_slice_type->is_volatile = slice_type_instruction->is_volatile; lazy_slice_type->is_allowzero = slice_type_instruction->is_allow_zero; return result; } static IrInstGen *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstSrcAsm *asm_instruction) { Error err; assert(asm_instruction->base.base.source_node->type == NodeTypeAsmExpr); AstNode *node = asm_instruction->base.base.source_node; AstNodeAsmExpr *asm_expr = &asm_instruction->base.base.source_node->data.asm_expr; Buf *template_buf = ir_resolve_str(ira, asm_instruction->asm_template->child); if (template_buf == nullptr) return ira->codegen->invalid_inst_gen; if (asm_instruction->is_global) { buf_append_char(&ira->codegen->global_asm, '\n'); buf_append_buf(&ira->codegen->global_asm, template_buf); return ir_const_void(ira, &asm_instruction->base.base); } if (!ir_emit_global_runtime_side_effect(ira, &asm_instruction->base.base)) return ira->codegen->invalid_inst_gen; ZigList tok_list = {}; if ((err = parse_asm_template(ira, node, template_buf, &tok_list))) { return ira->codegen->invalid_inst_gen; } for (size_t token_i = 0; token_i < tok_list.length; token_i += 1) { AsmToken asm_token = tok_list.at(token_i); if (asm_token.id == AsmTokenIdVar) { size_t index = find_asm_index(ira->codegen, node, &asm_token, template_buf); if (index == SIZE_MAX) { const char *ptr = buf_ptr(template_buf) + asm_token.start + 2; uint32_t len = asm_token.end - asm_token.start - 2; add_node_error(ira->codegen, node, buf_sprintf("could not find '%.*s' in the inputs or outputs", len, ptr)); return ira->codegen->invalid_inst_gen; } } } // TODO validate the output types and variable types IrInstGen **input_list = heap::c_allocator.allocate(asm_expr->input_list.length); IrInstGen **output_types = heap::c_allocator.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]->child; return_type = ir_resolve_type(ira, output_types[i]); if (type_is_invalid(return_type)) return ira->codegen->invalid_inst_gen; } } for (size_t i = 0; i < asm_expr->input_list.length; i += 1) { IrInstGen *const input_value = asm_instruction->input_list[i]->child; if (type_is_invalid(input_value->value->type)) return ira->codegen->invalid_inst_gen; if (instr_is_comptime(input_value) && (input_value->value->type->id == ZigTypeIdComptimeInt || input_value->value->type->id == ZigTypeIdComptimeFloat)) { ir_add_error(ira, &input_value->base, buf_sprintf("expected sized integer or sized float, found %s", buf_ptr(&input_value->value->type->name))); return ira->codegen->invalid_inst_gen; } input_list[i] = input_value; } return ir_build_asm_gen(ira, &asm_instruction->base.base, template_buf, tok_list.items, tok_list.length, input_list, output_types, asm_instruction->output_vars, asm_instruction->return_count, asm_instruction->has_side_effects, return_type); } static IrInstGen *ir_analyze_instruction_array_type(IrAnalyze *ira, IrInstSrcArrayType *array_type_instruction) { IrInstGen *result = ir_const(ira, &array_type_instruction->base.base, ira->codegen->builtin_types.entry_type); result->value->special = ConstValSpecialLazy; LazyValueArrayType *lazy_array_type = heap::c_allocator.create(); lazy_array_type->ira = ira; ira_ref(ira); result->value->data.x_lazy = &lazy_array_type->base; lazy_array_type->base.id = LazyValueIdArrayType; lazy_array_type->elem_type = array_type_instruction->child_type->child; if (ir_resolve_type_lazy(ira, lazy_array_type->elem_type) == nullptr) return ira->codegen->invalid_inst_gen; if (!ir_resolve_usize(ira, array_type_instruction->size->child, &lazy_array_type->length)) return ira->codegen->invalid_inst_gen; if (array_type_instruction->sentinel != nullptr) { lazy_array_type->sentinel = array_type_instruction->sentinel->child; if (ir_resolve_const(ira, lazy_array_type->sentinel, LazyOk) == nullptr) return ira->codegen->invalid_inst_gen; } return result; } static IrInstGen *ir_analyze_instruction_size_of(IrAnalyze *ira, IrInstSrcSizeOf *instruction) { IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_num_lit_int); result->value->special = ConstValSpecialLazy; LazyValueSizeOf *lazy_size_of = heap::c_allocator.create(); lazy_size_of->ira = ira; ira_ref(ira); result->value->data.x_lazy = &lazy_size_of->base; lazy_size_of->base.id = LazyValueIdSizeOf; lazy_size_of->bit_size = instruction->bit_size; lazy_size_of->target_type = instruction->type_value->child; if (ir_resolve_type_lazy(ira, lazy_size_of->target_type) == nullptr) return ira->codegen->invalid_inst_gen; return result; } static IrInstGen *ir_analyze_test_non_null(IrAnalyze *ira, IrInst *source_inst, IrInstGen *value) { ZigType *type_entry = value->value->type; if (type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.allow_zero) { if (instr_is_comptime(value)) { ZigValue *c_ptr_val = ir_resolve_const(ira, value, UndefOk); if (c_ptr_val == nullptr) return ira->codegen->invalid_inst_gen; if (c_ptr_val->special == ConstValSpecialUndef) return ir_const_undef(ira, source_inst, ira->codegen->builtin_types.entry_bool); bool is_null = c_ptr_val->data.x_ptr.special == ConstPtrSpecialNull || (c_ptr_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && c_ptr_val->data.x_ptr.data.hard_coded_addr.addr == 0); return ir_const_bool(ira, source_inst, !is_null); } return ir_build_test_non_null_gen(ira, source_inst, value); } else if (type_entry->id == ZigTypeIdOptional) { if (instr_is_comptime(value)) { ZigValue *maybe_val = ir_resolve_const(ira, value, UndefOk); if (maybe_val == nullptr) return ira->codegen->invalid_inst_gen; if (maybe_val->special == ConstValSpecialUndef) return ir_const_undef(ira, source_inst, ira->codegen->builtin_types.entry_bool); return ir_const_bool(ira, source_inst, !optional_value_is_null(maybe_val)); } return ir_build_test_non_null_gen(ira, source_inst, value); } else if (type_entry->id == ZigTypeIdNull) { return ir_const_bool(ira, source_inst, false); } else { return ir_const_bool(ira, source_inst, true); } } static IrInstGen *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrInstSrcTestNonNull *instruction) { IrInstGen *value = instruction->value->child; if (type_is_invalid(value->value->type)) return ira->codegen->invalid_inst_gen; return ir_analyze_test_non_null(ira, &instruction->base.base, value); } static IrInstGen *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInst* source_instr, IrInstGen *base_ptr, bool safety_check_on, bool initializing) { Error err; ZigType *type_entry = get_ptr_elem_type(ira->codegen, base_ptr); if (type_is_invalid(type_entry)) return ira->codegen->invalid_inst_gen; if (type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.ptr_len == PtrLenC) { if (instr_is_comptime(base_ptr)) { ZigValue *val = ir_resolve_const(ira, base_ptr, UndefBad); if (!val) return ira->codegen->invalid_inst_gen; if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { ZigValue *c_ptr_val = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); if (c_ptr_val == nullptr) return ira->codegen->invalid_inst_gen; bool is_null = c_ptr_val->data.x_ptr.special == ConstPtrSpecialNull || (c_ptr_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && c_ptr_val->data.x_ptr.data.hard_coded_addr.addr == 0); if (is_null) { ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null")); return ira->codegen->invalid_inst_gen; } return base_ptr; } } if (!safety_check_on) return base_ptr; IrInstGen *c_ptr_val = ir_get_deref(ira, source_instr, base_ptr, nullptr); ir_build_assert_non_null(ira, source_instr, c_ptr_val); return base_ptr; } if (type_entry->id != ZigTypeIdOptional) { ir_add_error(ira, &base_ptr->base, buf_sprintf("expected optional type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_inst_gen; } ZigType *child_type = type_entry->data.maybe.child_type; ZigType *result_type = get_pointer_to_type_extra(ira->codegen, child_type, base_ptr->value->type->data.pointer.is_const, base_ptr->value->type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0, false); bool same_comptime_repr = types_have_same_zig_comptime_repr(ira->codegen, child_type, type_entry); if (instr_is_comptime(base_ptr)) { ZigValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); if (ptr_val == nullptr) return ira->codegen->invalid_inst_gen; if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { ZigValue *optional_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); if (optional_val == nullptr) return ira->codegen->invalid_inst_gen; if (initializing) { switch (type_has_one_possible_value(ira->codegen, child_type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_inst_gen; case OnePossibleValueNo: if (!same_comptime_repr) { ZigValue *payload_val = ira->codegen->pass1_arena->create(); payload_val->type = child_type; payload_val->special = ConstValSpecialUndef; payload_val->parent.id = ConstParentIdOptionalPayload; payload_val->parent.data.p_optional_payload.optional_val = optional_val; optional_val->data.x_optional = payload_val; optional_val->special = ConstValSpecialStatic; } break; case OnePossibleValueYes: { optional_val->special = ConstValSpecialStatic; optional_val->data.x_optional = get_the_one_possible_value(ira->codegen, child_type); break; } } } else { if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, source_instr->source_node, optional_val, UndefBad))) return ira->codegen->invalid_inst_gen; if (optional_value_is_null(optional_val)) { ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null")); return ira->codegen->invalid_inst_gen; } } IrInstGen *result; if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { result = ir_build_optional_unwrap_ptr_gen(ira, source_instr, base_ptr, false, initializing, result_type); result->value->special = ConstValSpecialStatic; } else { result = ir_const(ira, source_instr, result_type); } ZigValue *result_val = result->value; result_val->data.x_ptr.special = ConstPtrSpecialRef; result_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; switch (type_has_one_possible_value(ira->codegen, child_type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_inst_gen; case OnePossibleValueNo: if (same_comptime_repr) { result_val->data.x_ptr.data.ref.pointee = optional_val; } else { assert(optional_val->data.x_optional != nullptr); result_val->data.x_ptr.data.ref.pointee = optional_val->data.x_optional; } break; case OnePossibleValueYes: assert(optional_val->data.x_optional != nullptr); result_val->data.x_ptr.data.ref.pointee = optional_val->data.x_optional; break; } return result; } } return ir_build_optional_unwrap_ptr_gen(ira, source_instr, base_ptr, safety_check_on, initializing, result_type); } static IrInstGen *ir_analyze_instruction_optional_unwrap_ptr(IrAnalyze *ira, IrInstSrcOptionalUnwrapPtr *instruction) { IrInstGen *base_ptr = instruction->base_ptr->child; if (type_is_invalid(base_ptr->value->type)) return ira->codegen->invalid_inst_gen; return ir_analyze_unwrap_optional_payload(ira, &instruction->base.base, base_ptr, instruction->safety_check_on, false); } static IrInstGen *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstSrcCtz *instruction) { ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child); if (type_is_invalid(int_type)) return ira->codegen->invalid_inst_gen; IrInstGen *op = ir_implicit_cast(ira, instruction->op->child, int_type); if (type_is_invalid(op->value->type)) return ira->codegen->invalid_inst_gen; if (int_type->data.integral.bit_count == 0) return ir_const_unsigned(ira, &instruction->base.base, 0); if (instr_is_comptime(op)) { ZigValue *val = ir_resolve_const(ira, op, UndefOk); if (val == nullptr) return ira->codegen->invalid_inst_gen; if (val->special == ConstValSpecialUndef) return ir_const_undef(ira, &instruction->base.base, ira->codegen->builtin_types.entry_num_lit_int); size_t result_usize = bigint_ctz(&op->value->data.x_bigint, int_type->data.integral.bit_count); return ir_const_unsigned(ira, &instruction->base.base, result_usize); } ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, int_type->data.integral.bit_count); return ir_build_ctz_gen(ira, &instruction->base.base, return_type, op); } static IrInstGen *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstSrcClz *instruction) { ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child); if (type_is_invalid(int_type)) return ira->codegen->invalid_inst_gen; IrInstGen *op = ir_implicit_cast(ira, instruction->op->child, int_type); if (type_is_invalid(op->value->type)) return ira->codegen->invalid_inst_gen; if (int_type->data.integral.bit_count == 0) return ir_const_unsigned(ira, &instruction->base.base, 0); if (instr_is_comptime(op)) { ZigValue *val = ir_resolve_const(ira, op, UndefOk); if (val == nullptr) return ira->codegen->invalid_inst_gen; if (val->special == ConstValSpecialUndef) return ir_const_undef(ira, &instruction->base.base, ira->codegen->builtin_types.entry_num_lit_int); size_t result_usize = bigint_clz(&op->value->data.x_bigint, int_type->data.integral.bit_count); return ir_const_unsigned(ira, &instruction->base.base, result_usize); } ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, int_type->data.integral.bit_count); return ir_build_clz_gen(ira, &instruction->base.base, return_type, op); } static IrInstGen *ir_analyze_instruction_pop_count(IrAnalyze *ira, IrInstSrcPopCount *instruction) { ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child); if (type_is_invalid(int_type)) return ira->codegen->invalid_inst_gen; IrInstGen *op = ir_implicit_cast(ira, instruction->op->child, int_type); if (type_is_invalid(op->value->type)) return ira->codegen->invalid_inst_gen; if (int_type->data.integral.bit_count == 0) return ir_const_unsigned(ira, &instruction->base.base, 0); if (instr_is_comptime(op)) { ZigValue *val = ir_resolve_const(ira, op, UndefOk); if (val == nullptr) return ira->codegen->invalid_inst_gen; if (val->special == ConstValSpecialUndef) return ir_const_undef(ira, &instruction->base.base, ira->codegen->builtin_types.entry_num_lit_int); if (bigint_cmp_zero(&val->data.x_bigint) != CmpLT) { size_t result = bigint_popcount_unsigned(&val->data.x_bigint); return ir_const_unsigned(ira, &instruction->base.base, result); } size_t result = bigint_popcount_signed(&val->data.x_bigint, int_type->data.integral.bit_count); return ir_const_unsigned(ira, &instruction->base.base, result); } ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, int_type->data.integral.bit_count); return ir_build_pop_count_gen(ira, &instruction->base.base, return_type, op); } static IrInstGen *ir_analyze_union_tag(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, bool is_gen) { if (type_is_invalid(value->value->type)) return ira->codegen->invalid_inst_gen; if (value->value->type->id != ZigTypeIdUnion) { ir_add_error(ira, &value->base, buf_sprintf("expected enum or union type, found '%s'", buf_ptr(&value->value->type->name))); return ira->codegen->invalid_inst_gen; } if (!value->value->type->data.unionation.have_explicit_tag_type && !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_inst_gen; } ZigType *tag_type = value->value->type->data.unionation.tag_type; assert(tag_type->id == ZigTypeIdEnum); if (instr_is_comptime(value)) { ZigValue *val = ir_resolve_const(ira, value, UndefBad); if (!val) return ira->codegen->invalid_inst_gen; IrInstGenConst *const_instruction = ir_create_inst_gen(&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; } return ir_build_union_tag(ira, source_instr, value, tag_type); } static IrInstGen *ir_analyze_instruction_switch_br(IrAnalyze *ira, IrInstSrcSwitchBr *switch_br_instruction) { IrInstGen *target_value = switch_br_instruction->target_value->child; if (type_is_invalid(target_value->value->type)) return ir_unreach_error(ira); if (switch_br_instruction->switch_prongs_void != nullptr) { if (type_is_invalid(switch_br_instruction->switch_prongs_void->child->value->type)) { return ir_unreach_error(ira); } } size_t case_count = switch_br_instruction->case_count; bool is_comptime; if (!ir_resolve_comptime(ira, switch_br_instruction->is_comptime->child, &is_comptime)) return ira->codegen->invalid_inst_gen; if (is_comptime || instr_is_comptime(target_value)) { ZigValue *target_val = ir_resolve_const(ira, target_value, UndefBad); if (!target_val) return ir_unreach_error(ira); IrBasicBlockSrc *old_dest_block = switch_br_instruction->else_block; for (size_t i = 0; i < case_count; i += 1) { IrInstSrcSwitchBrCase *old_case = &switch_br_instruction->cases[i]; IrInstGen *case_value = old_case->value->child; if (type_is_invalid(case_value->value->type)) return ir_unreach_error(ira); IrInstGen *casted_case_value = ir_implicit_cast(ira, case_value, target_value->value->type); if (type_is_invalid(casted_case_value->value->type)) return ir_unreach_error(ira); ZigValue *case_val = ir_resolve_const(ira, casted_case_value, UndefBad); if (!case_val) return ir_unreach_error(ira); if (const_values_equal(ira->codegen, target_val, case_val)) { old_dest_block = old_case->block; break; } } if (is_comptime || old_dest_block->ref_count == 1) { return ir_inline_bb(ira, &switch_br_instruction->base.base, old_dest_block); } else { IrBasicBlockGen *new_dest_block = ir_get_new_bb(ira, old_dest_block, &switch_br_instruction->base.base); IrInstGen *result = ir_build_br_gen(ira, &switch_br_instruction->base.base, new_dest_block); return ir_finish_anal(ira, result); } } IrInstGenSwitchBrCase *cases = heap::c_allocator.allocate(case_count); for (size_t i = 0; i < case_count; i += 1) { IrInstSrcSwitchBrCase *old_case = &switch_br_instruction->cases[i]; IrInstGenSwitchBrCase *new_case = &cases[i]; new_case->block = ir_get_new_bb(ira, old_case->block, &switch_br_instruction->base.base); new_case->value = ira->codegen->invalid_inst_gen; // 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; IrInstSrc *old_value = old_case->value; IrInstGen *new_value = old_value->child; if (type_is_invalid(new_value->value->type)) continue; IrInstGen *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) { IrInstGenSwitchBrCase *new_case = &cases[i]; if (type_is_invalid(new_case->value->value->type)) return ir_unreach_error(ira); new_case->block->ref_instruction = &switch_br_instruction->base.base; } IrBasicBlockGen *new_else_block = ir_get_new_bb(ira, switch_br_instruction->else_block, &switch_br_instruction->base.base); IrInstGenSwitchBr *switch_br = ir_build_switch_br_gen(ira, &switch_br_instruction->base.base, target_value, new_else_block, case_count, cases); return ir_finish_anal(ira, &switch_br->base); } static IrInstGen *ir_analyze_instruction_switch_target(IrAnalyze *ira, IrInstSrcSwitchTarget *switch_target_instruction) { Error err; IrInstGen *target_value_ptr = switch_target_instruction->target_value_ptr->child; if (type_is_invalid(target_value_ptr->value->type)) return ira->codegen->invalid_inst_gen; if (target_value_ptr->value->type->id == ZigTypeIdMetaType) { assert(instr_is_comptime(target_value_ptr)); ZigType *ptr_type = target_value_ptr->value->data.x_type; assert(ptr_type->id == ZigTypeIdPointer); return ir_const_type(ira, &switch_target_instruction->base.base, ptr_type->data.pointer.child_type); } ZigType *target_type = target_value_ptr->value->type->data.pointer.child_type; ZigValue *pointee_val = nullptr; if (instr_is_comptime(target_value_ptr) && target_value_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { pointee_val = const_ptr_pointee(ira, ira->codegen, target_value_ptr->value, target_value_ptr->base.source_node); if (pointee_val == nullptr) return ira->codegen->invalid_inst_gen; if (pointee_val->special == ConstValSpecialRuntime) pointee_val = nullptr; } if ((err = type_resolve(ira->codegen, target_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_inst_gen; switch (target_type->id) { case ZigTypeIdInvalid: zig_unreachable(); case ZigTypeIdMetaType: case ZigTypeIdVoid: case ZigTypeIdBool: case ZigTypeIdInt: case ZigTypeIdFloat: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdEnumLiteral: case ZigTypeIdPointer: case ZigTypeIdFn: case ZigTypeIdErrorSet: { if (pointee_val) { IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, nullptr); copy_const_val(ira->codegen, result->value, pointee_val); result->value->type = target_type; return result; } IrInstGen *result = ir_get_deref(ira, &switch_target_instruction->base.base, target_value_ptr, nullptr); result->value->type = target_type; return result; } case ZigTypeIdUnion: { AstNode *decl_node = target_type->data.unionation.decl_node; if (!decl_node->data.container_decl.auto_enum && decl_node->data.container_decl.init_arg_expr == nullptr) { ErrorMsg *msg = ir_add_error(ira, &target_value_ptr->base, buf_sprintf("switch on union which has no attached enum")); add_error_note(ira->codegen, msg, decl_node, buf_sprintf("consider 'union(enum)' here")); return ira->codegen->invalid_inst_gen; } ZigType *tag_type = target_type->data.unionation.tag_type; assert(tag_type != nullptr); assert(tag_type->id == ZigTypeIdEnum); if (pointee_val) { IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, tag_type); bigint_init_bigint(&result->value->data.x_enum_tag, &pointee_val->data.x_union.tag); return result; } if (tag_type->data.enumeration.src_field_count == 1) { IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, tag_type); TypeEnumField *only_field = &tag_type->data.enumeration.fields[0]; bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value); return result; } IrInstGen *union_value = ir_get_deref(ira, &switch_target_instruction->base.base, target_value_ptr, nullptr); union_value->value->type = target_type; return ir_build_union_tag(ira, &switch_target_instruction->base.base, union_value, tag_type); } case ZigTypeIdEnum: { if ((err = type_resolve(ira->codegen, target_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_inst_gen; if (target_type->data.enumeration.src_field_count == 1) { TypeEnumField *only_field = &target_type->data.enumeration.fields[0]; IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, target_type); bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value); return result; } if (pointee_val) { IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, target_type); bigint_init_bigint(&result->value->data.x_enum_tag, &pointee_val->data.x_enum_tag); return result; } IrInstGen *enum_value = ir_get_deref(ira, &switch_target_instruction->base.base, target_value_ptr, nullptr); enum_value->value->type = target_type; return enum_value; } case ZigTypeIdErrorUnion: case ZigTypeIdUnreachable: case ZigTypeIdArray: case ZigTypeIdStruct: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdOptional: case ZigTypeIdBoundFn: case ZigTypeIdOpaque: case ZigTypeIdVector: case ZigTypeIdFnFrame: case ZigTypeIdAnyFrame: ir_add_error(ira, &switch_target_instruction->base.base, buf_sprintf("invalid switch target type '%s'", buf_ptr(&target_type->name))); return ira->codegen->invalid_inst_gen; } zig_unreachable(); } static IrInstGen *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstSrcSwitchVar *instruction) { IrInstGen *target_value_ptr = instruction->target_value_ptr->child; if (type_is_invalid(target_value_ptr->value->type)) return ira->codegen->invalid_inst_gen; ZigType *ref_type = target_value_ptr->value->type; assert(ref_type->id == ZigTypeIdPointer); ZigType *target_type = target_value_ptr->value->type->data.pointer.child_type; if (target_type->id == ZigTypeIdUnion) { ZigType *enum_type = target_type->data.unionation.tag_type; assert(enum_type != nullptr); assert(enum_type->id == ZigTypeIdEnum); assert(instruction->prongs_len > 0); IrInstGen *first_prong_value = instruction->prongs_ptr[0]->child; if (type_is_invalid(first_prong_value->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *first_casted_prong_value = ir_implicit_cast(ira, first_prong_value, enum_type); if (type_is_invalid(first_casted_prong_value->value->type)) return ira->codegen->invalid_inst_gen; ZigValue *first_prong_val = ir_resolve_const(ira, first_casted_prong_value, UndefBad); if (first_prong_val == nullptr) return ira->codegen->invalid_inst_gen; TypeUnionField *first_field = find_union_field_by_tag(target_type, &first_prong_val->data.x_enum_tag); ErrorMsg *invalid_payload_msg = nullptr; for (size_t prong_i = 1; prong_i < instruction->prongs_len; prong_i += 1) { IrInstGen *this_prong_inst = instruction->prongs_ptr[prong_i]->child; if (type_is_invalid(this_prong_inst->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *this_casted_prong_value = ir_implicit_cast(ira, this_prong_inst, enum_type); if (type_is_invalid(this_casted_prong_value->value->type)) return ira->codegen->invalid_inst_gen; ZigValue *this_prong = ir_resolve_const(ira, this_casted_prong_value, UndefBad); if (this_prong == nullptr) return ira->codegen->invalid_inst_gen; TypeUnionField *payload_field = find_union_field_by_tag(target_type, &this_prong->data.x_enum_tag); ZigType *payload_type = payload_field->type_entry; if (first_field->type_entry != payload_type) { if (invalid_payload_msg == nullptr) { invalid_payload_msg = ir_add_error(ira, &instruction->base.base, buf_sprintf("capture group with incompatible types")); add_error_note(ira->codegen, invalid_payload_msg, first_prong_value->base.source_node, buf_sprintf("type '%s' here", buf_ptr(&first_field->type_entry->name))); } add_error_note(ira->codegen, invalid_payload_msg, this_prong_inst->base.source_node, buf_sprintf("type '%s' here", buf_ptr(&payload_field->type_entry->name))); } } if (invalid_payload_msg != nullptr) { return ira->codegen->invalid_inst_gen; } if (instr_is_comptime(target_value_ptr)) { ZigValue *target_val_ptr = ir_resolve_const(ira, target_value_ptr, UndefBad); if (!target_value_ptr) return ira->codegen->invalid_inst_gen; ZigValue *pointee_val = const_ptr_pointee(ira, ira->codegen, target_val_ptr, instruction->base.base.source_node); if (pointee_val == nullptr) return ira->codegen->invalid_inst_gen; IrInstGen *result = ir_const(ira, &instruction->base.base, get_pointer_to_type(ira->codegen, first_field->type_entry, target_val_ptr->type->data.pointer.is_const)); ZigValue *out_val = result->value; out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.mut = target_val_ptr->data.x_ptr.mut; out_val->data.x_ptr.data.ref.pointee = pointee_val->data.x_union.payload; return result; } ZigType *result_type = get_pointer_to_type(ira->codegen, first_field->type_entry, target_value_ptr->value->type->data.pointer.is_const); return ir_build_union_field_ptr(ira, &instruction->base.base, target_value_ptr, first_field, false, false, result_type); } else if (target_type->id == ZigTypeIdErrorSet) { // construct an error set from the prong values ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); err_set_type->size_in_bits = ira->codegen->builtin_types.entry_global_error_set->size_in_bits; err_set_type->abi_align = ira->codegen->builtin_types.entry_global_error_set->abi_align; err_set_type->abi_size = ira->codegen->builtin_types.entry_global_error_set->abi_size; ZigList error_list = {}; buf_resize(&err_set_type->name, 0); buf_appendf(&err_set_type->name, "error{"); for (size_t i = 0; i < instruction->prongs_len; i += 1) { ErrorTableEntry *err = ir_resolve_error(ira, instruction->prongs_ptr[i]->child); if (err == nullptr) return ira->codegen->invalid_inst_gen; error_list.append(err); buf_appendf(&err_set_type->name, "%s,", buf_ptr(&err->name)); } err_set_type->data.error_set.errors = error_list.items; err_set_type->data.error_set.err_count = error_list.length; buf_appendf(&err_set_type->name, "}"); ZigType *new_target_value_ptr_type = get_pointer_to_type_extra(ira->codegen, err_set_type, ref_type->data.pointer.is_const, ref_type->data.pointer.is_volatile, ref_type->data.pointer.ptr_len, ref_type->data.pointer.explicit_alignment, ref_type->data.pointer.bit_offset_in_host, ref_type->data.pointer.host_int_bytes, ref_type->data.pointer.allow_zero); return ir_analyze_ptr_cast(ira, &instruction->base.base, target_value_ptr, &instruction->target_value_ptr->base, new_target_value_ptr_type, &instruction->base.base, false, false); } else { ir_add_error(ira, &instruction->base.base, buf_sprintf("switch on type '%s' provides no expression parameter", buf_ptr(&target_type->name))); return ira->codegen->invalid_inst_gen; } } static IrInstGen *ir_analyze_instruction_switch_else_var(IrAnalyze *ira, IrInstSrcSwitchElseVar *instruction) { IrInstGen *target_value_ptr = instruction->target_value_ptr->child; if (type_is_invalid(target_value_ptr->value->type)) return ira->codegen->invalid_inst_gen; ZigType *ref_type = target_value_ptr->value->type; assert(ref_type->id == ZigTypeIdPointer); ZigType *target_type = target_value_ptr->value->type->data.pointer.child_type; if (target_type->id == ZigTypeIdErrorSet) { // make a new set that has the other cases removed if (!resolve_inferred_error_set(ira->codegen, target_type, instruction->base.base.source_node)) { return ira->codegen->invalid_inst_gen; } if (type_is_global_error_set(target_type)) { // the type of the else capture variable still has to be the global error set. // once the runtime hint system is more sophisticated, we could add some hint information here. return target_value_ptr; } // Make note of the errors handled by other cases ErrorTableEntry **errors = heap::c_allocator.allocate(ira->codegen->errors_by_index.length); // We may not have any case in the switch if this is a lone else const size_t switch_cases = instruction->switch_br ? instruction->switch_br->case_count : 0; for (size_t case_i = 0; case_i < switch_cases; case_i += 1) { IrInstSrcSwitchBrCase *br_case = &instruction->switch_br->cases[case_i]; IrInstGen *case_expr = br_case->value->child; if (case_expr->value->type->id == ZigTypeIdErrorSet) { ErrorTableEntry *err = ir_resolve_error(ira, case_expr); if (err == nullptr) return ira->codegen->invalid_inst_gen; errors[err->value] = err; } else if (case_expr->value->type->id == ZigTypeIdMetaType) { ZigType *err_set_type = ir_resolve_type(ira, case_expr); if (type_is_invalid(err_set_type)) return ira->codegen->invalid_inst_gen; populate_error_set_table(errors, err_set_type); } else { zig_unreachable(); } } ZigList result_list = {}; ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); buf_resize(&err_set_type->name, 0); buf_appendf(&err_set_type->name, "error{"); // Look at all the errors in the type switched on and add them to the result_list // if they are not handled by cases. for (uint32_t i = 0; i < target_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = target_type->data.error_set.errors[i]; ErrorTableEntry *existing_entry = errors[error_entry->value]; if (existing_entry == nullptr) { result_list.append(error_entry); buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name)); } } heap::c_allocator.deallocate(errors, ira->codegen->errors_by_index.length); err_set_type->data.error_set.err_count = result_list.length; err_set_type->data.error_set.errors = result_list.items; err_set_type->size_in_bits = ira->codegen->builtin_types.entry_global_error_set->size_in_bits; err_set_type->abi_align = ira->codegen->builtin_types.entry_global_error_set->abi_align; err_set_type->abi_size = ira->codegen->builtin_types.entry_global_error_set->abi_size; buf_appendf(&err_set_type->name, "}"); ZigType *new_target_value_ptr_type = get_pointer_to_type_extra(ira->codegen, err_set_type, ref_type->data.pointer.is_const, ref_type->data.pointer.is_volatile, ref_type->data.pointer.ptr_len, ref_type->data.pointer.explicit_alignment, ref_type->data.pointer.bit_offset_in_host, ref_type->data.pointer.host_int_bytes, ref_type->data.pointer.allow_zero); return ir_analyze_ptr_cast(ira, &instruction->base.base, target_value_ptr, &instruction->target_value_ptr->base, new_target_value_ptr_type, &instruction->base.base, false, false); } return target_value_ptr; } static IrInstGen *ir_analyze_instruction_import(IrAnalyze *ira, IrInstSrcImport *import_instruction) { Error err; IrInstGen *name_value = import_instruction->name->child; Buf *import_target_str = ir_resolve_str(ira, name_value); if (!import_target_str) return ira->codegen->invalid_inst_gen; AstNode *source_node = import_instruction->base.base.source_node; ZigType *import = source_node->owner; ZigType *target_import; Buf *import_target_path; Buf full_path = BUF_INIT; if ((err = analyze_import(ira->codegen, import, import_target_str, &target_import, &import_target_path, &full_path))) { if (err == ErrorImportOutsidePkgPath) { ir_add_error_node(ira, source_node, buf_sprintf("import of file outside package path: '%s'", buf_ptr(import_target_path))); return ira->codegen->invalid_inst_gen; } else if (err == ErrorFileNotFound) { ir_add_error_node(ira, source_node, buf_sprintf("unable to find '%s'", buf_ptr(import_target_path))); return ira->codegen->invalid_inst_gen; } else { ir_add_error_node(ira, source_node, buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err))); return ira->codegen->invalid_inst_gen; } } return ir_const_type(ira, &import_instruction->base.base, target_import); } static IrInstGen *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstSrcRef *ref_instruction) { IrInstGen *value = ref_instruction->value->child; if (type_is_invalid(value->value->type)) return ira->codegen->invalid_inst_gen; bool is_const = false; bool is_volatile = false; ZigValue *child_value = value->value; if (child_value->special == ConstValSpecialStatic) { is_const = true; } return ir_get_ref(ira, &ref_instruction->base.base, value, is_const, is_volatile); } static IrInstGen *ir_analyze_union_init(IrAnalyze *ira, IrInst* source_instruction, AstNode *field_source_node, ZigType *union_type, Buf *field_name, IrInstGen *field_result_loc, IrInstGen *result_loc) { Error err; assert(union_type->id == ZigTypeIdUnion); if ((err = type_resolve(ira->codegen, union_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_inst_gen; TypeUnionField *type_field = find_union_type_field(union_type, field_name); if (type_field == nullptr) { ir_add_error_node(ira, field_source_node, buf_sprintf("no member named '%s' in union '%s'", buf_ptr(field_name), buf_ptr(&union_type->name))); return ira->codegen->invalid_inst_gen; } if (type_is_invalid(type_field->type_entry)) return ira->codegen->invalid_inst_gen; if (result_loc->value->data.x_ptr.mut == ConstPtrMutInfer) { if (instr_is_comptime(field_result_loc) && field_result_loc->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { // nothing } else { result_loc->value->special = ConstValSpecialRuntime; } } bool is_comptime = ir_should_inline(ira->old_irb.exec, source_instruction->scope) || type_requires_comptime(ira->codegen, union_type) == ReqCompTimeYes; IrInstGen *result = ir_get_deref(ira, source_instruction, result_loc, nullptr); if (is_comptime && !instr_is_comptime(result)) { ir_add_error(ira, &field_result_loc->base, buf_sprintf("unable to evaluate constant expression")); return ira->codegen->invalid_inst_gen; } return result; } static IrInstGen *ir_analyze_container_init_fields(IrAnalyze *ira, IrInst *source_instr, ZigType *container_type, size_t instr_field_count, IrInstSrcContainerInitFieldsField *fields, IrInstGen *result_loc) { Error err; if (container_type->id == ZigTypeIdUnion) { if (instr_field_count != 1) { ir_add_error(ira, source_instr, buf_sprintf("union initialization expects exactly one field")); return ira->codegen->invalid_inst_gen; } IrInstSrcContainerInitFieldsField *field = &fields[0]; IrInstGen *field_result_loc = field->result_loc->child; if (type_is_invalid(field_result_loc->value->type)) return ira->codegen->invalid_inst_gen; return ir_analyze_union_init(ira, source_instr, field->source_node, container_type, field->name, field_result_loc, result_loc); } if (container_type->id != ZigTypeIdStruct || is_slice(container_type)) { ir_add_error(ira, source_instr, buf_sprintf("type '%s' does not support struct initialization syntax", buf_ptr(&container_type->name))); return ira->codegen->invalid_inst_gen; } if (container_type->data.structure.resolve_status == ResolveStatusBeingInferred) { // We're now done inferring the type. container_type->data.structure.resolve_status = ResolveStatusUnstarted; } if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_inst_gen; size_t actual_field_count = container_type->data.structure.src_field_count; IrInstGen *first_non_const_instruction = nullptr; AstNode **field_assign_nodes = heap::c_allocator.allocate(actual_field_count); ZigList const_ptrs = {}; bool is_comptime = ir_should_inline(ira->old_irb.exec, source_instr->scope) || type_requires_comptime(ira->codegen, container_type) == ReqCompTimeYes; // Here we iterate over the fields that have been initialized, and emit // compile errors for missing fields and duplicate fields. // It is only now that we find out whether the struct initialization can be a comptime // value, but we have already emitted runtime instructions for the fields that // were initialized with runtime values, and have omitted instructions that would have // initialized fields with comptime values. // So now we must clean up this situation. If it turns out the struct initialization can // be a comptime value, overwrite ConstPtrMutInfer with ConstPtrMutComptimeConst. // Otherwise, we must emit instructions to runtime-initialize the fields that have // comptime-known values. for (size_t i = 0; i < instr_field_count; i += 1) { IrInstSrcContainerInitFieldsField *field = &fields[i]; IrInstGen *field_result_loc = field->result_loc->child; if (type_is_invalid(field_result_loc->value->type)) return ira->codegen->invalid_inst_gen; TypeStructField *type_field = find_struct_type_field(container_type, field->name); if (!type_field) { ir_add_error_node(ira, field->source_node, buf_sprintf("no member named '%s' in struct '%s'", buf_ptr(field->name), buf_ptr(&container_type->name))); return ira->codegen->invalid_inst_gen; } if (type_is_invalid(type_field->type_entry)) return ira->codegen->invalid_inst_gen; size_t field_index = type_field->src_index; AstNode *existing_assign_node = field_assign_nodes[field_index]; if (existing_assign_node) { ErrorMsg *msg = ir_add_error_node(ira, field->source_node, buf_sprintf("duplicate field")); add_error_note(ira->codegen, msg, existing_assign_node, buf_sprintf("other field here")); return ira->codegen->invalid_inst_gen; } field_assign_nodes[field_index] = field->source_node; if (instr_is_comptime(field_result_loc) && field_result_loc->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { const_ptrs.append(field_result_loc); } else { first_non_const_instruction = field_result_loc; } } bool any_missing = false; for (size_t i = 0; i < actual_field_count; i += 1) { if (field_assign_nodes[i] != nullptr) continue; // look for a default field value TypeStructField *field = container_type->data.structure.fields[i]; memoize_field_init_val(ira->codegen, container_type, field); if (field->init_val == nullptr) { ir_add_error(ira, source_instr, buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i]->name))); any_missing = true; continue; } if (type_is_invalid(field->init_val->type)) return ira->codegen->invalid_inst_gen; IrInstGen *runtime_inst = ir_const(ira, source_instr, field->init_val->type); copy_const_val(ira->codegen, runtime_inst->value, field->init_val); IrInstGen *field_ptr = ir_analyze_struct_field_ptr(ira, source_instr, field, result_loc, container_type, true); ir_analyze_store_ptr(ira, source_instr, field_ptr, runtime_inst, false); if (instr_is_comptime(field_ptr) && field_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { const_ptrs.append(field_ptr); } else { first_non_const_instruction = result_loc; } } if (any_missing) return ira->codegen->invalid_inst_gen; if (result_loc->value->data.x_ptr.mut == ConstPtrMutInfer) { if (const_ptrs.length != actual_field_count) { result_loc->value->special = ConstValSpecialRuntime; for (size_t i = 0; i < const_ptrs.length; i += 1) { IrInstGen *field_result_loc = const_ptrs.at(i); IrInstGen *deref = ir_get_deref(ira, &field_result_loc->base, field_result_loc, nullptr); field_result_loc->value->special = ConstValSpecialRuntime; ir_analyze_store_ptr(ira, &field_result_loc->base, field_result_loc, deref, false); } } } IrInstGen *result = ir_get_deref(ira, source_instr, result_loc, nullptr); if (is_comptime && !instr_is_comptime(result)) { ir_add_error_node(ira, first_non_const_instruction->base.source_node, buf_sprintf("unable to evaluate constant expression")); return ira->codegen->invalid_inst_gen; } return result; } static IrInstGen *ir_analyze_instruction_container_init_list(IrAnalyze *ira, IrInstSrcContainerInitList *instruction) { ir_assert(instruction->result_loc != nullptr, &instruction->base.base); IrInstGen *result_loc = instruction->result_loc->child; if (type_is_invalid(result_loc->value->type)) return result_loc; ir_assert(result_loc->value->type->id == ZigTypeIdPointer, &instruction->base.base); if (result_loc->value->type->data.pointer.is_const) { ir_add_error(ira, &instruction->base.base, buf_sprintf("cannot assign to constant")); return ira->codegen->invalid_inst_gen; } ZigType *container_type = result_loc->value->type->data.pointer.child_type; size_t elem_count = instruction->item_count; if (is_slice(container_type)) { ir_add_error_node(ira, instruction->init_array_type_source_node, buf_sprintf("array literal requires address-of operator to coerce to slice type '%s'", buf_ptr(&container_type->name))); return ira->codegen->invalid_inst_gen; } if (container_type->id == ZigTypeIdVoid) { if (elem_count != 0) { ir_add_error_node(ira, instruction->base.base.source_node, buf_sprintf("void expression expects no arguments")); return ira->codegen->invalid_inst_gen; } return ir_const_void(ira, &instruction->base.base); } if (container_type->id == ZigTypeIdStruct && elem_count == 0) { ir_assert(instruction->result_loc != nullptr, &instruction->base.base); IrInstGen *result_loc = instruction->result_loc->child; if (type_is_invalid(result_loc->value->type)) return result_loc; return ir_analyze_container_init_fields(ira, &instruction->base.base, container_type, 0, nullptr, result_loc); } if (container_type->id == ZigTypeIdArray) { ZigType *child_type = container_type->data.array.child_type; if (container_type->data.array.len != elem_count) { ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count, nullptr); ir_add_error(ira, &instruction->base.base, buf_sprintf("expected %s literal, found %s literal", buf_ptr(&container_type->name), buf_ptr(&literal_type->name))); return ira->codegen->invalid_inst_gen; } } else if (container_type->id == ZigTypeIdStruct && container_type->data.structure.resolve_status == ResolveStatusBeingInferred) { // We're now done inferring the type. container_type->data.structure.resolve_status = ResolveStatusUnstarted; } else if (container_type->id == ZigTypeIdVector) { // OK } else { ir_add_error(ira, &instruction->base.base, buf_sprintf("type '%s' does not support array initialization", buf_ptr(&container_type->name))); return ira->codegen->invalid_inst_gen; } switch (type_has_one_possible_value(ira->codegen, container_type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_inst_gen; case OnePossibleValueYes: return ir_const_move(ira, &instruction->base.base, get_the_one_possible_value(ira->codegen, container_type)); case OnePossibleValueNo: break; } bool is_comptime; switch (type_requires_comptime(ira->codegen, container_type)) { case ReqCompTimeInvalid: return ira->codegen->invalid_inst_gen; case ReqCompTimeNo: is_comptime = ir_should_inline(ira->old_irb.exec, instruction->base.base.scope); break; case ReqCompTimeYes: is_comptime = true; break; } IrInstGen *first_non_const_instruction = nullptr; // The Result Location Mechanism has already emitted runtime instructions to // initialize runtime elements and has omitted instructions for the comptime // elements. However it is only now that we find out whether the array initialization // can be a comptime value. So we must clean up the situation. If it turns out // array initialization can be a comptime value, overwrite ConstPtrMutInfer with // ConstPtrMutComptimeConst. Otherwise, emit instructions to runtime-initialize the // elements that have comptime-known values. ZigList const_ptrs = {}; for (size_t i = 0; i < elem_count; i += 1) { IrInstGen *elem_result_loc = instruction->elem_result_loc_list[i]->child; if (type_is_invalid(elem_result_loc->value->type)) return ira->codegen->invalid_inst_gen; assert(elem_result_loc->value->type->id == ZigTypeIdPointer); if (instr_is_comptime(elem_result_loc) && elem_result_loc->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { const_ptrs.append(elem_result_loc); } else { first_non_const_instruction = elem_result_loc; } } if (result_loc->value->data.x_ptr.mut == ConstPtrMutInfer) { if (const_ptrs.length != elem_count) { result_loc->value->special = ConstValSpecialRuntime; for (size_t i = 0; i < const_ptrs.length; i += 1) { IrInstGen *elem_result_loc = const_ptrs.at(i); assert(elem_result_loc->value->special == ConstValSpecialStatic); if (elem_result_loc->value->type->data.pointer.inferred_struct_field != nullptr) { // This field will be generated comptime; no need to do this. continue; } IrInstGen *deref = ir_get_deref(ira, &elem_result_loc->base, elem_result_loc, nullptr); elem_result_loc->value->special = ConstValSpecialRuntime; ir_analyze_store_ptr(ira, &elem_result_loc->base, elem_result_loc, deref, false); } } } const_ptrs.deinit(); IrInstGen *result = ir_get_deref(ira, &instruction->base.base, result_loc, nullptr); // If the result is a tuple, we are allowed to return a struct that uses ConstValSpecialRuntime fields at comptime. if (instr_is_comptime(result) || is_tuple(container_type)) return result; if (is_comptime) { ir_add_error(ira, &first_non_const_instruction->base, buf_sprintf("unable to evaluate constant expression")); return ira->codegen->invalid_inst_gen; } ZigType *result_elem_type = result_loc->value->type->data.pointer.child_type; if (is_slice(result_elem_type)) { ErrorMsg *msg = ir_add_error(ira, &instruction->base.base, buf_sprintf("runtime-initialized array cannot be casted to slice type '%s'", buf_ptr(&result_elem_type->name))); add_error_note(ira->codegen, msg, first_non_const_instruction->base.source_node, buf_sprintf("this value is not comptime-known")); return ira->codegen->invalid_inst_gen; } return result; } static IrInstGen *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, IrInstSrcContainerInitFields *instruction) { ir_assert(instruction->result_loc != nullptr, &instruction->base.base); IrInstGen *result_loc = instruction->result_loc->child; if (type_is_invalid(result_loc->value->type)) return result_loc; ir_assert(result_loc->value->type->id == ZigTypeIdPointer, &instruction->base.base); if (result_loc->value->type->data.pointer.is_const) { ir_add_error(ira, &instruction->base.base, buf_sprintf("cannot assign to constant")); return ira->codegen->invalid_inst_gen; } ZigType *container_type = result_loc->value->type->data.pointer.child_type; return ir_analyze_container_init_fields(ira, &instruction->base.base, container_type, instruction->field_count, instruction->fields, result_loc); } static IrInstGen *ir_analyze_instruction_compile_err(IrAnalyze *ira, IrInstSrcCompileErr *instruction) { IrInstGen *msg_value = instruction->msg->child; Buf *msg_buf = ir_resolve_str(ira, msg_value); if (!msg_buf) return ira->codegen->invalid_inst_gen; ir_add_error(ira, &instruction->base.base, msg_buf); return ira->codegen->invalid_inst_gen; } static IrInstGen *ir_analyze_instruction_compile_log(IrAnalyze *ira, IrInstSrcCompileLog *instruction) { Buf buf = BUF_INIT; fprintf(stderr, "| "); for (size_t i = 0; i < instruction->msg_count; i += 1) { IrInstGen *msg = instruction->msg_list[i]->child; if (type_is_invalid(msg->value->type)) return ira->codegen->invalid_inst_gen; buf_resize(&buf, 0); if (msg->value->special == ConstValSpecialLazy) { // Resolve any lazy value that's passed, we need its value if (ir_resolve_lazy(ira->codegen, msg->base.source_node, msg->value)) return ira->codegen->invalid_inst_gen; } render_const_value(ira->codegen, &buf, msg->value); const char *comma_str = (i != 0) ? ", " : ""; fprintf(stderr, "%s%s", comma_str, buf_ptr(&buf)); } fprintf(stderr, "\n"); auto *expr = &instruction->base.base.source_node->data.fn_call_expr; if (!expr->seen) { // Here we bypass higher level functions such as ir_add_error because we do not want // invalidate_exec to be called. add_node_error(ira->codegen, instruction->base.base.source_node, buf_sprintf("found compile log statement")); } expr->seen = true; return ir_const_void(ira, &instruction->base.base); } static IrInstGen *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstSrcErrName *instruction) { IrInstGen *value = instruction->value->child; if (type_is_invalid(value->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_global_error_set); if (type_is_invalid(casted_value->value->type)) return ira->codegen->invalid_inst_gen; ZigType *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, true, false, PtrLenUnknown, 0, 0, 0, false); ZigType *str_type = get_slice_type(ira->codegen, u8_ptr_type); if (instr_is_comptime(casted_value)) { ZigValue *val = ir_resolve_const(ira, casted_value, UndefBad); if (val == nullptr) return ira->codegen->invalid_inst_gen; ErrorTableEntry *err = casted_value->value->data.x_err_set; if (!err->cached_error_name_val) { ZigValue *array_val = create_const_str_lit(ira->codegen, &err->name)->data.x_ptr.data.ref.pointee; err->cached_error_name_val = create_const_slice(ira->codegen, array_val, 0, buf_len(&err->name), true); } IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); copy_const_val(ira->codegen, result->value, err->cached_error_name_val); result->value->type = str_type; return result; } ira->codegen->generate_error_name_table = true; return ir_build_err_name_gen(ira, &instruction->base.base, value, str_type); } static IrInstGen *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstSrcTagName *instruction) { Error err; IrInstGen *target = instruction->target->child; if (type_is_invalid(target->value->type)) return ira->codegen->invalid_inst_gen; if (target->value->type->id == ZigTypeIdEnumLiteral) { IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); Buf *field_name = target->value->data.x_enum_literal; ZigValue *array_val = create_const_str_lit(ira->codegen, field_name)->data.x_ptr.data.ref.pointee; init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(field_name), true); return result; } if (target->value->type->id == ZigTypeIdUnion) { target = ir_analyze_union_tag(ira, &instruction->base.base, target, instruction->base.is_gen); if (type_is_invalid(target->value->type)) return ira->codegen->invalid_inst_gen; } if (target->value->type->id != ZigTypeIdEnum) { ir_add_error(ira, &target->base, buf_sprintf("expected enum tag, found '%s'", buf_ptr(&target->value->type->name))); return ira->codegen->invalid_inst_gen; } if (target->value->type->data.enumeration.src_field_count == 1 && !target->value->type->data.enumeration.non_exhaustive) { TypeEnumField *only_field = &target->value->type->data.enumeration.fields[0]; ZigValue *array_val = create_const_str_lit(ira->codegen, only_field->name)->data.x_ptr.data.ref.pointee; IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(only_field->name), true); return result; } if (instr_is_comptime(target)) { if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_inst_gen; TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint); if (field == nullptr) { Buf *int_buf = buf_alloc(); bigint_append_buf(int_buf, &target->value->data.x_bigint, 10); ir_add_error(ira, &target->base, buf_sprintf("no tag by value %s", buf_ptr(int_buf))); return ira->codegen->invalid_inst_gen; } ZigValue *array_val = create_const_str_lit(ira->codegen, field->name)->data.x_ptr.data.ref.pointee; IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(field->name), true); return result; } ZigType *u8_ptr_type = get_pointer_to_type_extra( ira->codegen, ira->codegen->builtin_types.entry_u8, true, false, PtrLenUnknown, 0, 0, 0, false); ZigType *result_type = get_slice_type(ira->codegen, u8_ptr_type); return ir_build_tag_name_gen(ira, &instruction->base.base, target, result_type); } static IrInstGen *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, IrInstSrcFieldParentPtr *instruction) { Error err; IrInstGen *type_value = instruction->type_value->child; ZigType *container_type = ir_resolve_type(ira, type_value); if (type_is_invalid(container_type)) return ira->codegen->invalid_inst_gen; IrInstGen *field_name_value = instruction->field_name->child; Buf *field_name = ir_resolve_str(ira, field_name_value); if (!field_name) return ira->codegen->invalid_inst_gen; IrInstGen *field_ptr = instruction->field_ptr->child; if (type_is_invalid(field_ptr->value->type)) return ira->codegen->invalid_inst_gen; if (container_type->id != ZigTypeIdStruct) { ir_add_error(ira, &type_value->base, buf_sprintf("expected struct type, found '%s'", buf_ptr(&container_type->name))); return ira->codegen->invalid_inst_gen; } if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_inst_gen; TypeStructField *field = find_struct_type_field(container_type, field_name); if (field == nullptr) { ir_add_error(ira, &field_name_value->base, buf_sprintf("struct '%s' has no field '%s'", buf_ptr(&container_type->name), buf_ptr(field_name))); return ira->codegen->invalid_inst_gen; } if (field_ptr->value->type->id != ZigTypeIdPointer) { ir_add_error(ira, &field_ptr->base, buf_sprintf("expected pointer, found '%s'", buf_ptr(&field_ptr->value->type->name))); return ira->codegen->invalid_inst_gen; } bool is_packed = (container_type->data.structure.layout == ContainerLayoutPacked); uint32_t field_ptr_align = is_packed ? 1 : get_abi_alignment(ira->codegen, field->type_entry); uint32_t parent_ptr_align = is_packed ? 1 : get_abi_alignment(ira->codegen, container_type); ZigType *field_ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry, field_ptr->value->type->data.pointer.is_const, field_ptr->value->type->data.pointer.is_volatile, PtrLenSingle, field_ptr_align, 0, 0, false); IrInstGen *casted_field_ptr = ir_implicit_cast(ira, field_ptr, field_ptr_type); if (type_is_invalid(casted_field_ptr->value->type)) return ira->codegen->invalid_inst_gen; ZigType *result_type = get_pointer_to_type_extra(ira->codegen, container_type, casted_field_ptr->value->type->data.pointer.is_const, casted_field_ptr->value->type->data.pointer.is_volatile, PtrLenSingle, parent_ptr_align, 0, 0, false); if (instr_is_comptime(casted_field_ptr)) { ZigValue *field_ptr_val = ir_resolve_const(ira, casted_field_ptr, UndefBad); if (!field_ptr_val) return ira->codegen->invalid_inst_gen; if (field_ptr_val->data.x_ptr.special != ConstPtrSpecialBaseStruct) { ir_add_error(ira, &field_ptr->base, buf_sprintf("pointer value not based on parent struct")); return ira->codegen->invalid_inst_gen; } 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.base, buf_sprintf("field '%s' has index %" ZIG_PRI_usize " but pointer value is index %" ZIG_PRI_usize " of struct '%s'", buf_ptr(field->name), field->src_index, ptr_field_index, buf_ptr(&container_type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *result = ir_const(ira, &instruction->base.base, result_type); ZigValue *out_val = result->value; out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.data.ref.pointee = field_ptr_val->data.x_ptr.data.base_struct.struct_val; out_val->data.x_ptr.mut = field_ptr_val->data.x_ptr.mut; return result; } return ir_build_field_parent_ptr_gen(ira, &instruction->base.base, casted_field_ptr, field, result_type); } static TypeStructField *validate_byte_offset(IrAnalyze *ira, IrInstGen *type_value, IrInstGen *field_name_value, size_t *byte_offset) { ZigType *container_type = ir_resolve_type(ira, type_value); if (type_is_invalid(container_type)) return nullptr; Error err; if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown))) return nullptr; Buf *field_name = ir_resolve_str(ira, field_name_value); if (!field_name) return nullptr; if (container_type->id != ZigTypeIdStruct) { ir_add_error(ira, &type_value->base, buf_sprintf("expected struct type, found '%s'", buf_ptr(&container_type->name))); return nullptr; } TypeStructField *field = find_struct_type_field(container_type, field_name); if (field == nullptr) { ir_add_error(ira, &field_name_value->base, buf_sprintf("struct '%s' has no field '%s'", buf_ptr(&container_type->name), buf_ptr(field_name))); return nullptr; } if (!type_has_bits(ira->codegen, field->type_entry)) { ir_add_error(ira, &field_name_value->base, buf_sprintf("zero-bit field '%s' in struct '%s' has no offset", buf_ptr(field_name), buf_ptr(&container_type->name))); return nullptr; } *byte_offset = field->offset; return field; } static IrInstGen *ir_analyze_instruction_byte_offset_of(IrAnalyze *ira, IrInstSrcByteOffsetOf *instruction) { IrInstGen *type_value = instruction->type_value->child; if (type_is_invalid(type_value->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *field_name_value = instruction->field_name->child; size_t byte_offset = 0; if (!validate_byte_offset(ira, type_value, field_name_value, &byte_offset)) return ira->codegen->invalid_inst_gen; return ir_const_unsigned(ira, &instruction->base.base, byte_offset); } static IrInstGen *ir_analyze_instruction_bit_offset_of(IrAnalyze *ira, IrInstSrcBitOffsetOf *instruction) { IrInstGen *type_value = instruction->type_value->child; if (type_is_invalid(type_value->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *field_name_value = instruction->field_name->child; size_t byte_offset = 0; TypeStructField *field = nullptr; if (!(field = validate_byte_offset(ira, type_value, field_name_value, &byte_offset))) return ira->codegen->invalid_inst_gen; size_t bit_offset = byte_offset * 8 + field->bit_offset_in_host; return ir_const_unsigned(ira, &instruction->base.base, bit_offset); } static void ensure_field_index(ZigType *type, const char *field_name, size_t index) { Buf *field_name_buf; assert(type != nullptr && !type_is_invalid(type)); field_name_buf = buf_create_from_str(field_name); TypeStructField *field = find_struct_type_field(type, field_name_buf); buf_deinit(field_name_buf); if (field == nullptr || field->src_index != index) zig_panic("reference to unknown field %s", field_name); } static ZigType *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, ZigType *root) { Error err; ZigType *type_info_type = get_builtin_type(ira->codegen, "TypeInfo"); assert(type_info_type->id == ZigTypeIdUnion); if ((err = type_resolve(ira->codegen, type_info_type, ResolveStatusSizeKnown))) { zig_unreachable(); } if (type_name == nullptr && root == nullptr) return type_info_type; else if (type_name == nullptr) return root; ZigType *root_type = (root == nullptr) ? type_info_type : root; ScopeDecls *type_info_scope = get_container_scope(root_type); assert(type_info_scope != nullptr); Buf field_name = BUF_INIT; buf_init_from_str(&field_name, type_name); auto entry = type_info_scope->decl_table.get(&field_name); buf_deinit(&field_name); TldVar *tld = (TldVar *)entry; assert(tld->base.id == TldIdVar); ZigVar *var = tld->var; assert(var->const_value->type->id == ZigTypeIdMetaType); return ir_resolve_const_type(ira->codegen, ira->new_irb.exec, nullptr, var->const_value); } static Error ir_make_type_info_decls(IrAnalyze *ira, IrInst* source_instr, ZigValue *out_val, ScopeDecls *decls_scope, bool resolve_types) { Error err; ZigType *type_info_declaration_type = ir_type_info_get_type(ira, "Declaration", nullptr); if ((err = type_resolve(ira->codegen, type_info_declaration_type, ResolveStatusSizeKnown))) return err; ensure_field_index(type_info_declaration_type, "name", 0); ensure_field_index(type_info_declaration_type, "is_pub", 1); ensure_field_index(type_info_declaration_type, "data", 2); if (!resolve_types) { ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, type_info_declaration_type, false, false, PtrLenUnknown, 0, 0, 0, false); out_val->special = ConstValSpecialLazy; out_val->type = get_slice_type(ira->codegen, ptr_type); LazyValueTypeInfoDecls *lazy_type_info_decls = heap::c_allocator.create(); lazy_type_info_decls->ira = ira; ira_ref(ira); out_val->data.x_lazy = &lazy_type_info_decls->base; lazy_type_info_decls->base.id = LazyValueIdTypeInfoDecls; lazy_type_info_decls->source_instr = source_instr; lazy_type_info_decls->decls_scope = decls_scope; return ErrorNone; } ZigType *type_info_declaration_data_type = ir_type_info_get_type(ira, "Data", type_info_declaration_type); if ((err = type_resolve(ira->codegen, type_info_declaration_data_type, ResolveStatusSizeKnown))) return err; ZigType *type_info_fn_decl_type = ir_type_info_get_type(ira, "FnDecl", type_info_declaration_data_type); if ((err = type_resolve(ira->codegen, type_info_fn_decl_type, ResolveStatusSizeKnown))) return err; ZigType *type_info_fn_decl_inline_type = ir_type_info_get_type(ira, "Inline", type_info_fn_decl_type); if ((err = type_resolve(ira->codegen, type_info_fn_decl_inline_type, ResolveStatusSizeKnown))) return err; // The unresolved declarations are collected in a separate queue to avoid // modifying decl_table while iterating over it ZigList resolve_decl_queue{}; auto decl_it = decls_scope->decl_table.entry_iterator(); decltype(decls_scope->decl_table)::Entry *curr_entry = nullptr; while ((curr_entry = decl_it.next()) != nullptr) { if (curr_entry->value->resolution == TldResolutionInvalid) { return ErrorSemanticAnalyzeFail; } if (curr_entry->value->resolution == TldResolutionResolving) { ir_error_dependency_loop(ira, source_instr); return ErrorSemanticAnalyzeFail; } // If the declaration is unresolved, force it to be resolved again. if (curr_entry->value->resolution == TldResolutionUnresolved) resolve_decl_queue.append(curr_entry->value); } for (size_t i = 0; i < resolve_decl_queue.length; i++) { Tld *decl = resolve_decl_queue.at(i); resolve_top_level_decl(ira->codegen, decl, decl->source_node, false); if (decl->resolution == TldResolutionInvalid) { return ErrorSemanticAnalyzeFail; } } resolve_decl_queue.deinit(); // Loop through our declarations once to figure out how many declarations we will generate info for. int declaration_count = 0; decl_it = decls_scope->decl_table.entry_iterator(); while ((curr_entry = decl_it.next()) != nullptr) { // Skip comptime blocks and test functions. if (curr_entry->value->id == TldIdCompTime) continue; if (curr_entry->value->id == TldIdFn) { ZigFn *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; if (fn_entry->is_test) continue; } declaration_count += 1; } ZigValue *declaration_array = ira->codegen->pass1_arena->create(); declaration_array->special = ConstValSpecialStatic; declaration_array->type = get_array_type(ira->codegen, type_info_declaration_type, declaration_count, nullptr); declaration_array->data.x_array.special = ConstArraySpecialNone; declaration_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(declaration_count); init_const_slice(ira->codegen, out_val, declaration_array, 0, declaration_count, false); // Loop through the declarations and generate info. decl_it = decls_scope->decl_table.entry_iterator(); curr_entry = nullptr; int declaration_index = 0; while ((curr_entry = decl_it.next()) != nullptr) { // Skip comptime blocks and test functions. if (curr_entry->value->id == TldIdCompTime) { continue; } else if (curr_entry->value->id == TldIdFn) { ZigFn *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; if (fn_entry->is_test) continue; } ZigValue *declaration_val = &declaration_array->data.x_array.data.s_none.elements[declaration_index]; declaration_val->special = ConstValSpecialStatic; declaration_val->type = type_info_declaration_type; ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 3); ZigValue *name = create_const_str_lit(ira->codegen, curr_entry->key)->data.x_ptr.data.ref.pointee; init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(curr_entry->key), true); inner_fields[1]->special = ConstValSpecialStatic; inner_fields[1]->type = ira->codegen->builtin_types.entry_bool; inner_fields[1]->data.x_bool = curr_entry->value->visib_mod == VisibModPub; inner_fields[2]->special = ConstValSpecialStatic; inner_fields[2]->type = type_info_declaration_data_type; inner_fields[2]->parent.id = ConstParentIdStruct; inner_fields[2]->parent.data.p_struct.struct_val = declaration_val; inner_fields[2]->parent.data.p_struct.field_index = 1; switch (curr_entry->value->id) { case TldIdVar: { ZigVar *var = ((TldVar *)curr_entry->value)->var; assert(var != nullptr); if ((err = type_resolve(ira->codegen, var->const_value->type, ResolveStatusSizeKnown))) return ErrorSemanticAnalyzeFail; if (var->const_value->type->id == ZigTypeIdMetaType) { // We have a variable of type 'type', so it's actually a type declaration. // 0: Data.Type: type bigint_init_unsigned(&inner_fields[2]->data.x_union.tag, 0); inner_fields[2]->data.x_union.payload = var->const_value; } else { // We have a variable of another type, so we store the type of the variable. // 1: Data.Var: type bigint_init_unsigned(&inner_fields[2]->data.x_union.tag, 1); ZigValue *payload = ira->codegen->pass1_arena->create(); payload->special = ConstValSpecialStatic; payload->type = ira->codegen->builtin_types.entry_type; payload->data.x_type = var->const_value->type; inner_fields[2]->data.x_union.payload = payload; } break; } case TldIdFn: { // 2: Data.Fn: Data.FnDecl bigint_init_unsigned(&inner_fields[2]->data.x_union.tag, 2); ZigFn *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; assert(!fn_entry->is_test); assert(fn_entry->type_entry != nullptr); AstNodeFnProto *fn_node = &fn_entry->proto_node->data.fn_proto; ZigValue *fn_decl_val = ira->codegen->pass1_arena->create(); fn_decl_val->special = ConstValSpecialStatic; fn_decl_val->type = type_info_fn_decl_type; fn_decl_val->parent.id = ConstParentIdUnion; fn_decl_val->parent.data.p_union.union_val = inner_fields[2]; ZigValue **fn_decl_fields = alloc_const_vals_ptrs(ira->codegen, 9); fn_decl_val->data.x_struct.fields = fn_decl_fields; // fn_type: type ensure_field_index(fn_decl_val->type, "fn_type", 0); fn_decl_fields[0]->special = ConstValSpecialStatic; fn_decl_fields[0]->type = ira->codegen->builtin_types.entry_type; fn_decl_fields[0]->data.x_type = fn_entry->type_entry; // inline_type: Data.FnDecl.Inline ensure_field_index(fn_decl_val->type, "inline_type", 1); fn_decl_fields[1]->special = ConstValSpecialStatic; fn_decl_fields[1]->type = type_info_fn_decl_inline_type; bigint_init_unsigned(&fn_decl_fields[1]->data.x_enum_tag, fn_entry->fn_inline); // is_var_args: bool ensure_field_index(fn_decl_val->type, "is_var_args", 2); bool is_varargs = fn_node->is_var_args; fn_decl_fields[2]->special = ConstValSpecialStatic; fn_decl_fields[2]->type = ira->codegen->builtin_types.entry_bool; fn_decl_fields[2]->data.x_bool = is_varargs; // is_extern: bool ensure_field_index(fn_decl_val->type, "is_extern", 3); fn_decl_fields[3]->special = ConstValSpecialStatic; fn_decl_fields[3]->type = ira->codegen->builtin_types.entry_bool; fn_decl_fields[3]->data.x_bool = fn_node->is_extern; // is_export: bool ensure_field_index(fn_decl_val->type, "is_export", 4); fn_decl_fields[4]->special = ConstValSpecialStatic; fn_decl_fields[4]->type = ira->codegen->builtin_types.entry_bool; fn_decl_fields[4]->data.x_bool = fn_node->is_export; // lib_name: ?[]const u8 ensure_field_index(fn_decl_val->type, "lib_name", 5); fn_decl_fields[5]->special = ConstValSpecialStatic; ZigType *u8_ptr = get_pointer_to_type_extra( ira->codegen, ira->codegen->builtin_types.entry_u8, true, false, PtrLenUnknown, 0, 0, 0, false); fn_decl_fields[5]->type = get_optional_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr)); if (fn_node->is_extern && fn_node->lib_name != nullptr && buf_len(fn_node->lib_name) > 0) { fn_decl_fields[5]->data.x_optional = ira->codegen->pass1_arena->create(); ZigValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name)->data.x_ptr.data.ref.pointee; init_const_slice(ira->codegen, fn_decl_fields[5]->data.x_optional, lib_name, 0, buf_len(fn_node->lib_name), true); } else { fn_decl_fields[5]->data.x_optional = nullptr; } // return_type: type ensure_field_index(fn_decl_val->type, "return_type", 6); fn_decl_fields[6]->special = ConstValSpecialStatic; fn_decl_fields[6]->type = ira->codegen->builtin_types.entry_type; fn_decl_fields[6]->data.x_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; // arg_names: [][] const u8 ensure_field_index(fn_decl_val->type, "arg_names", 7); size_t fn_arg_count = fn_entry->variable_list.length; ZigValue *fn_arg_name_array = ira->codegen->pass1_arena->create(); fn_arg_name_array->special = ConstValSpecialStatic; fn_arg_name_array->type = get_array_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr), fn_arg_count, nullptr); fn_arg_name_array->data.x_array.special = ConstArraySpecialNone; fn_arg_name_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(fn_arg_count); init_const_slice(ira->codegen, fn_decl_fields[7], fn_arg_name_array, 0, fn_arg_count, false); for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) { ZigVar *arg_var = fn_entry->variable_list.at(fn_arg_index); ZigValue *fn_arg_name_val = &fn_arg_name_array->data.x_array.data.s_none.elements[fn_arg_index]; ZigValue *arg_name = create_const_str_lit(ira->codegen, buf_create_from_str(arg_var->name))->data.x_ptr.data.ref.pointee; init_const_slice(ira->codegen, fn_arg_name_val, arg_name, 0, strlen(arg_var->name), true); fn_arg_name_val->parent.id = ConstParentIdArray; fn_arg_name_val->parent.data.p_array.array_val = fn_arg_name_array; fn_arg_name_val->parent.data.p_array.elem_index = fn_arg_index; } inner_fields[2]->data.x_union.payload = fn_decl_val; break; } case TldIdContainer: { ZigType *type_entry = ((TldContainer *)curr_entry->value)->type_entry; if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown))) return ErrorSemanticAnalyzeFail; // This is a type. bigint_init_unsigned(&inner_fields[2]->data.x_union.tag, 0); ZigValue *payload = ira->codegen->pass1_arena->create(); payload->special = ConstValSpecialStatic; payload->type = ira->codegen->builtin_types.entry_type; payload->data.x_type = type_entry; inner_fields[2]->data.x_union.payload = payload; break; } default: zig_unreachable(); } declaration_val->data.x_struct.fields = inner_fields; declaration_index += 1; } assert(declaration_index == declaration_count); return ErrorNone; } static BuiltinPtrSize ptr_len_to_size_enum_index(PtrLen ptr_len) { switch (ptr_len) { case PtrLenSingle: return BuiltinPtrSizeOne; case PtrLenUnknown: return BuiltinPtrSizeMany; case PtrLenC: return BuiltinPtrSizeC; } zig_unreachable(); } static PtrLen size_enum_index_to_ptr_len(BuiltinPtrSize size_enum_index) { switch (size_enum_index) { case BuiltinPtrSizeOne: return PtrLenSingle; case BuiltinPtrSizeMany: case BuiltinPtrSizeSlice: return PtrLenUnknown; case BuiltinPtrSizeC: return PtrLenC; } zig_unreachable(); } static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_entry) { Error err; ZigType *attrs_type; BuiltinPtrSize size_enum_index; if (is_slice(ptr_type_entry)) { attrs_type = ptr_type_entry->data.structure.fields[slice_ptr_index]->type_entry; size_enum_index = BuiltinPtrSizeSlice; } else if (ptr_type_entry->id == ZigTypeIdPointer) { attrs_type = ptr_type_entry; size_enum_index = ptr_len_to_size_enum_index(ptr_type_entry->data.pointer.ptr_len); } else { zig_unreachable(); } if ((err = type_resolve(ira->codegen, attrs_type->data.pointer.child_type, ResolveStatusSizeKnown))) return nullptr; ZigType *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer", nullptr); assertNoError(type_resolve(ira->codegen, type_info_pointer_type, ResolveStatusSizeKnown)); ZigValue *result = ira->codegen->pass1_arena->create(); result->special = ConstValSpecialStatic; result->type = type_info_pointer_type; ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 7); result->data.x_struct.fields = fields; // size: Size ensure_field_index(result->type, "size", 0); ZigType *type_info_pointer_size_type = ir_type_info_get_type(ira, "Size", type_info_pointer_type); assertNoError(type_resolve(ira->codegen, type_info_pointer_size_type, ResolveStatusSizeKnown)); fields[0]->special = ConstValSpecialStatic; fields[0]->type = type_info_pointer_size_type; bigint_init_unsigned(&fields[0]->data.x_enum_tag, size_enum_index); // is_const: bool ensure_field_index(result->type, "is_const", 1); fields[1]->special = ConstValSpecialStatic; fields[1]->type = ira->codegen->builtin_types.entry_bool; fields[1]->data.x_bool = attrs_type->data.pointer.is_const; // is_volatile: bool ensure_field_index(result->type, "is_volatile", 2); fields[2]->special = ConstValSpecialStatic; fields[2]->type = ira->codegen->builtin_types.entry_bool; fields[2]->data.x_bool = attrs_type->data.pointer.is_volatile; // alignment: u32 ensure_field_index(result->type, "alignment", 3); fields[3]->special = ConstValSpecialStatic; fields[3]->type = ira->codegen->builtin_types.entry_num_lit_int; bigint_init_unsigned(&fields[3]->data.x_bigint, get_ptr_align(ira->codegen, attrs_type)); // child: type ensure_field_index(result->type, "child", 4); fields[4]->special = ConstValSpecialStatic; fields[4]->type = ira->codegen->builtin_types.entry_type; fields[4]->data.x_type = attrs_type->data.pointer.child_type; // is_allowzero: bool ensure_field_index(result->type, "is_allowzero", 5); fields[5]->special = ConstValSpecialStatic; fields[5]->type = ira->codegen->builtin_types.entry_bool; fields[5]->data.x_bool = attrs_type->data.pointer.allow_zero; // sentinel: var ensure_field_index(result->type, "sentinel", 6); fields[6]->special = ConstValSpecialStatic; if (attrs_type->data.pointer.child_type->id != ZigTypeIdOpaque) { fields[6]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type); set_optional_payload(fields[6], attrs_type->data.pointer.sentinel); } else { fields[6]->type = ira->codegen->builtin_types.entry_null; } return result; }; static void make_enum_field_val(IrAnalyze *ira, ZigValue *enum_field_val, TypeEnumField *enum_field, ZigType *type_info_enum_field_type) { enum_field_val->special = ConstValSpecialStatic; enum_field_val->type = type_info_enum_field_type; ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 2); inner_fields[1]->special = ConstValSpecialStatic; inner_fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int; ZigValue *name = create_const_str_lit(ira->codegen, enum_field->name)->data.x_ptr.data.ref.pointee; init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(enum_field->name), true); bigint_init_bigint(&inner_fields[1]->data.x_bigint, &enum_field->value); enum_field_val->data.x_struct.fields = inner_fields; } static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigType *type_entry, ZigValue **out) { Error err; assert(type_entry != nullptr); assert(!type_is_invalid(type_entry)); if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown))) return err; auto entry = ira->codegen->type_info_cache.maybe_get(type_entry); if (entry != nullptr) { *out = entry->value; return ErrorNone; } ZigValue *result = nullptr; switch (type_entry->id) { case ZigTypeIdInvalid: zig_unreachable(); case ZigTypeIdMetaType: case ZigTypeIdVoid: case ZigTypeIdBool: case ZigTypeIdUnreachable: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdEnumLiteral: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdOpaque: result = ira->codegen->intern.for_void(); break; case ZigTypeIdInt: { result = ira->codegen->pass1_arena->create(); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Int", nullptr); ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 2); result->data.x_struct.fields = fields; // is_signed: bool ensure_field_index(result->type, "is_signed", 0); fields[0]->special = ConstValSpecialStatic; fields[0]->type = ira->codegen->builtin_types.entry_bool; fields[0]->data.x_bool = type_entry->data.integral.is_signed; // bits: u8 ensure_field_index(result->type, "bits", 1); fields[1]->special = ConstValSpecialStatic; fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int; bigint_init_unsigned(&fields[1]->data.x_bigint, type_entry->data.integral.bit_count); break; } case ZigTypeIdFloat: { result = ira->codegen->pass1_arena->create(); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Float", nullptr); ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 1); result->data.x_struct.fields = fields; // bits: u8 ensure_field_index(result->type, "bits", 0); fields[0]->special = ConstValSpecialStatic; fields[0]->type = ira->codegen->builtin_types.entry_num_lit_int; bigint_init_unsigned(&fields[0]->data.x_bigint, type_entry->data.floating.bit_count); break; } case ZigTypeIdPointer: { result = create_ptr_like_type_info(ira, type_entry); if (result == nullptr) return ErrorSemanticAnalyzeFail; break; } case ZigTypeIdArray: { result = ira->codegen->pass1_arena->create(); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Array", nullptr); ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 3); result->data.x_struct.fields = fields; // len: usize ensure_field_index(result->type, "len", 0); fields[0]->special = ConstValSpecialStatic; fields[0]->type = ira->codegen->builtin_types.entry_num_lit_int; bigint_init_unsigned(&fields[0]->data.x_bigint, type_entry->data.array.len); // child: type ensure_field_index(result->type, "child", 1); fields[1]->special = ConstValSpecialStatic; fields[1]->type = ira->codegen->builtin_types.entry_type; fields[1]->data.x_type = type_entry->data.array.child_type; // sentinel: var fields[2]->special = ConstValSpecialStatic; fields[2]->type = get_optional_type(ira->codegen, type_entry->data.array.child_type); fields[2]->data.x_optional = type_entry->data.array.sentinel; break; } case ZigTypeIdVector: { result = ira->codegen->pass1_arena->create(); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Vector", nullptr); ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 2); result->data.x_struct.fields = fields; // len: usize ensure_field_index(result->type, "len", 0); fields[0]->special = ConstValSpecialStatic; fields[0]->type = ira->codegen->builtin_types.entry_num_lit_int; bigint_init_unsigned(&fields[0]->data.x_bigint, type_entry->data.vector.len); // child: type ensure_field_index(result->type, "child", 1); fields[1]->special = ConstValSpecialStatic; fields[1]->type = ira->codegen->builtin_types.entry_type; fields[1]->data.x_type = type_entry->data.vector.elem_type; break; } case ZigTypeIdOptional: { result = ira->codegen->pass1_arena->create(); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Optional", nullptr); ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 1); result->data.x_struct.fields = fields; // child: type ensure_field_index(result->type, "child", 0); fields[0]->special = ConstValSpecialStatic; fields[0]->type = ira->codegen->builtin_types.entry_type; fields[0]->data.x_type = type_entry->data.maybe.child_type; break; } case ZigTypeIdAnyFrame: { result = ira->codegen->pass1_arena->create(); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "AnyFrame", nullptr); ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 1); result->data.x_struct.fields = fields; // child: ?type ensure_field_index(result->type, "child", 0); fields[0]->special = ConstValSpecialStatic; fields[0]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); fields[0]->data.x_optional = (type_entry->data.any_frame.result_type == nullptr) ? nullptr : create_const_type(ira->codegen, type_entry->data.any_frame.result_type); break; } case ZigTypeIdEnum: { result = ira->codegen->pass1_arena->create(); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Enum", nullptr); ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 5); result->data.x_struct.fields = fields; // layout: ContainerLayout ensure_field_index(result->type, "layout", 0); fields[0]->special = ConstValSpecialStatic; fields[0]->type = ir_type_info_get_type(ira, "ContainerLayout", nullptr); bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.enumeration.layout); // tag_type: type ensure_field_index(result->type, "tag_type", 1); fields[1]->special = ConstValSpecialStatic; fields[1]->type = ira->codegen->builtin_types.entry_type; fields[1]->data.x_type = type_entry->data.enumeration.tag_int_type; // fields: []TypeInfo.EnumField ensure_field_index(result->type, "fields", 2); ZigType *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField", nullptr); if ((err = type_resolve(ira->codegen, type_info_enum_field_type, ResolveStatusSizeKnown))) { zig_unreachable(); } uint32_t enum_field_count = type_entry->data.enumeration.src_field_count; ZigValue *enum_field_array = ira->codegen->pass1_arena->create(); enum_field_array->special = ConstValSpecialStatic; enum_field_array->type = get_array_type(ira->codegen, type_info_enum_field_type, enum_field_count, nullptr); enum_field_array->data.x_array.special = ConstArraySpecialNone; enum_field_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(enum_field_count); init_const_slice(ira->codegen, fields[2], enum_field_array, 0, enum_field_count, false); for (uint32_t enum_field_index = 0; enum_field_index < enum_field_count; enum_field_index++) { TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index]; ZigValue *enum_field_val = &enum_field_array->data.x_array.data.s_none.elements[enum_field_index]; make_enum_field_val(ira, enum_field_val, enum_field, type_info_enum_field_type); enum_field_val->parent.id = ConstParentIdArray; enum_field_val->parent.data.p_array.array_val = enum_field_array; enum_field_val->parent.data.p_array.elem_index = enum_field_index; } // decls: []TypeInfo.Declaration ensure_field_index(result->type, "decls", 3); if ((err = ir_make_type_info_decls(ira, source_instr, fields[3], type_entry->data.enumeration.decls_scope, false))) { return err; } // is_exhaustive: bool ensure_field_index(result->type, "is_exhaustive", 4); fields[4]->special = ConstValSpecialStatic; fields[4]->type = ira->codegen->builtin_types.entry_bool; fields[4]->data.x_bool = !type_entry->data.enumeration.non_exhaustive; break; } case ZigTypeIdErrorSet: { result = ira->codegen->pass1_arena->create(); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "ErrorSet", nullptr); ZigType *type_info_error_type = ir_type_info_get_type(ira, "Error", nullptr); if (!resolve_inferred_error_set(ira->codegen, type_entry, source_instr->source_node)) { return ErrorSemanticAnalyzeFail; } if (type_is_global_error_set(type_entry)) { result->data.x_optional = nullptr; break; } if ((err = type_resolve(ira->codegen, type_info_error_type, ResolveStatusSizeKnown))) { zig_unreachable(); } ZigValue *slice_val = ira->codegen->pass1_arena->create(); result->data.x_optional = slice_val; uint32_t error_count = type_entry->data.error_set.err_count; ZigValue *error_array = ira->codegen->pass1_arena->create(); error_array->special = ConstValSpecialStatic; error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count, nullptr); error_array->data.x_array.special = ConstArraySpecialNone; error_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(error_count); init_const_slice(ira->codegen, slice_val, error_array, 0, error_count, false); for (uint32_t error_index = 0; error_index < error_count; error_index++) { ErrorTableEntry *error = type_entry->data.error_set.errors[error_index]; ZigValue *error_val = &error_array->data.x_array.data.s_none.elements[error_index]; error_val->special = ConstValSpecialStatic; error_val->type = type_info_error_type; ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 2); inner_fields[1]->special = ConstValSpecialStatic; inner_fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int; ZigValue *name = nullptr; if (error->cached_error_name_val != nullptr) name = error->cached_error_name_val; if (name == nullptr) name = create_const_str_lit(ira->codegen, &error->name)->data.x_ptr.data.ref.pointee; init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(&error->name), true); bigint_init_unsigned(&inner_fields[1]->data.x_bigint, error->value); error_val->data.x_struct.fields = inner_fields; error_val->parent.id = ConstParentIdArray; error_val->parent.data.p_array.array_val = error_array; error_val->parent.data.p_array.elem_index = error_index; } break; } case ZigTypeIdErrorUnion: { result = ira->codegen->pass1_arena->create(); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "ErrorUnion", nullptr); ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 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 = ira->codegen->pass1_arena->create(); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Union", nullptr); ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 4); result->data.x_struct.fields = fields; // layout: ContainerLayout ensure_field_index(result->type, "layout", 0); fields[0]->special = ConstValSpecialStatic; fields[0]->type = ir_type_info_get_type(ira, "ContainerLayout", nullptr); bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.unionation.layout); // tag_type: ?type ensure_field_index(result->type, "tag_type", 1); fields[1]->special = ConstValSpecialStatic; fields[1]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); AstNode *union_decl_node = type_entry->data.unionation.decl_node; if (union_decl_node->data.container_decl.auto_enum || union_decl_node->data.container_decl.init_arg_expr != nullptr) { ZigValue *tag_type = ira->codegen->pass1_arena->create(); tag_type->special = ConstValSpecialStatic; tag_type->type = ira->codegen->builtin_types.entry_type; tag_type->data.x_type = type_entry->data.unionation.tag_type; fields[1]->data.x_optional = tag_type; } else { fields[1]->data.x_optional = nullptr; } // fields: []TypeInfo.UnionField ensure_field_index(result->type, "fields", 2); ZigType *type_info_union_field_type = ir_type_info_get_type(ira, "UnionField", nullptr); if ((err = type_resolve(ira->codegen, type_info_union_field_type, ResolveStatusSizeKnown))) zig_unreachable(); uint32_t union_field_count = type_entry->data.unionation.src_field_count; ZigValue *union_field_array = ira->codegen->pass1_arena->create(); union_field_array->special = ConstValSpecialStatic; union_field_array->type = get_array_type(ira->codegen, type_info_union_field_type, union_field_count, nullptr); union_field_array->data.x_array.special = ConstArraySpecialNone; union_field_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(union_field_count); init_const_slice(ira->codegen, fields[2], union_field_array, 0, union_field_count, false); ZigType *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField", nullptr); for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) { TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index]; ZigValue *union_field_val = &union_field_array->data.x_array.data.s_none.elements[union_field_index]; union_field_val->special = ConstValSpecialStatic; union_field_val->type = type_info_union_field_type; ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 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 = ira->codegen->pass1_arena->create(); make_enum_field_val(ira, inner_fields[1]->data.x_optional, union_field->enum_field, type_info_enum_field_type); } inner_fields[2]->special = ConstValSpecialStatic; inner_fields[2]->type = ira->codegen->builtin_types.entry_type; inner_fields[2]->data.x_type = union_field->type_entry; ZigValue *name = create_const_str_lit(ira->codegen, union_field->name)->data.x_ptr.data.ref.pointee; init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(union_field->name), true); union_field_val->data.x_struct.fields = inner_fields; union_field_val->parent.id = ConstParentIdArray; union_field_val->parent.data.p_array.array_val = union_field_array; union_field_val->parent.data.p_array.elem_index = union_field_index; } // decls: []TypeInfo.Declaration ensure_field_index(result->type, "decls", 3); if ((err = ir_make_type_info_decls(ira, source_instr, fields[3], type_entry->data.unionation.decls_scope, false))) { return err; } break; } case ZigTypeIdStruct: { if (type_entry->data.structure.special == StructSpecialSlice) { result = create_ptr_like_type_info(ira, type_entry); if (result == nullptr) return ErrorSemanticAnalyzeFail; break; } result = ira->codegen->pass1_arena->create(); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Struct", nullptr); ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 3); result->data.x_struct.fields = fields; // layout: ContainerLayout ensure_field_index(result->type, "layout", 0); fields[0]->special = ConstValSpecialStatic; fields[0]->type = ir_type_info_get_type(ira, "ContainerLayout", nullptr); bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.structure.layout); // fields: []TypeInfo.StructField ensure_field_index(result->type, "fields", 1); ZigType *type_info_struct_field_type = ir_type_info_get_type(ira, "StructField", nullptr); if ((err = type_resolve(ira->codegen, type_info_struct_field_type, ResolveStatusSizeKnown))) { zig_unreachable(); } uint32_t struct_field_count = type_entry->data.structure.src_field_count; ZigValue *struct_field_array = ira->codegen->pass1_arena->create(); struct_field_array->special = ConstValSpecialStatic; struct_field_array->type = get_array_type(ira->codegen, type_info_struct_field_type, struct_field_count, nullptr); struct_field_array->data.x_array.special = ConstArraySpecialNone; struct_field_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(struct_field_count); init_const_slice(ira->codegen, fields[1], struct_field_array, 0, struct_field_count, false); for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) { TypeStructField *struct_field = type_entry->data.structure.fields[struct_field_index]; ZigValue *struct_field_val = &struct_field_array->data.x_array.data.s_none.elements[struct_field_index]; struct_field_val->special = ConstValSpecialStatic; struct_field_val->type = type_info_struct_field_type; ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 4); inner_fields[1]->special = ConstValSpecialStatic; inner_fields[1]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int); ZigType *field_type = resolve_struct_field_type(ira->codegen, struct_field); if (field_type == nullptr) return ErrorSemanticAnalyzeFail; if ((err = type_resolve(ira->codegen, field_type, ResolveStatusZeroBitsKnown))) return err; if (!type_has_bits(ira->codegen, struct_field->type_entry)) { inner_fields[1]->data.x_optional = nullptr; } else { size_t byte_offset = struct_field->offset; inner_fields[1]->data.x_optional = ira->codegen->pass1_arena->create(); inner_fields[1]->data.x_optional->special = ConstValSpecialStatic; inner_fields[1]->data.x_optional->type = ira->codegen->builtin_types.entry_num_lit_int; bigint_init_unsigned(&inner_fields[1]->data.x_optional->data.x_bigint, byte_offset); } inner_fields[2]->special = ConstValSpecialStatic; inner_fields[2]->type = ira->codegen->builtin_types.entry_type; inner_fields[2]->data.x_type = struct_field->type_entry; // default_value: var inner_fields[3]->special = ConstValSpecialStatic; inner_fields[3]->type = get_optional_type2(ira->codegen, struct_field->type_entry); if (inner_fields[3]->type == nullptr) return ErrorSemanticAnalyzeFail; memoize_field_init_val(ira->codegen, type_entry, struct_field); if(struct_field->init_val != nullptr && type_is_invalid(struct_field->init_val->type)){ return ErrorSemanticAnalyzeFail; } set_optional_payload(inner_fields[3], struct_field->init_val); ZigValue *name = create_const_str_lit(ira->codegen, struct_field->name)->data.x_ptr.data.ref.pointee; init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(struct_field->name), true); struct_field_val->data.x_struct.fields = inner_fields; struct_field_val->parent.id = ConstParentIdArray; struct_field_val->parent.data.p_array.array_val = struct_field_array; struct_field_val->parent.data.p_array.elem_index = struct_field_index; } // decls: []TypeInfo.Declaration ensure_field_index(result->type, "decls", 2); if ((err = ir_make_type_info_decls(ira, source_instr, fields[2], type_entry->data.structure.decls_scope, false))) { return err; } break; } case ZigTypeIdFn: { result = ira->codegen->pass1_arena->create(); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Fn", nullptr); ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 5); result->data.x_struct.fields = fields; // calling_convention: TypeInfo.CallingConvention ensure_field_index(result->type, "calling_convention", 0); fields[0]->special = ConstValSpecialStatic; fields[0]->type = get_builtin_type(ira->codegen, "CallingConvention"); bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.fn.fn_type_id.cc); // is_generic: bool ensure_field_index(result->type, "is_generic", 1); bool is_generic = type_entry->data.fn.is_generic; fields[1]->special = ConstValSpecialStatic; fields[1]->type = ira->codegen->builtin_types.entry_bool; fields[1]->data.x_bool = is_generic; // is_varargs: bool ensure_field_index(result->type, "is_var_args", 2); bool is_varargs = type_entry->data.fn.fn_type_id.is_var_args; fields[2]->special = ConstValSpecialStatic; fields[2]->type = ira->codegen->builtin_types.entry_bool; fields[2]->data.x_bool = type_entry->data.fn.fn_type_id.is_var_args; // return_type: ?type ensure_field_index(result->type, "return_type", 3); fields[3]->special = ConstValSpecialStatic; fields[3]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (type_entry->data.fn.fn_type_id.return_type == nullptr) fields[3]->data.x_optional = nullptr; else { ZigValue *return_type = ira->codegen->pass1_arena->create(); return_type->special = ConstValSpecialStatic; return_type->type = ira->codegen->builtin_types.entry_type; return_type->data.x_type = type_entry->data.fn.fn_type_id.return_type; fields[3]->data.x_optional = return_type; } // args: []TypeInfo.FnArg ZigType *type_info_fn_arg_type = ir_type_info_get_type(ira, "FnArg", nullptr); if ((err = type_resolve(ira->codegen, type_info_fn_arg_type, ResolveStatusSizeKnown))) { zig_unreachable(); } size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count - (is_varargs && type_entry->data.fn.fn_type_id.cc != CallingConventionC); ZigValue *fn_arg_array = ira->codegen->pass1_arena->create(); fn_arg_array->special = ConstValSpecialStatic; fn_arg_array->type = get_array_type(ira->codegen, type_info_fn_arg_type, fn_arg_count, nullptr); fn_arg_array->data.x_array.special = ConstArraySpecialNone; fn_arg_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(fn_arg_count); init_const_slice(ira->codegen, fields[4], fn_arg_array, 0, fn_arg_count, false); for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) { FnTypeParamInfo *fn_param_info = &type_entry->data.fn.fn_type_id.param_info[fn_arg_index]; ZigValue *fn_arg_val = &fn_arg_array->data.x_array.data.s_none.elements[fn_arg_index]; fn_arg_val->special = ConstValSpecialStatic; fn_arg_val->type = type_info_fn_arg_type; bool arg_is_generic = fn_param_info->type == nullptr; if (arg_is_generic) assert(is_generic); ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 3); inner_fields[0]->special = ConstValSpecialStatic; inner_fields[0]->type = ira->codegen->builtin_types.entry_bool; inner_fields[0]->data.x_bool = arg_is_generic; inner_fields[1]->special = ConstValSpecialStatic; inner_fields[1]->type = ira->codegen->builtin_types.entry_bool; inner_fields[1]->data.x_bool = fn_param_info->is_noalias; inner_fields[2]->special = ConstValSpecialStatic; inner_fields[2]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (arg_is_generic) inner_fields[2]->data.x_optional = nullptr; else { ZigValue *arg_type = ira->codegen->pass1_arena->create(); arg_type->special = ConstValSpecialStatic; arg_type->type = ira->codegen->builtin_types.entry_type; arg_type->data.x_type = fn_param_info->type; inner_fields[2]->data.x_optional = arg_type; } fn_arg_val->data.x_struct.fields = inner_fields; fn_arg_val->parent.id = ConstParentIdArray; fn_arg_val->parent.data.p_array.array_val = fn_arg_array; fn_arg_val->parent.data.p_array.elem_index = fn_arg_index; } break; } case ZigTypeIdBoundFn: { ZigType *fn_type = type_entry->data.bound_fn.fn_type; assert(fn_type->id == ZigTypeIdFn); if ((err = ir_make_type_info_value(ira, source_instr, fn_type, &result))) return err; break; } case ZigTypeIdFnFrame: ir_add_error(ira, source_instr, buf_sprintf("compiler bug: TODO @typeInfo for async function frames. https://github.com/ziglang/zig/issues/3066")); return ErrorSemanticAnalyzeFail; } assert(result != nullptr); ira->codegen->type_info_cache.put(type_entry, result); *out = result; return ErrorNone; } static IrInstGen *ir_analyze_instruction_type_info(IrAnalyze *ira, IrInstSrcTypeInfo *instruction) { Error err; IrInstGen *type_value = instruction->type_value->child; ZigType *type_entry = ir_resolve_type(ira, type_value); if (type_is_invalid(type_entry)) return ira->codegen->invalid_inst_gen; ZigType *result_type = ir_type_info_get_type(ira, nullptr, nullptr); ZigValue *payload; if ((err = ir_make_type_info_value(ira, &instruction->base.base, type_entry, &payload))) return ira->codegen->invalid_inst_gen; IrInstGen *result = ir_const(ira, &instruction->base.base, result_type); ZigValue *out_val = result->value; bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry)); out_val->data.x_union.payload = payload; if (payload != nullptr) { payload->parent.id = ConstParentIdUnion; payload->parent.data.p_union.union_val = out_val; } return result; } static ZigValue *get_const_field(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, const char *name, size_t field_index) { Error err; ensure_field_index(struct_value->type, name, field_index); ZigValue *val = struct_value->data.x_struct.fields[field_index]; if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, source_node, val, UndefBad))) return nullptr; return val; } static Error get_const_field_sentinel(IrAnalyze *ira, IrInst* source_instr, ZigValue *struct_value, const char *name, size_t field_index, ZigType *elem_type, ZigValue **result) { ZigValue *field_val = get_const_field(ira, source_instr->source_node, struct_value, name, field_index); if (field_val == nullptr) return ErrorSemanticAnalyzeFail; IrInstGen *field_inst = ir_const_move(ira, source_instr, field_val); IrInstGen *casted_field_inst = ir_implicit_cast(ira, field_inst, get_optional_type(ira->codegen, elem_type)); if (type_is_invalid(casted_field_inst->value->type)) return ErrorSemanticAnalyzeFail; if (optional_value_is_null(casted_field_inst->value)) { *result = nullptr; } else { assert(type_has_optional_repr(casted_field_inst->value->type)); *result = casted_field_inst->value->data.x_optional; } return ErrorNone; } static Error get_const_field_bool(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, const char *name, size_t field_index, bool *out) { ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index); if (value == nullptr) return ErrorSemanticAnalyzeFail; assert(value->type == ira->codegen->builtin_types.entry_bool); *out = value->data.x_bool; return ErrorNone; } static BigInt *get_const_field_lit_int(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, const char *name, size_t field_index) { ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index); if (value == nullptr) return nullptr; assert(value->type == ira->codegen->builtin_types.entry_num_lit_int); return &value->data.x_bigint; } static ZigType *get_const_field_meta_type(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, const char *name, size_t field_index) { ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index); if (value == nullptr) return ira->codegen->invalid_inst_gen->value->type; assert(value->type == ira->codegen->builtin_types.entry_type); return value->data.x_type; } static ZigType *get_const_field_meta_type_optional(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, const char *name, size_t field_index) { ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index); if (value == nullptr) return ira->codegen->invalid_inst_gen->value->type; assert(value->type->id == ZigTypeIdOptional); assert(value->type->data.maybe.child_type == ira->codegen->builtin_types.entry_type); if (value->data.x_optional == nullptr) return nullptr; return value->data.x_optional->data.x_type; } static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeId tagTypeId, ZigValue *payload) { Error err; switch (tagTypeId) { case ZigTypeIdInvalid: zig_unreachable(); case ZigTypeIdMetaType: return ira->codegen->builtin_types.entry_type; case ZigTypeIdVoid: return ira->codegen->builtin_types.entry_void; case ZigTypeIdBool: return ira->codegen->builtin_types.entry_bool; case ZigTypeIdUnreachable: return ira->codegen->builtin_types.entry_unreachable; case ZigTypeIdInt: { assert(payload->special == ConstValSpecialStatic); assert(payload->type == ir_type_info_get_type(ira, "Int", nullptr)); BigInt *bi = get_const_field_lit_int(ira, source_instr->source_node, payload, "bits", 1); if (bi == nullptr) return ira->codegen->invalid_inst_gen->value->type; bool is_signed; if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_signed", 0, &is_signed))) return ira->codegen->invalid_inst_gen->value->type; return get_int_type(ira->codegen, is_signed, bigint_as_u32(bi)); } case ZigTypeIdFloat: { assert(payload->special == ConstValSpecialStatic); assert(payload->type == ir_type_info_get_type(ira, "Float", nullptr)); BigInt *bi = get_const_field_lit_int(ira, source_instr->source_node, payload, "bits", 0); if (bi == nullptr) return ira->codegen->invalid_inst_gen->value->type; uint32_t bits = bigint_as_u32(bi); switch (bits) { case 16: return ira->codegen->builtin_types.entry_f16; case 32: return ira->codegen->builtin_types.entry_f32; case 64: return ira->codegen->builtin_types.entry_f64; case 128: return ira->codegen->builtin_types.entry_f128; } ir_add_error(ira, source_instr, buf_sprintf("%d-bit float unsupported", bits)); return ira->codegen->invalid_inst_gen->value->type; } case ZigTypeIdPointer: { ZigType *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer", nullptr); assert(payload->special == ConstValSpecialStatic); assert(payload->type == type_info_pointer_type); ZigValue *size_value = get_const_field(ira, source_instr->source_node, payload, "size", 0); assert(size_value->type == ir_type_info_get_type(ira, "Size", type_info_pointer_type)); BuiltinPtrSize size_enum_index = (BuiltinPtrSize)bigint_as_u32(&size_value->data.x_enum_tag); PtrLen ptr_len = size_enum_index_to_ptr_len(size_enum_index); ZigType *elem_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 4); if (type_is_invalid(elem_type)) return ira->codegen->invalid_inst_gen->value->type; ZigValue *sentinel; if ((err = get_const_field_sentinel(ira, source_instr, payload, "sentinel", 6, elem_type, &sentinel))) { return ira->codegen->invalid_inst_gen->value->type; } BigInt *bi = get_const_field_lit_int(ira, source_instr->source_node, payload, "alignment", 3); if (bi == nullptr) return ira->codegen->invalid_inst_gen->value->type; bool is_const; if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_const", 1, &is_const))) return ira->codegen->invalid_inst_gen->value->type; bool is_volatile; if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_volatile", 2, &is_volatile))) { return ira->codegen->invalid_inst_gen->value->type; } bool is_allowzero; if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_allowzero", 5, &is_allowzero))) { return ira->codegen->invalid_inst_gen->value->type; } ZigType *ptr_type = get_pointer_to_type_extra2(ira->codegen, elem_type, is_const, is_volatile, ptr_len, bigint_as_u32(bi), 0, // bit_offset_in_host 0, // host_int_bytes is_allowzero, VECTOR_INDEX_NONE, nullptr, sentinel); if (size_enum_index != 2) return ptr_type; return get_slice_type(ira->codegen, ptr_type); } case ZigTypeIdArray: { assert(payload->special == ConstValSpecialStatic); assert(payload->type == ir_type_info_get_type(ira, "Array", nullptr)); ZigType *elem_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 1); if (type_is_invalid(elem_type)) return ira->codegen->invalid_inst_gen->value->type; ZigValue *sentinel; if ((err = get_const_field_sentinel(ira, source_instr, payload, "sentinel", 2, elem_type, &sentinel))) { return ira->codegen->invalid_inst_gen->value->type; } BigInt *bi = get_const_field_lit_int(ira, source_instr->source_node, payload, "len", 0); if (bi == nullptr) return ira->codegen->invalid_inst_gen->value->type; return get_array_type(ira->codegen, elem_type, bigint_as_u64(bi), sentinel); } case ZigTypeIdComptimeFloat: return ira->codegen->builtin_types.entry_num_lit_float; case ZigTypeIdComptimeInt: return ira->codegen->builtin_types.entry_num_lit_int; case ZigTypeIdUndefined: return ira->codegen->builtin_types.entry_undef; case ZigTypeIdNull: return ira->codegen->builtin_types.entry_null; case ZigTypeIdOptional: { assert(payload->special == ConstValSpecialStatic); assert(payload->type == ir_type_info_get_type(ira, "Optional", nullptr)); ZigType *child_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 0); return get_optional_type(ira->codegen, child_type); } case ZigTypeIdErrorUnion: { assert(payload->special == ConstValSpecialStatic); assert(payload->type == ir_type_info_get_type(ira, "ErrorUnion", nullptr)); ZigType *err_set_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "error_set", 0); ZigType *payload_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "payload", 1); return get_error_union_type(ira->codegen, err_set_type, payload_type); } case ZigTypeIdOpaque: { Buf *bare_name = buf_alloc(); Buf *full_name = get_anon_type_name(ira->codegen, ira->old_irb.exec, "opaque", source_instr->scope, source_instr->source_node, bare_name); return get_opaque_type(ira->codegen, source_instr->scope, source_instr->source_node, buf_ptr(full_name), bare_name); } case ZigTypeIdVector: { assert(payload->special == ConstValSpecialStatic); assert(payload->type == ir_type_info_get_type(ira, "Vector", nullptr)); BigInt *len = get_const_field_lit_int(ira, source_instr->source_node, payload, "len", 0); ZigType *child_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 1); Error err; if ((err = ir_validate_vector_elem_type(ira, source_instr->source_node, child_type))) { return ira->codegen->invalid_inst_gen->value->type; } return get_vector_type(ira->codegen, bigint_as_u32(len), child_type); } case ZigTypeIdAnyFrame: { assert(payload->special == ConstValSpecialStatic); assert(payload->type == ir_type_info_get_type(ira, "AnyFrame", nullptr)); ZigType *child_type = get_const_field_meta_type_optional(ira, source_instr->source_node, payload, "child", 0); return get_any_frame_type(ira->codegen, child_type); } case ZigTypeIdErrorSet: case ZigTypeIdEnum: case ZigTypeIdFnFrame: case ZigTypeIdEnumLiteral: ir_add_error(ira, source_instr, buf_sprintf( "TODO implement @Type for 'TypeInfo.%s': see https://github.com/ziglang/zig/issues/2907", type_id_name(tagTypeId))); return ira->codegen->invalid_inst_gen->value->type; case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdBoundFn: case ZigTypeIdStruct: ir_add_error(ira, source_instr, buf_sprintf( "@Type not available for 'TypeInfo.%s'", type_id_name(tagTypeId))); return ira->codegen->invalid_inst_gen->value->type; } zig_unreachable(); } static IrInstGen *ir_analyze_instruction_type(IrAnalyze *ira, IrInstSrcType *instruction) { IrInstGen *uncasted_type_info = instruction->type_info->child; if (type_is_invalid(uncasted_type_info->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *type_info = ir_implicit_cast(ira, uncasted_type_info, ir_type_info_get_type(ira, nullptr, nullptr)); if (type_is_invalid(type_info->value->type)) return ira->codegen->invalid_inst_gen; ZigValue *type_info_val = ir_resolve_const(ira, type_info, UndefBad); if (type_info_val == nullptr) return ira->codegen->invalid_inst_gen; ZigTypeId type_id_tag = type_id_at_index(bigint_as_usize(&type_info_val->data.x_union.tag)); ZigType *type = type_info_to_type(ira, &uncasted_type_info->base, type_id_tag, type_info_val->data.x_union.payload); if (type_is_invalid(type)) return ira->codegen->invalid_inst_gen; return ir_const_type(ira, &instruction->base.base, type); } static IrInstGen *ir_analyze_instruction_set_eval_branch_quota(IrAnalyze *ira, IrInstSrcSetEvalBranchQuota *instruction) { uint64_t new_quota; if (!ir_resolve_usize(ira, instruction->new_quota->child, &new_quota)) return ira->codegen->invalid_inst_gen; if (new_quota > *ira->new_irb.exec->backward_branch_quota) { *ira->new_irb.exec->backward_branch_quota = new_quota; } return ir_const_void(ira, &instruction->base.base); } static IrInstGen *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstSrcTypeName *instruction) { IrInstGen *type_value = instruction->type_value->child; ZigType *type_entry = ir_resolve_type(ira, type_value); if (type_is_invalid(type_entry)) return ira->codegen->invalid_inst_gen; if (!type_entry->cached_const_name_val) { type_entry->cached_const_name_val = create_const_str_lit(ira->codegen, type_bare_name(type_entry)); } IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); copy_const_val(ira->codegen, result->value, type_entry->cached_const_name_val); return result; } static void ir_cimport_cache_paths(Buf *cache_dir, Buf *tmp_c_file_digest, Buf *out_zig_dir, Buf *out_zig_path) { buf_resize(out_zig_dir, 0); buf_resize(out_zig_path, 0); buf_appendf(out_zig_dir, "%s" OS_SEP "o" OS_SEP "%s", buf_ptr(cache_dir), buf_ptr(tmp_c_file_digest)); buf_appendf(out_zig_path, "%s" OS_SEP "cimport.zig", buf_ptr(out_zig_dir)); } static IrInstGen *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstSrcCImport *instruction) { Error err; AstNode *node = instruction->base.base.source_node; assert(node->type == NodeTypeFnCallExpr); AstNode *block_node = node->data.fn_call_expr.params.at(0); ScopeCImport *cimport_scope = create_cimport_scope(ira->codegen, node, instruction->base.base.scope); // Execute the C import block like an inline function ZigType *void_type = ira->codegen->builtin_types.entry_void; ZigValue *cimport_result; ZigValue *result_ptr; create_result_ptr(ira->codegen, void_type, &cimport_result, &result_ptr); if ((err = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, result_ptr, ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, &cimport_scope->buf, block_node, nullptr, nullptr, nullptr, UndefBad))) { return ira->codegen->invalid_inst_gen; } if (type_is_invalid(cimport_result->type)) return ira->codegen->invalid_inst_gen; ZigPackage *cur_scope_pkg = scope_package(instruction->base.base.scope); Buf *namespace_name = buf_sprintf("%s.cimport:%" ZIG_PRI_usize ":%" ZIG_PRI_usize, buf_ptr(&cur_scope_pkg->pkg_path), node->line + 1, node->column + 1); ZigPackage *cimport_pkg = new_anonymous_package(); cimport_pkg->package_table.put(buf_create_from_str("builtin"), ira->codegen->compile_var_package); cimport_pkg->package_table.put(buf_create_from_str("std"), ira->codegen->std_package); buf_init_from_buf(&cimport_pkg->pkg_path, namespace_name); CacheHash *cache_hash; if ((err = create_c_object_cache(ira->codegen, &cache_hash, false))) { ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to create cache: %s", err_str(err))); return ira->codegen->invalid_inst_gen; } cache_buf(cache_hash, &cimport_scope->buf); // Set this because we're not adding any files before checking for a hit. cache_hash->force_check_manifest = true; Buf tmp_c_file_digest = BUF_INIT; buf_resize(&tmp_c_file_digest, 0); if ((err = cache_hit(cache_hash, &tmp_c_file_digest))) { if (err != ErrorInvalidFormat) { ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to check cache: %s", err_str(err))); return ira->codegen->invalid_inst_gen; } } ira->codegen->caches_to_release.append(cache_hash); Buf *out_zig_dir = buf_alloc(); Buf *out_zig_path = buf_alloc(); if (buf_len(&tmp_c_file_digest) == 0 || cache_hash->files.length == 0) { // Cache Miss Buf *tmp_c_file_dir = buf_sprintf("%s" OS_SEP "o" OS_SEP "%s", buf_ptr(ira->codegen->cache_dir), buf_ptr(&cache_hash->b64_digest)); Buf *resolve_paths[] = { tmp_c_file_dir, buf_create_from_str("cimport.h"), }; Buf tmp_c_file_path = os_path_resolve(resolve_paths, 2); if ((err = os_make_path(tmp_c_file_dir))) { ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to make dir: %s", err_str(err))); return ira->codegen->invalid_inst_gen; } if ((err = os_write_file(&tmp_c_file_path, &cimport_scope->buf))) { ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to write .h file: %s", err_str(err))); return ira->codegen->invalid_inst_gen; } if (ira->codegen->verbose_cimport) { fprintf(stderr, "@cImport source: %s\n", buf_ptr(&tmp_c_file_path)); } Buf *tmp_dep_file = buf_sprintf("%s.d", buf_ptr(&tmp_c_file_path)); ZigList clang_argv = {0}; add_cc_args(ira->codegen, clang_argv, buf_ptr(tmp_dep_file), true, FileExtC); clang_argv.append(buf_ptr(&tmp_c_file_path)); if (ira->codegen->verbose_cc) { fprintf(stderr, "clang"); for (size_t i = 0; i < clang_argv.length; i += 1) { fprintf(stderr, " %s", clang_argv.at(i)); } fprintf(stderr, "\n"); } clang_argv.append(nullptr); // to make the [start...end] argument work Stage2ErrorMsg *errors_ptr; size_t errors_len; Stage2Ast *ast; const char *resources_path = buf_ptr(ira->codegen->zig_c_headers_dir); if ((err = stage2_translate_c(&ast, &errors_ptr, &errors_len, &clang_argv.at(0), &clang_argv.last(), resources_path))) { if (err != ErrorCCompileErrors) { ir_add_error_node(ira, node, buf_sprintf("C import failed: %s", err_str(err))); return ira->codegen->invalid_inst_gen; } ErrorMsg *parent_err_msg = ir_add_error_node(ira, node, buf_sprintf("C import failed")); if (ira->codegen->libc_link_lib == nullptr) { add_error_note(ira->codegen, parent_err_msg, node, buf_sprintf("libc headers not available; compilation does not link against libc")); } for (size_t i = 0; i < errors_len; i += 1) { Stage2ErrorMsg *clang_err = &errors_ptr[i]; // Clang can emit "too many errors, stopping now", in which case `source` and `filename_ptr` are null if (clang_err->source && clang_err->filename_ptr) { ErrorMsg *err_msg = err_msg_create_with_offset( clang_err->filename_ptr ? buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) : buf_alloc(), clang_err->line, clang_err->column, clang_err->offset, clang_err->source, buf_create_from_mem(clang_err->msg_ptr, clang_err->msg_len)); err_msg_add_note(parent_err_msg, err_msg); } } return ira->codegen->invalid_inst_gen; } if (ira->codegen->verbose_cimport) { fprintf(stderr, "@cImport .d file: %s\n", buf_ptr(tmp_dep_file)); } if ((err = cache_add_dep_file(cache_hash, tmp_dep_file, false))) { ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to parse .d file: %s", err_str(err))); return ira->codegen->invalid_inst_gen; } if ((err = cache_final(cache_hash, &tmp_c_file_digest))) { ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to finalize cache: %s", err_str(err))); return ira->codegen->invalid_inst_gen; } ir_cimport_cache_paths(ira->codegen->cache_dir, &tmp_c_file_digest, out_zig_dir, out_zig_path); if ((err = os_make_path(out_zig_dir))) { ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to make output dir: %s", err_str(err))); return ira->codegen->invalid_inst_gen; } FILE *out_file = fopen(buf_ptr(out_zig_path), "wb"); if (out_file == nullptr) { ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to open output file: %s", strerror(errno))); return ira->codegen->invalid_inst_gen; } stage2_render_ast(ast, out_file); if (fclose(out_file) != 0) { ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to write to output file: %s", strerror(errno))); return ira->codegen->invalid_inst_gen; } if (ira->codegen->verbose_cimport) { fprintf(stderr, "@cImport output: %s\n", buf_ptr(out_zig_path)); } } else { // Cache Hit ir_cimport_cache_paths(ira->codegen->cache_dir, &tmp_c_file_digest, out_zig_dir, out_zig_path); if (ira->codegen->verbose_cimport) { fprintf(stderr, "@cImport cache hit: %s\n", buf_ptr(out_zig_path)); } } Buf *import_code = buf_alloc(); if ((err = file_fetch(ira->codegen, out_zig_path, import_code))) { ir_add_error_node(ira, node, buf_sprintf("unable to open '%s': %s", buf_ptr(out_zig_path), err_str(err))); return ira->codegen->invalid_inst_gen; } ZigType *child_import = add_source_file(ira->codegen, cimport_pkg, out_zig_path, import_code, SourceKindCImport); return ir_const_type(ira, &instruction->base.base, child_import); } static IrInstGen *ir_analyze_instruction_c_include(IrAnalyze *ira, IrInstSrcCInclude *instruction) { IrInstGen *name_value = instruction->name->child; if (type_is_invalid(name_value->value->type)) return ira->codegen->invalid_inst_gen; Buf *include_name = ir_resolve_str(ira, name_value); if (!include_name) return ira->codegen->invalid_inst_gen; Buf *c_import_buf = ira->new_irb.exec->c_import_buf; // We check for this error in pass1 assert(c_import_buf); buf_appendf(c_import_buf, "#include <%s>\n", buf_ptr(include_name)); return ir_const_void(ira, &instruction->base.base); } static IrInstGen *ir_analyze_instruction_c_define(IrAnalyze *ira, IrInstSrcCDefine *instruction) { IrInstGen *name = instruction->name->child; if (type_is_invalid(name->value->type)) return ira->codegen->invalid_inst_gen; Buf *define_name = ir_resolve_str(ira, name); if (!define_name) return ira->codegen->invalid_inst_gen; IrInstGen *value = instruction->value->child; if (type_is_invalid(value->value->type)) return ira->codegen->invalid_inst_gen; Buf *define_value = nullptr; // The second parameter is either a string or void (equivalent to "") if (value->value->type->id != ZigTypeIdVoid) { define_value = ir_resolve_str(ira, value); if (!define_value) return ira->codegen->invalid_inst_gen; } Buf *c_import_buf = ira->new_irb.exec->c_import_buf; // We check for this error in pass1 assert(c_import_buf); buf_appendf(c_import_buf, "#define %s %s\n", buf_ptr(define_name), define_value ? buf_ptr(define_value) : ""); return ir_const_void(ira, &instruction->base.base); } static IrInstGen *ir_analyze_instruction_c_undef(IrAnalyze *ira, IrInstSrcCUndef *instruction) { IrInstGen *name = instruction->name->child; if (type_is_invalid(name->value->type)) return ira->codegen->invalid_inst_gen; Buf *undef_name = ir_resolve_str(ira, name); if (!undef_name) return ira->codegen->invalid_inst_gen; Buf *c_import_buf = ira->new_irb.exec->c_import_buf; // We check for this error in pass1 assert(c_import_buf); buf_appendf(c_import_buf, "#undef %s\n", buf_ptr(undef_name)); return ir_const_void(ira, &instruction->base.base); } static IrInstGen *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstSrcEmbedFile *instruction) { IrInstGen *name = instruction->name->child; if (type_is_invalid(name->value->type)) return ira->codegen->invalid_inst_gen; Buf *rel_file_path = ir_resolve_str(ira, name); if (!rel_file_path) return ira->codegen->invalid_inst_gen; ZigType *import = get_scope_import(instruction->base.base.scope); // figure out absolute path to resource Buf source_dir_path = BUF_INIT; os_path_dirname(import->data.structure.root_struct->path, &source_dir_path); Buf *resolve_paths[] = { &source_dir_path, rel_file_path, }; Buf *file_path = buf_alloc(); *file_path = os_path_resolve(resolve_paths, 2); // load from file system into const expr Buf *file_contents = buf_alloc(); Error err; if ((err = file_fetch(ira->codegen, file_path, file_contents))) { if (err == ErrorFileNotFound) { ir_add_error(ira, &instruction->name->base, buf_sprintf("unable to find '%s'", buf_ptr(file_path))); return ira->codegen->invalid_inst_gen; } else { ir_add_error(ira, &instruction->name->base, buf_sprintf("unable to open '%s': %s", buf_ptr(file_path), err_str(err))); return ira->codegen->invalid_inst_gen; } } IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); init_const_str_lit(ira->codegen, result->value, file_contents); return result; } static IrInstGen *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstSrcCmpxchg *instruction) { ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->type_value->child); if (type_is_invalid(operand_type)) return ira->codegen->invalid_inst_gen; if (operand_type->id == ZigTypeIdFloat) { ir_add_error(ira, &instruction->type_value->child->base, buf_sprintf("expected integer, enum or pointer type, found '%s'", buf_ptr(&operand_type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *ptr = instruction->ptr->child; if (type_is_invalid(ptr->value->type)) return ira->codegen->invalid_inst_gen; // TODO let this be volatile ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); IrInstGen *casted_ptr = ir_implicit_cast2(ira, &instruction->ptr->base, ptr, ptr_type); if (type_is_invalid(casted_ptr->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *cmp_value = instruction->cmp_value->child; if (type_is_invalid(cmp_value->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *new_value = instruction->new_value->child; if (type_is_invalid(new_value->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *success_order_value = instruction->success_order_value->child; if (type_is_invalid(success_order_value->value->type)) return ira->codegen->invalid_inst_gen; AtomicOrder success_order; if (!ir_resolve_atomic_order(ira, success_order_value, &success_order)) return ira->codegen->invalid_inst_gen; IrInstGen *failure_order_value = instruction->failure_order_value->child; if (type_is_invalid(failure_order_value->value->type)) return ira->codegen->invalid_inst_gen; AtomicOrder failure_order; if (!ir_resolve_atomic_order(ira, failure_order_value, &failure_order)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_cmp_value = ir_implicit_cast2(ira, &instruction->cmp_value->base, cmp_value, operand_type); if (type_is_invalid(casted_cmp_value->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_new_value = ir_implicit_cast2(ira, &instruction->new_value->base, new_value, operand_type); if (type_is_invalid(casted_new_value->value->type)) return ira->codegen->invalid_inst_gen; if (success_order < AtomicOrderMonotonic) { ir_add_error(ira, &success_order_value->base, buf_sprintf("success atomic ordering must be Monotonic or stricter")); return ira->codegen->invalid_inst_gen; } if (failure_order < AtomicOrderMonotonic) { ir_add_error(ira, &failure_order_value->base, buf_sprintf("failure atomic ordering must be Monotonic or stricter")); return ira->codegen->invalid_inst_gen; } if (failure_order > success_order) { ir_add_error(ira, &failure_order_value->base, buf_sprintf("failure atomic ordering must be no stricter than success")); return ira->codegen->invalid_inst_gen; } if (failure_order == AtomicOrderRelease || failure_order == AtomicOrderAcqRel) { ir_add_error(ira, &failure_order_value->base, buf_sprintf("failure atomic ordering must not be Release or AcqRel")); return ira->codegen->invalid_inst_gen; } ZigType *result_type = get_optional_type(ira->codegen, operand_type); // special case zero bit types switch (type_has_one_possible_value(ira->codegen, operand_type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_inst_gen; case OnePossibleValueYes: { IrInstGen *result = ir_const(ira, &instruction->base.base, result_type); set_optional_value_to_null(result->value); return result; } case OnePossibleValueNo: break; } if (instr_is_comptime(casted_ptr) && casted_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar && instr_is_comptime(casted_cmp_value) && instr_is_comptime(casted_new_value)) { ZigValue *ptr_val = ir_resolve_const(ira, casted_ptr, UndefBad); if (ptr_val == nullptr) return ira->codegen->invalid_inst_gen; ZigValue *stored_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.base.source_node); if (stored_val == nullptr) return ira->codegen->invalid_inst_gen; ZigValue *expected_val = ir_resolve_const(ira, casted_cmp_value, UndefBad); if (expected_val == nullptr) return ira->codegen->invalid_inst_gen; ZigValue *new_val = ir_resolve_const(ira, casted_new_value, UndefBad); if (new_val == nullptr) return ira->codegen->invalid_inst_gen; bool eql = const_values_equal(ira->codegen, stored_val, expected_val); IrInstGen *result = ir_const(ira, &instruction->base.base, result_type); if (eql) { copy_const_val(ira->codegen, stored_val, new_val); set_optional_value_to_null(result->value); } else { set_optional_payload(result->value, stored_val); } return result; } IrInstGen *result_loc; if (handle_is_ptr(ira->codegen, result_type)) { result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, result_type, nullptr, true, true); if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) { return result_loc; } } else { result_loc = nullptr; } return ir_build_cmpxchg_gen(ira, &instruction->base.base, result_type, casted_ptr, casted_cmp_value, casted_new_value, success_order, failure_order, instruction->is_weak, result_loc); } static IrInstGen *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstSrcFence *instruction) { IrInstGen *order_inst = instruction->order->child; if (type_is_invalid(order_inst->value->type)) return ira->codegen->invalid_inst_gen; AtomicOrder order; if (!ir_resolve_atomic_order(ira, order_inst, &order)) return ira->codegen->invalid_inst_gen; if (order < AtomicOrderAcquire) { ir_add_error(ira, &order_inst->base, buf_sprintf("atomic ordering must be Acquire or stricter")); return ira->codegen->invalid_inst_gen; } return ir_build_fence_gen(ira, &instruction->base.base, order); } static IrInstGen *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstSrcTruncate *instruction) { IrInstGen *dest_type_value = instruction->dest_type->child; ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->invalid_inst_gen; if (dest_type->id != ZigTypeIdInt && dest_type->id != ZigTypeIdComptimeInt) { ir_add_error(ira, &dest_type_value->base, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *target = instruction->target->child; ZigType *src_type = target->value->type; if (type_is_invalid(src_type)) return ira->codegen->invalid_inst_gen; if (src_type->id != ZigTypeIdInt && src_type->id != ZigTypeIdComptimeInt) { ir_add_error(ira, &target->base, buf_sprintf("expected integer type, found '%s'", buf_ptr(&src_type->name))); return ira->codegen->invalid_inst_gen; } if (dest_type->id == ZigTypeIdComptimeInt) { return ir_implicit_cast2(ira, &instruction->target->base, target, dest_type); } if (instr_is_comptime(target)) { ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (val == nullptr) return ira->codegen->invalid_inst_gen; IrInstGen *result = ir_const(ira, &instruction->base.base, dest_type); bigint_truncate(&result->value->data.x_bigint, &val->data.x_bigint, dest_type->data.integral.bit_count, dest_type->data.integral.is_signed); return result; } if (src_type->data.integral.bit_count == 0 || dest_type->data.integral.bit_count == 0) { IrInstGen *result = ir_const(ira, &instruction->base.base, dest_type); bigint_init_unsigned(&result->value->data.x_bigint, 0); return result; } if (src_type->data.integral.is_signed != dest_type->data.integral.is_signed) { const char *sign_str = dest_type->data.integral.is_signed ? "signed" : "unsigned"; ir_add_error(ira, &target->base, buf_sprintf("expected %s integer type, found '%s'", sign_str, buf_ptr(&src_type->name))); return ira->codegen->invalid_inst_gen; } else if (src_type->data.integral.bit_count < dest_type->data.integral.bit_count) { ir_add_error(ira, &target->base, buf_sprintf("type '%s' has fewer bits than destination type '%s'", buf_ptr(&src_type->name), buf_ptr(&dest_type->name))); return ira->codegen->invalid_inst_gen; } return ir_build_truncate_gen(ira, &instruction->base.base, dest_type, target); } static IrInstGen *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstSrcIntCast *instruction) { ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) return ira->codegen->invalid_inst_gen; if (dest_type->id != ZigTypeIdInt && dest_type->id != ZigTypeIdComptimeInt) { ir_add_error(ira, &instruction->dest_type->base, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *target = instruction->target->child; if (type_is_invalid(target->value->type)) return ira->codegen->invalid_inst_gen; if (target->value->type->id != ZigTypeIdInt && target->value->type->id != ZigTypeIdComptimeInt) { ir_add_error(ira, &instruction->target->base, buf_sprintf("expected integer type, found '%s'", buf_ptr(&target->value->type->name))); return ira->codegen->invalid_inst_gen; } if (instr_is_comptime(target) || dest_type->id == ZigTypeIdComptimeInt) { return ir_implicit_cast2(ira, &instruction->target->base, target, dest_type); } return ir_analyze_widen_or_shorten(ira, &instruction->base.base, target, dest_type); } static IrInstGen *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstSrcFloatCast *instruction) { ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) return ira->codegen->invalid_inst_gen; if (dest_type->id != ZigTypeIdFloat && dest_type->id != ZigTypeIdComptimeFloat) { ir_add_error(ira, &instruction->dest_type->base, buf_sprintf("expected float type, found '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *target = instruction->target->child; if (type_is_invalid(target->value->type)) return ira->codegen->invalid_inst_gen; if (target->value->type->id == ZigTypeIdComptimeInt || target->value->type->id == ZigTypeIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, target, dest_type, true)) { CastOp op; if (target->value->type->id == ZigTypeIdComptimeInt) { op = CastOpIntToFloat; } else { op = CastOpNumLitToConcrete; } return ir_resolve_cast(ira, &instruction->base.base, target, dest_type, op); } else { return ira->codegen->invalid_inst_gen; } } if (instr_is_comptime(target) || dest_type->id == ZigTypeIdComptimeFloat) { return ir_implicit_cast2(ira, &instruction->target->base, target, dest_type); } if (target->value->type->id != ZigTypeIdFloat) { ir_add_error(ira, &instruction->target->base, buf_sprintf("expected float type, found '%s'", buf_ptr(&target->value->type->name))); return ira->codegen->invalid_inst_gen; } return ir_analyze_widen_or_shorten(ira, &instruction->base.base, target, dest_type); } static IrInstGen *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInstSrcErrSetCast *instruction) { ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) return ira->codegen->invalid_inst_gen; if (dest_type->id != ZigTypeIdErrorSet) { ir_add_error(ira, &instruction->dest_type->base, buf_sprintf("expected error set type, found '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *target = instruction->target->child; if (type_is_invalid(target->value->type)) return ira->codegen->invalid_inst_gen; if (target->value->type->id != ZigTypeIdErrorSet) { ir_add_error(ira, &instruction->target->base, buf_sprintf("expected error set type, found '%s'", buf_ptr(&target->value->type->name))); return ira->codegen->invalid_inst_gen; } return ir_analyze_err_set_cast(ira, &instruction->base.base, target, dest_type); } static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) { Error err; ZigType *ptr_type; if (is_slice(ty)) { TypeStructField *ptr_field = ty->data.structure.fields[slice_ptr_index]; ptr_type = resolve_struct_field_type(ira->codegen, ptr_field); } else { ptr_type = get_src_ptr_type(ty); } assert(ptr_type != nullptr); if (ptr_type->id == ZigTypeIdPointer) { if ((err = type_resolve(ira->codegen, ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) return err; } else if (is_slice(ptr_type)) { TypeStructField *ptr_field = ptr_type->data.structure.fields[slice_ptr_index]; ZigType *slice_ptr_type = resolve_struct_field_type(ira->codegen, ptr_field); if ((err = type_resolve(ira->codegen, slice_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) return err; } *result_align = get_ptr_align(ira->codegen, ty); return ErrorNone; } static IrInstGen *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstSrcIntToFloat *instruction) { ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) return ira->codegen->invalid_inst_gen; if (dest_type->id != ZigTypeIdFloat && dest_type->id != ZigTypeIdComptimeFloat) { ir_add_error(ira, &instruction->dest_type->base, buf_sprintf("expected float type, found '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *target = instruction->target->child; if (type_is_invalid(target->value->type)) return ira->codegen->invalid_inst_gen; if (target->value->type->id != ZigTypeIdInt && target->value->type->id != ZigTypeIdComptimeInt) { ir_add_error(ira, &instruction->target->base, buf_sprintf("expected int type, found '%s'", buf_ptr(&target->value->type->name))); return ira->codegen->invalid_inst_gen; } return ir_resolve_cast(ira, &instruction->base.base, target, dest_type, CastOpIntToFloat); } static IrInstGen *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrInstSrcFloatToInt *instruction) { ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) return ira->codegen->invalid_inst_gen; if (dest_type->id != ZigTypeIdInt && dest_type->id != ZigTypeIdComptimeInt) { ir_add_error(ira, &instruction->dest_type->base, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *target = instruction->target->child; if (type_is_invalid(target->value->type)) return ira->codegen->invalid_inst_gen; if (target->value->type->id == ZigTypeIdComptimeInt) { return ir_implicit_cast(ira, target, dest_type); } if (target->value->type->id != ZigTypeIdFloat && target->value->type->id != ZigTypeIdComptimeFloat) { ir_add_error_node(ira, target->base.source_node, buf_sprintf("expected float type, found '%s'", buf_ptr(&target->value->type->name))); return ira->codegen->invalid_inst_gen; } return ir_resolve_cast(ira, &instruction->base.base, target, dest_type, CastOpFloatToInt); } static IrInstGen *ir_analyze_instruction_err_to_int(IrAnalyze *ira, IrInstSrcErrToInt *instruction) { IrInstGen *target = instruction->target->child; if (type_is_invalid(target->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_target; if (target->value->type->id == ZigTypeIdErrorSet) { casted_target = target; } else { casted_target = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_global_error_set); if (type_is_invalid(casted_target->value->type)) return ira->codegen->invalid_inst_gen; } return ir_analyze_err_to_int(ira, &instruction->base.base, casted_target, ira->codegen->err_tag_type); } static IrInstGen *ir_analyze_instruction_int_to_err(IrAnalyze *ira, IrInstSrcIntToErr *instruction) { IrInstGen *target = instruction->target->child; if (type_is_invalid(target->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_target = ir_implicit_cast(ira, target, ira->codegen->err_tag_type); if (type_is_invalid(casted_target->value->type)) return ira->codegen->invalid_inst_gen; return ir_analyze_int_to_err(ira, &instruction->base.base, casted_target, ira->codegen->builtin_types.entry_global_error_set); } static IrInstGen *ir_analyze_instruction_bool_to_int(IrAnalyze *ira, IrInstSrcBoolToInt *instruction) { IrInstGen *target = instruction->target->child; if (type_is_invalid(target->value->type)) return ira->codegen->invalid_inst_gen; if (target->value->type->id != ZigTypeIdBool) { ir_add_error(ira, &instruction->target->base, buf_sprintf("expected bool, found '%s'", buf_ptr(&target->value->type->name))); return ira->codegen->invalid_inst_gen; } if (instr_is_comptime(target)) { bool is_true; if (!ir_resolve_bool(ira, target, &is_true)) return ira->codegen->invalid_inst_gen; return ir_const_unsigned(ira, &instruction->base.base, is_true ? 1 : 0); } ZigType *u1_type = get_int_type(ira->codegen, false, 1); return ir_resolve_cast(ira, &instruction->base.base, target, u1_type, CastOpBoolToInt); } static IrInstGen *ir_analyze_instruction_vector_type(IrAnalyze *ira, IrInstSrcVectorType *instruction) { uint64_t len; if (!ir_resolve_unsigned(ira, instruction->len->child, ira->codegen->builtin_types.entry_u32, &len)) return ira->codegen->invalid_inst_gen; ZigType *elem_type = ir_resolve_vector_elem_type(ira, instruction->elem_type->child); if (type_is_invalid(elem_type)) return ira->codegen->invalid_inst_gen; ZigType *vector_type = get_vector_type(ira->codegen, len, elem_type); return ir_const_type(ira, &instruction->base.base, vector_type); } static IrInstGen *ir_analyze_shuffle_vector(IrAnalyze *ira, IrInst* source_instr, ZigType *scalar_type, IrInstGen *a, IrInstGen *b, IrInstGen *mask) { Error err; ir_assert(source_instr && scalar_type && a && b && mask, source_instr); if ((err = ir_validate_vector_elem_type(ira, source_instr->source_node, scalar_type))) return ira->codegen->invalid_inst_gen; uint32_t len_mask; if (mask->value->type->id == ZigTypeIdVector) { len_mask = mask->value->type->data.vector.len; } else if (mask->value->type->id == ZigTypeIdArray) { len_mask = mask->value->type->data.array.len; } else { ir_add_error(ira, &mask->base, buf_sprintf("expected vector or array, found '%s'", buf_ptr(&mask->value->type->name))); return ira->codegen->invalid_inst_gen; } mask = ir_implicit_cast(ira, mask, get_vector_type(ira->codegen, len_mask, ira->codegen->builtin_types.entry_i32)); if (type_is_invalid(mask->value->type)) return ira->codegen->invalid_inst_gen; uint32_t len_a; if (a->value->type->id == ZigTypeIdVector) { len_a = a->value->type->data.vector.len; } else if (a->value->type->id == ZigTypeIdArray) { len_a = a->value->type->data.array.len; } else if (a->value->type->id == ZigTypeIdUndefined) { len_a = UINT32_MAX; } else { ir_add_error(ira, &a->base, buf_sprintf("expected vector or array with element type '%s', found '%s'", buf_ptr(&scalar_type->name), buf_ptr(&a->value->type->name))); return ira->codegen->invalid_inst_gen; } uint32_t len_b; if (b->value->type->id == ZigTypeIdVector) { len_b = b->value->type->data.vector.len; } else if (b->value->type->id == ZigTypeIdArray) { len_b = b->value->type->data.array.len; } else if (b->value->type->id == ZigTypeIdUndefined) { len_b = UINT32_MAX; } else { ir_add_error(ira, &b->base, buf_sprintf("expected vector or array with element type '%s', found '%s'", buf_ptr(&scalar_type->name), buf_ptr(&b->value->type->name))); return ira->codegen->invalid_inst_gen; } if (len_a == UINT32_MAX && len_b == UINT32_MAX) { return ir_const_undef(ira, &a->base, get_vector_type(ira->codegen, len_mask, scalar_type)); } if (len_a == UINT32_MAX) { len_a = len_b; a = ir_const_undef(ira, &a->base, get_vector_type(ira->codegen, len_a, scalar_type)); } else { a = ir_implicit_cast(ira, a, get_vector_type(ira->codegen, len_a, scalar_type)); if (type_is_invalid(a->value->type)) return ira->codegen->invalid_inst_gen; } if (len_b == UINT32_MAX) { len_b = len_a; b = ir_const_undef(ira, &b->base, get_vector_type(ira->codegen, len_b, scalar_type)); } else { b = ir_implicit_cast(ira, b, get_vector_type(ira->codegen, len_b, scalar_type)); if (type_is_invalid(b->value->type)) return ira->codegen->invalid_inst_gen; } ZigValue *mask_val = ir_resolve_const(ira, mask, UndefOk); if (mask_val == nullptr) return ira->codegen->invalid_inst_gen; expand_undef_array(ira->codegen, mask_val); for (uint32_t i = 0; i < len_mask; i += 1) { ZigValue *mask_elem_val = &mask_val->data.x_array.data.s_none.elements[i]; if (mask_elem_val->special == ConstValSpecialUndef) continue; int32_t v_i32 = bigint_as_signed(&mask_elem_val->data.x_bigint); uint32_t v; IrInstGen *chosen_operand; if (v_i32 >= 0) { v = (uint32_t)v_i32; chosen_operand = a; } else { v = (uint32_t)~v_i32; chosen_operand = b; } if (v >= chosen_operand->value->type->data.vector.len) { ErrorMsg *msg = ir_add_error(ira, &mask->base, buf_sprintf("mask index '%u' has out-of-bounds selection", i)); add_error_note(ira->codegen, msg, chosen_operand->base.source_node, buf_sprintf("selected index '%u' out of bounds of %s", v, buf_ptr(&chosen_operand->value->type->name))); if (chosen_operand == a && v < len_a + len_b) { add_error_note(ira->codegen, msg, b->base.source_node, buf_create_from_str("selections from the second vector are specified with negative numbers")); } return ira->codegen->invalid_inst_gen; } } ZigType *result_type = get_vector_type(ira->codegen, len_mask, scalar_type); if (instr_is_comptime(a) && instr_is_comptime(b)) { ZigValue *a_val = ir_resolve_const(ira, a, UndefOk); if (a_val == nullptr) return ira->codegen->invalid_inst_gen; ZigValue *b_val = ir_resolve_const(ira, b, UndefOk); if (b_val == nullptr) return ira->codegen->invalid_inst_gen; expand_undef_array(ira->codegen, a_val); expand_undef_array(ira->codegen, b_val); IrInstGen *result = ir_const(ira, source_instr, result_type); result->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(len_mask); for (uint32_t i = 0; i < mask_val->type->data.vector.len; i += 1) { ZigValue *mask_elem_val = &mask_val->data.x_array.data.s_none.elements[i]; ZigValue *result_elem_val = &result->value->data.x_array.data.s_none.elements[i]; if (mask_elem_val->special == ConstValSpecialUndef) { result_elem_val->special = ConstValSpecialUndef; continue; } int32_t v = bigint_as_signed(&mask_elem_val->data.x_bigint); // We've already checked for and emitted compile errors for index out of bounds here. ZigValue *src_elem_val = (v >= 0) ? &a->value->data.x_array.data.s_none.elements[v] : &b->value->data.x_array.data.s_none.elements[~v]; copy_const_val(ira->codegen, result_elem_val, src_elem_val); ir_assert(result_elem_val->special == ConstValSpecialStatic, source_instr); } result->value->special = ConstValSpecialStatic; return result; } // All static analysis passed, and not comptime. // For runtime codegen, vectors a and b must be the same length. Here we // recursively @shuffle the smaller vector to append undefined elements // to it up to the length of the longer vector. This recursion terminates // in 1 call because these calls to ir_analyze_shuffle_vector guarantee // len_a == len_b. if (len_a != len_b) { uint32_t len_min = min(len_a, len_b); uint32_t len_max = max(len_a, len_b); IrInstGen *expand_mask = ir_const(ira, &mask->base, get_vector_type(ira->codegen, len_max, ira->codegen->builtin_types.entry_i32)); expand_mask->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(len_max); uint32_t i = 0; for (; i < len_min; i += 1) bigint_init_unsigned(&expand_mask->value->data.x_array.data.s_none.elements[i].data.x_bigint, i); for (; i < len_max; i += 1) bigint_init_signed(&expand_mask->value->data.x_array.data.s_none.elements[i].data.x_bigint, -1); IrInstGen *undef = ir_const_undef(ira, source_instr, get_vector_type(ira->codegen, len_min, scalar_type)); if (len_b < len_a) { b = ir_analyze_shuffle_vector(ira, source_instr, scalar_type, b, undef, expand_mask); } else { a = ir_analyze_shuffle_vector(ira, source_instr, scalar_type, a, undef, expand_mask); } } return ir_build_shuffle_vector_gen(ira, source_instr->scope, source_instr->source_node, result_type, a, b, mask); } static IrInstGen *ir_analyze_instruction_shuffle_vector(IrAnalyze *ira, IrInstSrcShuffleVector *instruction) { ZigType *scalar_type = ir_resolve_vector_elem_type(ira, instruction->scalar_type->child); if (type_is_invalid(scalar_type)) return ira->codegen->invalid_inst_gen; IrInstGen *a = instruction->a->child; if (type_is_invalid(a->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *b = instruction->b->child; if (type_is_invalid(b->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *mask = instruction->mask->child; if (type_is_invalid(mask->value->type)) return ira->codegen->invalid_inst_gen; return ir_analyze_shuffle_vector(ira, &instruction->base.base, scalar_type, a, b, mask); } static IrInstGen *ir_analyze_instruction_splat(IrAnalyze *ira, IrInstSrcSplat *instruction) { Error err; IrInstGen *len = instruction->len->child; if (type_is_invalid(len->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *scalar = instruction->scalar->child; if (type_is_invalid(scalar->value->type)) return ira->codegen->invalid_inst_gen; uint64_t len_u64; if (!ir_resolve_unsigned(ira, len, ira->codegen->builtin_types.entry_u32, &len_u64)) return ira->codegen->invalid_inst_gen; uint32_t len_int = len_u64; if ((err = ir_validate_vector_elem_type(ira, scalar->base.source_node, scalar->value->type))) return ira->codegen->invalid_inst_gen; ZigType *return_type = get_vector_type(ira->codegen, len_int, scalar->value->type); if (instr_is_comptime(scalar)) { ZigValue *scalar_val = ir_resolve_const(ira, scalar, UndefOk); if (scalar_val == nullptr) return ira->codegen->invalid_inst_gen; if (scalar_val->special == ConstValSpecialUndef) return ir_const_undef(ira, &instruction->base.base, return_type); IrInstGen *result = ir_const(ira, &instruction->base.base, return_type); result->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(len_int); for (uint32_t i = 0; i < len_int; i += 1) { copy_const_val(ira->codegen, &result->value->data.x_array.data.s_none.elements[i], scalar_val); } return result; } return ir_build_splat_gen(ira, &instruction->base.base, return_type, scalar); } static IrInstGen *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstSrcBoolNot *instruction) { IrInstGen *value = instruction->value->child; if (type_is_invalid(value->value->type)) return ira->codegen->invalid_inst_gen; ZigType *bool_type = ira->codegen->builtin_types.entry_bool; IrInstGen *casted_value = ir_implicit_cast(ira, value, bool_type); if (type_is_invalid(casted_value->value->type)) return ira->codegen->invalid_inst_gen; if (instr_is_comptime(casted_value)) { ZigValue *value = ir_resolve_const(ira, casted_value, UndefBad); if (value == nullptr) return ira->codegen->invalid_inst_gen; return ir_const_bool(ira, &instruction->base.base, !value->data.x_bool); } return ir_build_bool_not_gen(ira, &instruction->base.base, casted_value); } static IrInstGen *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstSrcMemset *instruction) { Error err; IrInstGen *dest_ptr = instruction->dest_ptr->child; if (type_is_invalid(dest_ptr->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *byte_value = instruction->byte->child; if (type_is_invalid(byte_value->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *count_value = instruction->count->child; if (type_is_invalid(count_value->value->type)) return ira->codegen->invalid_inst_gen; ZigType *dest_uncasted_type = dest_ptr->value->type; bool dest_is_volatile = (dest_uncasted_type->id == ZigTypeIdPointer) && dest_uncasted_type->data.pointer.is_volatile; ZigType *usize = ira->codegen->builtin_types.entry_usize; ZigType *u8 = ira->codegen->builtin_types.entry_u8; uint32_t dest_align; if (dest_uncasted_type->id == ZigTypeIdPointer) { if ((err = resolve_ptr_align(ira, dest_uncasted_type, &dest_align))) return ira->codegen->invalid_inst_gen; } else { dest_align = get_abi_alignment(ira->codegen, u8); } ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, PtrLenUnknown, dest_align, 0, 0, false); IrInstGen *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr); if (type_is_invalid(casted_dest_ptr->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_byte = ir_implicit_cast(ira, byte_value, u8); if (type_is_invalid(casted_byte->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_count = ir_implicit_cast(ira, count_value, usize); if (type_is_invalid(casted_count->value->type)) return ira->codegen->invalid_inst_gen; // TODO test this at comptime with u8 and non-u8 types if (instr_is_comptime(casted_dest_ptr) && instr_is_comptime(casted_byte) && instr_is_comptime(casted_count)) { ZigValue *dest_ptr_val = ir_resolve_const(ira, casted_dest_ptr, UndefBad); if (dest_ptr_val == nullptr) return ira->codegen->invalid_inst_gen; ZigValue *byte_val = ir_resolve_const(ira, casted_byte, UndefOk); if (byte_val == nullptr) return ira->codegen->invalid_inst_gen; ZigValue *count_val = ir_resolve_const(ira, casted_count, UndefBad); if (count_val == nullptr) return ira->codegen->invalid_inst_gen; if (casted_dest_ptr->value->data.x_ptr.special != ConstPtrSpecialHardCodedAddr && casted_dest_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { ZigValue *dest_elements; size_t start; size_t bound_end; switch (dest_ptr_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: case ConstPtrSpecialDiscard: zig_unreachable(); case ConstPtrSpecialRef: dest_elements = dest_ptr_val->data.x_ptr.data.ref.pointee; start = 0; bound_end = 1; break; case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: { ZigValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val; expand_undef_array(ira->codegen, array_val); dest_elements = array_val->data.x_array.data.s_none.elements; start = dest_ptr_val->data.x_ptr.data.base_array.elem_index; bound_end = array_val->type->data.array.len; break; } case ConstPtrSpecialBaseStruct: zig_panic("TODO memset on const inner struct"); case ConstPtrSpecialBaseErrorUnionCode: zig_panic("TODO memset on const inner error union code"); case ConstPtrSpecialBaseErrorUnionPayload: zig_panic("TODO memset on const inner error union payload"); case ConstPtrSpecialBaseOptionalPayload: zig_panic("TODO memset on const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memset on ptr cast from function"); case ConstPtrSpecialNull: zig_panic("TODO memset on null ptr"); } size_t count = bigint_as_usize(&count_val->data.x_bigint); size_t end = start + count; if (end > bound_end) { ir_add_error(ira, &count_value->base, buf_sprintf("out of bounds pointer access")); return ira->codegen->invalid_inst_gen; } for (size_t i = start; i < end; i += 1) { copy_const_val(ira->codegen, &dest_elements[i], byte_val); } return ir_const_void(ira, &instruction->base.base); } } return ir_build_memset_gen(ira, &instruction->base.base, casted_dest_ptr, casted_byte, casted_count); } static IrInstGen *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstSrcMemcpy *instruction) { Error err; IrInstGen *dest_ptr = instruction->dest_ptr->child; if (type_is_invalid(dest_ptr->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *src_ptr = instruction->src_ptr->child; if (type_is_invalid(src_ptr->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *count_value = instruction->count->child; if (type_is_invalid(count_value->value->type)) return ira->codegen->invalid_inst_gen; ZigType *u8 = ira->codegen->builtin_types.entry_u8; ZigType *dest_uncasted_type = dest_ptr->value->type; ZigType *src_uncasted_type = src_ptr->value->type; bool dest_is_volatile = (dest_uncasted_type->id == ZigTypeIdPointer) && dest_uncasted_type->data.pointer.is_volatile; bool src_is_volatile = (src_uncasted_type->id == ZigTypeIdPointer) && src_uncasted_type->data.pointer.is_volatile; uint32_t dest_align; if (dest_uncasted_type->id == ZigTypeIdPointer) { if ((err = resolve_ptr_align(ira, dest_uncasted_type, &dest_align))) return ira->codegen->invalid_inst_gen; } else { dest_align = get_abi_alignment(ira->codegen, u8); } uint32_t src_align; if (src_uncasted_type->id == ZigTypeIdPointer) { if ((err = resolve_ptr_align(ira, src_uncasted_type, &src_align))) return ira->codegen->invalid_inst_gen; } else { src_align = get_abi_alignment(ira->codegen, u8); } ZigType *usize = ira->codegen->builtin_types.entry_usize; ZigType *u8_ptr_mut = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, PtrLenUnknown, dest_align, 0, 0, false); ZigType *u8_ptr_const = get_pointer_to_type_extra(ira->codegen, u8, true, src_is_volatile, PtrLenUnknown, src_align, 0, 0, false); IrInstGen *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr_mut); if (type_is_invalid(casted_dest_ptr->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_src_ptr = ir_implicit_cast(ira, src_ptr, u8_ptr_const); if (type_is_invalid(casted_src_ptr->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_count = ir_implicit_cast(ira, count_value, usize); if (type_is_invalid(casted_count->value->type)) return ira->codegen->invalid_inst_gen; // TODO test this at comptime with u8 and non-u8 types // TODO test with dest ptr being a global runtime variable if (instr_is_comptime(casted_dest_ptr) && instr_is_comptime(casted_src_ptr) && instr_is_comptime(casted_count)) { ZigValue *dest_ptr_val = ir_resolve_const(ira, casted_dest_ptr, UndefBad); if (dest_ptr_val == nullptr) return ira->codegen->invalid_inst_gen; ZigValue *src_ptr_val = ir_resolve_const(ira, casted_src_ptr, UndefBad); if (src_ptr_val == nullptr) return ira->codegen->invalid_inst_gen; ZigValue *count_val = ir_resolve_const(ira, casted_count, UndefBad); if (count_val == nullptr) return ira->codegen->invalid_inst_gen; if (dest_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { size_t count = bigint_as_usize(&count_val->data.x_bigint); ZigValue *dest_elements; size_t dest_start; size_t dest_end; switch (dest_ptr_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: case ConstPtrSpecialDiscard: zig_unreachable(); case ConstPtrSpecialRef: dest_elements = dest_ptr_val->data.x_ptr.data.ref.pointee; dest_start = 0; dest_end = 1; break; case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: { ZigValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val; expand_undef_array(ira->codegen, array_val); dest_elements = array_val->data.x_array.data.s_none.elements; dest_start = dest_ptr_val->data.x_ptr.data.base_array.elem_index; dest_end = array_val->type->data.array.len; break; } case ConstPtrSpecialBaseStruct: zig_panic("TODO memcpy on const inner struct"); case ConstPtrSpecialBaseErrorUnionCode: zig_panic("TODO memcpy on const inner error union code"); case ConstPtrSpecialBaseErrorUnionPayload: zig_panic("TODO memcpy on const inner error union payload"); case ConstPtrSpecialBaseOptionalPayload: zig_panic("TODO memcpy on const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memcpy on ptr cast from function"); case ConstPtrSpecialNull: zig_panic("TODO memcpy on null ptr"); } if (dest_start + count > dest_end) { ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds pointer access")); return ira->codegen->invalid_inst_gen; } ZigValue *src_elements; size_t src_start; size_t src_end; switch (src_ptr_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: case ConstPtrSpecialDiscard: zig_unreachable(); case ConstPtrSpecialRef: src_elements = src_ptr_val->data.x_ptr.data.ref.pointee; src_start = 0; src_end = 1; break; case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: { ZigValue *array_val = src_ptr_val->data.x_ptr.data.base_array.array_val; expand_undef_array(ira->codegen, array_val); src_elements = array_val->data.x_array.data.s_none.elements; src_start = src_ptr_val->data.x_ptr.data.base_array.elem_index; src_end = array_val->type->data.array.len; break; } case ConstPtrSpecialBaseStruct: zig_panic("TODO memcpy on const inner struct"); case ConstPtrSpecialBaseErrorUnionCode: zig_panic("TODO memcpy on const inner error union code"); case ConstPtrSpecialBaseErrorUnionPayload: zig_panic("TODO memcpy on const inner error union payload"); case ConstPtrSpecialBaseOptionalPayload: zig_panic("TODO memcpy on const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memcpy on ptr cast from function"); case ConstPtrSpecialNull: zig_panic("TODO memcpy on null ptr"); } if (src_start + count > src_end) { ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds pointer access")); return ira->codegen->invalid_inst_gen; } // TODO check for noalias violations - this should be generalized to work for any function for (size_t i = 0; i < count; i += 1) { copy_const_val(ira->codegen, &dest_elements[dest_start + i], &src_elements[src_start + i]); } return ir_const_void(ira, &instruction->base.base); } } return ir_build_memcpy_gen(ira, &instruction->base.base, casted_dest_ptr, casted_src_ptr, casted_count); } static ZigType *get_result_loc_type(IrAnalyze *ira, ResultLoc *result_loc) { if (result_loc == nullptr) return nullptr; if (result_loc->id == ResultLocIdCast) { return ir_resolve_type(ira, result_loc->source_instruction->child); } return nullptr; } static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *instruction) { Error err; IrInstGen *ptr_ptr = instruction->ptr->child; if (type_is_invalid(ptr_ptr->value->type)) return ira->codegen->invalid_inst_gen; ZigType *ptr_ptr_type = ptr_ptr->value->type; assert(ptr_ptr_type->id == ZigTypeIdPointer); ZigType *array_type = ptr_ptr_type->data.pointer.child_type; IrInstGen *start = instruction->start->child; if (type_is_invalid(start->value->type)) return ira->codegen->invalid_inst_gen; ZigType *usize = ira->codegen->builtin_types.entry_usize; IrInstGen *casted_start = ir_implicit_cast(ira, start, usize); if (type_is_invalid(casted_start->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *end; if (instruction->end) { end = instruction->end->child; if (type_is_invalid(end->value->type)) return ira->codegen->invalid_inst_gen; end = ir_implicit_cast(ira, end, usize); if (type_is_invalid(end->value->type)) return ira->codegen->invalid_inst_gen; } else { end = nullptr; } ZigValue *slice_sentinel_val = nullptr; ZigType *non_sentinel_slice_ptr_type; ZigType *elem_type; bool generate_non_null_assert = false; if (array_type->id == ZigTypeIdArray) { elem_type = array_type->data.array.child_type; non_sentinel_slice_ptr_type = get_pointer_to_type_extra(ira->codegen, elem_type, ptr_ptr_type->data.pointer.is_const, ptr_ptr_type->data.pointer.is_volatile, PtrLenUnknown, ptr_ptr_type->data.pointer.explicit_alignment, 0, 0, false); } else if (array_type->id == ZigTypeIdPointer) { if (array_type->data.pointer.ptr_len == PtrLenSingle) { ZigType *main_type = array_type->data.pointer.child_type; if (main_type->id == ZigTypeIdArray) { elem_type = main_type->data.pointer.child_type; non_sentinel_slice_ptr_type = get_pointer_to_type_extra(ira->codegen, elem_type, array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, PtrLenUnknown, array_type->data.pointer.explicit_alignment, 0, 0, false); } else { ir_add_error(ira, &instruction->base.base, buf_sprintf("slice of single-item pointer")); return ira->codegen->invalid_inst_gen; } } else { elem_type = array_type->data.pointer.child_type; if (array_type->data.pointer.ptr_len == PtrLenC) { array_type = adjust_ptr_len(ira->codegen, array_type, PtrLenUnknown); // C pointers are allowzero by default. // However, we want to be able to slice them without generating an allowzero slice (see issue #4401). // To achieve this, we generate a runtime safety check and make the slice type non-allowzero. if (array_type->data.pointer.allow_zero) { array_type = adjust_ptr_allow_zero(ira->codegen, array_type, false); generate_non_null_assert = true; } } ZigType *maybe_sentineled_slice_ptr_type = array_type; non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr); if (!end) { ir_add_error(ira, &instruction->base.base, buf_sprintf("slice of pointer must include end value")); return ira->codegen->invalid_inst_gen; } } } else if (is_slice(array_type)) { ZigType *maybe_sentineled_slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; slice_sentinel_val = maybe_sentineled_slice_ptr_type->data.pointer.sentinel; non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr); elem_type = non_sentinel_slice_ptr_type->data.pointer.child_type; } else { ir_add_error(ira, &instruction->base.base, buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name))); return ira->codegen->invalid_inst_gen; } ZigValue *sentinel_val = nullptr; if (instruction->sentinel) { IrInstGen *uncasted_sentinel = instruction->sentinel->child; if (type_is_invalid(uncasted_sentinel->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *sentinel = ir_implicit_cast(ira, uncasted_sentinel, elem_type); if (type_is_invalid(sentinel->value->type)) return ira->codegen->invalid_inst_gen; sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); if (sentinel_val == nullptr) return ira->codegen->invalid_inst_gen; } ZigType *child_array_type = (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle) ? array_type->data.pointer.child_type : array_type; ZigType *return_type; // If start index and end index are both comptime known, then the result type is a pointer to array // not a slice. However, if the start or end index is a lazy value, and the result location is a slice, // then the pointer-to-array would be casted to a slice anyway. So, we preserve the laziness of these // values by making the return type a slice. ZigType *res_loc_type = get_result_loc_type(ira, instruction->result_loc); bool result_loc_is_slice = (res_loc_type != nullptr && is_slice(res_loc_type)); bool end_is_known = !result_loc_is_slice && ((end != nullptr && value_is_comptime(end->value)) || (end == nullptr && child_array_type->id == ZigTypeIdArray)); ZigValue *array_sentinel = sentinel_val; if (end_is_known) { uint64_t end_scalar; if (end != nullptr) { ZigValue *end_val = ir_resolve_const(ira, end, UndefBad); if (!end_val) return ira->codegen->invalid_inst_gen; end_scalar = bigint_as_u64(&end_val->data.x_bigint); } else { end_scalar = child_array_type->data.array.len; } array_sentinel = (child_array_type->id == ZigTypeIdArray && end_scalar == child_array_type->data.array.len) ? child_array_type->data.array.sentinel : sentinel_val; if (value_is_comptime(casted_start->value)) { ZigValue *start_val = ir_resolve_const(ira, casted_start, UndefBad); if (!start_val) return ira->codegen->invalid_inst_gen; uint64_t start_scalar = bigint_as_u64(&start_val->data.x_bigint); if (start_scalar > end_scalar) { ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds slice")); return ira->codegen->invalid_inst_gen; } uint32_t base_ptr_align = non_sentinel_slice_ptr_type->data.pointer.explicit_alignment; uint32_t ptr_byte_alignment = 0; if (end_scalar > start_scalar) { if ((err = compute_elem_align(ira, elem_type, base_ptr_align, start_scalar, &ptr_byte_alignment))) return ira->codegen->invalid_inst_gen; } ZigType *return_array_type = get_array_type(ira->codegen, elem_type, end_scalar - start_scalar, array_sentinel); return_type = get_pointer_to_type_extra(ira->codegen, return_array_type, non_sentinel_slice_ptr_type->data.pointer.is_const, non_sentinel_slice_ptr_type->data.pointer.is_volatile, PtrLenSingle, ptr_byte_alignment, 0, 0, false); goto done_with_return_type; } } else if (array_sentinel == nullptr && end == nullptr) { array_sentinel = slice_sentinel_val; } if (array_sentinel != nullptr) { // TODO deal with non-abi-alignment here ZigType *slice_ptr_type = adjust_ptr_sentinel(ira->codegen, non_sentinel_slice_ptr_type, array_sentinel); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else { // TODO deal with non-abi-alignment here return_type = get_slice_type(ira->codegen, non_sentinel_slice_ptr_type); } done_with_return_type: if (instr_is_comptime(ptr_ptr) && value_is_comptime(casted_start->value) && (!end || value_is_comptime(end->value))) { ZigValue *array_val; ZigValue *parent_ptr; size_t abs_offset; size_t rel_end; bool ptr_is_undef = false; if (child_array_type->id == ZigTypeIdArray) { if (array_type->id == ZigTypeIdPointer) { parent_ptr = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.base.source_node); if (parent_ptr == nullptr) return ira->codegen->invalid_inst_gen; if (parent_ptr->special == ConstValSpecialUndef) { array_val = nullptr; abs_offset = 0; rel_end = SIZE_MAX; ptr_is_undef = true; } else if (parent_ptr->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { array_val = nullptr; abs_offset = 0; rel_end = SIZE_MAX; } else { array_val = const_ptr_pointee(ira, ira->codegen, parent_ptr, instruction->base.base.source_node); if (array_val == nullptr) return ira->codegen->invalid_inst_gen; rel_end = child_array_type->data.array.len; abs_offset = 0; } } else { array_val = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.base.source_node); if (array_val == nullptr) return ira->codegen->invalid_inst_gen; rel_end = array_type->data.array.len; parent_ptr = nullptr; abs_offset = 0; } } else if (array_type->id == ZigTypeIdPointer) { assert(array_type->data.pointer.ptr_len == PtrLenUnknown); parent_ptr = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.base.source_node); if (parent_ptr == nullptr) return ira->codegen->invalid_inst_gen; if (parent_ptr->special == ConstValSpecialUndef) { array_val = nullptr; abs_offset = 0; rel_end = SIZE_MAX; ptr_is_undef = true; } else switch (parent_ptr->data.x_ptr.special) { case ConstPtrSpecialInvalid: case ConstPtrSpecialDiscard: zig_unreachable(); case ConstPtrSpecialRef: if (parent_ptr->data.x_ptr.data.ref.pointee->type->id == ZigTypeIdArray) { array_val = parent_ptr->data.x_ptr.data.ref.pointee; abs_offset = 0; rel_end = array_val->type->data.array.len; } else { array_val = nullptr; abs_offset = SIZE_MAX; rel_end = 1; } break; case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: array_val = parent_ptr->data.x_ptr.data.base_array.array_val; abs_offset = parent_ptr->data.x_ptr.data.base_array.elem_index; rel_end = array_val->type->data.array.len - abs_offset; break; case ConstPtrSpecialBaseStruct: zig_panic("TODO slice const inner struct"); case ConstPtrSpecialBaseErrorUnionCode: zig_panic("TODO slice const inner error union code"); case ConstPtrSpecialBaseErrorUnionPayload: zig_panic("TODO slice const inner error union payload"); case ConstPtrSpecialBaseOptionalPayload: zig_panic("TODO slice const inner optional payload"); case ConstPtrSpecialHardCodedAddr: array_val = nullptr; abs_offset = 0; rel_end = SIZE_MAX; break; case ConstPtrSpecialFunction: zig_panic("TODO slice of ptr cast from function"); case ConstPtrSpecialNull: zig_panic("TODO slice of null ptr"); } } else if (is_slice(array_type)) { ZigValue *slice_ptr = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.base.source_node); if (slice_ptr == nullptr) return ira->codegen->invalid_inst_gen; if (slice_ptr->special == ConstValSpecialUndef) { ir_add_error(ira, &instruction->base.base, buf_sprintf("slice of undefined")); return ira->codegen->invalid_inst_gen; } parent_ptr = slice_ptr->data.x_struct.fields[slice_ptr_index]; if (parent_ptr->special == ConstValSpecialUndef) { ir_add_error(ira, &instruction->base.base, buf_sprintf("slice of undefined")); return ira->codegen->invalid_inst_gen; } ZigValue *len_val = slice_ptr->data.x_struct.fields[slice_len_index]; switch (parent_ptr->data.x_ptr.special) { case ConstPtrSpecialInvalid: case ConstPtrSpecialDiscard: zig_unreachable(); case ConstPtrSpecialRef: array_val = nullptr; abs_offset = SIZE_MAX; rel_end = 1; break; case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: array_val = parent_ptr->data.x_ptr.data.base_array.array_val; abs_offset = parent_ptr->data.x_ptr.data.base_array.elem_index; rel_end = bigint_as_usize(&len_val->data.x_bigint); break; case ConstPtrSpecialBaseStruct: zig_panic("TODO slice const inner struct"); case ConstPtrSpecialBaseErrorUnionCode: zig_panic("TODO slice const inner error union code"); case ConstPtrSpecialBaseErrorUnionPayload: zig_panic("TODO slice const inner error union payload"); case ConstPtrSpecialBaseOptionalPayload: zig_panic("TODO slice const inner optional payload"); case ConstPtrSpecialHardCodedAddr: array_val = nullptr; abs_offset = 0; rel_end = bigint_as_usize(&len_val->data.x_bigint); break; case ConstPtrSpecialFunction: zig_panic("TODO slice of slice cast from function"); case ConstPtrSpecialNull: zig_panic("TODO slice of null"); } } else { zig_unreachable(); } ZigValue *start_val = ir_resolve_const(ira, casted_start, UndefBad); if (!start_val) return ira->codegen->invalid_inst_gen; uint64_t start_scalar = bigint_as_u64(&start_val->data.x_bigint); if (!ptr_is_undef && start_scalar > rel_end) { ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds slice")); return ira->codegen->invalid_inst_gen; } uint64_t end_scalar = rel_end; if (end) { ZigValue *end_val = ir_resolve_const(ira, end, UndefBad); if (!end_val) return ira->codegen->invalid_inst_gen; end_scalar = bigint_as_u64(&end_val->data.x_bigint); } if (!ptr_is_undef) { if (end_scalar > rel_end) { ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds slice")); return ira->codegen->invalid_inst_gen; } if (start_scalar > end_scalar) { ir_add_error(ira, &instruction->base.base, buf_sprintf("slice start is greater than end")); return ira->codegen->invalid_inst_gen; } } if (ptr_is_undef && start_scalar != end_scalar) { ir_add_error(ira, &instruction->base.base, buf_sprintf("non-zero length slice of undefined pointer")); return ira->codegen->invalid_inst_gen; } // check sentinel when target is comptime-known { if (!sentinel_val) goto exit_check_sentinel; switch (ptr_ptr->value->data.x_ptr.mut) { case ConstPtrMutComptimeConst: case ConstPtrMutComptimeVar: break; case ConstPtrMutRuntimeVar: case ConstPtrMutInfer: goto exit_check_sentinel; } // prepare check parameters ZigValue *target = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.base.source_node); if (target == nullptr) return ira->codegen->invalid_inst_gen; uint64_t target_len = 0; ZigValue *target_sentinel = nullptr; ZigValue *target_elements = nullptr; for (;;) { if (target->type->id == ZigTypeIdArray) { // handle `[N]T` target_len = target->type->data.array.len; target_sentinel = target->type->data.array.sentinel; target_elements = target->data.x_array.data.s_none.elements; break; } else if (target->type->id == ZigTypeIdPointer && target->type->data.pointer.child_type->id == ZigTypeIdArray) { // handle `*[N]T` target = const_ptr_pointee(ira, ira->codegen, target, instruction->base.base.source_node); if (target == nullptr) return ira->codegen->invalid_inst_gen; assert(target->type->id == ZigTypeIdArray); continue; } else if (target->type->id == ZigTypeIdPointer) { // handle `[*]T` // handle `[*c]T` switch (target->data.x_ptr.special) { case ConstPtrSpecialInvalid: case ConstPtrSpecialDiscard: zig_unreachable(); case ConstPtrSpecialRef: target = target->data.x_ptr.data.ref.pointee; assert(target->type->id == ZigTypeIdArray); continue; case ConstPtrSpecialBaseArray: case ConstPtrSpecialSubArray: target = target->data.x_ptr.data.base_array.array_val; assert(target->type->id == ZigTypeIdArray); continue; case ConstPtrSpecialBaseStruct: zig_panic("TODO slice const inner struct"); case ConstPtrSpecialBaseErrorUnionCode: zig_panic("TODO slice const inner error union code"); case ConstPtrSpecialBaseErrorUnionPayload: zig_panic("TODO slice const inner error union payload"); case ConstPtrSpecialBaseOptionalPayload: zig_panic("TODO slice const inner optional payload"); case ConstPtrSpecialHardCodedAddr: // skip check goto exit_check_sentinel; case ConstPtrSpecialFunction: zig_panic("TODO slice of ptr cast from function"); case ConstPtrSpecialNull: zig_panic("TODO slice of null ptr"); } break; } else if (is_slice(target->type)) { // handle `[]T` target = target->data.x_struct.fields[slice_ptr_index]; assert(target->type->id == ZigTypeIdPointer); continue; } zig_unreachable(); } // perform check if (target_sentinel == nullptr) { if (end_scalar >= target_len) { ir_add_error(ira, &instruction->base.base, buf_sprintf("slice-sentinel is out of bounds")); return ira->codegen->invalid_inst_gen; } if (!const_values_equal(ira->codegen, sentinel_val, &target_elements[end_scalar])) { ir_add_error(ira, &instruction->base.base, buf_sprintf("slice-sentinel does not match memory at target index")); return ira->codegen->invalid_inst_gen; } } else { assert(end_scalar <= target_len); if (end_scalar == target_len) { if (!const_values_equal(ira->codegen, sentinel_val, target_sentinel)) { ir_add_error(ira, &instruction->base.base, buf_sprintf("slice-sentinel does not match target-sentinel")); return ira->codegen->invalid_inst_gen; } } else { if (!const_values_equal(ira->codegen, sentinel_val, &target_elements[end_scalar])) { ir_add_error(ira, &instruction->base.base, buf_sprintf("slice-sentinel does not match memory at target index")); return ira->codegen->invalid_inst_gen; } } } } exit_check_sentinel: IrInstGen *result = ir_const(ira, &instruction->base.base, return_type); ZigValue *ptr_val; if (return_type->id == ZigTypeIdPointer) { // pointer to array ptr_val = result->value; } else { // slice result->value->data.x_struct.fields = alloc_const_vals_ptrs(ira->codegen, 2); ptr_val = result->value->data.x_struct.fields[slice_ptr_index]; ZigValue *len_val = result->value->data.x_struct.fields[slice_len_index]; init_const_usize(ira->codegen, len_val, end_scalar - start_scalar); } bool return_type_is_const = non_sentinel_slice_ptr_type->data.pointer.is_const; if (array_val) { size_t index = abs_offset + start_scalar; init_const_ptr_array(ira->codegen, ptr_val, array_val, index, return_type_is_const, PtrLenUnknown); if (return_type->id == ZigTypeIdPointer) { ptr_val->data.x_ptr.special = ConstPtrSpecialSubArray; } 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, return_type_is_const); 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, return_type_is_const); break; case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: zig_unreachable(); case ConstPtrSpecialBaseStruct: zig_panic("TODO"); case ConstPtrSpecialBaseErrorUnionCode: zig_panic("TODO"); case ConstPtrSpecialBaseErrorUnionPayload: zig_panic("TODO"); case ConstPtrSpecialBaseOptionalPayload: zig_panic("TODO"); case ConstPtrSpecialHardCodedAddr: init_const_ptr_hard_coded_addr(ira->codegen, ptr_val, parent_ptr->type->data.pointer.child_type, parent_ptr->data.x_ptr.data.hard_coded_addr.addr + start_scalar, return_type_is_const); break; case ConstPtrSpecialFunction: zig_panic("TODO"); case ConstPtrSpecialNull: zig_panic("TODO"); } // In the case of pointer-to-array, we must restore this because above it overwrites ptr_val->type result->value->type = return_type; return result; } if (generate_non_null_assert) { IrInstGen *ptr_val = ir_get_deref(ira, &instruction->base.base, ptr_ptr, nullptr); if (type_is_invalid(ptr_val->value->type)) return ira->codegen->invalid_inst_gen; ir_build_assert_non_null(ira, &instruction->base.base, ptr_val); } IrInstGen *result_loc = nullptr; if (return_type->id != ZigTypeIdPointer) { result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, return_type, nullptr, true, true); if (result_loc != nullptr) { if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) { return result_loc; } ir_assert(result_loc->value->type->id == ZigTypeIdPointer, &instruction->base.base); if (result_loc->value->type->data.pointer.is_const) { ir_add_error(ira, &instruction->base.base, buf_sprintf("cannot assign to constant")); return ira->codegen->invalid_inst_gen; } IrInstGen *dummy_value = ir_const(ira, &instruction->base.base, return_type); dummy_value->value->special = ConstValSpecialRuntime; IrInstGen *dummy_result = ir_implicit_cast2(ira, &instruction->base.base, dummy_value, result_loc->value->type->data.pointer.child_type); if (type_is_invalid(dummy_result->value->type)) return ira->codegen->invalid_inst_gen; } } return ir_build_slice_gen(ira, &instruction->base.base, return_type, ptr_ptr, casted_start, end, instruction->safety_check_on, result_loc, sentinel_val); } static IrInstGen *ir_analyze_instruction_has_field(IrAnalyze *ira, IrInstSrcHasField *instruction) { Error err; ZigType *container_type = ir_resolve_type(ira, instruction->container_type->child); if (type_is_invalid(container_type)) return ira->codegen->invalid_inst_gen; if ((err = type_resolve(ira->codegen, container_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_inst_gen; Buf *field_name = ir_resolve_str(ira, instruction->field_name->child); if (field_name == nullptr) return ira->codegen->invalid_inst_gen; bool result; if (container_type->id == ZigTypeIdStruct) { result = find_struct_type_field(container_type, field_name) != nullptr; } else if (container_type->id == ZigTypeIdEnum) { result = find_enum_type_field(container_type, field_name) != nullptr; } else if (container_type->id == ZigTypeIdUnion) { result = find_union_type_field(container_type, field_name) != nullptr; } else { ir_add_error(ira, &instruction->container_type->base, buf_sprintf("type '%s' does not support @hasField", buf_ptr(&container_type->name))); return ira->codegen->invalid_inst_gen; } return ir_const_bool(ira, &instruction->base.base, result); } static IrInstGen *ir_analyze_instruction_wasm_memory_size(IrAnalyze *ira, IrInstSrcWasmMemorySize *instruction) { // TODO generate compile error for target_arch different than 32bit if (!target_is_wasm(ira->codegen->zig_target)) { ir_add_error_node(ira, instruction->base.base.source_node, buf_sprintf("@wasmMemorySize is a wasm32 feature only")); return ira->codegen->invalid_inst_gen; } return ir_build_wasm_memory_size_gen(ira, &instruction->base.base); } static IrInstGen *ir_analyze_instruction_wasm_memory_grow(IrAnalyze *ira, IrInstSrcWasmMemoryGrow *instruction) { // TODO generate compile error for target_arch different than 32bit if (!target_is_wasm(ira->codegen->zig_target)) { ir_add_error_node(ira, instruction->base.base.source_node, buf_sprintf("@wasmMemoryGrow is a wasm32 feature only")); return ira->codegen->invalid_inst_gen; } IrInstGen *delta = instruction->delta->child; if (type_is_invalid(delta->value->type)) return ira->codegen->invalid_inst_gen; ZigType *i32_type = ira->codegen->builtin_types.entry_i32; IrInstGen *casted_delta = ir_implicit_cast(ira, delta, i32_type); if (type_is_invalid(casted_delta->value->type)) return ira->codegen->invalid_inst_gen; return ir_build_wasm_memory_grow_gen(ira, &instruction->base.base, casted_delta); } static IrInstGen *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstSrcBreakpoint *instruction) { return ir_build_breakpoint_gen(ira, &instruction->base.base); } static IrInstGen *ir_analyze_instruction_return_address(IrAnalyze *ira, IrInstSrcReturnAddress *instruction) { return ir_build_return_address_gen(ira, &instruction->base.base); } static IrInstGen *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrInstSrcFrameAddress *instruction) { return ir_build_frame_address_gen(ira, &instruction->base.base); } static IrInstGen *ir_analyze_instruction_frame_handle(IrAnalyze *ira, IrInstSrcFrameHandle *instruction) { ZigFn *fn = ira->new_irb.exec->fn_entry; ir_assert(fn != nullptr, &instruction->base.base); if (fn->inferred_async_node == nullptr) { fn->inferred_async_node = instruction->base.base.source_node; } ZigType *frame_type = get_fn_frame_type(ira->codegen, fn); ZigType *ptr_frame_type = get_pointer_to_type(ira->codegen, frame_type, false); return ir_build_handle_gen(ira, &instruction->base.base, ptr_frame_type); } static IrInstGen *ir_analyze_instruction_frame_type(IrAnalyze *ira, IrInstSrcFrameType *instruction) { ZigFn *fn = ir_resolve_fn(ira, instruction->fn->child); if (fn == nullptr) return ira->codegen->invalid_inst_gen; if (fn->type_entry->data.fn.is_generic) { ir_add_error(ira, &instruction->base.base, buf_sprintf("@Frame() of generic function")); return ira->codegen->invalid_inst_gen; } ZigType *ty = get_fn_frame_type(ira->codegen, fn); return ir_const_type(ira, &instruction->base.base, ty); } static IrInstGen *ir_analyze_instruction_frame_size(IrAnalyze *ira, IrInstSrcFrameSize *instruction) { IrInstGen *fn = instruction->fn->child; if (type_is_invalid(fn->value->type)) return ira->codegen->invalid_inst_gen; if (fn->value->type->id != ZigTypeIdFn) { ir_add_error(ira, &fn->base, buf_sprintf("expected function, found '%s'", buf_ptr(&fn->value->type->name))); return ira->codegen->invalid_inst_gen; } ira->codegen->need_frame_size_prefix_data = true; return ir_build_frame_size_gen(ira, &instruction->base.base, fn); } static IrInstGen *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstSrcAlignOf *instruction) { // Here we create a lazy value in order to avoid resolving the alignment of the type // immediately. This avoids false positive dependency loops such as: // const Node = struct { // field: []align(@alignOf(Node)) Node, // }; IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_num_lit_int); result->value->special = ConstValSpecialLazy; LazyValueAlignOf *lazy_align_of = heap::c_allocator.create(); lazy_align_of->ira = ira; ira_ref(ira); result->value->data.x_lazy = &lazy_align_of->base; lazy_align_of->base.id = LazyValueIdAlignOf; lazy_align_of->target_type = instruction->type_value->child; if (ir_resolve_type_lazy(ira, lazy_align_of->target_type) == nullptr) return ira->codegen->invalid_inst_gen; return result; } static IrInstGen *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstSrcOverflowOp *instruction) { Error err; IrInstGen *type_value = instruction->type_value->child; if (type_is_invalid(type_value->value->type)) return ira->codegen->invalid_inst_gen; ZigType *dest_type = ir_resolve_type(ira, type_value); if (type_is_invalid(dest_type)) return ira->codegen->invalid_inst_gen; if (dest_type->id != ZigTypeIdInt) { ir_add_error(ira, &type_value->base, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *op1 = instruction->op1->child; if (type_is_invalid(op1->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, dest_type); if (type_is_invalid(casted_op1->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *op2 = instruction->op2->child; if (type_is_invalid(op2->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_op2; if (instruction->op == IrOverflowOpShl) { ZigType *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen, dest_type->data.integral.bit_count - 1); casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type); } else { casted_op2 = ir_implicit_cast(ira, op2, dest_type); } if (type_is_invalid(casted_op2->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *result_ptr = instruction->result_ptr->child; if (type_is_invalid(result_ptr->value->type)) return ira->codegen->invalid_inst_gen; ZigType *expected_ptr_type; if (result_ptr->value->type->id == ZigTypeIdPointer) { uint32_t alignment; if ((err = resolve_ptr_align(ira, result_ptr->value->type, &alignment))) return ira->codegen->invalid_inst_gen; expected_ptr_type = get_pointer_to_type_extra(ira->codegen, dest_type, false, result_ptr->value->type->data.pointer.is_volatile, PtrLenSingle, alignment, 0, 0, false); } else { expected_ptr_type = get_pointer_to_type(ira->codegen, dest_type, false); } IrInstGen *casted_result_ptr = ir_implicit_cast(ira, result_ptr, expected_ptr_type); if (type_is_invalid(casted_result_ptr->value->type)) return ira->codegen->invalid_inst_gen; if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2) && instr_is_comptime(casted_result_ptr)) { ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); if (op1_val == nullptr) return ira->codegen->invalid_inst_gen; ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->invalid_inst_gen; ZigValue *result_val = ir_resolve_const(ira, casted_result_ptr, UndefBad); if (result_val == nullptr) return ira->codegen->invalid_inst_gen; BigInt *op1_bigint = &op1_val->data.x_bigint; BigInt *op2_bigint = &op2_val->data.x_bigint; ZigValue *pointee_val = const_ptr_pointee(ira, ira->codegen, result_val, casted_result_ptr->base.source_node); if (pointee_val == nullptr) return ira->codegen->invalid_inst_gen; BigInt *dest_bigint = &pointee_val->data.x_bigint; switch (instruction->op) { case IrOverflowOpAdd: bigint_add(dest_bigint, op1_bigint, op2_bigint); break; case IrOverflowOpSub: bigint_sub(dest_bigint, op1_bigint, op2_bigint); break; case IrOverflowOpMul: bigint_mul(dest_bigint, op1_bigint, op2_bigint); break; case IrOverflowOpShl: bigint_shl(dest_bigint, op1_bigint, op2_bigint); break; } bool result_bool = false; if (!bigint_fits_in_bits(dest_bigint, dest_type->data.integral.bit_count, dest_type->data.integral.is_signed)) { result_bool = true; BigInt tmp_bigint; bigint_init_bigint(&tmp_bigint, dest_bigint); bigint_truncate(dest_bigint, &tmp_bigint, dest_type->data.integral.bit_count, dest_type->data.integral.is_signed); } pointee_val->special = ConstValSpecialStatic; return ir_const_bool(ira, &instruction->base.base, result_bool); } return ir_build_overflow_op_gen(ira, &instruction->base.base, instruction->op, casted_op1, casted_op2, casted_result_ptr, dest_type); } static void ir_eval_mul_add(IrAnalyze *ira, IrInstSrcMulAdd *source_instr, ZigType *float_type, ZigValue *op1, ZigValue *op2, ZigValue *op3, ZigValue *out_val) { if (float_type->id == ZigTypeIdComptimeFloat) { f128M_mulAdd(&out_val->data.x_bigfloat.value, &op1->data.x_bigfloat.value, &op2->data.x_bigfloat.value, &op3->data.x_bigfloat.value); } else if (float_type->id == ZigTypeIdFloat) { switch (float_type->data.floating.bit_count) { case 16: out_val->data.x_f16 = f16_mulAdd(op1->data.x_f16, op2->data.x_f16, op3->data.x_f16); break; case 32: out_val->data.x_f32 = fmaf(op1->data.x_f32, op2->data.x_f32, op3->data.x_f32); break; case 64: out_val->data.x_f64 = fma(op1->data.x_f64, op2->data.x_f64, op3->data.x_f64); break; case 128: f128M_mulAdd(&op1->data.x_f128, &op2->data.x_f128, &op3->data.x_f128, &out_val->data.x_f128); break; default: zig_unreachable(); } } else { zig_unreachable(); } } static IrInstGen *ir_analyze_instruction_mul_add(IrAnalyze *ira, IrInstSrcMulAdd *instruction) { IrInstGen *type_value = instruction->type_value->child; if (type_is_invalid(type_value->value->type)) return ira->codegen->invalid_inst_gen; ZigType *expr_type = ir_resolve_type(ira, type_value); if (type_is_invalid(expr_type)) return ira->codegen->invalid_inst_gen; // Only allow float types, and vectors of floats. ZigType *float_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type; if (float_type->id != ZigTypeIdFloat) { ir_add_error(ira, &type_value->base, buf_sprintf("expected float or vector of float type, found '%s'", buf_ptr(&float_type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *op1 = instruction->op1->child; if (type_is_invalid(op1->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, expr_type); if (type_is_invalid(casted_op1->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *op2 = instruction->op2->child; if (type_is_invalid(op2->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, expr_type); if (type_is_invalid(casted_op2->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *op3 = instruction->op3->child; if (type_is_invalid(op3->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_op3 = ir_implicit_cast(ira, op3, expr_type); if (type_is_invalid(casted_op3->value->type)) return ira->codegen->invalid_inst_gen; if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2) && instr_is_comptime(casted_op3)) { ZigValue *op1_const = ir_resolve_const(ira, casted_op1, UndefBad); if (!op1_const) return ira->codegen->invalid_inst_gen; ZigValue *op2_const = ir_resolve_const(ira, casted_op2, UndefBad); if (!op2_const) return ira->codegen->invalid_inst_gen; ZigValue *op3_const = ir_resolve_const(ira, casted_op3, UndefBad); if (!op3_const) return ira->codegen->invalid_inst_gen; IrInstGen *result = ir_const(ira, &instruction->base.base, expr_type); ZigValue *out_val = result->value; if (expr_type->id == ZigTypeIdVector) { expand_undef_array(ira->codegen, op1_const); expand_undef_array(ira->codegen, op2_const); expand_undef_array(ira->codegen, op3_const); out_val->special = ConstValSpecialUndef; expand_undef_array(ira->codegen, out_val); size_t len = expr_type->data.vector.len; for (size_t i = 0; i < len; i += 1) { ZigValue *float_operand_op1 = &op1_const->data.x_array.data.s_none.elements[i]; ZigValue *float_operand_op2 = &op2_const->data.x_array.data.s_none.elements[i]; ZigValue *float_operand_op3 = &op3_const->data.x_array.data.s_none.elements[i]; ZigValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i]; assert(float_operand_op1->type == float_type); assert(float_operand_op2->type == float_type); assert(float_operand_op3->type == float_type); assert(float_out_val->type == float_type); ir_eval_mul_add(ira, instruction, float_type, op1_const, op2_const, op3_const, float_out_val); float_out_val->type = float_type; } out_val->type = expr_type; out_val->special = ConstValSpecialStatic; } else { ir_eval_mul_add(ira, instruction, float_type, op1_const, op2_const, op3_const, out_val); } return result; } return ir_build_mul_add_gen(ira, &instruction->base.base, casted_op1, casted_op2, casted_op3, expr_type); } static IrInstGen *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstSrcTestErr *instruction) { IrInstGen *base_ptr = instruction->base_ptr->child; if (type_is_invalid(base_ptr->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *value; if (instruction->base_ptr_is_payload) { value = base_ptr; } else { value = ir_get_deref(ira, &instruction->base.base, base_ptr, nullptr); } ZigType *type_entry = value->value->type; if (type_is_invalid(type_entry)) return ira->codegen->invalid_inst_gen; if (type_entry->id == ZigTypeIdErrorUnion) { if (instr_is_comptime(value)) { ZigValue *err_union_val = ir_resolve_const(ira, value, UndefBad); if (!err_union_val) return ira->codegen->invalid_inst_gen; if (err_union_val->special != ConstValSpecialRuntime) { ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; return ir_const_bool(ira, &instruction->base.base, (err != nullptr)); } } if (instruction->resolve_err_set) { ZigType *err_set_type = type_entry->data.error_union.err_set_type; if (!resolve_inferred_error_set(ira->codegen, err_set_type, instruction->base.base.source_node)) { return ira->codegen->invalid_inst_gen; } if (!type_is_global_error_set(err_set_type) && err_set_type->data.error_set.err_count == 0) { assert(!err_set_type->data.error_set.incomplete); return ir_const_bool(ira, &instruction->base.base, false); } } return ir_build_test_err_gen(ira, &instruction->base.base, value); } else if (type_entry->id == ZigTypeIdErrorSet) { return ir_const_bool(ira, &instruction->base.base, true); } else { return ir_const_bool(ira, &instruction->base.base, false); } } static IrInstGen *ir_analyze_unwrap_err_code(IrAnalyze *ira, IrInst* source_instr, IrInstGen *base_ptr, bool initializing) { ZigType *ptr_type = base_ptr->value->type; // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing. assert(ptr_type->id == ZigTypeIdPointer); ZigType *type_entry = ptr_type->data.pointer.child_type; if (type_is_invalid(type_entry)) return ira->codegen->invalid_inst_gen; if (type_entry->id != ZigTypeIdErrorUnion) { ir_add_error(ira, &base_ptr->base, buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_inst_gen; } ZigType *err_set_type = type_entry->data.error_union.err_set_type; ZigType *result_type = get_pointer_to_type_extra(ira->codegen, err_set_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, ptr_type->data.pointer.explicit_alignment, 0, 0, false); if (instr_is_comptime(base_ptr)) { ZigValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); if (!ptr_val) return ira->codegen->invalid_inst_gen; if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar && ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { ZigValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); if (err_union_val == nullptr) return ira->codegen->invalid_inst_gen; if (initializing && err_union_val->special == ConstValSpecialUndef) { ZigValue *vals = ira->codegen->pass1_arena->allocate(2); ZigValue *err_set_val = &vals[0]; ZigValue *payload_val = &vals[1]; err_set_val->special = ConstValSpecialUndef; err_set_val->type = err_set_type; err_set_val->parent.id = ConstParentIdErrUnionCode; err_set_val->parent.data.p_err_union_code.err_union_val = err_union_val; payload_val->special = ConstValSpecialUndef; payload_val->type = type_entry->data.error_union.payload_type; payload_val->parent.id = ConstParentIdErrUnionPayload; payload_val->parent.data.p_err_union_payload.err_union_val = err_union_val; err_union_val->special = ConstValSpecialStatic; err_union_val->data.x_err_union.error_set = err_set_val; err_union_val->data.x_err_union.payload = payload_val; } ir_assert(err_union_val->special != ConstValSpecialRuntime, source_instr); IrInstGen *result; if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { result = ir_build_unwrap_err_code_gen(ira, source_instr->scope, source_instr->source_node, base_ptr, result_type); result->value->special = ConstValSpecialStatic; } else { result = ir_const(ira, source_instr, result_type); } ZigValue *const_val = result->value; const_val->data.x_ptr.special = ConstPtrSpecialBaseErrorUnionCode; const_val->data.x_ptr.data.base_err_union_code.err_union_val = err_union_val; const_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; return result; } } return ir_build_unwrap_err_code_gen(ira, source_instr->scope, source_instr->source_node, base_ptr, result_type); } static IrInstGen *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, IrInstSrcUnwrapErrCode *instruction) { IrInstGen *base_ptr = instruction->err_union_ptr->child; if (type_is_invalid(base_ptr->value->type)) return ira->codegen->invalid_inst_gen; return ir_analyze_unwrap_err_code(ira, &instruction->base.base, base_ptr, false); } static IrInstGen *ir_analyze_unwrap_error_payload(IrAnalyze *ira, IrInst* source_instr, IrInstGen *base_ptr, bool safety_check_on, bool initializing) { ZigType *ptr_type = base_ptr->value->type; // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing. assert(ptr_type->id == ZigTypeIdPointer); ZigType *type_entry = ptr_type->data.pointer.child_type; if (type_is_invalid(type_entry)) return ira->codegen->invalid_inst_gen; if (type_entry->id != ZigTypeIdErrorUnion) { ir_add_error(ira, &base_ptr->base, buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_inst_gen; } ZigType *payload_type = type_entry->data.error_union.payload_type; if (type_is_invalid(payload_type)) return ira->codegen->invalid_inst_gen; ZigType *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0, false); if (instr_is_comptime(base_ptr)) { ZigValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); if (!ptr_val) return ira->codegen->invalid_inst_gen; if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { ZigValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); if (err_union_val == nullptr) return ira->codegen->invalid_inst_gen; if (initializing && err_union_val->special == ConstValSpecialUndef) { ZigValue *vals = ira->codegen->pass1_arena->allocate(2); ZigValue *err_set_val = &vals[0]; ZigValue *payload_val = &vals[1]; err_set_val->special = ConstValSpecialStatic; err_set_val->type = type_entry->data.error_union.err_set_type; err_set_val->data.x_err_set = nullptr; payload_val->special = ConstValSpecialUndef; payload_val->type = payload_type; err_union_val->special = ConstValSpecialStatic; err_union_val->data.x_err_union.error_set = err_set_val; err_union_val->data.x_err_union.payload = payload_val; } if (err_union_val->special != ConstValSpecialRuntime) { ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; if (err != nullptr) { ir_add_error(ira, source_instr, buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *result; if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { result = ir_build_unwrap_err_payload_gen(ira, source_instr->scope, source_instr->source_node, base_ptr, safety_check_on, initializing, result_type); result->value->special = ConstValSpecialStatic; } else { result = ir_const(ira, source_instr, result_type); } result->value->data.x_ptr.special = ConstPtrSpecialRef; result->value->data.x_ptr.data.ref.pointee = err_union_val->data.x_err_union.payload; result->value->data.x_ptr.mut = ptr_val->data.x_ptr.mut; return result; } } } return ir_build_unwrap_err_payload_gen(ira, source_instr->scope, source_instr->source_node, base_ptr, safety_check_on, initializing, result_type); } static IrInstGen *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, IrInstSrcUnwrapErrPayload *instruction) { assert(instruction->value->child); IrInstGen *value = instruction->value->child; if (type_is_invalid(value->value->type)) return ira->codegen->invalid_inst_gen; return ir_analyze_unwrap_error_payload(ira, &instruction->base.base, value, instruction->safety_check_on, false); } static IrInstGen *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstSrcFnProto *instruction) { AstNode *proto_node = instruction->base.base.source_node; assert(proto_node->type == NodeTypeFnProto); IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type); result->value->special = ConstValSpecialLazy; LazyValueFnType *lazy_fn_type = heap::c_allocator.create(); lazy_fn_type->ira = ira; ira_ref(ira); result->value->data.x_lazy = &lazy_fn_type->base; lazy_fn_type->base.id = LazyValueIdFnType; if (proto_node->data.fn_proto.auto_err_set) { ir_add_error(ira, &instruction->base.base, buf_sprintf("inferring error set of return type valid only for function definitions")); return ira->codegen->invalid_inst_gen; } lazy_fn_type->cc = cc_from_fn_proto(&proto_node->data.fn_proto); if (instruction->callconv_value != nullptr) { ZigType *cc_enum_type = get_builtin_type(ira->codegen, "CallingConvention"); IrInstGen *casted_value = ir_implicit_cast(ira, instruction->callconv_value->child, cc_enum_type); if (type_is_invalid(casted_value->value->type)) return ira->codegen->invalid_inst_gen; ZigValue *const_value = ir_resolve_const(ira, casted_value, UndefBad); if (const_value == nullptr) return ira->codegen->invalid_inst_gen; lazy_fn_type->cc = (CallingConvention)bigint_as_u32(&const_value->data.x_enum_tag); } size_t param_count = proto_node->data.fn_proto.params.length; lazy_fn_type->proto_node = proto_node; lazy_fn_type->param_types = heap::c_allocator.allocate(param_count); for (size_t param_index = 0; param_index < param_count; param_index += 1) { AstNode *param_node = proto_node->data.fn_proto.params.at(param_index); assert(param_node->type == NodeTypeParamDecl); bool param_is_var_args = param_node->data.param_decl.is_var_args; if (param_is_var_args) { const CallingConvention cc = lazy_fn_type->cc; if (cc == CallingConventionC) { break; } else { ir_add_error(ira, &instruction->base.base, buf_sprintf("var args only allowed in functions with C calling convention")); return ira->codegen->invalid_inst_gen; } } if (instruction->param_types[param_index] == nullptr) { lazy_fn_type->is_generic = true; return result; } IrInstGen *param_type_value = instruction->param_types[param_index]->child; if (type_is_invalid(param_type_value->value->type)) return ira->codegen->invalid_inst_gen; if (ir_resolve_const(ira, param_type_value, LazyOk) == nullptr) return ira->codegen->invalid_inst_gen; lazy_fn_type->param_types[param_index] = param_type_value; } if (instruction->align_value != nullptr) { lazy_fn_type->align_inst = instruction->align_value->child; if (ir_resolve_const(ira, lazy_fn_type->align_inst, LazyOk) == nullptr) return ira->codegen->invalid_inst_gen; } lazy_fn_type->return_type = instruction->return_type->child; if (ir_resolve_const(ira, lazy_fn_type->return_type, LazyOk) == nullptr) return ira->codegen->invalid_inst_gen; return result; } static IrInstGen *ir_analyze_instruction_test_comptime(IrAnalyze *ira, IrInstSrcTestComptime *instruction) { IrInstGen *value = instruction->value->child; if (type_is_invalid(value->value->type)) return ira->codegen->invalid_inst_gen; return ir_const_bool(ira, &instruction->base.base, instr_is_comptime(value)); } static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, IrInstSrcCheckSwitchProngs *instruction) { IrInstGen *target_value = instruction->target_value->child; ZigType *switch_type = target_value->value->type; if (type_is_invalid(switch_type)) return ira->codegen->invalid_inst_gen; 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) { IrInstSrcCheckSwitchProngsRange *range = &instruction->ranges[range_i]; IrInstGen *start_value_uncasted = range->start->child; if (type_is_invalid(start_value_uncasted->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *start_value = ir_implicit_cast(ira, start_value_uncasted, switch_type); if (type_is_invalid(start_value->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *end_value_uncasted = range->end->child; if (type_is_invalid(end_value_uncasted->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *end_value = ir_implicit_cast(ira, end_value_uncasted, switch_type); if (type_is_invalid(end_value->value->type)) return ira->codegen->invalid_inst_gen; assert(start_value->value->type->id == ZigTypeIdEnum); BigInt start_index; bigint_init_bigint(&start_index, &start_value->value->data.x_enum_tag); assert(end_value->value->type->id == ZigTypeIdEnum); BigInt end_index; bigint_init_bigint(&end_index, &end_value->value->data.x_enum_tag); if (bigint_cmp(&start_index, &end_index) == CmpGT) { ir_add_error(ira, &start_value->base, buf_sprintf("range start value is greater than the end value")); } BigInt field_index; bigint_init_bigint(&field_index, &start_index); for (;;) { Cmp cmp = bigint_cmp(&field_index, &end_index); if (cmp == CmpGT) { break; } auto entry = field_prev_uses.put_unique(field_index, start_value->base.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->base, 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_underscore_prong) { if (!switch_type->data.enumeration.non_exhaustive){ ir_add_error(ira, &instruction->base.base, buf_sprintf("switch on non-exhaustive enum has `_` 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]; if (buf_eql_str(enum_field->name, "_")) continue; auto entry = field_prev_uses.maybe_get(enum_field->value); if (!entry) { ir_add_error(ira, &instruction->base.base, buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&switch_type->name), buf_ptr(enum_field->name))); } } } else if (!instruction->have_else_prong) { if (switch_type->data.enumeration.non_exhaustive) { ir_add_error(ira, &instruction->base.base, buf_sprintf("switch on non-exhaustive enum must include `else` or `_` 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.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->base.source_node)) { return ira->codegen->invalid_inst_gen; } size_t field_prev_uses_count = ira->codegen->errors_by_index.length; AstNode **field_prev_uses = heap::c_allocator.allocate(field_prev_uses_count); for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { IrInstSrcCheckSwitchProngsRange *range = &instruction->ranges[range_i]; IrInstGen *start_value_uncasted = range->start->child; if (type_is_invalid(start_value_uncasted->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *start_value = ir_implicit_cast(ira, start_value_uncasted, switch_type); if (type_is_invalid(start_value->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *end_value_uncasted = range->end->child; if (type_is_invalid(end_value_uncasted->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *end_value = ir_implicit_cast(ira, end_value_uncasted, switch_type); if (type_is_invalid(end_value->value->type)) return ira->codegen->invalid_inst_gen; ir_assert(start_value->value->type->id == ZigTypeIdErrorSet, &instruction->base.base); uint32_t start_index = start_value->value->data.x_err_set->value; ir_assert(end_value->value->type->id == ZigTypeIdErrorSet, &instruction->base.base); uint32_t end_index = end_value->value->data.x_err_set->value; if (start_index != end_index) { ir_add_error(ira, &end_value->base, buf_sprintf("ranges not allowed when switching on errors")); return ira->codegen->invalid_inst_gen; } 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->base, 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->base.source_node; } if (!instruction->have_else_prong) { if (type_is_global_error_set(switch_type)) { ir_add_error(ira, &instruction->base.base, buf_sprintf("else prong required when switching on type 'anyerror'")); return ira->codegen->invalid_inst_gen; } 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.base, buf_sprintf("error.%s not handled in switch", buf_ptr(&err_entry->name))); } } } } heap::c_allocator.deallocate(field_prev_uses, field_prev_uses_count); } else if (switch_type->id == ZigTypeIdInt) { RangeSet rs = {0}; for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { IrInstSrcCheckSwitchProngsRange *range = &instruction->ranges[range_i]; IrInstGen *start_value = range->start->child; if (type_is_invalid(start_value->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_start_value = ir_implicit_cast(ira, start_value, switch_type); if (type_is_invalid(casted_start_value->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *end_value = range->end->child; if (type_is_invalid(end_value->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_end_value = ir_implicit_cast(ira, end_value, switch_type); if (type_is_invalid(casted_end_value->value->type)) return ira->codegen->invalid_inst_gen; ZigValue *start_val = ir_resolve_const(ira, casted_start_value, UndefBad); if (!start_val) return ira->codegen->invalid_inst_gen; ZigValue *end_val = ir_resolve_const(ira, casted_end_value, UndefBad); if (!end_val) return ira->codegen->invalid_inst_gen; assert(start_val->type->id == ZigTypeIdInt || start_val->type->id == ZigTypeIdComptimeInt); assert(end_val->type->id == ZigTypeIdInt || end_val->type->id == ZigTypeIdComptimeInt); if (bigint_cmp(&start_val->data.x_bigint, &end_val->data.x_bigint) == CmpGT) { ir_add_error(ira, &start_value->base, buf_sprintf("range start value is greater than the end value")); } AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bigint, &end_val->data.x_bigint, start_value->base.source_node); if (prev_node != nullptr) { ErrorMsg *msg = ir_add_error(ira, &start_value->base, buf_sprintf("duplicate switch value")); add_error_note(ira->codegen, msg, prev_node, buf_sprintf("previous value is here")); return ira->codegen->invalid_inst_gen; } } 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.base, buf_sprintf("switch must handle all possibilities")); return ira->codegen->invalid_inst_gen; } } } else if (switch_type->id == ZigTypeIdBool) { int seenTrue = 0; int seenFalse = 0; for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { IrInstSrcCheckSwitchProngsRange *range = &instruction->ranges[range_i]; IrInstGen *value = range->start->child; IrInstGen *casted_value = ir_implicit_cast(ira, value, switch_type); if (type_is_invalid(casted_value->value->type)) return ira->codegen->invalid_inst_gen; ZigValue *const_expr_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_expr_val) return ira->codegen->invalid_inst_gen; assert(const_expr_val->type->id == ZigTypeIdBool); if (const_expr_val->data.x_bool == true) { seenTrue += 1; } else { seenFalse += 1; } if ((seenTrue > 1) || (seenFalse > 1)) { ir_add_error(ira, &value->base, buf_sprintf("duplicate switch value")); return ira->codegen->invalid_inst_gen; } } if (((seenTrue < 1) || (seenFalse < 1)) && !instruction->have_else_prong) { ir_add_error(ira, &instruction->base.base, buf_sprintf("switch must handle all possibilities")); return ira->codegen->invalid_inst_gen; } } else if (!instruction->have_else_prong) { ir_add_error(ira, &instruction->base.base, buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name))); return ira->codegen->invalid_inst_gen; } return ir_const_void(ira, &instruction->base.base); } static IrInstGen *ir_analyze_instruction_check_statement_is_void(IrAnalyze *ira, IrInstSrcCheckStatementIsVoid *instruction) { IrInstGen *statement_value = instruction->statement_value->child; ZigType *statement_type = statement_value->value->type; if (type_is_invalid(statement_type)) return ira->codegen->invalid_inst_gen; if (statement_type->id != ZigTypeIdVoid && statement_type->id != ZigTypeIdUnreachable) { ir_add_error(ira, &instruction->base.base, buf_sprintf("expression value is ignored")); } return ir_const_void(ira, &instruction->base.base); } static IrInstGen *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstSrcPanic *instruction) { IrInstGen *msg = instruction->msg->child; if (type_is_invalid(msg->value->type)) return ir_unreach_error(ira); if (ir_should_inline(ira->old_irb.exec, instruction->base.base.scope)) { ir_add_error(ira, &instruction->base.base, buf_sprintf("encountered @panic at compile-time")); return ir_unreach_error(ira); } ZigType *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, true, false, PtrLenUnknown, 0, 0, 0, false); ZigType *str_type = get_slice_type(ira->codegen, u8_ptr_type); IrInstGen *casted_msg = ir_implicit_cast(ira, msg, str_type); if (type_is_invalid(casted_msg->value->type)) return ir_unreach_error(ira); IrInstGen *new_instruction = ir_build_panic_gen(ira, &instruction->base.base, casted_msg); return ir_finish_anal(ira, new_instruction); } static IrInstGen *ir_align_cast(IrAnalyze *ira, IrInstGen *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_inst_gen; } 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 == ZigTypeIdAnyFrame) { if (align_bytes >= target_fn_align(ira->codegen->zig_target)) { result_type = target_type; } else { ir_add_error(ira, &target->base, buf_sprintf("sub-aligned anyframe not allowed")); return ira->codegen->invalid_inst_gen; } } 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_inst_gen; 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_inst_gen; 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->base, buf_sprintf("expected pointer or slice, found '%s'", buf_ptr(&target_type->name))); return ira->codegen->invalid_inst_gen; } if (instr_is_comptime(target)) { ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_inst_gen; if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && val->data.x_ptr.data.hard_coded_addr.addr % align_bytes != 0) { ir_add_error(ira, &target->base, 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_inst_gen; } IrInstGen *result = ir_const(ira, &target->base, result_type); copy_const_val(ira->codegen, result->value, val); result->value->type = result_type; return result; } if (safety_check_on && align_bytes > old_align_bytes && align_bytes != 1) { return ir_build_align_cast_gen(ira, target->base.scope, target->base.source_node, target, result_type); } else { return ir_build_cast(ira, &target->base, result_type, target, CastOpNoop); } } static IrInstGen *ir_analyze_ptr_cast(IrAnalyze *ira, IrInst* source_instr, IrInstGen *ptr, IrInst *ptr_src, ZigType *dest_type, IrInst *dest_type_src, bool safety_check_on, bool keep_bigger_alignment) { Error err; ZigType *src_type = ptr->value->type; assert(!type_is_invalid(src_type)); if (src_type == dest_type) { return ptr; } // We have a check for zero bits later so we use get_src_ptr_type to // validate src_type and dest_type. ZigType *if_slice_ptr_type; if (is_slice(src_type)) { TypeStructField *ptr_field = src_type->data.structure.fields[slice_ptr_index]; if_slice_ptr_type = resolve_struct_field_type(ira->codegen, ptr_field); } else { if_slice_ptr_type = src_type; ZigType *src_ptr_type = get_src_ptr_type(src_type); if (src_ptr_type == nullptr) { ir_add_error(ira, ptr_src, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name))); return ira->codegen->invalid_inst_gen; } } ZigType *dest_ptr_type = get_src_ptr_type(dest_type); if (dest_ptr_type == nullptr) { ir_add_error(ira, dest_type_src, buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_inst_gen; } if (get_ptr_const(ira->codegen, src_type) && !get_ptr_const(ira->codegen, dest_type)) { ir_add_error(ira, source_instr, buf_sprintf("cast discards const qualifier")); return ira->codegen->invalid_inst_gen; } uint32_t dest_align_bytes; if ((err = resolve_ptr_align(ira, dest_type, &dest_align_bytes))) return ira->codegen->invalid_inst_gen; uint32_t src_align_bytes = 0; if (keep_bigger_alignment || dest_align_bytes != 1) { if ((err = resolve_ptr_align(ira, src_type, &src_align_bytes))) return ira->codegen->invalid_inst_gen; } if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_inst_gen; if ((err = type_resolve(ira->codegen, src_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_inst_gen; if (safety_check_on && type_has_bits(ira->codegen, dest_type) && !type_has_bits(ira->codegen, if_slice_ptr_type)) { ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("'%s' and '%s' do not have the same in-memory representation", buf_ptr(&src_type->name), buf_ptr(&dest_type->name))); add_error_note(ira->codegen, msg, ptr_src->source_node, buf_sprintf("'%s' has no in-memory bits", buf_ptr(&src_type->name))); add_error_note(ira->codegen, msg, dest_type_src->source_node, buf_sprintf("'%s' has in-memory bits", buf_ptr(&dest_type->name))); return ira->codegen->invalid_inst_gen; } // For slices, follow the `ptr` field. if (is_slice(src_type)) { TypeStructField *ptr_field = src_type->data.structure.fields[slice_ptr_index]; IrInstGen *ptr_ref = ir_get_ref(ira, source_instr, ptr, true, false); IrInstGen *ptr_ptr = ir_analyze_struct_field_ptr(ira, source_instr, ptr_field, ptr_ref, src_type, false); ptr = ir_get_deref(ira, source_instr, ptr_ptr, nullptr); } if (instr_is_comptime(ptr)) { bool dest_allows_addr_zero = ptr_allows_addr_zero(dest_type); UndefAllowed is_undef_allowed = dest_allows_addr_zero ? UndefOk : UndefBad; ZigValue *val = ir_resolve_const(ira, ptr, is_undef_allowed); if (val == nullptr) return ira->codegen->invalid_inst_gen; if (value_is_comptime(val) && val->special != ConstValSpecialUndef) { bool is_addr_zero = val->data.x_ptr.special == ConstPtrSpecialNull || (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && val->data.x_ptr.data.hard_coded_addr.addr == 0); if (is_addr_zero && !dest_allows_addr_zero) { ir_add_error(ira, source_instr, buf_sprintf("null pointer casted to type '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_inst_gen; } } IrInstGen *result; if (val->data.x_ptr.mut == ConstPtrMutInfer) { result = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr, safety_check_on); } else { result = ir_const(ira, source_instr, dest_type); } InferredStructField *isf = (val->type->id == ZigTypeIdPointer) ? val->type->data.pointer.inferred_struct_field : nullptr; if (isf == nullptr) { copy_const_val(ira->codegen, result->value, val); } else { // The destination value should have x_ptr struct pointing to underlying struct value result->value->data.x_ptr.mut = val->data.x_ptr.mut; TypeStructField *field = find_struct_type_field(isf->inferred_struct_type, isf->field_name); assert(field != nullptr); if (field->is_comptime) { result->value->data.x_ptr.special = ConstPtrSpecialRef; result->value->data.x_ptr.data.ref.pointee = field->init_val; } else { assert(val->data.x_ptr.special == ConstPtrSpecialRef); result->value->data.x_ptr.special = ConstPtrSpecialBaseStruct; result->value->data.x_ptr.data.base_struct.struct_val = val->data.x_ptr.data.ref.pointee; result->value->data.x_ptr.data.base_struct.field_index = field->src_index; } result->value->special = ConstValSpecialStatic; } result->value->type = dest_type; // Keep the bigger alignment, it can only help- unless the target is zero bits. if (keep_bigger_alignment && src_align_bytes > dest_align_bytes && type_has_bits(ira->codegen, dest_type)) { result = ir_align_cast(ira, result, src_align_bytes, false); } return result; } if (src_align_bytes != 0 && dest_align_bytes > src_align_bytes) { ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment")); add_error_note(ira->codegen, msg, ptr_src->source_node, buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&src_type->name), src_align_bytes)); add_error_note(ira->codegen, msg, dest_type_src->source_node, buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&dest_type->name), dest_align_bytes)); return ira->codegen->invalid_inst_gen; } IrInstGen *casted_ptr = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr, safety_check_on); // Keep the bigger alignment, it can only help- unless the target is zero bits. IrInstGen *result; if (keep_bigger_alignment && src_align_bytes > dest_align_bytes && type_has_bits(ira->codegen, dest_type)) { result = ir_align_cast(ira, casted_ptr, src_align_bytes, false); if (type_is_invalid(result->value->type)) return ira->codegen->invalid_inst_gen; } else { result = casted_ptr; } return result; } static IrInstGen *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstSrcPtrCast *instruction) { IrInstGen *dest_type_value = instruction->dest_type->child; ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->invalid_inst_gen; IrInstGen *ptr = instruction->ptr->child; ZigType *src_type = ptr->value->type; if (type_is_invalid(src_type)) return ira->codegen->invalid_inst_gen; bool keep_bigger_alignment = true; return ir_analyze_ptr_cast(ira, &instruction->base.base, ptr, &instruction->ptr->base, dest_type, &dest_type_value->base, instruction->safety_check_on, keep_bigger_alignment); } static void buf_write_value_bytes_array(CodeGen *codegen, uint8_t *buf, ZigValue *val, size_t len) { size_t buf_i = 0; // TODO optimize the buf case expand_undef_array(codegen, val); for (size_t elem_i = 0; elem_i < val->type->data.array.len; elem_i += 1) { ZigValue *elem = &val->data.x_array.data.s_none.elements[elem_i]; buf_write_value_bytes(codegen, &buf[buf_i], elem); buf_i += type_size(codegen, elem->type); } if (val->type->id == ZigTypeIdArray && val->type->data.array.sentinel != nullptr) { buf_write_value_bytes(codegen, &buf[buf_i], val->type->data.array.sentinel); } } static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ZigValue *val) { if (val->special == ConstValSpecialUndef) { expand_undef_struct(codegen, val); val->special = ConstValSpecialStatic; } assert(val->special == ConstValSpecialStatic); switch (val->type->id) { case ZigTypeIdInvalid: case ZigTypeIdMetaType: case ZigTypeIdOpaque: case ZigTypeIdBoundFn: case ZigTypeIdUnreachable: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdEnumLiteral: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: zig_unreachable(); case ZigTypeIdVoid: return; case ZigTypeIdBool: buf[0] = val->data.x_bool ? 1 : 0; return; case ZigTypeIdInt: bigint_write_twos_complement(&val->data.x_bigint, buf, val->type->data.integral.bit_count, codegen->is_big_endian); return; case ZigTypeIdEnum: bigint_write_twos_complement(&val->data.x_enum_tag, buf, val->type->data.enumeration.tag_int_type->data.integral.bit_count, codegen->is_big_endian); return; case ZigTypeIdFloat: float_write_ieee597(val, buf, codegen->is_big_endian); return; case ZigTypeIdPointer: if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { BigInt bn; bigint_init_unsigned(&bn, val->data.x_ptr.data.hard_coded_addr.addr); bigint_write_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count, codegen->is_big_endian); return; } else { zig_unreachable(); } case ZigTypeIdArray: return buf_write_value_bytes_array(codegen, buf, val, val->type->data.array.len); case ZigTypeIdVector: return buf_write_value_bytes_array(codegen, buf, val, val->type->data.vector.len); case ZigTypeIdStruct: switch (val->type->data.structure.layout) { case ContainerLayoutAuto: zig_unreachable(); case ContainerLayoutExtern: { size_t src_field_count = val->type->data.structure.src_field_count; for (size_t field_i = 0; field_i < src_field_count; field_i += 1) { TypeStructField *struct_field = val->type->data.structure.fields[field_i]; if (struct_field->gen_index == SIZE_MAX) continue; ZigValue *field_val = val->data.x_struct.fields[field_i]; size_t offset = struct_field->offset; buf_write_value_bytes(codegen, buf + offset, field_val); } return; } case ContainerLayoutPacked: { size_t src_field_count = val->type->data.structure.src_field_count; size_t gen_field_count = val->type->data.structure.gen_field_count; size_t gen_i = 0; size_t src_i = 0; size_t offset = 0; bool is_big_endian = codegen->is_big_endian; uint8_t child_buf_prealloc[16]; size_t child_buf_len = 16; uint8_t *child_buf = child_buf_prealloc; while (gen_i < gen_field_count) { size_t big_int_byte_count = val->type->data.structure.host_int_bytes[gen_i]; if (big_int_byte_count > child_buf_len) { child_buf = heap::c_allocator.allocate_nonzero(big_int_byte_count); child_buf_len = big_int_byte_count; } BigInt big_int; bigint_init_unsigned(&big_int, 0); size_t used_bits = 0; while (src_i < src_field_count) { TypeStructField *field = val->type->data.structure.fields[src_i]; assert(field->gen_index != SIZE_MAX); if (field->gen_index != gen_i) break; uint32_t packed_bits_size = type_size_bits(codegen, field->type_entry); buf_write_value_bytes(codegen, child_buf, val->data.x_struct.fields[src_i]); BigInt child_val; bigint_read_twos_complement(&child_val, child_buf, packed_bits_size, is_big_endian, false); if (is_big_endian) { BigInt shift_amt; bigint_init_unsigned(&shift_amt, packed_bits_size); BigInt shifted; bigint_shl(&shifted, &big_int, &shift_amt); bigint_or(&big_int, &shifted, &child_val); } else { BigInt shift_amt; bigint_init_unsigned(&shift_amt, used_bits); BigInt child_val_shifted; bigint_shl(&child_val_shifted, &child_val, &shift_amt); BigInt tmp; bigint_or(&tmp, &big_int, &child_val_shifted); big_int = tmp; used_bits += packed_bits_size; } src_i += 1; } bigint_write_twos_complement(&big_int, buf + offset, big_int_byte_count * 8, is_big_endian); offset += big_int_byte_count; gen_i += 1; } return; } } zig_unreachable(); case ZigTypeIdOptional: zig_panic("TODO buf_write_value_bytes maybe type"); case ZigTypeIdFn: zig_panic("TODO buf_write_value_bytes fn type"); case ZigTypeIdUnion: zig_panic("TODO buf_write_value_bytes union type"); case ZigTypeIdFnFrame: zig_panic("TODO buf_write_value_bytes async fn frame type"); case ZigTypeIdAnyFrame: zig_panic("TODO buf_write_value_bytes anyframe type"); } zig_unreachable(); } static Error buf_read_value_bytes_array(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ZigValue *val, ZigType *elem_type, size_t len) { Error err; uint64_t elem_size = type_size(codegen, elem_type); switch (val->data.x_array.special) { case ConstArraySpecialNone: val->data.x_array.data.s_none.elements = codegen->pass1_arena->allocate(len); for (size_t i = 0; i < len; i++) { ZigValue *elem = &val->data.x_array.data.s_none.elements[i]; elem->special = ConstValSpecialStatic; elem->type = elem_type; if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem))) return err; } return ErrorNone; case ConstArraySpecialUndef: zig_panic("TODO buf_read_value_bytes ConstArraySpecialUndef array type"); case ConstArraySpecialBuf: zig_panic("TODO buf_read_value_bytes ConstArraySpecialBuf array type"); } zig_unreachable(); } static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ZigValue *val) { Error err; src_assert(val->special == ConstValSpecialStatic, source_node); switch (val->type->id) { case ZigTypeIdInvalid: case ZigTypeIdMetaType: case ZigTypeIdOpaque: case ZigTypeIdBoundFn: case ZigTypeIdUnreachable: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdEnumLiteral: case ZigTypeIdUndefined: case ZigTypeIdNull: zig_unreachable(); case ZigTypeIdVoid: return ErrorNone; case ZigTypeIdBool: val->data.x_bool = (buf[0] != 0); return ErrorNone; case ZigTypeIdInt: bigint_read_twos_complement(&val->data.x_bigint, buf, val->type->data.integral.bit_count, codegen->is_big_endian, val->type->data.integral.is_signed); return ErrorNone; case ZigTypeIdFloat: float_read_ieee597(val, buf, codegen->is_big_endian); return ErrorNone; case ZigTypeIdPointer: { val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; BigInt bn; bigint_read_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count, codegen->is_big_endian, false); val->data.x_ptr.data.hard_coded_addr.addr = bigint_as_usize(&bn); return ErrorNone; } case ZigTypeIdArray: return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.array.child_type, val->type->data.array.len); case ZigTypeIdVector: return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.vector.elem_type, val->type->data.vector.len); case ZigTypeIdEnum: switch (val->type->data.enumeration.layout) { case ContainerLayoutAuto: zig_panic("TODO buf_read_value_bytes enum auto"); case ContainerLayoutPacked: zig_panic("TODO buf_read_value_bytes enum packed"); case ContainerLayoutExtern: { ZigType *tag_int_type = val->type->data.enumeration.tag_int_type; src_assert(tag_int_type->id == ZigTypeIdInt, source_node); bigint_read_twos_complement(&val->data.x_enum_tag, buf, tag_int_type->data.integral.bit_count, codegen->is_big_endian, tag_int_type->data.integral.is_signed); return ErrorNone; } } zig_unreachable(); case ZigTypeIdStruct: switch (val->type->data.structure.layout) { case ContainerLayoutAuto: { switch(val->type->data.structure.special){ case StructSpecialNone: case StructSpecialInferredTuple: case StructSpecialInferredStruct: { ErrorMsg *msg = opt_ir_add_error_node(ira, codegen, source_node, buf_sprintf("non-extern, non-packed struct '%s' cannot have its bytes reinterpreted", buf_ptr(&val->type->name))); add_error_note(codegen, msg, val->type->data.structure.decl_node, buf_sprintf("declared here")); break; } case StructSpecialSlice: { opt_ir_add_error_node(ira, codegen, source_node, buf_sprintf("slice '%s' cannot have its bytes reinterpreted", buf_ptr(&val->type->name))); break; } } return ErrorSemanticAnalyzeFail; } case ContainerLayoutExtern: { size_t src_field_count = val->type->data.structure.src_field_count; val->data.x_struct.fields = alloc_const_vals_ptrs(codegen, src_field_count); for (size_t field_i = 0; field_i < src_field_count; field_i += 1) { ZigValue *field_val = val->data.x_struct.fields[field_i]; field_val->special = ConstValSpecialStatic; TypeStructField *struct_field = val->type->data.structure.fields[field_i]; field_val->type = struct_field->type_entry; if (struct_field->gen_index == SIZE_MAX) continue; size_t offset = struct_field->offset; uint8_t *new_buf = buf + offset; if ((err = buf_read_value_bytes(ira, codegen, source_node, new_buf, field_val))) return err; } return ErrorNone; } case ContainerLayoutPacked: { size_t src_field_count = val->type->data.structure.src_field_count; val->data.x_struct.fields = alloc_const_vals_ptrs(codegen, src_field_count); size_t gen_field_count = val->type->data.structure.gen_field_count; size_t gen_i = 0; size_t src_i = 0; size_t offset = 0; bool is_big_endian = codegen->is_big_endian; uint8_t child_buf_prealloc[16]; size_t child_buf_len = 16; uint8_t *child_buf = child_buf_prealloc; while (gen_i < gen_field_count) { size_t big_int_byte_count = val->type->data.structure.host_int_bytes[gen_i]; if (big_int_byte_count > child_buf_len) { child_buf = heap::c_allocator.allocate_nonzero(big_int_byte_count); child_buf_len = big_int_byte_count; } BigInt big_int; bigint_read_twos_complement(&big_int, buf + offset, big_int_byte_count * 8, is_big_endian, false); uint64_t bit_offset = 0; while (src_i < src_field_count) { TypeStructField *field = val->type->data.structure.fields[src_i]; src_assert(field->gen_index != SIZE_MAX, source_node); if (field->gen_index != gen_i) break; ZigValue *field_val = val->data.x_struct.fields[src_i]; field_val->special = ConstValSpecialStatic; field_val->type = field->type_entry; uint32_t packed_bits_size = type_size_bits(codegen, field->type_entry); BigInt child_val; if (is_big_endian) { BigInt packed_bits_size_bi; bigint_init_unsigned(&packed_bits_size_bi, big_int_byte_count * 8 - packed_bits_size - bit_offset); BigInt tmp; bigint_shr(&tmp, &big_int, &packed_bits_size_bi); bigint_truncate(&child_val, &tmp, packed_bits_size, false); } else { BigInt packed_bits_size_bi; bigint_init_unsigned(&packed_bits_size_bi, packed_bits_size); bigint_truncate(&child_val, &big_int, packed_bits_size, false); BigInt tmp; bigint_shr(&tmp, &big_int, &packed_bits_size_bi); big_int = tmp; } bigint_write_twos_complement(&child_val, child_buf, packed_bits_size, is_big_endian); if ((err = buf_read_value_bytes(ira, codegen, source_node, child_buf, field_val))) { return err; } bit_offset += packed_bits_size; src_i += 1; } offset += big_int_byte_count; gen_i += 1; } return ErrorNone; } } zig_unreachable(); case ZigTypeIdOptional: zig_panic("TODO buf_read_value_bytes maybe type"); case ZigTypeIdErrorUnion: zig_panic("TODO buf_read_value_bytes error union"); case ZigTypeIdErrorSet: zig_panic("TODO buf_read_value_bytes pure error type"); case ZigTypeIdFn: zig_panic("TODO buf_read_value_bytes fn type"); case ZigTypeIdUnion: zig_panic("TODO buf_read_value_bytes union type"); case ZigTypeIdFnFrame: zig_panic("TODO buf_read_value_bytes async fn frame type"); case ZigTypeIdAnyFrame: zig_panic("TODO buf_read_value_bytes anyframe type"); } zig_unreachable(); } static IrInstGen *ir_analyze_bit_cast(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, ZigType *dest_type) { Error err; ZigType *src_type = value->value->type; ir_assert(type_can_bit_cast(src_type), source_instr); ir_assert(type_can_bit_cast(dest_type), source_instr); if (dest_type->id == ZigTypeIdEnum) { ErrorMsg *msg = ir_add_error_node(ira, source_instr->source_node, buf_sprintf("cannot cast a value of type '%s'", buf_ptr(&dest_type->name))); add_error_note(ira->codegen, msg, source_instr->source_node, buf_sprintf("use @intToEnum for type coercion")); return ira->codegen->invalid_inst_gen; } if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_inst_gen; if ((err = type_resolve(ira->codegen, src_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_inst_gen; const bool src_is_ptr = handle_is_ptr(ira->codegen, src_type); const bool dest_is_ptr = handle_is_ptr(ira->codegen, dest_type); const uint64_t dest_size_bytes = type_size(ira->codegen, dest_type); const uint64_t src_size_bytes = type_size(ira->codegen, src_type); if (dest_size_bytes != src_size_bytes) { ir_add_error(ira, source_instr, buf_sprintf("destination type '%s' has size %" ZIG_PRI_u64 " but source type '%s' has size %" ZIG_PRI_u64, buf_ptr(&dest_type->name), dest_size_bytes, buf_ptr(&src_type->name), src_size_bytes)); return ira->codegen->invalid_inst_gen; } const uint64_t dest_size_bits = type_size_bits(ira->codegen, dest_type); const uint64_t src_size_bits = type_size_bits(ira->codegen, src_type); if (dest_size_bits != src_size_bits) { ir_add_error(ira, source_instr, buf_sprintf("destination type '%s' has %" ZIG_PRI_u64 " bits but source type '%s' has %" ZIG_PRI_u64 " bits", buf_ptr(&dest_type->name), dest_size_bits, buf_ptr(&src_type->name), src_size_bits)); return ira->codegen->invalid_inst_gen; } if (instr_is_comptime(value)) { ZigValue *val = ir_resolve_const(ira, value, UndefBad); if (!val) return ira->codegen->invalid_inst_gen; IrInstGen *result = ir_const(ira, source_instr, dest_type); uint8_t *buf = heap::c_allocator.allocate_nonzero(src_size_bytes); buf_write_value_bytes(ira->codegen, buf, val); if ((err = buf_read_value_bytes(ira, ira->codegen, source_instr->source_node, buf, result->value))) return ira->codegen->invalid_inst_gen; return result; } if (dest_is_ptr && !src_is_ptr) { // Spill the scalar into a local memory location and take its address value = ir_get_ref(ira, source_instr, value, false, false); } return ir_build_bit_cast_gen(ira, source_instr, value, dest_type); } static IrInstGen *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInst* source_instr, IrInstGen *target, ZigType *ptr_type) { Error err; ir_assert(get_src_ptr_type(ptr_type) != nullptr, source_instr); ir_assert(type_has_bits(ira->codegen, ptr_type), source_instr); IrInstGen *casted_int = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_usize); if (type_is_invalid(casted_int->value->type)) return ira->codegen->invalid_inst_gen; if (instr_is_comptime(casted_int)) { ZigValue *val = ir_resolve_const(ira, casted_int, UndefBad); if (!val) return ira->codegen->invalid_inst_gen; uint64_t addr = bigint_as_u64(&val->data.x_bigint); if (!ptr_allows_addr_zero(ptr_type) && addr == 0) { ir_add_error(ira, source_instr, buf_sprintf("pointer type '%s' does not allow address zero", buf_ptr(&ptr_type->name))); return ira->codegen->invalid_inst_gen; } uint32_t align_bytes; if ((err = resolve_ptr_align(ira, ptr_type, &align_bytes))) return ira->codegen->invalid_inst_gen; if (addr != 0 && addr % align_bytes != 0) { ir_add_error(ira, source_instr, buf_sprintf("pointer type '%s' requires aligned address", buf_ptr(&ptr_type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *result = ir_const(ira, source_instr, ptr_type); if (ptr_type->id == ZigTypeIdOptional && addr == 0) { result->value->data.x_ptr.special = ConstPtrSpecialNull; result->value->data.x_ptr.mut = ConstPtrMutComptimeConst; } else { result->value->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; result->value->data.x_ptr.mut = ConstPtrMutRuntimeVar; result->value->data.x_ptr.data.hard_coded_addr.addr = addr; } return result; } return ir_build_int_to_ptr_gen(ira, source_instr->scope, source_instr->source_node, casted_int, ptr_type); } static IrInstGen *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstSrcIntToPtr *instruction) { Error err; IrInstGen *dest_type_value = instruction->dest_type->child; ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->invalid_inst_gen; // We explicitly check for the size, so we can use get_src_ptr_type if (get_src_ptr_type(dest_type) == nullptr) { ir_add_error(ira, &dest_type_value->base, buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_inst_gen; } bool has_bits; if ((err = type_has_bits2(ira->codegen, dest_type, &has_bits))) return ira->codegen->invalid_inst_gen; if (!has_bits) { ir_add_error(ira, &dest_type_value->base, buf_sprintf("type '%s' has 0 bits and cannot store information", buf_ptr(&dest_type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *target = instruction->target->child; if (type_is_invalid(target->value->type)) return ira->codegen->invalid_inst_gen; return ir_analyze_int_to_ptr(ira, &instruction->base.base, target, dest_type); } static IrInstGen *ir_analyze_instruction_decl_ref(IrAnalyze *ira, IrInstSrcDeclRef *instruction) { IrInstGen *ref_instruction = ir_analyze_decl_ref(ira, &instruction->base.base, instruction->tld); if (type_is_invalid(ref_instruction->value->type)) { return ira->codegen->invalid_inst_gen; } if (instruction->lval == LValPtr || instruction->lval == LValAssign) { return ref_instruction; } else { return ir_get_deref(ira, &instruction->base.base, ref_instruction, nullptr); } } static IrInstGen *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstSrcPtrToInt *instruction) { Error err; IrInstGen *target = instruction->target->child; if (type_is_invalid(target->value->type)) return ira->codegen->invalid_inst_gen; ZigType *usize = ira->codegen->builtin_types.entry_usize; ZigType *src_ptr_type = get_src_ptr_type(target->value->type); if (src_ptr_type == nullptr) { ir_add_error(ira, &target->base, buf_sprintf("expected pointer, found '%s'", buf_ptr(&target->value->type->name))); return ira->codegen->invalid_inst_gen; } bool has_bits; if ((err = type_has_bits2(ira->codegen, src_ptr_type, &has_bits))) return ira->codegen->invalid_inst_gen; if (!has_bits) { ir_add_error(ira, &target->base, buf_sprintf("pointer to size 0 type has no address")); return ira->codegen->invalid_inst_gen; } if (instr_is_comptime(target)) { ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_inst_gen; // Since we've already run this type trough get_src_ptr_type it is // safe to access the x_ptr fields if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { IrInstGen *result = ir_const(ira, &instruction->base.base, usize); bigint_init_unsigned(&result->value->data.x_bigint, val->data.x_ptr.data.hard_coded_addr.addr); result->value->type = usize; return result; } else if (val->data.x_ptr.special == ConstPtrSpecialNull) { IrInstGen *result = ir_const(ira, &instruction->base.base, usize); bigint_init_unsigned(&result->value->data.x_bigint, 0); result->value->type = usize; return result; } } return ir_build_ptr_to_int_gen(ira, &instruction->base.base, target); } static IrInstGen *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstSrcPtrType *instruction) { IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type); result->value->special = ConstValSpecialLazy; LazyValuePtrType *lazy_ptr_type = heap::c_allocator.create(); lazy_ptr_type->ira = ira; ira_ref(ira); result->value->data.x_lazy = &lazy_ptr_type->base; lazy_ptr_type->base.id = LazyValueIdPtrType; if (instruction->sentinel != nullptr) { if (instruction->ptr_len != PtrLenUnknown) { ir_add_error(ira, &instruction->base.base, buf_sprintf("sentinels are only allowed on unknown-length pointers")); return ira->codegen->invalid_inst_gen; } lazy_ptr_type->sentinel = instruction->sentinel->child; if (ir_resolve_const(ira, lazy_ptr_type->sentinel, LazyOk) == nullptr) return ira->codegen->invalid_inst_gen; } lazy_ptr_type->elem_type = instruction->child_type->child; if (ir_resolve_type_lazy(ira, lazy_ptr_type->elem_type) == nullptr) return ira->codegen->invalid_inst_gen; if (instruction->align_value != nullptr) { lazy_ptr_type->align_inst = instruction->align_value->child; if (ir_resolve_const(ira, lazy_ptr_type->align_inst, LazyOk) == nullptr) return ira->codegen->invalid_inst_gen; } lazy_ptr_type->ptr_len = instruction->ptr_len; lazy_ptr_type->is_const = instruction->is_const; lazy_ptr_type->is_volatile = instruction->is_volatile; lazy_ptr_type->is_allowzero = instruction->is_allow_zero; lazy_ptr_type->bit_offset_in_host = instruction->bit_offset_start; lazy_ptr_type->host_int_bytes = instruction->host_int_bytes; return result; } static IrInstGen *ir_analyze_instruction_align_cast(IrAnalyze *ira, IrInstSrcAlignCast *instruction) { IrInstGen *target = instruction->target->child; if (type_is_invalid(target->value->type)) return ira->codegen->invalid_inst_gen; ZigType *elem_type = nullptr; if (is_slice(target->value->type)) { ZigType *slice_ptr_type = target->value->type->data.structure.fields[slice_ptr_index]->type_entry; elem_type = slice_ptr_type->data.pointer.child_type; } else if (target->value->type->id == ZigTypeIdPointer) { elem_type = target->value->type->data.pointer.child_type; } uint32_t align_bytes; IrInstGen *align_bytes_inst = instruction->align_bytes->child; if (!ir_resolve_align(ira, align_bytes_inst, elem_type, &align_bytes)) return ira->codegen->invalid_inst_gen; IrInstGen *result = ir_align_cast(ira, target, align_bytes, true); if (type_is_invalid(result->value->type)) return ira->codegen->invalid_inst_gen; return result; } static IrInstGen *ir_analyze_instruction_opaque_type(IrAnalyze *ira, IrInstSrcOpaqueType *instruction) { Buf *bare_name = buf_alloc(); Buf *full_name = get_anon_type_name(ira->codegen, ira->old_irb.exec, "opaque", instruction->base.base.scope, instruction->base.base.source_node, bare_name); ZigType *result_type = get_opaque_type(ira->codegen, instruction->base.base.scope, instruction->base.base.source_node, buf_ptr(full_name), bare_name); return ir_const_type(ira, &instruction->base.base, result_type); } static IrInstGen *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, IrInstSrcSetAlignStack *instruction) { uint32_t align_bytes; IrInstGen *align_bytes_inst = instruction->align_bytes->child; if (!ir_resolve_align(ira, align_bytes_inst, nullptr, &align_bytes)) return ira->codegen->invalid_inst_gen; if (align_bytes > 256) { ir_add_error(ira, &instruction->base.base, buf_sprintf("attempt to @setAlignStack(%" PRIu32 "); maximum is 256", align_bytes)); return ira->codegen->invalid_inst_gen; } ZigFn *fn_entry = ira->new_irb.exec->fn_entry; if (fn_entry == nullptr) { ir_add_error(ira, &instruction->base.base, buf_sprintf("@setAlignStack outside function")); return ira->codegen->invalid_inst_gen; } if (fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionNaked) { ir_add_error(ira, &instruction->base.base, buf_sprintf("@setAlignStack in naked function")); return ira->codegen->invalid_inst_gen; } if (fn_entry->fn_inline == FnInlineAlways) { ir_add_error(ira, &instruction->base.base, buf_sprintf("@setAlignStack in inline function")); return ira->codegen->invalid_inst_gen; } if (fn_entry->set_alignstack_node != nullptr) { ErrorMsg *msg = ir_add_error(ira, &instruction->base.base, buf_sprintf("alignstack set twice")); add_error_note(ira->codegen, msg, fn_entry->set_alignstack_node, buf_sprintf("first set here")); return ira->codegen->invalid_inst_gen; } fn_entry->set_alignstack_node = instruction->base.base.source_node; fn_entry->alignstack_value = align_bytes; return ir_const_void(ira, &instruction->base.base); } static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgType *instruction) { IrInstGen *fn_type_inst = instruction->fn_type->child; ZigType *fn_type = ir_resolve_type(ira, fn_type_inst); if (type_is_invalid(fn_type)) return ira->codegen->invalid_inst_gen; IrInstGen *arg_index_inst = instruction->arg_index->child; uint64_t arg_index; if (!ir_resolve_usize(ira, arg_index_inst, &arg_index)) return ira->codegen->invalid_inst_gen; if (fn_type->id == ZigTypeIdBoundFn) { fn_type = fn_type->data.bound_fn.fn_type; arg_index += 1; } if (fn_type->id != ZigTypeIdFn) { ir_add_error(ira, &fn_type_inst->base, buf_sprintf("expected function, found '%s'", buf_ptr(&fn_type->name))); return ira->codegen->invalid_inst_gen; } FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; if (arg_index >= fn_type_id->param_count) { if (instruction->allow_var) { // TODO remove this with var args return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_var); } ir_add_error(ira, &arg_index_inst->base, buf_sprintf("arg index %" ZIG_PRI_u64 " out of bounds; '%s' has %" ZIG_PRI_usize " arguments", arg_index, buf_ptr(&fn_type->name), fn_type_id->param_count)); return ira->codegen->invalid_inst_gen; } ZigType *result_type = fn_type_id->param_info[arg_index].type; if (result_type == nullptr) { // Args are only unresolved if our function is generic. ir_assert(fn_type->data.fn.is_generic, &instruction->base.base); if (instruction->allow_var) { return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_var); } else { ir_add_error(ira, &arg_index_inst->base, buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_u64 " because '%s' is generic", arg_index, buf_ptr(&fn_type->name))); return ira->codegen->invalid_inst_gen; } } return ir_const_type(ira, &instruction->base.base, result_type); } static IrInstGen *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstSrcTagType *instruction) { Error err; IrInstGen *target_inst = instruction->target->child; ZigType *enum_type = ir_resolve_type(ira, target_inst); if (type_is_invalid(enum_type)) return ira->codegen->invalid_inst_gen; if (enum_type->id == ZigTypeIdEnum) { if ((err = type_resolve(ira->codegen, enum_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_inst_gen; return ir_const_type(ira, &instruction->base.base, enum_type->data.enumeration.tag_int_type); } else if (enum_type->id == ZigTypeIdUnion) { ZigType *tag_type = ir_resolve_union_tag_type(ira, instruction->target->base.source_node, enum_type); if (type_is_invalid(tag_type)) return ira->codegen->invalid_inst_gen; return ir_const_type(ira, &instruction->base.base, tag_type); } else { ir_add_error(ira, &target_inst->base, buf_sprintf("expected enum or union, found '%s'", buf_ptr(&enum_type->name))); return ira->codegen->invalid_inst_gen; } } static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstGen *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 || operand_type->id == ZigTypeIdEnum) { ZigType *int_type; if (operand_type->id == ZigTypeIdEnum) { int_type = operand_type->data.enumeration.tag_int_type; } else { int_type = operand_type; } auto bit_count = int_type->data.integral.bit_count; uint32_t max_atomic_bits = target_arch_largest_atomic_bits(ira->codegen->zig_target->arch); if (bit_count > max_atomic_bits) { ir_add_error(ira, &op->base, buf_sprintf("expected %" PRIu32 "-bit integer type or smaller, found %" PRIu32 "-bit integer type", max_atomic_bits, bit_count)); return ira->codegen->builtin_types.entry_invalid; } } else if (operand_type->id == ZigTypeIdFloat) { uint32_t max_atomic_bits = target_arch_largest_atomic_bits(ira->codegen->zig_target->arch); if (operand_type->data.floating.bit_count > max_atomic_bits) { ir_add_error(ira, &op->base, buf_sprintf("expected %" PRIu32 "-bit float or smaller, found %" PRIu32 "-bit float", max_atomic_bits, (uint32_t) operand_type->data.floating.bit_count)); return ira->codegen->builtin_types.entry_invalid; } } else if (operand_type->id == ZigTypeIdBool) { // will be treated as u8 } else { Error err; ZigType *operand_ptr_type; if ((err = get_codegen_ptr_type(ira->codegen, operand_type, &operand_ptr_type))) return ira->codegen->builtin_types.entry_invalid; if (operand_ptr_type == nullptr) { ir_add_error(ira, &op->base, buf_sprintf("expected integer, float, enum or pointer type, found '%s'", buf_ptr(&operand_type->name))); return ira->codegen->builtin_types.entry_invalid; } } return operand_type; } static IrInstGen *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstSrcAtomicRmw *instruction) { ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->child); if (type_is_invalid(operand_type)) return ira->codegen->invalid_inst_gen; IrInstGen *ptr_inst = instruction->ptr->child; if (type_is_invalid(ptr_inst->value->type)) return ira->codegen->invalid_inst_gen; // TODO let this be volatile ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); IrInstGen *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type); if (type_is_invalid(casted_ptr->value->type)) return ira->codegen->invalid_inst_gen; AtomicRmwOp op; if (!ir_resolve_atomic_rmw_op(ira, instruction->op->child, &op)) { return ira->codegen->invalid_inst_gen; } if (operand_type->id == ZigTypeIdEnum && op != AtomicRmwOp_xchg) { ir_add_error(ira, &instruction->op->base, buf_sprintf("@atomicRmw with enum only allowed with .Xchg")); return ira->codegen->invalid_inst_gen; } else if (operand_type->id == ZigTypeIdBool && op != AtomicRmwOp_xchg) { ir_add_error(ira, &instruction->op->base, buf_sprintf("@atomicRmw with bool only allowed with .Xchg")); return ira->codegen->invalid_inst_gen; } else if (operand_type->id == ZigTypeIdFloat && op > AtomicRmwOp_sub) { ir_add_error(ira, &instruction->op->base, buf_sprintf("@atomicRmw with float only allowed with .Xchg, .Add and .Sub")); return ira->codegen->invalid_inst_gen; } IrInstGen *operand = instruction->operand->child; if (type_is_invalid(operand->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_operand = ir_implicit_cast(ira, operand, operand_type); if (type_is_invalid(casted_operand->value->type)) return ira->codegen->invalid_inst_gen; AtomicOrder ordering; if (!ir_resolve_atomic_order(ira, instruction->ordering->child, &ordering)) return ira->codegen->invalid_inst_gen; if (ordering == AtomicOrderUnordered) { ir_add_error(ira, &instruction->ordering->base, buf_sprintf("@atomicRmw atomic ordering must not be Unordered")); return ira->codegen->invalid_inst_gen; } // special case zero bit types switch (type_has_one_possible_value(ira->codegen, operand_type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_inst_gen; case OnePossibleValueYes: return ir_const_move(ira, &instruction->base.base, get_the_one_possible_value(ira->codegen, operand_type)); case OnePossibleValueNo: break; } IrInst *source_inst = &instruction->base.base; if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value->data.x_ptr.mut == ConstPtrMutComptimeVar) { ZigValue *ptr_val = ir_resolve_const(ira, casted_ptr, UndefBad); if (ptr_val == nullptr) return ira->codegen->invalid_inst_gen; ZigValue *op1_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.base.source_node); if (op1_val == nullptr) return ira->codegen->invalid_inst_gen; ZigValue *op2_val = ir_resolve_const(ira, casted_operand, UndefBad); if (op2_val == nullptr) return ira->codegen->invalid_inst_gen; IrInstGen *result = ir_const(ira, source_inst, operand_type); copy_const_val(ira->codegen, result->value, op1_val); if (op == AtomicRmwOp_xchg) { copy_const_val(ira->codegen, op1_val, op2_val); return result; } if (operand_type->id == ZigTypeIdPointer || operand_type->id == ZigTypeIdOptional) { ir_add_error(ira, &instruction->ordering->base, buf_sprintf("TODO comptime @atomicRmw with pointers other than .Xchg")); return ira->codegen->invalid_inst_gen; } ErrorMsg *msg; if (op == AtomicRmwOp_min || op == AtomicRmwOp_max) { IrBinOp bin_op; if (op == AtomicRmwOp_min) // store op2 if op2 < op1 bin_op = IrBinOpCmpGreaterThan; else // store op2 if op2 > op1 bin_op = IrBinOpCmpLessThan; IrInstGen *dummy_value = ir_const(ira, source_inst, operand_type); msg = ir_eval_bin_op_cmp_scalar(ira, source_inst, op1_val, bin_op, op2_val, dummy_value->value); if (msg != nullptr) { return ira->codegen->invalid_inst_gen; } if (dummy_value->value->data.x_bool) copy_const_val(ira->codegen, op1_val, op2_val); } else { IrBinOp bin_op; switch (op) { case AtomicRmwOp_xchg: case AtomicRmwOp_max: case AtomicRmwOp_min: zig_unreachable(); case AtomicRmwOp_add: if (operand_type->id == ZigTypeIdFloat) bin_op = IrBinOpAdd; else bin_op = IrBinOpAddWrap; break; case AtomicRmwOp_sub: if (operand_type->id == ZigTypeIdFloat) bin_op = IrBinOpSub; else bin_op = IrBinOpSubWrap; break; case AtomicRmwOp_and: case AtomicRmwOp_nand: bin_op = IrBinOpBinAnd; break; case AtomicRmwOp_or: bin_op = IrBinOpBinOr; break; case AtomicRmwOp_xor: bin_op = IrBinOpBinXor; break; } msg = ir_eval_math_op_scalar(ira, source_inst, operand_type, op1_val, bin_op, op2_val, op1_val); if (msg != nullptr) { return ira->codegen->invalid_inst_gen; } if (op == AtomicRmwOp_nand) { bigint_not(&op1_val->data.x_bigint, &op1_val->data.x_bigint, operand_type->data.integral.bit_count, operand_type->data.integral.is_signed); } } return result; } return ir_build_atomic_rmw_gen(ira, source_inst, casted_ptr, casted_operand, op, ordering, operand_type); } static IrInstGen *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstSrcAtomicLoad *instruction) { ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->child); if (type_is_invalid(operand_type)) return ira->codegen->invalid_inst_gen; IrInstGen *ptr_inst = instruction->ptr->child; if (type_is_invalid(ptr_inst->value->type)) return ira->codegen->invalid_inst_gen; ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, true); IrInstGen *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type); if (type_is_invalid(casted_ptr->value->type)) return ira->codegen->invalid_inst_gen; AtomicOrder ordering; if (!ir_resolve_atomic_order(ira, instruction->ordering->child, &ordering)) return ira->codegen->invalid_inst_gen; if (ordering == AtomicOrderRelease || ordering == AtomicOrderAcqRel) { ir_assert(instruction->ordering != nullptr, &instruction->base.base); ir_add_error(ira, &instruction->ordering->base, buf_sprintf("@atomicLoad atomic ordering must not be Release or AcqRel")); return ira->codegen->invalid_inst_gen; } if (instr_is_comptime(casted_ptr)) { IrInstGen *result = ir_get_deref(ira, &instruction->base.base, casted_ptr, nullptr); ir_assert(result->value->type != nullptr, &instruction->base.base); return result; } return ir_build_atomic_load_gen(ira, &instruction->base.base, casted_ptr, ordering, operand_type); } static IrInstGen *ir_analyze_instruction_atomic_store(IrAnalyze *ira, IrInstSrcAtomicStore *instruction) { ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->child); if (type_is_invalid(operand_type)) return ira->codegen->invalid_inst_gen; IrInstGen *ptr_inst = instruction->ptr->child; if (type_is_invalid(ptr_inst->value->type)) return ira->codegen->invalid_inst_gen; ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); IrInstGen *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type); if (type_is_invalid(casted_ptr->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *value = instruction->value->child; if (type_is_invalid(value->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_value = ir_implicit_cast(ira, value, operand_type); if (type_is_invalid(casted_value->value->type)) return ira->codegen->invalid_inst_gen; AtomicOrder ordering; if (!ir_resolve_atomic_order(ira, instruction->ordering->child, &ordering)) return ira->codegen->invalid_inst_gen; if (ordering == AtomicOrderAcquire || ordering == AtomicOrderAcqRel) { ir_assert(instruction->ordering != nullptr, &instruction->base.base); ir_add_error(ira, &instruction->ordering->base, buf_sprintf("@atomicStore atomic ordering must not be Acquire or AcqRel")); return ira->codegen->invalid_inst_gen; } // special case zero bit types switch (type_has_one_possible_value(ira->codegen, operand_type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_inst_gen; case OnePossibleValueYes: return ir_const_void(ira, &instruction->base.base); case OnePossibleValueNo: break; } if (instr_is_comptime(casted_value) && instr_is_comptime(casted_ptr)) { IrInstGen *result = ir_analyze_store_ptr(ira, &instruction->base.base, casted_ptr, value, false); result->value->type = ira->codegen->builtin_types.entry_void; return result; } return ir_build_atomic_store_gen(ira, &instruction->base.base, casted_ptr, casted_value, ordering); } static IrInstGen *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira, IrInstSrcSaveErrRetAddr *instruction) { return ir_build_save_err_ret_addr_gen(ira, &instruction->base.base); } static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, IrInst* source_instr, BuiltinFnId fop, ZigType *float_type, ZigValue *op, ZigValue *out_val) { assert(ira && source_instr && float_type && out_val && op); assert(float_type->id == ZigTypeIdFloat || float_type->id == ZigTypeIdComptimeFloat); unsigned bits; switch (float_type->id) { case ZigTypeIdComptimeFloat: bits = 128; break; case ZigTypeIdFloat: bits = float_type->data.floating.bit_count; break; default: zig_unreachable(); } switch (bits) { case 16: { switch (fop) { case BuiltinFnIdSqrt: out_val->data.x_f16 = f16_sqrt(op->data.x_f16); break; case BuiltinFnIdSin: out_val->data.x_f16 = zig_double_to_f16(sin(zig_f16_to_double(op->data.x_f16))); break; case BuiltinFnIdCos: out_val->data.x_f16 = zig_double_to_f16(cos(zig_f16_to_double(op->data.x_f16))); break; case BuiltinFnIdExp: out_val->data.x_f16 = zig_double_to_f16(exp(zig_f16_to_double(op->data.x_f16))); break; case BuiltinFnIdExp2: out_val->data.x_f16 = zig_double_to_f16(exp2(zig_f16_to_double(op->data.x_f16))); break; case BuiltinFnIdLog: out_val->data.x_f16 = zig_double_to_f16(log(zig_f16_to_double(op->data.x_f16))); break; case BuiltinFnIdLog10: out_val->data.x_f16 = zig_double_to_f16(log10(zig_f16_to_double(op->data.x_f16))); break; case BuiltinFnIdLog2: out_val->data.x_f16 = zig_double_to_f16(log2(zig_f16_to_double(op->data.x_f16))); break; case BuiltinFnIdFabs: out_val->data.x_f16 = zig_double_to_f16(fabs(zig_f16_to_double(op->data.x_f16))); break; case BuiltinFnIdFloor: out_val->data.x_f16 = zig_double_to_f16(floor(zig_f16_to_double(op->data.x_f16))); break; case BuiltinFnIdCeil: out_val->data.x_f16 = zig_double_to_f16(ceil(zig_f16_to_double(op->data.x_f16))); break; case BuiltinFnIdTrunc: out_val->data.x_f16 = zig_double_to_f16(trunc(zig_f16_to_double(op->data.x_f16))); break; case BuiltinFnIdNearbyInt: out_val->data.x_f16 = zig_double_to_f16(nearbyint(zig_f16_to_double(op->data.x_f16))); break; case BuiltinFnIdRound: out_val->data.x_f16 = zig_double_to_f16(round(zig_f16_to_double(op->data.x_f16))); break; default: zig_unreachable(); }; break; } case 32: { switch (fop) { case BuiltinFnIdSqrt: out_val->data.x_f32 = sqrtf(op->data.x_f32); break; case BuiltinFnIdSin: out_val->data.x_f32 = sinf(op->data.x_f32); break; case BuiltinFnIdCos: out_val->data.x_f32 = cosf(op->data.x_f32); break; case BuiltinFnIdExp: out_val->data.x_f32 = expf(op->data.x_f32); break; case BuiltinFnIdExp2: out_val->data.x_f32 = exp2f(op->data.x_f32); break; case BuiltinFnIdLog: out_val->data.x_f32 = logf(op->data.x_f32); break; case BuiltinFnIdLog10: out_val->data.x_f32 = log10f(op->data.x_f32); break; case BuiltinFnIdLog2: out_val->data.x_f32 = log2f(op->data.x_f32); break; case BuiltinFnIdFabs: out_val->data.x_f32 = fabsf(op->data.x_f32); break; case BuiltinFnIdFloor: out_val->data.x_f32 = floorf(op->data.x_f32); break; case BuiltinFnIdCeil: out_val->data.x_f32 = ceilf(op->data.x_f32); break; case BuiltinFnIdTrunc: out_val->data.x_f32 = truncf(op->data.x_f32); break; case BuiltinFnIdNearbyInt: out_val->data.x_f32 = nearbyintf(op->data.x_f32); break; case BuiltinFnIdRound: out_val->data.x_f32 = roundf(op->data.x_f32); break; default: zig_unreachable(); }; break; } case 64: { switch (fop) { case BuiltinFnIdSqrt: out_val->data.x_f64 = sqrt(op->data.x_f64); break; case BuiltinFnIdSin: out_val->data.x_f64 = sin(op->data.x_f64); break; case BuiltinFnIdCos: out_val->data.x_f64 = cos(op->data.x_f64); break; case BuiltinFnIdExp: out_val->data.x_f64 = exp(op->data.x_f64); break; case BuiltinFnIdExp2: out_val->data.x_f64 = exp2(op->data.x_f64); break; case BuiltinFnIdLog: out_val->data.x_f64 = log(op->data.x_f64); break; case BuiltinFnIdLog10: out_val->data.x_f64 = log10(op->data.x_f64); break; case BuiltinFnIdLog2: out_val->data.x_f64 = log2(op->data.x_f64); break; case BuiltinFnIdFabs: out_val->data.x_f64 = fabs(op->data.x_f64); break; case BuiltinFnIdFloor: out_val->data.x_f64 = floor(op->data.x_f64); break; case BuiltinFnIdCeil: out_val->data.x_f64 = ceil(op->data.x_f64); break; case BuiltinFnIdTrunc: out_val->data.x_f64 = trunc(op->data.x_f64); break; case BuiltinFnIdNearbyInt: out_val->data.x_f64 = nearbyint(op->data.x_f64); break; case BuiltinFnIdRound: out_val->data.x_f64 = round(op->data.x_f64); break; default: zig_unreachable(); } break; } case 80: return ir_add_error(ira, source_instr, buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026", float_op_to_name(fop), buf_ptr(&float_type->name))); case 128: { float128_t *out, *in; if (float_type->id == ZigTypeIdComptimeFloat) { out = &out_val->data.x_bigfloat.value; in = &op->data.x_bigfloat.value; } else { out = &out_val->data.x_f128; in = &op->data.x_f128; } switch (fop) { case BuiltinFnIdSqrt: f128M_sqrt(in, out); break; case BuiltinFnIdNearbyInt: case BuiltinFnIdSin: case BuiltinFnIdCos: case BuiltinFnIdExp: case BuiltinFnIdExp2: case BuiltinFnIdLog: case BuiltinFnIdLog10: case BuiltinFnIdLog2: case BuiltinFnIdFabs: case BuiltinFnIdFloor: case BuiltinFnIdCeil: case BuiltinFnIdTrunc: case BuiltinFnIdRound: return ir_add_error(ira, source_instr, buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026", float_op_to_name(fop), buf_ptr(&float_type->name))); default: zig_unreachable(); } break; } default: zig_unreachable(); } out_val->special = ConstValSpecialStatic; return nullptr; } static IrInstGen *ir_analyze_instruction_float_op(IrAnalyze *ira, IrInstSrcFloatOp *instruction) { IrInstGen *operand = instruction->operand->child; ZigType *operand_type = operand->value->type; if (type_is_invalid(operand_type)) return ira->codegen->invalid_inst_gen; // This instruction accepts floats and vectors of floats. ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; if (scalar_type->id != ZigTypeIdFloat && scalar_type->id != ZigTypeIdComptimeFloat) { ir_add_error(ira, &operand->base, buf_sprintf("expected float type, found '%s'", buf_ptr(&scalar_type->name))); return ira->codegen->invalid_inst_gen; } if (instr_is_comptime(operand)) { ZigValue *operand_val = ir_resolve_const(ira, operand, UndefOk); if (operand_val == nullptr) return ira->codegen->invalid_inst_gen; if (operand_val->special == ConstValSpecialUndef) return ir_const_undef(ira, &instruction->base.base, operand_type); IrInstGen *result = ir_const(ira, &instruction->base.base, operand_type); ZigValue *out_val = result->value; if (operand_type->id == ZigTypeIdVector) { expand_undef_array(ira->codegen, operand_val); out_val->special = ConstValSpecialUndef; expand_undef_array(ira->codegen, out_val); size_t len = operand_type->data.vector.len; for (size_t i = 0; i < len; i += 1) { ZigValue *elem_operand = &operand_val->data.x_array.data.s_none.elements[i]; ZigValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i]; ir_assert(elem_operand->type == scalar_type, &instruction->base.base); ir_assert(float_out_val->type == scalar_type, &instruction->base.base); ErrorMsg *msg = ir_eval_float_op(ira, &instruction->base.base, instruction->fn_id, scalar_type, elem_operand, float_out_val); if (msg != nullptr) { add_error_note(ira->codegen, msg, instruction->base.base.source_node, buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i)); return ira->codegen->invalid_inst_gen; } float_out_val->type = scalar_type; } out_val->type = operand_type; out_val->special = ConstValSpecialStatic; } else { if (ir_eval_float_op(ira, &instruction->base.base, instruction->fn_id, scalar_type, operand_val, out_val) != nullptr) { return ira->codegen->invalid_inst_gen; } } return result; } ir_assert(scalar_type->id == ZigTypeIdFloat, &instruction->base.base); return ir_build_float_op_gen(ira, &instruction->base.base, operand, instruction->fn_id, operand_type); } static IrInstGen *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstSrcBswap *instruction) { Error err; ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child); if (type_is_invalid(int_type)) return ira->codegen->invalid_inst_gen; IrInstGen *uncasted_op = instruction->op->child; if (type_is_invalid(uncasted_op->value->type)) return ira->codegen->invalid_inst_gen; uint32_t vector_len = UINT32_MAX; // means not a vector if (uncasted_op->value->type->id == ZigTypeIdArray) { bool can_be_vec_elem; if ((err = is_valid_vector_elem_type(ira->codegen, uncasted_op->value->type->data.array.child_type, &can_be_vec_elem))) { return ira->codegen->invalid_inst_gen; } if (can_be_vec_elem) { vector_len = uncasted_op->value->type->data.array.len; } } else if (uncasted_op->value->type->id == ZigTypeIdVector) { vector_len = uncasted_op->value->type->data.vector.len; } bool is_vector = (vector_len != UINT32_MAX); ZigType *op_type = is_vector ? get_vector_type(ira->codegen, vector_len, int_type) : int_type; IrInstGen *op = ir_implicit_cast(ira, uncasted_op, op_type); if (type_is_invalid(op->value->type)) return ira->codegen->invalid_inst_gen; if (int_type->data.integral.bit_count == 8 || int_type->data.integral.bit_count == 0) return op; if (int_type->data.integral.bit_count % 8 != 0) { ir_add_error(ira, &instruction->op->base, buf_sprintf("@byteSwap integer type '%s' has %" PRIu32 " bits which is not evenly divisible by 8", buf_ptr(&int_type->name), int_type->data.integral.bit_count)); return ira->codegen->invalid_inst_gen; } if (instr_is_comptime(op)) { ZigValue *val = ir_resolve_const(ira, op, UndefOk); if (val == nullptr) return ira->codegen->invalid_inst_gen; if (val->special == ConstValSpecialUndef) return ir_const_undef(ira, &instruction->base.base, op_type); IrInstGen *result = ir_const(ira, &instruction->base.base, op_type); const size_t buf_size = int_type->data.integral.bit_count / 8; uint8_t *buf = heap::c_allocator.allocate_nonzero(buf_size); if (is_vector) { expand_undef_array(ira->codegen, val); result->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(op_type->data.vector.len); for (unsigned i = 0; i < op_type->data.vector.len; i += 1) { ZigValue *op_elem_val = &val->data.x_array.data.s_none.elements[i]; if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, instruction->base.base.source_node, op_elem_val, UndefOk))) { return ira->codegen->invalid_inst_gen; } ZigValue *result_elem_val = &result->value->data.x_array.data.s_none.elements[i]; result_elem_val->type = int_type; result_elem_val->special = op_elem_val->special; if (op_elem_val->special == ConstValSpecialUndef) continue; bigint_write_twos_complement(&op_elem_val->data.x_bigint, buf, int_type->data.integral.bit_count, true); bigint_read_twos_complement(&result->value->data.x_array.data.s_none.elements[i].data.x_bigint, buf, int_type->data.integral.bit_count, false, int_type->data.integral.is_signed); } } else { bigint_write_twos_complement(&val->data.x_bigint, buf, int_type->data.integral.bit_count, true); bigint_read_twos_complement(&result->value->data.x_bigint, buf, int_type->data.integral.bit_count, false, int_type->data.integral.is_signed); } heap::c_allocator.deallocate(buf, buf_size); return result; } return ir_build_bswap_gen(ira, &instruction->base.base, op_type, op); } static IrInstGen *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstSrcBitReverse *instruction) { ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child); if (type_is_invalid(int_type)) return ira->codegen->invalid_inst_gen; IrInstGen *op = ir_implicit_cast(ira, instruction->op->child, int_type); if (type_is_invalid(op->value->type)) return ira->codegen->invalid_inst_gen; if (int_type->data.integral.bit_count == 0) { IrInstGen *result = ir_const(ira, &instruction->base.base, int_type); bigint_init_unsigned(&result->value->data.x_bigint, 0); return result; } if (instr_is_comptime(op)) { ZigValue *val = ir_resolve_const(ira, op, UndefOk); if (val == nullptr) return ira->codegen->invalid_inst_gen; if (val->special == ConstValSpecialUndef) return ir_const_undef(ira, &instruction->base.base, int_type); IrInstGen *result = ir_const(ira, &instruction->base.base, int_type); size_t num_bits = int_type->data.integral.bit_count; size_t buf_size = (num_bits + 7) / 8; uint8_t *comptime_buf = heap::c_allocator.allocate_nonzero(buf_size); uint8_t *result_buf = heap::c_allocator.allocate_nonzero(buf_size); memset(comptime_buf,0,buf_size); memset(result_buf,0,buf_size); bigint_write_twos_complement(&val->data.x_bigint,comptime_buf,num_bits,ira->codegen->is_big_endian); size_t bit_i = 0; size_t bit_rev_i = num_bits - 1; for (; bit_i < num_bits; bit_i++, bit_rev_i--) { if (comptime_buf[bit_i / 8] & (1 << (bit_i % 8))) { result_buf[bit_rev_i / 8] |= (1 << (bit_rev_i % 8)); } } bigint_read_twos_complement(&result->value->data.x_bigint, result_buf, int_type->data.integral.bit_count, ira->codegen->is_big_endian, int_type->data.integral.is_signed); return result; } return ir_build_bit_reverse_gen(ira, &instruction->base.base, int_type, op); } static IrInstGen *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstSrcEnumToInt *instruction) { IrInstGen *target = instruction->target->child; if (type_is_invalid(target->value->type)) return ira->codegen->invalid_inst_gen; return ir_analyze_enum_to_int(ira, &instruction->base.base, target); } static IrInstGen *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstSrcIntToEnum *instruction) { Error err; IrInstGen *dest_type_value = instruction->dest_type->child; ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->invalid_inst_gen; if (dest_type->id != ZigTypeIdEnum) { ir_add_error(ira, &instruction->dest_type->base, buf_sprintf("expected enum, found type '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_inst_gen; } if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_inst_gen; ZigType *tag_type = dest_type->data.enumeration.tag_int_type; IrInstGen *target = instruction->target->child; if (type_is_invalid(target->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *casted_target = ir_implicit_cast(ira, target, tag_type); if (type_is_invalid(casted_target->value->type)) return ira->codegen->invalid_inst_gen; return ir_analyze_int_to_enum(ira, &instruction->base.base, casted_target, dest_type); } static IrInstGen *ir_analyze_instruction_check_runtime_scope(IrAnalyze *ira, IrInstSrcCheckRuntimeScope *instruction) { IrInstGen *block_comptime_inst = instruction->scope_is_comptime->child; bool scope_is_comptime; if (!ir_resolve_bool(ira, block_comptime_inst, &scope_is_comptime)) return ira->codegen->invalid_inst_gen; IrInstGen *is_comptime_inst = instruction->is_comptime->child; bool is_comptime; if (!ir_resolve_bool(ira, is_comptime_inst, &is_comptime)) return ira->codegen->invalid_inst_gen; if (!scope_is_comptime && is_comptime) { ErrorMsg *msg = ir_add_error(ira, &instruction->base.base, buf_sprintf("comptime control flow inside runtime block")); add_error_note(ira->codegen, msg, block_comptime_inst->base.source_node, buf_sprintf("runtime block created here")); return ira->codegen->invalid_inst_gen; } return ir_const_void(ira, &instruction->base.base); } static IrInstGen *ir_analyze_instruction_has_decl(IrAnalyze *ira, IrInstSrcHasDecl *instruction) { ZigType *container_type = ir_resolve_type(ira, instruction->container->child); if (type_is_invalid(container_type)) return ira->codegen->invalid_inst_gen; Buf *name = ir_resolve_str(ira, instruction->name->child); if (name == nullptr) return ira->codegen->invalid_inst_gen; if (!is_container(container_type)) { ir_add_error(ira, &instruction->container->base, buf_sprintf("expected struct, enum, or union; found '%s'", buf_ptr(&container_type->name))); return ira->codegen->invalid_inst_gen; } ScopeDecls *container_scope = get_container_scope(container_type); Tld *tld = find_container_decl(ira->codegen, container_scope, name); if (tld == nullptr) return ir_const_bool(ira, &instruction->base.base, false); if (tld->visib_mod == VisibModPrivate && tld->import != get_scope_import(instruction->base.base.scope)) { return ir_const_bool(ira, &instruction->base.base, false); } return ir_const_bool(ira, &instruction->base.base, true); } static IrInstGen *ir_analyze_instruction_undeclared_ident(IrAnalyze *ira, IrInstSrcUndeclaredIdent *instruction) { // put a variable of same name with invalid type in global scope // so that future references to this same name will find a variable with an invalid type populate_invalid_variable_in_scope(ira->codegen, instruction->base.base.scope, instruction->base.base.source_node, instruction->name); ir_add_error(ira, &instruction->base.base, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(instruction->name))); return ira->codegen->invalid_inst_gen; } static IrInstGen *ir_analyze_instruction_end_expr(IrAnalyze *ira, IrInstSrcEndExpr *instruction) { IrInstGen *value = instruction->value->child; if (type_is_invalid(value->value->type)) return ira->codegen->invalid_inst_gen; bool was_written = instruction->result_loc->written; IrInstGen *result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, value->value->type, value, false, true); if (result_loc != nullptr) { if (type_is_invalid(result_loc->value->type)) return ira->codegen->invalid_inst_gen; if (result_loc->value->type->id == ZigTypeIdUnreachable) return result_loc; if (!was_written || instruction->result_loc->id == ResultLocIdPeer) { IrInstGen *store_ptr = ir_analyze_store_ptr(ira, &instruction->base.base, result_loc, value, instruction->result_loc->allow_write_through_const); if (type_is_invalid(store_ptr->value->type)) { return ira->codegen->invalid_inst_gen; } } if (result_loc->value->data.x_ptr.mut == ConstPtrMutInfer && instruction->result_loc->id != ResultLocIdPeer) { if (instr_is_comptime(value)) { result_loc->value->data.x_ptr.mut = ConstPtrMutComptimeConst; } else { result_loc->value->special = ConstValSpecialRuntime; } } } return ir_const_void(ira, &instruction->base.base); } static IrInstGen *ir_analyze_instruction_implicit_cast(IrAnalyze *ira, IrInstSrcImplicitCast *instruction) { IrInstGen *operand = instruction->operand->child; if (type_is_invalid(operand->value->type)) return operand; ZigType *dest_type = ir_resolve_type(ira, instruction->result_loc_cast->base.source_instruction->child); if (type_is_invalid(dest_type)) return ira->codegen->invalid_inst_gen; return ir_implicit_cast2(ira, &instruction->base.base, operand, dest_type); } static IrInstGen *ir_analyze_instruction_bit_cast_src(IrAnalyze *ira, IrInstSrcBitCast *instruction) { IrInstGen *operand = instruction->operand->child; if (type_is_invalid(operand->value->type)) return operand; IrInstGen *result_loc = ir_resolve_result(ira, &instruction->base.base, &instruction->result_loc_bit_cast->base, operand->value->type, operand, false, true); if (result_loc != nullptr && (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable)) { return result_loc; } ZigType *dest_type = ir_resolve_type(ira, instruction->result_loc_bit_cast->base.source_instruction->child); if (type_is_invalid(dest_type)) return ira->codegen->invalid_inst_gen; return ir_analyze_bit_cast(ira, &instruction->base.base, operand, dest_type); } static IrInstGen *ir_analyze_instruction_union_init_named_field(IrAnalyze *ira, IrInstSrcUnionInitNamedField *instruction) { ZigType *union_type = ir_resolve_type(ira, instruction->union_type->child); if (type_is_invalid(union_type)) return ira->codegen->invalid_inst_gen; if (union_type->id != ZigTypeIdUnion) { ir_add_error(ira, &instruction->union_type->base, buf_sprintf("non-union type '%s' passed to @unionInit", buf_ptr(&union_type->name))); return ira->codegen->invalid_inst_gen; } Buf *field_name = ir_resolve_str(ira, instruction->field_name->child); if (field_name == nullptr) return ira->codegen->invalid_inst_gen; IrInstGen *field_result_loc = instruction->field_result_loc->child; if (type_is_invalid(field_result_loc->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *result_loc = instruction->result_loc->child; if (type_is_invalid(result_loc->value->type)) return ira->codegen->invalid_inst_gen; return ir_analyze_union_init(ira, &instruction->base.base, instruction->base.base.source_node, union_type, field_name, field_result_loc, result_loc); } static IrInstGen *ir_analyze_instruction_suspend_begin(IrAnalyze *ira, IrInstSrcSuspendBegin *instruction) { return ir_build_suspend_begin_gen(ira, &instruction->base.base); } static IrInstGen *ir_analyze_instruction_suspend_finish(IrAnalyze *ira, IrInstSrcSuspendFinish *instruction) { IrInstGen *begin_base = instruction->begin->base.child; if (type_is_invalid(begin_base->value->type)) return ira->codegen->invalid_inst_gen; ir_assert(begin_base->id == IrInstGenIdSuspendBegin, &instruction->base.base); IrInstGenSuspendBegin *begin = reinterpret_cast(begin_base); ZigFn *fn_entry = ira->new_irb.exec->fn_entry; ir_assert(fn_entry != nullptr, &instruction->base.base); if (fn_entry->inferred_async_node == nullptr) { fn_entry->inferred_async_node = instruction->base.base.source_node; } return ir_build_suspend_finish_gen(ira, &instruction->base.base, begin); } static IrInstGen *analyze_frame_ptr_to_anyframe_T(IrAnalyze *ira, IrInst* source_instr, IrInstGen *frame_ptr, ZigFn **target_fn) { if (type_is_invalid(frame_ptr->value->type)) return ira->codegen->invalid_inst_gen; *target_fn = nullptr; ZigType *result_type; IrInstGen *frame; if (frame_ptr->value->type->id == ZigTypeIdPointer && frame_ptr->value->type->data.pointer.ptr_len == PtrLenSingle && frame_ptr->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame) { ZigFn *func = frame_ptr->value->type->data.pointer.child_type->data.frame.fn; result_type = func->type_entry->data.fn.fn_type_id.return_type; *target_fn = func; frame = frame_ptr; } else { frame = ir_get_deref(ira, source_instr, frame_ptr, nullptr); if (frame->value->type->id == ZigTypeIdPointer && frame->value->type->data.pointer.ptr_len == PtrLenSingle && frame->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame) { ZigFn *func = frame->value->type->data.pointer.child_type->data.frame.fn; result_type = func->type_entry->data.fn.fn_type_id.return_type; *target_fn = func; } else if (frame->value->type->id != ZigTypeIdAnyFrame || frame->value->type->data.any_frame.result_type == nullptr) { ir_add_error(ira, source_instr, buf_sprintf("expected anyframe->T, found '%s'", buf_ptr(&frame->value->type->name))); return ira->codegen->invalid_inst_gen; } else { result_type = frame->value->type->data.any_frame.result_type; } } ZigType *any_frame_type = get_any_frame_type(ira->codegen, result_type); IrInstGen *casted_frame = ir_implicit_cast(ira, frame, any_frame_type); if (type_is_invalid(casted_frame->value->type)) return ira->codegen->invalid_inst_gen; return casted_frame; } static IrInstGen *ir_analyze_instruction_await(IrAnalyze *ira, IrInstSrcAwait *instruction) { IrInstGen *operand = instruction->frame->child; if (type_is_invalid(operand->value->type)) return ira->codegen->invalid_inst_gen; ZigFn *target_fn; IrInstGen *frame = analyze_frame_ptr_to_anyframe_T(ira, &instruction->base.base, operand, &target_fn); if (type_is_invalid(frame->value->type)) return ira->codegen->invalid_inst_gen; ZigType *result_type = frame->value->type->data.any_frame.result_type; ZigFn *fn_entry = ira->new_irb.exec->fn_entry; ir_assert(fn_entry != nullptr, &instruction->base.base); // If it's not @Frame(func) then it's definitely a suspend point if (target_fn == nullptr && !instruction->is_nosuspend) { if (fn_entry->inferred_async_node == nullptr) { fn_entry->inferred_async_node = instruction->base.base.source_node; } } if (type_can_fail(result_type)) { fn_entry->calls_or_awaits_errorable_fn = true; } IrInstGen *result_loc; if (type_has_bits(ira->codegen, result_type)) { result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, result_type, nullptr, true, true); if (result_loc != nullptr && (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable)) { return result_loc; } } else { result_loc = nullptr; } IrInstGenAwait *result = ir_build_await_gen(ira, &instruction->base.base, frame, result_type, result_loc, instruction->is_nosuspend); result->target_fn = target_fn; fn_entry->await_list.append(result); return ir_finish_anal(ira, &result->base); } static IrInstGen *ir_analyze_instruction_resume(IrAnalyze *ira, IrInstSrcResume *instruction) { IrInstGen *frame_ptr = instruction->frame->child; if (type_is_invalid(frame_ptr->value->type)) return ira->codegen->invalid_inst_gen; IrInstGen *frame; if (frame_ptr->value->type->id == ZigTypeIdPointer && frame_ptr->value->type->data.pointer.ptr_len == PtrLenSingle && frame_ptr->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame) { frame = frame_ptr; } else { frame = ir_get_deref(ira, &instruction->base.base, frame_ptr, nullptr); } ZigType *any_frame_type = get_any_frame_type(ira->codegen, nullptr); IrInstGen *casted_frame = ir_implicit_cast2(ira, &instruction->frame->base, frame, any_frame_type); if (type_is_invalid(casted_frame->value->type)) return ira->codegen->invalid_inst_gen; return ir_build_resume_gen(ira, &instruction->base.base, casted_frame); } static IrInstGen *ir_analyze_instruction_spill_begin(IrAnalyze *ira, IrInstSrcSpillBegin *instruction) { if (ir_should_inline(ira->old_irb.exec, instruction->base.base.scope)) return ir_const_void(ira, &instruction->base.base); IrInstGen *operand = instruction->operand->child; if (type_is_invalid(operand->value->type)) return ira->codegen->invalid_inst_gen; if (!type_has_bits(ira->codegen, operand->value->type)) return ir_const_void(ira, &instruction->base.base); switch (instruction->spill_id) { case SpillIdInvalid: zig_unreachable(); case SpillIdRetErrCode: ira->new_irb.exec->need_err_code_spill = true; break; } return ir_build_spill_begin_gen(ira, &instruction->base.base, operand, instruction->spill_id); } static IrInstGen *ir_analyze_instruction_spill_end(IrAnalyze *ira, IrInstSrcSpillEnd *instruction) { IrInstGen *operand = instruction->begin->operand->child; if (type_is_invalid(operand->value->type)) return ira->codegen->invalid_inst_gen; if (ir_should_inline(ira->old_irb.exec, instruction->base.base.scope) || !type_has_bits(ira->codegen, operand->value->type) || instr_is_comptime(operand)) { return operand; } ir_assert(instruction->begin->base.child->id == IrInstGenIdSpillBegin, &instruction->base.base); IrInstGenSpillBegin *begin = reinterpret_cast(instruction->begin->base.child); return ir_build_spill_end_gen(ira, &instruction->base.base, begin, operand->value->type); } static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruction) { switch (instruction->id) { case IrInstSrcIdInvalid: zig_unreachable(); case IrInstSrcIdReturn: return ir_analyze_instruction_return(ira, (IrInstSrcReturn *)instruction); case IrInstSrcIdConst: return ir_analyze_instruction_const(ira, (IrInstSrcConst *)instruction); case IrInstSrcIdUnOp: return ir_analyze_instruction_un_op(ira, (IrInstSrcUnOp *)instruction); case IrInstSrcIdBinOp: return ir_analyze_instruction_bin_op(ira, (IrInstSrcBinOp *)instruction); case IrInstSrcIdMergeErrSets: return ir_analyze_instruction_merge_err_sets(ira, (IrInstSrcMergeErrSets *)instruction); case IrInstSrcIdDeclVar: return ir_analyze_instruction_decl_var(ira, (IrInstSrcDeclVar *)instruction); case IrInstSrcIdLoadPtr: return ir_analyze_instruction_load_ptr(ira, (IrInstSrcLoadPtr *)instruction); case IrInstSrcIdStorePtr: return ir_analyze_instruction_store_ptr(ira, (IrInstSrcStorePtr *)instruction); case IrInstSrcIdElemPtr: return ir_analyze_instruction_elem_ptr(ira, (IrInstSrcElemPtr *)instruction); case IrInstSrcIdVarPtr: return ir_analyze_instruction_var_ptr(ira, (IrInstSrcVarPtr *)instruction); case IrInstSrcIdFieldPtr: return ir_analyze_instruction_field_ptr(ira, (IrInstSrcFieldPtr *)instruction); case IrInstSrcIdCall: return ir_analyze_instruction_call(ira, (IrInstSrcCall *)instruction); case IrInstSrcIdCallArgs: return ir_analyze_instruction_call_args(ira, (IrInstSrcCallArgs *)instruction); case IrInstSrcIdCallExtra: return ir_analyze_instruction_call_extra(ira, (IrInstSrcCallExtra *)instruction); case IrInstSrcIdBr: return ir_analyze_instruction_br(ira, (IrInstSrcBr *)instruction); case IrInstSrcIdCondBr: return ir_analyze_instruction_cond_br(ira, (IrInstSrcCondBr *)instruction); case IrInstSrcIdUnreachable: return ir_analyze_instruction_unreachable(ira, (IrInstSrcUnreachable *)instruction); case IrInstSrcIdPhi: return ir_analyze_instruction_phi(ira, (IrInstSrcPhi *)instruction); case IrInstSrcIdTypeOf: return ir_analyze_instruction_typeof(ira, (IrInstSrcTypeOf *)instruction); case IrInstSrcIdSetCold: return ir_analyze_instruction_set_cold(ira, (IrInstSrcSetCold *)instruction); case IrInstSrcIdSetRuntimeSafety: return ir_analyze_instruction_set_runtime_safety(ira, (IrInstSrcSetRuntimeSafety *)instruction); case IrInstSrcIdSetFloatMode: return ir_analyze_instruction_set_float_mode(ira, (IrInstSrcSetFloatMode *)instruction); case IrInstSrcIdAnyFrameType: return ir_analyze_instruction_any_frame_type(ira, (IrInstSrcAnyFrameType *)instruction); case IrInstSrcIdSliceType: return ir_analyze_instruction_slice_type(ira, (IrInstSrcSliceType *)instruction); case IrInstSrcIdAsm: return ir_analyze_instruction_asm(ira, (IrInstSrcAsm *)instruction); case IrInstSrcIdArrayType: return ir_analyze_instruction_array_type(ira, (IrInstSrcArrayType *)instruction); case IrInstSrcIdSizeOf: return ir_analyze_instruction_size_of(ira, (IrInstSrcSizeOf *)instruction); case IrInstSrcIdTestNonNull: return ir_analyze_instruction_test_non_null(ira, (IrInstSrcTestNonNull *)instruction); case IrInstSrcIdOptionalUnwrapPtr: return ir_analyze_instruction_optional_unwrap_ptr(ira, (IrInstSrcOptionalUnwrapPtr *)instruction); case IrInstSrcIdClz: return ir_analyze_instruction_clz(ira, (IrInstSrcClz *)instruction); case IrInstSrcIdCtz: return ir_analyze_instruction_ctz(ira, (IrInstSrcCtz *)instruction); case IrInstSrcIdPopCount: return ir_analyze_instruction_pop_count(ira, (IrInstSrcPopCount *)instruction); case IrInstSrcIdBswap: return ir_analyze_instruction_bswap(ira, (IrInstSrcBswap *)instruction); case IrInstSrcIdBitReverse: return ir_analyze_instruction_bit_reverse(ira, (IrInstSrcBitReverse *)instruction); case IrInstSrcIdSwitchBr: return ir_analyze_instruction_switch_br(ira, (IrInstSrcSwitchBr *)instruction); case IrInstSrcIdSwitchTarget: return ir_analyze_instruction_switch_target(ira, (IrInstSrcSwitchTarget *)instruction); case IrInstSrcIdSwitchVar: return ir_analyze_instruction_switch_var(ira, (IrInstSrcSwitchVar *)instruction); case IrInstSrcIdSwitchElseVar: return ir_analyze_instruction_switch_else_var(ira, (IrInstSrcSwitchElseVar *)instruction); case IrInstSrcIdImport: return ir_analyze_instruction_import(ira, (IrInstSrcImport *)instruction); case IrInstSrcIdRef: return ir_analyze_instruction_ref(ira, (IrInstSrcRef *)instruction); case IrInstSrcIdContainerInitList: return ir_analyze_instruction_container_init_list(ira, (IrInstSrcContainerInitList *)instruction); case IrInstSrcIdContainerInitFields: return ir_analyze_instruction_container_init_fields(ira, (IrInstSrcContainerInitFields *)instruction); case IrInstSrcIdCompileErr: return ir_analyze_instruction_compile_err(ira, (IrInstSrcCompileErr *)instruction); case IrInstSrcIdCompileLog: return ir_analyze_instruction_compile_log(ira, (IrInstSrcCompileLog *)instruction); case IrInstSrcIdErrName: return ir_analyze_instruction_err_name(ira, (IrInstSrcErrName *)instruction); case IrInstSrcIdTypeName: return ir_analyze_instruction_type_name(ira, (IrInstSrcTypeName *)instruction); case IrInstSrcIdCImport: return ir_analyze_instruction_c_import(ira, (IrInstSrcCImport *)instruction); case IrInstSrcIdCInclude: return ir_analyze_instruction_c_include(ira, (IrInstSrcCInclude *)instruction); case IrInstSrcIdCDefine: return ir_analyze_instruction_c_define(ira, (IrInstSrcCDefine *)instruction); case IrInstSrcIdCUndef: return ir_analyze_instruction_c_undef(ira, (IrInstSrcCUndef *)instruction); case IrInstSrcIdEmbedFile: return ir_analyze_instruction_embed_file(ira, (IrInstSrcEmbedFile *)instruction); case IrInstSrcIdCmpxchg: return ir_analyze_instruction_cmpxchg(ira, (IrInstSrcCmpxchg *)instruction); case IrInstSrcIdFence: return ir_analyze_instruction_fence(ira, (IrInstSrcFence *)instruction); case IrInstSrcIdTruncate: return ir_analyze_instruction_truncate(ira, (IrInstSrcTruncate *)instruction); case IrInstSrcIdIntCast: return ir_analyze_instruction_int_cast(ira, (IrInstSrcIntCast *)instruction); case IrInstSrcIdFloatCast: return ir_analyze_instruction_float_cast(ira, (IrInstSrcFloatCast *)instruction); case IrInstSrcIdErrSetCast: return ir_analyze_instruction_err_set_cast(ira, (IrInstSrcErrSetCast *)instruction); case IrInstSrcIdIntToFloat: return ir_analyze_instruction_int_to_float(ira, (IrInstSrcIntToFloat *)instruction); case IrInstSrcIdFloatToInt: return ir_analyze_instruction_float_to_int(ira, (IrInstSrcFloatToInt *)instruction); case IrInstSrcIdBoolToInt: return ir_analyze_instruction_bool_to_int(ira, (IrInstSrcBoolToInt *)instruction); case IrInstSrcIdVectorType: return ir_analyze_instruction_vector_type(ira, (IrInstSrcVectorType *)instruction); case IrInstSrcIdShuffleVector: return ir_analyze_instruction_shuffle_vector(ira, (IrInstSrcShuffleVector *)instruction); case IrInstSrcIdSplat: return ir_analyze_instruction_splat(ira, (IrInstSrcSplat *)instruction); case IrInstSrcIdBoolNot: return ir_analyze_instruction_bool_not(ira, (IrInstSrcBoolNot *)instruction); case IrInstSrcIdMemset: return ir_analyze_instruction_memset(ira, (IrInstSrcMemset *)instruction); case IrInstSrcIdMemcpy: return ir_analyze_instruction_memcpy(ira, (IrInstSrcMemcpy *)instruction); case IrInstSrcIdSlice: return ir_analyze_instruction_slice(ira, (IrInstSrcSlice *)instruction); case IrInstSrcIdBreakpoint: return ir_analyze_instruction_breakpoint(ira, (IrInstSrcBreakpoint *)instruction); case IrInstSrcIdReturnAddress: return ir_analyze_instruction_return_address(ira, (IrInstSrcReturnAddress *)instruction); case IrInstSrcIdFrameAddress: return ir_analyze_instruction_frame_address(ira, (IrInstSrcFrameAddress *)instruction); case IrInstSrcIdFrameHandle: return ir_analyze_instruction_frame_handle(ira, (IrInstSrcFrameHandle *)instruction); case IrInstSrcIdFrameType: return ir_analyze_instruction_frame_type(ira, (IrInstSrcFrameType *)instruction); case IrInstSrcIdFrameSize: return ir_analyze_instruction_frame_size(ira, (IrInstSrcFrameSize *)instruction); case IrInstSrcIdAlignOf: return ir_analyze_instruction_align_of(ira, (IrInstSrcAlignOf *)instruction); case IrInstSrcIdOverflowOp: return ir_analyze_instruction_overflow_op(ira, (IrInstSrcOverflowOp *)instruction); case IrInstSrcIdTestErr: return ir_analyze_instruction_test_err(ira, (IrInstSrcTestErr *)instruction); case IrInstSrcIdUnwrapErrCode: return ir_analyze_instruction_unwrap_err_code(ira, (IrInstSrcUnwrapErrCode *)instruction); case IrInstSrcIdUnwrapErrPayload: return ir_analyze_instruction_unwrap_err_payload(ira, (IrInstSrcUnwrapErrPayload *)instruction); case IrInstSrcIdFnProto: return ir_analyze_instruction_fn_proto(ira, (IrInstSrcFnProto *)instruction); case IrInstSrcIdTestComptime: return ir_analyze_instruction_test_comptime(ira, (IrInstSrcTestComptime *)instruction); case IrInstSrcIdCheckSwitchProngs: return ir_analyze_instruction_check_switch_prongs(ira, (IrInstSrcCheckSwitchProngs *)instruction); case IrInstSrcIdCheckStatementIsVoid: return ir_analyze_instruction_check_statement_is_void(ira, (IrInstSrcCheckStatementIsVoid *)instruction); case IrInstSrcIdDeclRef: return ir_analyze_instruction_decl_ref(ira, (IrInstSrcDeclRef *)instruction); case IrInstSrcIdPanic: return ir_analyze_instruction_panic(ira, (IrInstSrcPanic *)instruction); case IrInstSrcIdPtrCast: return ir_analyze_instruction_ptr_cast(ira, (IrInstSrcPtrCast *)instruction); case IrInstSrcIdIntToPtr: return ir_analyze_instruction_int_to_ptr(ira, (IrInstSrcIntToPtr *)instruction); case IrInstSrcIdPtrToInt: return ir_analyze_instruction_ptr_to_int(ira, (IrInstSrcPtrToInt *)instruction); case IrInstSrcIdTagName: return ir_analyze_instruction_enum_tag_name(ira, (IrInstSrcTagName *)instruction); case IrInstSrcIdFieldParentPtr: return ir_analyze_instruction_field_parent_ptr(ira, (IrInstSrcFieldParentPtr *)instruction); case IrInstSrcIdByteOffsetOf: return ir_analyze_instruction_byte_offset_of(ira, (IrInstSrcByteOffsetOf *)instruction); case IrInstSrcIdBitOffsetOf: return ir_analyze_instruction_bit_offset_of(ira, (IrInstSrcBitOffsetOf *)instruction); case IrInstSrcIdTypeInfo: return ir_analyze_instruction_type_info(ira, (IrInstSrcTypeInfo *) instruction); case IrInstSrcIdType: return ir_analyze_instruction_type(ira, (IrInstSrcType *)instruction); case IrInstSrcIdHasField: return ir_analyze_instruction_has_field(ira, (IrInstSrcHasField *) instruction); case IrInstSrcIdSetEvalBranchQuota: return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstSrcSetEvalBranchQuota *)instruction); case IrInstSrcIdPtrType: return ir_analyze_instruction_ptr_type(ira, (IrInstSrcPtrType *)instruction); case IrInstSrcIdAlignCast: return ir_analyze_instruction_align_cast(ira, (IrInstSrcAlignCast *)instruction); case IrInstSrcIdImplicitCast: return ir_analyze_instruction_implicit_cast(ira, (IrInstSrcImplicitCast *)instruction); case IrInstSrcIdResolveResult: return ir_analyze_instruction_resolve_result(ira, (IrInstSrcResolveResult *)instruction); case IrInstSrcIdResetResult: return ir_analyze_instruction_reset_result(ira, (IrInstSrcResetResult *)instruction); case IrInstSrcIdOpaqueType: return ir_analyze_instruction_opaque_type(ira, (IrInstSrcOpaqueType *)instruction); case IrInstSrcIdSetAlignStack: return ir_analyze_instruction_set_align_stack(ira, (IrInstSrcSetAlignStack *)instruction); case IrInstSrcIdArgType: return ir_analyze_instruction_arg_type(ira, (IrInstSrcArgType *)instruction); case IrInstSrcIdTagType: return ir_analyze_instruction_tag_type(ira, (IrInstSrcTagType *)instruction); case IrInstSrcIdExport: return ir_analyze_instruction_export(ira, (IrInstSrcExport *)instruction); case IrInstSrcIdErrorReturnTrace: return ir_analyze_instruction_error_return_trace(ira, (IrInstSrcErrorReturnTrace *)instruction); case IrInstSrcIdErrorUnion: return ir_analyze_instruction_error_union(ira, (IrInstSrcErrorUnion *)instruction); case IrInstSrcIdAtomicRmw: return ir_analyze_instruction_atomic_rmw(ira, (IrInstSrcAtomicRmw *)instruction); case IrInstSrcIdAtomicLoad: return ir_analyze_instruction_atomic_load(ira, (IrInstSrcAtomicLoad *)instruction); case IrInstSrcIdAtomicStore: return ir_analyze_instruction_atomic_store(ira, (IrInstSrcAtomicStore *)instruction); case IrInstSrcIdSaveErrRetAddr: return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstSrcSaveErrRetAddr *)instruction); case IrInstSrcIdAddImplicitReturnType: return ir_analyze_instruction_add_implicit_return_type(ira, (IrInstSrcAddImplicitReturnType *)instruction); case IrInstSrcIdFloatOp: return ir_analyze_instruction_float_op(ira, (IrInstSrcFloatOp *)instruction); case IrInstSrcIdMulAdd: return ir_analyze_instruction_mul_add(ira, (IrInstSrcMulAdd *)instruction); case IrInstSrcIdIntToErr: return ir_analyze_instruction_int_to_err(ira, (IrInstSrcIntToErr *)instruction); case IrInstSrcIdErrToInt: return ir_analyze_instruction_err_to_int(ira, (IrInstSrcErrToInt *)instruction); case IrInstSrcIdIntToEnum: return ir_analyze_instruction_int_to_enum(ira, (IrInstSrcIntToEnum *)instruction); case IrInstSrcIdEnumToInt: return ir_analyze_instruction_enum_to_int(ira, (IrInstSrcEnumToInt *)instruction); case IrInstSrcIdCheckRuntimeScope: return ir_analyze_instruction_check_runtime_scope(ira, (IrInstSrcCheckRuntimeScope *)instruction); case IrInstSrcIdHasDecl: return ir_analyze_instruction_has_decl(ira, (IrInstSrcHasDecl *)instruction); case IrInstSrcIdUndeclaredIdent: return ir_analyze_instruction_undeclared_ident(ira, (IrInstSrcUndeclaredIdent *)instruction); case IrInstSrcIdAlloca: return nullptr; case IrInstSrcIdEndExpr: return ir_analyze_instruction_end_expr(ira, (IrInstSrcEndExpr *)instruction); case IrInstSrcIdBitCast: return ir_analyze_instruction_bit_cast_src(ira, (IrInstSrcBitCast *)instruction); case IrInstSrcIdUnionInitNamedField: return ir_analyze_instruction_union_init_named_field(ira, (IrInstSrcUnionInitNamedField *)instruction); case IrInstSrcIdSuspendBegin: return ir_analyze_instruction_suspend_begin(ira, (IrInstSrcSuspendBegin *)instruction); case IrInstSrcIdSuspendFinish: return ir_analyze_instruction_suspend_finish(ira, (IrInstSrcSuspendFinish *)instruction); case IrInstSrcIdResume: return ir_analyze_instruction_resume(ira, (IrInstSrcResume *)instruction); case IrInstSrcIdAwait: return ir_analyze_instruction_await(ira, (IrInstSrcAwait *)instruction); case IrInstSrcIdSpillBegin: return ir_analyze_instruction_spill_begin(ira, (IrInstSrcSpillBegin *)instruction); case IrInstSrcIdSpillEnd: return ir_analyze_instruction_spill_end(ira, (IrInstSrcSpillEnd *)instruction); case IrInstSrcIdWasmMemorySize: return ir_analyze_instruction_wasm_memory_size(ira, (IrInstSrcWasmMemorySize *)instruction); case IrInstSrcIdWasmMemoryGrow: return ir_analyze_instruction_wasm_memory_grow(ira, (IrInstSrcWasmMemoryGrow *)instruction); } zig_unreachable(); } // This function attempts to evaluate IR code while doing type checking and other analysis. // It emits to a new IrExecutableGen which is partially evaluated IR code. ZigType *ir_analyze(CodeGen *codegen, IrExecutableSrc *old_exec, IrExecutableGen *new_exec, ZigType *expected_type, AstNode *expected_type_source_node, ZigValue *result_ptr) { assert(old_exec->first_err_trace_msg == nullptr); assert(expected_type == nullptr || !type_is_invalid(expected_type)); IrAnalyze *ira = heap::c_allocator.create(); ira->ref_count = 1; old_exec->analysis = ira; ira->codegen = codegen; ira->explicit_return_type = expected_type; ira->explicit_return_type_source_node = expected_type_source_node; ira->old_irb.codegen = codegen; ira->old_irb.exec = old_exec; ira->new_irb.codegen = codegen; ira->new_irb.exec = new_exec; IrBasicBlockSrc *old_entry_bb = ira->old_irb.exec->basic_block_list.at(0); IrBasicBlockGen *new_entry_bb = ir_get_new_bb(ira, old_entry_bb, nullptr); ira->new_irb.current_basic_block = new_entry_bb; ira->old_bb_index = 0; ir_start_bb(ira, old_entry_bb, nullptr); if (result_ptr != nullptr) { assert(result_ptr->type->id == ZigTypeIdPointer); IrInstGenConst *const_inst = ir_create_inst_noval( &ira->new_irb, new_exec->begin_scope, new_exec->source_node); const_inst->base.value = result_ptr; ira->return_ptr = &const_inst->base; } else { assert(new_exec->begin_scope != nullptr); assert(new_exec->source_node != nullptr); ira->return_ptr = ir_build_return_ptr(ira, new_exec->begin_scope, new_exec->source_node, get_pointer_to_type(codegen, expected_type, false)); } while (ira->old_bb_index < ira->old_irb.exec->basic_block_list.length) { IrInstSrc *old_instruction = ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index); if (old_instruction->base.ref_count == 0 && !ir_inst_src_has_side_effects(old_instruction)) { ira->instruction_index += 1; continue; } if (ira->codegen->verbose_ir) { fprintf(stderr, "~ "); old_instruction->src(); fprintf(stderr, "~ "); ir_print_inst_src(codegen, stderr, old_instruction, 0); bool want_break = false; if (ira->break_debug_id == old_instruction->base.debug_id) { want_break = true; } else if (old_instruction->base.source_node != nullptr) { for (size_t i = 0; i < dbg_ir_breakpoints_count; i += 1) { if (dbg_ir_breakpoints_buf[i].line == old_instruction->base.source_node->line + 1 && buf_ends_with_str(old_instruction->base.source_node->owner->data.structure.root_struct->path, dbg_ir_breakpoints_buf[i].src_file)) { want_break = true; } } } if (want_break) BREAKPOINT; } IrInstGen *new_instruction = ir_analyze_instruction_base(ira, old_instruction); if (new_instruction != nullptr) { ir_assert(new_instruction->value->type != nullptr || new_instruction->value->type != nullptr, &old_instruction->base); old_instruction->child = new_instruction; if (type_is_invalid(new_instruction->value->type)) { if (ira->codegen->verbose_ir) { fprintf(stderr, "-> (invalid)"); } if (new_exec->first_err_trace_msg != nullptr) { ira->codegen->trace_err = new_exec->first_err_trace_msg; } else { new_exec->first_err_trace_msg = ira->codegen->trace_err; } if (new_exec->first_err_trace_msg != nullptr && !old_instruction->base.source_node->already_traced_this_node) { old_instruction->base.source_node->already_traced_this_node = true; new_exec->first_err_trace_msg = add_error_note(ira->codegen, new_exec->first_err_trace_msg, old_instruction->base.source_node, buf_create_from_str("referenced here")); } return ira->codegen->builtin_types.entry_invalid; } else if (ira->codegen->verbose_ir) { fprintf(stderr, "-> "); if (new_instruction->value->type->id == ZigTypeIdUnreachable) { fprintf(stderr, "(noreturn)\n"); } else { ir_print_inst_gen(codegen, stderr, new_instruction, 0); } } // unreachable instructions do their own control flow. if (new_instruction->value->type->id == ZigTypeIdUnreachable) continue; } else { if (ira->codegen->verbose_ir) { fprintf(stderr, "-> (null"); } } ira->instruction_index += 1; } ZigType *res_type; if (new_exec->first_err_trace_msg != nullptr) { codegen->trace_err = new_exec->first_err_trace_msg; if (codegen->trace_err != nullptr && new_exec->source_node != nullptr && !new_exec->source_node->already_traced_this_node) { new_exec->source_node->already_traced_this_node = true; codegen->trace_err = add_error_note(codegen, codegen->trace_err, new_exec->source_node, buf_create_from_str("referenced here")); } res_type = ira->codegen->builtin_types.entry_invalid; } else if (ira->src_implicit_return_type_list.length == 0) { res_type = codegen->builtin_types.entry_unreachable; } else { res_type = ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items, ira->src_implicit_return_type_list.length); } // It is now safe to free Pass 1 IR instructions. ira_deref(ira); return res_type; } bool ir_inst_gen_has_side_effects(IrInstGen *instruction) { switch (instruction->id) { case IrInstGenIdInvalid: zig_unreachable(); case IrInstGenIdBr: case IrInstGenIdCondBr: case IrInstGenIdSwitchBr: case IrInstGenIdDeclVar: case IrInstGenIdStorePtr: case IrInstGenIdVectorStoreElem: case IrInstGenIdCall: case IrInstGenIdReturn: case IrInstGenIdUnreachable: case IrInstGenIdFence: case IrInstGenIdMemset: case IrInstGenIdMemcpy: case IrInstGenIdBreakpoint: case IrInstGenIdOverflowOp: // TODO when we support multiple returns this can be side effect free case IrInstGenIdPanic: case IrInstGenIdSaveErrRetAddr: case IrInstGenIdAtomicRmw: case IrInstGenIdAtomicStore: case IrInstGenIdCmpxchg: case IrInstGenIdAssertZero: case IrInstGenIdAssertNonNull: case IrInstGenIdPtrOfArrayToSlice: case IrInstGenIdSlice: case IrInstGenIdOptionalWrap: case IrInstGenIdVectorToArray: case IrInstGenIdSuspendBegin: case IrInstGenIdSuspendFinish: case IrInstGenIdResume: case IrInstGenIdAwait: case IrInstGenIdSpillBegin: case IrInstGenIdWasmMemoryGrow: return true; case IrInstGenIdPhi: case IrInstGenIdBinOp: case IrInstGenIdConst: case IrInstGenIdCast: case IrInstGenIdElemPtr: case IrInstGenIdVarPtr: case IrInstGenIdReturnPtr: case IrInstGenIdStructFieldPtr: case IrInstGenIdTestNonNull: case IrInstGenIdClz: case IrInstGenIdCtz: case IrInstGenIdPopCount: case IrInstGenIdBswap: case IrInstGenIdBitReverse: case IrInstGenIdUnionTag: case IrInstGenIdTruncate: case IrInstGenIdShuffleVector: case IrInstGenIdSplat: case IrInstGenIdBoolNot: case IrInstGenIdReturnAddress: case IrInstGenIdFrameAddress: case IrInstGenIdFrameHandle: case IrInstGenIdFrameSize: case IrInstGenIdTestErr: case IrInstGenIdPtrCast: case IrInstGenIdBitCast: case IrInstGenIdWidenOrShorten: case IrInstGenIdPtrToInt: case IrInstGenIdIntToPtr: case IrInstGenIdIntToEnum: case IrInstGenIdIntToErr: case IrInstGenIdErrToInt: case IrInstGenIdErrName: case IrInstGenIdTagName: case IrInstGenIdFieldParentPtr: case IrInstGenIdAlignCast: case IrInstGenIdErrorReturnTrace: case IrInstGenIdFloatOp: case IrInstGenIdMulAdd: case IrInstGenIdAtomicLoad: case IrInstGenIdArrayToVector: case IrInstGenIdAlloca: case IrInstGenIdSpillEnd: case IrInstGenIdVectorExtractElem: case IrInstGenIdBinaryNot: case IrInstGenIdNegation: case IrInstGenIdNegationWrapping: case IrInstGenIdWasmMemorySize: return false; case IrInstGenIdAsm: { IrInstGenAsm *asm_instruction = (IrInstGenAsm *)instruction; return asm_instruction->has_side_effects; } case IrInstGenIdUnwrapErrPayload: { IrInstGenUnwrapErrPayload *unwrap_err_payload_instruction = (IrInstGenUnwrapErrPayload *)instruction; return unwrap_err_payload_instruction->safety_check_on || unwrap_err_payload_instruction->initializing; } case IrInstGenIdUnwrapErrCode: return reinterpret_cast(instruction)->initializing; case IrInstGenIdUnionFieldPtr: return reinterpret_cast(instruction)->initializing; case IrInstGenIdOptionalUnwrapPtr: return reinterpret_cast(instruction)->initializing; case IrInstGenIdErrWrapPayload: return reinterpret_cast(instruction)->result_loc != nullptr; case IrInstGenIdErrWrapCode: return reinterpret_cast(instruction)->result_loc != nullptr; case IrInstGenIdLoadPtr: return reinterpret_cast(instruction)->result_loc != nullptr; case IrInstGenIdRef: return reinterpret_cast(instruction)->result_loc != nullptr; } zig_unreachable(); } bool ir_inst_src_has_side_effects(IrInstSrc *instruction) { switch (instruction->id) { case IrInstSrcIdInvalid: zig_unreachable(); case IrInstSrcIdBr: case IrInstSrcIdCondBr: case IrInstSrcIdSwitchBr: case IrInstSrcIdDeclVar: case IrInstSrcIdStorePtr: case IrInstSrcIdCallExtra: case IrInstSrcIdCall: case IrInstSrcIdCallArgs: case IrInstSrcIdReturn: case IrInstSrcIdUnreachable: case IrInstSrcIdSetCold: case IrInstSrcIdSetRuntimeSafety: case IrInstSrcIdSetFloatMode: case IrInstSrcIdImport: case IrInstSrcIdCompileErr: case IrInstSrcIdCompileLog: case IrInstSrcIdCImport: case IrInstSrcIdCInclude: case IrInstSrcIdCDefine: case IrInstSrcIdCUndef: case IrInstSrcIdFence: case IrInstSrcIdMemset: case IrInstSrcIdMemcpy: case IrInstSrcIdBreakpoint: case IrInstSrcIdOverflowOp: // TODO when we support multiple returns this can be side effect free case IrInstSrcIdCheckSwitchProngs: case IrInstSrcIdCheckStatementIsVoid: case IrInstSrcIdCheckRuntimeScope: case IrInstSrcIdPanic: case IrInstSrcIdSetEvalBranchQuota: case IrInstSrcIdPtrType: case IrInstSrcIdSetAlignStack: case IrInstSrcIdExport: case IrInstSrcIdSaveErrRetAddr: case IrInstSrcIdAddImplicitReturnType: case IrInstSrcIdAtomicRmw: case IrInstSrcIdAtomicStore: case IrInstSrcIdCmpxchg: case IrInstSrcIdUndeclaredIdent: case IrInstSrcIdEndExpr: case IrInstSrcIdResetResult: case IrInstSrcIdSuspendBegin: case IrInstSrcIdSuspendFinish: case IrInstSrcIdResume: case IrInstSrcIdAwait: case IrInstSrcIdSpillBegin: case IrInstSrcIdWasmMemoryGrow: return true; case IrInstSrcIdPhi: case IrInstSrcIdUnOp: case IrInstSrcIdBinOp: case IrInstSrcIdMergeErrSets: case IrInstSrcIdLoadPtr: case IrInstSrcIdConst: case IrInstSrcIdContainerInitList: case IrInstSrcIdContainerInitFields: case IrInstSrcIdUnionInitNamedField: case IrInstSrcIdFieldPtr: case IrInstSrcIdElemPtr: case IrInstSrcIdVarPtr: case IrInstSrcIdTypeOf: case IrInstSrcIdArrayType: case IrInstSrcIdSliceType: case IrInstSrcIdAnyFrameType: case IrInstSrcIdSizeOf: case IrInstSrcIdTestNonNull: case IrInstSrcIdOptionalUnwrapPtr: case IrInstSrcIdClz: case IrInstSrcIdCtz: case IrInstSrcIdPopCount: case IrInstSrcIdBswap: case IrInstSrcIdBitReverse: case IrInstSrcIdSwitchVar: case IrInstSrcIdSwitchElseVar: case IrInstSrcIdSwitchTarget: case IrInstSrcIdRef: case IrInstSrcIdEmbedFile: case IrInstSrcIdTruncate: case IrInstSrcIdVectorType: case IrInstSrcIdShuffleVector: case IrInstSrcIdSplat: case IrInstSrcIdBoolNot: case IrInstSrcIdSlice: case IrInstSrcIdAlignOf: case IrInstSrcIdReturnAddress: case IrInstSrcIdFrameAddress: case IrInstSrcIdFrameHandle: case IrInstSrcIdFrameType: case IrInstSrcIdFrameSize: case IrInstSrcIdTestErr: case IrInstSrcIdFnProto: case IrInstSrcIdTestComptime: case IrInstSrcIdPtrCast: case IrInstSrcIdBitCast: case IrInstSrcIdPtrToInt: case IrInstSrcIdIntToPtr: case IrInstSrcIdIntToEnum: case IrInstSrcIdIntToErr: case IrInstSrcIdErrToInt: case IrInstSrcIdDeclRef: case IrInstSrcIdErrName: case IrInstSrcIdTypeName: case IrInstSrcIdTagName: case IrInstSrcIdFieldParentPtr: case IrInstSrcIdByteOffsetOf: case IrInstSrcIdBitOffsetOf: case IrInstSrcIdTypeInfo: case IrInstSrcIdType: case IrInstSrcIdHasField: case IrInstSrcIdAlignCast: case IrInstSrcIdImplicitCast: case IrInstSrcIdResolveResult: case IrInstSrcIdOpaqueType: case IrInstSrcIdArgType: case IrInstSrcIdTagType: case IrInstSrcIdErrorReturnTrace: case IrInstSrcIdErrorUnion: case IrInstSrcIdFloatOp: case IrInstSrcIdMulAdd: case IrInstSrcIdAtomicLoad: case IrInstSrcIdIntCast: case IrInstSrcIdFloatCast: case IrInstSrcIdErrSetCast: case IrInstSrcIdIntToFloat: case IrInstSrcIdFloatToInt: case IrInstSrcIdBoolToInt: case IrInstSrcIdEnumToInt: case IrInstSrcIdHasDecl: case IrInstSrcIdAlloca: case IrInstSrcIdSpillEnd: case IrInstSrcIdWasmMemorySize: return false; case IrInstSrcIdAsm: { IrInstSrcAsm *asm_instruction = (IrInstSrcAsm *)instruction; return asm_instruction->has_side_effects; } case IrInstSrcIdUnwrapErrPayload: { IrInstSrcUnwrapErrPayload *unwrap_err_payload_instruction = (IrInstSrcUnwrapErrPayload *)instruction; return unwrap_err_payload_instruction->safety_check_on || unwrap_err_payload_instruction->initializing; } case IrInstSrcIdUnwrapErrCode: return reinterpret_cast(instruction)->initializing; } zig_unreachable(); } static ZigType *ir_resolve_lazy_fn_type(IrAnalyze *ira, AstNode *source_node, LazyValueFnType *lazy_fn_type) { Error err; AstNode *proto_node = lazy_fn_type->proto_node; FnTypeId fn_type_id = {0}; init_fn_type_id(&fn_type_id, proto_node, lazy_fn_type->cc, proto_node->data.fn_proto.params.length); for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) { AstNode *param_node = proto_node->data.fn_proto.params.at(fn_type_id.next_param_index); assert(param_node->type == NodeTypeParamDecl); bool param_is_var_args = param_node->data.param_decl.is_var_args; if (param_is_var_args) { if (fn_type_id.cc == CallingConventionC) { fn_type_id.param_count = fn_type_id.next_param_index; break; } else { ir_add_error_node(ira, param_node, buf_sprintf("var args only allowed in functions with C calling convention")); return nullptr; } } FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index]; param_info->is_noalias = param_node->data.param_decl.is_noalias; if (lazy_fn_type->param_types[fn_type_id.next_param_index] == nullptr) { param_info->type = nullptr; return get_generic_fn_type(ira->codegen, &fn_type_id); } else { IrInstGen *param_type_inst = lazy_fn_type->param_types[fn_type_id.next_param_index]; ZigType *param_type = ir_resolve_type(ira, param_type_inst); if (type_is_invalid(param_type)) return nullptr; if(!is_valid_param_type(param_type)){ if(param_type->id == ZigTypeIdOpaque){ ir_add_error(ira, ¶m_type_inst->base, buf_sprintf("parameter of opaque type '%s' not allowed", buf_ptr(¶m_type->name))); } else { ir_add_error(ira, ¶m_type_inst->base, buf_sprintf("parameter of type '%s' not allowed", buf_ptr(¶m_type->name))); } return nullptr; } switch (type_requires_comptime(ira->codegen, param_type)) { case ReqCompTimeYes: if (!calling_convention_allows_zig_types(fn_type_id.cc)) { ir_add_error(ira, ¶m_type_inst->base, 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 nullptr; } param_info->type = param_type; fn_type_id.next_param_index += 1; return get_generic_fn_type(ira->codegen, &fn_type_id); case ReqCompTimeInvalid: return nullptr; case ReqCompTimeNo: break; } if (!calling_convention_allows_zig_types(fn_type_id.cc)) { bool has_bits; if ((err = type_has_bits2(ira->codegen, param_type, &has_bits))) return nullptr; if (!has_bits) { ir_add_error(ira, ¶m_type_inst->base, 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 nullptr; } } param_info->type = param_type; } } if (lazy_fn_type->align_inst != nullptr) { if (!ir_resolve_align(ira, lazy_fn_type->align_inst, nullptr, &fn_type_id.alignment)) return nullptr; } fn_type_id.return_type = ir_resolve_type(ira, lazy_fn_type->return_type); if (type_is_invalid(fn_type_id.return_type)) return nullptr; if (fn_type_id.return_type->id == ZigTypeIdOpaque) { ir_add_error(ira, &lazy_fn_type->return_type->base, buf_create_from_str("return type cannot be opaque")); return nullptr; } return get_fn_type(ira->codegen, &fn_type_id); } static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) { Error err; if (val->special != ConstValSpecialLazy) return ErrorNone; switch (val->data.x_lazy->id) { case LazyValueIdInvalid: zig_unreachable(); case LazyValueIdTypeInfoDecls: { LazyValueTypeInfoDecls *type_info_decls = reinterpret_cast(val->data.x_lazy); IrAnalyze *ira = type_info_decls->ira; if ((err = ir_make_type_info_decls(ira, type_info_decls->source_instr, val, type_info_decls->decls_scope, true))) { return err; }; // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } case LazyValueIdAlignOf: { LazyValueAlignOf *lazy_align_of = reinterpret_cast(val->data.x_lazy); IrAnalyze *ira = lazy_align_of->ira; if (lazy_align_of->target_type->value->special == ConstValSpecialStatic) { switch (lazy_align_of->target_type->value->data.x_type->id) { case ZigTypeIdInvalid: zig_unreachable(); case ZigTypeIdMetaType: case ZigTypeIdUnreachable: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdEnumLiteral: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdBoundFn: case ZigTypeIdVoid: case ZigTypeIdOpaque: ir_add_error(ira, &lazy_align_of->target_type->base, buf_sprintf("no align available for type '%s'", buf_ptr(&lazy_align_of->target_type->value->data.x_type->name))); return ErrorSemanticAnalyzeFail; case ZigTypeIdBool: case ZigTypeIdInt: case ZigTypeIdFloat: case ZigTypeIdPointer: case ZigTypeIdArray: case ZigTypeIdStruct: case ZigTypeIdOptional: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdEnum: case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdVector: case ZigTypeIdFnFrame: case ZigTypeIdAnyFrame: break; } } uint32_t align_in_bytes; if ((err = type_val_resolve_abi_align(ira->codegen, source_node, lazy_align_of->target_type->value, &align_in_bytes))) { return err; } val->special = ConstValSpecialStatic; assert(val->type->id == ZigTypeIdComptimeInt || val->type->id == ZigTypeIdInt); bigint_init_unsigned(&val->data.x_bigint, align_in_bytes); // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } case LazyValueIdSizeOf: { LazyValueSizeOf *lazy_size_of = reinterpret_cast(val->data.x_lazy); IrAnalyze *ira = lazy_size_of->ira; if (lazy_size_of->target_type->value->special == ConstValSpecialStatic) { switch (lazy_size_of->target_type->value->data.x_type->id) { case ZigTypeIdInvalid: // handled above zig_unreachable(); case ZigTypeIdUnreachable: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdBoundFn: case ZigTypeIdOpaque: ir_add_error(ira, &lazy_size_of->target_type->base, buf_sprintf("no size available for type '%s'", buf_ptr(&lazy_size_of->target_type->value->data.x_type->name))); return ErrorSemanticAnalyzeFail; case ZigTypeIdMetaType: case ZigTypeIdEnumLiteral: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdVoid: case ZigTypeIdBool: case ZigTypeIdInt: case ZigTypeIdFloat: case ZigTypeIdPointer: case ZigTypeIdArray: case ZigTypeIdStruct: case ZigTypeIdOptional: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdEnum: case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdVector: case ZigTypeIdFnFrame: case ZigTypeIdAnyFrame: break; } } size_t abi_size; size_t size_in_bits; if ((err = type_val_resolve_abi_size(ira->codegen, source_node, lazy_size_of->target_type->value, &abi_size, &size_in_bits))) { return err; } val->special = ConstValSpecialStatic; assert(val->type->id == ZigTypeIdComptimeInt || val->type->id == ZigTypeIdInt); if (lazy_size_of->bit_size) bigint_init_unsigned(&val->data.x_bigint, size_in_bits); else bigint_init_unsigned(&val->data.x_bigint, abi_size); // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } case LazyValueIdSliceType: { LazyValueSliceType *lazy_slice_type = reinterpret_cast(val->data.x_lazy); IrAnalyze *ira = lazy_slice_type->ira; ZigType *elem_type = ir_resolve_type(ira, lazy_slice_type->elem_type); if (type_is_invalid(elem_type)) return ErrorSemanticAnalyzeFail; ZigValue *sentinel_val; if (lazy_slice_type->sentinel != nullptr) { if (type_is_invalid(lazy_slice_type->sentinel->value->type)) return ErrorSemanticAnalyzeFail; IrInstGen *sentinel = ir_implicit_cast(ira, lazy_slice_type->sentinel, elem_type); if (type_is_invalid(sentinel->value->type)) return ErrorSemanticAnalyzeFail; sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); if (sentinel_val == nullptr) return ErrorSemanticAnalyzeFail; } else { sentinel_val = nullptr; } uint32_t align_bytes = 0; if (lazy_slice_type->align_inst != nullptr) { if (!ir_resolve_align(ira, lazy_slice_type->align_inst, elem_type, &align_bytes)) return ErrorSemanticAnalyzeFail; } switch (elem_type->id) { case ZigTypeIdInvalid: // handled above zig_unreachable(); case ZigTypeIdUnreachable: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdOpaque: ir_add_error(ira, &lazy_slice_type->elem_type->base, buf_sprintf("slice of type '%s' not allowed", buf_ptr(&elem_type->name))); return ErrorSemanticAnalyzeFail; case ZigTypeIdMetaType: case ZigTypeIdVoid: case ZigTypeIdBool: case ZigTypeIdInt: case ZigTypeIdFloat: case ZigTypeIdPointer: case ZigTypeIdArray: case ZigTypeIdStruct: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdEnumLiteral: case ZigTypeIdOptional: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdEnum: case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdBoundFn: case ZigTypeIdVector: case ZigTypeIdFnFrame: case ZigTypeIdAnyFrame: break; } ResolveStatus needed_status = (align_bytes == 0) ? ResolveStatusZeroBitsKnown : ResolveStatusAlignmentKnown; if ((err = type_resolve(ira->codegen, elem_type, needed_status))) return err; ZigType *slice_ptr_type = get_pointer_to_type_extra2(ira->codegen, elem_type, lazy_slice_type->is_const, lazy_slice_type->is_volatile, PtrLenUnknown, align_bytes, 0, 0, lazy_slice_type->is_allowzero, VECTOR_INDEX_NONE, nullptr, sentinel_val); val->special = ConstValSpecialStatic; assert(val->type->id == ZigTypeIdMetaType); val->data.x_type = get_slice_type(ira->codegen, slice_ptr_type); // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } case LazyValueIdPtrType: { LazyValuePtrType *lazy_ptr_type = reinterpret_cast(val->data.x_lazy); IrAnalyze *ira = lazy_ptr_type->ira; ZigType *elem_type = ir_resolve_type(ira, lazy_ptr_type->elem_type); if (type_is_invalid(elem_type)) return ErrorSemanticAnalyzeFail; ZigValue *sentinel_val; if (lazy_ptr_type->sentinel != nullptr) { if (type_is_invalid(lazy_ptr_type->sentinel->value->type)) return ErrorSemanticAnalyzeFail; IrInstGen *sentinel = ir_implicit_cast(ira, lazy_ptr_type->sentinel, elem_type); if (type_is_invalid(sentinel->value->type)) return ErrorSemanticAnalyzeFail; sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); if (sentinel_val == nullptr) return ErrorSemanticAnalyzeFail; } else { sentinel_val = nullptr; } uint32_t align_bytes = 0; if (lazy_ptr_type->align_inst != nullptr) { if (!ir_resolve_align(ira, lazy_ptr_type->align_inst, elem_type, &align_bytes)) return ErrorSemanticAnalyzeFail; } if (elem_type->id == ZigTypeIdUnreachable) { ir_add_error(ira, &lazy_ptr_type->elem_type->base, buf_create_from_str("pointer to noreturn not allowed")); return ErrorSemanticAnalyzeFail; } else if (elem_type->id == ZigTypeIdOpaque && lazy_ptr_type->ptr_len == PtrLenUnknown) { ir_add_error(ira, &lazy_ptr_type->elem_type->base, buf_create_from_str("unknown-length pointer to opaque")); return ErrorSemanticAnalyzeFail; } else if (lazy_ptr_type->ptr_len == PtrLenC) { bool ok_type; if ((err = type_allowed_in_extern(ira->codegen, elem_type, &ok_type))) return err; if (!ok_type) { ir_add_error(ira, &lazy_ptr_type->elem_type->base, buf_sprintf("C pointers cannot point to non-C-ABI-compatible type '%s'", buf_ptr(&elem_type->name))); return ErrorSemanticAnalyzeFail; } else if (elem_type->id == ZigTypeIdOpaque) { ir_add_error(ira, &lazy_ptr_type->elem_type->base, buf_sprintf("C pointers cannot point to opaque types")); return ErrorSemanticAnalyzeFail; } else if (lazy_ptr_type->is_allowzero) { ir_add_error(ira, &lazy_ptr_type->elem_type->base, buf_sprintf("C pointers always allow address zero")); return ErrorSemanticAnalyzeFail; } } if (align_bytes != 0) { if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusAlignmentKnown))) return err; if (!type_has_bits(ira->codegen, elem_type)) align_bytes = 0; } bool allow_zero = lazy_ptr_type->is_allowzero || lazy_ptr_type->ptr_len == PtrLenC; assert(val->type->id == ZigTypeIdMetaType); val->data.x_type = get_pointer_to_type_extra2(ira->codegen, elem_type, lazy_ptr_type->is_const, lazy_ptr_type->is_volatile, lazy_ptr_type->ptr_len, align_bytes, lazy_ptr_type->bit_offset_in_host, lazy_ptr_type->host_int_bytes, allow_zero, VECTOR_INDEX_NONE, nullptr, sentinel_val); val->special = ConstValSpecialStatic; // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } case LazyValueIdArrayType: { LazyValueArrayType *lazy_array_type = reinterpret_cast(val->data.x_lazy); IrAnalyze *ira = lazy_array_type->ira; ZigType *elem_type = ir_resolve_type(ira, lazy_array_type->elem_type); if (type_is_invalid(elem_type)) return ErrorSemanticAnalyzeFail; switch (elem_type->id) { case ZigTypeIdInvalid: // handled above zig_unreachable(); case ZigTypeIdUnreachable: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdOpaque: ir_add_error(ira, &lazy_array_type->elem_type->base, buf_sprintf("array of type '%s' not allowed", buf_ptr(&elem_type->name))); return ErrorSemanticAnalyzeFail; case ZigTypeIdMetaType: case ZigTypeIdVoid: case ZigTypeIdBool: case ZigTypeIdInt: case ZigTypeIdFloat: case ZigTypeIdPointer: case ZigTypeIdArray: case ZigTypeIdStruct: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdEnumLiteral: case ZigTypeIdOptional: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdEnum: case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdBoundFn: case ZigTypeIdVector: case ZigTypeIdFnFrame: case ZigTypeIdAnyFrame: break; } if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) return err; ZigValue *sentinel_val = nullptr; if (lazy_array_type->sentinel != nullptr) { if (type_is_invalid(lazy_array_type->sentinel->value->type)) return ErrorSemanticAnalyzeFail; IrInstGen *sentinel = ir_implicit_cast(ira, lazy_array_type->sentinel, elem_type); if (type_is_invalid(sentinel->value->type)) return ErrorSemanticAnalyzeFail; sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); if (sentinel_val == nullptr) return ErrorSemanticAnalyzeFail; } assert(val->type->id == ZigTypeIdMetaType); val->data.x_type = get_array_type(ira->codegen, elem_type, lazy_array_type->length, sentinel_val); val->special = ConstValSpecialStatic; // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } case LazyValueIdOptType: { LazyValueOptType *lazy_opt_type = reinterpret_cast(val->data.x_lazy); IrAnalyze *ira = lazy_opt_type->ira; ZigType *payload_type = ir_resolve_type(ira, lazy_opt_type->payload_type); if (type_is_invalid(payload_type)) return ErrorSemanticAnalyzeFail; if (payload_type->id == ZigTypeIdOpaque || payload_type->id == ZigTypeIdUnreachable) { ir_add_error(ira, &lazy_opt_type->payload_type->base, buf_sprintf("type '%s' cannot be optional", buf_ptr(&payload_type->name))); return ErrorSemanticAnalyzeFail; } if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown))) return err; assert(val->type->id == ZigTypeIdMetaType); val->data.x_type = get_optional_type(ira->codegen, payload_type); val->special = ConstValSpecialStatic; // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } case LazyValueIdFnType: { LazyValueFnType *lazy_fn_type = reinterpret_cast(val->data.x_lazy); IrAnalyze *ira = lazy_fn_type->ira; ZigType *fn_type = ir_resolve_lazy_fn_type(ira, source_node, lazy_fn_type); if (fn_type == nullptr) return ErrorSemanticAnalyzeFail; val->special = ConstValSpecialStatic; assert(val->type->id == ZigTypeIdMetaType); val->data.x_type = fn_type; // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } case LazyValueIdErrUnionType: { LazyValueErrUnionType *lazy_err_union_type = reinterpret_cast(val->data.x_lazy); IrAnalyze *ira = lazy_err_union_type->ira; ZigType *err_set_type = ir_resolve_type(ira, lazy_err_union_type->err_set_type); if (type_is_invalid(err_set_type)) return ErrorSemanticAnalyzeFail; ZigType *payload_type = ir_resolve_type(ira, lazy_err_union_type->payload_type); if (type_is_invalid(payload_type)) return ErrorSemanticAnalyzeFail; if (err_set_type->id != ZigTypeIdErrorSet) { ir_add_error(ira, &lazy_err_union_type->err_set_type->base, buf_sprintf("expected error set type, found type '%s'", buf_ptr(&err_set_type->name))); return ErrorSemanticAnalyzeFail; } if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown))) return ErrorSemanticAnalyzeFail; assert(val->type->id == ZigTypeIdMetaType); val->data.x_type = get_error_union_type(ira->codegen, err_set_type, payload_type); val->special = ConstValSpecialStatic; // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } } zig_unreachable(); } Error ir_resolve_lazy(CodeGen *codegen, AstNode *source_node, ZigValue *val) { Error err; if ((err = ir_resolve_lazy_raw(source_node, val))) { if (codegen->trace_err != nullptr && source_node != nullptr && !source_node->already_traced_this_node) { source_node->already_traced_this_node = true; codegen->trace_err = add_error_note(codegen, codegen->trace_err, source_node, buf_create_from_str("referenced here")); } return err; } if (type_is_invalid(val->type)) { return ErrorSemanticAnalyzeFail; } return ErrorNone; } void IrInst::src() { IrInst *inst = this; if (inst->source_node != nullptr) { inst->source_node->src(); } else { fprintf(stderr, "(null source node)\n"); } } void IrInst::dump() { this->src(); fprintf(stderr, "IrInst(#%" PRIu32 ")\n", this->debug_id); } void IrInstSrc::src() { this->base.src(); } void IrInstGen::src() { this->base.src(); } void IrInstSrc::dump() { IrInstSrc *inst = this; inst->src(); if (inst->base.scope == nullptr) { fprintf(stderr, "(null scope)\n"); } else { ir_print_inst_src(inst->base.scope->codegen, stderr, inst, 0); fprintf(stderr, "-> "); ir_print_inst_gen(inst->base.scope->codegen, stderr, inst->child, 0); } } void IrInstGen::dump() { IrInstGen *inst = this; inst->src(); if (inst->base.scope == nullptr) { fprintf(stderr, "(null scope)\n"); } else { ir_print_inst_gen(inst->base.scope->codegen, stderr, inst, 0); } } void IrAnalyze::dump() { ir_print_gen(this->codegen, stderr, this->new_irb.exec, 0); if (this->new_irb.current_basic_block != nullptr) { fprintf(stderr, "Current basic block:\n"); ir_print_basic_block_gen(this->codegen, stderr, this->new_irb.current_basic_block, 1); } } void dbg_ir_break(const char *src_file, uint32_t line) { dbg_ir_breakpoints_buf[dbg_ir_breakpoints_count] = {src_file, line}; dbg_ir_breakpoints_count += 1; } void dbg_ir_clear(void) { dbg_ir_breakpoints_count = 0; }