From 77b5eebf8ce090839713fc59fe0f60045524043f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 7 Apr 2025 11:57:55 +0100 Subject: Add trivial sanity check for assigning to return values within `defer` #5011 --- src/check_stmt.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index e81996566..1b44ff4d7 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2755,6 +2755,47 @@ gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) if (ctx->decl) { ctx->decl->defer_used += 1; } + + // NOTE(bill): Handling errors/warnings + + Ast *stmt = ds->stmt; + Ast *original_stmt = stmt; + + bool is_singular = true; + while (is_singular && stmt->kind == Ast_BlockStmt) { + Ast *inner_stmt = nullptr; + for (Ast *s : stmt->BlockStmt.stmts) { + if (s->kind == Ast_EmptyStmt) { + continue; + } + if (inner_stmt != nullptr) { + is_singular = false; + break; + } + inner_stmt = s; + } + + if (inner_stmt != nullptr) { + stmt = inner_stmt; + } + } + if (!is_singular) { + stmt = original_stmt; + } + + switch (stmt->kind) { + case_ast_node(as, AssignStmt, stmt); + if (as->op.kind != Token_Eq) { + break; + } + for (Ast *lhs : as->lhs) { + Entity *e = entity_of_node(lhs); + if (e && e->flags & EntityFlag_Result) { + error(lhs, "Assignments to named return values within 'defer' will not affect the value that is returned"); + } + } + case_end; + } } case_end; -- cgit v1.2.3 From 3c0ba5bb55fe59ed1644c3e61fd2a81fb856624e Mon Sep 17 00:00:00 2001 From: bogwi Date: Mon, 5 May 2025 22:39:03 +0900 Subject: CHECK 4 done The original errors: 1. `5024.odin(127:15) Error: Invalid use of a polymorphic type 'List($T)' in variable declaration` 2. `5024.odin(129:17) Error: Cannot determine polymorphic type from parameter: 'invalid type' to 'List($T)'` Are gone. We now have a single, different error: `5024.odin(124:28) Error: Unspecialized polymorphic types are not allowed in procedure parameters, got List($T)` This error points directly to the `list : List($T)` parameter within the `List_Filter` procedure definition. This seems much more relevant to the actual problem (the interaction between the generic `List_Filter` and the concrete `default_filter`) than the original error about the variable declaration. While this new error message might not be exactly pinpointing the default parameter issue, it correctly identifies the problematic procedure definition (`List_Filter`) as the source of the error, rather than the variable declaration (`my_list`). This seems like a step in the right direction for improving the error reporting for this kind of scenario. --- src/check_stmt.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 1b44ff4d7..0460f5bec 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2108,10 +2108,12 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f if (init_type == nullptr) { init_type = t_invalid; } else if (is_type_polymorphic(base_type(init_type))) { + /* DISABLED: This error seems too aggressive for instantiated generic types. gbString str = type_to_string(init_type); error(vd->type, "Invalid use of a polymorphic type '%s' in variable declaration", str); gb_string_free(str); init_type = t_invalid; + */ } if (init_type == t_invalid && entity_count == 1 && (mod_flags & (Stmt_BreakAllowed|Stmt_FallthroughAllowed))) { Entity *e = entities[0]; -- cgit v1.2.3 From 7853a1db1c1ab3662767d14da055e59a7434ebcd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 29 May 2025 16:35:28 +0100 Subject: Fix #5228 --- src/check_stmt.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 0460f5bec..6cacef3ee 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2855,6 +2855,7 @@ gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) case Ast_BlockStmt: case Ast_IfStmt: case Ast_SwitchStmt: + case Ast_TypeSwitchStmt: if (token.kind != Token_break) { error(bs->label, "Label '%.*s' can only be used with 'break'", LIT(e->token.string)); } -- cgit v1.2.3 From d046214f67baf9c580605fa4294818e7a12fc434 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Wed, 4 Jun 2025 14:00:33 -0400 Subject: Be strict with type switch case column alignment too This copies the same block used for regular switch cases. Fixes #4673 --- src/check_stmt.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 6cacef3ee..d92edf41d 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1593,6 +1593,20 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_ error_line("\tSuggestion: Was '#partial switch' wanted?\n"); } } + + if (build_context.strict_style) { + Token stok = ss->token; + for_array(i, bs->stmts) { + Ast *stmt = bs->stmts[i]; + if (stmt->kind != Ast_CaseClause) { + continue; + } + Token ctok = stmt->CaseClause.token; + if (ctok.pos.column > stok.pos.column) { + error(ctok, "With '-strict-style', 'case' statements must share the same column as the 'switch' token"); + } + } + } } gb_internal void check_block_stmt_for_errors(CheckerContext *ctx, Ast *body) { -- cgit v1.2.3 From 2259db9a5396f598cb1c38d5f0ad73b02fce1403 Mon Sep 17 00:00:00 2001 From: Airtz Date: Tue, 24 Jun 2025 02:54:14 +0200 Subject: Better error messages --- src/check_expr.cpp | 14 +++++++------- src/check_stmt.cpp | 20 +++++++++++--------- 2 files changed, 18 insertions(+), 16 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 3f947b6a7..fa1b4cd92 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2424,27 +2424,27 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o Type *s = src->Array.elem; Type *d = dst->Slice.elem; if (are_types_identical(s, d)) { - error_line("\tSuggestion: the array expression may be sliced with %s[:]\n", a); + error_line("\tSuggestion: The array expression may be sliced with %s[:]\n", a); } } else if (is_type_dynamic_array(src) && is_type_slice(dst)) { Type *s = src->DynamicArray.elem; Type *d = dst->Slice.elem; if (are_types_identical(s, d)) { - error_line("\tSuggestion: the dynamic array expression may be sliced with %s[:]\n", a); + error_line("\tSuggestion: The dynamic array expression may be sliced with %s[:]\n", a); } }else if (are_types_identical(src, dst) && !are_types_identical(o->type, type)) { - error_line("\tSuggestion: the expression may be directly casted to type %s\n", b); + error_line("\tSuggestion: The expression may be directly casted to type %s\n", b); } else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) { - error_line("\tSuggestion: a string may be transmuted to %s\n", b); - error_line("\t This is an UNSAFE operation as string data is assumed to be immutable, \n"); + error_line("\tSuggestion: A string may be transmuted to %s\n", b); + error_line("\t This is an UNSAFE operation as string data is assumed to be immutable,\n"); 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); + 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); + 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 && diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index d92edf41d..620e9fb74 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -418,7 +418,7 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { -gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, Operand *rhs) { +gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, Operand *rhs, String context_name) { if (rhs->mode == Addressing_Invalid) { return nullptr; } @@ -430,7 +430,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O Ast *node = unparen_expr(lhs->expr); - check_no_copy_assignment(*rhs, str_lit("assignment")); + check_no_copy_assignment(*rhs, context_name); // NOTE(bill): Ignore assignments to '_' if (is_blank_ident(node)) { @@ -630,7 +630,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O ctx->bit_field_bit_size = lhs_e->Variable.bit_field_bit_size; } - check_assignment(ctx, rhs, assignment_type, str_lit("assignment")); + check_assignment(ctx, rhs, assignment_type, context_name); ctx->bit_field_bit_size = prev_bit_field_bit_size; @@ -2418,7 +2418,7 @@ gb_internal void check_assign_stmt(CheckerContext *ctx, Ast *node) { isize lhs_count = as->lhs.count; if (lhs_count == 0) { - error(as->op, "Missing lhs in assignment statement"); + error(as->op, "Missing LHS in assignment statement"); return; } @@ -2451,7 +2451,7 @@ gb_internal void check_assign_stmt(CheckerContext *ctx, Ast *node) { if (lhs_to_ignore[i]) { continue; } - check_assignment_variable(ctx, &lhs_operands[i], &rhs_operands[i]); + check_assignment_variable(ctx, &lhs_operands[i], &rhs_operands[i], str_lit("assignment")); } if (lhs_count != rhs_count) { error(as->lhs[0], "Assignment count mismatch '%td' = '%td'", lhs_count, rhs_count); @@ -2461,11 +2461,11 @@ gb_internal void check_assign_stmt(CheckerContext *ctx, Ast *node) { // a += 1; // Single-sided Token op = as->op; if (as->lhs.count != 1 || as->rhs.count != 1) { - error(op, "Assignment operation '%.*s' requires single-valued expressions", LIT(op.string)); + error(op, "Assignment operator '%.*s' requires single-valued operands", LIT(op.string)); return; } if (!gb_is_between(op.kind, Token__AssignOpBegin+1, Token__AssignOpEnd-1)) { - error(op, "Unknown Assignment operation '%.*s'", LIT(op.string)); + error(op, "Unknown assignment operator '%.*s'", LIT(op.string)); return; } Operand lhs = {Addressing_Invalid}; @@ -2474,7 +2474,7 @@ gb_internal void check_assign_stmt(CheckerContext *ctx, Ast *node) { ast_node(be, BinaryExpr, binary_expr); be->op = op; be->op.kind = cast(TokenKind)(cast(i32)be->op.kind - (Token_AddEq - Token_Add)); - // NOTE(bill): Only use the first one will be used + // NOTE(bill): Only use the first one will be used be->left = as->lhs[0]; be->right = as->rhs[0]; @@ -2482,7 +2482,9 @@ gb_internal void check_assign_stmt(CheckerContext *ctx, Ast *node) { check_binary_expr(ctx, &rhs, binary_expr, nullptr, true); if (rhs.mode != Addressing_Invalid) { // NOTE(bill): Only use the first one will be used - check_assignment_variable(ctx, &lhs, &rhs); + be->op.string = substring(be->op.string, 0, 1); + rhs.expr = binary_expr; + check_assignment_variable(ctx, &lhs, &rhs, str_lit("assignment operation")); } } } -- cgit v1.2.3 From fb3bccdd3e31c8c237721f6658722e0bfb54e71c Mon Sep 17 00:00:00 2001 From: Airtz Date: Tue, 24 Jun 2025 16:09:42 +0200 Subject: Fix an oversight for operators with more than 2 chars --- src/check_stmt.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 620e9fb74..07801b477 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2481,8 +2481,7 @@ gb_internal void check_assign_stmt(CheckerContext *ctx, Ast *node) { check_expr(ctx, &lhs, as->lhs[0]); check_binary_expr(ctx, &rhs, binary_expr, nullptr, true); if (rhs.mode != Addressing_Invalid) { - // NOTE(bill): Only use the first one will be used - be->op.string = substring(be->op.string, 0, 1); + be->op.string = substring(be->op.string, 0, be->op.string.len - 1); rhs.expr = binary_expr; check_assignment_variable(ctx, &lhs, &rhs, str_lit("assignment operation")); } -- cgit v1.2.3 From e735e036b4df84db84aa7d66c1118b3e906f0022 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 9 Jul 2025 23:55:57 +0200 Subject: Fix #5447 Thanks to @FourteenBrush --- src/check_stmt.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 07801b477..7187d95b3 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2778,6 +2778,10 @@ gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) Ast *stmt = ds->stmt; Ast *original_stmt = stmt; + if (stmt->kind == Ast_BlockStmt && stmt->BlockStmt.stmts.count == 0) { + break; // empty defer statement + } + bool is_singular = true; while (is_singular && stmt->kind == Ast_BlockStmt) { Ast *inner_stmt = nullptr; -- cgit v1.2.3 From 7057fc8dfc960ad3e7ea4f76deeaafd0cdcf4cf6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 30 Jul 2025 23:14:29 +0100 Subject: Remove the semantics of `#no_copy`, keep the grammar --- base/runtime/core.odin | 2 +- src/check_decl.cpp | 7 ------- src/check_expr.cpp | 23 ----------------------- src/check_stmt.cpp | 2 -- src/check_type.cpp | 1 - src/llvm_backend_type.cpp | 2 +- src/name_canonicalization.cpp | 1 - src/types.cpp | 7 ------- 8 files changed, 2 insertions(+), 43 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/base/runtime/core.odin b/base/runtime/core.odin index 090d1a65b..baecb4146 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -115,7 +115,7 @@ Type_Info_Struct_Flags :: distinct bit_set[Type_Info_Struct_Flag; u8] Type_Info_Struct_Flag :: enum u8 { packed = 0, raw_union = 1, - no_copy = 2, + _ = 2, align = 3, } diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 0bacf891b..dd4c09e85 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -145,13 +145,6 @@ gb_internal void check_init_variables(CheckerContext *ctx, Entity **lhs, isize l if (d != nullptr) { d->init_expr = o->expr; } - - if (o->type && is_type_no_copy(o->type)) { - ERROR_BLOCK(); - if (check_no_copy_assignment(*o, str_lit("initialization"))) { - error_line("\tInitialization of a #no_copy type must be either implicitly zero, a constant literal, or a return value from a call expression"); - } - } } if (rhs_count > 0 && lhs_count != rhs_count) { error(lhs[0]->token, "Assignment count mismatch '%td' = '%td'", lhs_count, rhs_count); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index dd6a89e5b..6723a7580 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5763,22 +5763,6 @@ gb_internal bool check_identifier_exists(Scope *s, Ast *node, bool nested = fals return false; } -gb_internal bool check_no_copy_assignment(Operand const &o, String const &context) { - if (o.type && is_type_no_copy(o.type)) { - Ast *expr = unparen_expr(o.expr); - if (expr && o.mode != Addressing_Constant && o.mode != Addressing_Type) { - if (expr->kind == Ast_CallExpr) { - // Okay - } else { - error(o.expr, "Invalid use of #no_copy value in %.*s", LIT(context)); - return true; - } - } - } - return false; -} - - gb_internal bool check_assignment_arguments(CheckerContext *ctx, Array const &lhs, Array *operands, Slice const &rhs) { bool optional_ok = false; isize tuple_index = 0; @@ -5849,7 +5833,6 @@ gb_internal bool check_assignment_arguments(CheckerContext *ctx, Array for (Entity *e : tuple->variables) { o.type = e->type; array_add(operands, o); - check_no_copy_assignment(o, str_lit("assignment")); } tuple_index += tuple->variables.count; @@ -6236,12 +6219,6 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A } - for (Operand const &o : ordered_operands) { - if (o.mode != Addressing_Invalid) { - check_no_copy_assignment(o, str_lit("procedure call expression")); - } - } - for (isize i = 0; i < pt->param_count; i++) { if (!visited[i]) { Entity *e = pt->params->Tuple.variables[i]; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 7187d95b3..bc9b6c5dd 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -430,8 +430,6 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O Ast *node = unparen_expr(lhs->expr); - check_no_copy_assignment(*rhs, context_name); - // NOTE(bill): Ignore assignments to '_' if (is_blank_ident(node)) { check_assignment(ctx, rhs, nullptr, str_lit("assignment to '_' identifier")); diff --git a/src/check_type.cpp b/src/check_type.cpp index 5f9540ee0..79705b928 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -646,7 +646,6 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast * struct_type->Struct.node = node; struct_type->Struct.scope = ctx->scope; struct_type->Struct.is_packed = st->is_packed; - struct_type->Struct.is_no_copy = st->is_no_copy; 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 4e514c3d1..43c5f0b40 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -797,7 +797,7 @@ 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.is_no_copy) flags |= 1<<2; + // if (t->Struct.custom_align) flags |= 1<<3; vals[6] = lb_const_int(m, t_u8, flags).value; diff --git a/src/name_canonicalization.cpp b/src/name_canonicalization.cpp index 0372f5039..e3090368a 100644 --- a/src/name_canonicalization.cpp +++ b/src/name_canonicalization.cpp @@ -687,7 +687,6 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) { 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_no_copy) type_writer_appendc(w, "#no_copy"); 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/types.cpp b/src/types.cpp index 74da7f6aa..2e696810d 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -155,7 +155,6 @@ struct TypeStruct { bool are_offsets_being_processed : 1; bool is_packed : 1; bool is_raw_union : 1; - bool is_no_copy : 1; bool is_poly_specialized : 1; }; @@ -1780,10 +1779,6 @@ gb_internal bool is_type_raw_union(Type *t) { t = base_type(t); return (t->kind == Type_Struct && t->Struct.is_raw_union); } -gb_internal bool is_type_no_copy(Type *t) { - t = base_type(t); - return (t->kind == Type_Struct && t->Struct.is_no_copy); -} gb_internal bool is_type_enum(Type *t) { t = base_type(t); return (t->kind == Type_Enum); @@ -2859,7 +2854,6 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple case Type_Struct: if (x->Struct.is_raw_union == y->Struct.is_raw_union && - x->Struct.is_no_copy == y->Struct.is_no_copy && x->Struct.fields.count == y->Struct.fields.count && x->Struct.is_packed == y->Struct.is_packed && x->Struct.soa_kind == y->Struct.soa_kind && @@ -4832,7 +4826,6 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha if (type->Struct.is_packed) str = gb_string_appendc(str, " #packed"); if (type->Struct.is_raw_union) str = gb_string_appendc(str, " #raw_union"); - if (type->Struct.is_no_copy) str = gb_string_appendc(str, " #no_copy"); if (type->Struct.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Struct.custom_align); str = gb_string_appendc(str, " {"); -- cgit v1.2.3 From bb4bc316a4bd86774953f1e8fcefffb5ed8bbf37 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 2 Aug 2025 12:20:35 +0100 Subject: `for in string16`; Support `string16` across core --- base/runtime/internal.odin | 62 +++++++++++++++++ core/c/libc/locale.odin | 16 ++--- core/debug/trace/trace_windows.odin | 2 +- core/dynlib/lib_windows.odin | 2 +- core/mem/virtual/virtual_windows.odin | 2 +- core/os/dir_windows.odin | 2 +- core/os/os2/file_windows.odin | 2 +- core/os/stat_windows.odin | 4 +- core/path/filepath/path_windows.odin | 4 +- core/sys/info/platform_windows.odin | 12 ++-- core/sys/windows/comctl32.odin | 10 +-- core/sys/windows/ip_helper.odin | 6 +- core/sys/windows/types.odin | 4 +- core/sys/windows/util.odin | 50 +++++++------- core/time/timezone/tz_windows.odin | 8 +-- core/unicode/utf16/utf16.odin | 32 ++++++++- src/check_builtin.cpp | 6 +- src/check_expr.cpp | 2 +- src/check_stmt.cpp | 26 +++++++- src/llvm_backend_stmt.cpp | 121 +++++++++++++++++++++++++++++++++- src/llvm_backend_utility.cpp | 17 +++++ 21 files changed, 321 insertions(+), 69 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/base/runtime/internal.odin b/base/runtime/internal.odin index 660af58ab..4f9509b23 100644 --- a/base/runtime/internal.odin +++ b/base/runtime/internal.odin @@ -781,6 +781,68 @@ string_decode_last_rune :: proc "contextless" (s: string) -> (rune, int) { return r, size } + +string16_decode_rune :: #force_inline proc "contextless" (s: string16) -> (rune, int) { + REPLACEMENT_CHAR :: '\ufffd' + _surr1 :: 0xd800 + _surr2 :: 0xdc00 + _surr3 :: 0xe000 + _surr_self :: 0x10000 + + r := rune(REPLACEMENT_CHAR) + + if len(s) < 1 { + return r, 0 + } + + w := 1 + switch c := s[0]; { + case c < _surr1, _surr3 <= c: + r = rune(c) + case _surr1 <= c && c < _surr2 && 1 < len(s) && + _surr2 <= s[1] && s[1] < _surr3: + r1, r2 := rune(c), rune(s[1]) + if _surr1 <= r1 && r1 < _surr2 && _surr2 <= r2 && r2 < _surr3 { + r = (r1-_surr1)<<10 | (r2 - _surr2) + _surr_self + } + w += 1 + } + return r, w +} + +string16_decode_last_rune :: proc "contextless" (s: string16) -> (rune, int) { + REPLACEMENT_CHAR :: '\ufffd' + _surr1 :: 0xd800 + _surr2 :: 0xdc00 + _surr3 :: 0xe000 + _surr_self :: 0x10000 + + r := rune(REPLACEMENT_CHAR) + + if len(s) < 1 { + return r, 0 + } + + n := len(s)-1 + c := s[n] + w := 1 + if _surr2 <= c && c < _surr3 { + if n >= 1 { + r1 := rune(s[n-1]) + r2 := rune(c) + if _surr1 <= r1 && r1 < _surr2 { + r = (r1-_surr1)<<10 | (r2 - _surr2) + _surr_self + } + w = 2 + } + } else if c < _surr1 || _surr3 <= c { + r = rune(c) + } + return r, w +} + + + abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 { p, q := abs(real(x)), abs(imag(x)) if p < q { diff --git a/core/c/libc/locale.odin b/core/c/libc/locale.odin index 27317526c..3216e0f90 100644 --- a/core/c/libc/locale.odin +++ b/core/c/libc/locale.odin @@ -72,14 +72,14 @@ when ODIN_OS == .Windows { n_sep_by_space: c.char, p_sign_posn: c.char, n_sign_posn: c.char, - _W_decimal_point: [^]u16 `fmt:"s,0"`, - _W_thousands_sep: [^]u16 `fmt:"s,0"`, - _W_int_curr_symbol: [^]u16 `fmt:"s,0"`, - _W_currency_symbol: [^]u16 `fmt:"s,0"`, - _W_mon_decimal_point: [^]u16 `fmt:"s,0"`, - _W_mon_thousands_sep: [^]u16 `fmt:"s,0"`, - _W_positive_sign: [^]u16 `fmt:"s,0"`, - _W_negative_sign: [^]u16 `fmt:"s,0"`, + _W_decimal_point: cstring16, + _W_thousands_sep: cstring16, + _W_int_curr_symbol: cstring16, + _W_currency_symbol: cstring16, + _W_mon_decimal_point: cstring16, + _W_mon_thousands_sep: cstring16, + _W_positive_sign: cstring16, + _W_negative_sign: cstring16, } } else { lconv :: struct { diff --git a/core/debug/trace/trace_windows.odin b/core/debug/trace/trace_windows.odin index 96507714c..04e92f125 100644 --- a/core/debug/trace/trace_windows.odin +++ b/core/debug/trace/trace_windows.odin @@ -54,7 +54,7 @@ _resolve :: proc(ctx: ^Context, frame: Frame, allocator: runtime.Allocator) -> ( symbol.SizeOfStruct = size_of(symbol^) symbol.MaxNameLen = 255 if win32.SymFromAddrW(ctx.impl.hProcess, win32.DWORD64(frame), &{}, symbol) { - fl.procedure, _ = win32.wstring_to_utf8(&symbol.Name[0], -1, allocator) + fl.procedure, _ = win32.wstring_to_utf8(cstring16(&symbol.Name[0]), -1, allocator) } else { fl.procedure = fmt.aprintf("(procedure: 0x%x)", frame, allocator=allocator) } diff --git a/core/dynlib/lib_windows.odin b/core/dynlib/lib_windows.odin index 05cd2cb3c..95372dac6 100644 --- a/core/dynlib/lib_windows.odin +++ b/core/dynlib/lib_windows.odin @@ -13,7 +13,7 @@ _LIBRARY_FILE_EXTENSION :: "dll" _load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) { // NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL wide_path := win32.utf8_to_wstring(path, allocator) - defer free(wide_path, allocator) + defer free(rawptr(wide_path), allocator) handle := cast(Library)win32.LoadLibraryW(wide_path) return handle, handle != nil } diff --git a/core/mem/virtual/virtual_windows.odin b/core/mem/virtual/virtual_windows.odin index 0da8498d5..3fd4eeb68 100644 --- a/core/mem/virtual/virtual_windows.odin +++ b/core/mem/virtual/virtual_windows.odin @@ -72,7 +72,7 @@ foreign Kernel32 { flProtect: u32, dwMaximumSizeHigh: u32, dwMaximumSizeLow: u32, - lpName: [^]u16, + lpName: cstring16, ) -> rawptr --- MapViewOfFile :: proc( diff --git a/core/os/dir_windows.odin b/core/os/dir_windows.odin index ae3e6922c..40f4b9e9b 100644 --- a/core/os/dir_windows.odin +++ b/core/os/dir_windows.odin @@ -87,7 +87,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F defer delete(path) find_data := &win32.WIN32_FIND_DATAW{} - find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data) + find_handle := win32.FindFirstFileW(cstring16(raw_data(wpath_search)), find_data) if find_handle == win32.INVALID_HANDLE_VALUE { err = get_last_error() return dfi[:], err diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 40d012183..1134e765c 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -619,7 +619,7 @@ _symlink :: proc(old_name, new_name: string) -> Error { return .Unsupported } -_open_sym_link :: proc(p: [^]u16) -> (handle: win32.HANDLE, err: Error) { +_open_sym_link :: proc(p: cstring16) -> (handle: win32.HANDLE, err: Error) { attrs := u32(win32.FILE_FLAG_BACKUP_SEMANTICS) attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT handle = win32.CreateFileW(p, 0, 0, nil, win32.OPEN_EXISTING, attrs, nil) diff --git a/core/os/stat_windows.odin b/core/os/stat_windows.odin index ca4f87668..662c9f9e6 100644 --- a/core/os/stat_windows.odin +++ b/core/os/stat_windows.odin @@ -17,7 +17,7 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa buf := make([dynamic]u16, 100) defer delete(buf) for { - n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil) + n := win32.GetFullPathNameW(cstring16(raw_data(p)), u32(len(buf)), cstring16(raw_data(buf)), nil) if n == 0 { return "", get_last_error() } @@ -154,7 +154,7 @@ cleanpath_from_handle_u16 :: proc(fd: Handle, allocator: runtime.Allocator) -> ( return nil, get_last_error() } buf := make([]u16, max(n, win32.DWORD(260))+1, allocator) - buf_len := win32.GetFinalPathNameByHandleW(h, raw_data(buf), n, 0) + buf_len := win32.GetFinalPathNameByHandleW(h, cstring16(raw_data(buf)), n, 0) return buf[:buf_len], nil } @(private, require_results) diff --git a/core/path/filepath/path_windows.odin b/core/path/filepath/path_windows.odin index 0dcb28cf8..24c6e00a5 100644 --- a/core/path/filepath/path_windows.odin +++ b/core/path/filepath/path_windows.odin @@ -61,13 +61,13 @@ temp_full_path :: proc(name: string) -> (path: string, err: os.Error) { } p := win32.utf8_to_utf16(name, ta) - n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil) + n := win32.GetFullPathNameW(cstring16(raw_data(p)), 0, nil, nil) if n == 0 { return "", os.get_last_error() } buf := make([]u16, n, ta) - n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil) + n = win32.GetFullPathNameW(cstring16(raw_data(p)), u32(len(buf)), cstring16(raw_data(buf)), nil) if n == 0 { delete(buf) return "", os.get_last_error() diff --git a/core/sys/info/platform_windows.odin b/core/sys/info/platform_windows.odin index 4c00ddadf..dd1441d30 100644 --- a/core/sys/info/platform_windows.odin +++ b/core/sys/info/platform_windows.odin @@ -324,8 +324,8 @@ read_reg_string :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: string, ok status := sys.RegGetValueW( hkey, - &key_name_wide[0], - &val_name_wide[0], + cstring16(&key_name_wide[0]), + cstring16(&val_name_wide[0]), sys.RRF_RT_REG_SZ, nil, raw_data(result_wide[:]), @@ -359,8 +359,8 @@ read_reg_i32 :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: i32, ok: bool result_size := sys.DWORD(size_of(i32)) status := sys.RegGetValueW( hkey, - &key_name_wide[0], - &val_name_wide[0], + cstring16(&key_name_wide[0]), + cstring16(&val_name_wide[0]), sys.RRF_RT_REG_DWORD, nil, &res, @@ -386,8 +386,8 @@ read_reg_i64 :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: i64, ok: bool result_size := sys.DWORD(size_of(i64)) status := sys.RegGetValueW( hkey, - &key_name_wide[0], - &val_name_wide[0], + cstring16(&key_name_wide[0]), + cstring16(&val_name_wide[0]), sys.RRF_RT_REG_QWORD, nil, &res, diff --git a/core/sys/windows/comctl32.odin b/core/sys/windows/comctl32.odin index d954f952c..c7a166634 100644 --- a/core/sys/windows/comctl32.odin +++ b/core/sys/windows/comctl32.odin @@ -573,10 +573,10 @@ Button_GetTextMargin :: #force_inline proc "system" (hwnd: HWND, pmargin: ^RECT) return cast(BOOL)SendMessageW(hwnd, BCM_GETTEXTMARGIN, 0, cast(LPARAM)uintptr(pmargin)) } Button_SetNote :: #force_inline proc "system" (hwnd: HWND, psz: LPCWSTR) -> BOOL { - return cast(BOOL)SendMessageW(hwnd, BCM_SETNOTE, 0, cast(LPARAM)uintptr(psz)) + return cast(BOOL)SendMessageW(hwnd, BCM_SETNOTE, 0, cast(LPARAM)uintptr(rawptr(psz))) } Button_GetNote :: #force_inline proc "system" (hwnd: HWND, psz: LPCWSTR, pcc: ^c_int) -> BOOL { - return cast(BOOL)SendMessageW(hwnd, BCM_GETNOTE, uintptr(pcc), cast(LPARAM)uintptr(psz)) + return cast(BOOL)SendMessageW(hwnd, BCM_GETNOTE, uintptr(pcc), cast(LPARAM)uintptr(rawptr(psz))) } Button_GetNoteLength :: #force_inline proc "system" (hwnd: HWND) -> LRESULT { return SendMessageW(hwnd, BCM_GETNOTELENGTH, 0, 0) @@ -604,10 +604,10 @@ EDITBALLOONTIP :: struct { PEDITBALLOONTIP :: ^EDITBALLOONTIP Edit_SetCueBannerText :: #force_inline proc "system" (hwnd: HWND, lpcwText: LPCWSTR) -> BOOL { - return cast(BOOL)SendMessageW(hwnd, EM_SETCUEBANNER, 0, cast(LPARAM)uintptr(lpcwText)) + return cast(BOOL)SendMessageW(hwnd, EM_SETCUEBANNER, 0, cast(LPARAM)uintptr(rawptr(lpcwText))) } Edit_SetCueBannerTextFocused :: #force_inline proc "system" (hwnd: HWND, lpcwText: LPCWSTR, fDrawFocused: BOOL) -> BOOL { - return cast(BOOL)SendMessageW(hwnd, EM_SETCUEBANNER, cast(WPARAM)fDrawFocused, cast(LPARAM)uintptr(lpcwText)) + return cast(BOOL)SendMessageW(hwnd, EM_SETCUEBANNER, cast(WPARAM)fDrawFocused, cast(LPARAM)uintptr(rawptr(lpcwText))) } Edit_GetCueBannerText :: #force_inline proc "system" (hwnd: HWND, lpwText: LPWSTR, cchText: LONG) -> BOOL { return cast(BOOL)SendMessageW(hwnd, EM_GETCUEBANNER, uintptr(lpwText), cast(LPARAM)cchText) @@ -1197,7 +1197,7 @@ ListView_GetItemPosition :: #force_inline proc "system" (hwnd: HWND, i: c_int, p return cast(BOOL)SendMessageW(hwnd, LVM_GETITEMPOSITION, cast(WPARAM)i, cast(LPARAM)uintptr(ppt)) } ListView_GetStringWidth :: #force_inline proc "system" (hwndLV: HWND, psz: LPCWSTR) -> c_int { - return cast(c_int)SendMessageW(hwndLV, LVM_GETSTRINGWIDTHW, 0, cast(LPARAM)uintptr(psz)) + return cast(c_int)SendMessageW(hwndLV, LVM_GETSTRINGWIDTHW, 0, cast(LPARAM)uintptr(rawptr(psz))) } ListView_HitTest :: #force_inline proc "system" (hwndLV: HWND, pinfo: ^LV_HITTESTINFO) -> c_int { return cast(c_int)SendMessageW(hwndLV, LVM_HITTEST, 0, cast(LPARAM)uintptr(pinfo)) diff --git a/core/sys/windows/ip_helper.odin b/core/sys/windows/ip_helper.odin index 7a6e545ac..d2e75d531 100644 --- a/core/sys/windows/ip_helper.odin +++ b/core/sys/windows/ip_helper.odin @@ -38,9 +38,9 @@ IP_Adapter_Addresses :: struct { FirstAnycastAddress: ^IP_ADAPTER_ANYCAST_ADDRESS_XP, FirstMulticastAddress: ^IP_ADAPTER_MULTICAST_ADDRESS_XP, FirstDnsServerAddress: ^IP_ADAPTER_DNS_SERVER_ADDRESS_XP, - DnsSuffix: ^u16, - Description: ^u16, - FriendlyName: ^u16, + DnsSuffix: cstring16, + Description: cstring16, + FriendlyName: cstring16, PhysicalAddress: [8]u8, PhysicalAddressLength: u32, Anonymous2: struct #raw_union { diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index 92b1cb15c..be16d2fdd 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -107,8 +107,8 @@ PDWORD64 :: ^DWORD64 PDWORD_PTR :: ^DWORD_PTR ATOM :: distinct WORD -wstring :: [^]WCHAR -PWSTR :: [^]WCHAR +wstring :: cstring16 +PWSTR :: cstring16 PBYTE :: ^BYTE LPBYTE :: ^BYTE diff --git a/core/sys/windows/util.odin b/core/sys/windows/util.odin index 995e8e0e5..10dc907e7 100644 --- a/core/sys/windows/util.odin +++ b/core/sys/windows/util.odin @@ -122,14 +122,14 @@ utf8_to_utf16 :: proc{utf8_to_utf16_alloc, utf8_to_utf16_buf} utf8_to_wstring_alloc :: proc(s: string, allocator := context.temp_allocator) -> wstring { if res := utf8_to_utf16(s, allocator); len(res) > 0 { - return raw_data(res) + return wstring(raw_data(res)) } return nil } utf8_to_wstring_buf :: proc(buf: []u16, s: string) -> wstring { if res := utf8_to_utf16(buf, s); len(res) > 0 { - return raw_data(res) + return wstring(raw_data(res)) } return nil } @@ -215,7 +215,7 @@ utf16_to_utf8_alloc :: proc(s: []u16, allocator := context.temp_allocator) -> (r if len(s) == 0 { return "", nil } - return wstring_to_utf8(raw_data(s), len(s), allocator) + return wstring_to_utf8(wstring(raw_data(s)), len(s), allocator) } /* @@ -236,7 +236,7 @@ utf16_to_utf8_buf :: proc(buf: []u8, s: []u16) -> (res: string) { if len(s) == 0 { return } - return wstring_to_utf8(buf, raw_data(s), len(s)) + return wstring_to_utf8(buf, wstring(raw_data(s)), len(s)) } utf16_to_utf8 :: proc{utf16_to_utf8_alloc, utf16_to_utf8_buf} @@ -298,7 +298,7 @@ _add_user :: proc(servername: string, username: string, password: string) -> (ok servername_w = nil } else { server := utf8_to_utf16(servername, context.temp_allocator) - servername_w = &server[0] + servername_w = wstring(&server[0]) } if len(username) == 0 || len(username) > LM20_UNLEN { @@ -348,7 +348,7 @@ get_computer_name_and_account_sid :: proc(username: string) -> (computer_name: s res := LookupAccountNameW( nil, // Look on this computer first - &username_w[0], + wstring(&username_w[0]), &sid, &cbsid, nil, @@ -364,10 +364,10 @@ get_computer_name_and_account_sid :: proc(username: string) -> (computer_name: s res = LookupAccountNameW( nil, - &username_w[0], + wstring(&username_w[0]), &sid, &cbsid, - &cname_w[0], + wstring(&cname_w[0]), &computer_name_size, &pe_use, ) @@ -390,7 +390,7 @@ get_sid :: proc(username: string, sid: ^SID) -> (ok: bool) { res := LookupAccountNameW( nil, // Look on this computer first - &username_w[0], + wstring(&username_w[0]), sid, &cbsid, nil, @@ -406,10 +406,10 @@ get_sid :: proc(username: string, sid: ^SID) -> (ok: bool) { res = LookupAccountNameW( nil, - &username_w[0], + wstring(&username_w[0]), sid, &cbsid, - &cname_w[0], + wstring(&cname_w[0]), &computer_name_size, &pe_use, ) @@ -428,7 +428,7 @@ add_user_to_group :: proc(sid: ^SID, group: string) -> (ok: NET_API_STATUS) { group_name := utf8_to_utf16(group, context.temp_allocator) ok = NetLocalGroupAddMembers( nil, - &group_name[0], + wstring(&group_name[0]), 0, &group_member, 1, @@ -443,7 +443,7 @@ add_del_from_group :: proc(sid: ^SID, group: string) -> (ok: NET_API_STATUS) { group_name := utf8_to_utf16(group, context.temp_allocator) ok = NetLocalGroupDelMembers( nil, - &group_name[0], + cstring16(&group_name[0]), 0, &group_member, 1, @@ -465,19 +465,19 @@ add_user_profile :: proc(username: string) -> (ok: bool, profile_path: string) { if res == false { return false, "" } - defer LocalFree(sb) + defer LocalFree(rawptr(sb)) pszProfilePath := make([]u16, 257, context.temp_allocator) res2 := CreateProfile( sb, - &username_w[0], - &pszProfilePath[0], + cstring16(&username_w[0]), + cstring16(&pszProfilePath[0]), 257, ) if res2 != 0 { return false, "" } - profile_path = wstring_to_utf8(&pszProfilePath[0], 257) or_else "" + profile_path = wstring_to_utf8(wstring(&pszProfilePath[0]), 257) or_else "" return true, profile_path } @@ -495,7 +495,7 @@ delete_user_profile :: proc(username: string) -> (ok: bool) { if res == false { return false } - defer LocalFree(sb) + defer LocalFree(rawptr(sb)) res2 := DeleteProfileW( sb, @@ -548,13 +548,13 @@ delete_user :: proc(servername: string, username: string) -> (ok: bool) { servername_w = nil } else { server := utf8_to_utf16(servername, context.temp_allocator) - servername_w = &server[0] + servername_w = wstring(&server[0]) } username_w := utf8_to_utf16(username) res := NetUserDel( servername_w, - &username_w[0], + wstring(&username_w[0]), ) if res != .Success { return false @@ -586,9 +586,9 @@ run_as_user :: proc(username, password, application, commandline: string, pi: ^P user_token: HANDLE ok = bool(LogonUserW( - lpszUsername = &username_w[0], - lpszDomain = &domain_w[0], - lpszPassword = &password_w[0], + lpszUsername = wstring(&username_w[0]), + lpszDomain = wstring(&domain_w[0]), + lpszPassword = wstring(&password_w[0]), dwLogonType = .NEW_CREDENTIALS, dwLogonProvider = .WINNT50, phToken = &user_token, @@ -605,8 +605,8 @@ run_as_user :: proc(username, password, application, commandline: string, pi: ^P ok = bool(CreateProcessAsUserW( user_token, - &app_w[0], - &commandline_w[0], + wstring(&app_w[0]), + wstring(&commandline_w[0]), nil, // lpProcessAttributes, nil, // lpThreadAttributes, false, // bInheritHandles, diff --git a/core/time/timezone/tz_windows.odin b/core/time/timezone/tz_windows.odin index 8dc5f533c..fe00719a2 100644 --- a/core/time/timezone/tz_windows.odin +++ b/core/time/timezone/tz_windows.odin @@ -159,9 +159,9 @@ iana_to_windows_tz :: proc(iana_name: string, allocator := context.allocator) -> status: windows.UError iana_name_wstr := windows.utf8_to_wstring(iana_name, allocator) - defer free(iana_name_wstr, allocator) + defer free(rawptr(iana_name_wstr), allocator) - wintz_name_len := windows.ucal_getWindowsTimeZoneID(iana_name_wstr, -1, raw_data(wintz_name_buffer[:]), len(wintz_name_buffer), &status) + wintz_name_len := windows.ucal_getWindowsTimeZoneID(iana_name_wstr, -1, cstring16(raw_data(wintz_name_buffer[:])), len(wintz_name_buffer), &status) if status != .U_ZERO_ERROR { return } @@ -178,7 +178,7 @@ local_tz_name :: proc(allocator := context.allocator) -> (name: string, success: iana_name_buffer: [128]u16 status: windows.UError - zone_str_len := windows.ucal_getDefaultTimeZone(raw_data(iana_name_buffer[:]), len(iana_name_buffer), &status) + zone_str_len := windows.ucal_getDefaultTimeZone(cstring16(raw_data(iana_name_buffer[:])), len(iana_name_buffer), &status) if status != .U_ZERO_ERROR { return } @@ -291,7 +291,7 @@ _region_load :: proc(reg_str: string, allocator := context.allocator) -> (out_re defer delete(tz_key, allocator) tz_key_wstr := windows.utf8_to_wstring(tz_key, allocator) - defer free(tz_key_wstr, allocator) + defer free(rawptr(tz_key_wstr), allocator) key: windows.HKEY res := windows.RegOpenKeyExW(windows.HKEY_LOCAL_MACHINE, tz_key_wstr, 0, windows.KEY_READ, &key) diff --git a/core/unicode/utf16/utf16.odin b/core/unicode/utf16/utf16.odin index 9a8cfe438..d3f98584b 100644 --- a/core/unicode/utf16/utf16.odin +++ b/core/unicode/utf16/utf16.odin @@ -126,7 +126,37 @@ decode_rune_in_string :: proc(s: string16) -> (r: rune, width: int) { return } -rune_count :: proc(s: []u16) -> (n: int) { +string_to_runes :: proc "odin" (s: string16, allocator := context.allocator) -> (runes: []rune) { + n := rune_count(s) + + runes = make([]rune, n, allocator) + i := 0 + for r in s { + runes[i] = r + i += 1 + } + return +} + + +rune_count :: proc{ + rune_count_in_string, + rune_count_in_slice, +} +rune_count_in_string :: proc(s: string16) -> (n: int) { + for i := 0; i < len(s); i += 1 { + c := s[i] + if _surr1 <= c && c < _surr2 && i+1 < len(s) && + _surr2 <= s[i+1] && s[i+1] < _surr3 { + i += 1 + } + n += 1 + } + return +} + + +rune_count_in_slice :: proc(s: []u16) -> (n: int) { for i := 0; i < len(s); i += 1 { c := s[i] if _surr1 <= c && c < _surr2 && i+1 < len(s) && diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 4abace637..66ea0cfbd 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -7179,7 +7179,11 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return false; } operand->mode = Addressing_Value; - operand->type = alloc_type_multi_pointer(t_u16); + if (type_hint != nullptr && is_type_cstring16(type_hint)) { + operand->type = type_hint; + } else { + operand->type = alloc_type_multi_pointer(t_u16); + } operand->value = {}; break; } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8d2e4d637..34149f92b 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3426,7 +3426,7 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type if (are_types_identical(src, t_cstring16) && is_type_u16_multi_ptr(dst)) { return !is_constant; } - // cstring -> rawptr + // cstring16 -> rawptr if (are_types_identical(src, t_cstring16) && is_type_rawptr(dst)) { return !is_constant; } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index bc9b6c5dd..ae88ff333 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -974,7 +974,14 @@ gb_internal void check_unroll_range_stmt(CheckerContext *ctx, Ast *node, u32 mod Type *t = base_type(operand.type); switch (t->kind) { case Type_Basic: - if (is_type_string(t) && t->Basic.kind != Basic_cstring) { + if (is_type_string16(t) && t->Basic.kind != Basic_cstring) { + val0 = t_rune; + val1 = t_int; + inline_for_depth = exact_value_i64(operand.value.value_string.len); + if (unroll_count > 0) { + error(node, "#unroll(%lld) does not support strings", cast(long long)unroll_count); + } + } else if (is_type_string(t) && t->Basic.kind != Basic_cstring) { val0 = t_rune; val1 = t_int; inline_for_depth = exact_value_i64(operand.value.value_string.len); @@ -1236,7 +1243,11 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags add_to_seen_map(ctx, &seen, upper_op, x, lhs, rhs); - if (is_type_string(x.type)) { + if (is_type_string16(x.type)) { + // NOTE(bill): Force dependency for strings here + add_package_dependency(ctx, "runtime", "string16_le"); + add_package_dependency(ctx, "runtime", "string16_lt"); + } else if (is_type_string(x.type)) { // NOTE(bill): Force dependency for strings here add_package_dependency(ctx, "runtime", "string_le"); add_package_dependency(ctx, "runtime", "string_lt"); @@ -1770,7 +1781,16 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) switch (t->kind) { case Type_Basic: - if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) { + if (t->Basic.kind == Basic_string16) { + is_possibly_addressable = false; + array_add(&vals, t_rune); + array_add(&vals, t_int); + if (is_reverse) { + add_package_dependency(ctx, "runtime", "string16_decode_last_rune"); + } else { + add_package_dependency(ctx, "runtime", "string16_decode_rune"); + } + } else if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) { is_possibly_addressable = false; array_add(&vals, t_rune); array_add(&vals, t_int); diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 027837f3f..5481ca447 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -622,6 +622,121 @@ gb_internal void lb_build_range_string(lbProcedure *p, lbValue expr, Type *val_t if (done_) *done_ = done; } +gb_internal void lb_build_range_string16(lbProcedure *p, lbValue expr, Type *val_type, + lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_, + bool is_reverse) { + + lbModule *m = p->module; + lbValue count = lb_const_int(m, t_int, 0); + Type *expr_type = base_type(expr.type); + switch (expr_type->kind) { + case Type_Basic: + count = lb_string_len(p, expr); + break; + default: + GB_PANIC("Cannot do range_string of %s", type_to_string(expr_type)); + break; + } + + lbValue val = {}; + lbValue idx = {}; + lbBlock *loop = nullptr; + lbBlock *done = nullptr; + lbBlock *body = nullptr; + + loop = lb_create_block(p, "for.string16.loop"); + body = lb_create_block(p, "for.string16.body"); + done = lb_create_block(p, "for.string16.done"); + + lbAddr offset_ = lb_add_local_generated(p, t_int, false); + lbValue offset = {}; + lbValue cond = {}; + + if (!is_reverse) { + /* + for c, offset in str { + ... + } + + offset := 0 + for offset < len(str) { + c, _w := string16_decode_rune(str[offset:]) + ... + offset += _w + } + */ + lb_addr_store(p, offset_, lb_const_int(m, t_int, 0)); + + lb_emit_jump(p, loop); + lb_start_block(p, loop); + + + offset = lb_addr_load(p, offset_); + cond = lb_emit_comp(p, Token_Lt, offset, count); + } else { + // NOTE(bill): REVERSED LOGIC + /* + #reverse for c, offset in str { + ... + } + + offset := len(str) + for offset > 0 { + c, _w := string16_decode_last_rune(str[:offset]) + offset -= _w + ... + } + */ + lb_addr_store(p, offset_, count); + + lb_emit_jump(p, loop); + lb_start_block(p, loop); + + offset = lb_addr_load(p, offset_); + cond = lb_emit_comp(p, Token_Gt, offset, lb_const_int(m, t_int, 0)); + } + lb_emit_if(p, cond, body, done); + lb_start_block(p, body); + + + lbValue rune_and_len = {}; + if (!is_reverse) { + lbValue str_elem = lb_emit_ptr_offset(p, lb_string_elem(p, expr), offset); + lbValue str_len = lb_emit_arith(p, Token_Sub, count, offset, t_int); + auto args = array_make(permanent_allocator(), 1); + args[0] = lb_emit_string16(p, str_elem, str_len); + + rune_and_len = lb_emit_runtime_call(p, "string16_decode_rune", args); + lbValue len = lb_emit_struct_ev(p, rune_and_len, 1); + lb_addr_store(p, offset_, lb_emit_arith(p, Token_Add, offset, len, t_int)); + + idx = offset; + } else { + // NOTE(bill): REVERSED LOGIC + lbValue str_elem = lb_string_elem(p, expr); + lbValue str_len = offset; + auto args = array_make(permanent_allocator(), 1); + args[0] = lb_emit_string16(p, str_elem, str_len); + + rune_and_len = lb_emit_runtime_call(p, "string16_decode_last_rune", args); + lbValue len = lb_emit_struct_ev(p, rune_and_len, 1); + lb_addr_store(p, offset_, lb_emit_arith(p, Token_Sub, offset, len, t_int)); + + idx = lb_addr_load(p, offset_); + } + + + if (val_type != nullptr) { + val = lb_emit_struct_ev(p, rune_and_len, 0); + } + + if (val_) *val_ = val; + if (idx_) *idx_ = idx; + if (loop_) *loop_ = loop; + if (done_) *done_ = done; +} + + gb_internal Ast *lb_strip_and_prefix(Ast *ident) { if (ident != nullptr) { @@ -1138,7 +1253,11 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc } Type *t = base_type(string.type); GB_ASSERT(!is_type_cstring(t)); - lb_build_range_string(p, string, val0_type, &val, &key, &loop, &done, rs->reverse); + if (is_type_string16(t)) { + lb_build_range_string16(p, string, val0_type, &val, &key, &loop, &done, rs->reverse); + } else { + lb_build_range_string(p, string, val0_type, &val, &key, &loop, &done, rs->reverse); + } break; } case Type_Tuple: diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index ea1bae4e9..dcb95a9a2 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -191,6 +191,23 @@ gb_internal lbValue lb_emit_clamp(lbProcedure *p, Type *t, lbValue x, lbValue mi return z; } +gb_internal lbValue lb_emit_string16(lbProcedure *p, lbValue str_elem, lbValue str_len) { + if (false && lb_is_const(str_elem) && lb_is_const(str_len)) { + LLVMValueRef values[2] = { + str_elem.value, + str_len.value, + }; + lbValue res = {}; + res.type = t_string16; + res.value = llvm_const_named_struct(p->module, t_string16, values, gb_count_of(values)); + return res; + } else { + lbAddr res = lb_add_local_generated(p, t_string16, false); + lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 0), str_elem); + lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 1), str_len); + return lb_addr_load(p, res); + } +} gb_internal lbValue lb_emit_string(lbProcedure *p, lbValue str_elem, lbValue str_len) { -- cgit v1.2.3 From 21b1173076cec12f97c5779556509ef1b908c644 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Sep 2025 18:20:20 +0100 Subject: Minor clean up of permanent/temporary arena usage --- src/build_settings.cpp | 10 +++++----- src/bundle_command.cpp | 2 +- src/check_builtin.cpp | 26 +++++++++++++------------- src/check_expr.cpp | 15 +++++++-------- src/check_stmt.cpp | 2 +- src/check_type.cpp | 24 ++++++++++++------------ src/checker.cpp | 6 +++--- src/common_memory.cpp | 44 +++++++++++++++++++++++++++++++++++++++++++- src/gb/gb.h | 24 +++++++++++++----------- 9 files changed, 98 insertions(+), 55 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 4bee0ad4e..0081fabee 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1126,7 +1126,7 @@ gb_internal String internal_odin_root_dir(void) { mutex_lock(&string_buffer_mutex); defer (mutex_unlock(&string_buffer_mutex)); - text = gb_alloc_array(permanent_allocator(), wchar_t, len+1); + text = permanent_alloc_array(len+1); GetModuleFileNameW(nullptr, text, cast(int)len); path = string16_to_string(heap_allocator(), make_string16(cast(u16 *)text, len)); @@ -1181,7 +1181,7 @@ gb_internal String internal_odin_root_dir(void) { mutex_lock(&string_buffer_mutex); defer (mutex_unlock(&string_buffer_mutex)); - text = gb_alloc_array(permanent_allocator(), u8, len + 1); + text = permanent_alloc_array(len + 1); gb_memmove(text, &path_buf[0], len); path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr); @@ -1233,7 +1233,7 @@ gb_internal String internal_odin_root_dir(void) { mutex_lock(&string_buffer_mutex); defer (mutex_unlock(&string_buffer_mutex)); - text = gb_alloc_array(permanent_allocator(), u8, len + 1); + text = permanent_alloc_array(len + 1); gb_memmove(text, &path_buf[0], len); path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr); @@ -1394,7 +1394,7 @@ gb_internal String internal_odin_root_dir(void) { mutex_lock(&string_buffer_mutex); defer (mutex_unlock(&string_buffer_mutex)); - text = gb_alloc_array(permanent_allocator(), u8, len + 1); + text = permanent_alloc_array(len + 1); gb_memmove(text, &path_buf[0], len); @@ -1429,7 +1429,7 @@ gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_) { len = GetFullPathNameW(cast(wchar_t *)&string16[0], 0, nullptr, nullptr); if (len != 0) { - wchar_t *text = gb_alloc_array(permanent_allocator(), wchar_t, len+1); + wchar_t *text = permanent_alloc_array(len+1); GetFullPathNameW(cast(wchar_t *)&string16[0], len, text, nullptr); mutex_unlock(&fullpath_mutex); diff --git a/src/bundle_command.cpp b/src/bundle_command.cpp index cd0cd589f..7abd48104 100644 --- a/src/bundle_command.cpp +++ b/src/bundle_command.cpp @@ -83,7 +83,7 @@ i32 bundle_android(String original_init_directory) { return 1; } - int *dir_numbers = gb_alloc_array(temporary_allocator(), int, possible_valid_dirs.count); + int *dir_numbers = temporary_alloc_array(possible_valid_dirs.count); char buf[1024] = {}; for_array(i, possible_valid_dirs) { diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index a08382c9a..7e1567750 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -354,7 +354,7 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan } isize const arg_offset = 1; - auto param_types = slice_make(permanent_allocator(), ce->args.count-arg_offset); + auto param_types = permanent_slice_make(ce->args.count-arg_offset); param_types[0] = t_objc_id; param_types[1] = sel_type; @@ -462,7 +462,7 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan { // NOTE(harold): The last argument specified in the call is the handler proc, // any other arguments before it are capture by-copy arguments. - auto param_operands = slice_make(permanent_allocator(), ce->args.count); + auto param_operands = permanent_slice_make(ce->args.count); isize capture_arg_count = ce->args.count - 1; @@ -3554,7 +3554,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } case BuiltinProc_compress_values: { - Operand *ops = gb_alloc_array(temporary_allocator(), Operand, ce->args.count); + Operand *ops = temporary_alloc_array(ce->args.count); isize value_count = 0; @@ -3685,9 +3685,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As operand->mode = Addressing_Value; } else { Type *st = alloc_type_struct_complete(); - st->Struct.fields = slice_make(permanent_allocator(), value_count); - st->Struct.tags = gb_alloc_array(permanent_allocator(), String, value_count); - st->Struct.offsets = gb_alloc_array(permanent_allocator(), i64, value_count); + st->Struct.fields = permanent_slice_make(value_count); + st->Struct.tags = permanent_alloc_array(value_count); + st->Struct.offsets = permanent_alloc_array(value_count); Scope *scope = create_scope(c->info, nullptr); @@ -4387,7 +4387,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As elem = alloc_type_struct(); elem->Struct.scope = s; elem->Struct.fields = slice_from_array(fields); - elem->Struct.tags = gb_alloc_array(permanent_allocator(), String, fields.count); + elem->Struct.tags = permanent_alloc_array(fields.count); elem->Struct.node = dummy_node_struct; type_set_offsets(elem); wait_signal_set(&elem->Struct.fields_wait_signal); @@ -4419,7 +4419,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As gb_string_free(s); return false; } - auto types = slice_make(permanent_allocator(), t->Struct.fields.count-1); + auto types = permanent_slice_make(t->Struct.fields.count-1); for_array(i, types) { Entity *f = t->Struct.fields[i]; GB_ASSERT(f->type->kind == Type_MultiPointer); @@ -4755,8 +4755,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As if (is_type_array(elem)) { Type *old_array = base_type(elem); soa_struct = alloc_type_struct(); - soa_struct->Struct.fields = slice_make(heap_allocator(), cast(isize)old_array->Array.count); - soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, cast(isize)old_array->Array.count); + soa_struct->Struct.fields = permanent_slice_make(cast(isize)old_array->Array.count); + soa_struct->Struct.tags = permanent_alloc_array(cast(isize)old_array->Array.count); soa_struct->Struct.node = operand->expr; soa_struct->Struct.soa_kind = StructSoa_Fixed; soa_struct->Struct.soa_elem = elem; @@ -4788,8 +4788,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As Type *old_struct = base_type(elem); soa_struct = alloc_type_struct(); - soa_struct->Struct.fields = slice_make(heap_allocator(), old_struct->Struct.fields.count); - soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, old_struct->Struct.fields.count); + soa_struct->Struct.fields = permanent_slice_make(old_struct->Struct.fields.count); + soa_struct->Struct.tags = permanent_alloc_array(old_struct->Struct.fields.count); soa_struct->Struct.node = operand->expr; soa_struct->Struct.soa_kind = StructSoa_Fixed; soa_struct->Struct.soa_elem = elem; @@ -6181,7 +6181,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } Type *new_type = alloc_type_union(); - auto variants = slice_make(permanent_allocator(), bt->Union.variants.count); + auto variants = permanent_slice_make(bt->Union.variants.count); for_array(i, bt->Union.variants) { variants[i] = alloc_type_pointer(bt->Union.variants[i]); } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index e005b0bd0..84f1c6f0a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4639,7 +4639,6 @@ gb_internal ExactValue convert_exact_value_for_type(ExactValue v, Type *type) { } gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) { - // GB_ASSERT_NOT_NULL(target_type); if (target_type == nullptr || operand->mode == Addressing_Invalid || operand->mode == Addressing_Type || is_type_typed(operand->type) || @@ -4810,7 +4809,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar TEMPORARY_ALLOCATOR_GUARD(); isize count = t->Union.variants.count; - ValidIndexAndScore *valids = gb_alloc_array(temporary_allocator(), ValidIndexAndScore, count); + ValidIndexAndScore *valids = temporary_alloc_array(count); isize valid_count = 0; isize first_success_index = -1; for_array(i, t->Union.variants) { @@ -6257,7 +6256,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A } GB_ASSERT(ce->split_args); - auto visited = slice_make(temporary_allocator(), pt->param_count); + auto visited = temporary_slice_make(pt->param_count); auto ordered_operands = array_make(temporary_allocator(), pt->param_count); defer ({ for (Operand const &o : ordered_operands) { @@ -7254,7 +7253,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, } // Try to reduce the list further for `$T: typeid` like parameters - bool *possibly_ignore = gb_alloc_array(temporary_allocator(), bool, procs.count); + bool *possibly_ignore = temporary_alloc_array(procs.count); isize possibly_ignore_set = 0; if (true) { @@ -7342,7 +7341,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, } isize max_spaces = gb_max(max_name_length, max_type_length); - char *spaces = gb_alloc_array(temporary_allocator(), char, max_spaces+1); + char *spaces = temporary_alloc_array(max_spaces+1); for (isize i = 0; i < max_spaces; i++) { spaces[i] = ' '; } @@ -7706,7 +7705,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O } else { TEMPORARY_ALLOCATOR_GUARD(); - bool *visited = gb_alloc_array(temporary_allocator(), bool, param_count); + bool *visited = temporary_alloc_array(param_count); // LEAK(bill) ordered_operands = array_make(permanent_allocator(), param_count); @@ -8881,7 +8880,7 @@ gb_internal void add_constant_switch_case(CheckerContext *ctx, SeenMap *seen, Op isize count = multi_map_count(seen, key); if (count) { TEMPORARY_ALLOCATOR_GUARD(); - TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count); + TypeAndToken *taps = temporary_alloc_array(count); multi_map_get_all(seen, key, taps); for (isize i = 0; i < count; i++) { @@ -10927,7 +10926,7 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast } } - auto modified_args = slice_make(heap_allocator(), ce->args.count+1); + auto modified_args = permanent_slice_make(ce->args.count+1); modified_args[0] = first_arg; slice_copy(&modified_args, ce->args, 1); ce->args = modified_args; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index ae88ff333..e03d4a7ae 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1963,7 +1963,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) } auto rhs = slice_from_array(vals); - auto lhs = slice_make(temporary_allocator(), rhs.count); + auto lhs = temporary_slice_make(rhs.count); slice_copy(&lhs, rs->vals); isize addressable_index = cast(isize)is_map; diff --git a/src/check_type.cpp b/src/check_type.cpp index a104d6fc0..4c995588f 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1106,7 +1106,7 @@ 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); + auto bit_offsets = permanent_slice_make(fields.count); i64 curr_offset = 0; for_array(i, bit_sizes) { bit_offsets[i] = curr_offset; @@ -2707,7 +2707,7 @@ gb_internal Type *get_map_cell_type(Type *type) { // Padding exists Type *s = alloc_type_struct(); Scope *scope = create_scope(nullptr, nullptr); - s->Struct.fields = slice_make(permanent_allocator(), 2); + s->Struct.fields = permanent_slice_make(2); 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; @@ -2732,7 +2732,7 @@ gb_internal void init_map_internal_debug_types(Type *type) { Type *metadata_type = alloc_type_struct(); Scope *metadata_scope = create_scope(nullptr, nullptr); - metadata_type->Struct.fields = slice_make(permanent_allocator(), 5); + metadata_type->Struct.fields = permanent_slice_make(5); metadata_type->Struct.fields[0] = alloc_entity_field(metadata_scope, make_token_ident("key"), key, false, 0, EntityState_Resolved); metadata_type->Struct.fields[1] = alloc_entity_field(metadata_scope, make_token_ident("value"), value, false, 1, EntityState_Resolved); metadata_type->Struct.fields[2] = alloc_entity_field(metadata_scope, make_token_ident("hash"), t_uintptr, false, 2, EntityState_Resolved); @@ -2750,7 +2750,7 @@ gb_internal void init_map_internal_debug_types(Type *type) { Scope *scope = create_scope(nullptr, nullptr); Type *debug_type = alloc_type_struct(); - debug_type->Struct.fields = slice_make(permanent_allocator(), 3); + debug_type->Struct.fields = permanent_slice_make(3); debug_type->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("data"), metadata_type, false, 0, EntityState_Resolved); debug_type->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("len"), t_int, false, 1, EntityState_Resolved); debug_type->Struct.fields[2] = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, 2, EntityState_Resolved); @@ -2983,13 +2983,13 @@ gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finis 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()); + GB_ASSERT(old_struct->Struct.fields_wait_signal.futex.load() != 0); } 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); + t->Struct.fields = permanent_slice_make(field_count+extra_field_count); + t->Struct.tags = permanent_alloc_array(field_count+extra_field_count); auto const &add_entity = [](Scope *scope, Entity *entity) { @@ -3107,7 +3107,7 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e if (is_polymorphic) { field_count = 0; - soa_struct->Struct.fields = slice_make(permanent_allocator(), field_count+extra_field_count); + soa_struct->Struct.fields = permanent_slice_make(field_count+extra_field_count); soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); soa_struct->Struct.soa_count = 0; @@ -3117,7 +3117,7 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e Type *old_array = base_type(elem); field_count = cast(isize)old_array->Array.count; - soa_struct->Struct.fields = slice_make(permanent_allocator(), field_count+extra_field_count); + soa_struct->Struct.fields = permanent_slice_make(field_count+extra_field_count); soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); string_map_init(&scope->elements, 8); @@ -3159,8 +3159,8 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e if (old_struct->Struct.fields_wait_signal.futex.load()) { field_count = old_struct->Struct.fields.count; - soa_struct->Struct.fields = slice_make(permanent_allocator(), field_count+extra_field_count); - soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); + soa_struct->Struct.fields = permanent_slice_make(field_count+extra_field_count); + soa_struct->Struct.tags = permanent_alloc_array(field_count+extra_field_count); for_array(i, old_struct->Struct.fields) { Entity *old_field = old_struct->Struct.fields[i]; @@ -3355,7 +3355,7 @@ gb_internal void check_array_type_internal(CheckerContext *ctx, Ast *e, Type **t } } gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_type) { - GB_ASSERT_NOT_NULL(type); + GB_ASSERT(type != nullptr); if (e == nullptr) { *type = t_invalid; return true; diff --git a/src/checker.cpp b/src/checker.cpp index 0b54a3bbb..04e46d0e6 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -6177,7 +6177,7 @@ gb_internal bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *u switch (pi->decl->proc_checked_state.load()) { case ProcCheckedState_InProgress: if (e) { - GB_ASSERT(global_procedure_body_in_worker_queue.load()); + GB_ASSERT(global_procedure_body_in_worker_queue.load() != 0); } return false; case ProcCheckedState_Checked: @@ -6431,7 +6431,7 @@ gb_internal WORKER_TASK_PROC(check_proc_info_worker_proc) { gb_internal void check_init_worker_data(Checker *c) { u32 thread_count = cast(u32)global_thread_pool.threads.count; - check_procedure_bodies_worker_data = gb_alloc_array(permanent_allocator(), CheckProcedureBodyWorkerData, thread_count); + check_procedure_bodies_worker_data = permanent_alloc_array(thread_count); for (isize i = 0; i < thread_count; i++) { check_procedure_bodies_worker_data[i].c = c; @@ -6493,7 +6493,7 @@ gb_internal Type *tuple_to_pointers(Type *ot) { Type *t = alloc_type_tuple(); - t->Tuple.variables = slice_make(heap_allocator(), ot->Tuple.variables.count); + t->Tuple.variables = permanent_slice_make(ot->Tuple.variables.count); Scope *scope = nullptr; for_array(i, t->Tuple.variables) { diff --git a/src/common_memory.cpp b/src/common_memory.cpp index 9e0222bc4..0b95e1242 100644 --- a/src/common_memory.cpp +++ b/src/common_memory.cpp @@ -353,7 +353,7 @@ gb_internal gbAllocator arena_allocator(Arena *arena) { gb_internal GB_ALLOCATOR_PROC(arena_allocator_proc) { void *ptr = nullptr; Arena *arena = cast(Arena *)allocator_data; - GB_ASSERT_NOT_NULL(arena); + GB_ASSERT(arena != nullptr); switch (type) { case gbAllocation_Alloc: @@ -401,6 +401,48 @@ gb_internal Arena *get_arena(ThreadArenaKind kind) { } +template +gb_internal T *permanent_alloc_item() { + Arena *arena = get_arena(ThreadArena_Permanent); + return arena_alloc_item(arena); +} + +template +gb_internal T *permanent_alloc_array(isize count) { + Arena *arena = get_arena(ThreadArena_Permanent); + return cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T)); +} + +template +gb_internal Slice permanent_slice_make(isize count) { + Arena *arena = get_arena(ThreadArena_Permanent); + T *data = cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T)); + return {data, count}; +} + +template +gb_internal T *temporary_alloc_item() { + Arena *arena = get_arena(ThreadArena_Temporary); + return arena_alloc_item(arena); +} + +template +gb_internal T *temporary_alloc_array(isize count) { + Arena *arena = get_arena(ThreadArena_Temporary); + return cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T)); +} + +template +gb_internal Slice temporary_slice_make(isize count) { + Arena *arena = get_arena(ThreadArena_Temporary); + T *data = cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T)); + return {data, count}; +} + + + + + gb_internal GB_ALLOCATOR_PROC(thread_arena_allocator_proc) { void *ptr = nullptr; diff --git a/src/gb/gb.h b/src/gb/gb.h index ffc40b8ca..c52f63cec 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -714,13 +714,15 @@ extern "C++" { } while (0) #endif + +#if defined(DISABLE_ASSERT) +#define GB_ASSERT(cond) gb_unused(cond) +#endif + #ifndef GB_ASSERT #define GB_ASSERT(cond) GB_ASSERT_MSG(cond, NULL) #endif -#ifndef GB_ASSERT_NOT_NULL -#define GB_ASSERT_NOT_NULL(ptr) GB_ASSERT_MSG((ptr) != NULL, #ptr " must not be NULL") -#endif // NOTE(bill): Things that shouldn't happen with a message! #ifndef GB_PANIC @@ -3719,7 +3721,7 @@ gb_inline i32 gb_strcmp(char const *s1, char const *s2) { } gb_inline char *gb_strcpy(char *dest, char const *source) { - GB_ASSERT_NOT_NULL(dest); + GB_ASSERT(dest != NULL); if (source) { char *str = dest; while (*source) *str++ = *source++; @@ -3729,7 +3731,7 @@ gb_inline char *gb_strcpy(char *dest, char const *source) { gb_inline char *gb_strncpy(char *dest, char const *source, isize len) { - GB_ASSERT_NOT_NULL(dest); + GB_ASSERT(dest != NULL); if (source) { char *str = dest; while (len > 0 && *source) { @@ -3746,7 +3748,7 @@ gb_inline char *gb_strncpy(char *dest, char const *source, isize len) { gb_inline isize gb_strlcpy(char *dest, char const *source, isize len) { isize result = 0; - GB_ASSERT_NOT_NULL(dest); + GB_ASSERT(dest != NULL); if (source) { char const *source_start = source; char *str = dest; @@ -5636,7 +5638,7 @@ gbFileContents gb_file_read_contents(gbAllocator a, b32 zero_terminate, char con void gb_file_free_contents(gbFileContents *fc) { if (fc == NULL || fc->size == 0) return; - GB_ASSERT_NOT_NULL(fc->data); + GB_ASSERT(fc->data != NULL); gb_free(fc->allocator, fc->data); fc->data = NULL; fc->size = 0; @@ -5648,7 +5650,7 @@ void gb_file_free_contents(gbFileContents *fc) { gb_inline b32 gb_path_is_absolute(char const *path) { b32 result = false; - GB_ASSERT_NOT_NULL(path); + GB_ASSERT(path != NULL); #if defined(GB_SYSTEM_WINDOWS) result == (gb_strlen(path) > 2) && gb_char_is_alpha(path[0]) && @@ -5663,7 +5665,7 @@ gb_inline b32 gb_path_is_relative(char const *path) { return !gb_path_is_absolut gb_inline b32 gb_path_is_root(char const *path) { b32 result = false; - GB_ASSERT_NOT_NULL(path); + GB_ASSERT(path != NULL); #if defined(GB_SYSTEM_WINDOWS) result = gb_path_is_absolute(path) && (gb_strlen(path) == 3); #else @@ -5674,14 +5676,14 @@ gb_inline b32 gb_path_is_root(char const *path) { gb_inline char const *gb_path_base_name(char const *path) { char const *ls; - GB_ASSERT_NOT_NULL(path); + GB_ASSERT(path != NULL); ls = gb_char_last_occurence(path, '/'); return (ls == NULL) ? path : ls+1; } gb_inline char const *gb_path_extension(char const *path) { char const *ld; - GB_ASSERT_NOT_NULL(path); + GB_ASSERT(path != NULL); ld = gb_char_last_occurence(path, '.'); return (ld == NULL) ? NULL : ld+1; } -- cgit v1.2.3 From a36a8722dc823c6fe143f7935e79467c6569bc00 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Sep 2025 19:30:32 +0100 Subject: Minimize more thread contention --- src/build_settings.cpp | 2 +- src/check_decl.cpp | 4 ++-- src/check_expr.cpp | 16 ++++++-------- src/check_stmt.cpp | 5 +++-- src/check_type.cpp | 9 ++++---- src/checker.cpp | 58 +++++++++++++++++++++++++++----------------------- src/checker.hpp | 15 +++++++------ src/entity.cpp | 2 +- src/error.cpp | 11 ++++++++++ src/threading.cpp | 10 ++++----- 10 files changed, 74 insertions(+), 58 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 0081fabee..0c88f3d13 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1163,7 +1163,7 @@ gb_internal String internal_odin_root_dir(void) { return global_module_path; } - auto path_buf = array_make(heap_allocator(), 300); + auto path_buf = array_make(temporary_allocator(), 300); defer (array_free(&path_buf)); len = 0; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 7dd9db105..49731ad60 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1981,9 +1981,9 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de ast_node(bs, BlockStmt, body); + TEMPORARY_ALLOCATOR_GUARD(); Array using_entities = {}; - using_entities.allocator = heap_allocator(); - defer (array_free(&using_entities)); + using_entities.allocator = temporary_allocator(); { if (type->Proc.param_count > 0) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 84f1c6f0a..bdbccb4f8 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7516,11 +7516,10 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op return check_call_arguments_proc_group(c, operand, call); } - auto positional_operands = array_make(heap_allocator(), 0, positional_args.count); - auto named_operands = array_make(heap_allocator(), 0, 0); + TEMPORARY_ALLOCATOR_GUARD(); - defer (array_free(&positional_operands)); - defer (array_free(&named_operands)); + auto positional_operands = array_make(temporary_allocator(), 0, positional_args.count); + auto named_operands = array_make(temporary_allocator(), 0, 0); if (positional_args.count > 0) { Entity **lhs = nullptr; @@ -7623,11 +7622,10 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O { // NOTE(bill, 2019-10-26): Allow a cycle in the parameters but not in the fields themselves auto prev_type_path = c->type_path; - c->type_path = new_checker_type_path(); - defer ({ - destroy_checker_type_path(c->type_path); - c->type_path = prev_type_path; - }); + TEMPORARY_ALLOCATOR_GUARD(); + + c->type_path = new_checker_type_path(temporary_allocator()); + defer (c->type_path = prev_type_path); if (is_call_expr_field_value(ce)) { named_fields = true; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index e03d4a7ae..ba9c08fed 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2567,8 +2567,9 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) { result_count = proc_type->Proc.results->Tuple.variables.count; } - auto operands = array_make(heap_allocator(), 0, 2*rs->results.count); - defer (array_free(&operands)); + TEMPORARY_ALLOCATOR_GUARD(); + + auto operands = array_make(temporary_allocator(), 0, 2*rs->results.count); check_unpack_arguments(ctx, result_entities, result_count, &operands, rs->results, UnpackFlag_AllowOk); diff --git a/src/check_type.cpp b/src/check_type.cpp index 4c995588f..e99909d6b 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -3512,8 +3512,9 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T case_ast_node(pt, PointerType, e); CheckerContext c = *ctx; - c.type_path = new_checker_type_path(); - defer (destroy_checker_type_path(c.type_path)); + + TEMPORARY_ALLOCATOR_GUARD(); + c.type_path = new_checker_type_path(temporary_allocator()); Type *elem = t_invalid; Operand o = {}; @@ -3747,8 +3748,8 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T gb_internal Type *check_type(CheckerContext *ctx, Ast *e) { CheckerContext c = *ctx; - c.type_path = new_checker_type_path(); - defer (destroy_checker_type_path(c.type_path)); + TEMPORARY_ALLOCATOR_GUARD(); + c.type_path = new_checker_type_path(temporary_allocator()); return check_type_expr(&c, e, nullptr); } diff --git a/src/checker.cpp b/src/checker.cpp index 04e46d0e6..c1d6302ad 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1441,7 +1441,8 @@ gb_internal void init_checker_info(CheckerInfo *i) { map_init(&i->objc_method_implementations); string_map_init(&i->load_file_cache); - array_init(&i->all_procedures, heap_allocator()); + array_init(&i->all_procedures, a); + mpsc_init(&i->all_procedures_queue, a); mpsc_init(&i->entity_queue, a); // 1<<20); mpsc_init(&i->definition_queue, a); //); // 1<<20); @@ -1475,6 +1476,10 @@ gb_internal void destroy_checker_info(CheckerInfo *i) { array_free(&i->required_foreign_imports_through_force); array_free(&i->defineables); + array_free(&i->all_procedures); + + mpsc_destroy(&i->all_procedures_queue); + mpsc_destroy(&i->entity_queue); mpsc_destroy(&i->definition_queue); mpsc_destroy(&i->required_global_variable_queue); @@ -1502,12 +1507,12 @@ gb_internal CheckerContext make_checker_context(Checker *c) { ctx.scope = builtin_pkg->scope; ctx.pkg = builtin_pkg; - ctx.type_path = new_checker_type_path(); + ctx.type_path = new_checker_type_path(heap_allocator()); ctx.type_level = 0; return ctx; } gb_internal void destroy_checker_context(CheckerContext *ctx) { - destroy_checker_type_path(ctx->type_path); + destroy_checker_type_path(ctx->type_path, heap_allocator()); } gb_internal bool add_curr_ast_file(CheckerContext *ctx, AstFile *file) { @@ -1758,7 +1763,7 @@ gb_internal void add_untyped(CheckerContext *c, Ast *expr, AddressingMode mode, check_set_expr_info(c, expr, mode, type, value); } -gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMode mode, Type *type, ExactValue const &value) { +gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMode mode, Type *type, ExactValue const &value, bool use_mutex) { if (expr == nullptr) { return; } @@ -1776,7 +1781,7 @@ gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMo mutex = &ctx->pkg->type_and_value_mutex; } - mutex_lock(mutex); + if (use_mutex) mutex_lock(mutex); Ast *prev_expr = nullptr; while (prev_expr != expr) { prev_expr = expr; @@ -1801,7 +1806,7 @@ gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMo break; }; } - mutex_unlock(mutex); + if (use_mutex) mutex_unlock(mutex); } gb_internal void add_entity_definition(CheckerInfo *i, Ast *identifier, Entity *entity) { @@ -2345,11 +2350,9 @@ gb_internal void check_procedure_later(Checker *c, ProcInfo *info) { } if (DEBUG_CHECK_ALL_PROCEDURES) { - MUTEX_GUARD_BLOCK(&c->info.all_procedures_mutex) { - GB_ASSERT(info != nullptr); - GB_ASSERT(info->decl != nullptr); - array_add(&c->info.all_procedures, info); - } + GB_ASSERT(info != nullptr); + GB_ASSERT(info->decl != nullptr); + mpsc_enqueue(&c->info.all_procedures_queue, info); } } @@ -3195,19 +3198,17 @@ gb_internal Type *find_type_in_pkg(CheckerInfo *info, String const &pkg, String return e->type; } -gb_internal CheckerTypePath *new_checker_type_path() { - gbAllocator a = heap_allocator(); - auto *tp = gb_alloc_item(a, CheckerTypePath); - array_init(tp, a, 0, 16); +gb_internal CheckerTypePath *new_checker_type_path(gbAllocator allocator) { + auto *tp = gb_alloc_item(allocator, CheckerTypePath); + array_init(tp, allocator, 0, 16); return tp; } -gb_internal void destroy_checker_type_path(CheckerTypePath *tp) { +gb_internal void destroy_checker_type_path(CheckerTypePath *tp, gbAllocator allocator) { array_free(tp); - gb_free(heap_allocator(), tp); + gb_free(allocator, tp); } - gb_internal void check_type_path_push(CheckerContext *c, Entity *e) { GB_ASSERT(c->type_path != nullptr); GB_ASSERT(e != nullptr); @@ -5283,9 +5284,10 @@ gb_internal void check_add_import_decl(CheckerContext *ctx, Ast *decl) { GB_ASSERT(scope->flags&ScopeFlag_Pkg); - if (ptr_set_update(&parent_scope->imported, scope)) { - // error(token, "Multiple import of the same file within this scope"); - } + ptr_set_add(&parent_scope->imported, scope); + // if (ptr_set_update(&parent_scope->imported, scope)) { + // // error(token, "Multiple import of the same file within this scope"); + // } String import_name = path_to_entity_name(id->import_name.string, id->fullpath, false); if (is_blank_ident(import_name)) { @@ -6041,10 +6043,10 @@ gb_internal void calculate_global_init_order(Checker *c) { CheckerInfo *info = &c->info; TIME_SECTION("calculate_global_init_order: generate entity dependency graph"); - Arena *arena = get_arena(ThreadArena_Temporary); - ArenaTempGuard arena_guard(arena); + Arena *temporary_arena = get_arena(ThreadArena_Temporary); + ArenaTempGuard temporary_arena_guard(temporary_arena); - Array dep_graph = generate_entity_dependency_graph(info, arena); + Array dep_graph = generate_entity_dependency_graph(info, temporary_arena); TIME_SECTION("calculate_global_init_order: priority queue create"); // NOTE(bill): Priority queue @@ -6321,8 +6323,9 @@ gb_internal void check_safety_all_procedures_for_unchecked(Checker *c) { defer (map_destroy(&untyped)); - for_array(i, c->info.all_procedures) { - ProcInfo *pi = c->info.all_procedures[i]; + array_reserve(&c->info.all_procedures, c->info.all_procedures_queue.count.load()); + + for (ProcInfo *pi = nullptr; mpsc_dequeue(&c->info.all_procedures_queue, &pi); /**/) { GB_ASSERT(pi != nullptr); GB_ASSERT(pi->decl != nullptr); Entity *e = pi->decl->entity; @@ -6337,6 +6340,8 @@ gb_internal void check_safety_all_procedures_for_unchecked(Checker *c) { consume_proc_info(c, pi, &untyped); } } + + array_add(&c->info.all_procedures, pi); } } @@ -7412,7 +7417,6 @@ gb_internal void check_parsed_files(Checker *c) { TIME_SECTION("add untyped expression values"); - // Add untyped expression values for (UntypedExprInfo u = {}; mpsc_dequeue(&c->global_untyped_queue, &u); /**/) { GB_ASSERT(u.expr != nullptr && u.info != nullptr); if (is_type_typed(u.info->type)) { diff --git a/src/checker.hpp b/src/checker.hpp index 8b4d61ee2..5a40b10a0 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -309,11 +309,12 @@ struct EntityGraphNode; typedef PtrSet EntityGraphNodeSet; struct EntityGraphNode { - Entity * entity; // Procedure, Variable, Constant + Entity *entity; // Procedure, Variable, Constant + EntityGraphNodeSet pred; EntityGraphNodeSet succ; - isize index; // Index in array/queue - isize dep_count; + isize index; // Index in array/queue + isize dep_count; }; @@ -516,7 +517,7 @@ struct CheckerInfo { BlockingMutex load_file_mutex; StringMap load_file_cache; - BlockingMutex all_procedures_mutex; + MPSCQueue all_procedures_queue; Array all_procedures; BlockingMutex instrumentation_mutex; @@ -629,7 +630,7 @@ gb_internal void scope_lookup_parent (Scope *s, String const &name, Scope **s gb_internal Entity *scope_insert (Scope *s, Entity *entity); -gb_internal void add_type_and_value (CheckerContext *c, Ast *expression, AddressingMode mode, Type *type, ExactValue const &value); +gb_internal void add_type_and_value (CheckerContext *c, Ast *expression, AddressingMode mode, Type *type, ExactValue const &value, bool use_mutex=true); gb_internal ExprInfo *check_get_expr_info (CheckerContext *c, Ast *expr); gb_internal void add_untyped (CheckerContext *c, Ast *expression, AddressingMode mode, Type *basic_type, ExactValue const &value); gb_internal void add_entity_use (CheckerContext *c, Ast *identifier, Entity *entity); @@ -650,8 +651,8 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice const &n gb_internal void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws); gb_internal void check_delayed_file_import_entity(CheckerContext *c, Ast *decl); -gb_internal CheckerTypePath *new_checker_type_path(); -gb_internal void destroy_checker_type_path(CheckerTypePath *tp); +gb_internal CheckerTypePath *new_checker_type_path(gbAllocator allocator); +gb_internal void destroy_checker_type_path(CheckerTypePath *tp, gbAllocator allocator); gb_internal void check_type_path_push(CheckerContext *c, Entity *e); gb_internal Entity *check_type_path_pop (CheckerContext *c); diff --git a/src/entity.cpp b/src/entity.cpp index 5ca3fa916..e07e882f3 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -348,7 +348,7 @@ gb_internal Entity *alloc_entity(EntityKind kind, Scope *scope, Token token, Typ entity->type = type; entity->id = 1 + global_entity_id.fetch_add(1); if (token.pos.file_id) { - entity->file = thread_safe_get_ast_file_from_id(token.pos.file_id); + entity->file = thread_unsafe_get_ast_file_from_id(token.pos.file_id); } return entity; } diff --git a/src/error.cpp b/src/error.cpp index 10bf1caf5..53bc01654 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -153,6 +153,17 @@ gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) { } +// use AFTER PARSER +gb_internal AstFile *thread_unsafe_get_ast_file_from_id(i32 index) { + GB_ASSERT(index >= 0); + AstFile *file = nullptr; + if (index < global_files.count) { + file = global_files[index]; + } + return file; +} + + // NOTE: defined in build_settings.cpp gb_internal bool global_warnings_as_errors(void); diff --git a/src/threading.cpp b/src/threading.cpp index f1d9264e3..a35176ce6 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -448,9 +448,9 @@ gb_internal void semaphore_wait(Semaphore *s) { } #endif -static const int RWLOCK_WRITER = 1; -static const int RWLOCK_UPGRADED = 2; -static const int RWLOCK_READER = 4; +static const int RWLOCK_WRITER = 1<<0; +static const int RWLOCK_UPGRADED = 1<<1; +static const int RWLOCK_READER = 1<<2; struct RWSpinLock { Futex bits; }; @@ -467,7 +467,7 @@ bool rwlock_try_acquire_upgrade(RWSpinLock *l) { void rwlock_acquire_upgrade(RWSpinLock *l) { while (!rwlock_try_acquire_upgrade(l)) { - futex_wait(&l->bits, RWLOCK_UPGRADED); + futex_wait(&l->bits, RWLOCK_UPGRADED | RWLOCK_WRITER); } } void rwlock_release_upgrade(RWSpinLock *l) { @@ -481,7 +481,7 @@ bool rwlock_try_release_upgrade_and_acquire_write(RWSpinLock *l) { void rwlock_release_upgrade_and_acquire_write(RWSpinLock *l) { while (!rwlock_try_release_upgrade_and_acquire_write(l)) { - futex_wait(&l->bits, RWLOCK_WRITER); + futex_wait(&l->bits, RWLOCK_UPGRADED); } } -- 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/check_stmt.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 4165e8e8888f4894c039c0a55cf67c24fae83af0 Mon Sep 17 00:00:00 2001 From: Jwaxy <98110672+jwaxy@users.noreply.github.com> Date: Sat, 27 Sep 2025 13:53:04 +0300 Subject: Prevent returning struct containing compound literal slice --- src/check_stmt.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 62cfc256a..f509267fb 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2671,6 +2671,20 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) { ERROR_BLOCK(); unsafe_return_error(o, "a compound literal of a slice"); error_line("\tNote: A constant slice value will use the memory of the current stack frame\n"); + } else if (expr->kind == Ast_CompoundLit && is_type_struct(o.type)) { + ast_node(cl, CompoundLit, expr); + for (Ast *elem : cl->elems) { + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); + if (fv->value->kind == Ast_CompoundLit && is_type_slice(entity_of_node(fv->field)->type)) { + ast_node(sl, CompoundLit, fv->value); + if (sl->elems.count == 0) { + continue; + } + unsafe_return_error(o, "a compound literal containing a slice"); + } + } + } } } -- cgit v1.2.3 From 15fe0bfe599cd4300d3651b8e74b171cb50599f0 Mon Sep 17 00:00:00 2001 From: Jwaxy <98110672+jwaxy@users.noreply.github.com> Date: Sat, 27 Sep 2025 14:45:46 +0300 Subject: Make return struct with slice check recursive --- src/check_stmt.cpp | 135 ++++++++++++++++++++++++++--------------------------- 1 file changed, 66 insertions(+), 69 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index f509267fb..f6e201011 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2541,6 +2541,71 @@ gb_internal void check_if_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { check_close_scope(ctx); } +// NOTE(bill): This is very basic escape analysis +// This needs to be improved tremendously, and a lot of it done during the +// middle-end (or LLVM side) to improve checks and error messages +void check_unsafe_return(Operand const &o, Type *type, Ast *expr) { + auto unsafe_return_error = [](Operand const &o, char const *msg, Type *extra_type=nullptr) { + gbString s = expr_to_string(o.expr); + if (extra_type) { + gbString t = type_to_string(extra_type); + error(o.expr, "It is unsafe to return %s ('%s') of type ('%s') from a procedure, as it uses the current stack frame's memory", msg, s, t); + gb_string_free(t); + } else { + error(o.expr, "It is unsafe to return %s ('%s') from a procedure, as it uses the current stack frame's memory", msg, s); + } + gb_string_free(s); + }; + + if (expr->kind == Ast_CompoundLit && is_type_slice(type)) { + ast_node(cl, CompoundLit, expr); + if (cl->elems.count == 0) { + return; + } + unsafe_return_error(o, "a compound literal of a slice"); + } else if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) { + Ast *x = unparen_expr(expr->UnaryExpr.expr); + Entity *e = entity_of_node(x); + if (is_entity_local_variable(e)) { + unsafe_return_error(o, "the address of a local variable"); + } else if (x->kind == Ast_CompoundLit) { + unsafe_return_error(o, "the address of a compound literal"); + } else if (x->kind == Ast_IndexExpr) { + Entity *f = entity_of_node(x->IndexExpr.expr); + if (f && (is_type_array_like(f->type) || is_type_matrix(f->type))) { + if (is_entity_local_variable(f)) { + unsafe_return_error(o, "the address of an indexed variable", f->type); + } + } + } else if (x->kind == Ast_MatrixIndexExpr) { + Entity *f = entity_of_node(x->MatrixIndexExpr.expr); + if (f && (is_type_matrix(f->type) && is_entity_local_variable(f))) { + unsafe_return_error(o, "the address of an indexed variable", f->type); + } + } + } else if (expr->kind == Ast_SliceExpr) { + Ast *x = unparen_expr(expr->SliceExpr.expr); + Entity *e = entity_of_node(x); + if (is_entity_local_variable(e) && is_type_array(e->type)) { + unsafe_return_error(o, "a slice of a local variable"); + } else if (x->kind == Ast_CompoundLit) { + unsafe_return_error(o, "a slice of a compound literal"); + } + } else if (o.mode == Addressing_Constant && is_type_slice(type)) { + ERROR_BLOCK(); + unsafe_return_error(o, "a compound literal of a slice"); + error_line("\tNote: A constant slice value will use the memory of the current stack frame\n"); + } else if (expr->kind == Ast_CompoundLit) { + ast_node(cl, CompoundLit, expr); + for (Ast *elem : cl->elems) { + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); + check_unsafe_return(o, entity_of_node(fv->field)->type, fv->value); + } + } + } +} + gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) { ast_node(rs, ReturnStmt, node); @@ -2617,75 +2682,7 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) { expr = unparen_expr(arg); } - auto unsafe_return_error = [](Operand const &o, char const *msg, Type *extra_type=nullptr) { - gbString s = expr_to_string(o.expr); - if (extra_type) { - gbString t = type_to_string(extra_type); - error(o.expr, "It is unsafe to return %s ('%s') of type ('%s') from a procedure, as it uses the current stack frame's memory", msg, s, t); - gb_string_free(t); - } else { - error(o.expr, "It is unsafe to return %s ('%s') from a procedure, as it uses the current stack frame's memory", msg, s); - } - gb_string_free(s); - }; - - - // NOTE(bill): This is very basic escape analysis - // This needs to be improved tremendously, and a lot of it done during the - // middle-end (or LLVM side) to improve checks and error messages - if (expr->kind == Ast_CompoundLit && is_type_slice(o.type)) { - ast_node(cl, CompoundLit, expr); - if (cl->elems.count == 0) { - continue; - } - unsafe_return_error(o, "a compound literal of a slice"); - } else if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) { - Ast *x = unparen_expr(expr->UnaryExpr.expr); - Entity *e = entity_of_node(x); - if (is_entity_local_variable(e)) { - unsafe_return_error(o, "the address of a local variable"); - } else if (x->kind == Ast_CompoundLit) { - unsafe_return_error(o, "the address of a compound literal"); - } else if (x->kind == Ast_IndexExpr) { - Entity *f = entity_of_node(x->IndexExpr.expr); - if (f && (is_type_array_like(f->type) || is_type_matrix(f->type))) { - if (is_entity_local_variable(f)) { - unsafe_return_error(o, "the address of an indexed variable", f->type); - } - } - } else if (x->kind == Ast_MatrixIndexExpr) { - Entity *f = entity_of_node(x->MatrixIndexExpr.expr); - if (f && (is_type_matrix(f->type) && is_entity_local_variable(f))) { - unsafe_return_error(o, "the address of an indexed variable", f->type); - } - } - } else if (expr->kind == Ast_SliceExpr) { - Ast *x = unparen_expr(expr->SliceExpr.expr); - Entity *e = entity_of_node(x); - if (is_entity_local_variable(e) && is_type_array(e->type)) { - unsafe_return_error(o, "a slice of a local variable"); - } else if (x->kind == Ast_CompoundLit) { - unsafe_return_error(o, "a slice of a compound literal"); - } - } else if (o.mode == Addressing_Constant && is_type_slice(o.type)) { - ERROR_BLOCK(); - unsafe_return_error(o, "a compound literal of a slice"); - error_line("\tNote: A constant slice value will use the memory of the current stack frame\n"); - } else if (expr->kind == Ast_CompoundLit && is_type_struct(o.type)) { - ast_node(cl, CompoundLit, expr); - for (Ast *elem : cl->elems) { - if (elem->kind == Ast_FieldValue) { - ast_node(fv, FieldValue, elem); - if (fv->value->kind == Ast_CompoundLit && is_type_slice(entity_of_node(fv->field)->type)) { - ast_node(sl, CompoundLit, fv->value); - if (sl->elems.count == 0) { - continue; - } - unsafe_return_error(o, "a compound literal containing a slice"); - } - } - } - } + check_unsafe_return(o, o.type, expr); } } -- cgit v1.2.3 From af83c30b6f5303dfddf8e38019c0431658b6e176 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 27 Sep 2025 14:13:16 +0100 Subject: And extra safety checks --- src/check_stmt.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index f6e201011..5e7a8e323 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2545,7 +2545,7 @@ gb_internal void check_if_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { // This needs to be improved tremendously, and a lot of it done during the // middle-end (or LLVM side) to improve checks and error messages void check_unsafe_return(Operand const &o, Type *type, Ast *expr) { - auto unsafe_return_error = [](Operand const &o, char const *msg, Type *extra_type=nullptr) { + auto const unsafe_return_error = [](Operand const &o, char const *msg, Type *extra_type=nullptr) { gbString s = expr_to_string(o.expr); if (extra_type) { gbString t = type_to_string(extra_type); @@ -2557,6 +2557,10 @@ void check_unsafe_return(Operand const &o, Type *type, Ast *expr) { gb_string_free(s); }; + if (type == nullptr || expr == nullptr) { + return; + } + if (expr->kind == Ast_CompoundLit && is_type_slice(type)) { ast_node(cl, CompoundLit, expr); if (cl->elems.count == 0) { @@ -2600,7 +2604,10 @@ void check_unsafe_return(Operand const &o, Type *type, Ast *expr) { for (Ast *elem : cl->elems) { if (elem->kind == Ast_FieldValue) { ast_node(fv, FieldValue, elem); - check_unsafe_return(o, entity_of_node(fv->field)->type, fv->value); + Entity *e = entity_of_node(fv->field); + if (e != nullptr) { + check_unsafe_return(o, e->type, fv->value); + } } } } -- cgit v1.2.3