diff --git a/src/all_types.hpp b/src/all_types.hpp index b3c23dce7..8ea25ad8c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -22,6 +22,7 @@ struct FnTableEntry; struct BlockContext; struct TypeTableEntry; struct VariableTableEntry; +struct ErrorTableEntry; struct BuiltinFnEntry; struct LabelTableEntry; struct TypeStructField; @@ -69,6 +70,7 @@ struct ConstExprValue { bool x_bool; FnTableEntry *x_fn; TypeTableEntry *x_type; + ErrorTableEntry *x_err; ConstExprValue *x_maybe; ConstEnumValue x_enum; ConstStructValue x_struct; @@ -996,6 +998,7 @@ struct CodeGen { LLVMValueRef memset_fn_val; bool error_during_imports; uint32_t next_node_index; + uint32_t next_error_index; }; struct VariableTableEntry { @@ -1010,12 +1013,19 @@ struct VariableTableEntry { int gen_arg_index; }; +struct ErrorTableEntry { + Buf name; + uint32_t value; + AstNode *decl_node; +}; + struct BlockContext { AstNode *node; // either NodeTypeFnDef or NodeTypeBlock or NodeTypeRoot FnTableEntry *fn_entry; // null at the module scope BlockContext *parent; // null when this is the root HashMap variable_table; HashMap type_table; + HashMap error_table; ZigList cast_alloca_list; ZigList struct_val_expr_alloca_list; ZigList variable_list; diff --git a/src/analyze.cpp b/src/analyze.cpp index 9c9df599d..113791551 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -224,48 +224,26 @@ static TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) { return child_type->error_parent; } else { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdError); - zig_panic("TODO get_error_type"); - // create a struct with a boolean whether this is the null value assert(child_type->type_ref); - LLVMTypeRef elem_types[] = { - child_type->type_ref, - LLVMInt1Type(), - }; - entry->type_ref = LLVMStructType(elem_types, 2, false); - buf_resize(&entry->name, 0); - buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name)); - entry->size_in_bits = child_type->size_in_bits + 8; - entry->align_in_bits = child_type->align_in_bits; assert(child_type->di_type); + buf_resize(&entry->name, 0); + buf_appendf(&entry->name, "%%%s", buf_ptr(&child_type->name)); - LLVMZigDIScope *compile_unit_scope = LLVMZigCompileUnitToScope(g->compile_unit); - LLVMZigDIFile *di_file = nullptr; - unsigned line = 0; - entry->di_type = LLVMZigCreateReplaceableCompositeType(g->dbuilder, - LLVMZigTag_DW_structure_type(), buf_ptr(&entry->name), - compile_unit_scope, di_file, line); + entry->data.error.child_type = child_type; - LLVMZigDIType *di_element_types[] = { - LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type), - "val", di_file, line, child_type->size_in_bits, child_type->align_in_bits, 0, 0, - child_type->di_type), - LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type), - "maybe", di_file, line, 8, 8, 8, 0, - child_type->di_type), - }; - LLVMZigDIType *replacement_di_type = LLVMZigCreateDebugStructType(g->dbuilder, - compile_unit_scope, - buf_ptr(&entry->name), - di_file, line, entry->size_in_bits, entry->align_in_bits, 0, - nullptr, di_element_types, 2, 0, nullptr, ""); + if (child_type->size_in_bits == 0) { + TypeTableEntry *tag_type = get_smallest_unsigned_int_type(g, g->next_error_index); + entry->type_ref = tag_type->type_ref; + entry->size_in_bits = tag_type->size_in_bits; + entry->align_in_bits = tag_type->align_in_bits; + entry->di_type = tag_type->di_type; - LLVMZigReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type); - entry->di_type = replacement_di_type; + } else { + zig_panic("TODO get_error_type non-void"); + } - entry->data.maybe.child_type = child_type; - - child_type->maybe_parent = entry; + child_type->error_parent = entry; return entry; } } @@ -938,6 +916,40 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, } } +static void resolve_error_value_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) { + assert(node->type == NodeTypeErrorValueDecl); + + ErrorTableEntry *err = allocate(1); + + err->value = g->next_error_index; + g->next_error_index += 1; + + err->decl_node = node; + buf_init_from_buf(&err->name, &node->data.error_value_decl.name); + + auto existing_entry = import->block_context->error_table.maybe_get(&err->name); + if (existing_entry) { + add_node_error(g, node, buf_sprintf("redefinition of error '%s'", buf_ptr(&err->name))); + } else { + import->block_context->error_table.put(&err->name, err); + } + + bool is_pub = (node->data.error_value_decl.visib_mod != VisibModPrivate); + if (is_pub) { + for (int i = 0; i < import->importers.length; i += 1) { + ImporterInfo importer = import->importers.at(i); + auto table_entry = importer.import->block_context->error_table.maybe_get(&err->name); + if (table_entry) { + add_node_error(g, importer.source_node, + buf_sprintf("import of error '%s' overrides existing definition", + buf_ptr(&err->name))); + } else { + importer.import->block_context->error_table.put(&err->name, err); + } + } + } +} + static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) { switch (node->type) { case NodeTypeExternBlock: @@ -984,10 +996,8 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode break; } case NodeTypeErrorValueDecl: - { - zig_panic("TODO resolve_top_level_decl NodeTypeErrorValueDecl"); - break; - } + resolve_error_value_decl(g, import, node); + break; case NodeTypeUse: // nothing to do here break; @@ -1388,6 +1398,7 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) { context->parent = parent; context->variable_table.init(8); context->type_table.init(8); + context->error_table.init(8); if (parent) { context->parent_loop_node = parent->parent_loop_node; @@ -1799,6 +1810,13 @@ static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, F return fn->type_entry; } +static TypeTableEntry *resolve_expr_const_val_as_err(CodeGen *g, AstNode *node, ErrorTableEntry *err) { + Expr *expr = get_resolved_expr(node); + expr->const_val.ok = true; + expr->const_val.data.x_err = err; + return get_error_type(g, g->builtin_types.entry_void); +} + static TypeTableEntry *resolve_expr_const_val_as_bool(CodeGen *g, AstNode *node, bool value) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; @@ -2333,7 +2351,13 @@ static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, Block if (name) { buf_init_from_buf(&variable_entry->name, name); - VariableTableEntry *existing_var = find_local_variable(context, name); + VariableTableEntry *existing_var; + + if (context->fn_entry) { + existing_var = find_local_variable(context, name); + } else { + existing_var = find_variable(context, name); + } if (existing_var) { add_node_error(g, source_node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); @@ -2511,7 +2535,18 @@ static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *block_context, TypeTableEntry *expected_type, AstNode *node) { - zig_panic("TODO analyze_error_literal_expr"); + Buf *err_name = &node->data.error_literal.symbol; + + auto err_table_entry = import->block_context->error_table.maybe_get(err_name); + + if (err_table_entry) { + return resolve_expr_const_val_as_err(g, node, err_table_entry->value); + } + + add_node_error(g, node, + buf_sprintf("use of undeclared error value '%s'", buf_ptr(err_name))); + + return get_error_type(g, g->builtin_types.entry_void); } static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import, BlockContext *context, diff --git a/src/codegen.cpp b/src/codegen.cpp index dcc3c4dff..08d191a50 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -26,6 +26,7 @@ CodeGen *codegen_create(Buf *root_source_dir) { g->unresolved_top_level_decls.init(32); g->build_type = CodeGenBuildTypeDebug; g->root_source_dir = root_source_dir; + g->next_error_index = 1; return g; } diff --git a/test/run_tests.cpp b/test/run_tests.cpp index d4ff89535..fe8dc39ae 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -1484,6 +1484,21 @@ struct A { x : i32, } struct A { y : i32, } )SOURCE", 1, ".tmp_source.zig:3:1: error: redefinition of 'A'"); + add_compile_fail_case("redefinition of enums", R"SOURCE( +enum A {} +enum A {} + )SOURCE", 1, ".tmp_source.zig:3:1: error: redefinition of 'A'"); + + add_compile_fail_case("redefinition of error values", R"SOURCE( +%.A; +%.A; + )SOURCE", 1, ".tmp_source.zig:3:1: error: redefinition of error 'A'"); + + add_compile_fail_case("redefinition of global variables", R"SOURCE( +var a : i32 = 1; +var a : i32 = 2; + )SOURCE", 1, ".tmp_source.zig:3:1: error: redeclaration of variable 'a'"); + add_compile_fail_case("byvalue struct on exported functions", R"SOURCE( struct A { x : i32, } export fn f(a : A) => {} @@ -1608,6 +1623,7 @@ extern { } const x = foo(); )SOURCE", 1, ".tmp_source.zig:5:11: error: global variable initializer requires constant expression"); + } static void print_compiler_invocation(TestCase *test_case) {