From e19460cbd7c847bd5452b2c7bf96b38d06b2a182 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 10 Nov 2023 19:37:08 +0100 Subject: Add -microarch:? --- src/string.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'src/string.cpp') diff --git a/src/string.cpp b/src/string.cpp index 6eac4f53b..e85787c59 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -10,6 +10,10 @@ struct String { return text[i]; } }; +struct String_Iterator { + String const &str; + isize pos; +}; // NOTE(bill): used for printf style arguments #define LIT(x) ((int)(x).len), (x).text #if defined(GB_COMPILER_MSVC) && _MSC_VER < 1700 @@ -201,6 +205,26 @@ gb_internal gb_inline String string_trim_starts_with(String const &s, String con } +gb_internal String string_split_iterator(String_Iterator *it, const char sep) { + isize start = it->pos; + isize end = it->str.len; + + if (start == end) { + return str_lit(""); + } + + isize i = start; + for (; i < it->str.len; i++) { + if (it->str[i] == sep) { + String res = substring(it->str, start, i); + it->pos += res.len + 1; + return res; + } + } + it->pos = end; + return substring(it->str, start, end); +} + gb_internal gb_inline isize string_extension_position(String const &str) { isize dot_pos = -1; isize i = str.len; -- cgit v1.2.3 From 25e92551578466548584cfa9d4a2db25de9c6248 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 21 Nov 2023 16:53:14 +0100 Subject: Fix `string_extension_position` --- src/string.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/string.cpp') diff --git a/src/string.cpp b/src/string.cpp index 6eac4f53b..9d7ff7b89 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -205,7 +205,7 @@ gb_internal gb_inline isize string_extension_position(String const &str) { isize dot_pos = -1; isize i = str.len; while (i --> 0) { - if (str[i] == GB_PATH_SEPARATOR) + if (str[i] == '\\' || str[i] == '/') break; if (str[i] == '.') { dot_pos = i; -- cgit v1.2.3 From 5c4485f65767366c14dfd9a98945a5479ae0e449 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 9 Feb 2024 15:18:29 +0000 Subject: Add `#load_directory(path: string) > []runtime.Load_Directory_File` --- base/runtime/core.odin | 8 ++ src/check_builtin.cpp | 187 +++++++++++++++++++++++++++++++++------------- src/check_expr.cpp | 5 +- src/checker.cpp | 15 ++++ src/checker.hpp | 18 +++++ src/llvm_backend_proc.cpp | 67 +++++++++++++---- src/string.cpp | 12 +++ src/types.cpp | 4 + 8 files changed, 247 insertions(+), 69 deletions(-) (limited to 'src/string.cpp') diff --git a/base/runtime/core.odin b/base/runtime/core.odin index fbdf33085..85e64242d 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -296,6 +296,14 @@ Source_Code_Location :: struct { procedure: string, } +/* + Used by the built-in directory `#load_directory(path: string) -> []Load_Directory_File` +*/ +Load_Directory_File :: struct { + name: string, + data: []byte, // immutable data +} + Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location) -> ! // Allocation Stuff diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 4e374add6..d39be37a9 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1264,6 +1264,139 @@ gb_internal LoadDirectiveResult check_load_directive(CheckerContext *c, Operand } +gb_internal int file_cache_sort_cmp(void const *x, void const *y) { + LoadFileCache const *a = *(LoadFileCache const **)(x); + LoadFileCache const *b = *(LoadFileCache const **)(y); + return string_compare(a->path, b->path); +} + +gb_internal LoadDirectiveResult check_load_directory_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint, bool err_on_not_found) { + ast_node(ce, CallExpr, call); + ast_node(bd, BasicDirective, ce->proc); + String name = bd->name.string; + GB_ASSERT(name == "load_directory"); + + if (ce->args.count != 1) { + error(ce->args[0], "'#%.*s' expects 1 argument, got %td", LIT(name), ce->args.count); + return LoadDirective_Error; + } + + Ast *arg = ce->args[0]; + Operand o = {}; + check_expr(c, &o, arg); + if (o.mode != Addressing_Constant) { + error(arg, "'#%.*s' expected a constant string argument", LIT(name)); + return LoadDirective_Error; + } + + if (!is_type_string(o.type)) { + gbString str = type_to_string(o.type); + error(arg, "'#%.*s' expected a constant string, got %s", LIT(name), str); + gb_string_free(str); + return LoadDirective_Error; + } + + GB_ASSERT(o.value.kind == ExactValue_String); + + init_core_load_directory_file(c->checker); + + operand->type = t_load_directory_file_slice; + operand->mode = Addressing_Value; + + + String original_string = o.value.value_string; + String path; + if (gb_path_is_absolute((char*)original_string.text)) { + path = original_string; + } else { + String base_dir = dir_from_path(get_file_path_string(call->file_id)); + + BlockingMutex *ignore_mutex = nullptr; + bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path); + gb_unused(ok); + } + MUTEX_GUARD(&c->info->load_directory_mutex); + + + gbFileError file_error = gbFileError_None; + + Array file_caches = {}; + + LoadDirectoryCache **cache_ptr = string_map_get(&c->info->load_directory_cache, path); + LoadDirectoryCache *cache = cache_ptr ? *cache_ptr : nullptr; + if (cache) { + file_error = cache->file_error; + } + defer ({ + if (cache == nullptr) { + LoadDirectoryCache *new_cache = gb_alloc_item(permanent_allocator(), LoadDirectoryCache); + new_cache->path = path; + new_cache->files = file_caches; + new_cache->file_error = file_error; + string_map_set(&c->info->load_directory_cache, path, new_cache); + + map_set(&c->info->load_directory_map, call, new_cache); + } else { + cache->file_error = file_error; + } + }); + + + LoadDirectiveResult result = LoadDirective_Success; + + + if (cache == nullptr) { + Array list = {}; + ReadDirectoryError rd_err = read_directory(path, &list); + defer (array_free(&list)); + + if (list.count == 1) { + GB_ASSERT(path != list[0].fullpath); + } + + + switch (rd_err) { + case ReadDirectory_InvalidPath: + error(call, "%.*s error - invalid path: %.*s", LIT(name), LIT(original_string)); + return LoadDirective_NotFound; + case ReadDirectory_NotExists: + error(call, "%.*s error - path does not exist: %.*s", LIT(name), LIT(original_string)); + return LoadDirective_NotFound; + case ReadDirectory_Permission: + error(call, "%.*s error - unknown error whilst reading path, %.*s", LIT(name), LIT(original_string)); + return LoadDirective_Error; + case ReadDirectory_NotDir: + error(call, "%.*s error - expected a directory, got a file: %.*s", LIT(name), LIT(original_string)); + return LoadDirective_Error; + case ReadDirectory_Empty: + error(call, "%.*s error - empty directory: %.*s", LIT(name), LIT(original_string)); + return LoadDirective_NotFound; + case ReadDirectory_Unknown: + error(call, "%.*s error - unknown error whilst reading path %.*s", LIT(name), LIT(original_string)); + return LoadDirective_Error; + } + + isize files_to_reserve = list.count+1; // always reserve 1 + + file_caches = array_make(heap_allocator(), 0, files_to_reserve); + + for (FileInfo fi : list) { + LoadFileCache *cache = nullptr; + if (cache_load_file_directive(c, call, fi.fullpath, err_on_not_found, &cache)) { + array_add(&file_caches, cache); + } else { + result = LoadDirective_Error; + } + } + + gb_sort_array(file_caches.data, file_caches.count, file_cache_sort_cmp); + + } + + return result; +} + + gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint) { ast_node(ce, CallExpr, call); @@ -1291,6 +1424,8 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o operand->mode = Addressing_Value; } else if (name == "load") { return check_load_directive(c, operand, call, type_hint, true) == LoadDirective_Success; + } else if (name == "load_directory") { + return check_load_directory_directive(c, operand, call, type_hint, true) == LoadDirective_Success; } else if (name == "load_hash") { if (ce->args.count != 2) { if (ce->args.count == 0) { @@ -1408,58 +1543,6 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o return true; } return false; - } else if (name == "load_or") { - error(call, "'#load_or' has now been removed in favour of '#load(path) or_else default'"); - - if (ce->args.count != 2) { - if (ce->args.count == 0) { - error(ce->close, "'#load_or' expects 2 arguments, got 0"); - } else { - error(ce->args[0], "'#load_or' expects 2 arguments, got %td", ce->args.count); - } - return false; - } - - Ast *arg = ce->args[0]; - Operand o = {}; - check_expr(c, &o, arg); - if (o.mode != Addressing_Constant) { - error(arg, "'#load_or' expected a constant string argument"); - return false; - } - - if (!is_type_string(o.type)) { - gbString str = type_to_string(o.type); - error(arg, "'#load_or' expected a constant string, got %s", str); - gb_string_free(str); - return false; - } - - Ast *default_arg = ce->args[1]; - Operand default_op = {}; - check_expr_with_type_hint(c, &default_op, default_arg, t_u8_slice); - if (default_op.mode != Addressing_Constant) { - error(arg, "'#load_or' expected a constant '[]byte' argument"); - return false; - } - - if (!are_types_identical(base_type(default_op.type), t_u8_slice)) { - gbString str = type_to_string(default_op.type); - error(arg, "'#load_or' expected a constant '[]byte', got %s", str); - gb_string_free(str); - return false; - } - GB_ASSERT(o.value.kind == ExactValue_String); - String original_string = o.value.value_string; - - operand->type = t_u8_slice; - operand->mode = Addressing_Constant; - LoadFileCache *cache = nullptr; - if (cache_load_file_directive(c, call, original_string, false, &cache)) { - operand->value = exact_value_string(cache->data); - } else { - operand->value = default_op.value; - } } else if (name == "assert") { if (ce->args.count != 1 && ce->args.count != 2) { error(call, "'#assert' expects either 1 or 2 arguments, got %td", ce->args.count); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 9b71208cd..11eb4b533 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7107,8 +7107,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c name == "defined" || name == "config" || name == "load" || - name == "load_hash" || - name == "load_or" + name == "load_directory" || + name == "load_hash" ) { operand->mode = Addressing_Builtin; operand->builtin_id = BuiltinProc_DIRECTIVE; @@ -7958,6 +7958,7 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A name == "config" || name == "load" || name == "load_hash" || + name == "load_directory" || name == "load_or" ) { error(node, "'#%.*s' must be used as a call", LIT(name)); diff --git a/src/checker.cpp b/src/checker.cpp index 457ee6146..569a3c76f 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1257,6 +1257,9 @@ gb_internal void init_checker_info(CheckerInfo *i) { mpsc_init(&i->required_global_variable_queue, a); // 1<<10); mpsc_init(&i->required_foreign_imports_through_force_queue, a); // 1<<10); mpsc_init(&i->intrinsics_entry_point_usage, a); // 1<<10); // just waste some memory here, even if it probably never used + + string_map_init(&i->load_directory_cache); + map_init(&i->load_directory_map); } gb_internal void destroy_checker_info(CheckerInfo *i) { @@ -1280,6 +1283,8 @@ gb_internal void destroy_checker_info(CheckerInfo *i) { map_destroy(&i->objc_msgSend_types); string_map_destroy(&i->load_file_cache); + string_map_destroy(&i->load_directory_cache); + map_destroy(&i->load_directory_map); } gb_internal CheckerContext make_checker_context(Checker *c) { @@ -2958,6 +2963,16 @@ gb_internal void init_core_source_code_location(Checker *c) { t_source_code_location_ptr = alloc_type_pointer(t_source_code_location); } +gb_internal void init_core_load_directory_file(Checker *c) { + if (t_load_directory_file != nullptr) { + return; + } + t_load_directory_file = find_core_type(c, str_lit("Load_Directory_File")); + t_load_directory_file_ptr = alloc_type_pointer(t_load_directory_file); + t_load_directory_file_slice = alloc_type_slice(t_load_directory_file); +} + + gb_internal void init_core_map_type(Checker *c) { if (t_map_info != nullptr) { return; diff --git a/src/checker.hpp b/src/checker.hpp index 9da0f2950..9aee82257 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -340,6 +340,19 @@ struct LoadFileCache { StringMap hashes; }; + +struct LoadDirectoryFile { + String file_name; + String data; +}; + +struct LoadDirectoryCache { + String path; + gbFileError file_error; + Array files; +}; + + struct GenProcsData { Array procs; RwMutex mutex; @@ -416,6 +429,11 @@ struct CheckerInfo { BlockingMutex instrumentation_mutex; Entity *instrumentation_enter_entity; Entity *instrumentation_exit_entity; + + + BlockingMutex load_directory_mutex; + StringMap load_directory_cache; + PtrMap load_directory_map; // Key: Ast_CallExpr * }; struct CheckerContext { diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index e0aca2c10..9419f9a3c 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1693,24 +1693,61 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu case BuiltinProc_DIRECTIVE: { ast_node(bd, BasicDirective, ce->proc); String name = bd->name.string; - GB_ASSERT(name == "location"); - String procedure = p->entity->token.string; - TokenPos pos = ast_token(ce->proc).pos; - if (ce->args.count > 0) { - Ast *ident = unselector_expr(ce->args[0]); - GB_ASSERT(ident->kind == Ast_Ident); - Entity *e = entity_of_node(ident); - GB_ASSERT(e != nullptr); - - if (e->parent_proc_decl != nullptr && e->parent_proc_decl->entity != nullptr) { - procedure = e->parent_proc_decl->entity->token.string; - } else { - procedure = str_lit(""); + if (name == "location") { + String procedure = p->entity->token.string; + TokenPos pos = ast_token(ce->proc).pos; + if (ce->args.count > 0) { + Ast *ident = unselector_expr(ce->args[0]); + GB_ASSERT(ident->kind == Ast_Ident); + Entity *e = entity_of_node(ident); + GB_ASSERT(e != nullptr); + + if (e->parent_proc_decl != nullptr && e->parent_proc_decl->entity != nullptr) { + procedure = e->parent_proc_decl->entity->token.string; + } else { + procedure = str_lit(""); + } + pos = e->token.pos; + } - pos = e->token.pos; + return lb_emit_source_code_location_as_global(p, procedure, pos); + } else if (name == "load_directory") { + lbModule *m = p->module; + TEMPORARY_ALLOCATOR_GUARD(); + LoadDirectoryCache *cache = map_must_get(&m->info->load_directory_map, expr); + isize count = cache->files.count; + + LLVMValueRef *elements = gb_alloc_array(temporary_allocator(), LLVMValueRef, count); + for_array(i, cache->files) { + LoadFileCache *file = cache->files[i]; + String file_name = filename_without_directory(file->path); + + LLVMValueRef values[2] = {}; + values[0] = lb_const_string(m, file_name).value; + values[1] = lb_const_string(m, file->data).value; + LLVMValueRef element = llvm_const_named_struct(m, t_load_directory_file, values, gb_count_of(values)); + elements[i] = element; + } + + LLVMValueRef backing_array = llvm_const_array(lb_type(m, t_load_directory_file), elements, count); + + Type *array_type = alloc_type_array(t_load_directory_file, count); + lbAddr backing_array_addr = lb_add_global_generated(m, array_type, {backing_array, array_type}, nullptr); + lb_make_global_private_const(backing_array_addr); + + LLVMValueRef backing_array_ptr = backing_array_addr.addr.value; + backing_array_ptr = LLVMConstPointerCast(backing_array_ptr, lb_type(m, t_load_directory_file_ptr)); + + LLVMValueRef const_slice = llvm_const_slice_internal(m, backing_array_ptr, LLVMConstInt(lb_type(m, t_int), count, false)); + + lbAddr addr = lb_add_global_generated(p->module, tv.type, {const_slice, t_load_directory_file_slice}, nullptr); + lb_make_global_private_const(addr); + + return lb_addr_load(p, addr); + } else { + GB_PANIC("UNKNOWN DIRECTIVE: %.*s", LIT(name)); } - return lb_emit_source_code_location_as_global(p, procedure, pos); } case BuiltinProc_type_info_of: { diff --git a/src/string.cpp b/src/string.cpp index 9fb933b1b..bd703b2a6 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -293,6 +293,18 @@ gb_internal String filename_from_path(String s) { return make_string(nullptr, 0); } + +gb_internal String filename_without_directory(String s) { + isize j = 0; + for (j = s.len-1; j >= 0; j--) { + if (s[j] == '/' || + s[j] == '\\') { + break; + } + } + return substring(s, gb_max(j+1, 0), s.len); +} + gb_internal String concatenate_strings(gbAllocator a, String const &x, String const &y) { isize len = x.len+y.len; u8 *data = gb_alloc_array(a, u8, len+1); diff --git a/src/types.cpp b/src/types.cpp index c4b03c967..8275b87ba 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -679,6 +679,10 @@ gb_global Type *t_allocator_error = nullptr; gb_global Type *t_source_code_location = nullptr; gb_global Type *t_source_code_location_ptr = nullptr; +gb_global Type *t_load_directory_file = nullptr; +gb_global Type *t_load_directory_file_ptr = nullptr; +gb_global Type *t_load_directory_file_slice = nullptr; + gb_global Type *t_map_info = nullptr; gb_global Type *t_map_cell_info = nullptr; gb_global Type *t_raw_map = nullptr; -- cgit v1.2.3 From 00344e1323dc6d9baf09c26f31c409f26a0a1cca Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 18 Mar 2024 16:56:01 +0000 Subject: Add check to people trying to `foreign import` C files. --- src/checker.cpp | 16 ++++++++++++++++ src/string.cpp | 7 +++++++ 2 files changed, 23 insertions(+) (limited to 'src/string.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index 72c0ae574..fb7d401ab 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -4806,6 +4806,22 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) { return; } + for (String const &path : fl->fullpaths) { + String ext = path_extension(path); + if (str_eq_ignore_case(ext, ".c") || + str_eq_ignore_case(ext, ".cpp") || + str_eq_ignore_case(ext, ".cxx") || + str_eq_ignore_case(ext, ".h") || + str_eq_ignore_case(ext, ".hpp") || + str_eq_ignore_case(ext, ".hxx") || + false + ) { + error(fl->token, "With 'foreign import', you cannot import a %.*s file directory, you must precompile the library and link against that", LIT(ext)); + break; + } + } + + // if (fl->collection_name != "system") { // char *c_str = gb_alloc_array(heap_allocator(), char, fullpath.len+1); // defer (gb_free(heap_allocator(), c_str)); diff --git a/src/string.cpp b/src/string.cpp index bd703b2a6..f762dca40 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -104,6 +104,13 @@ gb_internal gb_inline bool str_eq_ignore_case(String const &a, String const &b) return false; } +template +gb_internal gb_inline bool str_eq_ignore_case(String const &a, char const (&b_)[N]) { + String b = {cast(u8 *)b_, N-1}; + return str_eq_ignore_case(a, b); +} + + gb_internal void string_to_lower(String *s) { for (isize i = 0; i < s->len; i++) { s->text[i] = gb_char_to_lower(s->text[i]); -- cgit v1.2.3 From 97be7feb99d4ff2b26cde0426c619e50bb0d758a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 18 Mar 2024 17:32:26 +0000 Subject: Add list of C identifier suggestions (types and keywords) --- src/check_expr.cpp | 68 +++++++++++++++++++++++++++++++++++++++++++----------- src/string.cpp | 3 +++ 2 files changed, 57 insertions(+), 14 deletions(-) (limited to 'src/string.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b58006427..7da113455 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1528,6 +1528,55 @@ gb_internal bool check_cycle(CheckerContext *c, Entity *curr, bool report) { return false; } +struct CIdentSuggestion { + String name; + String msg; +}; + +// NOTE(bill): this linear look-up table might be slow but because it's an error case, it should be fine +gb_internal CIdentSuggestion const c_ident_suggestions[] = { + {str_lit("while"), str_lit("Did you mean 'for'? Odin only has one loop construct: 'for'")}, + + {str_lit("sizeof"), str_lit("Did you mean 'size_of'?")}, + {str_lit("alignof"), str_lit("Did you mean 'align_of'?")}, + {str_lit("offsetof"), str_lit("Did you mean 'offset_of'?")}, + + {str_lit("_Bool"), str_lit("Did you mean 'bool'?")}, + + {str_lit("char"), str_lit("Did you mean 'u8', 'i8', or 'c.char' (which is part of 'core:c')?")}, + {str_lit("short"), str_lit("Did you mean 'i16' or 'c.short' (which is part of 'core:c')?")}, + {str_lit("long"), str_lit("Did you mean 'c.long' (which is part of 'core:c')?")}, + {str_lit("float"), str_lit("Did you mean 'f32'?")}, + {str_lit("double"), str_lit("Did you mean 'f64'?")}, + {str_lit("unsigned"), str_lit("Did you mean 'c.uint' (which is part of 'core:c')?")}, + {str_lit("signed"), str_lit("Did you mean 'c.int' (which is part of 'core:c')?")}, + + {str_lit("size_t"), str_lit("Did you mean 'uint', or 'c.size_t' (which is part of 'core:c')?")}, + {str_lit("ssize_t"), str_lit("Did you mean 'int', or 'c.ssize_t' (which is part of 'core:c')?")}, + + {str_lit("uintptr_t"), str_lit("Did you mean 'uintptr'?")}, + {str_lit("intptr_t"), str_lit("Did you mean 'uintptr' or `int` or something else?")}, + {str_lit("ptrdiff_t"), str_lit("Did you mean 'int' or 'c.ptrdiff_t' (which is part of 'core:c')?")}, + {str_lit("intmax_t"), str_lit("Dit you mean 'c.intmax_t' (which is part of 'core:c')?")}, + {str_lit("uintmax_t"), str_lit("Dit you mean 'c.uintmax_t' (which is part of 'core:c')?")}, + + {str_lit("uint8_t"), str_lit("Did you mean 'u8'?")}, + {str_lit("int8_t"), str_lit("Did you mean 'i8'?")}, + {str_lit("uint16_t"), str_lit("Did you mean 'u16'?")}, + {str_lit("int16_t"), str_lit("Did you mean 'i16'?")}, + {str_lit("uint32_t"), str_lit("Did you mean 'u32'?")}, + {str_lit("int32_t"), str_lit("Did you mean 'i32'?")}, + {str_lit("uint64_t"), str_lit("Did you mean 'u64'?")}, + {str_lit("int64_t"), str_lit("Did you mean 'i64'?")}, + {str_lit("uint128_t"), str_lit("Did you mean 'u128'?")}, + {str_lit("int128_t"), str_lit("Did you mean 'i128'?")}, + + {str_lit("float32"), str_lit("Did you mean 'f32'?")}, + {str_lit("float64"), str_lit("Did you mean 'f64'?")}, + {str_lit("float32_t"), str_lit("Did you mean 'f32'?")}, + {str_lit("float64_t"), str_lit("Did you mean 'f64'?")}, +}; + gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name) { GB_ASSERT(n->kind == Ast_Ident); o->mode = Addressing_Invalid; @@ -1543,20 +1592,11 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam error(n, "Undeclared name: %.*s", LIT(name)); // NOTE(bill): Loads of checks for C programmers - if (name == "float") { - error_line("\tSuggestion: Did you mean 'f32'?\n"); - } else if (name == "double") { - error_line("\tSuggestion: Did you mean 'f64'?\n"); - } else if (name == "short") { - error_line("\tSuggestion: Did you mean 'i16' or 'c.short' (which is part of 'core:c')?\n"); - } else if (name == "long") { - error_line("\tSuggestion: Did you mean 'c.long' (which is part of 'core:c')?\n"); - } else if (name == "unsigned") { - error_line("\tSuggestion: Did you mean 'c.uint' (which is part of 'core:c')?\n"); - } else if (name == "char") { - error_line("\tSuggestion: Did you mean 'u8', 'i8' or 'c.char' (which is part of 'core:c')?\n"); - } else if (name == "while") { - error_line("\tSuggestion: Did you mean 'for'? Odin only has one loop construct: 'for'\n"); + + for (CIdentSuggestion const &suggestion : c_ident_suggestions) { + if (name == suggestion.name) { + error_line("\tSuggestion: %s\n", LIT(suggestion.msg)); + } } } o->type = t_invalid; diff --git a/src/string.cpp b/src/string.cpp index f762dca40..8be40ec3c 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -106,6 +106,9 @@ gb_internal gb_inline bool str_eq_ignore_case(String const &a, String const &b) template gb_internal gb_inline bool str_eq_ignore_case(String const &a, char const (&b_)[N]) { + if (a.len != N-1) { + return false; + } String b = {cast(u8 *)b_, N-1}; return str_eq_ignore_case(a, b); } -- cgit v1.2.3 From 9a2fc6cf4c8b4434ae45170953b77b3239120fea Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 Mar 2024 15:34:29 +0000 Subject: Serialize errors to make them sortable, deterministic, and generally more control --- src/array.cpp | 7 ++ src/build_settings.cpp | 4 + src/check_builtin.cpp | 2 +- src/check_expr.cpp | 2 +- src/checker.cpp | 8 +- src/common.cpp | 2 +- src/docs.cpp | 4 +- src/docs_writer.cpp | 2 +- src/error.cpp | 213 ++++++++++++++++++++++++++++++------------------- src/llvm_backend.cpp | 2 +- src/main.cpp | 4 +- src/string.cpp | 1 - 12 files changed, 158 insertions(+), 93 deletions(-) (limited to 'src/string.cpp') 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 236d44a43..f359d5a54 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6485,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); 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/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..e5803e5a2 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -1,3 +1,14 @@ +enum ErrorValueKind : u32 { + ErrorValue_Error, + ErrorValue_Warning, +}; + +struct ErrorValue { + ErrorValueKind kind; + TokenPos pos; + Array msgs; +}; + struct ErrorCollector { TokenPos prev; std::atomic count; @@ -8,21 +19,54 @@ struct ErrorCollector { BlockingMutex string_mutex; RecursiveMutex block_mutex; - RecursiveMutex error_buffer_mutex; - Array error_buffer; - Array errors; + 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); } @@ -102,6 +146,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,55 +158,32 @@ 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 print_all_errors(void); -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; +#define ERROR_OUT_PROC(name) void name(char const *fmt, va_list va) +typedef ERROR_OUT_PROC(ErrorOutProc); - 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; - } - } +gb_internal ERROR_OUT_PROC(default_error_out_va) { + char buf[4096] = {}; + isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va); + isize n = len-1; - if (add_extra_newline) { - // add an extra new line as padding when the error line is being shown - error_line("\n"); - } + String msg = {(u8 *)buf, 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; + ErrorValue *ev = get_error_value(); + array_add(&ev->msgs, copy_string(permanent_allocator(), msg)); +} +gb_global ErrorOutProc *error_out_va = default_error_out_va; - 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); +gb_internal void begin_error_block(void) { + mutex_lock(&global_error_collector.block_mutex); + global_error_collector.in_block.store(true); +} - global_error_collector.error_buffer.count = 0; - } - mutex_unlock(&global_error_collector.error_buffer_mutex); +gb_internal void end_error_block(void) { + pop_error_value(); global_error_collector.in_block.store(false); mutex_unlock(&global_error_collector.block_mutex); } @@ -169,40 +191,6 @@ gb_internal void end_error_block(void) { #define ERROR_BLOCK() begin_error_block(); defer (end_error_block()) -#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); - - } - gb_file_write(f, buf, n); -} - -gb_global ErrorOutProc *error_out_va = default_error_out_va; gb_internal void error_out(char const *fmt, ...) { va_list va; @@ -357,9 +345,12 @@ 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_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red); @@ -377,6 +368,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,6 +379,9 @@ 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) { @@ -402,6 +397,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,9 +409,13 @@ 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_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red); @@ -428,6 +428,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 +437,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; @@ -451,15 +457,21 @@ gb_internal void syntax_error_va(TokenPos const &pos, TokenPos end, char const * 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_coloured("Syntax_Error: ", TerminalStyle_Normal, TerminalColour_Red); @@ -475,6 +487,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 +500,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) { @@ -501,6 +519,8 @@ gb_internal void syntax_warning_va(TokenPos const &pos, TokenPos end, char const error_out("\n"); } } + + try_pop_error_value(); mutex_unlock(&global_error_collector.mutex); } @@ -568,6 +588,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 +599,34 @@ 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_array(j, ev.msgs) { + String msg = ev.msgs[j]; + gb_file_write(f, msg.text, msg.len); + if (terse_errors()) { + if (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++) { -- cgit v1.2.3 From 36644a3c09630cc75e7826ec443bb760bdbbd4af Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 15 Apr 2024 12:43:45 +0100 Subject: Add template specialization for compared against `""` with `String` internally --- src/string.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/string.cpp') diff --git a/src/string.cpp b/src/string.cpp index 7bfa52f33..4adec7a90 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -171,6 +171,9 @@ template gb_internal bool operator > (String const &a, char const (&b template gb_internal bool operator <= (String const &a, char const (&b)[N]) { return str_le(a, make_string(cast(u8 *)b, N-1)); } template gb_internal bool operator >= (String const &a, char const (&b)[N]) { return str_ge(a, make_string(cast(u8 *)b, N-1)); } +template <> bool operator == (String const &a, char const (&b)[1]) { return a.len == 0; } +template <> bool operator != (String const &a, char const (&b)[1]) { return a.len != 0; } + gb_internal gb_inline bool string_starts_with(String const &s, String const &prefix) { if (prefix.len > s.len) { return false; -- cgit v1.2.3 From 8a0f9ae108a75d9ca86b8a91fca2f2423e0a58df Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 16 Apr 2024 13:15:23 +0100 Subject: Print to string buffer before printing errors --- src/error.cpp | 146 +++++++++++++++++++++++++++++++++------------------------ src/string.cpp | 37 +++++++++++++++ 2 files changed, 121 insertions(+), 62 deletions(-) (limited to 'src/string.cpp') diff --git a/src/error.cpp b/src/error.cpp index 2e6641e3b..8c9fb265b 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -7,7 +7,8 @@ struct ErrorValue { ErrorValueKind kind; TokenPos pos; TokenPos end; - Array msgs; + Array msg; + bool seen_newline; }; struct ErrorCollector { @@ -30,19 +31,21 @@ gb_global ErrorCollector global_error_collector; gb_internal void push_error_value(TokenPos const &pos, ErrorValueKind kind = ErrorValue_Error) { GB_ASSERT_MSG(global_error_collector.curr_error_value_set.load() == false, "Possible race condition in error handling system, please report this with an issue"); ErrorValue ev = {kind, pos}; - ev.msgs.allocator = heap_allocator(); + ev.msg.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) { + mutex_lock(&global_error_collector.mutex); 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); } + mutex_unlock(&global_error_collector.mutex); } @@ -180,9 +183,18 @@ gb_internal ERROR_OUT_PROC(default_error_out_va) { isize n = len-1; if (n > 0) { - String msg = copy_string(permanent_allocator(), {(u8 *)buf, n}); ErrorValue *ev = get_error_value(); - array_add(&ev->msgs, msg); + if (terse_errors()) { + for (isize i = 0; i < n && !ev->seen_newline; i++) { + u8 c = cast(u8)buf[i]; + if (c == '\n') { + ev->seen_newline = true; + } + array_add(&ev->msg, c); + } + } else { + array_add_elems(&ev->msg, (u8 *)buf, n); + } } } @@ -645,109 +657,119 @@ gb_internal int error_value_cmp(void const *a, void const *b) { } gb_internal void print_all_errors(void) { - auto const &escape_char = [](gbFile *f, u8 c) { + auto const &escape_char = [](gbString res, u8 c) -> gbString { switch (c) { - case '\n': gb_file_write(f, "\\n", 2); break; - case '"': gb_file_write(f, "\\\"", 2); break; - case '\\': gb_file_write(f, "\\\\", 2); break; - case '\b': gb_file_write(f, "\\b", 2); break; - case '\f': gb_file_write(f, "\\f", 2); break; - case '\r': gb_file_write(f, "\\r", 2); break; - case '\t': gb_file_write(f, "\\t", 2); break; + case '\n': res = gb_string_append_length(res, "\\n", 2); break; + case '"': res = gb_string_append_length(res, "\\\"", 2); break; + case '\\': res = gb_string_append_length(res, "\\\\", 2); break; + case '\b': res = gb_string_append_length(res, "\\b", 2); break; + case '\f': res = gb_string_append_length(res, "\\f", 2); break; + case '\r': res = gb_string_append_length(res, "\\r", 2); break; + case '\t': res = gb_string_append_length(res, "\\t", 2); break; default: if ('\x00' <= c && c <= '\x1f') { - gb_fprintf(f, "\\u%04x", c); + res = gb_string_append_fmt(res, "\\u%04x", c); } else { - gb_file_write(f, &c, 1); + res = gb_string_append_length(res, &c, 1); } break; } + return res; }; GB_ASSERT(any_errors() || any_warnings()); - gbFile *f = gb_file_get_standard(gbFileStandard_Error); + array_sort(global_error_collector.error_values, error_value_cmp); + gbString res = gb_string_make(heap_allocator(), ""); + defer (gb_string_free(res)); if (json_errors()) { - gb_fprintf(f, "{\n"); - gb_fprintf(f, "\t\"error_count\": %td,\n", global_error_collector.error_values.count); - gb_fprintf(f, "\t\"errors\": [\n"); + res = gb_string_append_fmt(res, "{\n"); + res = gb_string_append_fmt(res, "\t\"error_count\": %td,\n", global_error_collector.error_values.count); + res = gb_string_append_fmt(res, "\t\"errors\": [\n"); for_array(i, global_error_collector.error_values) { ErrorValue ev = global_error_collector.error_values[i]; - gb_fprintf(f, "\t\t{\n"); + res = gb_string_append_fmt(res, "\t\t{\n"); - gb_fprintf(f, "\t\t\t\"type\": \""); + res = gb_string_append_fmt(res, "\t\t\t\"type\": \""); if (ev.kind == ErrorValue_Warning) { - gb_fprintf(f, "warning"); + res = gb_string_append_fmt(res, "warning"); } else { - gb_fprintf(f, "error"); + res = gb_string_append_fmt(res, "error"); } - gb_fprintf(f, "\",\n"); + res = gb_string_append_fmt(res, "\",\n"); - gb_fprintf(f, "\t\t\t\"pos\": {\n"); + res = gb_string_append_fmt(res, "\t\t\t\"pos\": {\n"); if (ev.pos.file_id) { - gb_fprintf(f, "\t\t\t\t\"file\": \""); + res = gb_string_append_fmt(res, "\t\t\t\t\"file\": \""); String file = get_file_path_string(ev.pos.file_id); for (isize k = 0; k < file.len; k++) { - escape_char(f, file.text[k]); + res = escape_char(res, file.text[k]); } - gb_fprintf(f, "\",\n"); - gb_fprintf(f, "\t\t\t\t\"offset\": %d,\n", ev.pos.offset); - gb_fprintf(f, "\t\t\t\t\"line\": %d,\n", ev.pos.line); - gb_fprintf(f, "\t\t\t\t\"column\": %d,\n", ev.pos.column); + res = gb_string_append_fmt(res, "\",\n"); + res = gb_string_append_fmt(res, "\t\t\t\t\"offset\": %d,\n", ev.pos.offset); + res = gb_string_append_fmt(res, "\t\t\t\t\"line\": %d,\n", ev.pos.line); + res = gb_string_append_fmt(res, "\t\t\t\t\"column\": %d,\n", ev.pos.column); i32 end_column = gb_max(ev.end.column, ev.pos.column); - gb_fprintf(f, "\t\t\t\t\"end_column\": %d\n", end_column); - gb_fprintf(f, "\t\t\t},\n"); + res = gb_string_append_fmt(res, "\t\t\t\t\"end_column\": %d\n", end_column); + res = gb_string_append_fmt(res, "\t\t\t},\n"); } - gb_fprintf(f, "\t\t\t\"msgs\": [\n"); - - if (ev.msgs.count > 1) { - gb_fprintf(f, "\t\t\t\t\""); - - for (isize j = 1; j < ev.msgs.count; j++) { - String msg = ev.msgs[j]; - for (isize k = 0; k < msg.len; k++) { - u8 c = msg.text[k]; - if (c == '\n') { - if (k+1 == msg.len && j+1 == ev.msgs.count) { - // don't do the last one - } else { - gb_fprintf(f, "\",\n"); - gb_fprintf(f, "\t\t\t\t\""); - } - } else { - escape_char(f, c); - } + res = gb_string_append_fmt(res, "\t\t\t\"msgs\": [\n"); + + auto lines = split_lines_from_array(ev.msg, heap_allocator()); + defer (array_free(&lines)); + + if (lines.count > 0) { + res = gb_string_append_fmt(res, "\t\t\t\t\""); + + for (isize j = 0; j < lines.count; j++) { + String line = lines[j]; + for (isize k = 0; k < line.len; k++) { + u8 c = line.text[k]; + res = escape_char(res, c); + } + if (j+1 < lines.count) { + res = gb_string_append_fmt(res, "\",\n"); + res = gb_string_append_fmt(res, "\t\t\t\t\""); } } - gb_fprintf(f, "\"\n"); + res = gb_string_append_fmt(res, "\"\n"); } - gb_fprintf(f, "\t\t\t]\n"); - gb_fprintf(f, "\t\t}"); + res = gb_string_append_fmt(res, "\t\t\t]\n"); + res = gb_string_append_fmt(res, "\t\t}"); if (i+1 != global_error_collector.error_values.count) { - gb_fprintf(f, ","); + res = gb_string_append_fmt(res, ","); } - gb_fprintf(f, "\n"); + res = gb_string_append_fmt(res, "\n"); } - gb_fprintf(f, "\t]\n"); - gb_fprintf(f, "}\n"); + res = gb_string_append_fmt(res, "\t]\n"); + res = gb_string_append_fmt(res, "}\n"); } else { 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')) { + String_Iterator it = {{ev.msg.data, ev.msg.count}, 0}; + + for (isize line_idx = 0; /**/; line_idx++) { + String line = string_split_iterator(&it, '\n'); + if (line.len == 0) { + break; + } + line = string_trim_trailing_whitespace(line); + res = gb_string_append_length(res, line.text, line.len); + res = gb_string_append_length(res, " \n", 2); + if (line_idx == 0 && terse_errors()) { break; } } } } + gbFile *f = gb_file_get_standard(gbFileStandard_Error); + gb_file_write(f, res, gb_string_length(res)); } \ No newline at end of file diff --git a/src/string.cpp b/src/string.cpp index 4adec7a90..3747f4564 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -276,6 +276,43 @@ gb_internal String string_trim_whitespace(String str) { return str; } +gb_internal String string_trim_trailing_whitespace(String str) { + while (str.len > 0) { + u8 c = str[str.len-1]; + if (rune_is_whitespace(c) || c == 0) { + str.len -= 1; + } else { + break; + } + } + return str; +} + +gb_internal String split_lines_first_line_from_array(Array const &array, gbAllocator allocator) { + String_Iterator it = {{array.data, array.count}, 0}; + + String line = string_split_iterator(&it, '\n'); + line = string_trim_trailing_whitespace(line); + return line; +} + +gb_internal Array split_lines_from_array(Array const &array, gbAllocator allocator) { + Array lines = {}; + lines.allocator = allocator; + + String_Iterator it = {{array.data, array.count}, 0}; + + for (;;) { + String line = string_split_iterator(&it, '\n'); + if (line.len == 0) { + break; + } + line = string_trim_trailing_whitespace(line); + array_add(&lines, line); + } + + return lines; +} gb_internal bool string_contains_char(String const &s, u8 c) { isize i; -- cgit v1.2.3 From 60ef4fda4dd71e5474bb2598c4e0d18c58924e99 Mon Sep 17 00:00:00 2001 From: joakin Date: Wed, 3 Apr 2024 14:03:56 +0200 Subject: Recognize dynamic library names like libraylib.so.5.0.0 --- src/linker.cpp | 2 +- src/parser.cpp | 2 +- src/string.cpp | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) (limited to 'src/string.cpp') diff --git a/src/linker.cpp b/src/linker.cpp index 498a96c5f..245275bd3 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -432,7 +432,7 @@ gb_internal i32 linker_stage(LinkerData *gen) { if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o"))) { // static libs and object files, absolute full path relative to the file in which the lib was imported from lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib)); - } else if (string_ends_with(lib, str_lit(".so"))) { + } else if (string_ends_with(lib, str_lit(".so")) || string_contains_string(lib, str_lit(".so."))) { // dynamic lib, relative path to executable // NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible // at runtime to the executable diff --git a/src/parser.cpp b/src/parser.cpp index f4d3dc48d..2bf25c768 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5710,7 +5710,7 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node // working directory of the exe to the library search paths. // Static libraries can be linked directly with the full pathname // - if (node->kind == Ast_ForeignImportDecl && string_ends_with(file_str, str_lit(".so"))) { + if (node->kind == Ast_ForeignImportDecl && (string_ends_with(file_str, str_lit(".so")) || string_contains_string(file_str, str_lit(".so.")))) { *path = file_str; return true; } diff --git a/src/string.cpp b/src/string.cpp index 3747f4564..a68cf315f 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -323,6 +323,25 @@ gb_internal bool string_contains_char(String const &s, u8 c) { return false; } +gb_internal bool string_contains_string(String const &haystack, String const &needle) { + if (needle.len == 0) return true; + if (needle.len > haystack.len) return false; + + for (isize i = 0; i <= haystack.len - needle.len; i++) { + bool found = true; + for (isize j = 0; j < needle.len; j++) { + if (haystack[i + j] != needle[j]) { + found = false; + break; + } + } + if (found) { + return true; + } + } + return false; +} + gb_internal String filename_from_path(String s) { isize i = string_extension_position(s); if (i >= 0) { -- cgit v1.2.3 From 0a16f7a6f1e3e40dfed7cb93725d325787bc948b Mon Sep 17 00:00:00 2001 From: Thomas la Cour Date: Tue, 26 Mar 2024 12:22:18 +0100 Subject: normalize_path --- src/build_settings.cpp | 6 ++---- src/string.cpp | 34 ++++++++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 10 deletions(-) (limited to 'src/string.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index b806adcd6..03a95a19b 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -840,13 +840,11 @@ gb_internal String odin_root_dir(void) { char const *found = gb_get_env("ODIN_ROOT", a); if (found) { String path = path_to_full_path(a, make_string_c(found)); - if (path[path.len-1] != '/' && path[path.len-1] != '\\') { #if defined(GB_SYSTEM_WINDOWS) - path = concatenate_strings(a, path, WIN32_SEPARATOR_STRING); + path = normalize_path(a, path, WIN32_SEPARATOR_STRING); #else - path = concatenate_strings(a, path, NIX_SEPARATOR_STRING); + path = normalize_path(a, path, NIX_SEPARATOR_STRING); #endif - } global_module_path = path; global_module_path_set = true; diff --git a/src/string.cpp b/src/string.cpp index 3747f4564..b92dd589e 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -237,11 +237,16 @@ gb_internal String string_split_iterator(String_Iterator *it, const char sep) { return substring(it->str, start, end); } +gb_internal gb_inline bool is_separator(u8 const &ch) { + return (ch == '/' || ch == '\\'); +} + + gb_internal gb_inline isize string_extension_position(String const &str) { isize dot_pos = -1; isize i = str.len; while (i --> 0) { - if (str[i] == '\\' || str[i] == '/') + if (is_separator(str[i])) break; if (str[i] == '.') { dot_pos = i; @@ -332,8 +337,7 @@ gb_internal String filename_from_path(String s) { if (i > 0) { isize j = 0; for (j = s.len-1; j >= 0; j--) { - if (s[j] == '/' || - s[j] == '\\') { + if (is_separator(s[j])) { break; } } @@ -346,8 +350,7 @@ gb_internal String filename_from_path(String s) { gb_internal String filename_without_directory(String s) { isize j = 0; for (j = s.len-1; j >= 0; j--) { - if (s[j] == '/' || - s[j] == '\\') { + if (is_separator(s[j])) { break; } } @@ -410,7 +413,26 @@ gb_internal String copy_string(gbAllocator a, String const &s) { return make_string(data, s.len); } - +gb_internal String normalize_path(gbAllocator a, String const &path, String const &sep) { + String s; + if (sep.len < 1) { + return path; + } + if (path.len < 1) { + s = STR_LIT(""); + } else if (is_separator(path[path.len-1])) { + s = copy_string(a, path); + } else { + s = concatenate_strings(a, path, sep); + } + isize i; + for (i = 0; i < s.len; i++) { + if (is_separator(s.text[i])) { + s.text[i] = sep.text[0]; + } + } + return s; +} #if defined(GB_SYSTEM_WINDOWS) -- cgit v1.2.3 From 7dd4cccce73dfa7da86e97784a284dfb86cd73f5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Jul 2024 14:21:07 +0100 Subject: Use a temporary directory for -use-separate-modules Windows only currently --- src/llvm_backend.cpp | 37 ++++++++++++++++++++++++++--------- src/string.cpp | 54 ++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 66 insertions(+), 25 deletions(-) (limited to 'src/string.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 343edaeb0..375abb0dd 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2556,20 +2556,37 @@ gb_internal String lb_filepath_ll_for_module(lbModule *m) { return path; } + gb_internal String lb_filepath_obj_for_module(lbModule *m) { - String path = concatenate3_strings(permanent_allocator(), - build_context.build_paths[BuildPath_Output].basename, - STR_LIT("/"), - build_context.build_paths[BuildPath_Output].name - ); + String basename = build_context.build_paths[BuildPath_Output].basename; + String name = build_context.build_paths[BuildPath_Output].name; + + bool use_temporary_directory = false; + if (USE_SEPARATE_MODULES && build_context.build_mode == BuildMode_Executable) { + // NOTE(bill): use a temporary directory + String dir = temporary_directory(permanent_allocator()); + if (dir.len != 0) { + basename = dir; + use_temporary_directory = true; + } + } + + gbString path = gb_string_make_length(heap_allocator(), basename.text, basename.len); + path = gb_string_appendc(path, "/"); + path = gb_string_append_length(path, name.text, name.len); if (m->file) { char buf[32] = {}; isize n = gb_snprintf(buf, gb_size_of(buf), "-%u", m->file->id); String suffix = make_string((u8 *)buf, n-1); - path = concatenate_strings(permanent_allocator(), path, suffix); + path = gb_string_append_length(path, suffix.text, suffix.len); } else if (m->pkg) { - path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name); + path = gb_string_appendc(path, "-"); + path = gb_string_append_length(path, m->pkg->name.text, m->pkg->name.len); + } + + if (use_temporary_directory) { + path = gb_string_append_fmt(path, "-%p", m); } String ext = {}; @@ -2607,7 +2624,10 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) { } } - return concatenate_strings(permanent_allocator(), path, ext); + path = gb_string_append_length(path, ext.text, ext.len); + + return make_string(cast(u8 *)path, gb_string_length(path)); + } @@ -2666,7 +2686,6 @@ gb_internal bool lb_llvm_object_generation(lbGenerator *gen, bool do_threading) String filepath_ll = lb_filepath_ll_for_module(m); String filepath_obj = lb_filepath_obj_for_module(m); - // gb_printf_err("%.*s\n", LIT(filepath_obj)); array_add(&gen->output_object_paths, filepath_obj); array_add(&gen->output_temp_paths, filepath_ll); diff --git a/src/string.cpp b/src/string.cpp index 86eddeddd..ab08e3d4a 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -329,22 +329,22 @@ gb_internal bool string_contains_char(String const &s, u8 c) { } gb_internal bool string_contains_string(String const &haystack, String const &needle) { - if (needle.len == 0) return true; - if (needle.len > haystack.len) return false; - - for (isize i = 0; i <= haystack.len - needle.len; i++) { - bool found = true; - for (isize j = 0; j < needle.len; j++) { - if (haystack[i + j] != needle[j]) { - found = false; - break; - } - } - if (found) { - return true; - } - } - return false; + if (needle.len == 0) return true; + if (needle.len > haystack.len) return false; + + for (isize i = 0; i <= haystack.len - needle.len; i++) { + bool found = true; + for (isize j = 0; j < needle.len; j++) { + if (haystack[i + j] != needle[j]) { + found = false; + break; + } + } + if (found) { + return true; + } + } + return false; } gb_internal String filename_from_path(String s) { @@ -543,6 +543,28 @@ gb_internal String string16_to_string(gbAllocator a, String16 s) { +gb_internal String temporary_directory(gbAllocator allocator) { + String res = {}; +#if defined(GB_SYSTEM_WINDOWS) + DWORD n = GetTempPathW(0, nullptr); + if (n == 0) { + return res; + } + DWORD len = gb_max(MAX_PATH, n); + wchar_t *b = gb_alloc_array(heap_allocator(), wchar_t, len+1); + defer (gb_free(heap_allocator(), b)); + n = GetTempPathW(len, b); + if (n == 3 && b[1] == ':' && b[2] == '\\') { + + } else if (n > 0 && b[n-1] == '\\') { + n -= 1; + } + b[n] = 0; + String16 s = make_string16(b, n); + res = string16_to_string(allocator, s); +#endif + return res; +} -- cgit v1.2.3 From 9ff77397c6ee59833d8271cd1dac757f42092805 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Mon, 8 Jul 2024 18:58:17 +0200 Subject: implement `temporary_directory` on non-windows --- src/string.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'src/string.cpp') diff --git a/src/string.cpp b/src/string.cpp index ab08e3d4a..42bf51f80 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -544,11 +544,10 @@ gb_internal String string16_to_string(gbAllocator a, String16 s) { gb_internal String temporary_directory(gbAllocator allocator) { - String res = {}; #if defined(GB_SYSTEM_WINDOWS) DWORD n = GetTempPathW(0, nullptr); if (n == 0) { - return res; + return String{0}; } DWORD len = gb_max(MAX_PATH, n); wchar_t *b = gb_alloc_array(heap_allocator(), wchar_t, len+1); @@ -561,9 +560,22 @@ gb_internal String temporary_directory(gbAllocator allocator) { } b[n] = 0; String16 s = make_string16(b, n); - res = string16_to_string(allocator, s); + return string16_to_string(allocator, s); +#else + char const *tmp_env = gb_get_env("TMPDIR", allocator); + if (tmp_env) { + return make_string_c(tmp_env); + } + +#if defined(P_tmpdir) + String tmp_macro = make_string_c(P_tmpdir); + if (tmp_macro.len != 0) { + return copy_string(allocator, tmp_macro); + } +#endif + + return copy_string(allocator, str_lit("/tmp")); #endif - return res; } -- cgit v1.2.3 From 87ac68fcf2b5fcaa02118929b820e61bbd8c10c4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Jul 2024 23:39:14 +0100 Subject: Add `-internal-cached` --- src/build_settings.cpp | 10 +- src/cached.cpp | 213 +++++++++++++++++++++++++++++++++++++++++++ src/llvm_backend.cpp | 4 +- src/llvm_backend_general.cpp | 22 ++--- src/main.cpp | 26 +++++- src/string.cpp | 7 ++ 6 files changed, 265 insertions(+), 17 deletions(-) create mode 100644 src/cached.cpp (limited to 'src/string.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index c8c83422f..be896f6fa 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -326,7 +326,12 @@ enum SanitizerFlags : u32 { SanitizerFlag_Thread = 1u<<2, }; - +struct BuildCacheData { + u64 crc; + String cache_dir; + String manifest_path; + bool copy_already_done; +}; // This stores the information for the specify architecture of this build struct BuildContext { @@ -427,6 +432,9 @@ struct BuildContext { bool use_separate_modules; bool module_per_file; + bool cached; + BuildCacheData build_cache_data; + bool no_threaded_checker; bool show_debug_messages; diff --git a/src/cached.cpp b/src/cached.cpp new file mode 100644 index 000000000..89158ccbb --- /dev/null +++ b/src/cached.cpp @@ -0,0 +1,213 @@ +gb_internal GB_COMPARE_PROC(cached_file_cmp) { + String const &x = *(String *)a; + String const &y = *(String *)b; + return string_compare(x, y); +} + + +u64 crc64_with_seed(void const *data, isize len, u64 seed) { + isize remaining; + u64 result = ~seed; + u8 const *c = cast(u8 const *)data; + for (remaining = len; remaining--; c++) { + result = (result >> 8) ^ (GB__CRC64_TABLE[(result ^ *c) & 0xff]); + } + return ~result; +} + +bool check_if_exists_file_otherwise_create(String const &str) { + char const *str_c = alloc_cstring(permanent_allocator(), str); + if (!gb_file_exists(str_c)) { + gbFile f = {}; + gb_file_create(&f, str_c); + gb_file_close(&f); + return true; + } + return false; +} + + +bool check_if_exists_directory_otherwise_create(String const &str) { +#if defined(GB_SYSTEM_WINDOWS) + String16 wstr = string_to_string16(permanent_allocator(), str); + wchar_t *wstr_c = alloc_wstring(permanent_allocator(), wstr); + return CreateDirectoryW(wstr_c, nullptr); +#else + char const *str_c = alloc_cstring(permanent_allocator(), str); + if (!gb_file_exists(str_c)) { + return false; + } + return false; +#endif +} +bool try_copy_executable_cache_internal(bool to_cache) { + String exe_name = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]); + defer (gb_free(heap_allocator(), exe_name.text)); + + gbString cache_name = gb_string_make(heap_allocator(), ""); + defer (gb_string_free(cache_name)); + + String cache_dir = build_context.build_cache_data.cache_dir; + + cache_name = gb_string_append_length(cache_name, cache_dir.text, cache_dir.len); + cache_name = gb_string_appendc(cache_name, "/"); + + cache_name = gb_string_appendc(cache_name, "cached-exe"); + if (selected_target_metrics) { + cache_name = gb_string_appendc(cache_name, "-"); + cache_name = gb_string_append_length(cache_name, selected_target_metrics->name.text, selected_target_metrics->name.len); + } + cache_name = gb_string_appendc(cache_name, ".bin"); + + if (to_cache) { + return gb_file_copy( + alloc_cstring(temporary_allocator(), exe_name), + cache_name, + false + ); + } else { + return gb_file_copy( + cache_name, + alloc_cstring(temporary_allocator(), exe_name), + false + ); + } +} + + + +bool try_copy_executable_to_cache(void) { + if (try_copy_executable_cache_internal(true)) { + build_context.build_cache_data.copy_already_done = true; + return true; + } + return false; +} + +bool try_copy_executable_from_cache(void) { + if (try_copy_executable_cache_internal(false)) { + build_context.build_cache_data.copy_already_done = true; + return true; + } + return false; +} + + + + +// returns false if different, true if it is the same +bool try_cached_build(Checker *c) { + Parser *p = c->parser; + + auto files = array_make(heap_allocator()); + for (AstPackage *pkg : p->packages) { + for (AstFile *f : pkg->files) { + array_add(&files, f->fullpath); + } + } + + for (auto const &entry : c->info.load_file_cache) { + auto *cache = entry.value; + if (!cache || !cache->exists) { + continue; + } + array_add(&files, cache->path); + } + + array_sort(files, cached_file_cmp); + + u64 crc = 0; + for (String const &path : files) { + crc = crc64_with_seed(path.text, path.len, crc); + } + + String base_cache_dir = build_context.build_paths[BuildPath_Output].basename; + base_cache_dir = concatenate_strings(permanent_allocator(), base_cache_dir, str_lit("/.odin-cache")); + (void)check_if_exists_directory_otherwise_create(base_cache_dir); + + gbString crc_str = gb_string_make_reserve(permanent_allocator(), 16); + crc_str = gb_string_append_fmt(crc_str, "%016llx", crc); + String cache_dir = concatenate3_strings(permanent_allocator(), base_cache_dir, str_lit("/"), make_string_c(crc_str)); + String manifest_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("odin.manifest")); + + build_context.build_cache_data.cache_dir = cache_dir; + build_context.build_cache_data.manifest_path = manifest_path; + + if (check_if_exists_directory_otherwise_create(cache_dir)) { + goto do_write_file; + } + + if (check_if_exists_file_otherwise_create(manifest_path)) { + goto do_write_file; + } else { + // exists already + LoadedFile loaded_file = {}; + + LoadedFileError file_err = load_file_32( + alloc_cstring(temporary_allocator(), manifest_path), + &loaded_file, + false + ); + if (file_err) { + return false; + } + + String data = {cast(u8 *)loaded_file.data, loaded_file.size}; + String_Iterator it = {data, 0}; + + isize file_count = 0; + + for (; it.pos < data.len; file_count++) { + String line = string_split_iterator(&it, '\n'); + if (line.len == 0) { + break; + } + isize sep = string_index_byte(line, ' '); + if (sep < 0) { + goto do_write_file; + } + + String timestamp_str = substring(line, 0, sep); + String path_str = substring(line, sep+1, line.len); + + timestamp_str = string_trim_whitespace(timestamp_str); + path_str = string_trim_whitespace(path_str); + + if (files[file_count] != path_str) { + goto do_write_file; + } + + u64 timestamp = exact_value_to_u64(exact_value_integer_from_string(timestamp_str)); + gbFileTime last_write_time = gb_file_last_write_time(alloc_cstring(temporary_allocator(), path_str)); + if (last_write_time != timestamp) { + goto do_write_file; + } + } + + if (file_count != files.count) { + goto do_write_file; + } + + goto try_copy_executable; + } + +do_write_file:; + { + char const *manifest_path_c = alloc_cstring(temporary_allocator(), manifest_path); + gb_file_remove(manifest_path_c); + + gbFile f = {}; + defer (gb_file_close(&f)); + gb_file_open_mode(&f, gbFileMode_Write, manifest_path_c); + + for (String const &path : files) { + gbFileTime ft = gb_file_last_write_time(alloc_cstring(temporary_allocator(), path)); + gb_fprintf(&f, "%llu %.*s\n", cast(unsigned long long)ft, LIT(path)); + } + return false; + } + +try_copy_executable:; + return try_copy_executable_from_cache(); +} + diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 44b6e3839..b0df17778 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1397,8 +1397,8 @@ gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, Checker for (auto const &entry : gen->modules) { lbModule *m = entry.value; - gb_sort_array(m->global_types_to_create.data, m->global_types_to_create.count, llvm_global_entity_cmp); - gb_sort_array(m->global_procedures_to_create.data, m->global_procedures_to_create.count, llvm_global_entity_cmp); + array_sort(m->global_types_to_create, llvm_global_entity_cmp); + array_sort(m->global_procedures_to_create, llvm_global_entity_cmp); } if (do_threading) { diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 77f78a24c..bbeff562c 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -126,16 +126,17 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { m->gen = gen; map_set(&gen->modules, cast(void *)pkg, m); lb_init_module(m, c); - if (module_per_file) { - // NOTE(bill): Probably per file is not a good idea, so leave this for later - for (AstFile *file : pkg->files) { - auto m = gb_alloc_item(permanent_allocator(), lbModule); - m->file = file; - m->pkg = pkg; - m->gen = gen; - map_set(&gen->modules, cast(void *)file, m); - lb_init_module(m, c); - } + if (!module_per_file) { + continue; + } + // NOTE(bill): Probably per file is not a good idea, so leave this for later + for (AstFile *file : pkg->files) { + auto m = gb_alloc_item(permanent_allocator(), lbModule); + m->file = file; + m->pkg = pkg; + m->gen = gen; + map_set(&gen->modules, cast(void *)file, m); + lb_init_module(m, c); } } } @@ -144,7 +145,6 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { map_set(&gen->modules, cast(void *)1, &gen->default_module); lb_init_module(&gen->default_module, c); - for (auto const &entry : gen->modules) { lbModule *m = entry.value; LLVMContextRef ctx = LLVMGetModuleContext(m->mod); diff --git a/src/main.cpp b/src/main.cpp index bbb326af3..006a7ddbc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -71,6 +71,8 @@ gb_global Timings global_timings = {0}; #include "checker.cpp" #include "docs.cpp" +#include "cached.cpp" + #include "linker.cpp" #if defined(GB_SYSTEM_WINDOWS) && defined(ODIN_TILDE_BACKEND) @@ -391,6 +393,7 @@ enum BuildFlagKind { BuildFlag_InternalIgnoreLLVMBuild, BuildFlag_InternalIgnorePanic, BuildFlag_InternalModulePerFile, + BuildFlag_InternalCached, BuildFlag_Tilde, @@ -594,6 +597,7 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_InternalIgnoreLLVMBuild, str_lit("internal-ignore-llvm-build"),BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalIgnorePanic, str_lit("internal-ignore-panic"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalModulePerFile, str_lit("internal-module-per-file"), BuildFlagParam_None, Command_all); + add_flag(&build_flags, BuildFlag_InternalCached, str_lit("internal-cached"), BuildFlagParam_None, Command_all); #if ALLOW_TILDE add_flag(&build_flags, BuildFlag_Tilde, str_lit("tilde"), BuildFlagParam_None, Command__does_build); @@ -1413,6 +1417,10 @@ gb_internal bool parse_build_flags(Array args) { case BuildFlag_InternalModulePerFile: build_context.module_per_file = true; break; + case BuildFlag_InternalCached: + build_context.cached = true; + build_context.use_separate_modules = true; + break; case BuildFlag_Tilde: build_context.tilde_backend = true; @@ -1921,9 +1929,6 @@ gb_internal void show_timings(Checker *c, Timings *t) { gb_internal GB_COMPARE_PROC(file_path_cmp) { AstFile *x = *(AstFile **)a; AstFile *y = *(AstFile **)b; - if (x == y) { - return 0; - } return string_compare(x->fullpath, y->fullpath); } @@ -3322,6 +3327,13 @@ int main(int arg_count, char const **arg_ptr) { return 0; } + if (build_context.cached) { + MAIN_TIME_SECTION("check cached build"); + if (try_cached_build(checker)) { + goto end_of_code_gen; + } + } + #if ALLOW_TILDE if (build_context.tilde_backend) { LinkerData linker_data = {}; @@ -3383,6 +3395,8 @@ int main(int arg_count, char const **arg_ptr) { remove_temp_files(gen); } +end_of_code_gen:; + if (build_context.show_timings) { show_timings(checker, &global_timings); } @@ -3391,6 +3405,12 @@ int main(int arg_count, char const **arg_ptr) { export_dependencies(checker); } + + if (!build_context.build_cache_data.copy_already_done && + build_context.cached) { + try_copy_executable_to_cache(); + } + if (run_output) { String exe_name = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]); defer (gb_free(heap_allocator(), exe_name.text)); diff --git a/src/string.cpp b/src/string.cpp index ab08e3d4a..a39d93ad9 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -88,6 +88,13 @@ gb_internal char *alloc_cstring(gbAllocator a, String s) { return c_str; } +gb_internal wchar_t *alloc_wstring(gbAllocator a, String16 s) { + wchar_t *c_str = gb_alloc_array(a, wchar_t, s.len+1); + gb_memmove(c_str, s.text, s.len*2); + c_str[s.len] = '\0'; + return c_str; +} + gb_internal gb_inline bool str_eq_ignore_case(String const &a, String const &b) { if (a.len == b.len) { -- cgit v1.2.3 From c1496ab6c055974ac401865bdca87a4fc0a76ea3 Mon Sep 17 00:00:00 2001 From: bobsayshilol Date: Sun, 27 Oct 2024 20:11:42 +0000 Subject: Fix passing nullptr to args marked as non-null libstdc++'s |memcpy| and |memset| both state that their inputs should never be a nullptr since this matches the C spec. Some compilers act on these hints, so we shouldn't unconditionally call these as it would signal to the compiler that they can't be nullptrs. As an example, the following code will always call |do_something()| when compiled with optimisations since GCC version 4.9: ``` void clear(void *ptr, int size) { memset(ptr, 0, size); } void example(void *ptr, int size) { clear(ptr, size); if (ptr != nullptr) do_something(); } ``` --- src/gb/gb.h | 6 +++++- src/string.cpp | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'src/string.cpp') diff --git a/src/gb/gb.h b/src/gb/gb.h index 1fef4b4f5..f74026c7d 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -2541,7 +2541,11 @@ gb_inline void const *gb_pointer_add_const(void const *ptr, isize bytes) { gb_inline void const *gb_pointer_sub_const(void const *ptr, isize bytes) { return cast(void const *)(cast(u8 const *)ptr - bytes); } gb_inline isize gb_pointer_diff (void const *begin, void const *end) { return cast(isize)(cast(u8 const *)end - cast(u8 const *)begin); } -gb_inline void gb_zero_size(void *ptr, isize size) { memset(ptr, 0, size); } +gb_inline void gb_zero_size(void *ptr, isize size) { + if (size != 0) { + memset(ptr, 0, size); + } +} #if defined(_MSC_VER) && !defined(__clang__) diff --git a/src/string.cpp b/src/string.cpp index 3c7d96934..190b69041 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -156,6 +156,7 @@ gb_internal isize string_index_byte(String const &s, u8 x) { gb_internal gb_inline bool str_eq(String const &a, String const &b) { if (a.len != b.len) return false; + if (a.len == 0) return true; return memcmp(a.text, b.text, a.len) == 0; } gb_internal gb_inline bool str_ne(String const &a, String const &b) { return !str_eq(a, b); } -- cgit v1.2.3 From 6f966f30aab6f739dd65301500a6a0aff67da1d5 Mon Sep 17 00:00:00 2001 From: Dominik Pötzschke <16013351+dpoetzschke@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:24:50 +0100 Subject: fix: fix windows params bug --- src/string.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'src/string.cpp') diff --git a/src/string.cpp b/src/string.cpp index 3c7d96934..14f56e4f0 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -411,6 +411,25 @@ gb_internal String concatenate4_strings(gbAllocator a, String const &x, String c return make_string(data, len); } +#if defined(GB_SYSTEM_WINDOWS) +gb_internal String escape_char(gbAllocator a, String s, char cte) { + isize buf_len = s.len; + u8 *buf = gb_alloc_array(a, u8, buf_len); + isize i = 0; + for (isize j = 0; j < s.len; j++) { + u8 c = s.text[j]; + + if (c == cte) { + buf[i++] = '\\'; + buf[i++] = c; + } else { + buf[i++] = c; + } + } + return make_string(buf, i); +} +#endif + gb_internal String string_join_and_quote(gbAllocator a, Array strings) { if (!strings.count) { return make_string(nullptr, 0); @@ -426,7 +445,11 @@ gb_internal String string_join_and_quote(gbAllocator a, Array strings) { if (i > 0) { s = gb_string_append_fmt(s, " "); } +#if defined(GB_SYSTEM_WINDOWS) + s = gb_string_append_fmt(s, "\"%.*s\" ", LIT(escape_char(a, strings[i], '\\'))); +#else s = gb_string_append_fmt(s, "\"%.*s\" ", LIT(strings[i])); +#endif } return make_string(cast(u8 *) s, gb_string_length(s)); -- cgit v1.2.3 From d74f2154900c5d0d1335294265c65e01e44ccf51 Mon Sep 17 00:00:00 2001 From: Dominik Pötzschke <16013351+dpoetzschke@users.noreply.github.com> Date: Wed, 30 Oct 2024 22:30:56 +0100 Subject: adjust memory allocation --- src/string.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/string.cpp') diff --git a/src/string.cpp b/src/string.cpp index 14f56e4f0..ff20fb11c 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -414,7 +414,12 @@ gb_internal String concatenate4_strings(gbAllocator a, String const &x, String c #if defined(GB_SYSTEM_WINDOWS) gb_internal String escape_char(gbAllocator a, String s, char cte) { isize buf_len = s.len; - u8 *buf = gb_alloc_array(a, u8, buf_len); + isize cte_count = 0; + for (isize j = 0; j < s.len; j++) + if (s.text[j] == cte) + cte_count++; + + u8 *buf = gb_alloc_array(a, u8, buf_len+cte_count); isize i = 0; for (isize j = 0; j < s.len; j++) { u8 c = s.text[j]; -- cgit v1.2.3 From f1de4804a5d816d2a65f6e1d3d4faf7d3f340f64 Mon Sep 17 00:00:00 2001 From: Dominik Pötzschke <16013351+dpoetzschke@users.noreply.github.com> Date: Thu, 31 Oct 2024 16:18:12 +0100 Subject: added braces --- src/string.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/string.cpp') diff --git a/src/string.cpp b/src/string.cpp index ff20fb11c..c461602a7 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -415,9 +415,11 @@ gb_internal String concatenate4_strings(gbAllocator a, String const &x, String c gb_internal String escape_char(gbAllocator a, String s, char cte) { isize buf_len = s.len; isize cte_count = 0; - for (isize j = 0; j < s.len; j++) - if (s.text[j] == cte) + for (isize j = 0; j < s.len; j++) { + if (s.text[j] == cte) { cte_count++; + } + } u8 *buf = gb_alloc_array(a, u8, buf_len+cte_count); isize i = 0; -- cgit v1.2.3 From e2ba8ff6e6fdbd23a10dcaf8b04f85960b8aa7c3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 2 Dec 2024 11:23:55 +0000 Subject: Fix #4530 --- src/exact_value.cpp | 6 +++++- src/string.cpp | 10 +++++----- 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'src/string.cpp') diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 5d6016ecc..ceaed84c1 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -370,7 +370,11 @@ gb_internal ExactValue exact_value_from_basic_literal(TokenKind kind, String con } case Token_Rune: { Rune r = GB_RUNE_INVALID; - utf8_decode(string.text, string.len, &r); + if (string.len == 1) { + r = cast(Rune)string.text[0]; + } else { + utf8_decode(string.text, string.len, &r); + } return exact_value_i64(r); } } diff --git a/src/string.cpp b/src/string.cpp index f8ee6c53e..b001adf0e 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -718,12 +718,12 @@ gb_internal bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_byt Rune r = -1; isize size = utf8_decode(s.text, s.len, &r); *rune = r; - *multiple_bytes = true; - *tail_string = make_string(s.text+size, s.len-size); + if (multiple_bytes) *multiple_bytes = true; + if (tail_string) *tail_string = make_string(s.text+size, s.len-size); return true; } else if (s[0] != '\\') { *rune = s[0]; - *tail_string = make_string(s.text+1, s.len-1); + if (tail_string) *tail_string = make_string(s.text+1, s.len-1); return true; } @@ -809,10 +809,10 @@ gb_internal bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_byt return false; } *rune = r; - *multiple_bytes = true; + if (multiple_bytes) *multiple_bytes = true; } break; } - *tail_string = s; + if (tail_string) *tail_string = s; return true; } -- cgit v1.2.3