From ef33e551825258b51f095d3e52945b57fc795c4e Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Mon, 29 Dec 2025 15:31:12 -0500 Subject: Fix auto objc_msgSend incorrectly treating certain class methods as instance methods. --- src/check_expr.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 2fe6c0251..3b32d734a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8172,13 +8172,7 @@ gb_internal void check_objc_call_expr(CheckerContext *c, Operand *operand, Ast * Type *return_type = proc.result_count == 0 ? nullptr : proc.results->Tuple.variables[0]->type; bool is_return_instancetype = return_type != nullptr && return_type == t_objc_instancetype; - if (params.count == 0 || !is_type_objc_ptr_to_object(params[0]->type)) { - if (!proc_entity->Procedure.is_objc_class_method) { - // Not a class method, invalid call - error(call, "Invalid Objective-C call: The Objective-C method is not a class method but this first parameter is not an Objective-C object pointer."); - return; - } - + if (proc_entity->Procedure.is_objc_class_method) { if (is_return_instancetype) { if (ce->proc->kind == Ast_SelectorExpr) { ast_node(se, SelectorExpr, ce->proc); @@ -8194,7 +8188,8 @@ gb_internal void check_objc_call_expr(CheckerContext *c, Operand *operand, Ast * self_type = t_objc_Class; params_start = 0; - } else if (ce->args.count > 0) { + } else { + GB_ASSERT(ce->args.count > 0); GB_ASSERT(is_type_objc_ptr_to_object(params[0]->type)); if (ce->args[0]->tav.objc_super_target) { -- cgit v1.2.3 From 91c1950b9f198ab844b83dabb0f381810965a3ce Mon Sep 17 00:00:00 2001 From: ske Date: Wed, 7 Jan 2026 02:19:41 -0300 Subject: Fix constant conversion checks (#6104) --- src/check_expr.cpp | 23 ++++++----------------- tests/issues/test_issue_6068.odin | 16 ++++++++-------- 2 files changed, 14 insertions(+), 25 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 3b32d734a..2b2ae09cc 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3526,28 +3526,17 @@ gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) Type *elem = core_array_type(bt); if (core_type(bt)->kind == Type_Basic) { - if (check_representable_as_constant(c, x->value, type, &x->value)) { - return true; - } - goto check_castable; - } else if (!are_types_identical(elem, bt) && - elem->kind == Type_Basic && - check_representable_as_constant(c, x->value, elem, &x->value)) { - if (check_representable_as_constant(c, x->value, type, &x->value)) { - return true; - } - goto check_castable; + return check_representable_as_constant(c, x->value, type, &x->value) || + (is_type_pointer(type) && check_is_castable_to(c, x, type)); + } else if (!are_types_identical(elem, bt) && elem->kind == Type_Basic) { + return check_representable_as_constant(c, x->value, elem, &x->value) || + (is_type_pointer(elem) && check_is_castable_to(c, x, elem)); } else if (check_is_castable_to(c, x, type)) { x->value = {}; x->mode = Addressing_Value; return true; } - - return false; - } - -check_castable: - if (check_is_castable_to(c, x, type)) { + } else if (check_is_castable_to(c, x, type)) { if (x->mode != Addressing_Constant) { x->mode = Addressing_Value; } else if (is_type_slice(type) && is_type_string(x->type)) { diff --git a/tests/issues/test_issue_6068.odin b/tests/issues/test_issue_6068.odin index 011c95442..88a21ee27 100644 --- a/tests/issues/test_issue_6068.odin +++ b/tests/issues/test_issue_6068.odin @@ -58,14 +58,14 @@ test_issue_6068 :: proc(t: ^testing.T) { testing.expect(t, f64be(-1.234) == check_be) testing.expect(t, cast(f64be)value == check_be) testing.expect(t, cast(f64be)-1.234 == check_be) - testing.expect(t, f64be(int(-1.234)) == check_be) - testing.expect(t, cast(f64be)int(-1.234) == check_be) + testing.expect(t, f64be(f64(-1.234)) == check_be) + testing.expect(t, cast(f64be)f64(-1.234) == check_be) testing.expect(t, f64le(value) == check_le) testing.expect(t, f64le(-1.234) == check_le) testing.expect(t, cast(f64le)value == check_le) testing.expect(t, cast(f64le)-1.234 == check_le) - testing.expect(t, f64le(int(-1.234)) == check_le) - testing.expect(t, cast(f64le)int(-1.234) == check_le) + testing.expect(t, f64le(f64(-1.234)) == check_le) + testing.expect(t, cast(f64le)f64(-1.234) == check_le) testing.expect(t, f64le(reverse) == check_le) testing.expect(t, cast(f64le)reverse == check_le) @@ -74,14 +74,14 @@ test_issue_6068 :: proc(t: ^testing.T) { testing.expect(t, f64be(-1.234) == -1.234) testing.expect(t, cast(f64be)value == -1.234) testing.expect(t, cast(f64be)-1.234 == -1.234) - testing.expect(t, f64be(int(-1.234)) == -1.234) - testing.expect(t, cast(f64be)int(-1.234) == -1.234) + testing.expect(t, f64be(f64(-1.234)) == -1.234) + testing.expect(t, cast(f64be)f64(-1.234) == -1.234) testing.expect(t, f64le(value) == -1.234) testing.expect(t, f64le(-1.234) == -1.234) testing.expect(t, cast(f64le)value == -1.234) testing.expect(t, cast(f64le)-1.234 == -1.234) - testing.expect(t, f64le(int(-1.234)) == -1.234) - testing.expect(t, cast(f64le)int(-1.234) == -1.234) + testing.expect(t, f64le(f64(-1.234)) == -1.234) + testing.expect(t, cast(f64le)f64(-1.234) == -1.234) testing.expect(t, f64le(reverse) == -1.234) testing.expect(t, cast(f64le)reverse == -1.234) } -- cgit v1.2.3 From bf7c92bf0909aaf49a8590570085e1c6a1787d00 Mon Sep 17 00:00:00 2001 From: Tohei Ichikawa Date: Sat, 10 Jan 2026 03:02:25 -0500 Subject: Fix assertion error when imported proc groups are passed as proc arguments --- src/check_expr.cpp | 6 +++++- tests/internal/test_imported_proc_groups.odin | 10 ++++++++++ tests/internal/test_imported_proc_groups/proc_group.odin | 4 ++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 tests/internal/test_imported_proc_groups.odin create mode 100644 tests/internal/test_imported_proc_groups/proc_group.odin (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 2b2ae09cc..60fae74c6 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1133,7 +1133,11 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ x.mode = Addressing_Value; x.type = t; if (check_is_assignable_to(c, &x, type)) { - add_entity_use(c, operand->expr, e); + if (operand->expr->kind == Ast_SelectorExpr) { + add_entity_use(c, operand->expr->SelectorExpr.selector, e); + } else { + add_entity_use(c, operand->expr, e); + } good = true; break; } diff --git a/tests/internal/test_imported_proc_groups.odin b/tests/internal/test_imported_proc_groups.odin new file mode 100644 index 000000000..3f5b01fb3 --- /dev/null +++ b/tests/internal/test_imported_proc_groups.odin @@ -0,0 +1,10 @@ +package test_internal + +import "core:testing" +import "test_imported_proc_groups" + +@test +test_ :: proc(t: ^testing.T) { + use_proc :: proc(proc()) { } + use_proc(test_imported_proc_groups.proc_group) +} diff --git a/tests/internal/test_imported_proc_groups/proc_group.odin b/tests/internal/test_imported_proc_groups/proc_group.odin new file mode 100644 index 000000000..59519ab9c --- /dev/null +++ b/tests/internal/test_imported_proc_groups/proc_group.odin @@ -0,0 +1,4 @@ +package test_imported_proc_groups + +proc_group :: proc{empty_proc} +empty_proc :: proc() { } -- cgit v1.2.3 From 3e31055ca7dbfa42998f7f6b42c45d15b967622e Mon Sep 17 00:00:00 2001 From: ske Date: Sat, 10 Jan 2026 10:46:53 -0300 Subject: Fix regression in #6118 --- src/check_expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 2b2ae09cc..98185b0d3 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3528,7 +3528,7 @@ gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) if (core_type(bt)->kind == Type_Basic) { return check_representable_as_constant(c, x->value, type, &x->value) || (is_type_pointer(type) && check_is_castable_to(c, x, type)); - } else if (!are_types_identical(elem, bt) && elem->kind == Type_Basic) { + } else if (!are_types_identical(elem, bt) && elem->kind == Type_Basic && x->type->kind == Type_Basic) { return check_representable_as_constant(c, x->value, elem, &x->value) || (is_type_pointer(elem) && check_is_castable_to(c, x, elem)); } else if (check_is_castable_to(c, x, type)) { -- cgit v1.2.3 From 5938c783a69e826cd8c113521a3950a6bacdb9aa Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 11 Jan 2026 18:54:46 +0000 Subject: Fix `#type` not being handled as in normal expressions --- src/check_expr.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 3b32d734a..c4f235b51 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -11565,6 +11565,15 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast return kind; case_end; + case_ast_node(ht, HelperType, node); + Type *type = check_type(c, ht->type); + if (type != nullptr && type != t_invalid) { + o->mode = Addressing_Type; + o->type = type; + } + return kind; + case_end; + case_ast_node(i, Implicit, node); switch (i->kind) { case Token_context: -- cgit v1.2.3 From 0366cd3304b3910a397c4989e46bee4c575adeec Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 15 Jan 2026 16:32:52 +0000 Subject: Add `#must_tail` (similar syntax to `#force_inline` --- src/check_expr.cpp | 30 ++++++++++++++++++++++++++--- src/llvm_backend.cpp | 12 ++++++------ src/llvm_backend.hpp | 3 ++- src/llvm_backend_proc.cpp | 48 +++++++++++++++++++++++++++++++++++++++++------ src/parser.cpp | 23 ++++++++++++++++++----- src/parser.hpp | 13 ++++++++++--- 6 files changed, 105 insertions(+), 24 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index cd2307c6d..3b470cb88 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8210,7 +8210,7 @@ gb_internal void check_objc_call_expr(CheckerContext *c, Operand *operand, Ast * add_objc_proc_type(c, call, return_type, param_types); } -gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice const &args, ProcInlining inlining, Type *type_hint) { +gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice const &args, ProcInlining inlining, ProcTailing tailing, Type *type_hint) { if (proc != nullptr && proc->kind == Ast_BasicDirective) { ast_node(bd, BasicDirective, proc); @@ -8241,7 +8241,10 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c return Expr_Expr; } if (inlining != ProcInlining_none) { - error(call, "Inlining operators are not allowed on built-in procedures"); + error(call, "Inlining directives are not allowed on built-in procedures"); + } + if (tailing != ProcTailing_none) { + error(call, "Tailing directives are not allowed on built-in procedures"); } } else { if (proc != nullptr) { @@ -8383,6 +8386,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } bool is_call_inlined = false; + bool is_call_tailed = true; switch (inlining) { case ProcInlining_inline: @@ -8417,6 +8421,20 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } } + switch (tailing) { + case ProcTailing_none: + break; + case ProcTailing_must_tail: + is_call_tailed = true; + if (proc != nullptr) { + Entity *e = entity_from_expr(proc); + if (e != nullptr && e->kind == Entity_Procedure) { + // TODO(bill): `preserve_none` + } + } + break; + } + { String invalid; if (pt->kind == Type_Proc && pt->Proc.require_target_feature.len != 0) { @@ -11825,7 +11843,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast case_end; case_ast_node(ce, CallExpr, node); - return check_call_expr(c, o, node, ce->proc, ce->args, ce->inlining, type_hint); + return check_call_expr(c, o, node, ce->proc, ce->args, ce->inlining, ce->tailing, type_hint); case_end; case_ast_node(de, DerefExpr, node); @@ -12566,6 +12584,12 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan case_end; case_ast_node(ce, CallExpr, node); + switch (ce->tailing) { + case ProcTailing_must_tail: + str = gb_string_appendc(str, "#must_tail "); + break; + } + switch (ce->inlining) { case ProcInlining_inline: str = gb_string_appendc(str, "#force_inline "); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 1cde65640..0bf3d1125 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2111,7 +2111,7 @@ gb_internal void lb_create_startup_runtime_generate_body(lbModule *m, lbProcedur for (Entity *e : info->init_procedures) { lbValue value = lb_find_procedure_value_from_entity(m, e); - lb_emit_call(p, value, {}, ProcInlining_none); + lb_emit_call(p, value, {}, ProcInlining_none, ProcTailing_none); } @@ -2157,7 +2157,7 @@ gb_internal lbProcedure *lb_create_cleanup_runtime(lbModule *main_module) { // C for (Entity *e : info->fini_procedures) { lbValue value = lb_find_procedure_value_from_entity(main_module, e); - lb_emit_call(p, value, {}, ProcInlining_none); + lb_emit_call(p, value, {}, ProcInlining_none, ProcTailing_none); } lb_end_procedure_body(p); @@ -2850,7 +2850,7 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star } lbValue startup_runtime_value = {startup_runtime->value, startup_runtime->type}; - lb_emit_call(p, startup_runtime_value, {}, ProcInlining_none); + lb_emit_call(p, startup_runtime_value, {}, ProcInlining_none, ProcTailing_none); if (build_context.command_kind == Command_test) { Type *t_Internal_Test = find_type_in_pkg(m->info, str_lit("testing"), str_lit("Internal_Test")); @@ -2917,16 +2917,16 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star auto exit_args = array_make(temporary_allocator(), 1); exit_args[0] = lb_emit_select(p, result, lb_const_int(m, t_int, 0), lb_const_int(m, t_int, 1)); - lb_emit_call(p, exit_runner, exit_args, ProcInlining_none); + lb_emit_call(p, exit_runner, exit_args, ProcInlining_none, ProcTailing_none); } else { if (m->info->entry_point != nullptr) { lbValue entry_point = lb_find_procedure_value_from_entity(m, m->info->entry_point); - lb_emit_call(p, entry_point, {}, ProcInlining_no_inline); + lb_emit_call(p, entry_point, {}, ProcInlining_no_inline, ProcTailing_none); } if (call_cleanup) { lbValue cleanup_runtime_value = {cleanup_runtime->value, cleanup_runtime->type}; - lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none); + lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none, ProcTailing_none); } if (is_dll_main) { diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index da5d91f2e..9db7e94d9 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -345,6 +345,7 @@ struct lbProcedure { Ast * body; u64 tags; ProcInlining inlining; + ProcTailing tailing; bool is_foreign; bool is_export; bool is_entry_point; @@ -484,7 +485,7 @@ gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlo gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, Ast *node); gb_internal lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t); gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right); -gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, ProcInlining inlining = ProcInlining_none); +gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, ProcInlining inlining = ProcInlining_none, ProcTailing tailing = ProcTailing_none); gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t); gb_internal lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 216d600da..0703941c1 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -117,6 +117,7 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i p->type_expr = decl->type_expr; p->body = pl->body; p->inlining = pl->inlining; + p->tailing = pl->tailing; p->is_foreign = entity->Procedure.is_foreign; p->is_export = entity->Procedure.is_export; p->is_entry_point = false; @@ -178,6 +179,12 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i } } + switch (p->tailing) { + case ProcTailing_must_tail: + lb_add_attribute_to_proc(m, p->value, "preserve_none"); + break; + } + switch (entity->Procedure.optimization_mode) { case ProcedureOptimizationMode_None: lb_add_attribute_to_proc(m, p->value, "optnone"); @@ -387,6 +394,7 @@ gb_internal lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name p->body = nullptr; p->tags = 0; p->inlining = ProcInlining_none; + p->tailing = ProcTailing_none; p->is_foreign = false; p->is_export = false; p->is_entry_point = false; @@ -855,7 +863,7 @@ gb_internal Array lb_value_to_array(lbProcedure *p, gbAllocator const & -gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr, Array const &processed_args, Type *abi_rt, lbAddr context_ptr, ProcInlining inlining) { +gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr, Array const &processed_args, Type *abi_rt, lbAddr context_ptr, ProcInlining inlining, ProcTailing tailing) { GB_ASSERT(p->module->ctx == LLVMGetTypeContext(LLVMTypeOf(value.value))); unsigned arg_count = cast(unsigned)processed_args.count; @@ -972,6 +980,15 @@ gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue break; } + switch (tailing) { + case ProcTailing_none: + break; + case ProcTailing_must_tail: + LLVMSetTailCall(ret, true); + LLVMSetTailCallKind(ret, LLVMTailCallKindMustTail); + break; + } + lbValue res = {}; res.value = ret; res.type = abi_rt; @@ -1045,7 +1062,7 @@ gb_internal lbValue lb_emit_conjugate(lbProcedure *p, lbValue val, Type *type) { return lb_emit_load(p, res); } -gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, ProcInlining inlining) { +gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, ProcInlining inlining, ProcTailing tailing) { lbModule *m = p->module; Type *pt = base_type(value.type); @@ -1168,10 +1185,10 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array c if (return_by_pointer) { lbValue return_ptr = lb_add_local_generated(p, rt, true).addr; - lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining); + lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining, tailing); result = lb_emit_load(p, return_ptr); } else if (rt != nullptr) { - result = lb_emit_call_internal(p, value, {}, processed_args, rt, context_ptr, inlining); + result = lb_emit_call_internal(p, value, {}, processed_args, rt, context_ptr, inlining, tailing); if (ft->ret.cast_type) { result.value = OdinLLVMBuildTransmute(p, result.value, ft->ret.cast_type); } @@ -1184,7 +1201,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array c result = lb_emit_conv(p, result, rt); } } else { - lb_emit_call_internal(p, value, {}, processed_args, nullptr, context_ptr, inlining); + lb_emit_call_internal(p, value, {}, processed_args, nullptr, context_ptr, inlining, tailing); } if (original_rt != rt) { @@ -4402,6 +4419,25 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { return lb_handle_objc_auto_send(p, expr, slice(call_args, 0, call_args.count)); } - return lb_emit_call(p, value, call_args, ce->inlining); + + ProcInlining inlining = ce->inlining; + ProcTailing tailing = ce->tailing; + + if (tailing == ProcTailing_none && + proc_entity && proc_entity->kind == Entity_Procedure && + proc_entity->decl_info && + proc_entity->decl_info->proc_lit) { + ast_node(pl, ProcLit, proc_entity->decl_info->proc_lit); + + if (pl->inlining != ProcInlining_none) { + inlining = pl->inlining; + } + + if (pl->tailing != ProcTailing_none) { + tailing = pl->tailing; + } + } + + return lb_emit_call(p, value, call_args, inlining, tailing); } diff --git a/src/parser.cpp b/src/parser.cpp index 823021e04..d8a9df473 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2176,7 +2176,7 @@ gb_internal bool ast_on_same_line(Token const &x, Ast *yp) { return x.pos.line == y.pos.line; } -gb_internal Ast *parse_force_inlining_operand(AstFile *f, Token token) { +gb_internal Ast *parse_inlining_or_tailing_operand(AstFile *f, Token token) { Ast *expr = parse_unary_expr(f, false); Ast *e = strip_or_return_expr(expr); if (e == nullptr) { @@ -2187,11 +2187,14 @@ gb_internal Ast *parse_force_inlining_operand(AstFile *f, Token token) { return ast_bad_expr(f, token, f->curr_token); } ProcInlining pi = ProcInlining_none; + ProcTailing pt = ProcTailing_none; if (token.kind == Token_Ident) { if (token.string == "force_inline") { pi = ProcInlining_inline; } else if (token.string == "force_no_inline") { pi = ProcInlining_no_inline; + } else if (token.string == "must_tail") { + pt = ProcTailing_must_tail; } } @@ -2211,6 +2214,14 @@ gb_internal Ast *parse_force_inlining_operand(AstFile *f, Token token) { } } + if (pt != ProcTailing_none) { + if (e->kind == Ast_ProcLit) { + e->ProcLit.tailing = pt; + } else if (e->kind == Ast_CallExpr) { + e->CallExpr.tailing = pt; + } + } + return expr; } @@ -2507,8 +2518,9 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { syntax_error(tag, "#relative types have now been removed in favour of \"core:relative\""); return ast_relative_type(f, tag, type); } else if (name.string == "force_inline" || - name.string == "force_no_inline") { - return parse_force_inlining_operand(f, name); + name.string == "force_no_inline" || + name.string == "must_tail") { + return parse_inlining_or_tailing_operand(f, name); } return ast_basic_directive(f, token, name); } @@ -5399,8 +5411,9 @@ gb_internal Ast *parse_stmt(AstFile *f) { expect_semicolon(f); return stmt; } else if (name.string == "force_inline" || - name.string == "force_no_inline") { - Ast *expr = parse_force_inlining_operand(f, name); + name.string == "force_no_inline" || + name.string == "must_tail") { + Ast *expr = parse_inlining_or_tailing_operand(f, name); Ast *stmt = ast_expr_stmt(f, expr); expect_semicolon(f); return stmt; diff --git a/src/parser.hpp b/src/parser.hpp index 71b61d95f..30b11c730 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -263,12 +263,17 @@ struct ForeignFileWorkerData { -enum ProcInlining { - ProcInlining_none = 0, - ProcInlining_inline = 1, +enum ProcInlining : u8 { + ProcInlining_none = 0, + ProcInlining_inline = 1, ProcInlining_no_inline = 2, }; +enum ProcTailing : u8 { + ProcTailing_none = 0, + ProcTailing_must_tail = 1, +}; + enum ProcTag { ProcTag_bounds_check = 1<<0, ProcTag_no_bounds_check = 1<<1, @@ -441,6 +446,7 @@ struct AstSplitArgs { Ast *body; \ u64 tags; \ ProcInlining inlining; \ + ProcTailing tailing; \ Token where_token; \ Slice where_clauses; \ DeclInfo *decl; \ @@ -486,6 +492,7 @@ AST_KIND(_ExprBegin, "", bool) \ Token close; \ Token ellipsis; \ ProcInlining inlining; \ + ProcTailing tailing; \ bool optional_ok_one; \ bool was_selector; \ AstSplitArgs *split_args; \ -- cgit v1.2.3 From 5f07055ac1548e6254651f40a23e74093a9bebfe Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 15 Jan 2026 17:28:28 +0000 Subject: Add `#must_tail` and `"preserve/none"` calling convention --- src/check_expr.cpp | 13 ++++++++----- src/llvm_backend.hpp | 3 +++ src/llvm_backend_proc.cpp | 6 ------ src/parser.cpp | 3 +++ src/parser.hpp | 2 ++ src/types.cpp | 38 +++++--------------------------------- 6 files changed, 21 insertions(+), 44 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 3b470cb88..74ae02f94 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8426,11 +8426,14 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c break; case ProcTailing_must_tail: is_call_tailed = true; - if (proc != nullptr) { - Entity *e = entity_from_expr(proc); - if (e != nullptr && e->kind == Entity_Procedure) { - // TODO(bill): `preserve_none` - } + if (c->curr_proc_sig == nullptr || !are_types_identical(c->curr_proc_sig, pt)) { + ERROR_BLOCK(); + gbString a = type_to_string(pt); + gbString b = type_to_string(c->curr_proc_sig); + error(call, "Use of '#must_tail' of a procedure must have the same type as the procedure it was called within"); + error_line("\tCall type: %s, parent type: %s", a, b); + gb_string_free(b); + gb_string_free(a); } break; } diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 9db7e94d9..884b87998 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -671,6 +671,7 @@ enum lbCallingConventionKind : unsigned { lbCallingConvention_PreserveAll = 15, lbCallingConvention_Swift = 16, lbCallingConvention_CXX_FAST_TLS = 17, + lbCallingConvention_PreserveNone = 21, lbCallingConvention_FirstTargetCC = 64, lbCallingConvention_X86_StdCall = 64, lbCallingConvention_X86_FastCall = 65, @@ -724,6 +725,8 @@ lbCallingConventionKind const lb_calling_convention_map[ProcCC_MAX] = { lbCallingConvention_Win64, // ProcCC_Win64, lbCallingConvention_X86_64_SysV, // ProcCC_SysV, + lbCallingConvention_PreserveNone, // ProcCC_PreserveNone, + }; enum : LLVMDWARFTypeEncoding { diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 0703941c1..9ac51bba3 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -179,12 +179,6 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i } } - switch (p->tailing) { - case ProcTailing_must_tail: - lb_add_attribute_to_proc(m, p->value, "preserve_none"); - break; - } - switch (entity->Procedure.optimization_mode) { case ProcedureOptimizationMode_None: lb_add_attribute_to_proc(m, p->value, "optnone"); diff --git a/src/parser.cpp b/src/parser.cpp index d8a9df473..8782b3a2b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2216,6 +2216,7 @@ gb_internal Ast *parse_inlining_or_tailing_operand(AstFile *f, Token token) { if (pt != ProcTailing_none) { if (e->kind == Ast_ProcLit) { + syntax_error(expr, "'#must_call' can only be applied to a procedure call, not the procedure literal"); e->ProcLit.tailing = pt; } else if (e->kind == Ast_CallExpr) { e->CallExpr.tailing = pt; @@ -4020,6 +4021,8 @@ gb_internal ProcCallingConvention string_to_calling_convention(String const &s) if (s == "win64") return ProcCC_Win64; if (s == "sysv") return ProcCC_SysV; + if (s == "preserve/none") return ProcCC_PreserveNone; + if (s == "system") { if (build_context.metrics.os == TargetOs_windows) { return ProcCC_StdCall; diff --git a/src/parser.hpp b/src/parser.hpp index 30b11c730..77d38b291 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -301,6 +301,7 @@ enum ProcCallingConvention : i32 { ProcCC_Win64 = 9, ProcCC_SysV = 10, + ProcCC_PreserveNone = 11, ProcCC_MAX, @@ -320,6 +321,7 @@ gb_global char const *proc_calling_convention_strings[ProcCC_MAX] = { "inlineasm", "win64", "sysv", + "preserve/none", }; gb_internal ProcCallingConvention default_calling_convention(void) { diff --git a/src/types.cpp b/src/types.cpp index 18e3b56ac..911cd4448 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -5210,40 +5210,12 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha case Type_Proc: str = gb_string_appendc(str, "proc"); - switch (type->Proc.calling_convention) { - case ProcCC_Odin: - if (default_calling_convention() != ProcCC_Odin) { - str = gb_string_appendc(str, " \"odin\" "); - } - break; - case ProcCC_Contextless: - if (default_calling_convention() != ProcCC_Contextless) { - str = gb_string_appendc(str, " \"contextless\" "); - } - break; - case ProcCC_CDecl: - str = gb_string_appendc(str, " \"c\" "); - break; - case ProcCC_StdCall: - str = gb_string_appendc(str, " \"std\" "); - break; - case ProcCC_FastCall: - str = gb_string_appendc(str, " \"fastcall\" "); - break; - break; - case ProcCC_None: - str = gb_string_appendc(str, " \"none\" "); - break; - case ProcCC_Naked: - str = gb_string_appendc(str, " \"naked\" "); - break; - // case ProcCC_VectorCall: - // str = gb_string_appendc(str, " \"vectorcall\" "); - // break; - // case ProcCC_ClrCall: - // str = gb_string_appendc(str, " \"clrcall\" "); - // break; + if (type->Proc.calling_convention != default_calling_convention()) { + str = gb_string_appendc(str, " \""); + str = gb_string_appendc(str, proc_calling_convention_strings[type->Proc.calling_convention]); + str = gb_string_appendc(str, "\" "); } + str = gb_string_appendc(str, "("); if (type->Proc.params) { str = write_type_to_string(str, type->Proc.params, shorthand, allow_polymorphic); -- cgit v1.2.3 From b0064f38cfb7f24d760feb010c09472fc7f896ee Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Jan 2026 13:17:54 +0000 Subject: Minor rearrange of `check_is_operand_compound_lit_constant` for future optimization prep --- src/check_expr.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 99f803a08..8fdd5372b 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8768,6 +8768,14 @@ gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Opera if (is_operand_nil(*o)) { return true; } + if (is_type_any(field_type)) { + return false; + } + if (field_type != nullptr && is_type_typeid(field_type) && o->mode == Addressing_Type) { + add_type_info_type(c, o->type); + return true; + } + Ast *expr = unparen_expr(o->expr); if (expr != nullptr) { Entity *e = strip_entity_wrapping(entity_from_expr(expr)); @@ -8778,13 +8786,12 @@ gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Opera add_type_and_value(c, expr, Addressing_Constant, type_of_expr(expr), exact_value_procedure(expr)); return true; } - } - if (field_type != nullptr && is_type_typeid(field_type) && o->mode == Addressing_Type) { - add_type_info_type(c, o->type); - return true; - } - if (is_type_any(field_type)) { - return false; + + if (e != nullptr && e->kind == Entity_Variable && e->Variable.is_rodata) { + // DeclInfo *d = e->decl_info; + // TODO(bill): rodata inlining for non-indirect values + // e.g. **NOT** []T{...} + } } return o->mode == Addressing_Constant; } -- cgit v1.2.3 From 5e4895e76dc5bb5ee4357037433825907403457e Mon Sep 17 00:00:00 2001 From: Barinzaya Date: Fri, 23 Jan 2026 15:53:29 -0500 Subject: Fixed some issues with `in` and `not_in` on constant `bit_set`s. This addresses two issues: - With a `bit_set` having no underlying type and a non-zero lower bound, `in` and `not_in` were returning incorrect results when done at compile-time. - With a `bit_set` of more than 128 bits, `in` always returns false on values that fall within the upper 64 bits. --- src/check_expr.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8fdd5372b..d64c733f3 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4125,15 +4125,19 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ i64 upper = yt->BitSet.upper; if (lower <= key && key <= upper) { - i64 bit = 1ll<mode = Addressing_Constant; x->type = t_untyped_bool; if (op.kind == Token_in) { - x->value = exact_value_bool((bit & bits) != 0); + x->value = exact_value_bool(!big_int_is_zero(&mask)); } else { - x->value = exact_value_bool((bit & bits) == 0); + x->value = exact_value_bool(big_int_is_zero(&mask)); } x->expr = node; return; -- cgit v1.2.3 From e0ff16f98cf6c61115db50bb607479e6f8b0b98b Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 24 Jan 2026 10:53:38 +0100 Subject: Fix #6126 `ExactValue_Compound` wasn't handled properly. --- src/check_expr.cpp | 4 ++-- src/exact_value.cpp | 13 ++++--------- 2 files changed, 6 insertions(+), 11 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8fdd5372b..4e791af16 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -12128,12 +12128,12 @@ gb_internal bool is_exact_value_zero(ExactValue const &v) { -gb_internal bool compare_exact_values_compound_lit(TokenKind op, ExactValue x, ExactValue y, bool *do_break_) { +gb_internal bool compare_exact_values_compound_lit(TokenKind op, ExactValue x, ExactValue y) { ast_node(x_cl, CompoundLit, x.value_compound); ast_node(y_cl, CompoundLit, y.value_compound); if (x_cl->elems.count != y_cl->elems.count) { - if (do_break_) *do_break_ = true; + return false; } bool test = op == Token_CmpEq; diff --git a/src/exact_value.cpp b/src/exact_value.cpp index f266b8b24..0f425e043 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -947,7 +947,7 @@ gb_internal gb_inline i32 cmp_f64(f64 a, f64 b) { return (a > b) - (a < b); } -gb_internal bool compare_exact_values_compound_lit(TokenKind op, ExactValue x, ExactValue y, bool *do_break_); +gb_internal bool compare_exact_values_compound_lit(TokenKind op, ExactValue x, ExactValue y); gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) { match_exact_values(&x, &y); @@ -1060,18 +1060,13 @@ gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) case ExactValue_Compound: if (op != Token_CmpEq && op != Token_NotEq) { - break; + return false; } if (x.kind != y.kind) { - break; + return false; } - bool do_break = false; - bool res = compare_exact_values_compound_lit(op, x, y, &do_break); - if (do_break) { - break; - } - return res; + return compare_exact_values_compound_lit(op, x, y); } GB_PANIC("Invalid comparison: %d", x.kind); -- cgit v1.2.3 From 3586bda6ae80b1dca1984ae54b023d5fec69067e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 26 Jan 2026 18:23:29 +0000 Subject: Use `context.assertion_failure_proc` with type assertions when the `context` is available, otherwise use a trivial trap. --- base/runtime/error_checks.odin | 159 ++++++++++++++++---- base/runtime/print.odin | 331 +++++++++++++++++++++++++++++++++++++++++ src/check_expr.cpp | 16 +- src/llvm_backend_expr.cpp | 27 +++- src/llvm_backend_utility.cpp | 13 +- 5 files changed, 509 insertions(+), 37 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/base/runtime/error_checks.odin b/base/runtime/error_checks.odin index 32a895c3f..79245fc0c 100644 --- a/base/runtime/error_checks.odin +++ b/base/runtime/error_checks.odin @@ -137,7 +137,22 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32 when ODIN_NO_RTTI { - type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32) { + type_assertion_check_with_context :: proc "odin" (ok: bool, file: string, line, column: i32) { + if ok { + return + } + @(cold, no_instrumentation) + handle_error :: proc "odin" (file: string, line, column: i32) -> ! { + p := context.assertion_failure_proc + if p == nil { + p = default_assertion_failure_proc + } + p("type assertion", "Invalid type assertion", Source_Code_Location{file, line, column, ""}) + } + handle_error(file, line, column) + } + + type_assertion_check_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32) { if ok { return } @@ -150,7 +165,7 @@ when ODIN_NO_RTTI { handle_error(file, line, column) } - type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32) { + type_assertion_check2_with_context :: proc "odin" (ok: bool, file: string, line, column: i32) { if ok { return } @@ -162,8 +177,53 @@ when ODIN_NO_RTTI { } handle_error(file, line, column) } + + type_assertion_check2_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32) { + if ok { + return + } + @(cold, no_instrumentation) + handle_error :: proc "odin" (file: string, line, column: i32) -> ! { + p := context.assertion_failure_proc + if p == nil { + p = default_assertion_failure_proc + } + p("type assertion", "Invalid type assertion", Source_Code_Location{file, line, column, ""}) + } + handle_error(file, line, column) + } } else { - type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) { + @(private="file") + TYPE_ASSERTION_BUFFER_SIZE :: 1024 + + type_assertion_check_with_context :: proc "odin" (ok: bool, file: string, line, column: i32, from, to: typeid) { + if ok { + return + } + @(cold, no_instrumentation) + handle_error :: proc "odin" (file: string, line, column: i32, from, to: typeid) -> ! { + do_msg :: proc "contextless" (i: ^int, buf: []byte, file: string, line, column: i32, from, to: typeid) -> bool { + try_copy_string(i, buf, "Invalid type assertion from ") or_return + try_copy_typeid(i, buf, from) or_return + try_copy_string(i, buf, " to ") or_return + try_copy_typeid(i, buf, to) or_return + return true + } + + buf: [TYPE_ASSERTION_BUFFER_SIZE]byte + i := 0 + _ = do_msg(&i, buf[:], file, line, column, from, to) + + p := context.assertion_failure_proc + if p == nil { + p = default_assertion_failure_proc + } + p("type assertion", string(buf[:i]), Source_Code_Location{file, line, column, ""}) + } + handle_error(file, line, column, from, to) + } + + type_assertion_check_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) { if ok { return } @@ -180,42 +240,85 @@ when ODIN_NO_RTTI { handle_error(file, line, column, from, to) } - type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) { + @(private="file") + type_assertion_variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid { + if id == nil || data == nil { + return id + } + ti := type_info_base(type_info_of(id)) + #partial switch v in ti.variant { + case Type_Info_Any: + return (^any)(data).id + case Type_Info_Union: + if v.tag_type == nil { + if (^rawptr)(data)^ == nil { + return nil + } + return v.variants[0].id + + } + + tag_ptr := uintptr(data) + v.tag_offset + idx := 0 + switch v.tag_type.size { + case 1: idx = int( (^u8)(tag_ptr)^); if !v.no_nil { idx -= 1 } + case 2: idx = int( (^u16)(tag_ptr)^); if !v.no_nil { idx -= 1 } + case 4: idx = int( (^u32)(tag_ptr)^); if !v.no_nil { idx -= 1 } + case 8: idx = int( (^u64)(tag_ptr)^); if !v.no_nil { idx -= 1 } + case 16: idx = int((^u128)(tag_ptr)^); if !v.no_nil { idx -= 1 } + } + if idx < 0 { + return nil + } else if idx < len(v.variants) { + return v.variants[idx].id + } + } + return id + } + + type_assertion_check2_with_context :: proc "odin" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) { if ok { return } - variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid { - if id == nil || data == nil { - return id - } - ti := type_info_base(type_info_of(id)) - #partial switch v in ti.variant { - case Type_Info_Any: - return (^any)(data).id - case Type_Info_Union: - tag_ptr := uintptr(data) + v.tag_offset - idx := 0 - switch v.tag_type.size { - case 1: idx = int((^u8)(tag_ptr)^) - 1 - case 2: idx = int((^u16)(tag_ptr)^) - 1 - case 4: idx = int((^u32)(tag_ptr)^) - 1 - case 8: idx = int((^u64)(tag_ptr)^) - 1 - case 16: idx = int((^u128)(tag_ptr)^) - 1 - } - if idx < 0 { - return nil - } else if idx < len(v.variants) { - return v.variants[idx].id + @(cold, no_instrumentation) + handle_error :: proc "odin" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! { + do_msg :: proc "contextless" (i: ^int, buf: []byte, file: string, line, column: i32, from, to, actual: typeid) -> bool { + try_copy_string(i, buf, "Invalid type assertion from ") or_return + try_copy_typeid(i, buf, from) or_return + try_copy_string(i, buf, " to ") or_return + try_copy_typeid(i, buf, to) or_return + if actual != from { + try_copy_string(i, buf, ", actual type: ") or_return + try_copy_typeid(i, buf, actual) or_return } + return true } - return id + + actual := type_assertion_variant_type(from, from_data) + + buf: [TYPE_ASSERTION_BUFFER_SIZE]byte + i := 0 + _ = do_msg(&i, buf[:], file, line, column, from, to, actual) + + p := context.assertion_failure_proc + if p == nil { + p = default_assertion_failure_proc + } + p("type assertion", string(buf[:i]), Source_Code_Location{file, line, column, ""}) + } + handle_error(file, line, column, from, to, from_data) + } + + type_assertion_check2_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) { + if ok { + return } @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! { - actual := variant_type(from, from_data) + actual := type_assertion_variant_type(from, from_data) print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid type assertion from ") diff --git a/base/runtime/print.odin b/base/runtime/print.odin index 4ef142037..4a66f9743 100644 --- a/base/runtime/print.odin +++ b/base/runtime/print.odin @@ -502,3 +502,334 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) { print_type(info.elem) } } + + +@(require_results) +try_copy_string :: proc "contextless" (i: ^int, dst: []byte, src: string) -> bool { + if i^ < len(dst) { + i^ += copy(dst[i^:], src) + return true + } + return false +} + + +@(require_results) +try_copy_byte :: proc "contextless" (i: ^int, dst: []byte, src: byte) -> bool { + if i^ < len(dst) { + dst[i^] = src + i^ += 1 + return true + } + return false +} + + +@(require_results) +try_copy_u64 :: proc "contextless" (j: ^int, dst: []byte, x: u64) -> bool { + if j^ < len(dst) { + b :: u64(10) + u := x + + a: [129]byte + i := len(a) + for u >= b { + i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b] + u /= b + } + i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b] + + return try_copy_string(j, dst, string(a[i:])) + } + return false +} + + +@(require_results) +try_copy_caller_location :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, loc: Source_Code_Location) -> bool { + try_copy_string(i, buf, loc.file_path) or_return + + when ODIN_ERROR_POS_STYLE == .Default { + try_copy_byte(i, buf, '(') or_return + try_copy_u64(i, buf, u64(loc.line)) or_return + if loc.column != 0 { + try_copy_byte(i, buf, ':') or_return + try_copy_u64(i, buf, u64(loc.column)) or_return + } + try_copy_byte(i, buf, ')') or_return + return true + } else when ODIN_ERROR_POS_STYLE == .Unix { + try_copy_byte(i, buf, ':') or_return + try_copy_u64(i, buf, u64(loc.line)) or_return + if loc.column != 0 { + try_copy_try_copy_bytetring(i, buf, ':') or_return + try_copy_u64(i, buf, u64(loc.column)) or_return + } + try_copy_byte(i, buf, ':') or_return + return true + } else { + #panic("unhandled ODIN_ERROR_POS_STYLE") + } +} + +@(require_results) +try_copy_typeid :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, id: typeid) -> bool { + when ODIN_NO_RTTI { + if id == nil { + try_copy_string(i, buf, "nil") or_return + } else { + try_copy_string(i, buf, "") or_return + } + } else { + if id == nil { + try_copy_string(i, buf, "nil") or_return + } else { + ti := type_info_of(id) + try_copy_write_type(i, buf, ti) or_return + } + } + return true +} + +@(optimization_mode="favor_size") +try_copy_write_type :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, ti: ^Type_Info) -> bool { + if ti == nil { + try_copy_string(i, buf, "nil") or_return + return true + } + + switch info in ti.variant { + case Type_Info_Named: + try_copy_string(i, buf, info.name) or_return + case Type_Info_Integer: + switch ti.id { + case int: try_copy_string(i, buf, "int") or_return + case uint: try_copy_string(i, buf, "uint") or_return + case uintptr: try_copy_string(i, buf, "uintptr") or_return + case: + try_copy_byte(i, buf, 'i' if info.signed else 'u') or_return + try_copy_u64(i, buf, u64(8*ti.size)) or_return + } + case Type_Info_Rune: + try_copy_string(i, buf, "rune") or_return + case Type_Info_Float: + try_copy_byte(i, buf, 'f') or_return + try_copy_u64(i, buf, u64(8*ti.size)) or_return + case Type_Info_Complex: + try_copy_string(i, buf, "complex") or_return + try_copy_u64(i, buf, u64(8*ti.size)) or_return + case Type_Info_Quaternion: + try_copy_string(i, buf, "quaternion") or_return + try_copy_u64(i, buf, u64(8*ti.size)) or_return + case Type_Info_String: + if info.is_cstring { + try_copy_byte(i, buf, 'c') or_return + } + try_copy_string(i, buf, "string") or_return + switch info.encoding { + case .UTF_8: /**/ + case .UTF_16: try_copy_string(i, buf, "16") or_return + } + case Type_Info_Boolean: + switch ti.id { + case bool: try_copy_string(i, buf, "bool") or_return + case: + try_copy_byte(i, buf, 'b') or_return + try_copy_u64(i, buf, u64(8*ti.size)) or_return + } + case Type_Info_Any: + try_copy_string(i, buf, "any") or_return + case Type_Info_Type_Id: + try_copy_string(i, buf, "typeid") or_return + + case Type_Info_Pointer: + if info.elem == nil { + try_copy_string(i, buf, "rawptr") or_return + } else { + try_copy_string(i, buf, "^") or_return + try_copy_write_type(i, buf, info.elem) or_return + } + case Type_Info_Multi_Pointer: + try_copy_string(i, buf, "[^]") or_return + try_copy_write_type(i, buf, info.elem) or_return + case Type_Info_Soa_Pointer: + try_copy_string(i, buf, "#soa ^") or_return + try_copy_write_type(i, buf, info.elem) or_return + case Type_Info_Procedure: + try_copy_string(i, buf, "proc") or_return + if info.params == nil { + try_copy_string(i, buf, "()") or_return + } else { + t := info.params.variant.(Type_Info_Parameters) + try_copy_byte(i, buf, '(') or_return + for t, j in t.types { + if j > 0 { try_copy_string(i, buf, ", ") or_return } + try_copy_write_type(i, buf, t) or_return + } + try_copy_string(i, buf, ")") or_return + } + if info.results != nil { + try_copy_string(i, buf, " -> ") or_return + try_copy_write_type(i, buf, info.results) or_return + } + case Type_Info_Parameters: + count := len(info.names) + if count != 1 { try_copy_byte(i, buf, '(') or_return } + for name, j in info.names { + if j > 0 { try_copy_string(i, buf, ", ") or_return } + + t := info.types[j] + + if len(name) > 0 { + try_copy_string(i, buf, name) or_return + try_copy_string(i, buf, ": ") or_return + } + try_copy_write_type(i, buf, t) or_return + } + if count != 1 { try_copy_string(i, buf, ")") or_return } + + case Type_Info_Array: + try_copy_byte(i, buf, '[') or_return + try_copy_u64(i, buf, u64(info.count)) or_return + try_copy_byte(i, buf, ']') or_return + try_copy_write_type(i, buf, info.elem) or_return + + case Type_Info_Enumerated_Array: + if info.is_sparse { + try_copy_string(i, buf, "#sparse") or_return + } + try_copy_byte(i, buf, '[') or_return + try_copy_write_type(i, buf, info.index) or_return + try_copy_byte(i, buf, ']') or_return + try_copy_write_type(i, buf, info.elem) or_return + + + case Type_Info_Dynamic_Array: + try_copy_string(i, buf, "[dynamic]") or_return + try_copy_write_type(i, buf, info.elem) or_return + case Type_Info_Slice: + try_copy_string(i, buf, "[]") or_return + try_copy_write_type(i, buf, info.elem) or_return + + case Type_Info_Map: + try_copy_string(i, buf, "map[") or_return + try_copy_write_type(i, buf, info.key) or_return + try_copy_byte(i, buf, ']') or_return + try_copy_write_type(i, buf, info.value) or_return + + case Type_Info_Struct: + switch info.soa_kind { + case .None: // Ignore + case .Fixed: + try_copy_string(i, buf, "#soa[") or_return + try_copy_u64(i, buf, u64(info.soa_len)) or_return + try_copy_byte(i, buf, ']') or_return + try_copy_write_type(i, buf, info.soa_base_type) or_return + return true + case .Slice: + try_copy_string(i, buf, "#soa[]") or_return + try_copy_write_type(i, buf, info.soa_base_type) or_return + return true + case .Dynamic: + try_copy_string(i, buf, "#soa[dynamic]") or_return + try_copy_write_type(i, buf, info.soa_base_type) or_return + return true + } + + try_copy_string(i, buf, "struct ") or_return + if .packed in info.flags { try_copy_string(i, buf, "#packed ") or_return } + if .raw_union in info.flags { try_copy_string(i, buf, "#raw_union ") or_return } + if .all_or_none in info.flags { try_copy_string(i, buf, "#all_or_none ") or_return } + if .align in info.flags { + try_copy_string(i, buf, "#align(") or_return + try_copy_u64(i, buf, u64(ti.align)) or_return + try_copy_string(i, buf, ") ") or_return + } + try_copy_byte(i, buf, '{') or_return + for name, j in info.names[:info.field_count] { + if j > 0 { try_copy_string(i, buf, ", ") or_return } + try_copy_string(i, buf, name) or_return + try_copy_string(i, buf, ": ") or_return + try_copy_write_type(i, buf, info.types[j]) or_return + } + try_copy_byte(i, buf, '}') or_return + + case Type_Info_Union: + try_copy_string(i, buf, "union ") or_return + if info.custom_align { + try_copy_string(i, buf, "#align(") or_return + try_copy_u64(i, buf, u64(ti.align)) or_return + try_copy_string(i, buf, ") ") or_return + } + if info.no_nil { + try_copy_string(i, buf, "#no_nil ") or_return + } + try_copy_byte(i, buf, '{') or_return + for variant, j in info.variants { + if j > 0 { try_copy_string(i, buf, ", ") or_return } + try_copy_write_type(i, buf, variant) or_return + } + try_copy_string(i, buf, "}") or_return + + case Type_Info_Enum: + try_copy_string(i, buf, "enum ") or_return + try_copy_write_type(i, buf, info.base) or_return + try_copy_string(i, buf, " {") or_return + for name, j in info.names { + if j > 0 { try_copy_string(i, buf, ", ") or_return } + try_copy_string(i, buf, name) or_return + } + try_copy_string(i, buf, "}") or_return + + case Type_Info_Bit_Set: + try_copy_string(i, buf, "bit_set[") or_return + + #partial switch elem in type_info_base(info.elem).variant { + case Type_Info_Enum: + try_copy_write_type(i, buf, info.elem) or_return + case Type_Info_Rune: + print_encoded_rune(rune(info.lower)) + try_copy_string(i, buf, "..") or_return + print_encoded_rune(rune(info.upper)) + case: + print_i64(info.lower) + try_copy_string(i, buf, "..") or_return + print_i64(info.upper) + } + if info.underlying != nil { + try_copy_string(i, buf, "; ") or_return + try_copy_write_type(i, buf, info.underlying) or_return + } + try_copy_byte(i, buf, ']') or_return + + case Type_Info_Bit_Field: + try_copy_string(i, buf, "bit_field ") or_return + try_copy_write_type(i, buf, info.backing_type) or_return + try_copy_string(i, buf, " {") or_return + for name, j in info.names[:info.field_count] { + if j > 0 { try_copy_string(i, buf, ", ") or_return } + try_copy_string(i, buf, name) or_return + try_copy_string(i, buf, ": ") or_return + try_copy_write_type(i, buf, info.types[j]) or_return + try_copy_string(i, buf, " | ") or_return + try_copy_u64(i, buf, u64(info.bit_sizes[j])) or_return + } + try_copy_byte(i, buf, '}') or_return + + + case Type_Info_Simd_Vector: + try_copy_string(i, buf, "#simd[") or_return + try_copy_u64(i, buf, u64(info.count)) or_return + try_copy_byte(i, buf, ']') or_return + try_copy_write_type(i, buf, info.elem) or_return + + case Type_Info_Matrix: + try_copy_string(i, buf, "matrix[") or_return + try_copy_u64(i, buf, u64(info.row_count)) or_return + try_copy_string(i, buf, ", ") or_return + try_copy_u64(i, buf, u64(info.column_count)) or_return + try_copy_string(i, buf, "]") or_return + try_copy_write_type(i, buf, info.elem) or_return + } + return true +} \ No newline at end of file diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 42a398597..7d2240fdf 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -11064,8 +11064,20 @@ gb_internal ExprKind check_type_assertion(CheckerContext *c, Operand *o, Ast *no end:; if ((c->state_flags & StateFlag_no_type_assert) == 0) { - add_package_dependency(c, "runtime", "type_assertion_check"); - add_package_dependency(c, "runtime", "type_assertion_check2"); + bool has_context = true; + if (c->proc_name.len == 0 && c->curr_proc_sig == nullptr) { + has_context = false; + } else if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) { + has_context = false; + } + + if (has_context) { + add_package_dependency(c, "runtime", "type_assertion_check_with_context"); + add_package_dependency(c, "runtime", "type_assertion_check2_with_context"); + } else { + add_package_dependency(c, "runtime", "type_assertion_check_contextless"); + add_package_dependency(c, "runtime", "type_assertion_check2_contextless"); + } } return kind; } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 3c7092a32..aba196af8 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -3865,7 +3865,12 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { args[4] = lb_typeid(p->module, src_type); args[5] = lb_typeid(p->module, dst_type); } - lb_emit_runtime_call(p, "type_assertion_check", args); + + char const *name = "type_assertion_check_contextless"; + if (p->context_stack.count > 0) { + name = "type_assertion_check_with_context"; + } + lb_emit_runtime_call(p, name, args); } lbValue data_ptr = v; @@ -3881,16 +3886,28 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { lbValue any_id = lb_emit_struct_ev(p, v, 1); + isize arg_count = 6; + if (build_context.no_rtti) { + arg_count = 4; + } + lbValue id = lb_typeid(p->module, type); lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id); - auto args = array_make(permanent_allocator(), 6); + auto args = array_make(permanent_allocator(), arg_count); args[0] = ok; lb_set_file_line_col(p, array_slice(args, 1, args.count), pos); - args[4] = any_id; - args[5] = id; - lb_emit_runtime_call(p, "type_assertion_check", args); + if (!build_context.no_rtti) { + args[4] = any_id; + args[5] = id; + } + + char const *name = "type_assertion_check_contextless"; + if (p->context_stack.count > 0) { + name = "type_assertion_check_with_context"; + } + lb_emit_runtime_call(p, name, args); } return lb_emit_conv(p, data_ptr, tv.type); diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 43cac70c1..929239486 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -803,7 +803,12 @@ gb_internal lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type args[5] = lb_typeid(m, dst_type); args[6] = lb_emit_conv(p, value_, t_rawptr); } - lb_emit_runtime_call(p, "type_assertion_check2", args); + + char const *name = "type_assertion_check2_contextless"; + if (p->context_stack.count > 0) { + name = "type_assertion_check2_with_context"; + } + lb_emit_runtime_call(p, name, args); } return lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 0)); @@ -877,7 +882,11 @@ gb_internal lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *ty args[5] = dst_typeid; args[6] = lb_emit_struct_ev(p, value, 0); } - lb_emit_runtime_call(p, "type_assertion_check2", args); + char const *name = "type_assertion_check2_contextless"; + if (p->context_stack.count > 0) { + name = "type_assertion_check2_with_context"; + } + lb_emit_runtime_call(p, name, args); } return lb_addr(lb_emit_struct_ep(p, v.addr, 0)); -- cgit v1.2.3 From 27bd72c18a0c4ff36e8464df0232263a93a5372e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 26 Jan 2026 19:15:40 +0000 Subject: Fix constant indexing within a call that doesn't exist --- src/check_expr.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7d2240fdf..46455aacf 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5192,6 +5192,11 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v } } else { TypeAndValue index_tav = fv->field->tav; + if (index_tav.mode != Addressing_Constant) { + if (success_) *success_ = false; + if (finish_) *finish_ = true; + return empty_exact_value; + } GB_ASSERT(index_tav.mode == Addressing_Constant); ExactValue index_value = index_tav.value; if (is_type_enumerated_array(node->tav.type)) { -- cgit v1.2.3 From 07d814d9cf3bf2065e4122dba91817935dca7c60 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 29 Jan 2026 10:48:05 +0000 Subject: Add `struct #simple` to force a struct to use simple comparison if all of the fields "nearly simply comparable". --- base/runtime/core.odin | 1 + base/runtime/print.odin | 2 ++ core/odin/ast/ast.odin | 1 + core/odin/parser/parser.odin | 7 +++++++ core/reflect/types.odin | 5 +++-- src/check_expr.cpp | 6 ++++-- src/check_type.cpp | 15 +++++++++++++++ src/parser.cpp | 13 ++++++++++--- src/parser.hpp | 1 + src/types.cpp | 25 +++++++++++++++++++++++-- 10 files changed, 67 insertions(+), 9 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/base/runtime/core.odin b/base/runtime/core.odin index 58a0b8ad1..5a0b3766c 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -122,6 +122,7 @@ Type_Info_Struct_Flag :: enum u8 { raw_union = 1, all_or_none = 2, align = 3, + simple = 4, } Type_Info_Struct :: struct { diff --git a/base/runtime/print.odin b/base/runtime/print.odin index 2deb0f4b1..2cdde8152 100644 --- a/base/runtime/print.odin +++ b/base/runtime/print.odin @@ -421,6 +421,7 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) { if .packed in info.flags { print_string("#packed ") } if .raw_union in info.flags { print_string("#raw_union ") } if .all_or_none in info.flags { print_string("#all_or_none ") } + if .simple in info.flags { print_string("#simple ") } if .align in info.flags { print_string("#align(") print_u64(u64(ti.align)) @@ -835,6 +836,7 @@ write_write_type :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, t if .packed in info.flags { write_string(i, buf, "#packed ") or_return } if .raw_union in info.flags { write_string(i, buf, "#raw_union ") or_return } if .all_or_none in info.flags { write_string(i, buf, "#all_or_none ") or_return } + if .simple in info.flags { write_string(i, buf, "#simple ") or_return } if .align in info.flags { write_string(i, buf, "#align(") or_return write_u64(i, buf, u64(ti.align)) or_return diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index 6755ca4d0..ec22db434 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -798,6 +798,7 @@ Struct_Type :: struct { is_raw_union: bool, is_no_copy: bool, is_all_or_none: bool, + is_simple: bool, fields: ^Field_List, name_count: int, } diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 719cb0374..a18942e6b 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -2667,6 +2667,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { is_raw_union: bool is_no_copy: bool is_all_or_none: bool + is_simple: bool fields: ^ast.Field_List name_count: int @@ -2695,6 +2696,11 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { error(p, tag.pos, "duplicate struct tag '#%s'", tag.text) } is_all_or_none = true + case "simple": + if is_simple { + error(p, tag.pos, "duplicate struct tag '#%s'", tag.text) + } + is_simple = true case "align": if align != nil { error(p, tag.pos, "duplicate struct tag '#%s'", tag.text) @@ -2769,6 +2775,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { st.is_raw_union = is_raw_union st.is_no_copy = is_no_copy st.is_all_or_none = is_all_or_none + st.is_simple = is_simple st.fields = fields st.name_count = name_count st.where_token = where_token diff --git a/core/reflect/types.odin b/core/reflect/types.odin index d6d7011d1..385edb19b 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -696,9 +696,10 @@ write_type_writer :: #force_no_inline proc(w: io.Writer, ti: ^Type_Info, n_writt } io.write_string(w, "struct ", &n) or_return - if .packed in info.flags { io.write_string(w, "#packed ", &n) or_return } - if .raw_union in info.flags { io.write_string(w, "#raw_union ", &n) or_return } + if .packed in info.flags { io.write_string(w, "#packed ", &n) or_return } + if .raw_union in info.flags { io.write_string(w, "#raw_union ", &n) or_return } if .all_or_none in info.flags { io.write_string(w, "#all_or_none ", &n) or_return } + if .simple in info.flags { io.write_string(w, "#simple ", &n) or_return } if .align in info.flags { io.write_string(w, "#align(", &n) or_return io.write_i64(w, i64(ti.align), 10, &n) or_return diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 46455aacf..36b4f6ec6 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -12692,8 +12692,10 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan str = write_expr_to_string(str, st->polymorphic_params, shorthand); str = gb_string_appendc(str, ") "); } - if (st->is_packed) str = gb_string_appendc(str, "#packed "); - if (st->is_raw_union) str = gb_string_appendc(str, "#raw_union "); + if (st->is_packed) str = gb_string_appendc(str, "#packed "); + if (st->is_raw_union) str = gb_string_appendc(str, "#raw_union "); + if (st->is_all_or_none) str = gb_string_appendc(str, "#all_or_none "); + if (st->is_simple) str = gb_string_appendc(str, "#simple "); if (st->align) { str = gb_string_appendc(str, "#align "); str = write_expr_to_string(str, st->align, shorthand); diff --git a/src/check_type.cpp b/src/check_type.cpp index 4b8f7b6ac..41c5f48d1 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -679,6 +679,21 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast * gb_unused(where_clause_ok); } check_struct_fields(ctx, node, &struct_type->Struct.fields, &struct_type->Struct.tags, st->fields, min_field_count, struct_type, context); + + if (st->is_simple) { + bool success = true; + for (Entity *f : struct_type->Struct.fields) { + if (!is_type_nearly_simple_compare(f->type)) { + gbString s = type_to_string(f->type); + error(f->token, "'struct #simple' requires all fields to be at least 'nearly simple compare', got %s", s); + gb_string_free(s); + } + } + if (success) { + struct_type->Struct.is_simple = true; + } + } + wait_signal_set(&struct_type->Struct.fields_wait_signal); } diff --git a/src/parser.cpp b/src/parser.cpp index af533d9a3..2eb559e06 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1230,7 +1230,7 @@ gb_internal Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) { } gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice fields, isize field_count, - Ast *polymorphic_params, bool is_packed, bool is_raw_union, bool is_all_or_none, + Ast *polymorphic_params, bool is_packed, bool is_raw_union, bool is_all_or_none, bool is_simple, Ast *align, Ast *min_field_align, Ast *max_field_align, Token where_token, Array const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_StructType); @@ -1241,6 +1241,7 @@ gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice fields, i result->StructType.is_packed = is_packed; result->StructType.is_raw_union = is_raw_union; result->StructType.is_all_or_none = is_all_or_none; + result->StructType.is_simple = is_simple; result->StructType.align = align; result->StructType.min_field_align = min_field_align; result->StructType.max_field_align = max_field_align; @@ -2788,6 +2789,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { bool is_packed = false; bool is_all_or_none = false; bool is_raw_union = false; + bool is_simple = false; Ast *align = nullptr; Ast *min_field_align = nullptr; Ast *max_field_align = nullptr; @@ -2869,11 +2871,16 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { error_line("\tSuggestion: #max_field_align(%s)", s); gb_string_free(s); } - }else if (tag.string == "raw_union") { + } else if (tag.string == "raw_union") { if (is_raw_union) { syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); } is_raw_union = true; + } else if (tag.string == "simple") { + if (is_simple) { + syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); + } + is_simple = true; } else { syntax_error(tag, "Invalid struct tag '#%.*s'", LIT(tag.string)); } @@ -2919,7 +2926,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { parser_check_polymorphic_record_parameters(f, polymorphic_params); return ast_struct_type(f, token, decls, name_count, - polymorphic_params, is_packed, is_raw_union, is_all_or_none, + polymorphic_params, is_packed, is_raw_union, is_all_or_none, is_simple, align, min_field_align, max_field_align, where_token, where_clauses); } break; diff --git a/src/parser.hpp b/src/parser.hpp index 011330438..39f56ffae 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -780,6 +780,7 @@ AST_KIND(_TypeBegin, "", bool) \ bool is_raw_union; \ bool is_no_copy; \ bool is_all_or_none; \ + bool is_simple; \ }) \ AST_KIND(UnionType, "union type", struct { \ Scope *scope; \ diff --git a/src/types.cpp b/src/types.cpp index 03ff95033..a7f2bfda2 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -163,6 +163,7 @@ struct TypeStruct { bool is_packed : 1; bool is_raw_union : 1; bool is_all_or_none : 1; + bool is_simple : 1; bool is_poly_specialized : 1; std::atomic are_offsets_being_processed; @@ -2708,6 +2709,9 @@ gb_internal bool is_type_simple_compare(Type *t) { return is_type_simple_compare(t->Matrix.elem); case Type_Struct: + if (t->Struct.is_simple) { + return true; + } for_array(i, t->Struct.fields) { Entity *f = t->Struct.fields[i]; if (!is_type_simple_compare(f->type)) { @@ -2768,12 +2772,16 @@ gb_internal bool is_type_nearly_simple_compare(Type *t) { case Type_SoaPointer: case Type_Proc: case Type_BitSet: + case Type_BitField: return true; case Type_Matrix: return is_type_nearly_simple_compare(t->Matrix.elem); case Type_Struct: + if (t->Struct.is_simple) { + return true; + } for_array(i, t->Struct.fields) { Entity *f = t->Struct.fields[i]; if (!is_type_nearly_simple_compare(f->type)) { @@ -2795,6 +2803,17 @@ gb_internal bool is_type_nearly_simple_compare(Type *t) { case Type_SimdVector: return is_type_nearly_simple_compare(t->SimdVector.elem); + case Type_Tuple: + if (t->Tuple.variables.count == 1) { + return is_type_nearly_simple_compare(t->Tuple.variables[0]->type); + } + break; + + case Type_Slice: + case Type_DynamicArray: + case Type_Map: + return false; + } return false; @@ -5110,9 +5129,11 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha str = gb_string_appendc(str, ")"); } - 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_packed) str = gb_string_appendc(str, " #packed"); + if (type->Struct.is_raw_union) str = gb_string_appendc(str, " #raw_union"); if (type->Struct.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Struct.custom_align); + if (type->Struct.is_all_or_none) str = gb_string_appendc(str, " #all_or_none"); + if (type->Struct.is_simple) str = gb_string_appendc(str, " #simple"); str = gb_string_appendc(str, " {"); -- cgit v1.2.3 From 67541434f41de1eaeaa99cda0a0e3d0992869081 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 29 Jan 2026 11:58:37 +0000 Subject: Remove else check for untyped to typed --- src/check_expr.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 36b4f6ec6..1c09ad908 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4704,12 +4704,14 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar switch (t->kind) { case Type_Basic: if (operand->mode == Addressing_Constant) { - check_is_expressible(c, operand, t); + check_is_expressible(c, operand, target_type); if (operand->mode == Addressing_Invalid) { return; } update_untyped_expr_value(c, operand->expr, operand->value); - } else { + } + + { switch (operand->type->Basic.kind) { case Basic_UntypedBool: if (!is_type_boolean(target_type)) { -- cgit v1.2.3