From 403ca2fb2ea97d5567a70aea43336c87cba7a348 Mon Sep 17 00:00:00 2001 From: Tohei Ichikawa Date: Mon, 15 Sep 2025 20:40:20 -0400 Subject: [PATCH 1/4] Improve type inferencing of literals when calling proc groups --- src/check_expr.cpp | 6 + .../test_proc_group_type_inference.odin | 142 ++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 tests/internal/test_proc_group_type_inference.odin diff --git a/src/check_expr.cpp b/src/check_expr.cpp index d2505c047..417d1b9a4 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6977,6 +6977,10 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, array_unordered_remove(&procs, proc_index); continue; } + if (!pt->Proc.variadic && max_arg_count != ISIZE_MAX && param_count < max_arg_count) { + array_unordered_remove(&procs, proc_index); + continue; + } proc_index++; } } @@ -7014,6 +7018,8 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, { // NOTE(bill, 2019-07-13): This code is used to improve the type inference for procedure groups // where the same positional parameter has the same type value (and ellipsis) + + //TODO: get rid of proc_arg_count. make lhs as long as longest proc with most params. watch out for null safety isize proc_arg_count = -1; for (Entity *p : procs) { Type *pt = base_type(p->type); diff --git a/tests/internal/test_proc_group_type_inference.odin b/tests/internal/test_proc_group_type_inference.odin new file mode 100644 index 000000000..08ff2a3c5 --- /dev/null +++ b/tests/internal/test_proc_group_type_inference.odin @@ -0,0 +1,142 @@ +#+feature dynamic-literals + +package test_internal + +import "core:testing" + +@test +test_type_inference_on_literals_for_various_parameters_combinations :: proc(t: ^testing.T) { + Bit_Set :: bit_set[enum{A, B, C}] + group :: proc{proc_0, proc_1, proc_2, proc_3, proc_4, proc_5} + proc_0 :: proc() -> int { return 0 } + proc_1 :: proc(Bit_Set) -> int { return 1 } + proc_2 :: proc(int, Bit_Set) -> int { return 2 } + proc_3 :: proc(f32, Bit_Set) -> int { return 3 } + proc_4 :: proc(int, int, Bit_Set) -> int { return 4 } + proc_5 :: proc(Bit_Set, int, int, int) -> int { return 5 } + + testing.expect_value(t, group({.A}), 1) + testing.expect_value(t, group(9, {.A}), 2) + testing.expect_value(t, group(3.14, {.A}), 3) + testing.expect_value(t, group(9, 9, {.A}), 4) + testing.expect_value(t, group({.A}, 9, 9, 9), 5) +} + +@test +test_type_inference_on_literals_with_default_args :: proc(t: ^testing.T) { + { + Bit_Set :: bit_set[enum{A, B, C}] + proc_nil :: proc() { } + proc_default_arg :: proc(a: Bit_Set={.A}) -> Bit_Set { return a } + group :: proc{proc_nil, proc_default_arg} + + testing.expect_value(t, group(Bit_Set{.A}), Bit_Set{.A}) + testing.expect_value(t, group({.A}), Bit_Set{.A}) + } + { + Bit_Set :: bit_set[enum{A, B, C}] + proc_1 :: proc(a: Bit_Set={.A}) -> int { return 1 } + proc_2 :: proc(a: Bit_Set={.B}, b: Bit_Set={.C}) -> int { return 2 } + group :: proc{proc_1, proc_2} + + testing.expect_value(t, group(), 2) + testing.expect_value(t, group(Bit_Set{.A}), 2) + testing.expect_value(t, group({.A}), 2) + testing.expect_value(t, group({.B}, {.C}), 2) + } +} + +@test +test_type_inference_on_literals_for_various_types :: proc(t: ^testing.T) { + proc_nil :: proc() { } + + proc_array :: proc(a: [3]f32) -> [3]f32 { return a } + group_array :: proc{proc_nil, proc_array} + testing.expect_value(t, group_array([3]f32{1.1, 2.2, 3.3}), [3]f32{1.1, 2.2, 3.3}) + testing.expect_value(t, group_array({1.1, 2.2, 3.3}), [3]f32{1.1, 2.2, 3.3}) + testing.expect_value(t, group_array({0=1.1, 1=2.2, 2=3.3}), [3]f32{1.1, 2.2, 3.3}) + testing.expect_value(t, group_array({}), [3]f32{}) + + proc_slice_u8 :: proc(a: []u8) -> []u8 { return a } + group_slice_u8 :: proc{proc_nil, proc_slice_u8} + testing.expect_value(t, len(group_slice_u8([]u8{1, 2, 3})), 3) + testing.expect_value(t, len(group_slice_u8({1, 2, 3})), 3) + testing.expect_value(t, len(group_slice_u8({0=1, 1=2, 2=3})), 3) + testing.expect_value(t, len(group_slice_u8({})), 0) + testing.expect_value(t, group_slice_u8(nil) == nil, true) + + proc_dynamic_array :: proc(a: [dynamic]u8) -> [dynamic]u8 { return a } + group_dynamic_array :: proc{proc_nil, proc_dynamic_array} + testing.expect_value(t, len(group_dynamic_array([dynamic]u8{1, 2, 3})), 3) + testing.expect_value(t, len(group_dynamic_array({1, 2, 3})), 3) + testing.expect_value(t, len(group_dynamic_array({0=1, 1=2, 2=3})), 3) + testing.expect_value(t, len(group_dynamic_array({})), 0) + testing.expect_value(t, group_dynamic_array(nil) == nil, true) + + Enum :: enum{A, B, C} + proc_enum :: proc(a: Enum) -> Enum { return a } + group_enum :: proc{proc_nil, proc_enum} + testing.expect_value(t, group_enum(Enum.A), Enum.A) + testing.expect_value(t, group_enum(.A), Enum.A) + + proc_enumerated_array :: proc(a: [Enum]u8) -> [Enum]u8 { return a } + group_enumerated_array :: proc{proc_nil, proc_enumerated_array} + testing.expect_value(t, group_enumerated_array([Enum]u8{.A=1, .B=2, .C=3}), [Enum]u8{.A=1, .B=2, .C=3}) + testing.expect_value(t, group_enumerated_array({.A=1, .B=2, .C=3}), [Enum]u8{.A=1, .B=2, .C=3}) + + Bit_Set :: bit_set[enum{A, B, C}] + proc_bit_set :: proc(a: Bit_Set) -> Bit_Set { return a } + group_bit_set :: proc{proc_nil, proc_bit_set} + testing.expect_value(t, group_bit_set(Bit_Set{.A}), Bit_Set{.A}) + testing.expect_value(t, group_bit_set({.A}), Bit_Set{.A}) + testing.expect_value(t, group_bit_set({}), Bit_Set{}) + + Struct :: struct{a: int, b: int, c: int} + proc_struct :: proc(a: Struct) -> Struct { return a } + group_struct :: proc{proc_nil, proc_struct} + testing.expect_value(t, group_struct(Struct{a = 9}), Struct{a = 9}) + testing.expect_value(t, group_struct({a = 9}), Struct{a = 9}) + testing.expect_value(t, group_struct({}), Struct{}) + + Raw_Union :: struct #raw_union{int_: int, f32_: f32} + proc_raw_union :: proc(a: Raw_Union) -> Raw_Union { return a } + group_raw_union :: proc{proc_nil, proc_raw_union} + testing.expect_value(t, group_raw_union(Raw_Union{int_ = 9}).int_, 9) + testing.expect_value(t, group_raw_union({int_ = 9}).int_, 9) + testing.expect_value(t, group_raw_union({}).int_, 0) + + Union :: union{int, f32} + proc_union :: proc(a: Union) -> Union { return a } + group_union :: proc{proc_nil, proc_union} + testing.expect_value(t, group_union(int(9)).(int), 9) + testing.expect_value(t, group_union({}).(int), 0) + + proc_map :: proc(a: map[u8]u8) -> map[u8]u8 { return a } + group_map :: proc{proc_nil, proc_map} + testing.expect_value(t, len(group_map(map[u8]u8{1=1, 2=2})), 2) + testing.expect_value(t, len(group_map({1=1, 2=2})), 2) + testing.expect_value(t, len(group_map({})), 0) + testing.expect_value(t, group_map(nil) == nil, true) + + Bit_Field :: bit_field u16 {a: u8|4, b: u8|4, c: u8|4} + proc_bit_field :: proc(a: Bit_Field) -> Bit_Field { return a } + group_bit_field :: proc{proc_nil, proc_bit_field} + testing.expect_value(t, group_bit_field(Bit_Field{a = 1}), Bit_Field{a = 1}) + testing.expect_value(t, group_bit_field({a = 1}), Bit_Field{a = 1}) + testing.expect_value(t, group_bit_field({}), Bit_Field{}) + + SOA_Array :: #soa[2]struct{int, int} + proc_soa_array :: proc(a: SOA_Array) -> SOA_Array { return a } + group_soa_array :: proc{proc_nil, proc_soa_array} + testing.expect_value(t, len(group_soa_array(SOA_Array{{}, {}})), 2) + testing.expect_value(t, len(group_soa_array({struct{int, int}{1, 2}, struct{int, int}{1, 2}})), 1) + testing.expect_value(t, len(group_soa_array({})), 0) + testing.expect_value(t, len(soa_zip(a=[]int{1, 2}, b=[]int{3, 4})), 2) + + proc_matrix :: proc(a: matrix[2,2]f32) -> matrix[2,2]f32 { return a } + group_matrix :: proc{proc_nil, proc_matrix} + testing.expect_value(t, group_matrix(matrix[2,2]f32{1, 2, 3, 4}), matrix[2,2]f32{1, 2, 3, 4}) + testing.expect_value(t, group_matrix(1), (matrix[2,2]f32)(1)) + testing.expect_value(t, group_matrix({1, 2, 3, 4}), matrix[2,2]f32{1, 2, 3, 4}) + testing.expect_value(t, group_matrix({}), matrix[2,2]f32{}) +} From 5e71ba44562630c50fd04fb50513c5c7758e16f5 Mon Sep 17 00:00:00 2001 From: Tohei Ichikawa Date: Tue, 16 Sep 2025 10:57:54 -0400 Subject: [PATCH 2/4] Remove an outdated TODO --- src/check_expr.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 417d1b9a4..9a892245c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7018,8 +7018,6 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, { // NOTE(bill, 2019-07-13): This code is used to improve the type inference for procedure groups // where the same positional parameter has the same type value (and ellipsis) - - //TODO: get rid of proc_arg_count. make lhs as long as longest proc with most params. watch out for null safety isize proc_arg_count = -1; for (Entity *p : procs) { Type *pt = base_type(p->type); From 3c1238991ba6f68f4ea31ca8ed368387821fa7d7 Mon Sep 17 00:00:00 2001 From: Tohei Ichikawa Date: Wed, 24 Sep 2025 18:52:48 -0400 Subject: [PATCH 3/4] Fix test_proc_group_type_inference.odin --- .../test_proc_group_type_inference.odin | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/internal/test_proc_group_type_inference.odin b/tests/internal/test_proc_group_type_inference.odin index 08ff2a3c5..e26634992 100644 --- a/tests/internal/test_proc_group_type_inference.odin +++ b/tests/internal/test_proc_group_type_inference.odin @@ -29,16 +29,16 @@ test_type_inference_on_literals_with_default_args :: proc(t: ^testing.T) { proc_nil :: proc() { } proc_default_arg :: proc(a: Bit_Set={.A}) -> Bit_Set { return a } group :: proc{proc_nil, proc_default_arg} - + testing.expect_value(t, group(Bit_Set{.A}), Bit_Set{.A}) testing.expect_value(t, group({.A}), Bit_Set{.A}) } - { + { Bit_Set :: bit_set[enum{A, B, C}] proc_1 :: proc(a: Bit_Set={.A}) -> int { return 1 } proc_2 :: proc(a: Bit_Set={.B}, b: Bit_Set={.C}) -> int { return 2 } group :: proc{proc_1, proc_2} - + testing.expect_value(t, group(), 2) testing.expect_value(t, group(Bit_Set{.A}), 2) testing.expect_value(t, group({.A}), 2) @@ -56,7 +56,7 @@ test_type_inference_on_literals_for_various_types :: proc(t: ^testing.T) { testing.expect_value(t, group_array({1.1, 2.2, 3.3}), [3]f32{1.1, 2.2, 3.3}) testing.expect_value(t, group_array({0=1.1, 1=2.2, 2=3.3}), [3]f32{1.1, 2.2, 3.3}) testing.expect_value(t, group_array({}), [3]f32{}) - + proc_slice_u8 :: proc(a: []u8) -> []u8 { return a } group_slice_u8 :: proc{proc_nil, proc_slice_u8} testing.expect_value(t, len(group_slice_u8([]u8{1, 2, 3})), 3) @@ -72,7 +72,7 @@ test_type_inference_on_literals_for_various_types :: proc(t: ^testing.T) { testing.expect_value(t, len(group_dynamic_array({0=1, 1=2, 2=3})), 3) testing.expect_value(t, len(group_dynamic_array({})), 0) testing.expect_value(t, group_dynamic_array(nil) == nil, true) - + Enum :: enum{A, B, C} proc_enum :: proc(a: Enum) -> Enum { return a } group_enum :: proc{proc_nil, proc_enum} @@ -90,14 +90,14 @@ test_type_inference_on_literals_for_various_types :: proc(t: ^testing.T) { testing.expect_value(t, group_bit_set(Bit_Set{.A}), Bit_Set{.A}) testing.expect_value(t, group_bit_set({.A}), Bit_Set{.A}) testing.expect_value(t, group_bit_set({}), Bit_Set{}) - + Struct :: struct{a: int, b: int, c: int} proc_struct :: proc(a: Struct) -> Struct { return a } group_struct :: proc{proc_nil, proc_struct} testing.expect_value(t, group_struct(Struct{a = 9}), Struct{a = 9}) testing.expect_value(t, group_struct({a = 9}), Struct{a = 9}) testing.expect_value(t, group_struct({}), Struct{}) - + Raw_Union :: struct #raw_union{int_: int, f32_: f32} proc_raw_union :: proc(a: Raw_Union) -> Raw_Union { return a } group_raw_union :: proc{proc_nil, proc_raw_union} @@ -109,8 +109,8 @@ test_type_inference_on_literals_for_various_types :: proc(t: ^testing.T) { proc_union :: proc(a: Union) -> Union { return a } group_union :: proc{proc_nil, proc_union} testing.expect_value(t, group_union(int(9)).(int), 9) - testing.expect_value(t, group_union({}).(int), 0) - + testing.expect_value(t, group_union({}), nil) + proc_map :: proc(a: map[u8]u8) -> map[u8]u8 { return a } group_map :: proc{proc_nil, proc_map} testing.expect_value(t, len(group_map(map[u8]u8{1=1, 2=2})), 2) @@ -129,8 +129,8 @@ test_type_inference_on_literals_for_various_types :: proc(t: ^testing.T) { proc_soa_array :: proc(a: SOA_Array) -> SOA_Array { return a } group_soa_array :: proc{proc_nil, proc_soa_array} testing.expect_value(t, len(group_soa_array(SOA_Array{{}, {}})), 2) - testing.expect_value(t, len(group_soa_array({struct{int, int}{1, 2}, struct{int, int}{1, 2}})), 1) - testing.expect_value(t, len(group_soa_array({})), 0) + testing.expect_value(t, len(group_soa_array({struct{int, int}{1, 2}, struct{int, int}{1, 2}})), 2) + testing.expect_value(t, len(group_soa_array({})), 2) testing.expect_value(t, len(soa_zip(a=[]int{1, 2}, b=[]int{3, 4})), 2) proc_matrix :: proc(a: matrix[2,2]f32) -> matrix[2,2]f32 { return a } From 654c5b2c0662934c5965cef483a23e58a3c8ea3f Mon Sep 17 00:00:00 2001 From: Tohei Ichikawa Date: Wed, 24 Sep 2025 21:25:25 -0400 Subject: [PATCH 4/4] Fix memory leaks in type inference test --- .../test_proc_group_type_inference.odin | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/tests/internal/test_proc_group_type_inference.odin b/tests/internal/test_proc_group_type_inference.odin index e26634992..32498c836 100644 --- a/tests/internal/test_proc_group_type_inference.odin +++ b/tests/internal/test_proc_group_type_inference.odin @@ -65,13 +65,20 @@ test_type_inference_on_literals_for_various_types :: proc(t: ^testing.T) { testing.expect_value(t, len(group_slice_u8({})), 0) testing.expect_value(t, group_slice_u8(nil) == nil, true) - proc_dynamic_array :: proc(a: [dynamic]u8) -> [dynamic]u8 { return a } + proc_dynamic_array :: proc(t: ^testing.T, array: [dynamic]u8, expected_len: int) { + if expected_len < 0 { + testing.expect_value(t, array == nil, true) + } else { + testing.expect_value(t, len(array), expected_len) + } + delete(array) + } group_dynamic_array :: proc{proc_nil, proc_dynamic_array} - testing.expect_value(t, len(group_dynamic_array([dynamic]u8{1, 2, 3})), 3) - testing.expect_value(t, len(group_dynamic_array({1, 2, 3})), 3) - testing.expect_value(t, len(group_dynamic_array({0=1, 1=2, 2=3})), 3) - testing.expect_value(t, len(group_dynamic_array({})), 0) - testing.expect_value(t, group_dynamic_array(nil) == nil, true) + group_dynamic_array(t, [dynamic]u8{1, 2, 3}, 3) + group_dynamic_array(t, {1, 2, 3}, 3) + group_dynamic_array(t, {0=1, 1=2, 2=3}, 3) + group_dynamic_array(t, {}, 0) + group_dynamic_array(t, nil, -1) Enum :: enum{A, B, C} proc_enum :: proc(a: Enum) -> Enum { return a } @@ -111,12 +118,19 @@ test_type_inference_on_literals_for_various_types :: proc(t: ^testing.T) { testing.expect_value(t, group_union(int(9)).(int), 9) testing.expect_value(t, group_union({}), nil) - proc_map :: proc(a: map[u8]u8) -> map[u8]u8 { return a } + proc_map :: proc(t: ^testing.T, map_: map[u8]u8, expected_len: int) { + if expected_len < 0 { + testing.expect_value(t, map_ == nil, true) + } else { + testing.expect_value(t, len(map_), expected_len) + } + delete(map_) + } group_map :: proc{proc_nil, proc_map} - testing.expect_value(t, len(group_map(map[u8]u8{1=1, 2=2})), 2) - testing.expect_value(t, len(group_map({1=1, 2=2})), 2) - testing.expect_value(t, len(group_map({})), 0) - testing.expect_value(t, group_map(nil) == nil, true) + group_map(t, map[u8]u8{1=1, 2=2}, 2) + group_map(t, {1=1, 2=2}, 2) + group_map(t, {}, 0) + group_map(t, nil, -1) Bit_Field :: bit_field u16 {a: u8|4, b: u8|4, c: u8|4} proc_bit_field :: proc(a: Bit_Field) -> Bit_Field { return a }