From cbc6c2666bc3e88a29745a214456618262b09971 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 21 Aug 2018 14:11:18 +0100 Subject: [PATCH] Improve proc group scoring algorithm --- core/mem/alloc.odin | 19 ++++++------ core/runtime/core.odin | 59 +++++++++++++++++++++++++++++++++--- src/check_expr.cpp | 69 ++++++++++++++++++++++++++++-------------- src/types.cpp | 2 +- 4 files changed, 112 insertions(+), 37 deletions(-) diff --git a/core/mem/alloc.odin b/core/mem/alloc.odin index 04ecd8e2a..4e3dcbeea 100644 --- a/core/mem/alloc.odin +++ b/core/mem/alloc.odin @@ -24,6 +24,7 @@ Allocator :: struct { alloc_with_allocator :: inline proc(a: Allocator, size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> rawptr { + if size == 0 do return nil; return a.procedure(a.data, Allocator_Mode.Alloc, size, alignment, nil, 0, 0, loc); } alloc :: inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> rawptr { @@ -45,6 +46,10 @@ free_all :: inline proc(loc := #caller_location) { } resize_with_allocator :: inline proc(a: Allocator, ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> rawptr { + if new_size == 0 { + free_ptr_with_allocator(a, ptr, loc); + return nil; + } return a.procedure(a.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, 0, loc); } resize :: inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> rawptr { @@ -82,33 +87,30 @@ delete :: proc[ new :: inline proc(T: type, loc := #caller_location) -> ^T { ptr := (^T)(alloc(size_of(T), align_of(T), loc)); - ptr^ = T{}; + if ptr != nil do ptr^ = T{}; return ptr; } new_clone :: inline proc(data: $T, loc := #caller_location) -> ^T { ptr := (^T)(alloc(size_of(T), align_of(T), loc)); - ptr^ = data; + if ptr != nil do ptr^ = data; return ptr; } new_with_allocator :: inline proc(a: Allocator, T: type, loc := #caller_location) -> ^T { ptr := (^T)(alloc_with_allocator(a, size_of(T), align_of(T), loc)); - ptr^ = T{}; + if ptr != nil do ptr^ = T{}; return ptr; } new_clone_with_allocator :: inline proc(a: Allocator, data: $T, loc := #caller_location) -> ^T { ptr := (^T)(alloc_with_allocator(a, size_of(T), align_of(T), loc)); - ptr^ = data; + if ptr != nil do ptr^ = data; return ptr; } make_slice :: proc(T: type/[]$E, auto_cast len: int, loc := #caller_location) -> T { runtime.make_slice_error_loc(loc, len); - if len == 0 { - return nil; - } data := alloc(size_of(E)*len, align_of(E)); s := Raw_Slice{data, len}; return transmute(T)s; @@ -121,8 +123,7 @@ make_dynamic_array_len :: proc(T: type/[dynamic]$E, auto_cast len: int, loc := # } make_dynamic_array_len_cap :: proc(T: type/[dynamic]$E, auto_cast len: int, auto_cast cap: int, loc := #caller_location) -> T { runtime.make_dynamic_array_error_loc(loc, len, cap); - data: rawptr; - if cap > 0 do data = alloc(size_of(E)*cap, align_of(E)); + data := alloc(size_of(E)*cap, align_of(E)); s := Raw_Dynamic_Array{data, len, cap, context.allocator}; return transmute(T)s; } diff --git a/core/runtime/core.odin b/core/runtime/core.odin index ed6833df4..a111cddf4 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -366,7 +366,27 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) { @(builtin) -append :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> int { +append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> int { + if array == nil do return 0; + + arg_len := 1; + + if cap(array) <= len(array)+arg_len { + cap := 2 * cap(array) + max(8, arg_len); + _ = reserve(array, cap, loc); + } + arg_len = min(cap(array)-len(array), arg_len); + if arg_len > 0 { + a := (^mem.Raw_Dynamic_Array)(array); + data := (^E)(a.data); + assert(data != nil); + mem.copy(mem.ptr_offset(data, a.len), &arg, size_of(E)); + a.len += arg_len; + } + return len(array); +} +@(builtin) +append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> int { if array == nil do return 0; arg_len := len(args); @@ -387,6 +407,9 @@ append :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> in } return len(array); } +@(builtin) append :: proc[append_elem, append_elems]; + + @(builtin) append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> int { @@ -428,14 +451,42 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal return true; } + @(builtin) -incl :: inline proc(s: ^$B/bit_set[$T], elem: T) { +incl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S { s^ |= {elem}; + return s^; } @(builtin) -excl :: inline proc(s: ^$B/bit_set[$T], elem: T) { - s^ &~= {elem}; +incl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S { + for elem in elems do s^ |= {elem}; + return s^; } +@(builtin) +incl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S { + s^ |= other; + return s^; +} +@(builtin) +excl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S { + s^ &~= {elem}; + return s^; +} +@(builtin) +excl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S { + for elem in elems do s^ &~= {elem}; + return s^; +} +@(builtin) +excl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S { + s^ &~= other; + return s^; +} + +@(builtin) incl :: proc[incl_elem, incl_elems, incl_bit_set]; +@(builtin) excl :: proc[excl_elem, excl_elems, excl_bit_set]; + + diff --git a/src/check_expr.cpp b/src/check_expr.cpp index de3960101..8c9efd71b 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -403,6 +403,8 @@ bool check_type_specialization_to(CheckerContext *c, Type *specialization, Type bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, bool compound, bool modify_type); bool check_cast_internal(CheckerContext *c, Operand *x, Type *type); +#define MAXIMUM_TYPE_DISTANCE 10 + i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type) { if (operand->mode == Addressing_Invalid || type == t_invalid) { @@ -443,7 +445,7 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type if (is_type_any(dst)) { // NOTE(bill): Anything can cast to 'Any' add_type_info_type(c, s); - return 10; + return MAXIMUM_TYPE_DISTANCE; } if (dst->kind == Type_Basic) { if (operand->mode == Addressing_Constant) { @@ -577,7 +579,7 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type } else { // NOTE(bill): Anything can cast to 'Any' add_type_info_type(c, s); - return 10; + return MAXIMUM_TYPE_DISTANCE; } } } @@ -588,7 +590,7 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type x.expr = expr->AutoCast.expr; bool ok = check_cast_internal(c, &x, type); if (ok) { - return 10; + return MAXIMUM_TYPE_DISTANCE; } } @@ -596,18 +598,26 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type } -i64 assign_score_function(i64 distance) { +i64 assign_score_function(i64 distance, bool is_variadic=false) { + // 3*x^2 + 1 > x^2 + x + 1 (for positive x) + i64 const c = 3*MAXIMUM_TYPE_DISTANCE*MAXIMUM_TYPE_DISTANCE + 1; + // TODO(bill): A decent score function - return gb_max(1000000 - distance*distance, 0); + GB_ASSERT(distance <= MAXIMUM_TYPE_DISTANCE); + i64 d = distance*distance; // x^2 + if (is_variadic && d >= 0) { + d += distance + 1; // x^2 + x + 1 + } + return gb_max(c - d, 0); } -bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_) { +bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false) { i64 score = 0; i64 distance = check_distance_between_types(c, operand, type); bool ok = distance >= 0; if (ok) { - score = assign_score_function(distance); + score = assign_score_function(distance, is_variadic); } if (score_) *score_ = score; return ok; @@ -837,7 +847,17 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, case Type_BitSet: if (source->kind == Type_BitSet) { - return is_polymorphic_type_assignable(c, poly->BitSet.elem, source->BitSet.elem, true, modify_type); + if (!is_polymorphic_type_assignable(c, poly->BitSet.elem, source->BitSet.elem, true, modify_type)) { + return false; + } + if (poly->BitSet.underlying == nullptr) { + if (modify_type) { + poly->BitSet.underlying = source->BitSet.underlying; + } + } else if (!is_polymorphic_type_assignable(c, poly->BitSet.underlying, source->BitSet.underlying, true, modify_type)) { + return false; + } + return true; } return false; @@ -3198,7 +3218,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } case BuiltinProc_swizzle: { - // swizzle :: proc(v: [N]T, ...int) -> [M]T + // swizzle :: proc(v: [N]T, ..int) -> [M]T Type *type = base_type(operand->type); if (!is_type_array(type)) { gbString type_str = type_to_string(operand->type); @@ -3926,14 +3946,14 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { if (vari_expand && !variadic) { if (show_error) { error(ce->ellipsis, - "Cannot use '...' in call to a non-variadic procedure: '%.*s'", + "Cannot use '..' in call to a non-variadic procedure: '%.*s'", LIT(ce->proc->Ident.token.string)); } err = CallArgumentError_NonVariadicExpand; } else if (vari_expand && pt->c_vararg) { if (show_error) { error(ce->ellipsis, - "Cannot use '...' in call to a '#c_vararg' variadic procedure: '%.*s'", + "Cannot use '..' in call to a '#c_vararg' variadic procedure: '%.*s'", LIT(ce->proc->Ident.token.string)); } err = CallArgumentError_NonVariadicExpand; @@ -3998,21 +4018,22 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { if (are_types_identical(e->type, o.type)) { score += assign_score_function(1); } else { - score += assign_score_function(10); + score += assign_score_function(MAXIMUM_TYPE_DISTANCE); } continue; } + bool param_is_variadic = pt->variadic && pt->variadic_index == operand_index; i64 s = 0; - if (!check_is_assignable_to_with_score(c, &o, t, &s)) { + if (!check_is_assignable_to_with_score(c, &o, t, &s, param_is_variadic)) { bool ok = false; if (e->flags & EntityFlag_AutoCast) { ok = check_is_castable_to(c, &o, t); } if (ok) { - s = assign_score_function(10); + s = assign_score_function(MAXIMUM_TYPE_DISTANCE); } else { if (show_error) { check_assignment(c, &o, t, str_lit("argument")); @@ -4036,7 +4057,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { t = slice; if (operand_index != param_count) { if (show_error) { - error(o.expr, "'...' in a variadic procedure can only have one variadic argument at the end"); + error(o.expr, "'..' in a variadic procedure can only have one variadic argument at the end"); } if (data) { data->score = score; @@ -4047,7 +4068,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { } } i64 s = 0; - if (!check_is_assignable_to_with_score(c, &o, t, &s)) { + if (!check_is_assignable_to_with_score(c, &o, t, &s, true)) { if (show_error) { check_assignment(c, &o, t, str_lit("argument")); } @@ -4224,17 +4245,18 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) { if (are_types_identical(e->type, o->type)) { score += assign_score_function(1); } else { - score += assign_score_function(10); + score += assign_score_function(MAXIMUM_TYPE_DISTANCE); } } else { i64 s = 0; - if (!check_is_assignable_to_with_score(c, o, e->type, &s)) { + bool param_is_variadic = pt->variadic && pt->variadic_index == i; + if (!check_is_assignable_to_with_score(c, o, e->type, &s, param_is_variadic)) { bool ok = false; if (e->flags & EntityFlag_AutoCast) { ok = check_is_castable_to(c, o, e->type); } if (ok) { - s = assign_score_function(10); + s = assign_score_function(MAXIMUM_TYPE_DISTANCE); } else { if (show_error) { check_assignment(c, o, e->type, str_lit("procedure argument")); @@ -4276,7 +4298,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type bool vari_expand = (ce->ellipsis.pos.line != 0); if (vari_expand) { - // error(ce->ellipsis, "Invalid use of '...' with 'field = value' call'"); + // error(ce->ellipsis, "Invalid use of '..' with 'field = value' call'"); } } else { @@ -4451,6 +4473,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type sep = ":="; } gb_printf_err("\t%.*s %s %s at %.*s(%td:%td)\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column); + // gb_printf_err("\t%.*s %s %s at %.*s(%td:%td) %lld\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column, valids[i].score); } result_type = t_invalid; } else { @@ -4538,7 +4561,7 @@ CallArgumentError check_polymorphic_struct_type(CheckerContext *c, Operand *oper bool vari_expand = (ce->ellipsis.pos.line != 0); if (vari_expand) { - error(ce->ellipsis, "Invalid use of '...' in a polymorphic type call'"); + error(ce->ellipsis, "Invalid use of '..' in a polymorphic type call'"); } } else { @@ -4637,7 +4660,7 @@ CallArgumentError check_polymorphic_struct_type(CheckerContext *c, Operand *oper if (are_types_identical(e->type, o->type)) { score += assign_score_function(1); } else { - score += assign_score_function(10); + score += assign_score_function(MAXIMUM_TYPE_DISTANCE); } } else { i64 s = 0; @@ -6256,7 +6279,7 @@ gbString write_expr_to_string(gbString str, Ast *node) { case_end; case_ast_node(e, Ellipsis, node); - str = gb_string_appendc(str, "..."); + str = gb_string_appendc(str, ".."); str = write_expr_to_string(str, e->expr); case_end; diff --git a/src/types.cpp b/src/types.cpp index 92dd9d37e..4b3ce2edd 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2554,7 +2554,7 @@ gbString write_type_to_string(gbString str, Type *type) { } if (var->flags&EntityFlag_Ellipsis) { Type *slice = base_type(var->type); - str = gb_string_appendc(str, "..."); + str = gb_string_appendc(str, ".."); GB_ASSERT(var->type->kind == Type_Slice); str = write_type_to_string(str, slice->Slice.elem); } else {