diff --git a/src/all_types.hpp b/src/all_types.hpp index f51d242d3b..3b6484869f 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1178,7 +1178,6 @@ struct CodeGen { LLVMValueRef trap_fn_val; bool error_during_imports; uint32_t next_node_index; - ZigList error_decls; TypeTableEntry *err_tag_type; LLVMValueRef int_overflow_fns[2][3][4]; // [0-signed,1-unsigned][0-add,1-sub,2-mul][0-8,1-16,2-32,3-64] LLVMValueRef int_builtin_fns[2][4]; // [0-ctz,1-clz][0-8,1-16,2-32,3-64] @@ -1191,7 +1190,9 @@ struct CodeGen { bool check_unused; + ZigList error_decls; bool generate_error_name_table; + LLVMValueRef err_name_table; }; struct VariableTableEntry { diff --git a/src/analyze.cpp b/src/analyze.cpp index cffe076c9f..0ec07c8ac2 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -454,7 +454,7 @@ static void slice_type_common_init(CodeGen *g, TypeTableEntry *child_type, entry->data.structure.fields[1].gen_index = 1; } -static TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_const) { +TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_const) { assert(child_type->id != TypeTableEntryIdInvalid); TypeTableEntry **parent_pointer = &child_type->unknown_size_array_parent[(is_const ? 1 : 0)]; diff --git a/src/analyze.hpp b/src/analyze.hpp index 48a2f62ded..84ce17fa7b 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -26,6 +26,7 @@ TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry * TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id); TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type); TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size); +TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_const); TypeTableEntry *get_partial_container_type(CodeGen *g, ImportTableEntry *import, ContainerKind kind, AstNode *decl_node, const char *name); TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x); diff --git a/src/codegen.cpp b/src/codegen.cpp index c5234aa923..c86ce0ed10 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -331,12 +331,45 @@ static LLVMValueRef get_handle_value(CodeGen *g, AstNode *source_node, LLVMValue } static LLVMValueRef gen_err_name(CodeGen *g, AstNode *node) { - zig_panic("TODO"); - //assert(node->type == NodeTypeFnCallExpr); - //assert(g->generate_error_name_table); - //AstNode *err_val_node = node->data.fn_call_expr.params.at(0); - //LLVMValueRef err_val = gen_expr(g, err_val_node); - //arg + assert(node->type == NodeTypeFnCallExpr); + assert(g->generate_error_name_table); + + if (g->error_decls.length == 1) { + LLVMBuildUnreachable(g->builder); + return nullptr; + } + + + AstNode *err_val_node = node->data.fn_call_expr.params.at(0); + LLVMValueRef err_val = gen_expr(g, err_val_node); + add_debug_source_node(g, node); + + if (!g->is_release_build) { + LLVMBasicBlockRef bounds_check_fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoundsCheckFail"); + LLVMBasicBlockRef lower_ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "LowerBoundsCheckOk"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoundsCheckOk"); + + LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(err_val)); + LLVMValueRef is_zero_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, ""); + LLVMBuildCondBr(g->builder, is_zero_val, bounds_check_fail_block, lower_ok_block); + + LLVMPositionBuilderAtEnd(g->builder, bounds_check_fail_block); + LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, ""); + LLVMBuildUnreachable(g->builder); + + LLVMPositionBuilderAtEnd(g->builder, lower_ok_block); + LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(err_val), g->error_decls.length, false); + LLVMValueRef is_too_big_val = LLVMBuildICmp(g->builder, LLVMIntUGE, err_val, end_val, ""); + LLVMBuildCondBr(g->builder, is_too_big_val, bounds_check_fail_block, ok_block); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + + LLVMValueRef indices[] = { + LLVMConstNull(g->builtin_types.entry_isize->type_ref), + err_val, + }; + return LLVMBuildInBoundsGEP(g->builder, g->err_name_table, indices, 2, ""); } static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { @@ -3030,6 +3063,46 @@ static LLVMValueRef gen_test_fn_val(CodeGen *g, FnTableEntry *fn_entry) { return LLVMConstStruct(fields, 2, false); } +static void generate_error_name_table(CodeGen *g) { + if (!g->generate_error_name_table || g->error_decls.length == 1) { + return; + } + + assert(g->error_decls.length > 0); + + TypeTableEntry *str_type = get_slice_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = str_type->data.structure.fields[0].type_entry; + + LLVMValueRef *values = allocate(g->error_decls.length); + values[0] = LLVMGetUndef(str_type->type_ref); + for (int i = 1; i < g->error_decls.length; i += 1) { + AstNode *error_decl_node = g->error_decls.at(i); + assert(error_decl_node->type == NodeTypeErrorValueDecl); + Buf *name = &error_decl_node->data.error_value_decl.name; + + LLVMValueRef str_init = LLVMConstString(buf_ptr(name), buf_len(name), true); + LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), ""); + LLVMSetInitializer(str_global, str_init); + LLVMSetLinkage(str_global, LLVMPrivateLinkage); + LLVMSetGlobalConstant(str_global, true); + LLVMSetUnnamedAddr(str_global, true); + + LLVMValueRef fields[] = { + LLVMConstBitCast(str_global, u8_ptr_type->type_ref), + LLVMConstInt(g->builtin_types.entry_isize->type_ref, buf_len(name), false), + }; + values[i] = LLVMConstNamedStruct(str_type->type_ref, fields, 2); + } + + LLVMValueRef err_name_table_init = LLVMConstArray(str_type->type_ref, values, g->error_decls.length); + + g->err_name_table = LLVMAddGlobal(g->module, LLVMTypeOf(err_name_table_init), "err_name_table"); + LLVMSetInitializer(g->err_name_table, err_name_table_init); + LLVMSetLinkage(g->err_name_table, LLVMPrivateLinkage); + LLVMSetGlobalConstant(g->err_name_table, true); + LLVMSetUnnamedAddr(g->err_name_table, true); +} + static void do_code_gen(CodeGen *g) { assert(!g->errors.length); @@ -3037,6 +3110,7 @@ static void do_code_gen(CodeGen *g) { gen_const_globals(g); + generate_error_name_table(g); // Generate module level variables for (int i = 0; i < g->global_vars.length; i += 1) { diff --git a/std/index.zig b/std/index.zig index 07f06ea07c..a7bcfc4461 100644 --- a/std/index.zig +++ b/std/index.zig @@ -9,7 +9,7 @@ pub fn assert(b: bool) { pub const str_eql = slice_eql(u8); -pub fn slice_eql(T: type)(a: []T, b: []T) -> bool { +pub fn slice_eql(T: type)(a: []const T, b: []const T) -> bool { if (a.len != b.len) return false; for (a) |item, index| { if (b[index] != item) return false; diff --git a/test/self_hosted.zig b/test/self_hosted.zig index 0d8320994d..7b7c829d94 100644 --- a/test/self_hosted.zig +++ b/test/self_hosted.zig @@ -566,3 +566,12 @@ fn accepts_string(foo: []u8) { } fn hex_escape() { assert(str_eql("\x68\x65\x6c\x6c\x6f", "hello")); } + + +error AnError; +error ALongerErrorName; +#attribute("test") +fn error_name_string() { + assert(str_eql(@err_name(error.AnError), "AnError")); + assert(str_eql(@err_name(error.ALongerErrorName), "ALongerErrorName")); +}