diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 92847560e..783174a30 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -47,16 +47,19 @@ struct CallArgumentData { Entity *gen_entity; i64 score; Type * result_type; + bool bound_by_later_arg; }; struct PolyProcData { Entity * gen_entity; ProcInfo *proc_info; + bool bound_by_later_arg; }; struct ValidIndexAndScore { isize index; i64 score; + bool bound_by_later_arg; }; gb_internal int valid_index_and_score_cmp(void const *a, void const *b) { @@ -126,6 +129,7 @@ gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize * gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr); gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types, isize param_count, Array const &ordered_operands); +gb_internal Array poly_operands_with_definite_types(CheckerContext *c, TypeProc *pt, Array const &operands); gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finish); @@ -658,6 +662,13 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E Array operands = {}; if (param_operands) { operands = *param_operands; + Array bound = poly_operands_with_definite_types(old_c, &src->Proc, operands); + if (bound.data != nullptr) { + operands = bound; + if (poly_proc_data) { + poly_proc_data->bound_by_later_arg = true; + } + } } else { operands = array_make(a, 0, dst->Proc.param_count); for (isize i = 0; i < dst->Proc.param_count; i++) { @@ -7047,6 +7058,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A Type *final_proc_type = proc_type; Entity *gen_entity = nullptr; + bool bound_by_later_arg = false; if (vari_expand && !variadic) { if (show_error) { @@ -7378,6 +7390,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A PolyProcData poly_proc_data = {}; if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &ordered_operands, call, &poly_proc_data)) { gen_entity = poly_proc_data.gen_entity; + bound_by_later_arg = poly_proc_data.bound_by_later_arg; Type *gept = base_type(proc_entity_full_type(gen_entity)); GB_ASSERT(is_type_proc(gept)); final_proc_type = proc_entity_full_type(gen_entity); @@ -7504,6 +7517,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A data->score = score; data->result_type = final_proc_type->Proc.results; data->gen_entity = gen_entity; + data->bound_by_later_arg = bound_by_later_arg; Ast *proc_lit = nullptr; @@ -8076,6 +8090,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, ValidIndexAndScore item = {}; item.score = data.score; + item.bound_by_later_arg = data.bound_by_later_arg; if (data.gen_entity != nullptr) { array_add(&proc_entities, data.gen_entity); @@ -8122,6 +8137,16 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, } c->in_proc_group = false; + isize direct_count = 0; + for (ValidIndexAndScore const &v : valids) { + if (!v.bound_by_later_arg) { + valids[direct_count++] = v; + } + } + if (direct_count > 0) { + valids.count = direct_count; + } + if (max_matched_features > 0) { for_array(i, valids) { // NOTE: A polymorphic candidate appends its instantiated entity to proc_entities above, diff --git a/src/check_type.cpp b/src/check_type.cpp index b38f6ad86..c325936b2 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2871,6 +2871,94 @@ gb_internal Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *pol return t_invalid; } +gb_internal Array poly_operands_with_definite_types(CheckerContext *c, TypeProc *pt, Array const &operands) { + Array result = {}; + if (pt->params == nullptr) { + return result; + } + Slice const ¶ms = pt->params->Tuple.variables; + isize n = gb_min(params.count, operands.count); + + auto is_untyped_declaring_generic = [&](isize i) -> bool { + Entity *p = params[i]; + Operand const &o = operands[i]; + if (p->kind != Entity_Variable || p->type->kind != Type_Generic) { + return false; + } + if (!o.deferred_untyped_arg && !(is_operand_value(o) && is_type_untyped(o.type))) { + return false; + } + + // NOTE(bill): `$T` is written after the names of the parameters it declares + Entity *te = poly_generic_entity(p->type); + if (te == nullptr || te->token.pos <= p->token.pos) { + return false; + } + for (isize k = 0; k < i; k++) { + if (params[k]->type == p->type) { + return false; + } + } + return true; + }; + + bool any_untyped = false; + for (isize i = 0; i < n; i++) { + if (is_untyped_declaring_generic(i)) { + any_untyped = true; + break; + } + } + if (!any_untyped) { + return result; + } + + PolySubst subst = {}; + subst.items.allocator = temporary_allocator(); + for (isize i = 0; i < n; i++) { + Entity *p = params[i]; + Operand const &o = operands[i]; + bool definite = false; + if (p->kind == Entity_TypeName) { + definite = o.mode == Addressing_Type; + } else if (p->kind == Entity_Variable) { + definite = !o.deferred_untyped_arg && is_operand_value(o) && is_type_typed(o.type); + } + if (!definite || !is_type_polymorphic(p->type) || is_type_polymorphic(o.type)) { + continue; + } + PolySubst one = {}; + one.items.allocator = temporary_allocator(); + if (subst_unify(c, p->type, o.type, &one) == Subst_Matched) { + for (PolyBinding const &b : one.items) { + poly_subst_add(&subst, b.key, b); + } + } + } + + for (isize i = 0; i < n; i++) { + if (!is_untyped_declaring_generic(i)) { + continue; + } + PolyBinding *b = poly_subst_find(&subst, poly_generic_entity(params[i]->type)); + if (b == nullptr || b->kind != PolyBind_Type) { + continue; + } + if (!operands[i].deferred_untyped_arg && are_types_identical(b->type, default_type(operands[i].type))) { + continue; + } + if (result.data == nullptr) { + result = array_make(temporary_allocator(), operands.count); + array_copy(&result, operands, 0); + } + Operand bound = {Addressing_Value}; + bound.type = b->type; + bound.expr = operands[i].expr; + result[i] = bound; + } + return result; +} + gb_internal bool is_expr_from_a_parameter(CheckerContext *ctx, Ast *expr) { if (expr == nullptr) { return false; diff --git a/tests/internal/test_proc_group_type_inference.odin b/tests/internal/test_proc_group_type_inference.odin index 4a77dc1a6..1311cc93e 100644 --- a/tests/internal/test_proc_group_type_inference.odin +++ b/tests/internal/test_proc_group_type_inference.odin @@ -309,3 +309,20 @@ test_type_inference_on_literals_for_various_types :: proc(t: ^testing.T) { 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{}) } + +@test +test_type_inference_on_address_of_literals :: proc(t: ^testing.T) { + S :: struct {x, y: int} + T :: struct {a: f32} + proc_s :: proc(p: ^S) -> int { return p.y } + proc_t :: proc(p: ^T) -> int { return int(p.a) } + proc_string :: proc(s: string) -> int { return -1 } + group :: proc{proc_s, proc_t, proc_string} + + testing.expect_value(t, group(&{1, 2}), 2) + testing.expect_value(t, group(&{y = 3}), 3) + testing.expect_value(t, group(&{a = 4}), 4) + + poly :: proc(a: $E, p: ^E) -> E { return p^ } + testing.expect_value(t, poly(S{}, &{5, 6}), S{5, 6}) +} diff --git a/tests/internal/test_sibling_type_inference.odin b/tests/internal/test_sibling_type_inference.odin index 831a2e8ec..650a7cc54 100644 --- a/tests/internal/test_sibling_type_inference.odin +++ b/tests/internal/test_sibling_type_inference.odin @@ -55,3 +55,52 @@ test_type_inference_in_min_max_clamp :: proc(t: ^testing.T) { x: E = min(.B, .C) testing.expect_value(t, x, E.B) } + +@test +test_type_inference_of_implicit_selectors_against_arrays :: proc(t: ^testing.T) { + E :: enum {A, B, C} + + a: [3]E = .B + testing.expect_value(t, a, [3]E{.B, .B, .B}) + testing.expect(t, a == .B) + + b: [2][2]E = .C + testing.expect_value(t, b[1][0], E.C) + + second :: proc(x: [2]E) -> E { return x[1] } + testing.expect_value(t, second(.A), E.A) +} + +@test +test_type_inference_of_polymorphic_types_from_later_arguments :: proc(t: ^testing.T) { + E :: enum {A, B, C} + second :: proc(a: $T, b: T) -> T { return b } + + e := E.C + testing.expect_value(t, second(.A, e), E.C) + testing.expect_value(t, second(b = e, a = .A), E.C) + + x: f32 = 0.5 + y := second(1, x) + #assert(type_of(y) == f32) + testing.expect_value(t, y, 0.5) + + v := [2]f32{1, 2} + testing.expect_value(t, second({3, 4}, v), v) + + p := new(int) + defer free(p) + testing.expect_value(t, second(nil, p), p) + + #assert(type_of(second(1, 2.0)) == int) + + member_any :: proc(a: int, b: any) -> int { return 1 } + member_poly :: proc(a: $T, b: T) -> int { return 2 } + member_string :: proc(s: string) -> int { return 3 } + group_any :: proc{member_any, member_poly} + group_string :: proc{member_string, member_poly} + + f: f64 = 2.5 + testing.expect_value(t, group_any(1, f), 1) + testing.expect_value(t, group_string(1, f), 2) +}