From d3f0b31fcc48772f1457fa1eb487fd7afcc6d43d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 27 Mar 2025 15:08:53 +0000 Subject: Improve `or_else` type inference logic --- src/check_expr.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 6fba64765..91d9e669f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8977,8 +8977,14 @@ gb_internal ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node o->expr = node; return Expr_Expr; } + + Type *left_type = nullptr; + Type *right_type = nullptr; + check_or_else_split_types(c, &x, name, &left_type, &right_type); + add_type_and_value(c, arg, x.mode, x.type, x.value); + bool y_is_diverging = false; - check_expr_base(c, &y, default_value, x.type); + check_expr_base(c, &y, default_value, left_type); switch (y.mode) { case Addressing_NoValue: if (is_diverging_expr(y.expr)) { @@ -9003,11 +9009,6 @@ gb_internal ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node return Expr_Expr; } - Type *left_type = nullptr; - Type *right_type = nullptr; - check_or_else_split_types(c, &x, name, &left_type, &right_type); - add_type_and_value(c, arg, x.mode, x.type, x.value); - if (left_type != nullptr) { if (!y_is_diverging) { check_assignment(c, &y, left_type, name); -- cgit v1.2.3 From 0de802d388051c752cfce8fdc07dddfa19b45bfe Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 29 Apr 2025 17:39:28 +0100 Subject: All N-valued (N>=2) expressions in `or_else` expressions --- src/check_expr.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 91d9e669f..5185876e6 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -9011,7 +9011,19 @@ gb_internal ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node if (left_type != nullptr) { if (!y_is_diverging) { - check_assignment(c, &y, left_type, name); + if (is_type_tuple(left_type)) { + if (!is_type_tuple(y.type)) { + error(y.expr, "Found a single value where a %td-valued expression was expected", left_type->Tuple.variables.count); + } else if (!are_types_identical(left_type, y.type)) { + gbString xt = type_to_string(left_type); + gbString yt = type_to_string(y.type); + error(y.expr, "Mismatched types, expected (%s), got (%s)", xt, yt); + gb_string_free(yt); + gb_string_free(xt); + } + } else { + check_assignment(c, &y, left_type, name); + } } } else { check_or_else_expr_no_value_error(c, name, x, type_hint); -- cgit v1.2.3 From 9fb938244920b1a9d9a085ceaf651219baddc854 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Wed, 30 Apr 2025 19:39:38 +0200 Subject: fixes assigning null as a type if it's an alias but the base type isn't resolved yet It's a bit of a band aid fix because the field will get the type of the alias, not the base type, but that was already the case before #5045 so it's forward progression. Closes #5092 Fixes #5061 --- src/check_expr.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 5185876e6..69f6bb855 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1854,7 +1854,10 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam o->type = t_invalid; } if (o->type != nullptr && o->type->kind == Type_Named && o->type->Named.type_name->TypeName.is_type_alias) { - o->type = base_type(o->type); + Type *bt = base_type(o->type); + if (bt != nullptr) { + o->type = bt; + } } break; -- cgit v1.2.3 From 9681d88cd3d801f56456bdbe91fe437ebcda96cb Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 3 May 2025 14:42:20 +0200 Subject: Fix #5107 Fixes #5107 by checking whether `result_count` is non-zero before indexing `type->Proc.results->Tuple.variables`. --- 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 69f6bb855..6f585fe73 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8143,7 +8143,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c type = pt; } type = base_type(type); - if (type->kind == Type_Proc && type->Proc.optional_ok) { + if (type->kind == Type_Proc && type->Proc.optional_ok && type->Proc.result_count > 0) { operand->mode = Addressing_OptionalOk; operand->type = type->Proc.results->Tuple.variables[0]->type; if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) { -- cgit v1.2.3 From c96d8237ba1dfcc0db455bb8652ea7d8a947f817 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 3 May 2025 22:31:01 +0200 Subject: Clarify error messages for types that aren't simply comparable. Previously, it implied that these are different types: ``` W:/Scratch/scratch.odin(17:5) Error: Cannot compare expression, operator '==' not defined between the types 'Handle_Map($T=u32, $HT=u32, $Max=10000)' and 'Handle_Map($T=u32, $HT=u32, $Max=10000)' if m == {} { ^~~~~~^ ``` Now: ``` W:/Scratch/scratch.odin(20:5) Error: Cannot compare expression. Type 'Handle_Map($T=u32, $HT=u32, $Max=10000)' is not simply comparable, so operator '==' is not defined for it. if m == {} { ^~~~~~^ ``` --- src/check_expr.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 6f585fe73..10b37bbf3 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2910,9 +2910,20 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper if (!defined) { gbString xs = type_to_string(x->type, temporary_allocator()); gbString ys = type_to_string(y->type, temporary_allocator()); - err_str = gb_string_make(temporary_allocator(), - gb_bprintf("operator '%.*s' not defined between the types '%s' and '%s'", LIT(token_strings[op]), xs, ys) - ); + + if (!is_type_comparable(x->type)) { + err_str = gb_string_make(temporary_allocator(), + gb_bprintf("Type '%s' is not simply comparable, so operator '%.*s' is not defined for it", xs, LIT(token_strings[op])) + ); + } else if (!is_type_comparable(y->type)) { + err_str = gb_string_make(temporary_allocator(), + gb_bprintf("Type '%s' is not simply comparable, so operator '%.*s' is not defined for it", ys, LIT(token_strings[op])) + ); + } else { + err_str = gb_string_make(temporary_allocator(), + gb_bprintf("Operator '%.*s' not defined between the types '%s' and '%s'", LIT(token_strings[op]), xs, ys) + ); + } } else { Type *comparison_type = x->type; if (x->type == err_type && is_operand_nil(*x)) { @@ -2933,11 +2944,11 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper } else { yt = type_to_string(y->type); } - err_str = gb_string_make(temporary_allocator(), gb_bprintf("mismatched types '%s' and '%s'", xt, yt)); + err_str = gb_string_make(temporary_allocator(), gb_bprintf("Mismatched types '%s' and '%s'", xt, yt)); } if (err_str != nullptr) { - error(node, "Cannot compare expression, %s", err_str); + error(node, "Cannot compare expression. %s.", err_str); x->type = t_untyped_bool; } else { if (x->mode == Addressing_Constant && -- cgit v1.2.3 From af6b763449a7876f08b3edaf9875c57769f10bd4 Mon Sep 17 00:00:00 2001 From: bogwi Date: Mon, 5 May 2025 17:53:32 +0900 Subject: CHECK 3 done Enhance support for polymorphic procedures in type checking 1. In src/check_type.cpp, added special handling for polymorphic procedures used as default parameter values. We now allow a polymorphic procedure to be used as a default parameter value, even when its type parameters can't be immediately determined. 2. In src/check_expr.cpp, we modified the check_is_assignable_to_with_score function to handle the special case of assigning a polymorphic procedure as a default parameter. The function now allows a polymorphic procedure to be assigned to a concrete procedure type in this specific context. --- src/check_expr.cpp | 38 +++++++++++++++++++++++++++++--------- src/check_type.cpp | 11 ++++++++++- 2 files changed, 39 insertions(+), 10 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 10b37bbf3..7ccca1b57 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -643,7 +643,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E gb_internal bool check_polymorphic_procedure_assignment(CheckerContext *c, Operand *operand, Type *type, Ast *poly_def_node, PolyProcData *poly_proc_data) { if (operand->expr == nullptr) return false; - Entity *base_entity = entity_of_node(operand->expr); + Entity *base_entity = entity_from_expr(operand->expr); if (base_entity == nullptr) return false; return find_or_generate_polymorphic_procedure(c, base_entity, type, nullptr, poly_def_node, poly_proc_data); } @@ -995,14 +995,34 @@ gb_internal i64 assign_score_function(i64 distance, bool is_variadic=false) { gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false, bool allow_array_programming=true) { - i64 score = 0; - i64 distance = check_distance_between_types(c, operand, type, allow_array_programming); - bool ok = distance >= 0; - if (ok) { - score = assign_score_function(distance, is_variadic); + if (c == nullptr) { + GB_ASSERT(operand->mode == Addressing_Value); + GB_ASSERT(is_type_typed(operand->type)); + } + if (operand->mode == Addressing_Invalid || type == t_invalid) { + if (score_) *score_ = 0; + return false; + } + + // Handle polymorphic procedure used as default parameter + if (operand->mode == Addressing_Value && is_type_proc(type) && is_type_proc(operand->type)) { + Entity *e = entity_from_expr(operand->expr); + if (e != nullptr && e->kind == Entity_Procedure && is_type_polymorphic(e->type) && !is_type_polymorphic(type)) { + // Special case: Allow a polymorphic procedure to be used as default value for concrete proc type + // during the initial check. It will be properly instantiated when actually used. + if (score_) *score_ = assign_score_function(1); + return true; + } + } + + i64 score = check_distance_between_types(c, operand, type, allow_array_programming); + if (score >= 0) { + if (score_) *score_ = assign_score_function(score, is_variadic); + return true; } - if (score_) *score_ = score; - return ok; + + if (score_) *score_ = 0; + return false; } @@ -10996,7 +11016,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast return kind; case_end; - case_ast_node(i, Implicit, node) + case_ast_node(i, Implicit, node); switch (i->kind) { case Token_context: { diff --git a/src/check_type.cpp b/src/check_type.cpp index 452da4023..431698459 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1910,9 +1910,18 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para case ParameterValue_Location: case ParameterValue_Expression: case ParameterValue_Value: + // Special case for polymorphic procedures as default values + if (param_value.ast_value != nullptr) { + Entity *e = entity_from_expr(param_value.ast_value); + if (e != nullptr && e->kind == Entity_Procedure && is_type_polymorphic(e->type)) { + // Allow polymorphic procedures as default parameter values + // The type will be correctly determined at call site + break; + } + } gbString str = type_to_string(type); error(params[i], "A default value for a parameter must not be a polymorphic constant type, got %s", str); - gb_string_free(str); + gb_string_free(str); break; } } -- cgit v1.2.3 From ab9593250295137d0a654e942965feee7f506206 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 19 May 2025 20:44:27 +0200 Subject: -dynamic-literals --- src/build_settings.cpp | 1 + src/check_expr.cpp | 2 +- src/llvm_backend_expr.cpp | 2 +- src/main.cpp | 5 +++++ 4 files changed, 8 insertions(+), 2 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 04101761c..8364bbfbe 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -459,6 +459,7 @@ struct BuildContext { bool ignore_unknown_attributes; bool no_bounds_check; bool no_type_assert; + bool dynamic_literals; // Opt-in to `#+feature dynamic-literals` project-wide. bool no_output_files; bool no_crt; bool no_rpath; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7ccca1b57..167052772 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -9433,7 +9433,7 @@ gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr) { } gb_internal bool check_for_dynamic_literals(CheckerContext *c, Ast *node, AstCompoundLit *cl) { - if (cl->elems.count > 0 && (check_feature_flags(c, node) & OptInFeatureFlag_DynamicLiterals) == 0) { + if (cl->elems.count > 0 && (check_feature_flags(c, node) & OptInFeatureFlag_DynamicLiterals) == 0 && !build_context.dynamic_literals) { ERROR_BLOCK(); error(node, "Compound literals of dynamic types are disabled by default"); error_line("\tSuggestion: If you want to enable them for this specific file, add '#+feature dynamic-literals' at the top of the file\n"); diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 0909b189a..e17d958d7 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4844,7 +4844,7 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { if (cl->elems.count == 0) { break; } - GB_ASSERT(expr->file()->feature_flags & OptInFeatureFlag_DynamicLiterals); + GB_ASSERT(expr->file()->feature_flags & OptInFeatureFlag_DynamicLiterals || build_context.dynamic_literals); lbValue err = lb_dynamic_map_reserve(p, v.addr, 2*cl->elems.count, pos); gb_unused(err); diff --git a/src/main.cpp b/src/main.cpp index 00032c1ff..3692e4f06 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -319,6 +319,7 @@ enum BuildFlagKind { BuildFlag_NoBoundsCheck, BuildFlag_NoTypeAssert, BuildFlag_NoDynamicLiterals, + BuildFlag_DynamicLiterals, BuildFlag_NoCRT, BuildFlag_NoRPath, BuildFlag_NoEntryPoint, @@ -538,6 +539,7 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_NoTypeAssert, str_lit("no-type-assert"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_NoThreadLocal, str_lit("no-thread-local"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_DynamicLiterals, str_lit("dynamic-literals"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_NoRPath, str_lit("no-rpath"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test); @@ -1207,6 +1209,9 @@ gb_internal bool parse_build_flags(Array args) { case BuildFlag_NoDynamicLiterals: gb_printf_err("Warning: Use of -no-dynamic-literals is now redundant\n"); break; + case BuildFlag_DynamicLiterals: + build_context.dynamic_literals = true; + break; case BuildFlag_NoCRT: build_context.no_crt = true; break; -- cgit v1.2.3 From 96fd07e0eec5e75ab746efc9b0fe2d46caa52950 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 21 May 2025 19:20:58 +0200 Subject: Fix #5177 - Tweak error messages. --- src/check_expr.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 167052772..8721aab5d 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5461,6 +5461,16 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod } } + if (operand->type && is_type_simd_vector(type_deref(operand->type))) { + String field_name = selector->Ident.token.string; + if (field_name.len == 1) { + error(op_expr, "Extracting an element from a #simd array using .%.*s syntax is disallowed, prefer `simd.extract`", LIT(field_name)); + } else { + error(op_expr, "Extracting elements from a #simd array using .%.*s syntax is disallowed, prefer `swizzle`", LIT(field_name)); + } + return nullptr; + } + if (entity == nullptr && selector->kind == Ast_Ident && operand->type != nullptr && (is_type_array(type_deref(operand->type)) || is_type_simd_vector(type_deref(operand->type)))) { String field_name = selector->Ident.token.string; -- cgit v1.2.3 From 95183e4b9ce7f5cecf652e129ace6b7b15362167 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 21 May 2025 19:37:09 +0200 Subject: Remove now unnecessary checks. --- src/check_expr.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8721aab5d..95c898adf 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5472,7 +5472,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod } if (entity == nullptr && selector->kind == Ast_Ident && operand->type != nullptr && - (is_type_array(type_deref(operand->type)) || is_type_simd_vector(type_deref(operand->type)))) { + (is_type_array(type_deref(operand->type)))) { String field_name = selector->Ident.token.string; if (1 < field_name.len && field_name.len <= 4) { u8 swizzles_xyzw[4] = {'x', 'y', 'z', 'w'}; @@ -5527,7 +5527,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod Type *original_type = operand->type; Type *array_type = base_type(type_deref(original_type)); - GB_ASSERT(array_type->kind == Type_Array || array_type->kind == Type_SimdVector); + GB_ASSERT(array_type->kind == Type_Array); i64 array_count = get_array_type_count(array_type); @@ -5568,10 +5568,6 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod break; } - if (array_type->kind == Type_SimdVector) { - operand->mode = Addressing_Value; - } - Entity *swizzle_entity = alloc_entity_variable(nullptr, make_token_ident(field_name), operand->type, EntityState_Resolved); add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value); return swizzle_entity; -- cgit v1.2.3 From 594f1b30b4a0eadba24a712963da7bd3fc87a8eb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 24 May 2025 14:18:16 +0100 Subject: Add `Suggestion: 'context = runtime.default_context()'` --- src/check_expr.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 95c898adf..60a4d3a98 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8076,7 +8076,9 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c if (pt->kind == Type_Proc && pt->Proc.calling_convention == ProcCC_Odin) { if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) { + ERROR_BLOCK(); error(call, "'context' has not been defined within this scope, but is required for this procedure call"); + error_line("\tSuggestion: 'context = runtime.default_context()'"); } } -- cgit v1.2.3 From 57019f199cc18d75970023c88a428930527b70d4 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 31 May 2025 07:09:29 -0400 Subject: Add suggestions for `quaternionN` or `complexN` conversions Quaternions and complex numbers are constructed with `quaternion` and `complex`, but their types are of the `*N` form. These suggestions should point the user in the right direction. --- src/check_expr.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 60a4d3a98..20918c8f9 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7960,7 +7960,27 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c default: { gbString str = type_to_string(t); - error(call, "Too many arguments in conversion to '%s'", str); + if (t->kind == Type_Basic) { + ERROR_BLOCK(); + switch (t->Basic.kind) { + case Basic_complex32: + case Basic_complex64: + case Basic_complex128: + error(call, "Too many arguments in conversion to '%s'", str); + error_line("\tSuggestion: %s(1+2i) or construct with 'complex'\n", str); + break; + case Basic_quaternion64: + case Basic_quaternion128: + case Basic_quaternion256: + error(call, "Too many arguments in conversion to '%s'", str); + error_line("\tSuggestion: %s(1+2i+3j+4k) or construct with 'quaternion'\n", str); + break; + default: + error(call, "Too many arguments in conversion to '%s'", str); + } + } else { + error(call, "Too many arguments in conversion to '%s'", str); + } gb_string_free(str); } break; case 1: { -- cgit v1.2.3 From 648fa9e54a4f7e9ef5ee03f8cbb931a05ab58e56 Mon Sep 17 00:00:00 2001 From: spiel0meister Date: Mon, 2 Jun 2025 23:24:46 +0200 Subject: check if expr is null --- src/check_expr.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 20918c8f9..2287ea40b 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -263,11 +263,13 @@ gb_internal void check_did_you_mean_scope(String const &name, Scope *scope, char gb_internal Entity *entity_from_expr(Ast *expr) { expr = unparen_expr(expr); - switch (expr->kind) { - case Ast_Ident: - return expr->Ident.entity; - case Ast_SelectorExpr: - return entity_from_expr(expr->SelectorExpr.selector); + if (expr != nullptr) { + switch (expr->kind) { + case Ast_Ident: + return expr->Ident.entity; + case Ast_SelectorExpr: + return entity_from_expr(expr->SelectorExpr.selector); + } } return nullptr; } -- cgit v1.2.3 From 7b612a3a1587edcafc6301785a3f126fee692eb9 Mon Sep 17 00:00:00 2001 From: LeandroLibanio28H Date: Mon, 2 Jun 2025 20:14:19 -0300 Subject: Checking if entity is exported before checking if it's a procedure group --- src/check_expr.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 20918c8f9..12655460b 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1,3 +1,5 @@ +#include + enum CallArgumentError { CallArgumentError_None, CallArgumentError_NoneProcedureType, @@ -5373,16 +5375,6 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod return nullptr; } - check_entity_decl(c, entity, nullptr, nullptr); - if (entity->kind == Entity_ProcGroup) { - operand->mode = Addressing_ProcGroup; - operand->proc_group = entity; - - add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value); - return entity; - } - GB_ASSERT_MSG(entity->type != nullptr, "%.*s (%.*s)", LIT(entity->token.string), LIT(entity_strings[entity->kind])); - if (!is_entity_exported(entity, allow_builtin)) { gbString sel_str = expr_to_string(selector); error(node, "'%s' is not exported by '%.*s'", sel_str, LIT(import_name)); @@ -5393,6 +5385,16 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod // return nullptr; } + check_entity_decl(c, entity, nullptr, nullptr); + if (entity->kind == Entity_ProcGroup) { + operand->mode = Addressing_ProcGroup; + operand->proc_group = entity; + + add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value); + return entity; + } + GB_ASSERT_MSG(entity->type != nullptr, "%.*s (%.*s)", LIT(entity->token.string), LIT(entity_strings[entity->kind])); + if (entity->kind == Entity_ProcGroup) { Array procs = entity->ProcGroup.entities; bool skip = false; -- cgit v1.2.3 From e6b086e78d2b8619fccd86108b4725d3d2de4a59 Mon Sep 17 00:00:00 2001 From: LeandroLibanio28H Date: Mon, 2 Jun 2025 20:48:25 -0300 Subject: Removing unreacheable block (since e86fde3) --- src/check_expr.cpp | 31 ------------------------------- 1 file changed, 31 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 12655460b..a60eaaf3d 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5394,37 +5394,6 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod return entity; } GB_ASSERT_MSG(entity->type != nullptr, "%.*s (%.*s)", LIT(entity->token.string), LIT(entity_strings[entity->kind])); - - if (entity->kind == Entity_ProcGroup) { - Array procs = entity->ProcGroup.entities; - bool skip = false; - for (Entity *p : procs) { - Type *t = base_type(p->type); - if (t == t_invalid) { - continue; - } - - Operand x = {}; - x.mode = Addressing_Value; - x.type = t; - if (type_hint != nullptr) { - if (check_is_assignable_to(c, &x, type_hint)) { - entity = p; - skip = true; - break; - } - } - } - - if (!skip) { - GB_ASSERT(entity != nullptr); - operand->mode = Addressing_ProcGroup; - operand->type = t_invalid; - operand->expr = node; - operand->proc_group = entity; - return entity; - } - } } } -- cgit v1.2.3 From 82dfb436638335a7527262783bc60ffad5262cba Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 3 Jun 2025 11:18:11 +0200 Subject: Fix #5265 --- src/check_builtin.cpp | 2 +- src/check_expr.cpp | 109 ++++++++++++++++++++------------------ tests/issues/run.bat | 1 + tests/issues/run.sh | 1 + tests/issues/test_issue_5265.odin | 8 +++ 5 files changed, 68 insertions(+), 53 deletions(-) create mode 100644 tests/issues/test_issue_5265.odin (limited to 'src/check_expr.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index f8bf4b0ce..700978a76 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -5592,7 +5592,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } if (sz >= 64) { - if (is_type_unsigned(x.type)) { + if (is_type_unsigned(x.type) || is_type_unsigned(y.type)) { add_package_dependency(c, "runtime", "umodti3", true); add_package_dependency(c, "runtime", "udivti3", true); } else { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 20918c8f9..95a724d9b 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3884,6 +3884,59 @@ matrix_error: } +gb_internal void check_binary_expr_dependency(CheckerContext *c, Token op, Type *bt, bool REQUIRE) { + if (op.kind == Token_Mod || op.kind == Token_ModEq || + op.kind == Token_ModMod || op.kind == Token_ModModEq) { + if (bt->kind == Type_Basic) switch (bt->Basic.kind) { + case Basic_u128: add_package_dependency(c, "runtime", "umodti3", REQUIRE); break; + case Basic_i128: add_package_dependency(c, "runtime", "modti3", REQUIRE); break; + } + } else if (op.kind == Token_Quo || op.kind == Token_QuoEq) { + if (bt->kind == Type_Basic) switch (bt->Basic.kind) { + case Basic_complex32: add_package_dependency(c, "runtime", "quo_complex32"); break; + case Basic_complex64: add_package_dependency(c, "runtime", "quo_complex64"); break; + case Basic_complex128: add_package_dependency(c, "runtime", "quo_complex128"); break; + case Basic_quaternion64: add_package_dependency(c, "runtime", "quo_quaternion64"); break; + case Basic_quaternion128: add_package_dependency(c, "runtime", "quo_quaternion128"); break; + case Basic_quaternion256: add_package_dependency(c, "runtime", "quo_quaternion256"); break; + + case Basic_u128: add_package_dependency(c, "runtime", "udivti3", REQUIRE); break; + case Basic_i128: add_package_dependency(c, "runtime", "divti3", REQUIRE); break; + } + } else if (op.kind == Token_Mul || op.kind == Token_MulEq) { + if (bt->kind == Type_Basic) switch (bt->Basic.kind) { + case Basic_quaternion64: add_package_dependency(c, "runtime", "mul_quaternion64"); break; + case Basic_quaternion128: add_package_dependency(c, "runtime", "mul_quaternion128"); break; + case Basic_quaternion256: add_package_dependency(c, "runtime", "mul_quaternion256"); break; + + + case Basic_u128: + case Basic_i128: + if (is_arch_wasm()) { + add_package_dependency(c, "runtime", "__multi3", REQUIRE); + } + break; + } + } else if (op.kind == Token_Shl || op.kind == Token_ShlEq) { + if (bt->kind == Type_Basic) switch (bt->Basic.kind) { + case Basic_u128: + case Basic_i128: + if (is_arch_wasm()) { + add_package_dependency(c, "runtime", "__ashlti3", REQUIRE); + } + break; + } + } else if (op.kind == Token_Shr || op.kind == Token_ShrEq) { + if (bt->kind == Type_Basic) switch (bt->Basic.kind) { + case Basic_u128: + case Basic_i128: + if (is_arch_wasm()) { + add_package_dependency(c, "runtime", "__lshrti3", REQUIRE); + } + break; + } + } +} gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint, bool use_lhs_as_type_hint=false) { GB_ASSERT(node->kind == Ast_BinaryExpr); @@ -4081,58 +4134,10 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ } bool REQUIRE = true; - Type *bt = base_type(x->type); - if (op.kind == Token_Mod || op.kind == Token_ModEq || - op.kind == Token_ModMod || op.kind == Token_ModModEq) { - if (bt->kind == Type_Basic) switch (bt->Basic.kind) { - case Basic_u128: add_package_dependency(c, "runtime", "umodti3", REQUIRE); break; - case Basic_i128: add_package_dependency(c, "runtime", "modti3", REQUIRE); break; - } - } else if (op.kind == Token_Quo || op.kind == Token_QuoEq) { - if (bt->kind == Type_Basic) switch (bt->Basic.kind) { - case Basic_complex32: add_package_dependency(c, "runtime", "quo_complex32"); break; - case Basic_complex64: add_package_dependency(c, "runtime", "quo_complex64"); break; - case Basic_complex128: add_package_dependency(c, "runtime", "quo_complex128"); break; - case Basic_quaternion64: add_package_dependency(c, "runtime", "quo_quaternion64"); break; - case Basic_quaternion128: add_package_dependency(c, "runtime", "quo_quaternion128"); break; - case Basic_quaternion256: add_package_dependency(c, "runtime", "quo_quaternion256"); break; - - case Basic_u128: add_package_dependency(c, "runtime", "udivti3", REQUIRE); break; - case Basic_i128: add_package_dependency(c, "runtime", "divti3", REQUIRE); break; - } - } else if (op.kind == Token_Mul || op.kind == Token_MulEq) { - if (bt->kind == Type_Basic) switch (bt->Basic.kind) { - case Basic_quaternion64: add_package_dependency(c, "runtime", "mul_quaternion64"); break; - case Basic_quaternion128: add_package_dependency(c, "runtime", "mul_quaternion128"); break; - case Basic_quaternion256: add_package_dependency(c, "runtime", "mul_quaternion256"); break; - - - case Basic_u128: - case Basic_i128: - if (is_arch_wasm()) { - add_package_dependency(c, "runtime", "__multi3", REQUIRE); - } - break; - } - } else if (op.kind == Token_Shl || op.kind == Token_ShlEq) { - if (bt->kind == Type_Basic) switch (bt->Basic.kind) { - case Basic_u128: - case Basic_i128: - if (is_arch_wasm()) { - add_package_dependency(c, "runtime", "__ashlti3", REQUIRE); - } - break; - } - } else if (op.kind == Token_Shr || op.kind == Token_ShrEq) { - if (bt->kind == Type_Basic) switch (bt->Basic.kind) { - case Basic_u128: - case Basic_i128: - if (is_arch_wasm()) { - add_package_dependency(c, "runtime", "__lshrti3", REQUIRE); - } - break; - } - } + Type *btx = base_type(x->type); + Type *bty = base_type(y->type); + check_binary_expr_dependency(c, op, btx, REQUIRE); + check_binary_expr_dependency(c, op, bty, REQUIRE); if (token_is_shift(op.kind)) { check_shift(c, x, y, node, type_hint); diff --git a/tests/issues/run.bat b/tests/issues/run.bat index 156b1754e..bbe9b7ca6 100644 --- a/tests/issues/run.bat +++ b/tests/issues/run.bat @@ -21,6 +21,7 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style ..\..\..\odin build ..\test_issue_5043.odin %COMMON% || exit /b ..\..\..\odin build ..\test_issue_5097.odin %COMMON% || exit /b ..\..\..\odin build ..\test_issue_5097-2.odin %COMMON% || exit /b +..\..\..\odin build ..\test_issue_5265.odin %COMMON% || exit /b @echo off diff --git a/tests/issues/run.sh b/tests/issues/run.sh index cd70f6401..228efce7f 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -28,6 +28,7 @@ fi $ODIN build ../test_issue_5043.odin $COMMON $ODIN build ../test_issue_5097.odin $COMMON $ODIN build ../test_issue_5097-2.odin $COMMON +$ODIN build ../test_issue_5265.odin $COMMON set +x diff --git a/tests/issues/test_issue_5265.odin b/tests/issues/test_issue_5265.odin new file mode 100644 index 000000000..f61f05b0a --- /dev/null +++ b/tests/issues/test_issue_5265.odin @@ -0,0 +1,8 @@ +// Tests issue #5265 https://github.com/odin-lang/Odin/issues/5265 +package test_issues + +main :: proc() { + a: i128 = 1 + assert(1 / a == 1) + assert(a / 1 == 1) +} \ No newline at end of file -- cgit v1.2.3 From 0f5e30783cd3b13ff41a2e57f68795e02502c27c Mon Sep 17 00:00:00 2001 From: Leandro Libanio Date: Tue, 3 Jun 2025 08:55:38 -0300 Subject: Update check_expr.cpp Removing iostream --- src/check_expr.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index a60eaaf3d..d4c173adc 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1,5 +1,3 @@ -#include - enum CallArgumentError { CallArgumentError_None, CallArgumentError_NoneProcedureType, -- cgit v1.2.3 From d1fcec63cc9c34e13b20d5815abdba7ae80b78c1 Mon Sep 17 00:00:00 2001 From: spiel0meister Date: Tue, 3 Jun 2025 14:46:44 +0200 Subject: early return; fix indentation --- src/check_expr.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 2287ea40b..e3186a9b1 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -263,13 +263,14 @@ gb_internal void check_did_you_mean_scope(String const &name, Scope *scope, char gb_internal Entity *entity_from_expr(Ast *expr) { expr = unparen_expr(expr); - if (expr != nullptr) { - switch (expr->kind) { - case Ast_Ident: - return expr->Ident.entity; - case Ast_SelectorExpr: - return entity_from_expr(expr->SelectorExpr.selector); - } + if (expr == nullptr) { + return nullptr; + } + switch (expr->kind) { + case Ast_Ident: + return expr->Ident.entity; + case Ast_SelectorExpr: + return entity_from_expr(expr->SelectorExpr.selector); } return nullptr; } -- cgit v1.2.3 From 6804f4c4716c65f2729945de3e7b7413fb061ee1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 4 Jun 2025 13:56:46 +0100 Subject: Add support for `#soa[N]T` compound literals --- src/check_expr.cpp | 184 ++++++++++++++++++++++++++------------------- src/llvm_backend_const.cpp | 142 ++++++++++++++++++++++++++++++++++ 2 files changed, 249 insertions(+), 77 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 99d464da5..9308eab8e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -9464,6 +9464,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * } bool is_to_be_determined_array_count = false; bool is_constant = true; + bool is_soa = false; Ast *type_expr = cl->type; @@ -9496,8 +9497,14 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * GB_ASSERT(tag->kind == Ast_BasicDirective); String name = tag->BasicDirective.name.string; if (name == "soa") { - error(node, "#soa arrays are not supported for compound literals"); - return kind; + is_soa = true; + if (count == nullptr) { + error(node, "#soa slices are not supported for compound literals"); + return kind; + } else if (count->kind == Ast_UnaryExpr && + count->UnaryExpr.op.kind == Token_Question) { + error(node, "#soa fixed length arrays must specify their length and cannot use ?"); + } } } } @@ -9507,7 +9514,8 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * GB_ASSERT(tag->kind == Ast_BasicDirective); String name = tag->BasicDirective.name.string; if (name == "soa") { - error(node, "#soa arrays are not supported for compound literals"); + is_soa = true; + error(node, "#soa dynamic arrays are not supported for compound literals"); return kind; } } @@ -9536,101 +9544,101 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * switch (t->kind) { - case Type_Struct: { + case Type_Struct: if (cl->elems.count == 0) { break; // NOTE(bill): No need to init } - if (t->Struct.soa_kind != StructSoa_None) { - error(node, "#soa arrays are not supported for compound literals"); - break; - } - - if (t->Struct.is_raw_union) { - if (cl->elems.count > 0) { - // NOTE: unions cannot be constant - is_constant = false; + if (t->Struct.soa_kind == StructSoa_None) { + if (t->Struct.is_raw_union) { + if (cl->elems.count > 0) { + // NOTE: unions cannot be constant + is_constant = false; - if (cl->elems[0]->kind != Ast_FieldValue) { - gbString type_str = type_to_string(type); - error(node, "%s ('struct #raw_union') compound literals are only allowed to contain 'field = value' elements", type_str); - gb_string_free(type_str); - } else { - if (cl->elems.count != 1) { + if (cl->elems[0]->kind != Ast_FieldValue) { gbString type_str = type_to_string(type); - error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count); + error(node, "%s ('struct #raw_union') compound literals are only allowed to contain 'field = value' elements", type_str); gb_string_free(type_str); } else { - check_compound_literal_field_values(c, cl->elems, o, type, is_constant); + if (cl->elems.count != 1) { + gbString type_str = type_to_string(type); + error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count); + gb_string_free(type_str); + } else { + check_compound_literal_field_values(c, cl->elems, o, type, is_constant); + } } } - } - break; - } - - wait_signal_until_available(&t->Struct.fields_wait_signal); - isize field_count = t->Struct.fields.count; - isize min_field_count = t->Struct.fields.count; - for (isize i = min_field_count-1; i >= 0; i--) { - Entity *e = t->Struct.fields[i]; - GB_ASSERT(e->kind == Entity_Variable); - if (e->Variable.param_value.kind != ParameterValue_Invalid) { - min_field_count--; - } else { break; } - } - if (cl->elems[0]->kind == Ast_FieldValue) { - check_compound_literal_field_values(c, cl->elems, o, type, is_constant); - } else { - bool seen_field_value = false; - - for_array(index, cl->elems) { - Entity *field = nullptr; - Ast *elem = cl->elems[index]; - if (elem->kind == Ast_FieldValue) { - seen_field_value = true; - error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); - continue; - } else if (seen_field_value) { - error(elem, "Value elements cannot be used after a 'field = value'"); - continue; - } - if (index >= field_count) { - error(elem, "Too many values in structure literal, expected %td, got %td", field_count, cl->elems.count); + wait_signal_until_available(&t->Struct.fields_wait_signal); + isize field_count = t->Struct.fields.count; + isize min_field_count = t->Struct.fields.count; + for (isize i = min_field_count-1; i >= 0; i--) { + Entity *e = t->Struct.fields[i]; + GB_ASSERT(e->kind == Entity_Variable); + if (e->Variable.param_value.kind != ParameterValue_Invalid) { + min_field_count--; + } else { break; } + } - if (field == nullptr) { - field = t->Struct.fields[index]; - } + if (cl->elems[0]->kind == Ast_FieldValue) { + check_compound_literal_field_values(c, cl->elems, o, type, is_constant); + } else { + bool seen_field_value = false; - Operand o = {}; - check_expr_or_type(c, &o, elem, field->type); + for_array(index, cl->elems) { + Entity *field = nullptr; + Ast *elem = cl->elems[index]; + if (elem->kind == Ast_FieldValue) { + seen_field_value = true; + error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } else if (seen_field_value) { + error(elem, "Value elements cannot be used after a 'field = value'"); + continue; + } + if (index >= field_count) { + error(elem, "Too many values in structure literal, expected %td, got %td", field_count, cl->elems.count); + break; + } - if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) { - is_constant = false; - } - if (is_constant) { - is_constant = check_is_operand_compound_lit_constant(c, &o); - } + if (field == nullptr) { + field = t->Struct.fields[index]; + } - check_assignment(c, &o, field->type, str_lit("structure literal")); - } - if (cl->elems.count < field_count) { - if (min_field_count < field_count) { - if (cl->elems.count < min_field_count) { - error(cl->close, "Too few values in structure literal, expected at least %td, got %td", min_field_count, cl->elems.count); - } - } else { - error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count); + Operand o = {}; + check_expr_or_type(c, &o, elem, field->type); + + if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) { + is_constant = false; + } + if (is_constant) { + is_constant = check_is_operand_compound_lit_constant(c, &o); + } + + check_assignment(c, &o, field->type, str_lit("structure literal")); + } + if (cl->elems.count < field_count) { + if (min_field_count < field_count) { + if (cl->elems.count < min_field_count) { + error(cl->close, "Too few values in structure literal, expected at least %td, got %td", min_field_count, cl->elems.count); + } + } else { + error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count); + } } } - } - break; - } + break; + } else if (t->Struct.soa_kind != StructSoa_Fixed) { + error(node, "#soa slices and dynamic arrays are not supported for compound literals"); + break; + } + /*fallthrough*/ case Type_Slice: case Type_Array: @@ -9641,7 +9649,14 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * Type *elem_type = nullptr; String context_name = {}; i64 max_type_count = -1; - if (t->kind == Type_Slice) { + if (t->kind == Type_Struct) { + GB_ASSERT(t->Struct.soa_kind == StructSoa_Fixed); + elem_type = t->Struct.soa_elem; + context_name = str_lit("#soa array literal"); + if (!is_to_be_determined_array_count) { + max_type_count = t->Struct.soa_count; + } + } else if (t->kind == Type_Slice) { elem_type = t->Slice.elem; context_name = str_lit("slice literal"); } else if (t->kind == Type_Array) { @@ -9650,6 +9665,12 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * if (!is_to_be_determined_array_count) { max_type_count = t->Array.count; } + } else if (t->kind == Type_Array) { + elem_type = t->Array.elem; + context_name = str_lit("array literal"); + if (!is_to_be_determined_array_count) { + max_type_count = t->Array.count; + } } else if (t->kind == Type_DynamicArray) { elem_type = t->DynamicArray.elem; context_name = str_lit("dynamic array literal"); @@ -9817,6 +9838,15 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max); } } + } else if (t->kind == Type_Struct) { + GB_ASSERT(t->Struct.soa_kind == StructSoa_Fixed); + if (is_to_be_determined_array_count) { + t->Struct.soa_count = cast(i32)max; + } else if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) { + if (0 < max && max < t->Struct.soa_count) { + error(node, "Expected %lld values for this #soa array literal, got %lld", cast(long long)t->Struct.soa_count, cast(long long)max); + } + } } diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 51c8a4449..02bb7473c 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -851,6 +851,148 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb case ExactValue_Compound: if (is_type_slice(type)) { return lb_const_value(m, type, value, cc); + } else if (is_type_soa_struct(type)) { + GB_ASSERT(type->kind == Type_Struct); + GB_ASSERT(type->Struct.soa_kind == StructSoa_Fixed); + ast_node(cl, CompoundLit, value.value_compound); + Type *elem_type = type->Struct.soa_elem; + isize elem_count = cl->elems.count; + if (elem_count == 0 || !elem_type_can_be_constant(elem_type)) { + return lb_const_nil(m, original_type); + } + if (cl->elems[0]->kind == Ast_FieldValue) { + TEMPORARY_ALLOCATOR_GUARD(); + + // TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand + + isize elem_count = cast(isize)type->Struct.soa_count; + + LLVMValueRef *aos_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, elem_count); + + isize value_index = 0; + for (i64 i = 0; i < elem_count; i++) { + bool found = false; + + for (isize j = 0; j < elem_count; j++) { + Ast *elem = cl->elems[j]; + ast_node(fv, FieldValue, elem); + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op != Token_RangeHalf) { + hi += 1; + } + if (lo == i) { + TypeAndValue tav = fv->value->tav; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; + for (i64 k = lo; k < hi; k++) { + aos_values[value_index++] = val; + } + + found = true; + i += (hi-lo-1); + break; + } + } else { + TypeAndValue index_tav = fv->field->tav; + GB_ASSERT(index_tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(index_tav.value); + if (index == i) { + TypeAndValue tav = fv->value->tav; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; + aos_values[value_index++] = val; + found = true; + break; + } + } + } + + if (!found) { + aos_values[value_index++] = nullptr; + } + } + + + isize field_count = type->Struct.fields.count; + LLVMValueRef *soa_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, field_count); + + for (isize i = 0; i < field_count; i++) { + TEMPORARY_ALLOCATOR_GUARD(); + + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, elem_count); + + Entity *f = type->Struct.fields[i]; + Type *array_type = f->type; + GB_ASSERT(array_type->kind == Type_Array); + Type *field_type = array_type->Array.elem; + + for (isize j = 0; j < elem_count; j++) { + LLVMValueRef v = aos_values[j]; + if (v != nullptr) { + values[j] = llvm_const_extract_value(m, v, cast(unsigned)i); + } else { + values[j] = LLVMConstNull(lb_type(m, field_type)); + } + } + + soa_values[i] = lb_build_constant_array_values(m, array_type, field_type, elem_count, values, cc); + } + + res.value = llvm_const_named_struct(m, type, soa_values, field_count); + return res; + } else { + GB_ASSERT_MSG(elem_count == type->Struct.soa_count, "%td != %td", elem_count, type->Struct.soa_count); + + TEMPORARY_ALLOCATOR_GUARD(); + + isize elem_count = cast(isize)type->Struct.soa_count; + + LLVMValueRef *aos_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, elem_count); + + for (isize i = 0; i < elem_count; i++) { + TypeAndValue tav = cl->elems[i]->tav; + GB_ASSERT(tav.mode != Addressing_Invalid); + aos_values[i] = lb_const_value(m, elem_type, tav.value, cc).value; + } + for (isize i = elem_count; i < type->Struct.soa_count; i++) { + aos_values[i] = nullptr; + } + + isize field_count = type->Struct.fields.count; + LLVMValueRef *soa_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, field_count); + + for (isize i = 0; i < field_count; i++) { + TEMPORARY_ALLOCATOR_GUARD(); + + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, elem_count); + + Entity *f = type->Struct.fields[i]; + Type *array_type = f->type; + GB_ASSERT(array_type->kind == Type_Array); + Type *field_type = array_type->Array.elem; + + for (isize j = 0; j < elem_count; j++) { + LLVMValueRef v = aos_values[j]; + if (v != nullptr) { + values[j] = llvm_const_extract_value(m, v, cast(unsigned)i); + } else { + values[j] = LLVMConstNull(lb_type(m, field_type)); + } + } + + soa_values[i] = lb_build_constant_array_values(m, array_type, field_type, elem_count, values, cc); + } + + res.value = llvm_const_named_struct(m, type, soa_values, field_count); + return res; + } } else if (is_type_array(type)) { ast_node(cl, CompoundLit, value.value_compound); Type *elem_type = type->Array.elem; -- cgit v1.2.3 From 5ed2735658c4ae1ad9aa5d790a6ebd64136bdf27 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 10 Jun 2025 06:13:14 -0400 Subject: Guard against invalid proc types in parameter list Fixes #4362 --- src/check_expr.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 9308eab8e..335b82f1a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6497,7 +6497,7 @@ gb_internal Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_ Entity **lhs = nullptr; isize lhs_count = -1; - if (proc_type == nullptr) { + if (proc_type == nullptr || proc_type == t_invalid) { return nullptr; } @@ -6697,6 +6697,9 @@ gb_internal bool check_call_arguments_single(CheckerContext *c, Ast *call, Opera GB_ASSERT(proc_type != nullptr); proc_type = base_type(proc_type); + if (proc_type == t_invalid) { + return false; + } GB_ASSERT(proc_type->kind == Type_Proc); CallArgumentError err = check_call_arguments_internal(c, call, e, proc_type, positional_operands, named_operands, show_error_mode, data); -- cgit v1.2.3