diff --git a/src/array.cpp b/src/array.cpp index 4583a31a9..ec2c97d0e 100644 --- a/src/array.cpp +++ b/src/array.cpp @@ -51,6 +51,13 @@ template gb_internal void array_copy(Array *array, Array cons template gb_internal T *array_end_ptr(Array *array); +template +gb_internal void array_sort(Array &array, gbCompareProc compare_proc) { + gb_sort_array(array.data, array.count, compare_proc); +} + + + template struct Slice { T *data; diff --git a/src/build_settings.cpp b/src/build_settings.cpp index fdaa971f1..c4073f329 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1272,6 +1272,10 @@ gb_internal String get_fullpath_core_collection(gbAllocator a, String path, bool gb_internal bool show_error_line(void) { return !build_context.hide_error_line; } + +gb_internal bool terse_errors(void) { + return build_context.terse_errors; +} gb_internal bool has_ansi_terminal_colours(void) { return build_context.has_ansi_terminal_colours; } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index e1b1cd693..6de3b27f2 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1389,7 +1389,7 @@ gb_internal LoadDirectiveResult check_load_directory_directive(CheckerContext *c } } - gb_sort_array(file_caches.data, file_caches.count, file_cache_sort_cmp); + array_sort(file_caches, file_cache_sort_cmp); } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index bb31a1646..f359d5a54 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -119,6 +119,8 @@ gb_internal bool is_diverging_expr(Ast *expr); gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_); +gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr); + enum LoadDirectiveResult { LoadDirective_Success = 0, LoadDirective_Error = 1, @@ -2242,6 +2244,10 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o error_line("\tSuggestion: the expression may be casted to %s\n", b); } else if (check_integer_exceed_suggestion(c, o, type, max_bit_size)) { return; + } else if (is_expr_inferred_fixed_array(c->type_hint_expr) && is_type_array_like(type) && is_type_array_like(o->type)) { + gbString s = expr_to_string(c->type_hint_expr); + error_line("\tSuggestion: make sure that `%s` is attached to the compound literal directly\n", s); + gb_string_free(s); } } @@ -6479,7 +6485,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, } if (valids.count > 1) { - gb_sort_array(valids.data, valids.count, valid_index_and_score_cmp); + array_sort(valids, valid_index_and_score_cmp); i64 best_score = valids[0].score; Entity *best_entity = proc_entities[valids[0].index]; GB_ASSERT(best_entity != nullptr); @@ -8672,6 +8678,23 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slicekind == Ast_ArrayType && type_expr->ArrayType.count != nullptr) { + Ast *count = type_expr->ArrayType.count; + if (count->kind == Ast_UnaryExpr && + count->UnaryExpr.op.kind == Token_Question) { + return true; + } + } + return false; +} + gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { ExprKind kind = Expr_Expr; ast_node(cl, CompoundLit, node); @@ -8682,20 +8705,31 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * } bool is_to_be_determined_array_count = false; bool is_constant = true; - if (cl->type != nullptr) { + + Ast *type_expr = cl->type; + + bool used_type_hint_expr = false; + if (type_expr == nullptr && c->type_hint_expr != nullptr) { + if (is_expr_inferred_fixed_array(c->type_hint_expr)) { + type_expr = clone_ast(c->type_hint_expr); + used_type_hint_expr = true; + } + } + + if (type_expr != nullptr) { type = nullptr; // [?]Type - if (cl->type->kind == Ast_ArrayType && cl->type->ArrayType.count != nullptr) { - Ast *count = cl->type->ArrayType.count; + if (type_expr->kind == Ast_ArrayType && type_expr->ArrayType.count != nullptr) { + Ast *count = type_expr->ArrayType.count; if (count->kind == Ast_UnaryExpr && count->UnaryExpr.op.kind == Token_Question) { - type = alloc_type_array(check_type(c, cl->type->ArrayType.elem), -1); + type = alloc_type_array(check_type(c, type_expr->ArrayType.elem), -1); is_to_be_determined_array_count = true; } if (cl->elems.count > 0) { - if (cl->type->ArrayType.tag != nullptr) { - Ast *tag = cl->type->ArrayType.tag; + if (type_expr->ArrayType.tag != nullptr) { + Ast *tag = type_expr->ArrayType.tag; GB_ASSERT(tag->kind == Ast_BasicDirective); String name = tag->BasicDirective.name.string; if (name == "soa") { @@ -8705,9 +8739,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * } } } - if (cl->type->kind == Ast_DynamicArrayType && cl->type->DynamicArrayType.tag != nullptr) { + if (type_expr->kind == Ast_DynamicArrayType && type_expr->DynamicArrayType.tag != nullptr) { if (cl->elems.count > 0) { - Ast *tag = cl->type->DynamicArrayType.tag; + Ast *tag = type_expr->DynamicArrayType.tag; GB_ASSERT(tag->kind == Ast_BasicDirective); String name = tag->BasicDirective.name.string; if (name == "soa") { @@ -8718,7 +8752,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * } if (type == nullptr) { - type = check_type(c, cl->type); + type = check_type(c, type_expr); } } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a6ca4b9dd..4280e7578 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1919,17 +1919,19 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f e->Variable.thread_local_model = ac.thread_local_model; } - if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) { - // error(e->token, "@(thread_local) is not supported for this target platform"); - } - - if (ac.is_static && ac.thread_local_model != "") { error(e->token, "The 'static' attribute is not needed if 'thread_local' is applied"); } } + // NOTE(bill): This is to improve error handling for things like `x: [?]T = {...}` + Ast *prev_type_hint_expr = ctx->type_hint_expr; + ctx->type_hint_expr = vd->type; + check_init_variables(ctx, entities, entity_count, vd->values, str_lit("variable declaration")); + + ctx->type_hint_expr = prev_type_hint_expr; + check_arity_match(ctx, vd, false); for (isize i = 0; i < entity_count; i++) { diff --git a/src/check_type.cpp b/src/check_type.cpp index da4479f6e..d5cf187a4 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2834,6 +2834,111 @@ gb_internal Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_ return make_soa_struct_internal(ctx, array_typ_expr, elem_expr, elem, -1, nullptr, StructSoa_Dynamic); } +gb_internal void check_array_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_type) { + ast_node(at, ArrayType, e); + if (at->count != nullptr) { + Operand o = {}; + i64 count = check_array_count(ctx, &o, at->count); + Type *generic_type = nullptr; + + Type *elem = check_type_expr(ctx, at->elem, nullptr); + + if (o.mode == Addressing_Type && o.type->kind == Type_Generic) { + generic_type = o.type; + } else if (o.mode == Addressing_Type && is_type_enum(o.type)) { + Type *index = o.type; + Type *bt = base_type(index); + GB_ASSERT(bt->kind == Type_Enum); + + Type *t = alloc_type_enumerated_array(elem, index, bt->Enum.min_value, bt->Enum.max_value, bt->Enum.fields.count, Token_Invalid); + + bool is_sparse = false; + if (at->tag != nullptr) { + GB_ASSERT(at->tag->kind == Ast_BasicDirective); + String name = at->tag->BasicDirective.name.string; + if (name == "sparse") { + is_sparse = true; + } else { + error(at->tag, "Invalid tag applied to an enumerated array, got #%.*s", LIT(name)); + } + } + + if (!is_sparse && t->EnumeratedArray.count > bt->Enum.fields.count) { + error(e, "Non-contiguous enumeration used as an index in an enumerated array"); + long long ea_count = cast(long long)t->EnumeratedArray.count; + long long enum_count = cast(long long)bt->Enum.fields.count; + error_line("\tenumerated array length: %lld\n", ea_count); + error_line("\tenum field count: %lld\n", enum_count); + error_line("\tSuggestion: prepend #sparse to the enumerated array to allow for non-contiguous elements\n"); + if (2*enum_count < ea_count) { + error_line("\tWarning: the number of named elements is much smaller than the length of the array, are you sure this is what you want?\n"); + error_line("\t this warning will be removed if #sparse is applied\n"); + } + } + t->EnumeratedArray.is_sparse = is_sparse; + + *type = t; + + return; + } + + if (count < 0) { + error(at->count, "? can only be used in conjuction with compound literals"); + count = 0; + } + + + if (at->tag != nullptr) { + GB_ASSERT(at->tag->kind == Ast_BasicDirective); + String name = at->tag->BasicDirective.name.string; + if (name == "soa") { + *type = make_soa_struct_fixed(ctx, e, at->elem, elem, count, generic_type); + } else if (name == "simd") { + if (!is_type_valid_vector_elem(elem) && !is_type_polymorphic(elem)) { + gbString str = type_to_string(elem); + error(at->elem, "Invalid element type for #simd, expected an integer, float, or boolean with no specific endianness, got '%s'", str); + gb_string_free(str); + *type = alloc_type_array(elem, count, generic_type); + return; + } + + if (generic_type != nullptr) { + // Ignore + } else if (count < 1 || !is_power_of_two(count)) { + error(at->count, "Invalid length for #simd, expected a power of two length, got '%lld'", cast(long long)count); + *type = alloc_type_array(elem, count, generic_type); + return; + } + + *type = alloc_type_simd_vector(count, elem, generic_type); + + if (count > SIMD_ELEMENT_COUNT_MAX) { + error(at->count, "#simd support a maximum element count of %d, got %lld", SIMD_ELEMENT_COUNT_MAX, cast(long long)count); + } + } else { + error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name)); + *type = alloc_type_array(elem, count, generic_type); + } + } else { + *type = alloc_type_array(elem, count, generic_type); + } + } else { + Type *elem = check_type(ctx, at->elem); + + if (at->tag != nullptr) { + GB_ASSERT(at->tag->kind == Ast_BasicDirective); + String name = at->tag->BasicDirective.name.string; + if (name == "soa") { + *type = make_soa_struct_slice(ctx, e, at->elem, elem); + } else { + error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name)); + *type = alloc_type_slice(elem); + } + } else { + *type = alloc_type_slice(elem); + } + } +} gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_type) { GB_ASSERT_NOT_NULL(type); if (e == nullptr) { @@ -3072,109 +3177,7 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T case_end; case_ast_node(at, ArrayType, e); - if (at->count != nullptr) { - Operand o = {}; - i64 count = check_array_count(ctx, &o, at->count); - Type *generic_type = nullptr; - - Type *elem = check_type_expr(ctx, at->elem, nullptr); - - if (o.mode == Addressing_Type && o.type->kind == Type_Generic) { - generic_type = o.type; - } else if (o.mode == Addressing_Type && is_type_enum(o.type)) { - Type *index = o.type; - Type *bt = base_type(index); - GB_ASSERT(bt->kind == Type_Enum); - - Type *t = alloc_type_enumerated_array(elem, index, bt->Enum.min_value, bt->Enum.max_value, bt->Enum.fields.count, Token_Invalid); - - bool is_sparse = false; - if (at->tag != nullptr) { - GB_ASSERT(at->tag->kind == Ast_BasicDirective); - String name = at->tag->BasicDirective.name.string; - if (name == "sparse") { - is_sparse = true; - } else { - error(at->tag, "Invalid tag applied to an enumerated array, got #%.*s", LIT(name)); - } - } - - if (!is_sparse && t->EnumeratedArray.count > bt->Enum.fields.count) { - error(e, "Non-contiguous enumeration used as an index in an enumerated array"); - long long ea_count = cast(long long)t->EnumeratedArray.count; - long long enum_count = cast(long long)bt->Enum.fields.count; - error_line("\tenumerated array length: %lld\n", ea_count); - error_line("\tenum field count: %lld\n", enum_count); - error_line("\tSuggestion: prepend #sparse to the enumerated array to allow for non-contiguous elements\n"); - if (2*enum_count < ea_count) { - error_line("\tWarning: the number of named elements is much smaller than the length of the array, are you sure this is what you want?\n"); - error_line("\t this warning will be removed if #sparse is applied\n"); - } - } - t->EnumeratedArray.is_sparse = is_sparse; - - *type = t; - - goto array_end; - } - - if (count < 0) { - error(at->count, "? can only be used in conjuction with compound literals"); - count = 0; - } - - - if (at->tag != nullptr) { - GB_ASSERT(at->tag->kind == Ast_BasicDirective); - String name = at->tag->BasicDirective.name.string; - if (name == "soa") { - *type = make_soa_struct_fixed(ctx, e, at->elem, elem, count, generic_type); - } else if (name == "simd") { - if (!is_type_valid_vector_elem(elem) && !is_type_polymorphic(elem)) { - gbString str = type_to_string(elem); - error(at->elem, "Invalid element type for #simd, expected an integer, float, or boolean with no specific endianness, got '%s'", str); - gb_string_free(str); - *type = alloc_type_array(elem, count, generic_type); - goto array_end; - } - - if (generic_type != nullptr) { - // Ignore - } else if (count < 1 || !is_power_of_two(count)) { - error(at->count, "Invalid length for #simd, expected a power of two length, got '%lld'", cast(long long)count); - *type = alloc_type_array(elem, count, generic_type); - goto array_end; - } - - *type = alloc_type_simd_vector(count, elem, generic_type); - - if (count > SIMD_ELEMENT_COUNT_MAX) { - error(at->count, "#simd support a maximum element count of %d, got %lld", SIMD_ELEMENT_COUNT_MAX, cast(long long)count); - } - } else { - error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name)); - *type = alloc_type_array(elem, count, generic_type); - } - } else { - *type = alloc_type_array(elem, count, generic_type); - } - } else { - Type *elem = check_type(ctx, at->elem); - - if (at->tag != nullptr) { - GB_ASSERT(at->tag->kind == Ast_BasicDirective); - String name = at->tag->BasicDirective.name.string; - if (name == "soa") { - *type = make_soa_struct_slice(ctx, e, at->elem, elem); - } else { - error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name)); - *type = alloc_type_slice(elem); - } - } else { - *type = alloc_type_slice(elem); - } - } - array_end: + check_array_type_internal(ctx, e, type, named_type); set_base_type(named_type, *type); return true; case_end; @@ -3346,9 +3349,13 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) gbString err_str = expr_to_string(e); defer (gb_string_free(err_str)); - ERROR_BLOCK(); + begin_error_block(); error(e, "'%s' is not a type", err_str); + type = t_invalid; + + + // NOTE(bill): Check for common mistakes from C programmers e.g. T[] and T[N] Ast *node = unparen_expr(e); if (node && node->kind == Ast_IndexExpr) { gbString index_str = nullptr; @@ -3361,9 +3368,16 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) defer (gb_string_free(type_str)); error_line("\tSuggestion: Did you mean '[%s]%s'?", index_str ? index_str : "", type_str); - } + end_error_block(); - type = t_invalid; + // NOTE(bill): Minimize error propagation of bad array syntax by treating this like a type + if (node->IndexExpr.expr != nullptr) { + Ast *pseudo_array_expr = ast_array_type(e->file(), ast_token(node->IndexExpr.expr), node->IndexExpr.index, node->IndexExpr.expr); + check_array_type_internal(ctx, pseudo_array_expr, &type, nullptr); + } + } else { + end_error_block(); + } } if (type == nullptr) { diff --git a/src/checker.cpp b/src/checker.cpp index fb7d401ab..836f803fc 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -5044,7 +5044,7 @@ gb_internal void check_create_file_scopes(Checker *c) { for_array(i, c->parser->packages) { AstPackage *pkg = c->parser->packages[i]; - gb_sort_array(pkg->files.data, pkg->files.count, sort_file_by_name); + array_sort(pkg->files, sort_file_by_name); isize total_pkg_decl_count = 0; for_array(j, pkg->files) { @@ -5673,7 +5673,7 @@ gb_internal void remove_neighbouring_duplicate_entires_from_sorted_array(Arrayinfo.testing_procedures.data, c->info.testing_procedures.count, init_procedures_cmp); + array_sort(c->info.testing_procedures, init_procedures_cmp); remove_neighbouring_duplicate_entires_from_sorted_array(&c->info.testing_procedures); if (build_context.test_names.entries.count == 0) { @@ -6122,8 +6122,8 @@ gb_internal GB_COMPARE_PROC(fini_procedures_cmp) { } gb_internal void check_sort_init_and_fini_procedures(Checker *c) { - gb_sort_array(c->info.init_procedures.data, c->info.init_procedures.count, init_procedures_cmp); - gb_sort_array(c->info.fini_procedures.data, c->info.fini_procedures.count, fini_procedures_cmp); + array_sort(c->info.init_procedures, init_procedures_cmp); + array_sort(c->info.fini_procedures, fini_procedures_cmp); // NOTE(bill): remove possible duplicates from the init/fini lists // NOTE(bill): because the arrays are sorted, you only need to check the previous element diff --git a/src/checker.hpp b/src/checker.hpp index 066d6bb4a..eea99578e 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -451,6 +451,7 @@ struct CheckerContext { u32 state_flags; bool in_defer; Type * type_hint; + Ast * type_hint_expr; String proc_name; DeclInfo * curr_proc_decl; diff --git a/src/common.cpp b/src/common.cpp index 90632def3..aad420325 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -913,7 +913,7 @@ gb_internal void did_you_mean_append(DidYouMeanAnswers *d, String const &target) array_add(&d->distances, dat); } gb_internal Slice did_you_mean_results(DidYouMeanAnswers *d) { - gb_sort_array(d->distances.data, d->distances.count, gb_isize_cmp(gb_offset_of(DistanceAndTarget, distance))); + array_sort(d->distances, gb_isize_cmp(gb_offset_of(DistanceAndTarget, distance))); isize count = 0; for (isize i = 0; i < d->distances.count; i++) { isize distance = d->distances[i].distance; diff --git a/src/docs.cpp b/src/docs.cpp index f00d4e15a..004134a5c 100644 --- a/src/docs.cpp +++ b/src/docs.cpp @@ -237,7 +237,7 @@ gb_internal void print_doc_package(CheckerInfo *info, AstPackage *pkg) { } array_add(&entities, e); } - gb_sort_array(entities.data, entities.count, cmp_entities_for_printing); + array_sort(entities, cmp_entities_for_printing); bool show_docs = (build_context.cmd_doc_flags & CmdDocFlag_Short) == 0; @@ -358,7 +358,7 @@ gb_internal void generate_documentation(Checker *c) { } } - gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name); + array_sort(pkgs, cmp_ast_package_by_name); for_array(i, pkgs) { print_doc_package(info, pkgs[i]); diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 1bc244918..26d8027a9 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -1107,7 +1107,7 @@ gb_internal void odin_doc_write_docs(OdinDocWriter *w) { } debugf("odin_doc_update_entities sort pkgs %s\n", w->state ? "preparing" : "writing"); - gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name); + array_sort(pkgs, cmp_ast_package_by_name); for_array(i, pkgs) { gbAllocator allocator = heap_allocator(); diff --git a/src/error.cpp b/src/error.cpp index e63682829..d116781fb 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -1,28 +1,71 @@ +enum ErrorValueKind : u32 { + ErrorValue_Error, + ErrorValue_Warning, +}; + +struct ErrorValue { + ErrorValueKind kind; + TokenPos pos; + Array msgs; +}; + struct ErrorCollector { TokenPos prev; std::atomic count; std::atomic warning_count; std::atomic in_block; - BlockingMutex mutex; - BlockingMutex error_out_mutex; - BlockingMutex string_mutex; - RecursiveMutex block_mutex; - RecursiveMutex error_buffer_mutex; - Array error_buffer; - Array errors; + RecursiveMutex mutex; + BlockingMutex path_mutex; + + Array error_values; + ErrorValue curr_error_value; + std::atomic curr_error_value_set; }; gb_global ErrorCollector global_error_collector; +gb_internal void push_error_value(TokenPos const &pos, ErrorValueKind kind = ErrorValue_Error) { + GB_ASSERT(global_error_collector.curr_error_value_set.load() == false); + ErrorValue ev = {kind, pos}; + ev.msgs.allocator = heap_allocator(); + + global_error_collector.curr_error_value = ev; + global_error_collector.curr_error_value_set.store(true); +} + +gb_internal void pop_error_value(void) { + if (global_error_collector.curr_error_value_set.load()) { + array_add(&global_error_collector.error_values, global_error_collector.curr_error_value); + + global_error_collector.curr_error_value = {}; + global_error_collector.curr_error_value_set.store(false); + } +} + + +gb_internal void try_pop_error_value(void) { + if (!global_error_collector.in_block.load()) { + pop_error_value(); + } +} + +gb_internal ErrorValue *get_error_value(void) { + GB_ASSERT(global_error_collector.curr_error_value_set.load() == true); + return &global_error_collector.curr_error_value; +} + + + gb_internal bool any_errors(void) { return global_error_collector.count.load() != 0; } + + gb_internal void init_global_error_collector(void) { - array_init(&global_error_collector.errors, heap_allocator()); - array_init(&global_error_collector.error_buffer, heap_allocator()); + array_init(&global_error_collector.error_values, heap_allocator()); array_init(&global_file_path_strings, heap_allocator(), 1, 4096); array_init(&global_files, heap_allocator(), 1, 4096); } @@ -37,7 +80,7 @@ gb_internal char *token_pos_to_string(TokenPos const &pos); gb_internal bool set_file_path_string(i32 index, String const &path) { bool ok = false; GB_ASSERT(index >= 0); - mutex_lock(&global_error_collector.string_mutex); + mutex_lock(&global_error_collector.path_mutex); if (index >= global_file_path_strings.count) { array_resize(&global_file_path_strings, index+1); @@ -48,14 +91,14 @@ gb_internal bool set_file_path_string(i32 index, String const &path) { ok = true; } - mutex_unlock(&global_error_collector.string_mutex); + mutex_unlock(&global_error_collector.path_mutex); return ok; } gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) { bool ok = false; GB_ASSERT(index >= 0); - mutex_lock(&global_error_collector.string_mutex); + mutex_lock(&global_error_collector.path_mutex); if (index >= global_files.count) { array_resize(&global_files, index+1); @@ -66,33 +109,33 @@ gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) { ok = true; } - mutex_unlock(&global_error_collector.string_mutex); + mutex_unlock(&global_error_collector.path_mutex); return ok; } gb_internal String get_file_path_string(i32 index) { GB_ASSERT(index >= 0); - mutex_lock(&global_error_collector.string_mutex); + mutex_lock(&global_error_collector.path_mutex); String path = {}; if (index < global_file_path_strings.count) { path = global_file_path_strings[index]; } - mutex_unlock(&global_error_collector.string_mutex); + mutex_unlock(&global_error_collector.path_mutex); return path; } gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) { GB_ASSERT(index >= 0); - mutex_lock(&global_error_collector.string_mutex); + mutex_lock(&global_error_collector.path_mutex); AstFile *file = nullptr; if (index < global_files.count) { file = global_files[index]; } - mutex_unlock(&global_error_collector.string_mutex); + mutex_unlock(&global_error_collector.path_mutex); return file; } @@ -102,6 +145,7 @@ gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) { gb_internal bool global_warnings_as_errors(void); gb_internal bool global_ignore_warnings(void); gb_internal bool show_error_line(void); +gb_internal bool terse_errors(void); gb_internal bool has_ansi_terminal_colours(void); gb_internal gbString get_file_line_as_string(TokenPos const &pos, i32 *offset); @@ -113,97 +157,43 @@ gb_internal void syntax_error(Token const &token, char const *fmt, ...); gb_internal void syntax_error(TokenPos pos, char const *fmt, ...); gb_internal void syntax_warning(Token const &token, char const *fmt, ...); gb_internal void compiler_error(char const *fmt, ...); - -gb_internal void begin_error_block(void) { - mutex_lock(&global_error_collector.block_mutex); - global_error_collector.in_block.store(true); -} - -gb_internal void end_error_block(void) { - mutex_lock(&global_error_collector.error_buffer_mutex); - isize n = global_error_collector.error_buffer.count; - if (n > 0) { - u8 *text = global_error_collector.error_buffer.data; - - bool add_extra_newline = false; - - if (show_error_line()) { - if (n >= 2 && !(text[n-2] == '\n' && text[n-1] == '\n')) { - add_extra_newline = true; - } - } else { - isize newline_count = 0; - for (isize i = 0; i < n; i++) { - if (text[i] == '\n') { - newline_count += 1; - } - } - if (newline_count > 1) { - add_extra_newline = true; - } - } - - if (add_extra_newline) { - // add an extra new line as padding when the error line is being shown - error_line("\n"); - } - - n = global_error_collector.error_buffer.count; - text = gb_alloc_array(permanent_allocator(), u8, n+1); - gb_memmove(text, global_error_collector.error_buffer.data, n); - text[n] = 0; - - - mutex_lock(&global_error_collector.error_out_mutex); - String s = {text, n}; - array_add(&global_error_collector.errors, s); - mutex_unlock(&global_error_collector.error_out_mutex); - - global_error_collector.error_buffer.count = 0; - } - mutex_unlock(&global_error_collector.error_buffer_mutex); - global_error_collector.in_block.store(false); - mutex_unlock(&global_error_collector.block_mutex); -} - -#define ERROR_BLOCK() begin_error_block(); defer (end_error_block()) +gb_internal void print_all_errors(void); #define ERROR_OUT_PROC(name) void name(char const *fmt, va_list va) typedef ERROR_OUT_PROC(ErrorOutProc); gb_internal ERROR_OUT_PROC(default_error_out_va) { - gbFile *f = gb_file_get_standard(gbFileStandard_Error); - char buf[4096] = {}; isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va); isize n = len-1; - if (global_error_collector.in_block) { - mutex_lock(&global_error_collector.error_buffer_mutex); - - isize cap = global_error_collector.error_buffer.count + n; - array_reserve(&global_error_collector.error_buffer, cap); - u8 *data = global_error_collector.error_buffer.data + global_error_collector.error_buffer.count; - gb_memmove(data, buf, n); - global_error_collector.error_buffer.count += n; - - mutex_unlock(&global_error_collector.error_buffer_mutex); - } else { - mutex_lock(&global_error_collector.error_out_mutex); - { - u8 *text = gb_alloc_array(permanent_allocator(), u8, n+1); - gb_memmove(text, buf, n); - text[n] = 0; - array_add(&global_error_collector.errors, make_string(text, n)); - } - mutex_unlock(&global_error_collector.error_out_mutex); + String msg = {}; + if (n) { + msg = copy_string(permanent_allocator(), {(u8 *)buf, n}); } - gb_file_write(f, buf, n); + + ErrorValue *ev = get_error_value(); + array_add(&ev->msgs, msg); } gb_global ErrorOutProc *error_out_va = default_error_out_va; +gb_internal void begin_error_block(void) { + mutex_lock(&global_error_collector.mutex); + global_error_collector.in_block.store(true); +} + +gb_internal void end_error_block(void) { + pop_error_value(); + global_error_collector.in_block.store(false); + mutex_unlock(&global_error_collector.mutex); +} + +#define ERROR_BLOCK() begin_error_block(); defer (end_error_block()) + + + gb_internal void error_out(char const *fmt, ...) { va_list va; va_start(va, fmt); @@ -340,6 +330,9 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { return false; } +gb_internal void error_out_empty(void) { + error_out(""); +} gb_internal void error_out_pos(TokenPos pos) { terminal_set_colours(TerminalStyle_Bold, TerminalColour_White); error_out("%s ", token_pos_to_string(pos)); @@ -357,11 +350,15 @@ gb_internal void error_out_coloured(char const *str, TerminalStyle style, Termin gb_internal void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) { global_error_collector.count.fetch_add(1); if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) { + print_all_errors(); gb_exit(1); } mutex_lock(&global_error_collector.mutex); + + push_error_value(pos, ErrorValue_Error); // NOTE(bill): Duplicate error, skip it if (pos.line == 0) { + error_out_empty(); error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red); error_out_va(fmt, va); error_out("\n"); @@ -377,6 +374,7 @@ gb_internal void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va } else { global_error_collector.count.fetch_sub(1); } + try_pop_error_value(); mutex_unlock(&global_error_collector.mutex); } @@ -387,9 +385,13 @@ gb_internal void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, } global_error_collector.warning_count.fetch_add(1); mutex_lock(&global_error_collector.mutex); + + push_error_value(pos, ErrorValue_Warning); + if (!global_ignore_warnings()) { // NOTE(bill): Duplicate error, skip it if (pos.line == 0) { + error_out_empty(); error_out_coloured("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); error_out_va(fmt, va); error_out("\n"); @@ -402,6 +404,7 @@ gb_internal void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, show_error_on_line(pos, end); } } + try_pop_error_value(); mutex_unlock(&global_error_collector.mutex); } @@ -413,11 +416,16 @@ gb_internal void error_line_va(char const *fmt, va_list va) { gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) { global_error_collector.count.fetch_add(1); if (global_error_collector.count.load() > MAX_ERROR_COLLECTOR_COUNT()) { + print_all_errors(); gb_exit(1); } mutex_lock(&global_error_collector.mutex); + + push_error_value(pos, ErrorValue_Error); + // NOTE(bill): Duplicate error, skip it if (pos.line == 0) { + error_out_empty(); error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red); error_out_va(fmt, va); } else if (global_error_collector.prev != pos) { @@ -428,6 +436,8 @@ gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_li } error_out_va(fmt, va); } + + try_pop_error_value(); mutex_unlock(&global_error_collector.mutex); } @@ -435,9 +445,13 @@ gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_li gb_internal void syntax_error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) { global_error_collector.count.fetch_add(1); if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) { + print_all_errors(); gb_exit(1); } mutex_lock(&global_error_collector.mutex); + + push_error_value(pos, ErrorValue_Warning); + // NOTE(bill): Duplicate error, skip it if (global_error_collector.prev != pos) { global_error_collector.prev = pos; @@ -447,21 +461,29 @@ gb_internal void syntax_error_va(TokenPos const &pos, TokenPos end, char const * error_out("\n"); // show_error_on_line(pos, end); } else if (pos.line == 0) { + error_out_empty(); error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red); error_out_va(fmt, va); error_out("\n"); } + + try_pop_error_value(); mutex_unlock(&global_error_collector.mutex); } gb_internal void syntax_error_with_verbose_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) { global_error_collector.count.fetch_add(1); if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) { + print_all_errors(); gb_exit(1); } mutex_lock(&global_error_collector.mutex); + + push_error_value(pos, ErrorValue_Warning); + // NOTE(bill): Duplicate error, skip it if (pos.line == 0) { + error_out_empty(); error_out_coloured("Syntax_Error: ", TerminalStyle_Normal, TerminalColour_Red); error_out_va(fmt, va); error_out("\n"); @@ -475,6 +497,8 @@ gb_internal void syntax_error_with_verbose_va(TokenPos const &pos, TokenPos end, error_out("\n"); show_error_on_line(pos, end); } + + try_pop_error_value(); mutex_unlock(&global_error_collector.mutex); } @@ -486,6 +510,10 @@ gb_internal void syntax_warning_va(TokenPos const &pos, TokenPos end, char const } mutex_lock(&global_error_collector.mutex); global_error_collector.warning_count++; + + + push_error_value(pos, ErrorValue_Warning); + if (!global_ignore_warnings()) { // NOTE(bill): Duplicate error, skip it if (global_error_collector.prev != pos) { @@ -496,11 +524,14 @@ gb_internal void syntax_warning_va(TokenPos const &pos, TokenPos end, char const error_out("\n"); // show_error_on_line(pos, end); } else if (pos.line == 0) { + error_out_empty(); error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); error_out_va(fmt, va); error_out("\n"); } } + + try_pop_error_value(); mutex_unlock(&global_error_collector.mutex); } @@ -568,6 +599,8 @@ gb_internal void syntax_error_with_verbose(TokenPos pos, TokenPos end, char cons gb_internal void compiler_error(char const *fmt, ...) { + print_all_errors(); + va_list va; va_start(va, fmt); @@ -577,3 +610,32 @@ gb_internal void compiler_error(char const *fmt, ...) { GB_DEBUG_TRAP(); gb_exit(1); } + + + + + +gb_internal int error_value_cmp(void const *a, void const *b) { + ErrorValue *x = cast(ErrorValue *)a; + ErrorValue *y = cast(ErrorValue *)b; + return token_pos_cmp(x->pos, y->pos); +} + +gb_internal void print_all_errors(void) { + GB_ASSERT(any_errors()); + gbFile *f = gb_file_get_standard(gbFileStandard_Error); + + array_sort(global_error_collector.error_values, error_value_cmp); + + for_array(i, global_error_collector.error_values) { + ErrorValue ev = global_error_collector.error_values[i]; + for (isize j = 0; j < ev.msgs.count; j++) { + String msg = ev.msgs[j]; + gb_file_write(f, msg.text, msg.len); + + if (terse_errors() && string_contains_char(msg, '\n')) { + break; + } + } + } +} \ No newline at end of file diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index ca4341525..b8ee7e7fa 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3021,7 +3021,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { } } - gb_sort_array(gen->foreign_libraries.data, gen->foreign_libraries.count, foreign_library_cmp); + array_sort(gen->foreign_libraries, foreign_library_cmp); return true; } diff --git a/src/main.cpp b/src/main.cpp index 7951ca2db..0f28e137f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2095,7 +2095,7 @@ gb_internal void print_show_unused(Checker *c) { array_add(&unused, e); } - gb_sort_array(unused.data, unused.count, cmp_entities_for_printing); + array_sort(unused, cmp_entities_for_printing); print_usage_line(0, "Unused Package Declarations"); @@ -2680,6 +2680,7 @@ int main(int arg_count, char const **arg_ptr) { } if (any_errors()) { + print_all_errors(); return 1; } @@ -2691,6 +2692,7 @@ int main(int arg_count, char const **arg_ptr) { check_parsed_files(checker); if (any_errors()) { + print_all_errors(); return 1; } diff --git a/src/string.cpp b/src/string.cpp index 8be40ec3c..7bfa52f33 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -89,7 +89,6 @@ gb_internal char *alloc_cstring(gbAllocator a, String s) { } - gb_internal gb_inline bool str_eq_ignore_case(String const &a, String const &b) { if (a.len == b.len) { for (isize i = 0; i < a.len; i++) {