From e2b276f3ea2b00640a028c5455c283aa45261908 Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Sat, 1 Nov 2025 01:32:46 -0400 Subject: [PATCH 1/5] Parse empty idents after selector as a selector expr with an empty field --- core/odin/parser/parser.odin | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 7ebe275ed..9ce484a10 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -3210,6 +3210,17 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr { return ce } +empty_selector_expr :: proc(tok: tokenizer.Token, operand: ^ast.Expr) -> ^ast.Selector_Expr { + field := ast.new(ast.Ident, tok.pos, end_pos(tok)) + field.name = "" + + sel := ast.new(ast.Selector_Expr, operand.pos, field) + sel.expr = operand + sel.op = tok + sel.field = field + + return sel +} parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^ast.Expr) { operand = value @@ -3343,8 +3354,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a case: error(p, p.curr_tok.pos, "expected a selector") - advance_token(p) - operand = ast.new(ast.Bad_Expr, operand.pos, end_pos(tok)) + operand = empty_selector_expr(tok, operand) } case .Arrow_Right: @@ -3361,8 +3371,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a operand = sel case: error(p, p.curr_tok.pos, "expected a selector") - advance_token(p) - operand = ast.new(ast.Bad_Expr, operand.pos, end_pos(tok)) + operand = empty_selector_expr(tok, operand) } case .Pointer: From 505b85ead546a093a94b157889fbfe83731a55c2 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 2 Nov 2025 07:45:40 +0100 Subject: [PATCH 2/5] Add #subtype using to name canonicalization --- src/name_canonicalization.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/name_canonicalization.cpp b/src/name_canonicalization.cpp index 8bacfabc6..88235484a 100644 --- a/src/name_canonicalization.cpp +++ b/src/name_canonicalization.cpp @@ -756,6 +756,14 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) { if (i > 0) { type_writer_appendc(w, CANONICAL_FIELD_SEPARATOR); } + + if (f->flags & EntityFlags_IsSubtype) { + type_writer_appendc(w, "#subtype"); + } + + if (f->flags & EntityFlag_Using) { + type_writer_appendc(w, "using"); + } type_writer_append(w, f->token.string.text, f->token.string.len); type_writer_appendc(w, CANONICAL_TYPE_SEPARATOR); write_type_to_canonical_string(w, f->type); From e364e76f7f8978ffef78321f962324a4fc4dcaae Mon Sep 17 00:00:00 2001 From: thetarnav Date: Sun, 2 Nov 2025 12:13:11 +0100 Subject: [PATCH 3/5] Add `inject_at_soa` and `append_nothing_soa` procedures --- base/runtime/core_builtin_soa.odin | 111 ++++++++++++++++++++++ tests/core/runtime/test_core_runtime.odin | 59 ++++++++++++ 2 files changed, 170 insertions(+) diff --git a/base/runtime/core_builtin_soa.odin b/base/runtime/core_builtin_soa.odin index 331254ac7..5c3253f97 100644 --- a/base/runtime/core_builtin_soa.odin +++ b/base/runtime/core_builtin_soa.odin @@ -501,6 +501,117 @@ append_soa :: proc{ } +// `append_nothing_soa` appends an empty value to a dynamic SOA array. It returns `1, nil` if successful, and `0, err` when it was not possible, +// whatever `err` happens to be. +@builtin +append_nothing_soa :: proc(array: ^$T/#soa[dynamic]$E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { + if array == nil { + return 0, nil + } + prev_len := len(array) + resize(array, len(array)+1, loc) or_return + return len(array)-prev_len, nil +} + + +// `inject_at_elem_soa` injects an element in a dynamic SOA array at a specified index and moves the previous elements after that index "across" +@builtin +inject_at_elem_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { + when !ODIN_NO_BOUNDS_CHECK { + ensure(index >= 0, "Index must be positive.", loc) + } + if array == nil { + return + } + n := max(len(array), index) + m :: 1 + new_len := n + m + + resize(array, new_len, loc) or_return + when size_of(E) != 0 { + ti := type_info_base(type_info_of(typeid_of(T))) + si := &ti.variant.(Type_Info_Struct) + + field_count := len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E) + + item_offset := 0 + + arg_copy := arg + arg_ptr := &arg_copy + + for i in 0.. (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { + when !ODIN_NO_BOUNDS_CHECK { + ensure(index >= 0, "Index must be positive.", loc) + } + if array == nil { + return + } + if len(args) == 0 { + ok = true + return + } + + n := max(len(array), index) + m := len(args) + new_len := n + m + + resize(array, new_len, loc) or_return + when size_of(E) != 0 { + ti := type_info_base(type_info_of(typeid_of(T))) + si := &ti.variant.(Type_Info_Struct) + + field_count := len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E) + + item_offset := 0 + + args_ptr := &args[0] + + for i in 0.. Allocator_Error { field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E) when field_count != 0 { diff --git a/tests/core/runtime/test_core_runtime.odin b/tests/core/runtime/test_core_runtime.odin index 881b5f41a..f3559954d 100644 --- a/tests/core/runtime/test_core_runtime.odin +++ b/tests/core/runtime/test_core_runtime.odin @@ -292,6 +292,65 @@ test_soa_array_allocator_resize_overlapping :: proc(t: ^testing.T) { testing.expect_value(t, array[3], [3]int{0, 0, 0}) } +@(test) +test_soa_array_inject_at_elem :: proc(t: ^testing.T) { + + V :: struct {a: u8, b: f32} + + array := make(#soa[dynamic]V, 0, 2) + defer delete(array) + + append(&array, V{1, 1.5}, V{2, 2.5}, V{3, 3.5}) + + expect_inject(t, &array, 0, {0, 0.5}, {{0, 0.5}, {1, 1.5}, {2, 2.5}, {3, 3.5}}) + expect_inject(t, &array, 2, {5, 5.5}, {{0, 0.5}, {1, 1.5}, {5, 5.5}, {2, 2.5}, {3, 3.5}}) + expect_inject(t, &array, 5, {9, 9.5}, {{0, 0.5}, {1, 1.5}, {5, 5.5}, {2, 2.5}, {3, 3.5}, {9, 9.5}}) + + expect_inject :: proc(t: ^testing.T, arr: ^#soa[dynamic]V, index: int, arg: V, expected_slice: []V) { + ok, err := inject_at_soa(arr, index, arg) + testing.expectf(t, ok == true, "Injection of %v at index %d failed", arg, index) + testing.expectf(t, err == nil, "Injection allocation of %v at index %d failed: %v", arg, index, err) + equals := len(arr) == len(expected_slice) + for e, i in expected_slice { + if arr[i] != e { + equals = false + break + } + } + testing.expectf(t, equals, "After injection of %v at index %d, expected array to be\n&%v, got\n%v", arg, index, expected_slice, arr) + } +} + +@(test) +test_soa_array_inject_at_elems :: proc(t: ^testing.T) { + + V :: struct {a: u8, b: f32} + + array := make(#soa[dynamic]V, 0, 2) + defer delete(array) + + append(&array, V{1, 1.5}, V{2, 2.5}, V{3, 3.5}) + + expect_inject(t, &array, 0, {{0, 0.5}}, {{0, 0.5}, {1, 1.5}, {2, 2.5}, {3, 3.5}}) + expect_inject(t, &array, 2, {{5, 5.5}, {6, 6.5}}, {{0, 0.5}, {1, 1.5}, {5, 5.5}, {6, 6.5}, {2, 2.5}, {3, 3.5}}) + expect_inject(t, &array, 6, {{9, 9.5}, {10, 10.5}}, {{0, 0.5}, {1, 1.5}, {5, 5.5}, {6, 6.5}, {2, 2.5}, {3, 3.5}, {9, 9.5}, {10, 10.5}}) + expect_inject(t, &array, 6, {}, {{0, 0.5}, {1, 1.5}, {5, 5.5}, {6, 6.5}, {2, 2.5}, {3, 3.5}, {9, 9.5}, {10, 10.5}}) + + expect_inject :: proc(t: ^testing.T, arr: ^#soa[dynamic]V, index: int, args: []V, expected_slice: []V) { + ok, err := inject_at_soa(arr, index, ..args) + testing.expectf(t, ok == true, "Injection of %v at index %d failed", args, index) + testing.expectf(t, err == nil, "Injection allocation of %v at index %d failed: %v", args, index, err) + equals := len(arr) == len(expected_slice) + for e, i in expected_slice { + if arr[i] != e { + equals = false + break + } + } + testing.expectf(t, equals, "After injection of %v at index %d, expected array to be\n&%v, got\n%v", args, index, expected_slice, arr) + } +} + @(test) test_memory_equal :: proc(t: ^testing.T) { data: [256]u8 From b6181a768e368536819621b145ceaa5bb05d60ac Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 2 Nov 2025 13:28:34 +0100 Subject: [PATCH 4/5] Add space --- src/name_canonicalization.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/name_canonicalization.cpp b/src/name_canonicalization.cpp index 88235484a..edadde35e 100644 --- a/src/name_canonicalization.cpp +++ b/src/name_canonicalization.cpp @@ -758,11 +758,11 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) { } if (f->flags & EntityFlags_IsSubtype) { - type_writer_appendc(w, "#subtype"); + type_writer_appendc(w, "#subtype "); } if (f->flags & EntityFlag_Using) { - type_writer_appendc(w, "using"); + type_writer_appendc(w, "using "); } type_writer_append(w, f->token.string.text, f->token.string.len); type_writer_appendc(w, CANONICAL_TYPE_SEPARATOR); From 72754962ab6941c85a6bf373b18a149146461730 Mon Sep 17 00:00:00 2001 From: thetarnav Date: Sun, 2 Nov 2025 14:20:55 +0100 Subject: [PATCH 5/5] Use resize_soa instead of resize proc group in soa procs --- base/runtime/core_builtin_soa.odin | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/base/runtime/core_builtin_soa.odin b/base/runtime/core_builtin_soa.odin index 5c3253f97..0ccc8fd9b 100644 --- a/base/runtime/core_builtin_soa.odin +++ b/base/runtime/core_builtin_soa.odin @@ -509,7 +509,7 @@ append_nothing_soa :: proc(array: ^$T/#soa[dynamic]$E, loc := #caller_location) return 0, nil } prev_len := len(array) - resize(array, len(array)+1, loc) or_return + resize_soa(array, len(array)+1, loc) or_return return len(array)-prev_len, nil } @@ -527,7 +527,8 @@ inject_at_elem_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, #no_ m :: 1 new_len := n + m - resize(array, new_len, loc) or_return + resize_soa(array, new_len, loc) or_return + when size_of(E) != 0 { ti := type_info_base(type_info_of(typeid_of(T))) si := &ti.variant.(Type_Info_Struct) @@ -553,6 +554,7 @@ inject_at_elem_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, #no_ item_offset += type.size } } + ok = true return } @@ -575,7 +577,8 @@ inject_at_elems_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, #no m := len(args) new_len := n + m - resize(array, new_len, loc) or_return + resize_soa(array, new_len, loc) or_return + when size_of(E) != 0 { ti := type_info_base(type_info_of(typeid_of(T))) si := &ti.variant.(Type_Info_Struct) @@ -604,6 +607,7 @@ inject_at_elems_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, #no item_offset += type.size } } + ok = true return }