diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/all_types.hpp | 16 | ||||
| -rw-r--r-- | src/codegen.cpp | 74 | ||||
| -rw-r--r-- | src/ir.cpp | 176 | ||||
| -rw-r--r-- | src/ir_print.cpp | 16 |
4 files changed, 245 insertions, 37 deletions
diff --git a/src/all_types.hpp b/src/all_types.hpp index e682255602..5d8a823e75 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -520,7 +520,6 @@ struct AstNodeUnwrapErrorExpr { enum CastOp { CastOpNoCast, // signifies the function call expression is not a cast CastOpNoop, // fn call expr is a cast, but does nothing - CastOpErrToInt, CastOpIntToFloat, CastOpFloatToInt, CastOpBoolToInt, @@ -1223,6 +1222,7 @@ enum PanicMsgId { PanicMsgIdSliceWidenRemainder, PanicMsgIdUnwrapMaybeFail, PanicMsgIdUnwrapErrFail, + PanicMsgIdInvalidErrorCode, PanicMsgIdCount, }; @@ -1728,6 +1728,8 @@ enum IrInstructionId { IrInstructionIdIntToPtr, IrInstructionIdPtrToInt, IrInstructionIdIntToEnum, + IrInstructionIdIntToErr, + IrInstructionIdErrToInt, IrInstructionIdCheckSwitchProngs, IrInstructionIdTestType, IrInstructionIdTypeName, @@ -2404,6 +2406,18 @@ struct IrInstructionIntToEnum { IrInstruction *target; }; +struct IrInstructionIntToErr { + IrInstruction base; + + IrInstruction *target; +}; + +struct IrInstructionErrToInt { + IrInstruction base; + + IrInstruction *target; +}; + struct IrInstructionCheckSwitchProngsRange { IrInstruction *start; IrInstruction *end; diff --git a/src/codegen.cpp b/src/codegen.cpp index cbc0d4c0b0..ed4b0e0df2 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -570,6 +570,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { return buf_create_from_str("attempt to unwrap error"); case PanicMsgIdUnreachable: return buf_create_from_str("reached unreachable code"); + case PanicMsgIdInvalidErrorCode: + return buf_create_from_str("invalid error code"); } zig_unreachable(); } @@ -1227,14 +1229,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, zig_unreachable(); case CastOpNoop: return expr_val; - case CastOpErrToInt: - assert(actual_type->id == TypeTableEntryIdErrorUnion); - if (!type_has_bits(actual_type->data.error.child_type)) { - return gen_widen_or_shorten(g, ir_want_debug_safety(g, &cast_instruction->base), - g->err_tag_type, wanted_type, expr_val); - } else { - zig_panic("TODO"); - } case CastOpResizeSlice: { assert(cast_instruction->tmp_ptr); @@ -1402,6 +1396,66 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable, instruction->target->value.type, wanted_int_type, target_val); } +static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, IrInstructionIntToErr *instruction) { + TypeTableEntry *wanted_type = instruction->base.value.type; + assert(wanted_type->id == TypeTableEntryIdPureError); + + TypeTableEntry *actual_type = instruction->target->value.type; + assert(actual_type->id == TypeTableEntryIdInt); + assert(!actual_type->data.integral.is_signed); + + LLVMValueRef target_val = ir_llvm_value(g, instruction->target); + + if (ir_want_debug_safety(g, &instruction->base)) { + LLVMValueRef zero = LLVMConstNull(actual_type->type_ref); + LLVMValueRef neq_zero_bit = LLVMBuildICmp(g->builder, LLVMIntNE, target_val, zero, ""); + LLVMValueRef ok_bit; + uint64_t biggest_possible_err_val = max_unsigned_val(actual_type); + if (biggest_possible_err_val < g->error_decls.length) { + ok_bit = neq_zero_bit; + } else { + LLVMValueRef error_value_count = LLVMConstInt(actual_type->type_ref, g->error_decls.length, false); + LLVMValueRef in_bounds_bit = LLVMBuildICmp(g->builder, LLVMIntULT, target_val, error_value_count, ""); + ok_bit = LLVMBuildAnd(g->builder, neq_zero_bit, in_bounds_bit, ""); + } + + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrFail"); + + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_debug_safety_crash(g, PanicMsgIdInvalidErrorCode); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + + return gen_widen_or_shorten(g, false, actual_type, g->err_tag_type, target_val); +} + +static LLVMValueRef ir_render_err_to_int(CodeGen *g, IrExecutable *executable, IrInstructionErrToInt *instruction) { + TypeTableEntry *wanted_type = instruction->base.value.type; + assert(wanted_type->id == TypeTableEntryIdInt); + assert(!wanted_type->data.integral.is_signed); + + TypeTableEntry *actual_type = instruction->target->value.type; + LLVMValueRef target_val = ir_llvm_value(g, instruction->target); + + if (actual_type->id == TypeTableEntryIdPureError) { + return gen_widen_or_shorten(g, ir_want_debug_safety(g, &instruction->base), + g->err_tag_type, wanted_type, target_val); + } else if (actual_type->id == TypeTableEntryIdErrorUnion) { + if (!type_has_bits(actual_type->data.error.child_type)) { + return gen_widen_or_shorten(g, ir_want_debug_safety(g, &instruction->base), + g->err_tag_type, wanted_type, target_val); + } else { + zig_panic("TODO"); + } + } else { + zig_unreachable(); + } +} + static LLVMValueRef ir_render_unreachable(CodeGen *g, IrExecutable *executable, IrInstructionUnreachable *unreachable_instruction) { @@ -2786,6 +2840,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_int_to_ptr(g, executable, (IrInstructionIntToPtr *)instruction); case IrInstructionIdIntToEnum: return ir_render_int_to_enum(g, executable, (IrInstructionIntToEnum *)instruction); + case IrInstructionIdIntToErr: + return ir_render_int_to_err(g, executable, (IrInstructionIntToErr *)instruction); + case IrInstructionIdErrToInt: + return ir_render_err_to_int(g, executable, (IrInstructionErrToInt *)instruction); case IrInstructionIdContainerInitList: return ir_render_container_init_list(g, executable, (IrInstructionContainerInitList *)instruction); case IrInstructionIdPanic: diff --git a/src/ir.cpp b/src/ir.cpp index 1fdb8f2e3b..a33fb3cd22 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -500,6 +500,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToEnum *) { return IrInstructionIdIntToEnum; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToErr *) { + return IrInstructionIdIntToErr; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionErrToInt *) { + return IrInstructionIdErrToInt; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckSwitchProngs *) { return IrInstructionIdCheckSwitchProngs; } @@ -2002,6 +2010,30 @@ static IrInstruction *ir_build_int_to_enum(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } +static IrInstruction *ir_build_int_to_err(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *target) +{ + IrInstructionIntToErr *instruction = ir_build_instruction<IrInstructionIntToErr>( + irb, scope, source_node); + instruction->target = target; + + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_err_to_int(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *target) +{ + IrInstructionErrToInt *instruction = ir_build_instruction<IrInstructionErrToInt>( + irb, scope, source_node); + instruction->target = target; + + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target_value, IrInstructionCheckSwitchProngsRange *ranges, size_t range_count) { @@ -2704,6 +2736,20 @@ static IrInstruction *ir_instruction_inttoenum_get_dep(IrInstructionIntToEnum *i } } +static IrInstruction *ir_instruction_inttoerr_get_dep(IrInstructionIntToErr *instruction, size_t index) { + switch (index) { + case 0: return instruction->target; + default: return nullptr; + } +} + +static IrInstruction *ir_instruction_errtoint_get_dep(IrInstructionErrToInt *instruction, size_t index) { + switch (index) { + case 0: return instruction->target; + default: return nullptr; + } +} + static IrInstruction *ir_instruction_checkswitchprongs_get_dep(IrInstructionCheckSwitchProngs *instruction, size_t index) { @@ -2938,6 +2984,10 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t return ir_instruction_ptrtoint_get_dep((IrInstructionPtrToInt *) instruction, index); case IrInstructionIdIntToEnum: return ir_instruction_inttoenum_get_dep((IrInstructionIntToEnum *) instruction, index); + case IrInstructionIdIntToErr: + return ir_instruction_inttoerr_get_dep((IrInstructionIntToErr *) instruction, index); + case IrInstructionIdErrToInt: + return ir_instruction_errtoint_get_dep((IrInstructionErrToInt *) instruction, index); case IrInstructionIdCheckSwitchProngs: return ir_instruction_checkswitchprongs_get_dep((IrInstructionCheckSwitchProngs *) instruction, index); case IrInstructionIdTestType: @@ -6001,20 +6051,6 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, case CastOpBytesToSlice: // can't do it break; - case CastOpErrToInt: - { - uint64_t value; - if (other_type->id == TypeTableEntryIdErrorUnion) { - value = other_val->data.x_err_union.err ? other_val->data.x_err_union.err->value : 0; - } else if (other_type->id == TypeTableEntryIdPureError) { - value = other_val->data.x_pure_err->value; - } else { - zig_unreachable(); - } - bignum_init_unsigned(&const_val->data.x_bignum, value); - const_val->special = ConstValSpecialStatic; - break; - } case CastOpIntToFloat: bignum_cast_to_float(&const_val->data.x_bignum, &other_val->data.x_bignum); const_val->special = ConstValSpecialStatic; @@ -6707,6 +6743,87 @@ static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction return result; } +static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target) { + assert(target->value.type->id == TypeTableEntryIdInt); + assert(!target->value.type->data.integral.is_signed); + + if (instr_is_comptime(target)) { + ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + if (!val) + return ira->codegen->invalid_instruction; + + IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, + source_instr->source_node, ira->codegen->builtin_types.entry_pure_error); + + uint64_t index = val->data.x_bignum.data.x_uint; + if (index == 0 || index >= ira->codegen->error_decls.length) { + ir_add_error(ira, source_instr, + buf_sprintf("integer value %" PRIu64 " represents no error", index)); + return ira->codegen->invalid_instruction; + } + + AstNode *error_decl_node = ira->codegen->error_decls.at(index); + result->value.data.x_pure_err = error_decl_node->data.error_value_decl.err; + return result; + } + + IrInstruction *result = ir_build_int_to_err(&ira->new_irb, source_instr->scope, source_instr->source_node, target); + result->value.type = ira->codegen->builtin_types.entry_pure_error; + return result; +} + +static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, + TypeTableEntry *wanted_type) +{ + assert(wanted_type->id == TypeTableEntryIdInt); + + TypeTableEntry *err_type = target->value.type; + + if (instr_is_comptime(target)) { + ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + if (!val) + return ira->codegen->invalid_instruction; + + IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, + source_instr->source_node, wanted_type); + + ErrorTableEntry *err; + if (err_type->id == TypeTableEntryIdErrorUnion) { + err = val->data.x_err_union.err; + } else if (err_type->id == TypeTableEntryIdPureError) { + err = val->data.x_pure_err; + } else { + zig_unreachable(); + } + result->value.type = wanted_type; + uint64_t err_value = err ? err->value : 0; + bignum_init_unsigned(&result->value.data.x_bignum, err_value); + + if (!bignum_fits_in_bits(&result->value.data.x_bignum, + wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) + { + ir_add_error_node(ira, source_instr->source_node, + buf_sprintf("error code '%s' does not fit in '%s'", + buf_ptr(&err->name), buf_ptr(&wanted_type->name))); + return ira->codegen->invalid_instruction; + } + + return result; + } + + BigNum bn; + bignum_init_unsigned(&bn, ira->codegen->error_decls.length); + if (!bignum_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) { + ir_add_error_node(ira, source_instr->source_node, + buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name))); + return ira->codegen->invalid_instruction; + } + + IrInstruction *result = ir_build_err_to_int(&ira->new_irb, source_instr->scope, source_instr->source_node, target); + result->value.type = wanted_type; + return result; +} + static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, TypeTableEntry *wanted_type, IrInstruction *value) { @@ -6781,7 +6898,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpFloatToInt, false); } - // explicit cast from array to slice + // explicit cast from [N]T to []const T if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray) { TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); @@ -6909,17 +7026,14 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst if ((actual_type_is_void_err || actual_type_is_pure_err) && wanted_type->id == TypeTableEntryIdInt) { - BigNum bn; - bignum_init_unsigned(&bn, ira->codegen->error_decls.length); - if (bignum_fits_in_bits(&bn, wanted_type->data.integral.bit_count, - wanted_type->data.integral.is_signed)) - { - return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpErrToInt, false); - } else { - ir_add_error_node(ira, source_instr->source_node, - buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name))); - return ira->codegen->invalid_instruction; - } + return ir_analyze_err_to_int(ira, source_instr, value, wanted_type); + } + + // explicit cast from integer to pure error + if (wanted_type->id == TypeTableEntryIdPureError && actual_type->id == TypeTableEntryIdInt && + !actual_type->data.integral.is_signed) + { + return ir_analyze_int_to_err(ira, source_instr, value); } // explicit cast from integer to enum type with no payload @@ -7843,7 +7957,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc result_type = ira->codegen->builtin_types.entry_invalid; } - bool is_comptime_var = ir_get_var_is_comptime(var); + bool is_comptime_var = ir_get_var_is_comptime(var); switch (result_type->id) { case TypeTableEntryIdTypeDecl: @@ -7852,6 +7966,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc break; // handled above case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdUndefLit: if (is_export || is_extern || (!var->src_is_const && !is_comptime_var)) { ir_add_error_node(ira, source_node, buf_sprintf("unable to infer variable type")); result_type = ira->codegen->builtin_types.entry_invalid; @@ -7873,7 +7988,6 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc result_type = ira->codegen->builtin_types.entry_invalid; } break; - case TypeTableEntryIdUndefLit: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: @@ -10765,6 +10879,8 @@ static TypeTableEntry *ir_analyze_instruction_container_init_list(IrAnalyze *ira TypeTableEntry *this_field_type = field->type_entry; IrInstruction *init_value = instruction->items[0]->other; + if (type_is_invalid(init_value->value.type)) + return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_init_value = ir_implicit_cast(ira, init_value, this_field_type); if (casted_init_value == ira->codegen->invalid_instruction) @@ -12311,6 +12427,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi case IrInstructionIdIntToPtr: case IrInstructionIdPtrToInt: case IrInstructionIdIntToEnum: + case IrInstructionIdIntToErr: + case IrInstructionIdErrToInt: case IrInstructionIdStructInit: case IrInstructionIdStructFieldPtr: case IrInstructionIdEnumFieldPtr: @@ -12650,6 +12768,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdPtrToInt: case IrInstructionIdIntToPtr: case IrInstructionIdIntToEnum: + case IrInstructionIdIntToErr: + case IrInstructionIdErrToInt: case IrInstructionIdTestType: case IrInstructionIdTypeName: case IrInstructionIdCanImplicitCast: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 7814064f85..fd912d6919 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -799,6 +799,16 @@ static void ir_print_int_to_enum(IrPrint *irp, IrInstructionIntToEnum *instructi fprintf(irp->f, ")"); } +static void ir_print_int_to_err(IrPrint *irp, IrInstructionIntToErr *instruction) { + fprintf(irp->f, "inttoerr "); + ir_print_other_instruction(irp, instruction->target); +} + +static void ir_print_err_to_int(IrPrint *irp, IrInstructionErrToInt *instruction) { + fprintf(irp->f, "errtoint "); + ir_print_other_instruction(irp, instruction->target); +} + static void ir_print_check_switch_prongs(IrPrint *irp, IrInstructionCheckSwitchProngs *instruction) { fprintf(irp->f, "@checkSwitchProngs("); ir_print_other_instruction(irp, instruction->target_value); @@ -1117,6 +1127,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdIntToEnum: ir_print_int_to_enum(irp, (IrInstructionIntToEnum *)instruction); break; + case IrInstructionIdIntToErr: + ir_print_int_to_err(irp, (IrInstructionIntToErr *)instruction); + break; + case IrInstructionIdErrToInt: + ir_print_err_to_int(irp, (IrInstructionErrToInt *)instruction); + break; case IrInstructionIdCheckSwitchProngs: ir_print_check_switch_prongs(irp, (IrInstructionCheckSwitchProngs *)instruction); break; |
