From 34e3d3078057fdf22c2f91847096e0a3e098fa82 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Sep 2025 20:51:52 +0100 Subject: More thread contention removal --- src/parser.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index a05e183ce..a7006dd39 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,5 +1,7 @@ #include "parser_pos.cpp" +gb_global std::atomic g_parsing_done; + gb_internal bool in_vet_packages(AstFile *file) { if (file == nullptr) { return true; @@ -176,7 +178,11 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) { return nullptr; } if (f == nullptr) { - f = node->thread_safe_file(); + if (g_parsing_done.load(std::memory_order_relaxed)) { + f = node->file(); + } else { + f = node->thread_safe_file(); + } } Ast *n = alloc_ast_node(f, node->kind); gb_memmove(n, node, ast_node_size(node->kind)); @@ -6924,6 +6930,8 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) { } } + g_parsing_done.store(true, std::memory_order_relaxed); + return ParseFile_None; } -- cgit v1.2.3 From 5ea2e1fe60872c5d2b20e180a1514a082b6513e4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Sep 2025 21:41:52 +0100 Subject: Minimize mutex usage when in single threaded mode. --- src/check_decl.cpp | 2 +- src/check_expr.cpp | 10 +++++----- src/check_stmt.cpp | 12 +++++++----- src/checker.cpp | 39 +++++++++++++++++++++++++-------------- src/checker.hpp | 4 ++-- src/parser.cpp | 1 + src/parser.hpp | 1 + 7 files changed, 42 insertions(+), 27 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 58e4f0120..8fbcb5e40 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -948,7 +948,7 @@ gb_internal Entity *init_entity_foreign_library(CheckerContext *ctx, Entity *e) error(ident, "foreign library names must be an identifier"); } else { String name = ident->Ident.token.string; - Entity *found = scope_lookup(ctx->scope, name); + Entity *found = scope_lookup(ctx->scope, name, ident->Ident.hash); if (found == nullptr) { if (is_blank_ident(name)) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index abfd11485..d2505c047 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 o->expr = n; String name = n->Ident.token.string; - Entity *e = scope_lookup(c->scope, name); + Entity *e = scope_lookup(c->scope, name, n->Ident.hash); if (e == nullptr) { if (is_blank_ident(name)) { error(n, "'_' cannot be used as a value"); @@ -5361,7 +5361,7 @@ gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast * } } else */if (node->kind == Ast_Ident) { String name = node->Ident.token.string; - return scope_lookup(c->scope, name); + return scope_lookup(c->scope, name, node->Ident.hash); } else if (!ident_only) if (node->kind == Ast_SelectorExpr) { ast_node(se, SelectorExpr, node); if (se->token.kind == Token_ArrowRight) { @@ -5383,7 +5383,7 @@ gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast * if (op_expr->kind == Ast_Ident) { String op_name = op_expr->Ident.token.string; - Entity *e = scope_lookup(c->scope, op_name); + Entity *e = scope_lookup(c->scope, op_name, op_expr->Ident.hash); if (e == nullptr) { return nullptr; } @@ -5480,7 +5480,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod if (op_expr->kind == Ast_Ident) { String op_name = op_expr->Ident.token.string; - Entity *e = scope_lookup(c->scope, op_name); + Entity *e = scope_lookup(c->scope, op_name, op_expr->Ident.hash); add_entity_use(c, op_expr, e); expr_entity = e; @@ -5903,7 +5903,7 @@ gb_internal bool check_identifier_exists(Scope *s, Ast *node, bool nested = fals return true; } } else { - Entity *e = scope_lookup(s, name); + Entity *e = scope_lookup(s, name, i->hash); if (e != nullptr) { if (out_scope) *out_scope = e->scope; return true; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index ba9c08fed..62cfc256a 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -484,7 +484,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O } if (ident_node != nullptr) { ast_node(i, Ident, ident_node); - e = scope_lookup(ctx->scope, i->token.string); + e = scope_lookup(ctx->scope, i->token.string, i->hash); if (e != nullptr && e->kind == Entity_Variable) { used = (e->flags & EntityFlag_Used) != 0; // NOTE(bill): Make backup just in case } @@ -1812,8 +1812,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) error(node, "Iteration over a bit_set of an enum is not allowed runtime type information (RTTI) has been disallowed"); } if (rs->vals.count == 1 && rs->vals[0] && rs->vals[0]->kind == Ast_Ident) { - String name = rs->vals[0]->Ident.token.string; - Entity *found = scope_lookup(ctx->scope, name); + AstIdent *ident = &rs->vals[0]->Ident; + String name = ident->token.string; + Entity *found = scope_lookup(ctx->scope, name, ident->hash); if (found && are_types_identical(found->type, t->BitSet.elem)) { ERROR_BLOCK(); gbString s = expr_to_string(expr); @@ -1857,8 +1858,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) error(node, "#reverse for is not supported for map types, as maps are unordered"); } if (rs->vals.count == 1 && rs->vals[0] && rs->vals[0]->kind == Ast_Ident) { - String name = rs->vals[0]->Ident.token.string; - Entity *found = scope_lookup(ctx->scope, name); + AstIdent *ident = &rs->vals[0]->Ident; + String name = ident->token.string; + Entity *found = scope_lookup(ctx->scope, name, ident->hash); if (found && are_types_identical(found->type, t->Map.key)) { ERROR_BLOCK(); gbString s = expr_to_string(expr); diff --git a/src/checker.cpp b/src/checker.cpp index eb334e147..7da3948dc 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -380,16 +380,25 @@ gb_internal Entity *scope_lookup_current(Scope *s, String const &name) { } -gb_internal void scope_lookup_parent(Scope *scope, String const &name, Scope **scope_, Entity **entity_) { +gb_global std::atomic in_single_threaded_checker_stage; + +gb_internal void scope_lookup_parent(Scope *scope, String const &name, Scope **scope_, Entity **entity_, u32 hash) { + bool is_single_threaded = in_single_threaded_checker_stage.load(std::memory_order_relaxed); if (scope != nullptr) { bool gone_thru_proc = false; bool gone_thru_package = false; - StringHashKey key = string_hash_string(name); + StringHashKey key = {}; + if (hash) { + key.hash = hash; + key.string = name; + } else { + key = string_hash_string(name); + } for (Scope *s = scope; s != nullptr; s = s->parent) { Entity **found = nullptr; - rw_mutex_shared_lock(&s->mutex); + if (!is_single_threaded) rw_mutex_shared_lock(&s->mutex); found = string_map_get(&s->elements, key); - rw_mutex_shared_unlock(&s->mutex); + if (!is_single_threaded) rw_mutex_shared_unlock(&s->mutex); if (found) { Entity *e = *found; if (gone_thru_proc) { @@ -424,9 +433,9 @@ gb_internal void scope_lookup_parent(Scope *scope, String const &name, Scope **s if (scope_) *scope_ = nullptr; } -gb_internal Entity *scope_lookup(Scope *s, String const &name) { +gb_internal Entity *scope_lookup(Scope *s, String const &name, u32 hash) { Entity *entity = nullptr; - scope_lookup_parent(s, name, nullptr, &entity); + scope_lookup_parent(s, name, nullptr, &entity, hash); return entity; } @@ -507,11 +516,9 @@ end:; return result; } -gb_global bool in_single_threaded_checker_stage = false; - gb_internal Entity *scope_insert(Scope *s, Entity *entity) { String name = entity->token.string; - if (in_single_threaded_checker_stage) { + if (in_single_threaded_checker_stage.load(std::memory_order_relaxed)) { return scope_insert_with_name_no_mutex(s, name, entity); } else { return scope_insert_with_name(s, name, entity); @@ -853,9 +860,13 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) { gb_internal void add_dependency(CheckerInfo *info, DeclInfo *d, Entity *e) { - rw_mutex_lock(&d->deps_mutex); - ptr_set_add(&d->deps, e); - rw_mutex_unlock(&d->deps_mutex); + if (in_single_threaded_checker_stage.load(std::memory_order_relaxed)) { + ptr_set_add(&d->deps, e); + } else { + rw_mutex_lock(&d->deps_mutex); + ptr_set_add(&d->deps, e); + rw_mutex_unlock(&d->deps_mutex); + } } gb_internal void add_type_info_dependency(CheckerInfo *info, DeclInfo *d, Type *type) { if (d == nullptr || type == nullptr) { @@ -4958,7 +4969,7 @@ gb_internal void check_single_global_entity(Checker *c, Entity *e, DeclInfo *d) } gb_internal void check_all_global_entities(Checker *c) { - in_single_threaded_checker_stage = true; + in_single_threaded_checker_stage.store(true, std::memory_order_relaxed); // NOTE(bill): This must be single threaded // Don't bother trying @@ -4980,7 +4991,7 @@ gb_internal void check_all_global_entities(Checker *c) { } } - in_single_threaded_checker_stage = false; + in_single_threaded_checker_stage.store(false, std::memory_order_relaxed); } diff --git a/src/checker.hpp b/src/checker.hpp index 968988962..68779c280 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -623,8 +623,8 @@ gb_internal Entity *entity_of_node(Ast *expr); gb_internal Entity *scope_lookup_current(Scope *s, String const &name); -gb_internal Entity *scope_lookup (Scope *s, String const &name); -gb_internal void scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entity **entity_); +gb_internal Entity *scope_lookup (Scope *s, String const &name, u32 hash=0); +gb_internal void scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entity **entity_, u32 hash=0); gb_internal Entity *scope_insert (Scope *s, Entity *entity); diff --git a/src/parser.cpp b/src/parser.cpp index a7006dd39..a32494c05 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -750,6 +750,7 @@ gb_internal Ast *ast_matrix_index_expr(AstFile *f, Ast *expr, Token open, Token gb_internal Ast *ast_ident(AstFile *f, Token token) { Ast *result = alloc_ast_node(f, Ast_Ident); result->Ident.token = token; + result->Ident.hash = string_hash(token.string); return result; } diff --git a/src/parser.hpp b/src/parser.hpp index d2dd22667..56447df43 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -396,6 +396,7 @@ struct AstSplitArgs { AST_KIND(Ident, "identifier", struct { \ Token token; \ Entity *entity; \ + u32 hash; \ }) \ AST_KIND(Implicit, "implicit", Token) \ AST_KIND(Uninit, "uninitialized value", Token) \ -- cgit v1.2.3 From 547477abf60c4c643940b55ed729541fbca21cf7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 28 Sep 2025 20:47:32 +0100 Subject: Add `#+test` to replace `_test.odin` --- src/parser.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index a32494c05..363eb0c55 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -6561,6 +6561,10 @@ gb_internal bool parse_file_tag(const String &lc, const Token &tok, AstFile *f) } else if (string_starts_with(lc, str_lit("vet"))) { f->vet_flags = parse_vet_tag(tok, lc); f->vet_flags_set = true; + } else if (string_starts_with(lc, str_lit("test"))) { + if ((build_context.command_kind & Command_test) == 0) { + return false; + } } else if (string_starts_with(lc, str_lit("ignore"))) { return false; } else if (string_starts_with(lc, str_lit("private"))) { -- cgit v1.2.3 From 025cb03242fea23bf632967291419dcd1f881e28 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 9 Oct 2025 15:02:16 +0100 Subject: Add `all-bits` to feature tag --- src/parser.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 363eb0c55..94c6083f7 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -6424,6 +6424,7 @@ gb_internal u64 parse_feature_tag(Token token_for_pos, String s) { switch (flag) { case OptInFeatureFlag_IntegerDivisionByZero_Trap: case OptInFeatureFlag_IntegerDivisionByZero_Zero: + case OptInFeatureFlag_IntegerDivisionByZero_AllBits: syntax_error(token_for_pos, "Feature flag does not support notting with '!' - '%.*s'", LIT(p)); break; } @@ -6436,6 +6437,7 @@ gb_internal u64 parse_feature_tag(Token token_for_pos, String s) { error_line("\tinteger-division-by-zero:trap\n"); error_line("\tinteger-division-by-zero:zero\n"); error_line("\tinteger-division-by-zero:self\n"); + error_line("\tinteger-division-by-zero:all-bits\n"); return OptInFeatureFlag_NONE; } } -- cgit v1.2.3 From 0972690e14f1b7426e2c87bc2dd0152408657ff4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 30 Oct 2025 09:16:19 +0000 Subject: Add suggestion for `T[]` to be `[]T` if a type is allowed in that parsing context --- src/parser.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 94c6083f7..152e55f8b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2739,7 +2739,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { while (allow_token(f, Token_Comma)) { Ast *dummy_name = parse_ident(f); if (!err_once) { - error(dummy_name, "'bit_field' fields do not support multiple names per field"); + syntax_error(dummy_name, "'bit_field' fields do not support multiple names per field"); err_once = true; } } @@ -3299,8 +3299,16 @@ gb_internal Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) { open = expect_token(f, Token_OpenBracket); if (f->curr_token.kind == Token_CloseBracket) { - error(f->curr_token, "Expected an operand, got ]"); + ERROR_BLOCK(); + syntax_error(f->curr_token, "Expected an operand, got ]"); close = expect_token(f, Token_CloseBracket); + + if (f->allow_type) { + gbString s = expr_to_string(operand); + error_line("\tSuggestion: If a type was wanted, did you mean '[]%s'?", s); + gb_string_free(s); + } + operand = ast_index_expr(f, operand, nullptr, open, close); break; } @@ -6594,7 +6602,7 @@ gb_internal bool parse_file_tag(const String &lc, const Token &tok, AstFile *f) } else if (lc == "no-instrumentation") { f->flags |= AstFile_NoInstrumentation; } else { - error(tok, "Unknown tag '%.*s'", LIT(lc)); + syntax_error(tok, "Unknown tag '%.*s'", LIT(lc)); } return true; -- cgit v1.2.3 From 593d2e6daa1f0dd9c24c7fb8704463c8db757af0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 5 Nov 2025 13:30:40 +0000 Subject: Add `#all_or_none` --- base/runtime/core.odin | 8 +++---- base/runtime/print.odin | 6 ++--- core/odin/ast/ast.odin | 1 + core/odin/doc-format/doc_format.odin | 1 + core/odin/parser/parser.odin | 44 ++++++++++++++++++++++------------- core/reflect/types.odin | 6 ++--- src/check_expr.cpp | 45 ++++++++++++++++++++++++++++++++++++ src/check_type.cpp | 7 +++--- src/llvm_backend_type.cpp | 8 +++---- src/name_canonicalization.cpp | 5 ++-- src/parser.cpp | 18 +++++++++++++-- src/parser.hpp | 1 + src/types.cpp | 8 ++++--- 13 files changed, 118 insertions(+), 40 deletions(-) (limited to 'src/parser.cpp') diff --git a/base/runtime/core.odin b/base/runtime/core.odin index 2e70147db..58a0b8ad1 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -118,10 +118,10 @@ Type_Info_Parameters :: struct { // Only used for procedures parameters and resu Type_Info_Struct_Flags :: distinct bit_set[Type_Info_Struct_Flag; u8] Type_Info_Struct_Flag :: enum u8 { - packed = 0, - raw_union = 1, - _ = 2, - align = 3, + packed = 0, + raw_union = 1, + all_or_none = 2, + align = 3, } Type_Info_Struct :: struct { diff --git a/base/runtime/print.odin b/base/runtime/print.odin index 2cfb6661b..90119c699 100644 --- a/base/runtime/print.odin +++ b/base/runtime/print.odin @@ -408,9 +408,9 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) { } print_string("struct ") - if .packed in info.flags { print_string("#packed ") } - if .raw_union in info.flags { print_string("#raw_union ") } - // if .no_copy in info.flags { print_string("#no_copy ") } + if .packed in info.flags { print_string("#packed ") } + if .raw_union in info.flags { print_string("#raw_union ") } + if .all_or_none in info.flags { print_string("#all_or_none ") } if .align in info.flags { print_string("#align(") print_u64(u64(ti.align)) diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index 4dcc1f215..a8c198476 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -790,6 +790,7 @@ Struct_Type :: struct { is_packed: bool, is_raw_union: bool, is_no_copy: bool, + is_all_or_none: bool, fields: ^Field_List, name_count: int, } diff --git a/core/odin/doc-format/doc_format.odin b/core/odin/doc-format/doc_format.odin index e37092948..a60768931 100644 --- a/core/odin/doc-format/doc_format.odin +++ b/core/odin/doc-format/doc_format.odin @@ -281,6 +281,7 @@ Type_Flag_Struct :: enum u32le { Polymorphic = 0, Packed = 1, Raw_Union = 2, + All_Or_None = 3, } Type_Flags_Union :: distinct bit_set[Type_Flag_Union; u32le] diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 9ce484a10..ce088fc64 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -2658,11 +2658,12 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { align: ^ast.Expr min_field_align: ^ast.Expr max_field_align: ^ast.Expr - is_packed: bool - is_raw_union: bool - is_no_copy: bool - fields: ^ast.Field_List - name_count: int + is_packed: bool + is_raw_union: bool + is_no_copy: bool + is_all_or_none: bool + fields: ^ast.Field_List + name_count: int if allow_token(p, .Open_Paren) { param_count: int @@ -2684,6 +2685,11 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { error(p, tag.pos, "duplicate struct tag '#%s'", tag.text) } is_packed = true + case "all_or_none": + if is_all_or_none { + error(p, tag.pos, "duplicate struct tag '#%s'", tag.text) + } + is_all_or_none = true case "align": if align != nil { error(p, tag.pos, "duplicate struct tag '#%s'", tag.text) @@ -2726,6 +2732,11 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { error(p, tok.pos, "'#raw_union' cannot also be '#packed") } + if is_raw_union && is_all_or_none { + is_all_or_none = false + error(p, tok.pos, "'#raw_union' cannot also be '#all_or_none") + } + where_token: tokenizer.Token where_clauses: []^ast.Expr @@ -2745,17 +2756,18 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { close := expect_closing_brace_of_field_list(p) st := ast.new(ast.Struct_Type, tok.pos, end_pos(close)) - st.poly_params = poly_params - st.align = align - st.min_field_align = min_field_align - st.max_field_align = max_field_align - st.is_packed = is_packed - st.is_raw_union = is_raw_union - st.is_no_copy = is_no_copy - st.fields = fields - st.name_count = name_count - st.where_token = where_token - st.where_clauses = where_clauses + st.poly_params = poly_params + st.align = align + st.min_field_align = min_field_align + st.max_field_align = max_field_align + st.is_packed = is_packed + st.is_raw_union = is_raw_union + st.is_no_copy = is_no_copy + st.is_all_or_none = is_all_or_none + st.fields = fields + st.name_count = name_count + st.where_token = where_token + st.where_clauses = where_clauses return st case .Union: diff --git a/core/reflect/types.odin b/core/reflect/types.odin index 2e82e29b1..f71395298 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -696,9 +696,9 @@ write_type_writer :: #force_no_inline proc(w: io.Writer, ti: ^Type_Info, n_writt } io.write_string(w, "struct ", &n) or_return - if .packed in info.flags { io.write_string(w, "#packed ", &n) or_return } - if .raw_union in info.flags { io.write_string(w, "#raw_union ", &n) or_return } - // if .no_copy in info.flags { io.write_string(w, "#no_copy ", &n) or_return } + if .packed in info.flags { io.write_string(w, "#packed ", &n) or_return } + if .raw_union in info.flags { io.write_string(w, "#raw_union ", &n) or_return } + if .all_or_none in info.flags { io.write_string(w, "#all_or_none ", &n) or_return } if .align in info.flags { io.write_string(w, "#align(", &n) or_return io.write_i64(w, i64(ti.align), 10, &n) or_return diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8ac277917..677735c44 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -9838,6 +9838,51 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slicebit_field_bit_size = prev_bit_field_bit_size; } + + if (bt->kind == Type_Struct && bt->Struct.is_all_or_none && elems.count > 0 && bt->Struct.fields.count > 0) { + PtrSet missing_fields = {}; + defer (ptr_set_destroy(&missing_fields)); + + for_array(i, bt->Struct.fields) { + Entity *field = bt->Struct.fields[i]; + String name = field->token.string; + if (is_blank_ident(name) || name == "") { + continue; + } + bool found = string_set_exists(&fields_visited, name); + String *raw_union = string_map_get(&fields_visited_through_raw_union, name); + if (!found && raw_union == nullptr) { + ptr_set_add(&missing_fields, field); + } + } + + if (missing_fields.count > 0) { + ERROR_BLOCK(); + + if (build_context.terse_errors) { + gbString fields_string = gb_string_make(heap_allocator(), ""); + defer (gb_string_free(fields_string)); + isize i = 0; + FOR_PTR_SET(field, missing_fields) { + if (i > 0) { + fields_string = gb_string_appendc(fields_string, ", "); + } + String name = field->token.string; + fields_string = gb_string_append_length(fields_string, name.text, name.len); + i += 1; + } + + error(o->expr, "All or none of the fields must be assigned to a struct with '#all_or_none' applied, missing fields: %s", fields_string); + } else { + error(o->expr, "All or none of the fields must be assigned to a struct with '#all_or_none' applied, missing fields:"); + FOR_PTR_SET(field, missing_fields) { + gbString s = type_to_string(field->type); + error_line("\t%.*s: %s\n", LIT(field->token.string), s); + gb_string_free(s); + } + } + } + } } gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr) { diff --git a/src/check_type.cpp b/src/check_type.cpp index 5accfbd9f..af07efd8f 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -654,9 +654,10 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast * context = str_lit("struct #raw_union"); } - struct_type->Struct.node = node; - struct_type->Struct.scope = ctx->scope; - struct_type->Struct.is_packed = st->is_packed; + struct_type->Struct.node = node; + struct_type->Struct.scope = ctx->scope; + struct_type->Struct.is_packed = st->is_packed; + struct_type->Struct.is_all_or_none = st->is_all_or_none; struct_type->Struct.polymorphic_params = check_record_polymorphic_params( ctx, st->polymorphic_params, &struct_type->Struct.is_polymorphic, diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index 474999191..382304a4e 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -817,10 +817,10 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ { u8 flags = 0; - if (t->Struct.is_packed) flags |= 1<<0; - if (t->Struct.is_raw_union) flags |= 1<<1; - // - if (t->Struct.custom_align) flags |= 1<<3; + if (t->Struct.is_packed) flags |= 1<<0; + if (t->Struct.is_raw_union) flags |= 1<<1; + if (t->Struct.is_all_or_none) flags |= 1<<2; + if (t->Struct.custom_align) flags |= 1<<3; vals[6] = lb_const_int(m, t_u8, flags).value; if (is_type_comparable(t) && !is_type_simple_compare(t)) { diff --git a/src/name_canonicalization.cpp b/src/name_canonicalization.cpp index 87d7a8522..7cc4ad893 100644 --- a/src/name_canonicalization.cpp +++ b/src/name_canonicalization.cpp @@ -749,8 +749,9 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) { write_canonical_params(w, type->Struct.polymorphic_params); } - if (type->Struct.is_packed) type_writer_appendc(w, "#packed"); - if (type->Struct.is_raw_union) type_writer_appendc(w, "#raw_union"); + if (type->Struct.is_packed) type_writer_appendc(w, "#packed"); + if (type->Struct.is_raw_union) type_writer_appendc(w, "#raw_union"); + if (type->Struct.is_all_or_none) type_writer_appendc(w, "#all_or_none"); if (type->Struct.custom_min_field_align != 0) type_writer_append_fmt(w, "#min_field_align(%lld)", cast(long long)type->Struct.custom_min_field_align); if (type->Struct.custom_max_field_align != 0) type_writer_append_fmt(w, "#max_field_align(%lld)", cast(long long)type->Struct.custom_max_field_align); if (type->Struct.custom_align != 0) type_writer_append_fmt(w, "#align(%lld)", cast(long long)type->Struct.custom_align); diff --git a/src/parser.cpp b/src/parser.cpp index 152e55f8b..d3b35f3f4 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1230,7 +1230,7 @@ gb_internal Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) { } gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice fields, isize field_count, - Ast *polymorphic_params, bool is_packed, bool is_raw_union, bool is_no_copy, + Ast *polymorphic_params, bool is_packed, bool is_raw_union, bool is_no_copy, bool is_all_or_none, Ast *align, Ast *min_field_align, Ast *max_field_align, Token where_token, Array const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_StructType); @@ -1241,6 +1241,7 @@ gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice fields, i result->StructType.is_packed = is_packed; result->StructType.is_raw_union = is_raw_union; result->StructType.is_no_copy = is_no_copy; + result->StructType.is_all_or_none = is_all_or_none; result->StructType.align = align; result->StructType.min_field_align = min_field_align; result->StructType.max_field_align = max_field_align; @@ -2773,6 +2774,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { Token token = expect_token(f, Token_struct); Ast *polymorphic_params = nullptr; bool is_packed = false; + bool is_all_or_none = false; bool is_raw_union = false; bool no_copy = false; Ast *align = nullptr; @@ -2802,6 +2804,11 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); } is_packed = true; + } else if (tag.string == "all_or_none") { + if (is_packed) { + syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); + } + is_all_or_none = true; } else if (tag.string == "align") { if (align) { syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); @@ -2872,6 +2879,10 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { is_packed = false; syntax_error(token, "'#raw_union' cannot also be '#packed'"); } + if (is_raw_union && is_all_or_none) { + is_all_or_none = false; + syntax_error(token, "'#raw_union' cannot also be '#all_or_none'"); + } Token where_token = {}; Array where_clauses = {}; @@ -2901,7 +2912,10 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { parser_check_polymorphic_record_parameters(f, polymorphic_params); - return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, no_copy, align, min_field_align, max_field_align, where_token, where_clauses); + return ast_struct_type(f, token, decls, name_count, + polymorphic_params, is_packed, is_raw_union, no_copy, is_all_or_none, + align, min_field_align, max_field_align, + where_token, where_clauses); } break; case Token_union: { diff --git a/src/parser.hpp b/src/parser.hpp index 6127468d4..71b61d95f 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -766,6 +766,7 @@ AST_KIND(_TypeBegin, "", bool) \ bool is_packed; \ bool is_raw_union; \ bool is_no_copy; \ + bool is_all_or_none; \ }) \ AST_KIND(UnionType, "union type", struct { \ Scope *scope; \ diff --git a/src/types.cpp b/src/types.cpp index b9089b9fc..eb20b8edf 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -162,6 +162,7 @@ struct TypeStruct { bool are_offsets_set : 1; bool is_packed : 1; bool is_raw_union : 1; + bool is_all_or_none : 1; bool is_poly_specialized : 1; std::atomic are_offsets_being_processed; @@ -3084,9 +3085,10 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple break; case Type_Struct: - if (x->Struct.is_raw_union == y->Struct.is_raw_union && - x->Struct.fields.count == y->Struct.fields.count && - x->Struct.is_packed == y->Struct.is_packed && + if (x->Struct.is_raw_union == y->Struct.is_raw_union && + x->Struct.fields.count == y->Struct.fields.count && + x->Struct.is_packed == y->Struct.is_packed && + x->Struct.is_all_or_none == y->Struct.is_all_or_none && x->Struct.soa_kind == y->Struct.soa_kind && x->Struct.soa_count == y->Struct.soa_count && are_types_identical(x->Struct.soa_elem, y->Struct.soa_elem)) { -- cgit v1.2.3 From fc2cb8fb39c510bf6d91a7dcef415840a843cd9c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 5 Nov 2025 13:44:14 +0000 Subject: Remove `#no_copy` --- base/runtime/wasm_allocator.odin | 2 +- core/odin/doc-format/doc_format.odin | 13 +++++++------ core/sync/extended.odin | 18 +++++++++--------- core/sync/primitives.odin | 10 +++++----- core/sync/primitives_atomic.odin | 10 +++++----- src/parser.cpp | 11 ++--------- src/tilde_type_info.cpp | 12 +++++------- 7 files changed, 34 insertions(+), 42 deletions(-) (limited to 'src/parser.cpp') diff --git a/base/runtime/wasm_allocator.odin b/base/runtime/wasm_allocator.odin index 325b1d4fa..d5fd71ba6 100644 --- a/base/runtime/wasm_allocator.odin +++ b/base/runtime/wasm_allocator.odin @@ -43,7 +43,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -WASM_Allocator :: struct #no_copy { +WASM_Allocator :: struct { // The minimum alignment of allocations. alignment: uint, // A region that contains as payload a single forward linked list of pointers to diff --git a/core/odin/doc-format/doc_format.odin b/core/odin/doc-format/doc_format.odin index a60768931..df36c1b55 100644 --- a/core/odin/doc-format/doc_format.odin +++ b/core/odin/doc-format/doc_format.odin @@ -219,12 +219,13 @@ Type :: struct { custom_align: String, // Used by: - // .Array - 1 count: 0=len - // .Enumerated_Array - 1 count: 0=len - // .SOA_Struct_Fixed - 1 count: 0=len - // .Bit_Set - 2 count: 0=lower, 1=upper - // .Simd_Vector - 1 count: 0=len - // .Matrix - 2 count: 0=row_count, 1=column_count + // .Array - 1 count: 0=len + // .Enumerated_Array - 1 count: 0=len + // .SOA_Struct_Fixed - 1 count: 0=len + // .Bit_Set - 2 count: 0=lower, 1=upper + // .Simd_Vector - 1 count: 0=len + // .Matrix - 2 count: 0=row_count, 1=column_count + // .Struct - <=2 count: 0=min_field_align, 1=max_field_align elem_count_len: u32le, elem_counts: [Type_Elems_Cap]i64le, diff --git a/core/sync/extended.odin b/core/sync/extended.odin index 82fc3d751..8566eccc8 100644 --- a/core/sync/extended.odin +++ b/core/sync/extended.odin @@ -26,7 +26,7 @@ is not allowed to become negative. **Note**: Just like any synchronization primitives, a wait group cannot be copied after first use. See documentation for `Mutex` or `Cond`. */ -Wait_Group :: struct #no_copy { +Wait_Group :: struct { counter: int, mutex: Mutex, cond: Cond, @@ -144,7 +144,7 @@ thread procedure. thread.destroy(t) } */ -Barrier :: struct #no_copy { +Barrier :: struct { mutex: Mutex, cond: Cond, index: int, @@ -206,7 +206,7 @@ When a thread calls `auto_reset_event_wait`, its execution will be blocked, until the event is signalled by another thread. The call to `auto_reset_event_signal` wakes up exactly one thread waiting for the event. */ -Auto_Reset_Event :: struct #no_copy { +Auto_Reset_Event :: struct { // status == 0: Event is reset and no threads are waiting // status == 1: Event is signalled // status == -N: Event is reset and N threads are waiting @@ -260,7 +260,7 @@ of entries into the critical section. This type of synchronization primitive is applicable for short critical sections in low-contention systems, as it uses a spinlock under the hood. */ -Ticket_Mutex :: struct #no_copy { +Ticket_Mutex :: struct { ticket: uint, serving: uint, } @@ -332,7 +332,7 @@ Once a lock on a benaphore is acquired by a thread, no other thread is allowed into any critical sections, associted with the same benaphore, until the lock is released. */ -Benaphore :: struct #no_copy { +Benaphore :: struct { counter: i32, sema: Sema, } @@ -424,7 +424,7 @@ to acquire another lock on the same benaphore. When a thread has acquired the lock on a benaphore, the benaphore will stay locked until the thread releases the lock as many times as it has been locked by the thread. */ -Recursive_Benaphore :: struct #no_copy { +Recursive_Benaphore :: struct { counter: int, owner: int, recursion: i32, @@ -536,7 +536,7 @@ Once action. `Once` a synchronization primitive, that only allows a single entry into a critical section from a single thread. */ -Once :: struct #no_copy { +Once :: struct { m: Mutex, done: bool, } @@ -636,7 +636,7 @@ A Parker is an associated token which is initially not present: * The `unpark` procedure automatically makes the token available if it was not already. */ -Parker :: struct #no_copy { +Parker :: struct { state: Futex, } @@ -713,7 +713,7 @@ A one-shot event is an associated token which is initially not present: * The `one_shot_event_signal` procedure automatically makes the token available if its was not already. */ -One_Shot_Event :: struct #no_copy { +One_Shot_Event :: struct { state: Futex, } diff --git a/core/sync/primitives.odin b/core/sync/primitives.odin index f091de045..276427477 100644 --- a/core/sync/primitives.odin +++ b/core/sync/primitives.odin @@ -36,7 +36,7 @@ holding another lock, that will cause a trivial case of deadlock. Do not use `Mutex` in recursive functions. In case multiple locks by the same thread are desired, use `Recursive_Mutex`. */ -Mutex :: struct #no_copy { +Mutex :: struct { impl: _Mutex, } @@ -147,7 +147,7 @@ result in broken and unsafe behavior. For this reason, mutexes are marked as exclusive lock more than once from the same thread, or an exclusive and shared lock on the same thread. Taking a shared lock multiple times is acceptable. */ -RW_Mutex :: struct #no_copy { +RW_Mutex :: struct { impl: _RW_Mutex, } @@ -313,7 +313,7 @@ released. Trying to use a copy of the lock at a different memory address will result in broken and unsafe behavior. For this reason, mutexes are marked as `#no_copy`. */ -Recursive_Mutex :: struct #no_copy { +Recursive_Mutex :: struct { impl: _Recursive_Mutex, } @@ -414,7 +414,7 @@ lock has been released. Trying to use a copy of the lock at a different memory address will result in broken and unsafe behavior. For this reason, condition variables are marked as `#no_copy`. */ -Cond :: struct #no_copy { +Cond :: struct { impl: _Cond, } @@ -494,7 +494,7 @@ must watch the same memory address to know when the lock has been released. Trying to use a copy of the lock at a different memory address will result in broken and unsafe behavior. For this reason, semaphores are marked as `#no_copy`. */ -Sema :: struct #no_copy { +Sema :: struct { impl: _Sema, } diff --git a/core/sync/primitives_atomic.odin b/core/sync/primitives_atomic.odin index c694e5f96..9d5bdf7ee 100644 --- a/core/sync/primitives_atomic.odin +++ b/core/sync/primitives_atomic.odin @@ -13,7 +13,7 @@ Atomic_Mutex_State :: enum Futex { // The zero value for a Atomic_Mutex is an unlocked mutex // // An Atomic_Mutex must not be copied after first use -Atomic_Mutex :: struct #no_copy { +Atomic_Mutex :: struct { state: Atomic_Mutex_State, } @@ -105,7 +105,7 @@ Atomic_RW_Mutex_State_Reader_Mask :: ~Atomic_RW_Mutex_State_Is_Writing // The zero value for an Atomic_RW_Mutex is an unlocked mutex. // // An Atomic_RW_Mutex must not be copied after first use. -Atomic_RW_Mutex :: struct #no_copy { +Atomic_RW_Mutex :: struct { state: Atomic_RW_Mutex_State, mutex: Atomic_Mutex, sema: Atomic_Sema, @@ -246,7 +246,7 @@ atomic_rw_mutex_shared_guard :: proc "contextless" (m: ^Atomic_RW_Mutex) -> bool // The zero value for a Recursive_Mutex is an unlocked mutex // // An Atomic_Recursive_Mutex must not be copied after first use -Atomic_Recursive_Mutex :: struct #no_copy { +Atomic_Recursive_Mutex :: struct { owner: int, recursion: int, mutex: Mutex, @@ -309,7 +309,7 @@ atomic_recursive_mutex_guard :: proc "contextless" (m: ^Atomic_Recursive_Mutex) // waiting for signalling the occurence of an event // // An Atomic_Cond must not be copied after first use -Atomic_Cond :: struct #no_copy { +Atomic_Cond :: struct { state: Futex, } @@ -344,7 +344,7 @@ atomic_cond_broadcast :: proc "contextless" (c: ^Atomic_Cond) { // Posting to the semaphore increases the count by one, or the provided amount. // // An Atomic_Sema must not be copied after first use -Atomic_Sema :: struct #no_copy { +Atomic_Sema :: struct { count: Futex, } diff --git a/src/parser.cpp b/src/parser.cpp index d3b35f3f4..06703d643 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1230,7 +1230,7 @@ gb_internal Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) { } gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice fields, isize field_count, - Ast *polymorphic_params, bool is_packed, bool is_raw_union, bool is_no_copy, bool is_all_or_none, + Ast *polymorphic_params, bool is_packed, bool is_raw_union, bool is_all_or_none, Ast *align, Ast *min_field_align, Ast *max_field_align, Token where_token, Array const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_StructType); @@ -1240,7 +1240,6 @@ gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice fields, i result->StructType.polymorphic_params = polymorphic_params; result->StructType.is_packed = is_packed; result->StructType.is_raw_union = is_raw_union; - result->StructType.is_no_copy = is_no_copy; result->StructType.is_all_or_none = is_all_or_none; result->StructType.align = align; result->StructType.min_field_align = min_field_align; @@ -2776,7 +2775,6 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { bool is_packed = false; bool is_all_or_none = false; bool is_raw_union = false; - bool no_copy = false; Ast *align = nullptr; Ast *min_field_align = nullptr; Ast *max_field_align = nullptr; @@ -2863,11 +2861,6 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); } is_raw_union = true; - } else if (tag.string == "no_copy") { - if (no_copy) { - syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); - } - no_copy = true; } else { syntax_error(tag, "Invalid struct tag '#%.*s'", LIT(tag.string)); } @@ -2913,7 +2906,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { parser_check_polymorphic_record_parameters(f, polymorphic_params); return ast_struct_type(f, token, decls, name_count, - polymorphic_params, is_packed, is_raw_union, no_copy, is_all_or_none, + polymorphic_params, is_packed, is_raw_union, is_all_or_none, align, min_field_align, max_field_align, where_token, where_clauses); } break; diff --git a/src/tilde_type_info.cpp b/src/tilde_type_info.cpp index 58e8d3087..96a101376 100644 --- a/src/tilde_type_info.cpp +++ b/src/tilde_type_info.cpp @@ -783,14 +783,13 @@ gb_internal void cg_setup_type_info_data(cgModule *m) { i64 is_packed_offset = type_offset_of(tag_type, 5); i64 is_raw_union_offset = type_offset_of(tag_type, 6); - i64 is_no_copy_offset = type_offset_of(tag_type, 7); - i64 custom_align_offset = type_offset_of(tag_type, 8); + i64 custom_align_offset = type_offset_of(tag_type, 7); - i64 equal_offset = type_offset_of(tag_type, 9); + i64 equal_offset = type_offset_of(tag_type, 8); - i64 soa_kind_offset = type_offset_of(tag_type, 10); - i64 soa_base_type_offset = type_offset_of(tag_type, 11); - i64 soa_len_offset = type_offset_of(tag_type, 12); + i64 soa_kind_offset = type_offset_of(tag_type, 9); + i64 soa_base_type_offset = type_offset_of(tag_type, 10); + i64 soa_len_offset = type_offset_of(tag_type, 11); // TODO(bill): equal proc stuff gb_unused(equal_offset); @@ -825,7 +824,6 @@ gb_internal void cg_setup_type_info_data(cgModule *m) { set_bool(m, global, offset+is_packed_offset, t->Struct.is_packed); set_bool(m, global, offset+is_raw_union_offset, t->Struct.is_raw_union); - set_bool(m, global, offset+is_no_copy_offset, t->Struct.is_no_copy); set_bool(m, global, offset+custom_align_offset, t->Struct.custom_align != 0); if (t->Struct.soa_kind != StructSoa_None) { -- cgit v1.2.3 From a5809d0185dfeb2473f2a3947459b889c4dd5098 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Thu, 18 Dec 2025 15:04:22 +0100 Subject: Fix #packed #all_or_none --- src/parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 06703d643..de5655ce1 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2803,7 +2803,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { } is_packed = true; } else if (tag.string == "all_or_none") { - if (is_packed) { + if (is_all_or_none) { syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); } is_all_or_none = true; -- cgit v1.2.3