From 15b40a4f292ec0fbb71d2a16a0f8b3cd52f41a9e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 26 Jan 2024 12:56:16 +0000 Subject: Fix #3122 --- src/check_expr.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index bc7ff1bbb..f8c5540f4 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2988,6 +2988,9 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type } // proc <-> proc if (is_type_proc(src) && is_type_proc(dst)) { + if (is_type_polymorphic(src) || is_type_polymorphic(dst)) { + return false; + } return true; } @@ -3067,7 +3070,6 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) { bool is_const_expr = x->mode == Addressing_Constant; bool can_convert = check_cast_internal(c, x, type); - if (!can_convert) { TEMPORARY_ALLOCATOR_GUARD(); gbString expr_str = expr_to_string(x->expr, temporary_allocator()); -- cgit v1.2.3 From d04c82e5471bb291cddbef883a36b1caad3b2b99 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 28 Jan 2024 21:20:30 +0000 Subject: Move matrix compiler `builtin`s to `intrinsics`; alias within core_builtin_matrix.odin --- base/runtime/core_builtin_matrix.odin | 9 +++++++++ src/check_expr.cpp | 3 ++- src/checker.cpp | 1 + src/checker_builtin_procs.hpp | 20 ++++++++++---------- 4 files changed, 22 insertions(+), 11 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/base/runtime/core_builtin_matrix.odin b/base/runtime/core_builtin_matrix.odin index 7d60d625c..ed1b5b1e6 100644 --- a/base/runtime/core_builtin_matrix.odin +++ b/base/runtime/core_builtin_matrix.odin @@ -3,6 +3,15 @@ package runtime import "core:intrinsics" _ :: intrinsics +@(builtin) +transpose :: intrinsics.transpose +@(builtin) +outer_product :: intrinsics.outer_product +@(builtin) +hadamard_product :: intrinsics.hadamard_product +@(builtin) +matrix_flatten :: intrinsics.matrix_flatten + @(builtin) determinant :: proc{ diff --git a/src/check_expr.cpp b/src/check_expr.cpp index f8c5540f4..a6081a1cc 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4577,7 +4577,8 @@ gb_internal bool is_entity_declared_for_selector(Entity *entity, Scope *import_s if (entity->kind == Entity_Builtin) { // NOTE(bill): Builtin's are in the universal scope which is part of every scopes hierarchy // This means that we should just ignore the found result through it - *allow_builtin = entity->scope == import_scope || entity->scope != builtin_pkg->scope; + *allow_builtin = entity->scope == import_scope || + (entity->scope != builtin_pkg->scope && entity->scope != intrinsics_pkg->scope); } else if ((entity->scope->flags&ScopeFlag_Global) == ScopeFlag_Global && (import_scope->flags&ScopeFlag_Global) == 0) { is_declared = false; } diff --git a/src/checker.cpp b/src/checker.cpp index 563bb2781..47fcd3d8f 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3835,6 +3835,7 @@ gb_internal void check_builtin_attributes(CheckerContext *ctx, Entity *e, Array< case Entity_ProcGroup: case Entity_Procedure: case Entity_TypeName: + case Entity_Constant: // Okay break; default: diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 3bab16293..42ffa6938 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -34,11 +34,6 @@ enum BuiltinProcId { BuiltinProc_soa_zip, BuiltinProc_soa_unzip, - - BuiltinProc_transpose, - BuiltinProc_outer_product, - BuiltinProc_hadamard_product, - BuiltinProc_matrix_flatten, BuiltinProc_unreachable, @@ -48,6 +43,11 @@ enum BuiltinProcId { // "Intrinsics" BuiltinProc_is_package_imported, + + BuiltinProc_transpose, + BuiltinProc_outer_product, + BuiltinProc_hadamard_product, + BuiltinProc_matrix_flatten, BuiltinProc_soa_struct, @@ -341,11 +341,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("soa_zip"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("soa_unzip"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - - {STR_LIT("transpose"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("outer_product"), 2, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("hadamard_product"), 2, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("matrix_flatten"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("unreachable"), 0, false, Expr_Expr, BuiltinProcPkg_builtin, /*diverging*/true}, @@ -356,6 +351,11 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { // "Intrinsics" {STR_LIT("is_package_imported"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("transpose"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("outer_product"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("hadamard_product"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("matrix_flatten"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("soa_struct"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type -- cgit v1.2.3 From a78f062499c7f0112558872a500904e6fbc6761b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 28 Jan 2024 23:49:59 +0000 Subject: Fix semantics for casting between polymorphic specialized procedures --- src/check_expr.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index f8c5540f4..4f47ba523 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2988,7 +2988,11 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type } // proc <-> proc if (is_type_proc(src) && is_type_proc(dst)) { - if (is_type_polymorphic(src) || is_type_polymorphic(dst)) { + if (is_type_polymorphic(dst)) { + if (is_type_polymorphic(src) && + operand->mode == Addressing_Variable) { + return true; + } return false; } return true; -- cgit v1.2.3 From e7122a095045440380e2eed65e9afa90b035c277 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 2 Feb 2024 11:42:22 +0000 Subject: Minimize extra dependencies if u128/i128 and f16 are not used --- src/check_expr.cpp | 53 ++++++++++++++++++++++++++++++++++++++++++++++++----- src/checker.cpp | 42 +++++++++++++++++++++--------------------- 2 files changed, 69 insertions(+), 26 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 041bf1703..d7ecbbe8d 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3114,6 +3114,25 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) { final_type = default_type(x->type); } update_untyped_expr_type(c, x->expr, final_type, true); + } else { + Type *src = core_type(x->type); + Type *dst = core_type(type); + if (src != dst) { + if (is_type_integer_128bit(src) && is_type_float(dst)) { + add_package_dependency(c, "runtime", "floattidf_unsigned"); + add_package_dependency(c, "runtime", "floattidf"); + } else if (is_type_integer_128bit(dst) && is_type_float(src)) { + add_package_dependency(c, "runtime", "fixunsdfti"); + add_package_dependency(c, "runtime", "fixunsdfdi"); + } else if (src == t_f16 && is_type_float(dst)) { + add_package_dependency(c, "runtime", "gnu_h2f_ieee"); + add_package_dependency(c, "runtime", "extendhfsf2"); + } else if (is_type_float(dst) && dst == t_f16) { + add_package_dependency(c, "runtime", "truncsfhf2"); + add_package_dependency(c, "runtime", "truncdfhf2"); + add_package_dependency(c, "runtime", "gnu_f2h_ieee"); + } + } } x->type = type; @@ -3734,9 +3753,14 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ x->mode = Addressing_Invalid; return; } - - if (op.kind == Token_Quo || op.kind == Token_QuoEq) { - Type *bt = base_type(x->type); + Type *bt = base_type(x->type); + if (op.kind == Token_Mod || op.kind == Token_ModEq || + op.kind == Token_ModMod || op.kind == Token_ModModEq) { + if (bt->kind == Type_Basic) switch (bt->Basic.kind) { + case Basic_u128: add_package_dependency(c, "runtime", "umodti3"); break; + case Basic_i128: add_package_dependency(c, "runtime", "modti3"); break; + } + } else if (op.kind == Token_Quo || op.kind == Token_QuoEq) { if (bt->kind == Type_Basic) switch (bt->Basic.kind) { case Basic_complex32: add_package_dependency(c, "runtime", "quo_complex32"); break; case Basic_complex64: add_package_dependency(c, "runtime", "quo_complex64"); break; @@ -3744,13 +3768,32 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ case Basic_quaternion64: add_package_dependency(c, "runtime", "quo_quaternion64"); break; case Basic_quaternion128: add_package_dependency(c, "runtime", "quo_quaternion128"); break; case Basic_quaternion256: add_package_dependency(c, "runtime", "quo_quaternion256"); break; + + case Basic_u128: add_package_dependency(c, "runtime", "udivti3"); break; + case Basic_i128: add_package_dependency(c, "runtime", "divti3"); break; } } else if (op.kind == Token_Mul || op.kind == Token_MulEq) { - Type *bt = base_type(x->type); if (bt->kind == Type_Basic) switch (bt->Basic.kind) { - case Basic_quaternion64: add_package_dependency(c, "runtime", "mul_quaternion64"); break; + case Basic_quaternion64: add_package_dependency(c, "runtime", "mul_quaternion64"); break; case Basic_quaternion128: add_package_dependency(c, "runtime", "mul_quaternion128"); break; case Basic_quaternion256: add_package_dependency(c, "runtime", "mul_quaternion256"); break; + + + case Basic_u128: + case Basic_i128: + if (is_arch_wasm()) { + add_package_dependency(c, "runtime", "__multi3"); + } + break; + } + } else if (op.kind == Token_Shl || op.kind == Token_ShlEq) { + if (bt->kind == Type_Basic) switch (bt->Basic.kind) { + case Basic_u128: + case Basic_i128: + if (is_arch_wasm()) { + add_package_dependency(c, "runtime", "__ashlti3"); + } + break; } } diff --git a/src/checker.cpp b/src/checker.cpp index 8c94ddf86..5e0eaacc7 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2567,27 +2567,27 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) { str_lit("memmove"), ); - FORCE_ADD_RUNTIME_ENTITIES(!build_context.tilde_backend, - // Extended data type internal procedures - str_lit("umodti3"), - str_lit("udivti3"), - str_lit("modti3"), - str_lit("divti3"), - str_lit("fixdfti"), - str_lit("fixunsdfti"), - str_lit("fixunsdfdi"), - str_lit("floattidf"), - str_lit("floattidf_unsigned"), - str_lit("truncsfhf2"), - str_lit("truncdfhf2"), - str_lit("gnu_h2f_ieee"), - str_lit("gnu_f2h_ieee"), - str_lit("extendhfsf2"), - - // WASM Specific - str_lit("__ashlti3"), - str_lit("__multi3"), - ); + // FORCE_ADD_RUNTIME_ENTITIES(!build_context.tilde_backend, + // // Extended data type internal procedures + // str_lit("umodti3"), + // str_lit("udivti3"), + // str_lit("modti3"), + // str_lit("divti3"), + // str_lit("fixdfti"), + // str_lit("fixunsdfti"), + // str_lit("fixunsdfdi"), + // str_lit("floattidf"), + // str_lit("floattidf_unsigned"), + // str_lit("truncsfhf2"), + // str_lit("truncdfhf2"), + // str_lit("gnu_h2f_ieee"), + // str_lit("gnu_f2h_ieee"), + // str_lit("extendhfsf2"), + + // // WASM Specific + // str_lit("__ashlti3"), + // str_lit("__multi3"), + // ); FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_rtti, // Odin types -- cgit v1.2.3 From 19535d872162b7968f10822c658b51069cf81e65 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 5 Feb 2024 11:11:28 +0000 Subject: Add require flags to 128-bit integer procedures --- src/check_expr.cpp | 15 +++++++++------ src/checker.cpp | 5 ++++- 2 files changed, 13 insertions(+), 7 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index d7ecbbe8d..7049e5974 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3753,12 +3753,15 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ x->mode = Addressing_Invalid; return; } + + bool REQUIRE = true; + Type *bt = base_type(x->type); if (op.kind == Token_Mod || op.kind == Token_ModEq || op.kind == Token_ModMod || op.kind == Token_ModModEq) { if (bt->kind == Type_Basic) switch (bt->Basic.kind) { - case Basic_u128: add_package_dependency(c, "runtime", "umodti3"); break; - case Basic_i128: add_package_dependency(c, "runtime", "modti3"); break; + case Basic_u128: add_package_dependency(c, "runtime", "umodti3", REQUIRE); break; + case Basic_i128: add_package_dependency(c, "runtime", "modti3", REQUIRE); break; } } else if (op.kind == Token_Quo || op.kind == Token_QuoEq) { if (bt->kind == Type_Basic) switch (bt->Basic.kind) { @@ -3769,8 +3772,8 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ case Basic_quaternion128: add_package_dependency(c, "runtime", "quo_quaternion128"); break; case Basic_quaternion256: add_package_dependency(c, "runtime", "quo_quaternion256"); break; - case Basic_u128: add_package_dependency(c, "runtime", "udivti3"); break; - case Basic_i128: add_package_dependency(c, "runtime", "divti3"); break; + case Basic_u128: add_package_dependency(c, "runtime", "udivti3", REQUIRE); break; + case Basic_i128: add_package_dependency(c, "runtime", "divti3", REQUIRE); break; } } else if (op.kind == Token_Mul || op.kind == Token_MulEq) { if (bt->kind == Type_Basic) switch (bt->Basic.kind) { @@ -3782,7 +3785,7 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ case Basic_u128: case Basic_i128: if (is_arch_wasm()) { - add_package_dependency(c, "runtime", "__multi3"); + add_package_dependency(c, "runtime", "__multi3", REQUIRE); } break; } @@ -3791,7 +3794,7 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ case Basic_u128: case Basic_i128: if (is_arch_wasm()) { - add_package_dependency(c, "runtime", "__ashlti3"); + add_package_dependency(c, "runtime", "__ashlti3", REQUIRE); } break; } diff --git a/src/checker.cpp b/src/checker.cpp index 5e0eaacc7..4fdcec5f3 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -810,13 +810,16 @@ gb_internal AstPackage *get_core_package(CheckerInfo *info, String name) { return *found; } -gb_internal void add_package_dependency(CheckerContext *c, char const *package_name, char const *name) { +gb_internal void add_package_dependency(CheckerContext *c, char const *package_name, char const *name, bool required=false) { String n = make_string_c(name); AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name)); Entity *e = scope_lookup(p->scope, n); GB_ASSERT_MSG(e != nullptr, "%s", name); GB_ASSERT(c->decl != nullptr); e->flags |= EntityFlag_Used; + if (required) { + e->flags |= EntityFlag_Require; + } add_dependency(c->info, c->decl, e); } -- cgit v1.2.3 From 27feb5998c5a86ffa5ce661313f8beae585804d8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 5 Feb 2024 13:49:10 +0000 Subject: Add require to 128-bit and f16 casts --- src/check_expr.cpp | 19 ++++++++++--------- src/checker.cpp | 42 +++++++++++++++++++++--------------------- 2 files changed, 31 insertions(+), 30 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7049e5974..9b71208cd 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3118,19 +3118,20 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) { Type *src = core_type(x->type); Type *dst = core_type(type); if (src != dst) { + bool const REQUIRE = true; if (is_type_integer_128bit(src) && is_type_float(dst)) { - add_package_dependency(c, "runtime", "floattidf_unsigned"); - add_package_dependency(c, "runtime", "floattidf"); + add_package_dependency(c, "runtime", "floattidf_unsigned", REQUIRE); + add_package_dependency(c, "runtime", "floattidf", REQUIRE); } else if (is_type_integer_128bit(dst) && is_type_float(src)) { - add_package_dependency(c, "runtime", "fixunsdfti"); - add_package_dependency(c, "runtime", "fixunsdfdi"); + add_package_dependency(c, "runtime", "fixunsdfti", REQUIRE); + add_package_dependency(c, "runtime", "fixunsdfdi", REQUIRE); } else if (src == t_f16 && is_type_float(dst)) { - add_package_dependency(c, "runtime", "gnu_h2f_ieee"); - add_package_dependency(c, "runtime", "extendhfsf2"); + add_package_dependency(c, "runtime", "gnu_h2f_ieee", REQUIRE); + add_package_dependency(c, "runtime", "extendhfsf2", REQUIRE); } else if (is_type_float(dst) && dst == t_f16) { - add_package_dependency(c, "runtime", "truncsfhf2"); - add_package_dependency(c, "runtime", "truncdfhf2"); - add_package_dependency(c, "runtime", "gnu_f2h_ieee"); + add_package_dependency(c, "runtime", "truncsfhf2", REQUIRE); + add_package_dependency(c, "runtime", "truncdfhf2", REQUIRE); + add_package_dependency(c, "runtime", "gnu_f2h_ieee", REQUIRE); } } } diff --git a/src/checker.cpp b/src/checker.cpp index 7d8f456df..8f1aa5336 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2570,27 +2570,27 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) { str_lit("memmove"), ); - FORCE_ADD_RUNTIME_ENTITIES(is_arch_wasm() && !build_context.tilde_backend, - // Extended data type internal procedures - str_lit("umodti3"), - str_lit("udivti3"), - str_lit("modti3"), - str_lit("divti3"), - str_lit("fixdfti"), - str_lit("fixunsdfti"), - str_lit("fixunsdfdi"), - str_lit("floattidf"), - str_lit("floattidf_unsigned"), - str_lit("truncsfhf2"), - str_lit("truncdfhf2"), - str_lit("gnu_h2f_ieee"), - str_lit("gnu_f2h_ieee"), - str_lit("extendhfsf2"), - - // WASM Specific - str_lit("__ashlti3"), - str_lit("__multi3"), - ); + // FORCE_ADD_RUNTIME_ENTITIES(is_arch_wasm() && !build_context.tilde_backend, + // // Extended data type internal procedures + // str_lit("umodti3"), + // str_lit("udivti3"), + // str_lit("modti3"), + // str_lit("divti3"), + // str_lit("fixdfti"), + // str_lit("fixunsdfti"), + // str_lit("fixunsdfdi"), + // str_lit("floattidf"), + // str_lit("floattidf_unsigned"), + // str_lit("truncsfhf2"), + // str_lit("truncdfhf2"), + // str_lit("gnu_h2f_ieee"), + // str_lit("gnu_f2h_ieee"), + // str_lit("extendhfsf2"), + + // // WASM Specific + // str_lit("__ashlti3"), + // str_lit("__multi3"), + // ); FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_rtti, // Odin types -- 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/check_expr.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 fea38f6910f4abb037e2581f28d7592c31991efe Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Feb 2024 14:01:39 +0000 Subject: Minor changes to futex implementation on Linux --- src/check_expr.cpp | 6 ++++-- src/threading.cpp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 11eb4b533..685bcdd6e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1241,7 +1241,7 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T } case Type_Pointer: if (source->kind == Type_Pointer) { - isize level = check_is_assignable_to_using_subtype(source->Pointer.elem, poly->Pointer.elem); + isize level = check_is_assignable_to_using_subtype(source->Pointer.elem, poly->Pointer.elem, /*level*/0, /*src_is_ptr*/false, /*allow_polymorphic*/true); if (level > 0) { return true; } @@ -1413,7 +1413,9 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T return ok; } - // return check_is_assignable_to(c, &o, poly); + + // NOTE(bill): Check for subtypes of + // return check_is_assignable_to(c, &o, poly); // && is_type_subtype_of_and_allow_polymorphic(o.type, poly); } return false; case Type_Tuple: diff --git a/src/threading.cpp b/src/threading.cpp index 725b58c89..684b13bc3 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -656,7 +656,7 @@ gb_internal void futex_wait(Futex *addr, Footex val) { for (;;) { int ret = syscall(SYS_futex, addr, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, val, NULL, NULL, 0); if (ret == -1) { - if (errno != EAGAIN) { + if (errno != EAGAIN && errno != EINTR) { perror("Futex wait"); GB_PANIC("Failed in futex wait!\n"); } else { -- cgit v1.2.3 From 5a84a0822596fac47dd35bf1c2f1d9bb60bbe5c1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Feb 2024 17:24:42 +0000 Subject: Add general support for `bit_field`s --- base/runtime/internal.odin | 22 ++++++++++++ core/fmt/fmt.odin | 10 ++++-- src/check_expr.cpp | 86 ++++++++++++++++++++++++++++++++++++++------ src/check_stmt.cpp | 10 ++++++ src/check_type.cpp | 21 +++++++++-- src/checker.hpp | 1 + src/entity.cpp | 2 ++ src/llvm_backend.hpp | 8 +++++ src/llvm_backend_expr.cpp | 16 +++++++++ src/llvm_backend_general.cpp | 51 ++++++++++++++++++++++++-- src/parser.hpp | 1 + src/types.cpp | 34 ++++++++++++++++++ 12 files changed, 245 insertions(+), 17 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/base/runtime/internal.odin b/base/runtime/internal.odin index 691f76ff1..62bee8620 100644 --- a/base/runtime/internal.odin +++ b/base/runtime/internal.odin @@ -1034,3 +1034,25 @@ fixdfti :: proc(a: u64) -> i128 { } } + + + +__write_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) { + for i in 0..value.kind == ExactValue_Integer) { gbString b = type_to_string(type); i64 sz = type_size_of(type); + i64 bit_size = 8*sz; + bool size_changed = false; + if (max_bit_size > 0) { + size_changed = (bit_size != max_bit_size); + bit_size = gb_min(bit_size, max_bit_size); + } BigInt *bi = &o->value.value_integer; if (is_type_unsigned(type)) { if (big_int_is_neg(bi)) { @@ -2083,25 +2089,36 @@ gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, } else { BigInt one = big_int_make_u64(1); BigInt max_size = big_int_make_u64(1); - BigInt bits = big_int_make_i64(8*sz); + BigInt bits = big_int_make_i64(bit_size); big_int_shl_eq(&max_size, &bits); big_int_sub_eq(&max_size, &one); String max_size_str = big_int_to_string(temporary_allocator(), &max_size); - error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str)); + + if (size_changed) { + error_line("\tThe maximum value that can be represented with that bit_field's field of '%s | %u' is '%.*s'\n", b, bit_size, LIT(max_size_str)); + } else { + error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str)); + } } } else { BigInt zero = big_int_make_u64(0); BigInt one = big_int_make_u64(1); BigInt max_size = big_int_make_u64(1); - BigInt bits = big_int_make_i64(8*sz - 1); + BigInt bits = big_int_make_i64(bit_size - 1); big_int_shl_eq(&max_size, &bits); + + String max_size_str = {}; if (big_int_is_neg(bi)) { big_int_neg(&max_size, &max_size); - String max_size_str = big_int_to_string(temporary_allocator(), &max_size); - error_line("\tThe minimum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str)); + max_size_str = big_int_to_string(temporary_allocator(), &max_size); } else { big_int_sub_eq(&max_size, &one); - String max_size_str = big_int_to_string(temporary_allocator(), &max_size); + max_size_str = big_int_to_string(temporary_allocator(), &max_size); + } + + if (size_changed) { + error_line("\tThe maximum value that can be represented with that bit_field's field of '%s | %u' is '%.*s'\n", b, bit_size, LIT(max_size_str)); + } else { error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str)); } } @@ -2112,7 +2129,7 @@ gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, } return false; } -gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type) { +gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size) { gbString a = expr_to_string(o->expr); gbString b = type_to_string(type); defer( @@ -2143,7 +2160,7 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o error_line("\t whereas slices in general are assumed to be mutable.\n"); } else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) { error_line("\tSuggestion: the expression may be casted to %s\n", b); - } else if (check_integer_exceed_suggestion(c, o, type)) { + } else if (check_integer_exceed_suggestion(c, o, type, max_bit_size)) { return; } } @@ -2217,13 +2234,18 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ if (!is_type_integer(o->type) && is_type_integer(type)) { error(o->expr, "'%s' truncated to '%s', got %s", a, b, s); } else { + i64 max_bit_size = 0; + if (ctx->bit_field_bit_size) { + max_bit_size = ctx->bit_field_bit_size; + } + if (are_types_identical(o->type, type)) { error(o->expr, "Numeric value '%s' from '%s' cannot be represented by '%s'", s, a, b); } else { error(o->expr, "Cannot convert numeric value '%s' from '%s' to '%s' from '%s'", s, a, b, c); } - check_assignment_error_suggestion(ctx, o, type); + check_assignment_error_suggestion(ctx, o, type, max_bit_size); } } else { error(o->expr, "Cannot convert '%s' to '%s' from '%s', got %s", a, b, c, s); @@ -2234,6 +2256,11 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ } gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) { + if (o->expr && o->expr->kind == Ast_SelectorExpr) { + if (o->expr->SelectorExpr.is_bit_field) { + return true; + } + } if (o->mode == Addressing_OptionalOk) { Ast *expr = unselector_expr(o->expr); if (expr->kind != Ast_TypeAssertion) { @@ -2306,6 +2333,8 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * Entity *e = entity_of_node(ue->expr); if (e != nullptr && (e->flags & EntityFlag_Param) != 0) { error(op, "Cannot take the pointer address of '%s' which is a procedure parameter", str); + } else if (e != nullptr && (e->flags & EntityFlag_BitFieldField) != 0) { + error(op, "Cannot take the pointer address of '%s' which is a bit_field's field", str); } else { switch (o->mode) { case Addressing_Constant: @@ -5067,6 +5096,11 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod operand->type = entity->type; operand->expr = node; + if (entity->flags & EntityFlag_BitFieldField) { + add_package_dependency(c, "runtime", "__write_bits"); + add_package_dependency(c, "runtime", "__read_bits"); + } + switch (entity->kind) { case Entity_Constant: operand->value = entity->Constant.value; @@ -5080,6 +5114,9 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod } break; case Entity_Variable: + if (sel.is_bit_field) { + se->is_bit_field = true; + } if (sel.indirect) { operand->mode = Addressing_Variable; } else if (operand->mode == Addressing_Context) { @@ -11115,6 +11152,33 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan case_end; + case_ast_node(f, BitFieldField, node); + str = write_expr_to_string(str, f->name, shorthand); + str = gb_string_appendc(str, ": "); + str = write_expr_to_string(str, f->type, shorthand); + str = gb_string_appendc(str, " | "); + str = write_expr_to_string(str, f->bit_size, shorthand); + case_end; + case_ast_node(bf, BitFieldType, node); + str = gb_string_appendc(str, "bit_field "); + if (!shorthand) { + str = write_expr_to_string(str, bf->backing_type, shorthand); + } + str = gb_string_appendc(str, " {"); + if (shorthand) { + str = gb_string_appendc(str, "..."); + } else { + for_array(i, bf->fields) { + if (i > 0) { + str = gb_string_appendc(str, ", "); + } + str = write_expr_to_string(str, bf->fields[i], false); + } + return str; + } + str = gb_string_appendc(str, "}"); + case_end; + case_ast_node(ia, InlineAsmExpr, node); str = gb_string_appendc(str, "asm("); for_array(i, ia->param_types) { diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 6897701d6..a7dd9743b 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -485,7 +485,17 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O } } + Entity *lhs_e = entity_of_node(lhs->expr); + u8 prev_bit_field_bit_size = ctx->bit_field_bit_size; + if (lhs_e && lhs_e->kind == Entity_Variable && lhs_e->Variable.bit_field_bit_size) { + // HACK NOTE(bill): This is a bit of a hack, but it will work fine for this use case + ctx->bit_field_bit_size = lhs_e->Variable.bit_field_bit_size; + } + check_assignment(ctx, rhs, assignment_type, str_lit("assignment")); + + ctx->bit_field_bit_size = prev_bit_field_bit_size; + if (rhs->mode == Addressing_Invalid) { return nullptr; } diff --git a/src/check_type.cpp b/src/check_type.cpp index 8afac2fc5..8c746a2f7 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1035,11 +1035,19 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, error(f->bit_size, "A bit_field's specified bit size cannot exceed 64 bits, got %lld", cast(long long)bit_size_i64); bit_size_i64 = 64; } + i64 sz = 8*type_size_of(type); + if (bit_size_i64 > sz) { + error(f->bit_size, "A bit_field's specified bit size cannot exceed its type, got %lld, expect <=%lld", cast(long long)bit_size_i64, cast(long long)sz); + bit_size_i64 = sz; + } + bit_size_u8 = cast(u8)bit_size_i64; Entity *e = alloc_entity_field(ctx->scope, f->name->Ident.token, type, false, field_src_index); e->Variable.docs = docs; e->Variable.comment = comment; + e->Variable.bit_field_bit_size = bit_size_u8; + e->flags |= EntityFlag_BitFieldField; add_entity(ctx, ctx->scope, nullptr, e); array_add(&fields, e); @@ -1050,6 +1058,14 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, GB_ASSERT(fields.count <= bf->fields.count); + auto bit_offsets = slice_make(permanent_allocator(), fields.count); + i64 curr_offset = 0; + for_array(i, bit_sizes) { + bit_offsets[i] = curr_offset; + curr_offset += cast(i64)bit_sizes[i]; + } + + if (total_bit_size > maximum_bit_size) { gbString s = type_to_string(backing_type); error(node, "The numbers required %llu exceeds the backing type's (%s) bit size %llu", @@ -1059,8 +1075,9 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, gb_string_free(s); } - bit_field_type->BitField.fields = slice_from_array(fields); - bit_field_type->BitField.bit_sizes = slice_from_array(bit_sizes); + bit_field_type->BitField.fields = slice_from_array(fields); + bit_field_type->BitField.bit_sizes = slice_from_array(bit_sizes); + bit_field_type->BitField.bit_offsets = bit_offsets; } gb_internal bool is_type_valid_bit_set_range(Type *t) { diff --git a/src/checker.hpp b/src/checker.hpp index 9aee82257..066d6bb4a 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -475,6 +475,7 @@ struct CheckerContext { bool hide_polymorphic_errors; bool in_polymorphic_specialization; bool allow_arrow_right_selector_expr; + u8 bit_field_bit_size; Scope * polymorphic_scope; Ast *assignment_lhs_hint; diff --git a/src/entity.cpp b/src/entity.cpp index e6c46d37e..916c2b2bd 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -43,6 +43,7 @@ enum EntityFlag : u64 { EntityFlag_NoAlias = 1ull<<9, EntityFlag_TypeField = 1ull<<10, EntityFlag_Value = 1ull<<11, + EntityFlag_BitFieldField = 1ull<<12, @@ -212,6 +213,7 @@ struct Entity { Ast *init_expr; // only used for some variables within procedure bodies i32 field_index; i32 field_group_index; + u8 bit_field_bit_size; ParameterValue param_value; diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 026454c81..00d1b7a21 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -84,6 +84,8 @@ enum lbAddrKind { lbAddr_Swizzle, lbAddr_SwizzleLarge, + + lbAddr_BitField, }; struct lbAddr { @@ -118,6 +120,12 @@ struct lbAddr { Type *type; Slice indices; } swizzle_large; + struct { + Type *type; + i64 index; + i64 bit_offset; + i64 bit_size; + } bitfield; }; }; diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 0c06c8c1b..6bef21822 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4627,6 +4627,22 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { Selection sel = lookup_field(type, selector, false); GB_ASSERT(sel.entity != nullptr); + if (sel.is_bit_field) { + lbAddr addr = lb_build_addr(p, se->expr); + Type *bf_type = base_type(type_deref(lb_addr_type(addr))); + GB_ASSERT(bf_type->kind == Type_BitField); + + lbValue a = lb_addr_get_ptr(p, addr); + Selection sub_sel = sel; + sub_sel.index.count -= 1; + i32 index = sel.index[sel.index.count-1]; + + Entity *f = bf_type->BitField.fields[index]; + u8 bit_size = bf_type->BitField.bit_sizes[index]; + i64 bit_offset = bf_type->BitField.bit_offsets[index]; + + return lb_addr_bit_field(a, f->type, index, bit_offset, bit_size); + } if (sel.pseudo_field) { GB_ASSERT(sel.entity->kind == Entity_Procedure || sel.entity->kind == Entity_ProcGroup); Entity *e = entity_of_node(sel_node); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 2102420f8..4ff8482a7 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -451,6 +451,20 @@ gb_internal lbAddr lb_addr_swizzle_large(lbValue addr, Type *array_type, Slice(temporary_allocator(), 4); + args[0] = dst; + args[1] = lb_address_from_load_or_generate_local(p, value); + args[2] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_offset); + args[3] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_size); + lb_emit_runtime_call(p, "__write_bits", args); + return; + } else if (addr.kind == lbAddr_RelativePointer) { Type *rel_ptr = base_type(lb_addr_type(addr)); GB_ASSERT(rel_ptr->kind == Type_RelativePointer || rel_ptr->kind == Type_RelativeMultiPointer); @@ -1074,8 +1098,31 @@ gb_internal lbValue lb_emit_load(lbProcedure *p, lbValue value) { gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) { GB_ASSERT(addr.addr.value != nullptr); + if (addr.kind == lbAddr_BitField) { + lbAddr dst = lb_add_local_generated(p, addr.bitfield.type, true); + lbValue src = addr.addr; - if (addr.kind == lbAddr_RelativePointer) { + auto args = array_make(temporary_allocator(), 4); + args[0] = dst.addr; + args[1] = src; + args[2] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_offset); + args[3] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_size); + lb_emit_runtime_call(p, "__read_bits", args); + + lbValue r = lb_addr_load(p, dst); + + if (!is_type_unsigned(core_type(addr.bitfield.type))) { + // Sign extension + // m := 1<<(bit_size-1) + // r = (r XOR m) - m + Type *t = addr.bitfield.type; + lbValue m = lb_const_int(p->module, t, 1ull<<(addr.bitfield.bit_size-1)); + r = lb_emit_arith(p, Token_Xor, r, m, t); + r = lb_emit_arith(p, Token_Sub, r, m, t); + } + + return r; + } else if (addr.kind == lbAddr_RelativePointer) { Type *rel_ptr = base_type(lb_addr_type(addr)); Type *base_integer = nullptr; Type *pointer_type = nullptr; diff --git a/src/parser.hpp b/src/parser.hpp index ff77c88c7..1f4ec8726 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -429,6 +429,7 @@ AST_KIND(_ExprBegin, "", bool) \ Ast *expr, *selector; \ u8 swizzle_count; /*maximum of 4 components, if set, count >= 2*/ \ u8 swizzle_indices; /*2 bits per component*/ \ + bool is_bit_field; \ }) \ AST_KIND(ImplicitSelectorExpr, "implicit selector expression", struct { Token token; Ast *selector; }) \ AST_KIND(SelectorCallExpr, "selector call expression", struct { \ diff --git a/src/types.cpp b/src/types.cpp index 1c28e6583..be4b8944b 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -287,6 +287,7 @@ struct TypeProc { Type * backing_type; \ Slice fields; \ Slice bit_sizes; \ + Slice bit_offsets; \ Ast * node; \ }) \ TYPE_KIND(SoaPointer, struct { Type *elem; }) @@ -408,6 +409,7 @@ struct Selection { bool indirect; // Set if there was a pointer deref anywhere down the line u8 swizzle_count; // maximum components = 4 u8 swizzle_indices; // 2 bits per component, representing which swizzle index + bool is_bit_field; bool pseudo_field; }; gb_global Selection const empty_selection = {0}; @@ -3187,6 +3189,21 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name else if (field_name == "a") mapped_field_name = str_lit("w"); return lookup_field_with_selection(type, mapped_field_name, is_type, sel, allow_blank_ident); } + } else if (type->kind == Type_BitField) { + for_array(i, type->BitField.fields) { + Entity *f = type->BitField.fields[i]; + if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) { + continue; + } + String str = f->token.string; + if (field_name == str) { + selection_add_index(&sel, i); // HACK(bill): Leaky memory + sel.entity = f; + sel.is_bit_field = true; + return sel; + } + } + } else if (type->kind == Type_Basic) { switch (type->Basic.kind) { case Basic_any: { @@ -4551,6 +4568,23 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha str = gb_string_appendc(str, gb_bprintf("matrix[%d, %d]", cast(int)type->Matrix.row_count, cast(int)type->Matrix.column_count)); str = write_type_to_string(str, type->Matrix.elem); break; + + case Type_BitField: + str = gb_string_appendc(str, "bit_field "); + str = write_type_to_string(str, type->BitField.backing_type); + str = gb_string_appendc(str, " {"); + for (isize i = 0; i < type->BitField.fields.count; i++) { + Entity *f = type->BitField.fields[i]; + if (i > 0) { + str = gb_string_appendc(str, ", "); + } + str = gb_string_append_length(str, f->token.string.text, f->token.string.len); + str = gb_string_appendc(str, ": "); + str = write_type_to_string(str, f->type); + str = gb_string_append_fmt(str, " | %u", type->BitField.bit_sizes[i]); + } + str = gb_string_appendc(str, " }"); + break; } return str; -- cgit v1.2.3 From dcbcf75269a75a9d677104f40dd99d3610e1353d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Feb 2024 17:37:42 +0000 Subject: Add doc-format support for `bit_field` --- core/odin/doc-format/doc_format.odin | 8 ++++++-- src/check_expr.cpp | 1 - src/docs_format.cpp | 9 ++++++--- src/docs_writer.cpp | 24 +++++++++++++++++++++++- 4 files changed, 35 insertions(+), 7 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/core/odin/doc-format/doc_format.odin b/core/odin/doc-format/doc_format.odin index d22dafd27..360ba0d85 100644 --- a/core/odin/doc-format/doc_format.odin +++ b/core/odin/doc-format/doc_format.odin @@ -10,8 +10,8 @@ Array :: struct($T: typeid) { String :: distinct Array(byte) Version_Type_Major :: 0 -Version_Type_Minor :: 2 -Version_Type_Patch :: 4 +Version_Type_Minor :: 3 +Version_Type_Patch :: 0 Version_Type :: struct { major, minor, patch: u8, @@ -110,6 +110,8 @@ Entity_Flag :: enum u32le { Param_No_Alias = 7, // #no_alias Param_Any_Int = 8, // #any_int + Bit_Field_Field = 19, + Type_Alias = 20, Builtin_Pkg_Builtin = 30, @@ -137,6 +139,7 @@ Entity :: struct { // May be used by (Struct fields and procedure fields): // .Variable // .Constant + // This is equal to the "bit size" it this is a `bit_field`s field field_group_index: i32le, // May used by: @@ -187,6 +190,7 @@ Type_Kind :: enum u32le { Multi_Pointer = 22, Matrix = 23, Soa_Pointer = 24, + Bit_Field = 25, } Type_Elems_Cap :: 4 diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 9b15ae3b2..aba2f5831 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -11174,7 +11174,6 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan } str = write_expr_to_string(str, bf->fields[i], false); } - return str; } str = gb_string_appendc(str, "}"); case_end; diff --git a/src/docs_format.cpp b/src/docs_format.cpp index d0bca214b..779190e62 100644 --- a/src/docs_format.cpp +++ b/src/docs_format.cpp @@ -14,8 +14,8 @@ struct OdinDocVersionType { }; #define OdinDocVersionType_Major 0 -#define OdinDocVersionType_Minor 2 -#define OdinDocVersionType_Patch 4 +#define OdinDocVersionType_Minor 3 +#define OdinDocVersionType_Patch 0 struct OdinDocHeaderBase { u8 magic[8]; @@ -84,6 +84,7 @@ enum OdinDocTypeKind : u32 { OdinDocType_MultiPointer = 22, OdinDocType_Matrix = 23, OdinDocType_SoaPointer = 24, + OdinDocType_BitField = 25, }; enum OdinDocTypeFlag_Basic : u32 { @@ -170,6 +171,8 @@ enum OdinDocEntityFlag : u64 { OdinDocEntityFlag_Param_NoAlias = 1ull<<7, OdinDocEntityFlag_Param_AnyInt = 1ull<<8, + OdinDocEntityFlag_BitField_Field = 1ull<<19, + OdinDocEntityFlag_Type_Alias = 1ull<<20, OdinDocEntityFlag_Builtin_Pkg_Builtin = 1ull<<30, @@ -192,7 +195,7 @@ struct OdinDocEntity { u32 reserved_for_init; OdinDocString comment; // line comment OdinDocString docs; // preceding comment - i32 field_group_index; + i32 field_group_index; // For `bit_field`s this is the "bit_size" OdinDocEntityIndex foreign_library; OdinDocString link_name; OdinDocArray attributes; diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 6816ae8eb..88d9df40c 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -615,6 +615,19 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) { doc_type.types = odin_write_slice(w, types, gb_count_of(types)); } break; + case Type_BitField: + doc_type.kind = OdinDocType_BitField; + { + auto fields = array_make(heap_allocator(), type->BitField.fields.count); + defer (array_free(&fields)); + + for_array(i, type->BitField.fields) { + fields[i] = odin_doc_add_entity(w, type->BitField.fields[i]); + } + doc_type.entities = odin_write_slice(w, fields.data, fields.count); + } + break; + case Type_Struct: doc_type.kind = OdinDocType_Struct; if (type->Struct.soa_kind != StructSoa_None) { @@ -863,6 +876,10 @@ gb_internal OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) } break; case Entity_Variable: + if (e->flags & EntityFlag_BitFieldField) { + flags |= OdinDocEntityFlag_BitField_Field; + } + if (e->Variable.is_foreign) { flags |= OdinDocEntityFlag_Foreign; } if (e->Variable.is_export) { flags |= OdinDocEntityFlag_Export; } if (e->Variable.thread_local_model != "") { @@ -873,7 +890,12 @@ gb_internal OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) if (init_expr == nullptr) { init_expr = e->Variable.init_expr; } - field_group_index = e->Variable.field_group_index; + + if (e->flags & EntityFlag_BitFieldField) { + field_group_index = cast(i32)e->Variable.bit_field_bit_size; + } else { + field_group_index = e->Variable.field_group_index; + } break; case Entity_Constant: field_group_index = e->Constant.field_group_index; -- cgit v1.2.3 From 5f001f6d5138d61fbb4900c951e2ccb12894d5ed Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Feb 2024 18:15:13 +0000 Subject: Allow casting between a `bit_field` and its backing type --- src/check_decl.cpp | 1 + src/check_expr.cpp | 7 +++++++ src/llvm_backend_expr.cpp | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 3ccf1b97a..2c0f7a7b8 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -210,6 +210,7 @@ gb_internal bool is_type_distinct(Ast *node) { case Ast_UnionType: case Ast_EnumType: case Ast_ProcType: + case Ast_BitFieldType: return true; case Ast_PointerType: diff --git a/src/check_expr.cpp b/src/check_expr.cpp index aba2f5831..792e5b43c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2908,6 +2908,13 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type } } + if (is_type_bit_field(src)) { + return are_types_identical(core_type(src->BitField.backing_type), dst); + } + if (is_type_bit_field(dst)) { + return are_types_identical(src, core_type(dst->BitField.backing_type)); + } + if (is_type_integer(src) && is_type_rune(dst)) { return true; } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 6bef21822..7b31ca989 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1946,6 +1946,24 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { } } + // bit_field <-> backing type + if (is_type_bit_field(src)) { + if (are_types_identical(src->BitField.backing_type, dst)) { + lbValue res = {}; + res.type = t; + res.value = value.value; + return res; + } + } + if (is_type_bit_field(dst)) { + if (are_types_identical(src, dst->BitField.backing_type)) { + lbValue res = {}; + res.type = t; + res.value = value.value; + return res; + } + } + // Pointer <-> uintptr if (is_type_pointer(src) && is_type_uintptr(dst)) { -- cgit v1.2.3 From afcc2889ecf532e03878825d7eb713674d1e7af7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Feb 2024 18:41:15 +0000 Subject: Support compound literals for `bit_field` --- core/odin/doc-format/doc_format.odin | 2 +- src/check_expr.cpp | 49 ++++++++++++++++++++++++++++++++---- src/llvm_backend_expr.cpp | 32 +++++++++++++++++++++++ 3 files changed, 77 insertions(+), 6 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/core/odin/doc-format/doc_format.odin b/core/odin/doc-format/doc_format.odin index ebc05c4dc..5636b1059 100644 --- a/core/odin/doc-format/doc_format.odin +++ b/core/odin/doc-format/doc_format.odin @@ -139,7 +139,7 @@ Entity :: struct { // May be used by (Struct fields and procedure fields): // .Variable // .Constant - // This is equal to the "bit size" it this is a `bit_field`s field + // This is equal to the negative of the "bit size" it this is a `bit_field`s field field_group_index: i32le, // May used by: diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 792e5b43c..d5890b191 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8440,6 +8440,11 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice fields_visited_through_raw_union = {}; defer (string_map_destroy(&fields_visited_through_raw_union)); + String assignment_str = str_lit("structure literal"); + if (bt->kind == Type_BitField) { + assignment_str = str_lit("bit_field literal"); + } + for (Ast *elem : elems) { if (elem->kind != Ast_FieldValue) { error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); @@ -8461,17 +8466,26 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, SliceStruct.fields[sel.index[0]]; + Entity *field = nullptr; + if (bt->kind == Type_Struct) { + field = bt->Struct.fields[sel.index[0]]; + } else if (bt->kind == Type_BitField) { + field = bt->BitField.fields[sel.index[0]]; + } else { + GB_PANIC("Unknown type"); + } + + add_entity_use(c, fv->field, field); if (string_set_update(&fields_visited, name)) { if (sel.index.count > 1) { if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) { error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found)); } else { - error(fv->field, "Duplicate or reused field '%.*s' in structure literal", LIT(sel.entity->token.string)); + error(fv->field, "Duplicate or reused field '%.*s' in %.*s", LIT(sel.entity->token.string), LIT(assignment_str)); } } else { - error(fv->field, "Duplicate field '%.*s' in structure literal", LIT(field->token.string)); + error(fv->field, "Duplicate field '%.*s' in %.*s", LIT(field->token.string), LIT(assignment_str)); } continue; } else if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) { @@ -8479,11 +8493,13 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slicefield, "Cannot assign to the %d-nested anonymous indirect field '%.*s' in a structure literal", cast(int)sel.index.count-1, LIT(name)); + error(fv->field, "Cannot assign to the %d-nested anonymous indirect field '%.*s' in a %.*s", cast(int)sel.index.count-1, LIT(name), LIT(assignment_str)); continue; } if (sel.index.count > 1) { + GB_ASSERT(bt->kind == Type_Struct); + if (is_constant) { Type *ft = type; for (i32 index : sel.index) { @@ -8544,7 +8560,15 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slicetype, str_lit("structure literal")); + u8 prev_bit_field_bit_size = c->bit_field_bit_size; + if (field->kind == Entity_Variable && field->Variable.bit_field_bit_size) { + // HACK NOTE(bill): This is a bit of a hack, but it will work fine for this use case + c->bit_field_bit_size = field->Variable.bit_field_bit_size; + } + + check_assignment(c, &o, field->type, assignment_str); + + c->bit_field_bit_size = prev_bit_field_bit_size; } } @@ -9346,6 +9370,21 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * } break; } + case Type_BitField: { + if (cl->elems.count == 0) { + break; // NOTE(bill): No need to init + } + is_constant = false; + if (cl->elems[0]->kind != Ast_FieldValue) { + gbString type_str = type_to_string(type); + error(node, "%s ('bit_field') compound literals are only allowed to contain 'field = value' elements", type_str); + gb_string_free(type_str); + } else { + check_compound_literal_field_values(c, cl->elems, o, type, is_constant); + } + break; + } + default: { if (cl->elems.count == 0) { diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 7b31ca989..7e000c9e8 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4235,6 +4235,38 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { switch (bt->kind) { default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break; + case Type_BitField: + for (Ast *elem : cl->elems) { + ast_node(fv, FieldValue, elem); + String name = fv->field->Ident.token.string; + Selection sel = lookup_field(bt, name, false); + GB_ASSERT(sel.is_bit_field); + GB_ASSERT(!sel.indirect); + GB_ASSERT(sel.index.count == 1); + GB_ASSERT(sel.entity != nullptr); + + i64 index = sel.index[0]; + i64 bit_offset = 0; + i64 bit_size = -1; + for_array(i, bt->BitField.fields) { + Entity *f = bt->BitField.fields[i]; + if (f == sel.entity) { + bit_offset = bt->BitField.bit_offsets[i]; + bit_size = bt->BitField.bit_sizes[i]; + break; + } + } + GB_ASSERT(bit_size > 0); + + Type *field_type = sel.entity->type; + lbValue field_expr = lb_build_expr(p, fv->value); + field_expr = lb_emit_conv(p, field_expr, field_type); + + lbAddr field_addr = lb_addr_bit_field(v.addr, field_type, index, bit_offset, bit_size); + lb_addr_store(p, field_addr, field_expr); + } + return v; + case Type_Struct: { // TODO(bill): "constant" '#raw_union's are not initialized constantly at the moment. // NOTE(bill): This is due to the layout of the unions when printed to LLVM-IR -- cgit v1.2.3 From fd987b29ff670e6dd7c33c1682927f7bc9c47ef6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Feb 2024 18:47:43 +0000 Subject: Improve error handling with assignments to `bit_field` using constants --- src/check_expr.cpp | 48 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 13 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index d5890b191..3a8cdf0b1 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1886,33 +1886,55 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i BigInt i = v.value_integer; - i64 bit_size = type_size_of(type); + i64 byte_size = type_size_of(type); BigInt umax = {}; BigInt imin = {}; BigInt imax = {}; - if (bit_size < 16) { - big_int_from_u64(&umax, unsigned_integer_maxs[bit_size]); - big_int_from_i64(&imin, signed_integer_mins[bit_size]); - big_int_from_i64(&imax, signed_integer_maxs[bit_size]); - } else { + if (c->bit_field_bit_size > 0) { + i64 bit_size = gb_min(cast(i64)(8*byte_size), cast(i64)c->bit_field_bit_size); + big_int_from_u64(&umax, 1); big_int_from_i64(&imin, 1); big_int_from_i64(&imax, 1); - BigInt bi128 = {}; - BigInt bi127 = {}; - big_int_from_i64(&bi128, 128); - big_int_from_i64(&bi127, 127); + BigInt bu = {}; + BigInt bi = {}; + big_int_from_i64(&bu, bit_size); + big_int_from_i64(&bi, bit_size-1); - big_int_shl_eq(&umax, &bi128); + big_int_shl_eq(&umax, &bu); mp_decr(&umax); - big_int_shl_eq(&imin, &bi127); + big_int_shl_eq(&imin, &bi); big_int_neg(&imin, &imin); - big_int_shl_eq(&imax, &bi127); + big_int_shl_eq(&imax, &bi); mp_decr(&imax); + } else { + if (byte_size < 16) { + big_int_from_u64(&umax, unsigned_integer_maxs[byte_size]); + big_int_from_i64(&imin, signed_integer_mins[byte_size]); + big_int_from_i64(&imax, signed_integer_maxs[byte_size]); + } else { + big_int_from_u64(&umax, 1); + big_int_from_i64(&imin, 1); + big_int_from_i64(&imax, 1); + + BigInt bi128 = {}; + BigInt bi127 = {}; + big_int_from_i64(&bi128, 128); + big_int_from_i64(&bi127, 127); + + big_int_shl_eq(&umax, &bi128); + mp_decr(&umax); + + big_int_shl_eq(&imin, &bi127); + big_int_neg(&imin, &imin); + + big_int_shl_eq(&imax, &bi127); + mp_decr(&imax); + } } switch (type->Basic.kind) { -- cgit v1.2.3 From c7c68520577133d6332bd6df98c44e751b571c03 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 12 Mar 2024 12:11:48 +0000 Subject: Support swizzle selector syntax `.xyzw` for `#simd` vectors --- src/check_expr.cpp | 13 +++++--- src/llvm_backend_expr.cpp | 4 +-- src/llvm_backend_general.cpp | 26 +++++++++++++++- src/types.cpp | 71 ++++++++++++++++++++++++++++---------------- 4 files changed, 82 insertions(+), 32 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 3a8cdf0b1..0911e48cf 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4920,7 +4920,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod } } - if (entity == nullptr && selector->kind == Ast_Ident && is_type_array(type_deref(operand->type))) { + if (entity == nullptr && selector->kind == Ast_Ident && (is_type_array(type_deref(operand->type)) || is_type_simd_vector(type_deref(operand->type)))) { String field_name = selector->Ident.token.string; if (1 < field_name.len && field_name.len <= 4) { u8 swizzles_xyzw[4] = {'x', 'y', 'z', 'w'}; @@ -4975,8 +4975,10 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod Type *original_type = operand->type; Type *array_type = base_type(type_deref(original_type)); - GB_ASSERT(array_type->kind == Type_Array); - i64 array_count = array_type->Array.count; + GB_ASSERT(array_type->kind == Type_Array || array_type->kind == Type_SimdVector); + + i64 array_count = get_array_type_count(array_type); + for (u8 i = 0; i < index_count; i++) { u8 idx = indices>>(i*2) & 3; if (idx >= array_count) { @@ -4996,7 +4998,6 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod se->swizzle_count = index_count; se->swizzle_indices = indices; - AddressingMode prev_mode = operand->mode; operand->mode = Addressing_SwizzleValue; operand->type = determine_swizzle_array_type(original_type, type_hint, index_count); @@ -5010,6 +5011,10 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod break; } + if (array_type->kind == Type_SimdVector) { + operand->mode = Addressing_Value; + } + Entity *swizzle_entity = alloc_entity_variable(nullptr, make_token_ident(field_name), operand->type, EntityState_Resolved); add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value); return swizzle_entity; diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 5bc961af2..98618798b 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4655,7 +4655,7 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { if (se->swizzle_count > 0) { Type *array_type = base_type(type_deref(tav.type)); - GB_ASSERT(array_type->kind == Type_Array); + GB_ASSERT(array_type->kind == Type_Array || array_type->kind == Type_SimdVector); u8 swizzle_count = se->swizzle_count; u8 swizzle_indices_raw = se->swizzle_indices; u8 swizzle_indices[4] = {}; @@ -4671,7 +4671,7 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { a = lb_addr_get_ptr(p, addr); } - GB_ASSERT(is_type_array(expr->tav.type)); + GB_ASSERT(is_type_array(expr->tav.type) || is_type_simd_vector(expr->tav.type)); return lb_addr_swizzle(a, expr->tav.type, swizzle_count, swizzle_indices); } diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 4ff8482a7..09de90dc9 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -434,7 +434,7 @@ gb_internal lbAddr lb_addr_soa_variable(lbValue addr, lbValue index, Ast *index_ } gb_internal lbAddr lb_addr_swizzle(lbValue addr, Type *array_type, u8 swizzle_count, u8 swizzle_indices[4]) { - GB_ASSERT(is_type_array(array_type)); + GB_ASSERT(is_type_array(array_type) || is_type_simd_vector(array_type)); GB_ASSERT(1 < swizzle_count && swizzle_count <= 4); lbAddr v = {lbAddr_Swizzle, addr}; v.swizzle.type = array_type; @@ -1264,6 +1264,30 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) { return lb_addr_load(p, res); } else if (addr.kind == lbAddr_Swizzle) { Type *array_type = base_type(addr.swizzle.type); + if (array_type->kind == Type_SimdVector) { + lbValue vec = lb_emit_load(p, addr.addr); + u8 index_count = addr.swizzle.count; + if (index_count == 0) { + return vec; + } + + unsigned mask_len = cast(unsigned)index_count; + LLVMValueRef *mask_elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, index_count); + for (isize i = 0; i < index_count; i++) { + mask_elems[i] = LLVMConstInt(lb_type(p->module, t_u32), addr.swizzle.indices[i], false); + } + + LLVMValueRef mask = LLVMConstVector(mask_elems, mask_len); + + LLVMValueRef v1 = vec.value; + LLVMValueRef v2 = vec.value; + + lbValue res = {}; + res.type = addr.swizzle.type; + res.value = LLVMBuildShuffleVector(p->builder, v1, v2, mask, ""); + return res; + } + GB_ASSERT(array_type->kind == Type_Array); unsigned res_align = cast(unsigned)type_align_of(addr.swizzle.type); diff --git a/src/types.cpp b/src/types.cpp index e9e91dcd4..5a3ad5d6b 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -3430,31 +3430,6 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name } return sel; - } else if (type->kind == Type_Array) { - if (type->Array.count <= 4) { - // HACK(bill): Memory leak - switch (type->Array.count) { - #define _ARRAY_FIELD_CASE_IF(_length, _name) \ - if (field_name == (_name)) { \ - selection_add_index(&sel, (_length)-1); \ - sel.entity = alloc_entity_array_elem(nullptr, make_token_ident(str_lit(_name)), type->Array.elem, (_length)-1); \ - return sel; \ - } - #define _ARRAY_FIELD_CASE(_length, _name0, _name1) \ - case (_length): \ - _ARRAY_FIELD_CASE_IF(_length, _name0); \ - _ARRAY_FIELD_CASE_IF(_length, _name1); \ - /*fallthrough*/ - - _ARRAY_FIELD_CASE(4, "w", "a"); - _ARRAY_FIELD_CASE(3, "z", "b"); - _ARRAY_FIELD_CASE(2, "y", "g"); - _ARRAY_FIELD_CASE(1, "x", "r"); - default: break; - - #undef _ARRAY_FIELD_CASE - } - } } else if (type->kind == Type_DynamicArray) { GB_ASSERT(t_allocator != nullptr); String allocator_str = str_lit("allocator"); @@ -3475,7 +3450,53 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name sel.entity = entity__allocator; return sel; } + + +#define _ARRAY_FIELD_CASE_IF(_length, _name) \ + if (field_name == (_name)) { \ + selection_add_index(&sel, (_length)-1); \ + sel.entity = alloc_entity_array_elem(nullptr, make_token_ident(str_lit(_name)), elem, (_length)-1); \ + return sel; \ } +#define _ARRAY_FIELD_CASE(_length, _name0, _name1) \ +case (_length): \ + _ARRAY_FIELD_CASE_IF(_length, _name0); \ + _ARRAY_FIELD_CASE_IF(_length, _name1); \ + /*fallthrough*/ + + + } else if (type->kind == Type_Array) { + + Type *elem = type->Array.elem; + + if (type->Array.count <= 4) { + // HACK(bill): Memory leak + switch (type->Array.count) { + + _ARRAY_FIELD_CASE(4, "w", "a"); + _ARRAY_FIELD_CASE(3, "z", "b"); + _ARRAY_FIELD_CASE(2, "y", "g"); + _ARRAY_FIELD_CASE(1, "x", "r"); + default: break; + } + } + } else if (type->kind == Type_SimdVector) { + + Type *elem = type->SimdVector.elem; + if (type->SimdVector.count <= 4) { + // HACK(bill): Memory leak + switch (type->SimdVector.count) { + _ARRAY_FIELD_CASE(4, "w", "a"); + _ARRAY_FIELD_CASE(3, "z", "b"); + _ARRAY_FIELD_CASE(2, "y", "g"); + _ARRAY_FIELD_CASE(1, "x", "r"); + default: break; + } + } + } + +#undef _ARRAY_FIELD_CASE +#undef _ARRAY_FIELD_CASE return sel; } -- cgit v1.2.3 From f7ec628cb20375645f99a851d55e031c76688724 Mon Sep 17 00:00:00 2001 From: rick-masters Date: Tue, 12 Mar 2024 23:57:13 +0000 Subject: Fix check for too many arguments to a polymorphic record type. --- src/check_expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 0911e48cf..c5517ffa3 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6847,7 +6847,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O Array ordered_operands = operands; if (!named_fields) { - ordered_operands = array_make(permanent_allocator(), param_count); + ordered_operands = array_make(permanent_allocator(), operands.count); array_copy(&ordered_operands, operands, 0); } else { TEMPORARY_ALLOCATOR_GUARD(); -- cgit v1.2.3 From 7bc962b852e9e4ad0a8619f7265192f528cbe0be Mon Sep 17 00:00:00 2001 From: rick-masters Date: Tue, 12 Mar 2024 23:58:20 +0000 Subject: Fix variable used to index polymorphic parameter. --- src/check_expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index c5517ffa3..1e4c7499b 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6796,7 +6796,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O isize index = lookup_polymorphic_record_parameter(original_type, name); if (index >= 0) { TypeTuple *params = get_record_polymorphic_params(original_type); - Entity *e = params->variables[i]; + Entity *e = params->variables[index]; if (e->kind == Entity_Constant) { check_expr_with_type_hint(c, &operands[i], fv->value, e->type); continue; -- cgit v1.2.3 From 3875fb08e839d45d2c855115008857b3134f8337 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 16 Mar 2024 22:12:17 +0000 Subject: Fix #3284 --- src/check_expr.cpp | 2 ++ src/check_stmt.cpp | 6 ++++++ src/parser.hpp | 2 ++ 3 files changed, 10 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 1e4c7499b..3f46e2bdd 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8405,6 +8405,7 @@ gb_internal ExprKind check_or_branch_expr(CheckerContext *c, Operand *o, Ast *no switch (be->token.kind) { case Token_or_break: + node->viral_state_flags |= ViralStateFlag_ContainsOrBreak; if ((c->stmt_flags & Stmt_BreakAllowed) == 0 && label == nullptr) { error(be->token, "'%.*s' only allowed in non-inline loops or 'switch' statements", LIT(name)); } @@ -10254,6 +10255,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast case_end; case_ast_node(re, OrReturnExpr, node); + node->viral_state_flags |= ViralStateFlag_ContainsOrReturn; return check_or_return_expr(c, o, node, type_hint); case_end; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a7dd9743b..1aa473fd6 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -221,6 +221,12 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) return true; } break; + + case Ast_ExprStmt: + if (stmt->ExprStmt.expr->viral_state_flags & ViralStateFlag_ContainsOrBreak) { + return true; + } + break; } return false; diff --git a/src/parser.hpp b/src/parser.hpp index f410419d4..f5997c4bd 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -310,6 +310,8 @@ enum StateFlag : u8 { enum ViralStateFlag : u8 { ViralStateFlag_ContainsDeferredProcedure = 1<<0, + ViralStateFlag_ContainsOrBreak = 1<<1, + ViralStateFlag_ContainsOrReturn = 1<<2, }; -- cgit v1.2.3 From 43d695a9900ff72064c93ada30a882720af8d489 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 18 Mar 2024 11:21:06 +0000 Subject: Fix `for x in y` where `y` is an "optional ok" value, but ignores `#optional_allocator_error` values --- src/check_expr.cpp | 22 ++++++++++++++-------- src/check_stmt.cpp | 6 +++++- 2 files changed, 19 insertions(+), 9 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 3f46e2bdd..0f74a8ccb 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -108,7 +108,7 @@ gb_internal Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint); -gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_); +gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_, bool change_operand=true); gb_internal void check_or_else_right_type(CheckerContext *c, Ast *expr, String const &name, Type *right_type); gb_internal void check_or_else_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_); @@ -7801,7 +7801,7 @@ gb_internal ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o, } -gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_) { +gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_, bool change_operand) { switch (x->mode) { case Addressing_MapIndex: case Addressing_OptionalOk: @@ -7819,22 +7819,28 @@ gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type * Type *pt = base_type(type_of_expr(expr->CallExpr.proc)); if (is_type_proc(pt)) { Type *tuple = pt->Proc.results; - add_type_and_value(c, x->expr, x->mode, tuple, x->value); if (pt->Proc.result_count >= 2) { if (ok_type_) *ok_type_ = tuple->Tuple.variables[1]->type; } - expr->CallExpr.optional_ok_one = false; - x->type = tuple; + if (change_operand) { + expr->CallExpr.optional_ok_one = false; + x->type = tuple; + add_type_and_value(c, x->expr, x->mode, tuple, x->value); + } return; } } Type *tuple = make_optional_ok_type(x->type); + if (ok_type_) *ok_type_ = tuple->Tuple.variables[1]->type; - add_type_and_value(c, x->expr, x->mode, tuple, x->value); - x->type = tuple; - GB_ASSERT(is_type_tuple(type_of_expr(x->expr))); + + if (change_operand) { + add_type_and_value(c, x->expr, x->mode, tuple, x->value); + x->type = tuple; + GB_ASSERT(is_type_tuple(type_of_expr(x->expr))); + } } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index d31af08da..a6ca4b9dd 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1544,7 +1544,11 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) } } else if (operand.mode != Addressing_Invalid) { if (operand.mode == Addressing_OptionalOk || operand.mode == Addressing_OptionalOkPtr) { - check_promote_optional_ok(ctx, &operand, nullptr, nullptr); + Type *end_type = nullptr; + check_promote_optional_ok(ctx, &operand, nullptr, &end_type, false); + if (is_type_boolean(end_type)) { + check_promote_optional_ok(ctx, &operand, nullptr, &end_type, true); + } } bool is_ptr = is_type_pointer(operand.type); Type *t = base_type(type_deref(operand.type)); -- cgit v1.2.3 From ebd3065aa2806118f1f0868eb633edb72c230aa5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 18 Mar 2024 14:17:13 +0000 Subject: Add error message for C programmers which do `Foo{.x = 123}` rather than `Foo{x = 123}` --- src/check_expr.cpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 0f74a8ccb..bea204e2b 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8485,18 +8485,26 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slicefield->kind != Ast_Ident) { - gbString expr_str = expr_to_string(fv->field); + Ast *ident = fv->field; + if (ident->kind == Ast_ImplicitSelectorExpr) { + gbString expr_str = expr_to_string(ident); + error(ident, "Field names do not start with a '.', remove the '.' in structure literal", expr_str); + gb_string_free(expr_str); + + ident = ident->ImplicitSelectorExpr.selector; + } + if (ident->kind != Ast_Ident) { + gbString expr_str = expr_to_string(ident); error(elem, "Invalid field name '%s' in structure literal", expr_str); gb_string_free(expr_str); continue; } - String name = fv->field->Ident.token.string; + String name = ident->Ident.token.string; Selection sel = lookup_field(type, name, o->mode == Addressing_Type); bool is_unknown = sel.entity == nullptr; if (is_unknown) { - error(fv->field, "Unknown field '%.*s' in structure literal", LIT(name)); + error(ident, "Unknown field '%.*s' in structure literal", LIT(name)); continue; } @@ -8510,24 +8518,24 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slicefield, field); + add_entity_use(c, ident, field); if (string_set_update(&fields_visited, name)) { if (sel.index.count > 1) { if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) { - error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found)); + error(ident, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found)); } else { - error(fv->field, "Duplicate or reused field '%.*s' in %.*s", LIT(sel.entity->token.string), LIT(assignment_str)); + error(ident, "Duplicate or reused field '%.*s' in %.*s", LIT(sel.entity->token.string), LIT(assignment_str)); } } else { - error(fv->field, "Duplicate field '%.*s' in %.*s", LIT(field->token.string), LIT(assignment_str)); + error(ident, "Duplicate field '%.*s' in %.*s", LIT(field->token.string), LIT(assignment_str)); } continue; } else if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) { - error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found)); + error(ident, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found)); continue; } if (sel.indirect) { - error(fv->field, "Cannot assign to the %d-nested anonymous indirect field '%.*s' in a %.*s", cast(int)sel.index.count-1, LIT(name), LIT(assignment_str)); + error(ident, "Cannot assign to the %d-nested anonymous indirect field '%.*s' in a %.*s", cast(int)sel.index.count-1, LIT(name), LIT(assignment_str)); continue; } -- cgit v1.2.3 From 009b6f44e379e7644e0f2987663d52186dea5656 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 18 Mar 2024 15:18:10 +0000 Subject: Add loads of checks for common mistakes for C programmer --- src/check_expr.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index bea204e2b..b58006427 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1539,7 +1539,25 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam if (is_blank_ident(name)) { error(n, "'_' cannot be used as a value"); } else { + ERROR_BLOCK(); 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"); + } } o->type = t_invalid; o->mode = Addressing_Invalid; -- 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/check_expr.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 ec9ac593232d8e201639615ba075717dab066752 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 18 Mar 2024 21:37:40 +0000 Subject: Unify "Did you mean" strings --- src/check_expr.cpp | 82 +++++++++++++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 41 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7da113455..bb31a1646 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1535,46 +1535,46 @@ struct CIdentSuggestion { // 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'?")}, + {str_lit("while"), str_lit("'for'? Odin only has one loop construct: 'for'")}, + + {str_lit("sizeof"), str_lit("'size_of'?")}, + {str_lit("alignof"), str_lit("'align_of'?")}, + {str_lit("offsetof"), str_lit("'offset_of'?")}, + + {str_lit("_Bool"), str_lit("'bool'?")}, + + {str_lit("char"), str_lit("'u8', 'i8', or 'c.char' (which is part of 'core:c')?")}, + {str_lit("short"), str_lit("'i16' or 'c.short' (which is part of 'core:c')?")}, + {str_lit("long"), str_lit("'c.long' (which is part of 'core:c')?")}, + {str_lit("float"), str_lit("'f32'?")}, + {str_lit("double"), str_lit("'f64'?")}, + {str_lit("unsigned"), str_lit("'c.uint' (which is part of 'core:c')?")}, + {str_lit("signed"), str_lit("'c.int' (which is part of 'core:c')?")}, + + {str_lit("size_t"), str_lit("'uint', or 'c.size_t' (which is part of 'core:c')?")}, + {str_lit("ssize_t"), str_lit("'int', or 'c.ssize_t' (which is part of 'core:c')?")}, + + {str_lit("uintptr_t"), str_lit("'uintptr'?")}, + {str_lit("intptr_t"), str_lit("'uintptr' or `int` or something else?")}, + {str_lit("ptrdiff_t"), str_lit("'int' or 'c.ptrdiff_t' (which is part of 'core:c')?")}, + {str_lit("intmax_t"), str_lit("'c.intmax_t' (which is part of 'core:c')?")}, + {str_lit("uintmax_t"), str_lit("'c.uintmax_t' (which is part of 'core:c')?")}, + + {str_lit("uint8_t"), str_lit("'u8'?")}, + {str_lit("int8_t"), str_lit("'i8'?")}, + {str_lit("uint16_t"), str_lit("'u16'?")}, + {str_lit("int16_t"), str_lit("'i16'?")}, + {str_lit("uint32_t"), str_lit("'u32'?")}, + {str_lit("int32_t"), str_lit("'i32'?")}, + {str_lit("uint64_t"), str_lit("'u64'?")}, + {str_lit("int64_t"), str_lit("'i64'?")}, + {str_lit("uint128_t"), str_lit("'u128'?")}, + {str_lit("int128_t"), str_lit("'i128'?")}, + + {str_lit("float32"), str_lit("'f32'?")}, + {str_lit("float64"), str_lit("'f64'?")}, + {str_lit("float32_t"), str_lit("'f32'?")}, + {str_lit("float64_t"), str_lit("'f64'?")}, }; gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name) { @@ -1595,7 +1595,7 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam for (CIdentSuggestion const &suggestion : c_ident_suggestions) { if (name == suggestion.name) { - error_line("\tSuggestion: %s\n", LIT(suggestion.msg)); + error_line("\tSuggestion: Did you mean %s\n", LIT(suggestion.msg)); } } } -- cgit v1.2.3 From 80ecf5b68aa02a2445aa4cbf3d1d46ed5a007d75 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 Mar 2024 13:32:37 +0000 Subject: On `x: [?]T = {...}`, minimize errors by using the `[?]T` expression as a kind of hint --- src/check_expr.cpp | 47 ++++++++++++++++++++++++++++++++++++++--------- src/check_stmt.cpp | 12 +++++++----- src/checker.hpp | 1 + 3 files changed, 46 insertions(+), 14 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index bb31a1646..ed6182928 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8672,6 +8672,24 @@ 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 +8700,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 +8734,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 +8747,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/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; -- cgit v1.2.3 From 8ff788f4fff7504b8b6bca1183a9be98b8765cbc Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 Mar 2024 13:38:13 +0000 Subject: Add better suggestion for [?]T mistake --- src/check_expr.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ed6182928..236d44a43 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); } } @@ -8678,7 +8684,6 @@ gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr) { return false; } - // [?]Type if (type_expr->kind == Ast_ArrayType && type_expr->ArrayType.count != nullptr) { Ast *count = type_expr->ArrayType.count; -- 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/check_expr.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 a750fc0ba63c9f1461bba4cc0446b1b4c2d2b3a9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 Mar 2024 21:05:23 +0000 Subject: Add `#row_major matrix[R, C]T` As well as `#column_major matrix[R, C]T` as an alias for just `matrix[R, C]T`. This is because some libraries require a row_major internal layout but still want to be used with row or major oriented vectors. --- base/runtime/core.odin | 4 ++++ core/fmt/fmt.odin | 12 ++++++++++-- core/reflect/types.odin | 4 ++++ src/check_expr.cpp | 12 +++++++++--- src/check_type.cpp | 2 +- src/llvm_backend_const.cpp | 4 ++-- src/llvm_backend_expr.cpp | 39 +++++++++++++++++++++++++++++---------- src/llvm_backend_type.cpp | 3 ++- src/llvm_backend_utility.cpp | 22 +++++++++++++++------- src/parser.cpp | 13 +++++++++++++ src/parser.hpp | 1 + src/types.cpp | 19 +++++++++++++++---- 12 files changed, 105 insertions(+), 30 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/base/runtime/core.odin b/base/runtime/core.odin index 8f27ca674..7ad3ef1d6 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -177,6 +177,10 @@ Type_Info_Matrix :: struct { row_count: int, column_count: int, // Total element count = column_count * elem_stride + layout: enum u8 { + Column_Major, // array of column vectors + Row_Major, // array of row vectors + }, } Type_Info_Soa_Pointer :: struct { elem: ^Type_Info, diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 02803f882..6f9801bc8 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -2396,7 +2396,11 @@ fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix for col in 0.. 0 { io.write_string(fi.writer, ", ", &fi.n) } - offset := (row + col*info.elem_stride)*info.elem_size + offset: int + switch info.layout { + case .Column_Major: offset = (row + col*info.elem_stride)*info.elem_size + case .Row_Major: offset = (col + row*info.elem_stride)*info.elem_size + } data := uintptr(v.data) + uintptr(offset) fmt_arg(fi, any{rawptr(data), info.elem.id}, verb) @@ -2410,7 +2414,11 @@ fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix for col in 0.. 0 { io.write_string(fi.writer, ", ", &fi.n) } - offset := (row + col*info.elem_stride)*info.elem_size + offset: int + switch info.layout { + case .Column_Major: offset = (row + col*info.elem_stride)*info.elem_size + case .Row_Major: offset = (col + row*info.elem_stride)*info.elem_size + } data := uintptr(v.data) + uintptr(offset) fmt_arg(fi, any{rawptr(data), info.elem.id}, verb) diff --git a/core/reflect/types.odin b/core/reflect/types.odin index 2b96dd4fb..9cff46a00 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -173,6 +173,7 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool { y := b.variant.(Type_Info_Matrix) or_return if x.row_count != y.row_count { return false } if x.column_count != y.column_count { return false } + if x.layout != y.layout { return false } return are_types_identical(x.elem, y.elem) case Type_Info_Bit_Field: @@ -689,6 +690,9 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) - write_type(w, info.pointer, &n) or_return case Type_Info_Matrix: + if info.layout == .Row_Major { + io.write_string(w, "#row_major ", &n) or_return + } io.write_string(w, "matrix[", &n) or_return io.write_i64(w, i64(info.row_count), 10, &n) or_return io.write_string(w, ", ", &n) or_return diff --git a/src/check_expr.cpp b/src/check_expr.cpp index f359d5a54..67c7f1a04 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3431,6 +3431,11 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand if (xt->Matrix.column_count != yt->Matrix.row_count) { goto matrix_error; } + + if (xt->Matrix.is_row_major != yt->Matrix.is_row_major) { + goto matrix_error; + } + x->mode = Addressing_Value; if (are_types_identical(xt, yt)) { if (!is_type_named(x->type) && is_type_named(y->type)) { @@ -3438,7 +3443,8 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand x->type = y->type; } } else { - x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, yt->Matrix.column_count); + bool is_row_major = xt->Matrix.is_row_major && yt->Matrix.is_row_major; + x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, yt->Matrix.column_count, nullptr, nullptr, is_row_major); } goto matrix_success; } else if (yt->kind == Type_Array) { @@ -3452,7 +3458,7 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand // Treat arrays as column vectors x->mode = Addressing_Value; - if (type_hint == nullptr && xt->Matrix.row_count == yt->Array.count) { + if (xt->Matrix.row_count == yt->Array.count) { x->type = y->type; } else { x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, 1); @@ -3483,7 +3489,7 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand // Treat arrays as row vectors x->mode = Addressing_Value; - if (type_hint == nullptr && yt->Matrix.column_count == xt->Array.count) { + if (yt->Matrix.column_count == xt->Array.count) { x->type = x->type; } else { x->type = alloc_type_matrix(yt->Matrix.elem, 1, yt->Matrix.column_count); diff --git a/src/check_type.cpp b/src/check_type.cpp index d5cf187a4..3fe633892 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2658,7 +2658,7 @@ gb_internal void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node) } type_assign:; - *type = alloc_type_matrix(elem, row_count, column_count, generic_row, generic_column); + *type = alloc_type_matrix(elem, row_count, column_count, generic_row, generic_column, mt->is_row_major); return; } diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 2291f24ac..bbb0b8387 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -1302,11 +1302,11 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo GB_ASSERT_MSG(elem_count == max_count, "%td != %td", elem_count, max_count); LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)total_count); - for_array(i, cl->elems) { TypeAndValue tav = cl->elems[i]->tav; GB_ASSERT(tav.mode != Addressing_Invalid); - i64 offset = matrix_row_major_index_to_offset(type, i); + i64 offset = 0; + offset = matrix_row_major_index_to_offset(type, i); values[offset] = lb_const_value(m, elem_type, tav.value, allow_local).value; } for (isize i = 0; i < total_count; i++) { diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 98618798b..6eb8fdcc6 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -684,12 +684,6 @@ gb_internal lbValue lb_emit_matrix_flatten(lbProcedure *p, lbValue m, Type *type Type *mt = base_type(m.type); GB_ASSERT(mt->kind == Type_Matrix); - // TODO(bill): Determine why this fails on Windows sometimes - if (false && lb_is_matrix_simdable(mt)) { - LLVMValueRef vector = lb_matrix_to_trimmed_vector(p, m); - return lb_matrix_cast_vector_to_type(p, vector, type); - } - lbAddr res = lb_add_local_generated(p, type, true); i64 row_count = mt->Matrix.row_count; @@ -763,6 +757,7 @@ gb_internal lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs, GB_ASSERT(is_type_matrix(yt)); GB_ASSERT(xt->Matrix.column_count == yt->Matrix.row_count); GB_ASSERT(are_types_identical(xt->Matrix.elem, yt->Matrix.elem)); + GB_ASSERT(xt->Matrix.is_row_major == yt->Matrix.is_row_major); Type *elem = xt->Matrix.elem; @@ -770,7 +765,7 @@ gb_internal lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs, unsigned inner = cast(unsigned)xt->Matrix.column_count; unsigned outer_columns = cast(unsigned)yt->Matrix.column_count; - if (lb_is_matrix_simdable(xt)) { + if (!xt->Matrix.is_row_major && lb_is_matrix_simdable(xt)) { unsigned x_stride = cast(unsigned)matrix_type_stride_in_elems(xt); unsigned y_stride = cast(unsigned)matrix_type_stride_in_elems(yt); @@ -812,7 +807,7 @@ gb_internal lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs, return lb_addr_load(p, res); } - { + if (!xt->Matrix.is_row_major) { lbAddr res = lb_add_local_generated(p, type, true); auto inners = slice_make(permanent_allocator(), inner); @@ -835,6 +830,30 @@ gb_internal lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs, } } + return lb_addr_load(p, res); + } else { + lbAddr res = lb_add_local_generated(p, type, true); + + auto inners = slice_make(permanent_allocator(), inner); + + for (unsigned i = 0; i < outer_rows; i++) { + for (unsigned j = 0; j < outer_columns; j++) { + lbValue dst = lb_emit_matrix_epi(p, res.addr, i, j); + for (unsigned k = 0; k < inner; k++) { + inners[k][0] = lb_emit_matrix_ev(p, lhs, i, k); + inners[k][1] = lb_emit_matrix_ev(p, rhs, k, j); + } + + lbValue sum = lb_const_nil(p->module, elem); + for (unsigned k = 0; k < inner; k++) { + lbValue a = inners[k][0]; + lbValue b = inners[k][1]; + sum = lb_emit_mul_add(p, a, b, sum, elem); + } + lb_emit_store(p, dst, sum); + } + } + return lb_addr_load(p, res); } } @@ -855,7 +874,7 @@ gb_internal lbValue lb_emit_matrix_mul_vector(lbProcedure *p, lbValue lhs, lbVal Type *elem = mt->Matrix.elem; - if (lb_is_matrix_simdable(mt)) { + if (!mt->Matrix.is_row_major && lb_is_matrix_simdable(mt)) { unsigned stride = cast(unsigned)matrix_type_stride_in_elems(mt); unsigned row_count = cast(unsigned)mt->Matrix.row_count; @@ -924,7 +943,7 @@ gb_internal lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbVal Type *elem = mt->Matrix.elem; - if (lb_is_matrix_simdable(mt)) { + if (!mt->Matrix.is_row_major && lb_is_matrix_simdable(mt)) { unsigned stride = cast(unsigned)matrix_type_stride_in_elems(mt); unsigned row_count = cast(unsigned)mt->Matrix.row_count; diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index aec1fb201..de83f5058 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -979,12 +979,13 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ tag_type = t_type_info_matrix; i64 ez = type_size_of(t->Matrix.elem); - LLVMValueRef vals[5] = { + LLVMValueRef vals[6] = { get_type_info_ptr(m, t->Matrix.elem), lb_const_int(m, t_int, ez).value, lb_const_int(m, t_int, matrix_type_stride_in_elems(t)).value, lb_const_int(m, t_int, t->Matrix.row_count).value, lb_const_int(m, t_int, t->Matrix.column_count).value, + lb_const_int(m, t_u8, cast(u8)t->Matrix.is_row_major).value, }; variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals)); diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index f18aa5521..fb61c41c3 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1464,14 +1464,16 @@ gb_internal lbValue lb_emit_matrix_epi(lbProcedure *p, lbValue s, isize row, isi Type *t = s.type; GB_ASSERT(is_type_pointer(t)); Type *mt = base_type(type_deref(t)); - if (column == 0) { - GB_ASSERT_MSG(is_type_matrix(mt) || is_type_array_like(mt), "%s", type_to_string(mt)); - return lb_emit_epi(p, s, row); - } else if (row == 0 && is_type_array_like(mt)) { - return lb_emit_epi(p, s, column); + + if (!mt->Matrix.is_row_major) { + if (column == 0) { + GB_ASSERT_MSG(is_type_matrix(mt) || is_type_array_like(mt), "%s", type_to_string(mt)); + return lb_emit_epi(p, s, row); + } else if (row == 0 && is_type_array_like(mt)) { + return lb_emit_epi(p, s, column); + } } - GB_ASSERT_MSG(is_type_matrix(mt), "%s", type_to_string(mt)); isize offset = matrix_indices_to_offset(mt, row, column); @@ -1491,7 +1493,13 @@ gb_internal lbValue lb_emit_matrix_ep(lbProcedure *p, lbValue s, lbValue row, lb row = lb_emit_conv(p, row, t_int); column = lb_emit_conv(p, column, t_int); - LLVMValueRef index = LLVMBuildAdd(p->builder, row.value, LLVMBuildMul(p->builder, column.value, stride_elems, ""), ""); + LLVMValueRef index = nullptr; + + if (mt->Matrix.is_row_major) { + index = LLVMBuildAdd(p->builder, column.value, LLVMBuildMul(p->builder, row.value, stride_elems, ""), ""); + } else { + index = LLVMBuildAdd(p->builder, row.value, LLVMBuildMul(p->builder, column.value, stride_elems, ""), ""); + } LLVMValueRef indices[2] = { LLVMConstInt(lb_type(p->module, t_int), 0, false), diff --git a/src/parser.cpp b/src/parser.cpp index 1aa40ccbf..eb9e73342 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2329,6 +2329,19 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { break; } return original_type; + } else if (name.string == "row_major" || + name.string == "column_major") { + Ast *original_type = parse_type(f); + Ast *type = unparen_expr(original_type); + switch (type->kind) { + case Ast_MatrixType: + type->MatrixType.is_row_major = (name.string == "row_major"); + break; + default: + syntax_error(type, "Expected a matrix type after #%.*s, got %.*s", LIT(name.string), LIT(ast_strings[type->kind])); + break; + } + return original_type; } else if (name.string == "partial") { Ast *tag = ast_basic_directive(f, token, name); Ast *original_expr = parse_expr(f, lhs); diff --git a/src/parser.hpp b/src/parser.hpp index f5997c4bd..ff3c5eb34 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -772,6 +772,7 @@ AST_KIND(_TypeBegin, "", bool) \ Ast *row_count; \ Ast *column_count; \ Ast *elem; \ + bool is_row_major; \ }) \ AST_KIND(_TypeEnd, "", bool) diff --git a/src/types.cpp b/src/types.cpp index 5a3ad5d6b..ab5e4de03 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -281,6 +281,7 @@ struct TypeProc { Type *generic_row_count; \ Type *generic_column_count; \ i64 stride_in_bytes; \ + bool is_row_major; \ }) \ TYPE_KIND(BitField, struct { \ Scope * scope; \ @@ -1002,7 +1003,7 @@ gb_internal Type *alloc_type_array(Type *elem, i64 count, Type *generic_count = return t; } -gb_internal Type *alloc_type_matrix(Type *elem, i64 row_count, i64 column_count, Type *generic_row_count = nullptr, Type *generic_column_count = nullptr) { +gb_internal Type *alloc_type_matrix(Type *elem, i64 row_count, i64 column_count, Type *generic_row_count = nullptr, Type *generic_column_count = nullptr, bool is_row_major = false) { if (generic_row_count != nullptr || generic_column_count != nullptr) { Type *t = alloc_type(Type_Matrix); t->Matrix.elem = elem; @@ -1010,12 +1011,14 @@ gb_internal Type *alloc_type_matrix(Type *elem, i64 row_count, i64 column_count, t->Matrix.column_count = column_count; t->Matrix.generic_row_count = generic_row_count; t->Matrix.generic_column_count = generic_column_count; + t->Matrix.is_row_major = is_row_major; return t; } Type *t = alloc_type(Type_Matrix); t->Matrix.elem = elem; t->Matrix.row_count = row_count; t->Matrix.column_count = column_count; + t->Matrix.is_row_major = is_row_major; return t; } @@ -1512,14 +1515,18 @@ gb_internal i64 matrix_indices_to_offset(Type *t, i64 row_index, i64 column_inde GB_ASSERT(0 <= row_index && row_index < t->Matrix.row_count); GB_ASSERT(0 <= column_index && column_index < t->Matrix.column_count); i64 stride_elems = matrix_type_stride_in_elems(t); - // NOTE(bill): Column-major layout internally - return row_index + stride_elems*column_index; + if (t->Matrix.is_row_major) { + return column_index + stride_elems*row_index; + } else { + // NOTE(bill): Column-major layout internally + return row_index + stride_elems*column_index; + } } gb_internal i64 matrix_row_major_index_to_offset(Type *t, i64 index) { t = base_type(t); GB_ASSERT(t->kind == Type_Matrix); - + i64 row_index = index/t->Matrix.column_count; i64 column_index = index%t->Matrix.column_count; return matrix_indices_to_offset(t, row_index, column_index); @@ -2690,6 +2697,7 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple case Type_Matrix: return x->Matrix.row_count == y->Matrix.row_count && x->Matrix.column_count == y->Matrix.column_count && + x->Matrix.is_row_major == y->Matrix.is_row_major && are_types_identical(x->Matrix.elem, y->Matrix.elem); case Type_DynamicArray: @@ -4735,6 +4743,9 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha break; case Type_Matrix: + if (type->Matrix.is_row_major) { + str = gb_string_appendc(str, "#row_major "); + } str = gb_string_appendc(str, gb_bprintf("matrix[%d, %d]", cast(int)type->Matrix.row_count, cast(int)type->Matrix.column_count)); str = write_type_to_string(str, type->Matrix.elem); break; -- cgit v1.2.3 From 18fb665bf684b6f55cc513eddd14e3224f9e70ad Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 Mar 2024 21:15:47 +0000 Subject: Correct matrix builtins for `#row_major` --- src/check_builtin.cpp | 4 ++-- src/check_expr.cpp | 11 +++++++++-- src/types.cpp | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 6de3b27f2..d3158961e 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -3488,7 +3488,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } } else { GB_ASSERT(t->kind == Type_Matrix); - operand->type = alloc_type_matrix(t->Matrix.elem, t->Matrix.column_count, t->Matrix.row_count); + operand->type = alloc_type_matrix(t->Matrix.elem, t->Matrix.column_count, t->Matrix.row_count, nullptr, nullptr, t->Matrix.is_row_major); } operand->type = check_matrix_type_hint(operand->type, type_hint); break; @@ -3556,7 +3556,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } operand->mode = Addressing_Value; - operand->type = alloc_type_matrix(elem, xt->Array.count, yt->Array.count); + operand->type = alloc_type_matrix(elem, xt->Array.count, yt->Array.count, nullptr, nullptr, false); operand->type = check_matrix_type_hint(operand->type, type_hint); break; } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 67c7f1a04..e9a6f5122 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3397,6 +3397,13 @@ gb_internal Type *check_matrix_type_hint(Type *matrix, Type *type_hint) { Type *th = base_type(type_hint); if (are_types_identical(th, xt)) { return type_hint; + } else if (xt->kind == Type_Matrix && th->kind == Type_Matrix) { + if (!are_types_identical(xt->Matrix.elem, th->Matrix.elem)) { + // ignore + } if (xt->Matrix.row_count == th->Matrix.row_count && + xt->Matrix.column_count == th->Matrix.column_count) { + return type_hint; + } } else if (xt->kind == Type_Matrix && th->kind == Type_Array) { if (!are_types_identical(xt->Matrix.elem, th->Array.elem)) { // ignore @@ -3461,7 +3468,7 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand if (xt->Matrix.row_count == yt->Array.count) { x->type = y->type; } else { - x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, 1); + x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, 1, nullptr, nullptr, xt->Matrix.is_row_major); } goto matrix_success; } @@ -3492,7 +3499,7 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand if (yt->Matrix.column_count == xt->Array.count) { x->type = x->type; } else { - x->type = alloc_type_matrix(yt->Matrix.elem, 1, yt->Matrix.column_count); + x->type = alloc_type_matrix(yt->Matrix.elem, 1, yt->Matrix.column_count, nullptr, nullptr, yt->Matrix.is_row_major); } goto matrix_success; } else if (are_types_identical(yt->Matrix.elem, xt)) { diff --git a/src/types.cpp b/src/types.cpp index ab5e4de03..c2358056b 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1003,7 +1003,7 @@ gb_internal Type *alloc_type_array(Type *elem, i64 count, Type *generic_count = return t; } -gb_internal Type *alloc_type_matrix(Type *elem, i64 row_count, i64 column_count, Type *generic_row_count = nullptr, Type *generic_column_count = nullptr, bool is_row_major = false) { +gb_internal Type *alloc_type_matrix(Type *elem, i64 row_count, i64 column_count, Type *generic_row_count, Type *generic_column_count, bool is_row_major) { if (generic_row_count != nullptr || generic_column_count != nullptr) { Type *t = alloc_type(Type_Matrix); t->Matrix.elem = elem; -- cgit v1.2.3 From b1dae2d59ab1f35992d0f24b4f01d4d2123f9262 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 20 Mar 2024 13:28:17 +0000 Subject: Add `x: T; y: ^T = x` suggestion to do `&x` --- src/check_expr.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index e9a6f5122..e22d57814 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2248,6 +2248,12 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o 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); + } else if (is_type_pointer(type) && + o->mode == Addressing_Variable && + are_types_identical(type_deref(type), o->type)) { + gbString s = expr_to_string(o->expr); + error_line("\tSuggestion: Did you mean `&%s`\n", s); + gb_string_free(s); } } -- cgit v1.2.3 From ba77a9464c7e912dda52dd512dc98f3c241581ac Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 20 Mar 2024 13:36:14 +0000 Subject: Add suggestion for `x: ^T; y = x` to be `x^` --- src/check_expr.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index e22d57814..8fb2cf36b 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2254,6 +2254,11 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o gbString s = expr_to_string(o->expr); error_line("\tSuggestion: Did you mean `&%s`\n", s); gb_string_free(s); + } else if (is_type_pointer(o->type) && + are_types_identical(type_deref(o->type), type)) { + gbString s = expr_to_string(o->expr); + error_line("\tSuggestion: Did you mean `%s^`\n", s); + gb_string_free(s); } } -- cgit v1.2.3 From 29e5f94c2a0d666eed93a1013f895f3c86d6373f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 21 Mar 2024 11:52:48 +0000 Subject: Add `#no_broadcast` procedure parameter to disallow automatic array programming broadcasting on procedure arguments --- base/runtime/core_builtin.odin | 12 +++---- core/image/common.odin | 36 ++++++++++---------- core/math/big/prime.odin | 2 +- core/odin/ast/ast.odin | 16 +++++---- core/thread/thread.odin | 2 +- src/check_expr.cpp | 76 ++++++++++++++++++++++++++---------------- src/check_type.cpp | 16 ++++++++- src/entity.cpp | 1 + src/parser.cpp | 17 +++++----- src/parser.hpp | 3 +- vendor/raylib/raymath.odin | 4 +-- 11 files changed, 113 insertions(+), 72 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin index 401dcb857..fba2e1328 100644 --- a/base/runtime/core_builtin.odin +++ b/base/runtime/core_builtin.odin @@ -447,12 +447,12 @@ _append_elem :: #force_inline proc(array: ^$T/[dynamic]$E, arg: E, should_zero: } @builtin -append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { +append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { return _append_elem(array, arg, true, loc=loc) } @builtin -non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { +non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { return _append_elem(array, arg, false, loc=loc) } @@ -496,12 +496,12 @@ _append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, l } @builtin -append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { +append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { return _append_elems(array, true, loc, ..args) } @builtin -non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { +non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { return _append_elems(array, false, loc, ..args) } @@ -556,7 +556,7 @@ append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: i @builtin -inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { +inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { if array == nil { return } @@ -574,7 +574,7 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle } @builtin -inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { +inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { if array == nil { return } diff --git a/core/image/common.odin b/core/image/common.odin index c7507a85f..b576a9521 100644 --- a/core/image/common.odin +++ b/core/image/common.odin @@ -651,7 +651,7 @@ alpha_add_if_missing :: proc(img: ^Image, alpha_key := Alpha_Key{}, allocator := // We have keyed alpha. o: GA_Pixel for p in inp { - if p == key.r { + if p.r == key.r { o = GA_Pixel{0, key.g} } else { o = GA_Pixel{p.r, 255} @@ -710,7 +710,7 @@ alpha_add_if_missing :: proc(img: ^Image, alpha_key := Alpha_Key{}, allocator := // We have keyed alpha. o: GA_Pixel_16 for p in inp { - if p == key.r { + if p.r == key.r { o = GA_Pixel_16{0, key.g} } else { o = GA_Pixel_16{p.r, 65535} @@ -842,11 +842,11 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al bg := G_Pixel{} if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok { // Background is RGB 16-bit, take just the red channel's topmost byte. - bg = u8(temp_bg.r >> 8) + bg.r = u8(temp_bg.r >> 8) } for p in inp { - out[0] = bg if p == key else p + out[0] = bg if p.r == key else p out = out[1:] } @@ -865,8 +865,8 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al for p in inp { a := f32(p.g) / 255.0 c := ((1.0 - a) * bg + a * f32(p.r)) - out[0] = u8(c) - out = out[1:] + out[0].r = u8(c) + out = out[1:] } } else if .alpha_premultiply in options { @@ -874,14 +874,14 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al for p in inp { a := f32(p.g) / 255.0 c := f32(p.r) * a - out[0] = u8(c) - out = out[1:] + out[0].r = u8(c) + out = out[1:] } } else { // Just drop alpha on the floor. for p in inp { - out[0] = p.r - out = out[1:] + out[0].r = p.r + out = out[1:] } } @@ -951,11 +951,11 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al bg := G_Pixel_16{} if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok { // Background is RGB 16-bit, take just the red channel. - bg = temp_bg.r + bg.r = temp_bg.r } for p in inp { - out[0] = bg if p == key else p + out[0] = bg if p.r == key else p out = out[1:] } @@ -974,8 +974,8 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al for p in inp { a := f32(p.g) / 65535.0 c := ((1.0 - a) * bg + a * f32(p.r)) - out[0] = u16(c) - out = out[1:] + out[0].r = u16(c) + out = out[1:] } } else if .alpha_premultiply in options { @@ -983,14 +983,14 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al for p in inp { a := f32(p.g) / 65535.0 c := f32(p.r) * a - out[0] = u16(c) - out = out[1:] + out[0].r = u16(c) + out = out[1:] } } else { // Just drop alpha on the floor. for p in inp { - out[0] = p.r - out = out[1:] + out[0].r = p.r + out = out[1:] } } diff --git a/core/math/big/prime.odin b/core/math/big/prime.odin index cb0b08dbb..b02b7cb4e 100644 --- a/core/math/big/prime.odin +++ b/core/math/big/prime.odin @@ -1112,7 +1112,7 @@ internal_int_prime_next_prime :: proc(a: ^Int, trials: int, bbs_style: bool, all Generate the restable. */ for x := 1; x < _PRIME_TAB_SIZE; x += 1 { - res_tab = internal_mod(a, _private_prime_table[x]) or_return + res_tab = cast(type_of(res_tab))(internal_mod(a, _private_prime_table[x]) or_return) } for { diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index f6bcbab4e..3e215e0f2 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -597,6 +597,7 @@ Field_Flag :: enum { Any_Int, Subtype, By_Ptr, + No_Broadcast, Results, Tags, @@ -616,6 +617,7 @@ field_flag_strings := [Field_Flag]string{ .Any_Int = "#any_int", .Subtype = "#subtype", .By_Ptr = "#by_ptr", + .No_Broadcast ="#no_broadcast", .Results = "results", .Tags = "field tag", @@ -624,12 +626,13 @@ field_flag_strings := [Field_Flag]string{ } field_hash_flag_strings := []struct{key: string, flag: Field_Flag}{ - {"no_alias", .No_Alias}, - {"c_vararg", .C_Vararg}, - {"const", .Const}, - {"any_int", .Any_Int}, - {"subtype", .Subtype}, - {"by_ptr", .By_Ptr}, + {"no_alias", .No_Alias}, + {"c_vararg", .C_Vararg}, + {"const", .Const}, + {"any_int", .Any_Int}, + {"subtype", .Subtype}, + {"by_ptr", .By_Ptr}, + {"no_broadcast", .No_Broadcast}, } @@ -650,6 +653,7 @@ Field_Flags_Signature :: Field_Flags{ .Const, .Any_Int, .By_Ptr, + .No_Broadcast, .Default_Parameters, } diff --git a/core/thread/thread.odin b/core/thread/thread.odin index 1c473bd1d..55f73d106 100644 --- a/core/thread/thread.odin +++ b/core/thread/thread.odin @@ -163,7 +163,7 @@ create_and_start_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_co t := create(thread_proc, priority) t.data = rawptr(fn) t.user_index = 1 - t.user_args = data + t.user_args[0] = data if self_cleanup { t.flags += {.Self_Cleanup} } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8fb2cf36b..51fe3fc8a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -623,7 +623,7 @@ gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type); #define MAXIMUM_TYPE_DISTANCE 10 -gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type) { +gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type, bool allow_array_programming) { if (c == nullptr) { GB_ASSERT(operand->mode == Addressing_Value); GB_ASSERT(is_type_typed(operand->type)); @@ -832,7 +832,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand if (dst->Union.variants.count == 1) { Type *vt = dst->Union.variants[0]; - i64 score = check_distance_between_types(c, operand, vt); + i64 score = check_distance_between_types(c, operand, vt, allow_array_programming); if (score >= 0) { return score+2; } @@ -840,7 +840,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand i64 prev_lowest_score = -1; i64 lowest_score = -1; for (Type *vt : dst->Union.variants) { - i64 score = check_distance_between_types(c, operand, vt); + i64 score = check_distance_between_types(c, operand, vt, allow_array_programming); if (score >= 0) { if (lowest_score < 0) { lowest_score = score; @@ -863,14 +863,14 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand } if (is_type_relative_pointer(dst)) { - i64 score = check_distance_between_types(c, operand, dst->RelativePointer.pointer_type); + i64 score = check_distance_between_types(c, operand, dst->RelativePointer.pointer_type, allow_array_programming); if (score >= 0) { return score+2; } } if (is_type_relative_multi_pointer(dst)) { - i64 score = check_distance_between_types(c, operand, dst->RelativeMultiPointer.pointer_type); + i64 score = check_distance_between_types(c, operand, dst->RelativeMultiPointer.pointer_type, allow_array_programming); if (score >= 0) { return score+2; } @@ -896,19 +896,21 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand } } - if (is_type_array(dst)) { - Type *elem = base_array_type(dst); - i64 distance = check_distance_between_types(c, operand, elem); - if (distance >= 0) { - return distance + 6; + if (allow_array_programming) { + if (is_type_array(dst)) { + Type *elem = base_array_type(dst); + i64 distance = check_distance_between_types(c, operand, elem, allow_array_programming); + if (distance >= 0) { + return distance + 6; + } } - } - if (is_type_simd_vector(dst)) { - Type *dst_elem = base_array_type(dst); - i64 distance = check_distance_between_types(c, operand, dst_elem); - if (distance >= 0) { - return distance + 6; + if (is_type_simd_vector(dst)) { + Type *dst_elem = base_array_type(dst); + i64 distance = check_distance_between_types(c, operand, dst_elem, allow_array_programming); + if (distance >= 0) { + return distance + 6; + } } } @@ -918,7 +920,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand } if (dst->Matrix.row_count == dst->Matrix.column_count) { Type *dst_elem = base_array_type(dst); - i64 distance = check_distance_between_types(c, operand, dst_elem); + i64 distance = check_distance_between_types(c, operand, dst_elem, allow_array_programming); if (distance >= 0) { return distance + 7; } @@ -966,9 +968,9 @@ gb_internal i64 assign_score_function(i64 distance, bool is_variadic=false) { } -gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false) { +gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false, bool allow_array_programming=true) { i64 score = 0; - i64 distance = check_distance_between_types(c, operand, type); + i64 distance = check_distance_between_types(c, operand, type, allow_array_programming); bool ok = distance >= 0; if (ok) { score = assign_score_function(distance, is_variadic); @@ -978,9 +980,9 @@ gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *o } -gb_internal bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type) { +gb_internal bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type, bool allow_array_programming=true) { i64 score = 0; - return check_is_assignable_to_with_score(c, operand, type, &score); + return check_is_assignable_to_with_score(c, operand, type, &score, /*is_variadic*/false, allow_array_programming); } gb_internal bool internal_check_is_assignable_to(Type *src, Type *dst) { @@ -3142,6 +3144,14 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type return true; } + + if (is_type_array(dst)) { + Type *elem = base_array_type(dst); + if (check_is_castable_to(c, operand, elem)) { + return true; + } + } + if (is_type_simd_vector(src) && is_type_simd_vector(dst)) { if (src->SimdVector.count != dst->SimdVector.count) { return false; @@ -5853,15 +5863,20 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A } } - auto eval_param_and_score = [](CheckerContext *c, Operand *o, Type *param_type, CallArgumentError &err, bool param_is_variadic, Entity *e, bool show_error) -> i64 { + auto eval_param_and_score = [](CheckerContext *c, Operand *o, Type *param_type, CallArgumentError &err, bool param_is_variadic, Entity *e, bool show_error, bool allow_array_programming) -> i64 { i64 s = 0; - if (!check_is_assignable_to_with_score(c, o, param_type, &s, param_is_variadic)) { + if (!check_is_assignable_to_with_score(c, o, param_type, &s, param_is_variadic, allow_array_programming)) { bool ok = false; - if (e && e->flags & EntityFlag_AnyInt) { + if (e && (e->flags & EntityFlag_AnyInt)) { if (is_type_integer(param_type)) { ok = check_is_castable_to(c, o, param_type); } } + if (!allow_array_programming && check_is_assignable_to_with_score(c, o, param_type, nullptr, param_is_variadic, !allow_array_programming)) { + if (show_error) { + error(o->expr, "'#no_broadcast' disallows automatic broadcasting a value across all elements of an array-like type in a procedure argument"); + } + } if (ok) { s = assign_score_function(MAXIMUM_TYPE_DISTANCE); } else { @@ -5878,7 +5893,6 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A } err = CallArgumentError_WrongTypes; } - } else if (show_error) { check_assignment(c, o, param_type, str_lit("procedure argument")); } @@ -5963,12 +5977,14 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A if (param_is_variadic) { continue; } - score += eval_param_and_score(c, o, e->type, err, param_is_variadic, e, show_error); + bool allow_array_programming = !(e && (e->flags & EntityFlag_NoBroadcast)); + score += eval_param_and_score(c, o, e->type, err, param_is_variadic, e, show_error, allow_array_programming); } } if (variadic) { - Type *slice = pt->params->Tuple.variables[pt->variadic_index]->type; + Entity *var_entity = pt->params->Tuple.variables[pt->variadic_index]; + Type *slice = var_entity->type; GB_ASSERT(is_type_slice(slice)); Type *elem = base_type(slice)->Slice.elem; Type *t = elem; @@ -5994,7 +6010,8 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A return CallArgumentError_MultipleVariadicExpand; } } - score += eval_param_and_score(c, o, t, err, true, nullptr, show_error); + bool allow_array_programming = !(var_entity && (var_entity->flags & EntityFlag_NoBroadcast)); + score += eval_param_and_score(c, o, t, err, true, nullptr, show_error, allow_array_programming); } } @@ -11148,6 +11165,9 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan if (f->flags&FieldFlag_any_int) { str = gb_string_appendc(str, "#any_int "); } + if (f->flags&FieldFlag_no_broadcast) { + str = gb_string_appendc(str, "#no_broadcast "); + } if (f->flags&FieldFlag_const) { str = gb_string_appendc(str, "#const "); } diff --git a/src/check_type.cpp b/src/check_type.cpp index 3fe633892..96885bd27 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1869,6 +1869,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para error(name, "'#any_int' can only be applied to variable fields"); p->flags &= ~FieldFlag_any_int; } + if (p->flags&FieldFlag_no_broadcast) { + error(name, "'#no_broadcast' can only be applied to variable fields"); + p->flags &= ~FieldFlag_no_broadcast; + } if (p->flags&FieldFlag_by_ptr) { error(name, "'#by_ptr' can only be applied to variable fields"); p->flags &= ~FieldFlag_by_ptr; @@ -1926,7 +1930,13 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para } } } - if (type != t_invalid && !check_is_assignable_to(ctx, &op, type)) { + + bool allow_array_programming = true; + if (p->flags&FieldFlag_no_broadcast) { + allow_array_programming = false; + } + + if (type != t_invalid && !check_is_assignable_to(ctx, &op, type, allow_array_programming)) { bool ok = true; if (p->flags&FieldFlag_any_int) { if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) { @@ -2002,6 +2012,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para if (p->flags&FieldFlag_no_alias) { param->flags |= EntityFlag_NoAlias; } + if (p->flags&FieldFlag_no_broadcast) { + param->flags |= EntityFlag_NoBroadcast; + } + if (p->flags&FieldFlag_any_int) { if (!is_type_integer(param->type) && !is_type_enum(param->type)) { gbString str = type_to_string(param->type); diff --git a/src/entity.cpp b/src/entity.cpp index 916c2b2bd..9161ea733 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -61,6 +61,7 @@ enum EntityFlag : u64 { EntityFlag_CVarArg = 1ull<<22, + EntityFlag_NoBroadcast = 1ull<<23, EntityFlag_AnyInt = 1ull<<24, EntityFlag_Disabled = 1ull<<25, diff --git a/src/parser.cpp b/src/parser.cpp index 6a7be8a7c..b6b62461f 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3898,14 +3898,15 @@ struct ParseFieldPrefixMapping { FieldFlag flag; }; -gb_global ParseFieldPrefixMapping parse_field_prefix_mappings[] = { - {str_lit("using"), Token_using, FieldFlag_using}, - {str_lit("no_alias"), Token_Hash, FieldFlag_no_alias}, - {str_lit("c_vararg"), Token_Hash, FieldFlag_c_vararg}, - {str_lit("const"), Token_Hash, FieldFlag_const}, - {str_lit("any_int"), Token_Hash, FieldFlag_any_int}, - {str_lit("subtype"), Token_Hash, FieldFlag_subtype}, - {str_lit("by_ptr"), Token_Hash, FieldFlag_by_ptr}, +gb_global ParseFieldPrefixMapping const parse_field_prefix_mappings[] = { + {str_lit("using"), Token_using, FieldFlag_using}, + {str_lit("no_alias"), Token_Hash, FieldFlag_no_alias}, + {str_lit("c_vararg"), Token_Hash, FieldFlag_c_vararg}, + {str_lit("const"), Token_Hash, FieldFlag_const}, + {str_lit("any_int"), Token_Hash, FieldFlag_any_int}, + {str_lit("subtype"), Token_Hash, FieldFlag_subtype}, + {str_lit("by_ptr"), Token_Hash, FieldFlag_by_ptr}, + {str_lit("no_broadcast"), Token_Hash, FieldFlag_no_broadcast}, }; diff --git a/src/parser.hpp b/src/parser.hpp index ff3c5eb34..5820275c8 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -326,6 +326,7 @@ enum FieldFlag : u32 { FieldFlag_any_int = 1<<6, FieldFlag_subtype = 1<<7, FieldFlag_by_ptr = 1<<8, + FieldFlag_no_broadcast = 1<<9, // disallow array programming // Internal use by the parser only FieldFlag_Tags = 1<<10, @@ -336,7 +337,7 @@ enum FieldFlag : u32 { FieldFlag_Invalid = 1u<<31, // Parameter List Restrictions - FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr, + FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr|FieldFlag_no_broadcast, FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags, }; diff --git a/vendor/raylib/raymath.odin b/vendor/raylib/raymath.odin index beeda7989..9682ffe4f 100644 --- a/vendor/raylib/raymath.odin +++ b/vendor/raylib/raymath.odin @@ -159,7 +159,7 @@ Vector2Transform :: proc "c" (v: Vector2, m: Matrix) -> Vector2 { // Calculate linear interpolation between two vectors @(require_results, deprecated="Prefer = linalg.lerp(v1, v2, amount)") Vector2Lerp :: proc "c" (v1, v2: Vector2, amount: f32) -> Vector2 { - return linalg.lerp(v1, v2, amount) + return linalg.lerp(v1, v2, Vector2(amount)) } // Calculate reflected vector to normal @(require_results, deprecated="Prefer = linalg.reflect(v, normal)") @@ -405,7 +405,7 @@ Vector3Transform :: proc "c" (v: Vector3, m: Matrix) -> Vector3 { // Calculate linear interpolation between two vectors @(require_results, deprecated="Prefer = linalg.lerp(v1, v2, amount)") Vector3Lerp :: proc "c" (v1, v2: Vector3, amount: f32) -> Vector3 { - return linalg.lerp(v1, v2, amount) + return linalg.lerp(v1, v2, Vector3(amount)) } // Calculate reflected vector to normal @(require_results, deprecated="Prefer = linalg.reflect(v, normal)") -- cgit v1.2.3 From 010ffc486cb22b21136fdfa4f5ad04532767fa70 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 21 Mar 2024 11:54:40 +0000 Subject: Minor clean up of `#no_broadcast` handling --- src/check_expr.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 51fe3fc8a..1f060506a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5863,7 +5863,8 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A } } - auto eval_param_and_score = [](CheckerContext *c, Operand *o, Type *param_type, CallArgumentError &err, bool param_is_variadic, Entity *e, bool show_error, bool allow_array_programming) -> i64 { + auto eval_param_and_score = [](CheckerContext *c, Operand *o, Type *param_type, CallArgumentError &err, bool param_is_variadic, Entity *e, bool show_error) -> i64 { + bool allow_array_programming = !(e && (e->flags & EntityFlag_NoBroadcast)); i64 s = 0; if (!check_is_assignable_to_with_score(c, o, param_type, &s, param_is_variadic, allow_array_programming)) { bool ok = false; @@ -5977,8 +5978,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A if (param_is_variadic) { continue; } - bool allow_array_programming = !(e && (e->flags & EntityFlag_NoBroadcast)); - score += eval_param_and_score(c, o, e->type, err, param_is_variadic, e, show_error, allow_array_programming); + score += eval_param_and_score(c, o, e->type, err, false, e, show_error); } } @@ -6010,8 +6010,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A return CallArgumentError_MultipleVariadicExpand; } } - bool allow_array_programming = !(var_entity && (var_entity->flags & EntityFlag_NoBroadcast)); - score += eval_param_and_score(c, o, t, err, true, nullptr, show_error, allow_array_programming); + score += eval_param_and_score(c, o, t, err, true, var_entity, show_error); } } -- cgit v1.2.3 From 6b6f72e7bd3030c53d7a59dacdcb28146340188d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 21 Mar 2024 14:59:47 +0000 Subject: Fix #3305 --- src/check_expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 1f060506a..97ceea600 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1599,7 +1599,7 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam for (CIdentSuggestion const &suggestion : c_ident_suggestions) { if (name == suggestion.name) { - error_line("\tSuggestion: Did you mean %s\n", LIT(suggestion.msg)); + error_line("\tSuggestion: Did you mean %.*s\n", LIT(suggestion.msg)); } } } -- cgit v1.2.3 From d47ba09743446f787d5b427e52d189cc91d47529 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 21 Mar 2024 20:43:52 +0000 Subject: Fix at error printing bug --- src/check_expr.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 97ceea600..bb573e1e4 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6138,7 +6138,10 @@ gb_internal bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Sco } } - if (call_expr) error(call_expr, "at caller location"); + if (call_expr) { + TokenPos pos = ast_token(call_expr).pos; + error_line("%s at caller location\n", token_pos_to_string(pos)); + } } return false; } -- cgit v1.2.3 From 7abb459861f5501b55893d1d2cf7590268f1dfb2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Mar 2024 12:56:04 +0000 Subject: Add missing field flags in debug printing --- src/check_expr.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index bb573e1e4..d1f393bef 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -11236,6 +11236,18 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan if (field->flags&FieldFlag_c_vararg) { str = gb_string_appendc(str, "#c_vararg "); } + if (field->flags&FieldFlag_any_int) { + str = gb_string_appendc(str, "#any_int "); + } + if (field->flags&FieldFlag_no_broadcast) { + str = gb_string_appendc(str, "#no_broadcast "); + } + if (field->flags&FieldFlag_const) { + str = gb_string_appendc(str, "#const "); + } + if (field->flags&FieldFlag_subtype) { + str = gb_string_appendc(str, "#subtype "); + } str = write_expr_to_string(str, field->type, shorthand); } -- cgit v1.2.3 From 1d46adb598328b4977d5b4cfad2a1dc679b05a21 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Mar 2024 16:51:04 +0000 Subject: Treat `*x` as an unary operator to improve error messages for common C-programmer mistakes --- src/check_expr.cpp | 13 +++++++++++++ src/check_type.cpp | 16 +++++++++++++++- src/parser.cpp | 7 ++++++- 3 files changed, 34 insertions(+), 2 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index d1f393bef..f09c8fe3c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1800,6 +1800,19 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) { } break; + case Token_Mul: + { + ERROR_BLOCK(); + error(op, "Operator '%.*s' is not a valid unary operator in Odin", LIT(op.string)); + if (is_type_pointer(o->type)) { + str = expr_to_string(o->expr); + error_line("\tSuggestion: Did you mean '%s^'?\n", str); + } else if (is_type_multi_pointer(o->type)) { + str = expr_to_string(o->expr); + error_line("\tSuggestion: The value is a multi-pointer, did you mean '%s[0]'?\n", str); + } + } + break; default: error(op, "Unknown operator '%.*s'", LIT(op.string)); return false; diff --git a/src/check_type.cpp b/src/check_type.cpp index 38bfa56ef..0b9042905 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -3375,7 +3375,9 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) type = t_invalid; - // NOTE(bill): Check for common mistakes from C programmers e.g. T[] and T[N] + // NOTE(bill): Check for common mistakes from C programmers + // e.g. T[] and T[N] + // e.g. *T Ast *node = unparen_expr(e); if (node && node->kind == Ast_IndexExpr) { gbString index_str = nullptr; @@ -3395,6 +3397,18 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) 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 if (node && node->kind == Ast_UnaryExpr && node->UnaryExpr.op.kind == Token_Mul) { + gbString type_str = expr_to_string(node->UnaryExpr.expr); + defer (gb_string_free(type_str)); + + error_line("\tSuggestion: Did you mean '^%s'?\n", type_str); + end_error_block(); + + // NOTE(bill): Minimize error propagation of bad array syntax by treating this like a type + if (node->UnaryExpr.expr != nullptr) { + Ast *pseudo_pointer_expr = ast_pointer_type(e->file(), ast_token(node->UnaryExpr.expr), node->UnaryExpr.expr); + return check_type_expr(ctx, pseudo_pointer_expr, named_type); + } } else { end_error_block(); } diff --git a/src/parser.cpp b/src/parser.cpp index b6b62461f..bb9a526fe 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2541,6 +2541,9 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { return ast_pointer_type(f, token, elem); } break; + case Token_Mul: + return parse_unary_expr(f, true); + case Token_OpenBracket: { Token token = expect_token(f, Token_OpenBracket); Ast *count_expr = nullptr; @@ -3274,7 +3277,9 @@ gb_internal Ast *parse_unary_expr(AstFile *f, bool lhs) { case Token_Sub: case Token_Xor: case Token_And: - case Token_Not: { + case Token_Not: + case Token_Mul: // Used for error handling when people do C-like things + { Token token = advance_token(f); Ast *expr = parse_unary_expr(f, lhs); return ast_unary_expr(f, token, expr); -- cgit v1.2.3 From eb51cc6835a202d0a5fde297d87277041fc433e7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Mar 2024 16:53:40 +0000 Subject: Improve error propagation for `(*x).y` style errors from C-programmers --- src/check_expr.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index f09c8fe3c..ab8fe78fc 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1807,9 +1807,11 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) { if (is_type_pointer(o->type)) { str = expr_to_string(o->expr); error_line("\tSuggestion: Did you mean '%s^'?\n", str); + o->type = type_deref(o->type); } else if (is_type_multi_pointer(o->type)) { str = expr_to_string(o->expr); error_line("\tSuggestion: The value is a multi-pointer, did you mean '%s[0]'?\n", str); + o->type = type_deref(o->type, true); } } break; -- cgit v1.2.3 From efb0933965669a5b4b8c5534d4f77fc29ddab732 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Mar 2024 16:57:35 +0000 Subject: Improve error suggestion and propagation for `x->y` --- src/check_expr.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ab8fe78fc..80008d73a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4900,10 +4900,18 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod Selection sel = {}; // NOTE(bill): Not used if it's an import name if (!c->allow_arrow_right_selector_expr && se->token.kind == Token_ArrowRight) { + ERROR_BLOCK(); error(node, "Illegal use of -> selector shorthand outside of a call"); - operand->mode = Addressing_Invalid; - operand->expr = node; - return nullptr; + gbString x = expr_to_string(se->expr); + gbString y = expr_to_string(se->selector); + error_line("\tSuggestion: Did you mean '%s.%s'?\n", x, y); + gb_string_free(y); + gb_string_free(x); + + // TODO(bill): Should this terminate here or propagate onwards? + // operand->mode = Addressing_Invalid; + // operand->expr = node; + // return nullptr; } operand->expr = node; -- cgit v1.2.3 From 517d7ae0b0fd400ceb6a213e7d644c19b8088bfd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Mar 2024 17:51:56 +0000 Subject: Add error block around `error_line` calls --- src/check_builtin.cpp | 3 +++ src/check_decl.cpp | 1 + src/check_expr.cpp | 5 +++++ src/check_stmt.cpp | 5 +++++ src/checker.cpp | 12 +++++++++++- src/parser.cpp | 6 +++--- 6 files changed, 28 insertions(+), 4 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index d3158961e..53e4acbd1 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -89,6 +89,7 @@ gb_internal void check_or_else_split_types(CheckerContext *c, Operand *x, String gb_internal void check_or_else_expr_no_value_error(CheckerContext *c, String const &name, Operand const &x, Type *type_hint) { + ERROR_BLOCK(); gbString t = type_to_string(x.type); error(x.expr, "'%.*s' does not return a value, value is of type %s", LIT(name), t); if (is_type_union(type_deref(x.type))) { @@ -1565,6 +1566,7 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o } if (!operand->value.value_bool) { + ERROR_BLOCK(); gbString arg1 = expr_to_string(ce->args[0]); gbString arg2 = {}; @@ -1590,6 +1592,7 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o operand->type = t_untyped_bool; operand->mode = Addressing_Constant; } else if (name == "panic") { + ERROR_BLOCK(); if (ce->args.count != 1) { error(call, "'#panic' expects 1 argument, got %td", ce->args.count); return false; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 2c0f7a7b8..952a877a4 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1630,6 +1630,7 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de Entity *uvar = entry.uvar; Entity *prev = scope_insert_no_mutex(ctx->scope, uvar); if (prev != nullptr) { + ERROR_BLOCK(); error(e->token, "Namespace collision while 'using' procedure argument '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string)); error_line("%.*s != %.*s\n", LIT(uvar->token.string), LIT(prev->token.string)); break; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 80008d73a..ecc8a804c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5905,6 +5905,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A s = assign_score_function(MAXIMUM_TYPE_DISTANCE); } else { if (show_error) { + ERROR_BLOCK(); check_assignment(c, o, param_type, str_lit("procedure argument")); Type *src = base_type(o->type); @@ -8459,6 +8460,7 @@ gb_internal ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *no // NOTE(bill): allow implicit conversion between boolean types // within 'or_return' to improve the experience using third-party code } else if (!check_is_assignable_to(c, &rhs, end_type)) { + ERROR_BLOCK(); // TODO(bill): better error message gbString a = type_to_string(right_type); gbString b = type_to_string(end_type); @@ -10030,6 +10032,7 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, bool ok = check_index_value(c, t, false, ie->index, max_count, &index, index_type_hint); if (is_const) { if (index < 0) { + ERROR_BLOCK(); gbString str = expr_to_string(o->expr); error(o->expr, "Cannot index a constant '%s'", str); if (!build_context.terse_errors) { @@ -10046,6 +10049,7 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, bool finish = false; o->value = get_constant_field_single(c, value, cast(i32)index, &success, &finish); if (!success) { + ERROR_BLOCK(); gbString str = expr_to_string(o->expr); error(o->expr, "Cannot index a constant '%s' with index %lld", str, cast(long long)index); if (!build_context.terse_errors) { @@ -10236,6 +10240,7 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, } } if (!all_constant) { + ERROR_BLOCK(); gbString str = expr_to_string(o->expr); error(o->expr, "Cannot slice '%s' with non-constant indices", str); if (!build_context.terse_errors) { diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index d34695a3a..1d7e7d4e9 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -883,6 +883,7 @@ gb_internal void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod } if (ctx->inline_for_depth >= MAX_INLINE_FOR_DEPTH && prev_inline_for_depth < MAX_INLINE_FOR_DEPTH) { + ERROR_BLOCK(); if (prev_inline_for_depth > 0) { error(node, "Nested '#unroll for' loop cannot be inlined as it exceeds the maximum '#unroll for' depth (%lld levels >= %lld maximum levels)", v, MAX_INLINE_FOR_DEPTH); } else { @@ -1592,6 +1593,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { isize count = t->Tuple.variables.count; if (count < 1 || count > 3) { + ERROR_BLOCK(); check_not_tuple(ctx, &operand); error_line("\tMultiple return valued parameters in a range statement are limited to a maximum of 2 usable values with a trailing boolean for the conditional\n"); break; @@ -2085,6 +2087,9 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) { } return; } + + ERROR_BLOCK(); + gbString expr_str = expr_to_string(operand.expr); error(node, "Expression is not used: '%s'", expr_str); gb_string_free(expr_str); diff --git a/src/checker.cpp b/src/checker.cpp index bf6a84588..6456cab0c 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3180,6 +3180,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { linkage == "link_once") { ac->linkage = linkage; } else { + ERROR_BLOCK(); error(elem, "Invalid linkage '%.*s'. Valid kinds:", LIT(linkage)); error_line("\tinternal\n"); error_line("\tstrong\n"); @@ -3428,6 +3429,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { } else if (mode == "speed") { ac->optimization_mode = ProcedureOptimizationMode_Speed; } else { + ERROR_BLOCK(); error(elem, "Invalid optimization_mode for '%.*s'. Valid modes:", LIT(name)); error_line("\tnone\n"); error_line("\tminimal\n"); @@ -3558,6 +3560,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { model == "localexec") { ac->thread_local_model = model; } else { + ERROR_BLOCK(); error(elem, "Invalid thread local model '%.*s'. Valid models:", LIT(model)); error_line("\tdefault\n"); error_line("\tlocaldynamic\n"); @@ -3608,6 +3611,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { linkage == "link_once") { ac->linkage = linkage; } else { + ERROR_BLOCK(); error(elem, "Invalid linkage '%.*s'. Valid kinds:", LIT(linkage)); error_line("\tinternal\n"); error_line("\tstrong\n"); @@ -3762,6 +3766,7 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array const &at if (!proc(c, elem, name, value, ac)) { if (!build_context.ignore_unknown_attributes) { + ERROR_BLOCK(); error(elem, "Unknown attribute element name '%.*s'", LIT(name)); error_line("\tDid you forget to use build flag '-ignore-unknown-attributes'?\n"); } @@ -3831,6 +3836,8 @@ gb_internal bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_ gb_string_free(str); return false; } else if (is_global) { + ERROR_BLOCK(); + Ast *n = vd->values[rhs-1]; error(n, "Expected %td expressions on the right hand side, got %td", lhs, rhs); error_line("Note: Global declarations do not allow for multi-valued expressions"); @@ -6052,11 +6059,14 @@ gb_internal void check_unique_package_names(Checker *c) { continue; } + + begin_error_block(); error(curr, "Duplicate declaration of 'package %.*s'", LIT(name)); error_line("\tA package name must be unique\n" "\tThere is no relation between a package name and the directory that contains it, so they can be completely different\n" "\tA package name is required for link name prefixing to have a consistent ABI\n"); - error(prev, "found at previous location"); + error_line("%s found at previous location\n", token_pos_to_string(ast_token(prev).pos)); + end_error_block(); } } diff --git a/src/parser.cpp b/src/parser.cpp index bb9a526fe..b4a2e060c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -6295,7 +6295,7 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) { if (!path_is_directory(init_fullpath)) { String const ext = str_lit(".odin"); if (!string_ends_with(init_fullpath, ext)) { - error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename)); + error({}, "Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename)); return ParseFile_WrongExtension; } } else if (init_fullpath.len != 0) { @@ -6308,7 +6308,7 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) { String short_path = filename_from_path(path); char *cpath = alloc_cstring(temporary_allocator(), short_path); if (gb_file_exists(cpath)) { - error_line("Please specify the executable name with -out: as a directory exists with the same name in the current working directory"); + error({}, "Please specify the executable name with -out: as a directory exists with the same name in the current working directory"); return ParseFile_DirectoryAlreadyExists; } } @@ -6344,7 +6344,7 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) { if (!path_is_directory(fullpath)) { String const ext = str_lit(".odin"); if (!string_ends_with(fullpath, ext)) { - error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(fullpath)); + error({}, "Expected either a directory or a .odin file, got '%.*s'\n", LIT(fullpath)); return ParseFile_WrongExtension; } } -- cgit v1.2.3 From 6da6393735dd18d68b1f4662a157f6044f87b470 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 24 Mar 2024 13:40:10 +0000 Subject: Fix #3320 --- src/check_expr.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ecc8a804c..bd2c576cf 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1174,6 +1174,16 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ type_str, type_extra, LIT(context_name)); check_assignment_error_suggestion(c, operand, type); + + if (context_name == "procedure argument") { + Type *src = base_type(operand->type); + Type *dst = base_type(type); + if (is_type_slice(src) && are_types_identical(src->Slice.elem, dst)) { + gbString a = expr_to_string(operand->expr); + error_line("\tSuggestion: Did you mean to pass the slice into the variadic parameter with ..%s?\n\n", a); + gb_string_free(a); + } + } } break; } @@ -5905,16 +5915,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A s = assign_score_function(MAXIMUM_TYPE_DISTANCE); } else { if (show_error) { - ERROR_BLOCK(); check_assignment(c, o, param_type, str_lit("procedure argument")); - - Type *src = base_type(o->type); - Type *dst = base_type(param_type); - if (is_type_slice(src) && are_types_identical(src->Slice.elem, dst)) { - gbString a = expr_to_string(o->expr); - error_line("\tSuggestion: Did you mean to pass the slice into the variadic parameter with ..%s?\n\n", a); - gb_string_free(a); - } } err = CallArgumentError_WrongTypes; } -- cgit v1.2.3 From d92767cb7784fab76663a82eb84db680af6ec6f6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 24 Mar 2024 13:49:51 +0000 Subject: Fix #3226 --- src/check_expr.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index bd2c576cf..fd10374c1 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5830,10 +5830,14 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A Operand *variadic_operand = &ordered_operands[pt->variadic_index]; if (vari_expand) { - GB_ASSERT(variadic_operands.count != 0); - *variadic_operand = variadic_operands[0]; - variadic_operand->type = default_type(variadic_operand->type); - actually_variadic = true; + if (variadic_operands.count == 0) { + error(call, "'..' in the wrong position"); + } else { + GB_ASSERT(variadic_operands.count != 0); + *variadic_operand = variadic_operands[0]; + variadic_operand->type = default_type(variadic_operand->type); + actually_variadic = true; + } } else { AstFile *f = call->file(); -- cgit v1.2.3 From 6d4f30de1a85fe51159808d70a342c1c915d15de Mon Sep 17 00:00:00 2001 From: rick-masters Date: Sun, 24 Mar 2024 16:28:55 +0000 Subject: Fix fields_wait_signal futex. --- src/check_builtin.cpp | 2 ++ src/check_expr.cpp | 1 + src/check_type.cpp | 4 ++++ src/threading.cpp | 2 +- 4 files changed, 8 insertions(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 53e4acbd1..f4aa9567d 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -3393,6 +3393,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As elem->Struct.tags = gb_alloc_array(permanent_allocator(), String, fields.count); elem->Struct.node = dummy_node_struct; type_set_offsets(elem); + wait_signal_set(&elem->Struct.fields_wait_signal); } Type *soa_type = make_soa_struct_slice(c, dummy_node_soa, nullptr, elem); @@ -3766,6 +3767,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As soa_struct->Struct.tags[i] = old_struct->Struct.tags[i]; } } + wait_signal_set(&soa_struct->Struct.fields_wait_signal); Token token = {}; token.string = str_lit("Base_Type"); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index fd10374c1..d19af4a62 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8873,6 +8873,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * break; } + wait_signal_until_available(&t->Struct.fields_wait_signal); isize field_count = t->Struct.fields.count; isize min_field_count = t->Struct.fields.count; for (isize i = min_field_count-1; i >= 0; i--) { diff --git a/src/check_type.cpp b/src/check_type.cpp index 0b9042905..ae79d4edc 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2490,6 +2490,7 @@ gb_internal Type *get_map_cell_type(Type *type) { s->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("v"), alloc_type_array(type, len), false, 0, EntityState_Resolved); s->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("_"), alloc_type_array(t_u8, padding), false, 1, EntityState_Resolved); s->Struct.scope = scope; + wait_signal_set(&s->Struct.fields_wait_signal); gb_unused(type_size_of(s)); return s; @@ -2520,6 +2521,7 @@ gb_internal void init_map_internal_types(Type *type) { metadata_type->Struct.fields[4] = alloc_entity_field(metadata_scope, make_token_ident("value_cell"), value_cell, false, 4, EntityState_Resolved); metadata_type->Struct.scope = metadata_scope; metadata_type->Struct.node = nullptr; + wait_signal_set(&metadata_type->Struct.fields_wait_signal); gb_unused(type_size_of(metadata_type)); @@ -2537,6 +2539,7 @@ gb_internal void init_map_internal_types(Type *type) { debug_type->Struct.fields[3] = alloc_entity_field(scope, make_token_ident("__metadata"), metadata_type, false, 3, EntityState_Resolved); debug_type->Struct.scope = scope; debug_type->Struct.node = nullptr; + wait_signal_set(&debug_type->Struct.fields_wait_signal); gb_unused(type_size_of(debug_type)); @@ -2832,6 +2835,7 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e add_entity(ctx, scope, nullptr, base_type_entity); add_type_info_type(ctx, soa_struct); + wait_signal_set(&soa_struct->Struct.fields_wait_signal); return soa_struct; } diff --git a/src/threading.cpp b/src/threading.cpp index a469435d2..9e4a1607c 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -113,7 +113,7 @@ struct Wait_Signal { gb_internal void wait_signal_until_available(Wait_Signal *ws) { if (ws->futex.load() == 0) { - futex_wait(&ws->futex, 1); + futex_wait(&ws->futex, 0); } } -- cgit v1.2.3 From e5629dafd0be1be42a3bd183a09ff82492b6b386 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 25 Mar 2024 13:23:43 +0000 Subject: Potentially fix a race condition with parapoly types (related to #3328) --- src/check_expr.cpp | 11 +++- src/check_type.cpp | 179 +++++++++++++++++++++++++++-------------------------- src/checker.hpp | 9 ++- src/threading.cpp | 2 - 4 files changed, 104 insertions(+), 97 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index fd10374c1..44d65e376 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -79,7 +79,6 @@ gb_internal Type * check_type_expr (CheckerContext *c, Ast *exp gb_internal Type * make_optional_ok_type (Type *value, bool typed=true); gb_internal Entity * check_selector (CheckerContext *c, Operand *operand, Ast *node, Type *type_hint); gb_internal Entity * check_ident (CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name); -gb_internal Entity * find_polymorphic_record_entity (CheckerContext *c, Type *original_type, isize param_count, Array const &ordered_operands, bool *failure); gb_internal void check_not_tuple (CheckerContext *c, Operand *operand); gb_internal void convert_to_typed (CheckerContext *c, Operand *operand, Type *target_type); gb_internal gbString expr_to_string (Ast *expression); @@ -121,6 +120,8 @@ gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize * gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr); +gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types, isize param_count, Array const &ordered_operands); + enum LoadDirectiveResult { LoadDirective_Success = 0, LoadDirective_Error = 1, @@ -7171,8 +7172,12 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O } { - bool failure = false; - Entity *found_entity = find_polymorphic_record_entity(c, original_type, param_count, ordered_operands, &failure); + GenTypesData *found_gen_types = ensure_polymorphic_record_entity_has_gen_types(c, original_type); + + mutex_lock(&found_gen_types->mutex); + defer (mutex_unlock(&found_gen_types->mutex)); + Entity *found_entity = find_polymorphic_record_entity(found_gen_types, param_count, ordered_operands); + if (found_entity) { operand->mode = Addressing_Type; operand->type = found_entity->type; diff --git a/src/check_type.cpp b/src/check_type.cpp index 0b9042905..e22d4b62b 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -260,84 +260,21 @@ gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_, } -gb_internal Entity *find_polymorphic_record_entity(CheckerContext *ctx, Type *original_type, isize param_count, Array const &ordered_operands, bool *failure) { - rw_mutex_shared_lock(&ctx->info->gen_types_mutex); // @@global - - auto *found_gen_types = map_get(&ctx->info->gen_types, original_type); - if (found_gen_types == nullptr) { - rw_mutex_shared_unlock(&ctx->info->gen_types_mutex); // @@global - return nullptr; - } - - rw_mutex_shared_lock(&found_gen_types->mutex); // @@local - defer (rw_mutex_shared_unlock(&found_gen_types->mutex)); // @@local - - rw_mutex_shared_unlock(&ctx->info->gen_types_mutex); // @@global - - for (Entity *e : found_gen_types->types) { - Type *t = base_type(e->type); - TypeTuple *tuple = nullptr; - switch (t->kind) { - case Type_Struct: - if (t->Struct.polymorphic_params) { - tuple = &t->Struct.polymorphic_params->Tuple; - } - break; - case Type_Union: - if (t->Union.polymorphic_params) { - tuple = &t->Union.polymorphic_params->Tuple; - } - break; - } - GB_ASSERT_MSG(tuple != nullptr, "%s :: %s", type_to_string(e->type), type_to_string(t)); - GB_ASSERT(param_count == tuple->variables.count); - - bool skip = false; - - for (isize j = 0; j < param_count; j++) { - Entity *p = tuple->variables[j]; - Operand o = {}; - if (j < ordered_operands.count) { - o = ordered_operands[j]; - } - if (o.expr == nullptr) { - continue; - } - Entity *oe = entity_of_node(o.expr); - if (p == oe) { - // NOTE(bill): This is the same type, make sure that it will be be same thing and use that - // Saves on a lot of checking too below - continue; - } - - if (p->kind == Entity_TypeName) { - if (is_type_polymorphic(o.type)) { - // NOTE(bill): Do not add polymorphic version to the gen_types - skip = true; - break; - } - if (!are_types_identical(o.type, p->type)) { - skip = true; - break; - } - } else if (p->kind == Entity_Constant) { - if (!compare_exact_values(Token_CmpEq, o.value, p->Constant.value)) { - skip = true; - break; - } - if (!are_types_identical(o.type, p->type)) { - skip = true; - break; - } - } else { - GB_PANIC("Unknown entity kind"); - } - } - if (!skip) { - return e; - } +gb_internal GenTypesData *ensure_polymorphic_record_entity_has_gen_types(CheckerContext *ctx, Type *original_type) { + mutex_lock(&ctx->info->gen_types_mutex); // @@global + + GenTypesData *found_gen_types = nullptr; + auto *found_gen_types_ptr = map_get(&ctx->info->gen_types, original_type); + if (found_gen_types_ptr == nullptr) { + GenTypesData *gen_types = gb_alloc_item(permanent_allocator(), GenTypesData); + gen_types->types = array_make(heap_allocator()); + map_set(&ctx->info->gen_types, original_type, gen_types); + found_gen_types_ptr = map_get(&ctx->info->gen_types, original_type); } - return nullptr; + found_gen_types = *found_gen_types_ptr; + GB_ASSERT(found_gen_types != nullptr); + mutex_unlock(&ctx->info->gen_types_mutex); // @@global + return found_gen_types; } @@ -367,19 +304,16 @@ gb_internal void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, T // TODO(bill): Is this even correct? Or should the metadata be copied? e->TypeName.objc_metadata = original_type->Named.type_name->TypeName.objc_metadata; - rw_mutex_lock(&ctx->info->gen_types_mutex); - auto *found_gen_types = map_get(&ctx->info->gen_types, original_type); - if (found_gen_types) { - rw_mutex_lock(&found_gen_types->mutex); - array_add(&found_gen_types->types, e); - rw_mutex_unlock(&found_gen_types->mutex); - } else { - GenTypesData gen_types = {}; - gen_types.types = array_make(heap_allocator()); - array_add(&gen_types.types, e); - map_set(&ctx->info->gen_types, original_type, gen_types); + auto *found_gen_types = ensure_polymorphic_record_entity_has_gen_types(ctx, original_type); + mutex_lock(&found_gen_types->mutex); + defer (mutex_unlock(&found_gen_types->mutex)); + + for (Entity *prev : found_gen_types->types) { + if (prev == e) { + return; + } } - rw_mutex_unlock(&ctx->info->gen_types_mutex); + array_add(&found_gen_types->types, e); } @@ -612,6 +546,73 @@ gb_internal bool check_record_poly_operand_specialization(CheckerContext *ctx, T return true; } +gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types, isize param_count, Array const &ordered_operands) { + for (Entity *e : found_gen_types->types) { + Type *t = base_type(e->type); + TypeTuple *tuple = nullptr; + switch (t->kind) { + case Type_Struct: + if (t->Struct.polymorphic_params) { + tuple = &t->Struct.polymorphic_params->Tuple; + } + break; + case Type_Union: + if (t->Union.polymorphic_params) { + tuple = &t->Union.polymorphic_params->Tuple; + } + break; + } + GB_ASSERT_MSG(tuple != nullptr, "%s :: %s", type_to_string(e->type), type_to_string(t)); + GB_ASSERT(param_count == tuple->variables.count); + + bool skip = false; + + for (isize j = 0; j < param_count; j++) { + Entity *p = tuple->variables[j]; + Operand o = {}; + if (j < ordered_operands.count) { + o = ordered_operands[j]; + } + if (o.expr == nullptr) { + continue; + } + Entity *oe = entity_of_node(o.expr); + if (p == oe) { + // NOTE(bill): This is the same type, make sure that it will be be same thing and use that + // Saves on a lot of checking too below + continue; + } + + if (p->kind == Entity_TypeName) { + if (is_type_polymorphic(o.type)) { + // NOTE(bill): Do not add polymorphic version to the gen_types + skip = true; + break; + } + if (!are_types_identical(o.type, p->type)) { + skip = true; + break; + } + } else if (p->kind == Entity_Constant) { + if (!compare_exact_values(Token_CmpEq, o.value, p->Constant.value)) { + skip = true; + break; + } + if (!are_types_identical(o.type, p->type)) { + skip = true; + break; + } + } else { + GB_PANIC("Unknown entity kind"); + } + } + if (!skip) { + return e; + } + } + return nullptr; +}; + gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array *poly_operands, Type *named_type, Type *original_type_for_poly) { GB_ASSERT(is_type_struct(struct_type)); diff --git a/src/checker.hpp b/src/checker.hpp index eea99578e..e0dc54a87 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -360,7 +360,7 @@ struct GenProcsData { struct GenTypesData { Array types; - RwMutex mutex; + RecursiveMutex mutex; }; // CheckerInfo stores all the symbol information for a type-checked program @@ -400,8 +400,8 @@ struct CheckerInfo { RecursiveMutex lazy_mutex; // Mutex required for lazy type checking of specific files - RwMutex gen_types_mutex; - PtrMap gen_types; + BlockingMutex gen_types_mutex; + PtrMap gen_types; BlockingMutex type_info_mutex; // NOT recursive Array type_info_types; @@ -560,3 +560,6 @@ gb_internal void init_core_context(Checker *c); gb_internal void init_mem_allocator(Checker *c); gb_internal void add_untyped_expressions(CheckerInfo *cinfo, UntypedExprInfoMap *untyped); + + +gb_internal GenTypesData *ensure_polymorphic_record_entity_has_gen_types(CheckerContext *ctx, Type *original_type); \ No newline at end of file diff --git a/src/threading.cpp b/src/threading.cpp index a469435d2..d9538f66e 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -122,8 +122,6 @@ gb_internal void wait_signal_set(Wait_Signal *ws) { futex_broadcast(&ws->futex); } - - struct MutexGuard { MutexGuard() = delete; MutexGuard(MutexGuard const &) = delete; -- cgit v1.2.3 From 50618759a65bfbc09e9f7b62878ecefff78ab1be Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 25 Mar 2024 13:44:00 +0000 Subject: Fix error reporting for type cycles --- src/check_expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 0dc6fdb02..a34b425c2 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1738,7 +1738,7 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam if (check_cycle(c, e, true)) { o->type = t_invalid; } - if (o->type != nullptr && type->kind == Type_Named && o->type->Named.type_name->TypeName.is_type_alias) { + if (o->type != nullptr && o->type->kind == Type_Named && o->type->Named.type_name->TypeName.is_type_alias) { o->type = base_type(o->type); } -- cgit v1.2.3 From 1009182f7b35e38e0fba375ad830fc609a7be831 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 26 Mar 2024 14:13:55 +0000 Subject: Fix another #soa race condition bug --- src/check_expr.cpp | 5 ++++ src/check_type.cpp | 60 ++++++++++++++++++++++++++++++----------------- src/checker.cpp | 31 ++++++++++++++++++++++++ src/checker.hpp | 1 + src/llvm_backend_type.cpp | 2 +- src/types.cpp | 2 ++ 6 files changed, 78 insertions(+), 23 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index a34b425c2..3a275729f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -122,6 +122,8 @@ gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr); gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types, isize param_count, Array const &ordered_operands); +gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finish); + enum LoadDirectiveResult { LoadDirective_Success = 0, LoadDirective_Error = 1, @@ -5031,6 +5033,9 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod } } + if (operand->type && is_type_soa_struct(type_deref(operand->type))) { + complete_soa_type(c->checker, type_deref(operand->type), false); + } if (entity == nullptr && selector->kind == Ast_Ident) { String field_name = selector->Ident.token.string; diff --git a/src/check_type.cpp b/src/check_type.cpp index d26d9b660..40a7ec947 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2690,17 +2690,19 @@ struct SoaTypeWorkerData { }; -gb_internal void complete_soa_type(CheckerContext *ctx, Type *t, bool wait_to_finish) { +gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finish) { Type *original_type = t; + gb_unused(original_type); + t = base_type(t); if (t == nullptr || !is_type_soa_struct(t)) { - return; + return true; } MUTEX_GUARD(&t->Struct.soa_mutex); if (t->Struct.fields_wait_signal.futex.load()) { - return; + return true; } isize field_count = 0; @@ -2711,8 +2713,6 @@ gb_internal void complete_soa_type(CheckerContext *ctx, Type *t, bool wait_to_fi case StructSoa_Dynamic: extra_field_count = 3; break; } - - Ast *node = t->Struct.node; Scope *scope = t->Struct.scope; i64 soa_count = t->Struct.soa_count; Type *elem = t->Struct.soa_elem; @@ -2721,18 +2721,26 @@ gb_internal void complete_soa_type(CheckerContext *ctx, Type *t, bool wait_to_fi if (wait_to_finish) { wait_signal_until_available(&old_struct->Struct.fields_wait_signal); + } else { + GB_ASSERT(old_struct->Struct.fields_wait_signal.futex.load()); } - field_count = old_struct->Struct.fields.count; t->Struct.fields = slice_make(permanent_allocator(), field_count+extra_field_count); t->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); - if (soa_count > I32_MAX) { - soa_count = I32_MAX; - error(node, "Array count too large for an #soa struct, got %lld", cast(long long)soa_count); - } - t->Struct.soa_count = cast(i32)soa_count; + + + auto const &add_entity = [](Scope *scope, Entity *entity) { + String name = entity->token.string; + if (!is_blank_ident(name)) { + Entity *ie = scope_insert(scope, entity); + if (ie != nullptr) { + redeclaration_error(name, entity, ie); + } + } + }; + for_array(i, old_struct->Struct.fields) { Entity *old_field = old_struct->Struct.fields[i]; @@ -2746,8 +2754,8 @@ gb_internal void complete_soa_type(CheckerContext *ctx, Type *t, bool wait_to_fi } Entity *new_field = alloc_entity_field(scope, old_field->token, field_type, false, old_field->Variable.field_index); t->Struct.fields[i] = new_field; - add_entity(ctx, scope, nullptr, new_field); - add_entity_use(ctx, nullptr, new_field); + add_entity(scope, new_field); + new_field->flags |= EntityFlag_Used; } else { t->Struct.fields[i] = old_field; } @@ -2758,29 +2766,32 @@ gb_internal void complete_soa_type(CheckerContext *ctx, Type *t, bool wait_to_fi if (t->Struct.soa_kind != StructSoa_Fixed) { Entity *len_field = alloc_entity_field(scope, make_token_ident("__$len"), t_int, false, cast(i32)field_count+0); t->Struct.fields[field_count+0] = len_field; - add_entity(ctx, scope, nullptr, len_field); - add_entity_use(ctx, nullptr, len_field); + add_entity(scope, len_field); + len_field->flags |= EntityFlag_Used; if (t->Struct.soa_kind == StructSoa_Dynamic) { Entity *cap_field = alloc_entity_field(scope, make_token_ident("__$cap"), t_int, false, cast(i32)field_count+1); t->Struct.fields[field_count+1] = cap_field; - add_entity(ctx, scope, nullptr, cap_field); - add_entity_use(ctx, nullptr, cap_field); + add_entity(scope, cap_field); + cap_field->flags |= EntityFlag_Used; - init_mem_allocator(ctx->checker); + init_mem_allocator(checker); Entity *allocator_field = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, cast(i32)field_count+2); t->Struct.fields[field_count+2] = allocator_field; - add_entity(ctx, scope, nullptr, allocator_field); - add_entity_use(ctx, nullptr, allocator_field); + add_entity(scope, allocator_field); + allocator_field->flags |= EntityFlag_Used; } } - add_type_info_type(ctx, original_type); + // add_type_info_type(ctx, original_type); + + wait_signal_set(&t->Struct.fields_wait_signal); + return true; } gb_internal WORKER_TASK_PROC(complete_soa_type_worker) { SoaTypeWorkerData *wd = cast(SoaTypeWorkerData *)data; - complete_soa_type(&wd->ctx, wd->type, wd->wait_to_finish); + complete_soa_type(wd->ctx.checker, wd->type, wd->wait_to_finish); return 0; } @@ -2825,6 +2836,9 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e scope = create_scope(ctx->info, ctx->scope); soa_struct->Struct.scope = scope; + if (elem && elem->kind == Type_Named) { + add_declaration_dependency(ctx, elem->Named.type_name); + } if (is_polymorphic) { field_count = 0; @@ -2938,6 +2952,8 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e wd->ctx = *ctx; wd->type = soa_struct; wd->wait_to_finish = true; + + mpsc_enqueue(&ctx->checker->soa_types_to_complete, soa_struct); thread_pool_add_task(complete_soa_type_worker, wd); } diff --git a/src/checker.cpp b/src/checker.cpp index e7d0ad9cb..ccda31a4f 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1371,6 +1371,7 @@ gb_internal void init_checker(Checker *c) { array_init(&c->nested_proc_lits, heap_allocator(), 0, 1<<20); mpsc_init(&c->global_untyped_queue, a); // , 1<<20); + mpsc_init(&c->soa_types_to_complete, a); // , 1<<20); c->builtin_ctx = make_checker_context(c); } @@ -1383,6 +1384,7 @@ gb_internal void destroy_checker(Checker *c) { array_free(&c->nested_proc_lits); array_free(&c->procs_to_check); mpsc_destroy(&c->global_untyped_queue); + mpsc_destroy(&c->soa_types_to_complete); } @@ -1682,6 +1684,26 @@ gb_internal bool add_entity_with_name(CheckerContext *c, Scope *scope, Ast *iden } return true; } + +gb_internal bool add_entity_with_name(CheckerInfo *info, Scope *scope, Ast *identifier, Entity *entity, String name) { + if (scope == nullptr) { + return false; + } + + + if (!is_blank_ident(name)) { + Entity *ie = scope_insert(scope, entity); + if (ie != nullptr) { + return redeclaration_error(name, entity, ie); + } + } + if (identifier != nullptr) { + GB_ASSERT(entity->file != nullptr); + add_entity_definition(info, identifier, entity); + } + return true; +} + gb_internal bool add_entity(CheckerContext *c, Scope *scope, Ast *identifier, Entity *entity) { return add_entity_with_name(c, scope, identifier, entity, entity->token.string); } @@ -4440,6 +4462,10 @@ gb_internal void check_all_global_entities(Checker *c) { DeclInfo *d = e->decl_info; check_single_global_entity(c, e, d); if (e->type != nullptr && is_type_typed(e->type)) { + for (Type *t = nullptr; mpsc_dequeue(&c->soa_types_to_complete, &t); /**/) { + complete_soa_type(c, t, false); + } + (void)type_size_of(e->type); (void)type_align_of(e->type); } @@ -6108,6 +6134,9 @@ gb_internal void check_add_definitions_from_queues(Checker *c) { } gb_internal void check_merge_queues_into_arrays(Checker *c) { + for (Type *t = nullptr; mpsc_dequeue(&c->soa_types_to_complete, &t); /**/) { + complete_soa_type(c, t, false); + } check_add_entities_from_queues(c); check_add_definitions_from_queues(c); } @@ -6318,6 +6347,8 @@ gb_internal void check_parsed_files(Checker *c) { TIME_SECTION("check bodies have all been checked"); check_unchecked_bodies(c); + TIME_SECTION("check #soa types"); + check_merge_queues_into_arrays(c); thread_pool_wait(); diff --git a/src/checker.hpp b/src/checker.hpp index e0dc54a87..1701da58d 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -500,6 +500,7 @@ struct Checker { MPSCQueue global_untyped_queue; + MPSCQueue soa_types_to_complete; }; diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index de83f5058..20e4991e7 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -860,7 +860,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ Entity *f = t->Struct.fields[source_index]; i64 foffset = 0; if (!t->Struct.is_raw_union) { - GB_ASSERT(t->Struct.offsets != nullptr); + GB_ASSERT_MSG(t->Struct.offsets != nullptr, "%s", type_to_string(t)); GB_ASSERT(0 <= f->Variable.field_index && f->Variable.field_index < count); foffset = t->Struct.offsets[source_index]; } diff --git a/src/types.cpp b/src/types.cpp index 6e63f56ed..ebe6271f2 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -3772,6 +3772,8 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) { return 1; } + type_set_offsets(t); + i64 max = 1; for_array(i, t->Struct.fields) { Type *field_type = t->Struct.fields[i]->type; -- cgit v1.2.3 From 2bdf5f58ef162a74e132e9a2277aaecccca0df4b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 30 Mar 2024 14:23:47 +0000 Subject: Enforce error on old style for/switch l-value --- src/check_expr.cpp | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 3a275729f..c7a1b460a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2419,10 +2419,6 @@ gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) { } gb_internal void check_old_for_or_switch_value_usage(Ast *expr) { - if (!(build_context.strict_style || (check_vet_flags(expr) & VetFlag_Style))) { - return; - } - Entity *e = entity_of_node(expr); if (e != nullptr && (e->flags & EntityFlag_OldForOrSwitchValue) != 0) { GB_ASSERT(e->kind == Entity_Variable); -- cgit v1.2.3 From f482cc8374d041795dc106709093f194da5ce5bb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 1 Apr 2024 12:33:56 +0100 Subject: Fix error message --- src/check_expr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index c7a1b460a..4142968cc 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2428,7 +2428,7 @@ gb_internal void check_old_for_or_switch_value_usage(Ast *expr) { if ((e->flags & EntityFlag_ForValue) != 0) { Type *parent_type = type_deref(e->Variable.for_loop_parent_type); - error(expr, "Assuming a for-in defined value is addressable as the iterable is passed by value has been disallowed with '-strict-style'."); + error(expr, "Assuming a for-in defined value is addressable as the iterable is passed by value has been disallowed."); if (is_type_map(parent_type)) { error_line("\tSuggestion: Prefer doing 'for key, &%.*s in ...'\n", LIT(e->token.string)); @@ -2438,7 +2438,7 @@ gb_internal void check_old_for_or_switch_value_usage(Ast *expr) { } else { GB_ASSERT((e->flags & EntityFlag_SwitchValue) != 0); - error(expr, "Assuming a switch-in defined value is addressable as the iterable is passed by value has been disallowed with '-strict-style'."); + error(expr, "Assuming a switch-in defined value is addressable as the iterable is passed by value has been disallowed."); error_line("\tSuggestion: Prefer doing 'switch &%.*s in ...'\n", LIT(e->token.string)); } } -- cgit v1.2.3 From bb72ff9c35499078ef5ce22e905ddf919bcc531c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 1 Apr 2024 14:28:11 +0100 Subject: Fix nested `ERROR_BLOCK` bug --- src/check_expr.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 4142968cc..c80b08695 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4129,7 +4129,7 @@ gb_internal void update_untyped_expr_value(CheckerContext *c, Ast *e, ExactValue } } -gb_internal void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_type) { +gb_internal void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_type, bool ignore_error_block=false) { gbString expr_str = expr_to_string(operand->expr); gbString type_str = type_to_string(target_type); gbString from_type_str = type_to_string(operand->type); @@ -4143,7 +4143,9 @@ gb_internal void convert_untyped_error(CheckerContext *c, Operand *operand, Type } } } - ERROR_BLOCK(); + if (!ignore_error_block) { + begin_error_block(); + } error(operand->expr, "Cannot convert untyped value '%s' to '%s' from '%s'%s", expr_str, type_str, from_type_str, extra_text); if (operand->value.kind == ExactValue_String) { @@ -4158,6 +4160,11 @@ gb_internal void convert_untyped_error(CheckerContext *c, Operand *operand, Type gb_string_free(type_str); gb_string_free(expr_str); operand->mode = Addressing_Invalid; + + if (!ignore_error_block) { + end_error_block(); + } + } gb_internal ExactValue convert_exact_value_for_type(ExactValue v, Type *type) { @@ -4287,7 +4294,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar operand->mode = Addressing_Invalid; ERROR_BLOCK(); - convert_untyped_error(c, operand, target_type); + convert_untyped_error(c, operand, target_type, true); error_line("\tNote: Only a square matrix types can be initialized with a scalar value\n"); return; } else { @@ -4350,7 +4357,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar GB_ASSERT(first_success_index >= 0); operand->mode = Addressing_Invalid; - convert_untyped_error(c, operand, target_type); + convert_untyped_error(c, operand, target_type, true); error_line("Ambiguous type conversion to '%s', which variant did you mean:\n\t", type_str); i32 j = 0; @@ -4375,9 +4382,10 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar ERROR_BLOCK(); operand->mode = Addressing_Invalid; - convert_untyped_error(c, operand, target_type); + convert_untyped_error(c, operand, target_type, true); if (count > 0) { error_line("'%s' is a union which only excepts the following types:\n", type_str); + error_line("\t"); for (i32 i = 0; i < count; i++) { Type *v = t->Union.variants[i]; -- cgit v1.2.3 From 114ddc4a108eff7feb0cd83c7920998ddffd6dd1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Apr 2024 12:06:06 +0100 Subject: Fix printing error when field name could not be found --- src/check_expr.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index c80b08695..fe8c9599f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4595,7 +4595,8 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v String name = fv->field->Ident.token.string; Selection sub_sel = lookup_field(node->tav.type, name, false); defer (array_free(&sub_sel.index)); - if (sub_sel.index[0] == index) { + if (sub_sel.index.count > 0 && + sub_sel.index[0] == index) { value = fv->value->tav.value; found = true; break; -- cgit v1.2.3 From 810cf22e5ddd772ee214eec306b1ba148623302c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Apr 2024 16:08:35 +0100 Subject: Obfuscate `#file` and `#procedure` when `-obfuscate-source-code-locations` is enabled --- src/check_expr.cpp | 12 ++++++++++-- src/common.cpp | 13 +++++++++++++ src/llvm_backend_const.cpp | 15 ++------------- 3 files changed, 25 insertions(+), 15 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index fe8c9599f..7d8e0f829 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8157,8 +8157,12 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A o->mode = Addressing_Constant; String name = bd->name.string; if (name == "file") { + String file = get_file_path_string(bd->token.pos.file_id); + if (build_context.obfuscate_source_code_locations) { + file = obfuscate_string(file, "F"); + } o->type = t_untyped_string; - o->value = exact_value_string(get_file_path_string(bd->token.pos.file_id)); + o->value = exact_value_string(file); } else if (name == "line") { o->type = t_untyped_integer; o->value = exact_value_i64(bd->token.pos.line); @@ -8168,8 +8172,12 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A o->type = t_untyped_string; o->value = exact_value_string(str_lit("")); } else { + String p = c->proc_name; + if (build_context.obfuscate_source_code_locations) { + p = obfuscate_string(p, "P"); + } o->type = t_untyped_string; - o->value = exact_value_string(c->proc_name); + o->value = exact_value_string(p); } } else if (name == "caller_location") { init_core_source_code_location(c->checker); diff --git a/src/common.cpp b/src/common.cpp index aad420325..6a53332d9 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -353,6 +353,19 @@ gb_global bool global_module_path_set = false; #include "thread_pool.cpp" +gb_internal String obfuscate_string(String const &s, char const *prefix) { + if (s.len == 0) { + return {}; + } + GB_ASSERT(prefix != nullptr); + u64 hash = gb_fnv64a(s.text, s.len); + gbString res = gb_string_make(temporary_allocator(), prefix); + res = gb_string_append_fmt(res, "x%llx", cast(long long unsigned)hash); + return make_string_c(res); +} + + + struct StringIntern { StringIntern *next; isize len; diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 1ca5f4965..5b2af1049 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -287,17 +287,6 @@ gb_internal lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type return lb_const_value(m, t, tv.value); } -gb_internal String lb_obfuscate_string(String const &s, char const *prefix) { - if (s.len == 0) { - return {}; - } - GB_ASSERT(prefix != nullptr); - u64 hash = gb_fnv64a(s.text, s.len); - gbString res = gb_string_make(temporary_allocator(), prefix); - res = gb_string_append_fmt(res, "x%llx", cast(long long unsigned)hash); - return make_string_c(res); -} - gb_internal i32 lb_obfuscate_i32(i32 i) { i32 x = cast(i32)gb_fnv64a(&i, sizeof(i)); if (x < 0) { @@ -314,8 +303,8 @@ gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String cons i32 column = pos.column; if (build_context.obfuscate_source_code_locations) { - file = lb_obfuscate_string(file, "F"); - procedure = lb_obfuscate_string(procedure, "P"); + file = obfuscate_string(file, "F"); + procedure = obfuscate_string(procedure, "P"); line = lb_obfuscate_i32(line); column = lb_obfuscate_i32(column); -- cgit v1.2.3 From ec455046316ba1bf44a6f2118512341a3f68b10c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Apr 2024 16:14:47 +0100 Subject: Obfuscate `#line` --- src/check_expr.cpp | 6 +++++- src/common.cpp | 8 ++++++++ src/llvm_backend_const.cpp | 11 ++--------- 3 files changed, 15 insertions(+), 10 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7d8e0f829..b893b3a00 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8164,8 +8164,12 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A o->type = t_untyped_string; o->value = exact_value_string(file); } else if (name == "line") { + i32 line = bd->token.pos.line; + if (build_context.obfuscate_source_code_locations) { + line = obfuscate_i32(line); + } o->type = t_untyped_integer; - o->value = exact_value_i64(bd->token.pos.line); + o->value = exact_value_i64(line); } else if (name == "procedure") { if (c->curr_proc_decl == nullptr) { error(node, "#procedure may only be used within procedures"); diff --git a/src/common.cpp b/src/common.cpp index 6a53332d9..e0a579c5d 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -364,6 +364,14 @@ gb_internal String obfuscate_string(String const &s, char const *prefix) { return make_string_c(res); } +gb_internal i32 obfuscate_i32(i32 i) { + i32 x = cast(i32)gb_fnv64a(&i, sizeof(i)); + if (x < 0) { + x = 1-x; + } + return cast(i32)x; +} + struct StringIntern { diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 5b2af1049..8035336d3 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -287,13 +287,6 @@ gb_internal lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type return lb_const_value(m, t, tv.value); } -gb_internal i32 lb_obfuscate_i32(i32 i) { - i32 x = cast(i32)gb_fnv64a(&i, sizeof(i)); - if (x < 0) { - x = 1-x; - } - return cast(i32)x; -} gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String const &procedure_, TokenPos const &pos) { String file = get_file_path_string(pos.file_id); @@ -306,8 +299,8 @@ gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String cons file = obfuscate_string(file, "F"); procedure = obfuscate_string(procedure, "P"); - line = lb_obfuscate_i32(line); - column = lb_obfuscate_i32(column); + line = obfuscate_i32(line); + column = obfuscate_i32(column); } LLVMValueRef fields[4] = {}; -- cgit v1.2.3