mirror of
https://github.com/odin-lang/Odin.git
synced 2026-10-11 15:27:06 -04:00
Bind a $T whose argument has no definite type from the leftmost argument with one without changing which procedure group member a call picks
This commit is contained in:
1 parent
d3eeb87cad
commit
a88548cb6a
4 files changed
+179
No files matched your search
@@ -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<Operand> const &ordered_operands);
|
||||
gb_internal Array<Operand> poly_operands_with_definite_types(CheckerContext *c, TypeProc *pt, Array<Operand> 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<Operand> operands = {};
|
||||
if (param_operands) {
|
||||
operands = *param_operands;
|
||||
Array<Operand> 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<Operand>(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,
|
||||
|
||||
@@ -2871,6 +2871,94 @@ gb_internal Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *pol
|
||||
return t_invalid;
|
||||
}
|
||||
|
||||
gb_internal Array<Operand> poly_operands_with_definite_types(CheckerContext *c, TypeProc *pt, Array<Operand> const &operands) {
|
||||
Array<Operand> result = {};
|
||||
if (pt->params == nullptr) {
|
||||
return result;
|
||||
}
|
||||
Slice<Entity *> 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<Operand>(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;
|
||||
|
||||
@@ -309,3 +309,20 @@ test_type_inference_on_literals_for_various_types :: proc(t: ^testing.T) {
|
||||
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})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
Reference in new issue
Block a user