diff --git a/src/array.cpp b/src/array.cpp index f1a1f93e2..d8e25d25d 100644 --- a/src/array.cpp +++ b/src/array.cpp @@ -80,7 +80,9 @@ gb_internal Slice slice_make(gbAllocator const &allocator, isize count) { GB_ASSERT(count >= 0); Slice s = {}; s.data = gb_alloc_array(allocator, T, count); - GB_ASSERT(s.data != nullptr); + if (count > 0) { + GB_ASSERT(s.data != nullptr); + } s.count = count; return s; } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index c3f6002c7..e0c6be1d1 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -66,7 +66,7 @@ gb_internal int valid_index_and_score_cmp(void const *a, void const *b) { -#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(CheckerContext *c, Ast *call, Type *proc_type, Entity *entity, Array operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data) +#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(CheckerContext *c, Ast *call, Type *proc_type, Entity *entity, Array operands, Array const &named_operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data) typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType); @@ -5314,7 +5314,24 @@ gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize * } +gb_internal isize lookup_procedure_parameter(TypeProc *pt, String parameter_name) { + isize param_count = pt->param_count; + for (isize i = 0; i < param_count; i++) { + Entity *e = pt->params->Tuple.variables[i]; + String name = e->token.string; + if (is_blank_ident(name)) { + continue; + } + if (name == parameter_name) { + return i; + } + } + return -1; +} + gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { + CallArgumentError err = CallArgumentError_None; + ast_node(ce, CallExpr, call); GB_ASSERT(is_type_proc(proc_type)); proc_type = base_type(proc_type); @@ -5327,6 +5344,52 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { i64 score = 0; bool show_error = show_error_mode == CallArgumentMode_ShowErrors; + auto ordered_operands = array_make(temporary_allocator(), pt->param_count); + bool *visited = gb_alloc_array(temporary_allocator(), bool, pt->param_count); + + if (ce->split_args) { + GB_ASSERT(ce->split_args->named.count == named_operands.count); + for_array(i, ce->split_args->named) { + Ast *arg = ce->split_args->named[i]; + Operand operand = named_operands[i]; + + ast_node(fv, FieldValue, arg); + if (fv->field->kind != Ast_Ident) { + if (show_error) { + gbString expr_str = expr_to_string(fv->field); + error(arg, "Invalid parameter name '%s' in procedure call", expr_str); + gb_string_free(expr_str); + } + err = CallArgumentError_InvalidFieldValue; + continue; + } + String name = fv->field->Ident.token.string; + isize param_index = lookup_procedure_parameter(pt, name); + if (param_index < 0) { + if (show_error) { + error(arg, "No parameter named '%.*s' for this procedure type", LIT(name)); + } + err = CallArgumentError_ParameterNotFound; + continue; + } + if (visited[param_index]) { + if (show_error) { + error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name)); + } + err = CallArgumentError_DuplicateParameter; + continue; + } + + visited[param_index] = true; + ordered_operands[param_index] = operand; + + err = CallArgumentError_DuplicateParameter; + } + if (err) { + return err; + } + } + TypeTuple *param_tuple = nullptr; if (pt->params != nullptr) { @@ -5334,7 +5397,6 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { } - CallArgumentError err = CallArgumentError_None; Type *final_proc_type = proc_type; Entity *gen_entity = nullptr; @@ -5571,21 +5633,6 @@ gb_internal bool is_call_expr_field_value(AstCallExpr *ce) { return ce->args[0]->kind == Ast_FieldValue; } -gb_internal isize lookup_procedure_parameter(TypeProc *pt, String parameter_name) { - isize param_count = pt->param_count; - for (isize i = 0; i < param_count; i++) { - Entity *e = pt->params->Tuple.variables[i]; - String name = e->token.string; - if (is_blank_ident(name)) { - continue; - } - if (name == parameter_name) { - return i; - } - } - return -1; -} - gb_internal CALL_ARGUMENT_CHECKER(check_named_call_arguments) { ast_node(ce, CallExpr, call); GB_ASSERT(is_type_proc(proc_type)); @@ -5969,8 +6016,561 @@ gb_internal CallArgumentError check_order_of_call_arguments(CheckerContext *c, T return err; } +gb_internal bool check_named_arguments(CheckerContext *c, Type *type, Slice const &named_args, Array *named_operands, bool show_error) { + bool success = true; -gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContext *c, Operand *operand, Ast *call) { + type = base_type(type); + if (named_args.count > 0) { + TypeProc *pt = nullptr; + if (is_type_proc(type)) { + pt = &type->Proc; + } + + for_array(i, named_args) { + Ast *arg = named_args[i]; + if (arg->kind != Ast_FieldValue) { + if (show_error) { + error(arg, "Expected a 'field = value'"); + } + return false; + } + ast_node(fv, FieldValue, arg); + if (fv->field->kind != Ast_Ident) { + if (show_error) { + gbString expr_str = expr_to_string(fv->field); + error(arg, "Invalid parameter name '%s' in procedure call", expr_str); + gb_string_free(expr_str); + } + success = false; + continue; + } + String key = fv->field->Ident.token.string; + Ast *value = fv->value; + + Type *type_hint = nullptr; + if (pt) { + isize param_index = lookup_procedure_parameter(pt, key); + if (param_index < 0) { + if (show_error) { + error(value, "No parameter named '%.*s' for this procedure type %s", LIT(key), type_to_string(type)); + } + success = false; + continue; + } + + // bool prev_visited = visited[param_index]; + // if (prev_visited) { + // error(value, "Duplicate parameter named '%.*s' in procedure call", LIT(key)); + // success = false; + // } + + Entity *e = pt->params->Tuple.variables[param_index]; + if (!is_type_polymorphic(e->type)) { + type_hint = e->type; + } + + } + Operand o = {}; + check_expr_with_type_hint(c, &o, value, type_hint); + if (o.mode == Addressing_Invalid) { + success = false; + } + array_add(named_operands, o); + } + + } + return success; +} + + +gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(CheckerContext *c, Operand *operand, Ast *call) { + ast_node(ce, CallExpr, call); + GB_ASSERT(ce->split_args != nullptr); + + Slice const &positional_args = ce->split_args->positional; + Slice const &named_args = ce->split_args->named; + + CallArgumentData data = {}; + data.result_type = t_invalid; + + GB_ASSERT(operand->mode == Addressing_ProcGroup); + auto procs = proc_group_entities_cloned(c, *operand); + + if (procs.count > 1) { + isize max_arg_count = positional_args.count + named_args.count; + for (Ast *arg : positional_args) { + // NOTE(bill): The only thing that may have multiple values + // will be a call expression (assuming `or_return` and `()` will be stripped) + arg = strip_or_return_expr(arg); + if (arg && arg->kind == Ast_CallExpr) { + max_arg_count = ISIZE_MAX; + break; + } + } + if (max_arg_count != ISIZE_MAX) for (Ast *arg : named_args) { + // NOTE(bill): The only thing that may have multiple values + // will be a call expression (assuming `or_return` and `()` will be stripped) + if (arg->kind == Ast_FieldValue) { + arg = strip_or_return_expr(arg->FieldValue.value); + if (arg && arg->kind == Ast_CallExpr) { + max_arg_count = ISIZE_MAX; + break; + } + } + } + + for (isize proc_index = 0; proc_index < procs.count; /**/) { + Entity *proc = procs[proc_index]; + Type *pt = base_type(proc->type); + if (!(pt != nullptr && is_type_proc(pt))) { + proc_index++; + continue; + } + + isize param_count = 0; + isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(pt, ¶m_count); + + if (param_count_excluding_defaults > max_arg_count) { + array_unordered_remove(&procs, proc_index); + continue; + } + + // for (Ast *arg : named_args) { + // if (arg->kind != Ast_FieldValue) { + // continue; + // } + // ast_node(fv, FieldValue, arg); + // if (fv->field->kind != Ast_Ident) { + // continue; + // } + // bool ok = false; + // String key = fv->field->Ident.token.string; + // if (param_count) for (Entity *e : pt->Proc.params->Tuple.variables) { + // if (e->token.string == key) { + // ok = true; + // break; + // } + // } + // if (!ok) { + // if (proc_index < procs.count) { + // array_unordered_remove(&procs, proc_index); + // continue; + // } else { + // break; + // } + // } + // } + + proc_index++; + } + } + + Entity **lhs = nullptr; + isize lhs_count = -1; + bool is_variadic = false; + + auto positional_operands = array_make(heap_allocator(), 0, 0); + auto named_operands = array_make(heap_allocator(), 0, 0); + defer (array_free(&positional_operands)); + defer (array_free(&named_operands)); + + if (procs.count == 1) { + Ast *ident = operand->expr; + while (ident->kind == Ast_SelectorExpr) { + Ast *s = ident->SelectorExpr.selector; + ident = s; + } + + + Entity *e = procs[0]; + + lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic); + check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); + + if (!check_named_arguments(c, e->type, named_args, &named_operands, true)) { + return data; + } + + CallArgumentError err = check_call_arguments_internal(c, call, e->type, e, positional_operands, named_operands, CallArgumentMode_ShowErrors, &data); + if (err != CallArgumentError_None) { + // handle error + } + + Type *proc_type = base_type(operand->type); + + Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; + add_entity_use(c, ident, entity_to_use); + if (entity_to_use != nullptr) { + update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); + + proc_type = base_type(entity_to_use->type); + } + + if (proc_type && proc_type->kind == Type_Proc) { + data.result_type = proc_type->Proc.results; + add_type_and_value(c, operand->expr, operand->mode, proc_type, operand->value); + } else if (err == CallArgumentError_None) { + data.result_type = nullptr; + } + + if (data.gen_entity != nullptr) { + Entity *e = data.gen_entity; + DeclInfo *decl = data.gen_entity->decl_info; + CheckerContext ctx = *c; + ctx.scope = decl->scope; + ctx.decl = decl; + ctx.proc_name = e->token.string; + ctx.curr_proc_decl = decl; + ctx.curr_proc_sig = e->type; + + GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); + bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); + decl->where_clauses_evaluated = true; + + if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { + check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); + } + } + return data; + } + + { + // 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) + bool proc_arg_count_all_equal = true; + isize proc_arg_count = -1; + for (Entity *p : procs) { + Type *pt = base_type(p->type); + if (pt != nullptr && is_type_proc(pt)) { + if (proc_arg_count < 0) { + proc_arg_count = pt->Proc.param_count; + } else { + if (proc_arg_count != pt->Proc.param_count) { + proc_arg_count_all_equal = false; + break; + } + } + } + } + + if (proc_arg_count >= 0 && proc_arg_count_all_equal) { + lhs_count = proc_arg_count; + if (lhs_count > 0) { + lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count); + for (isize param_index = 0; param_index < lhs_count; param_index++) { + Entity *e = nullptr; + for (Entity *p : procs) { + Type *pt = base_type(p->type); + if (!(pt != nullptr && is_type_proc(pt))) { + continue; + } + + if (e == nullptr) { + e = pt->Proc.params->Tuple.variables[param_index]; + } else { + Entity *f = pt->Proc.params->Tuple.variables[param_index]; + if (e == f) { + continue; + } + if (are_types_identical(e->type, f->type)) { + bool ee = (e->flags & EntityFlag_Ellipsis) != 0; + bool fe = (f->flags & EntityFlag_Ellipsis) != 0; + if (ee == fe) { + continue; + } + } + // NOTE(bill): Entities are not close enough to be used + e = nullptr; + break; + } + } + lhs[param_index] = e; + } + } + } + } + + check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); + + for_array(i, named_args) { + Ast *arg = named_args[i]; + if (arg->kind != Ast_FieldValue) { + error(arg, "Expected a 'field = value'"); + return data; + } + ast_node(fv, FieldValue, arg); + if (fv->field->kind != Ast_Ident) { + gbString expr_str = expr_to_string(fv->field); + error(arg, "Invalid parameter name '%s' in procedure call", expr_str); + gb_string_free(expr_str); + return data; + } + String key = fv->field->Ident.token.string; + Ast *value = fv->value; + + Type *type_hint = nullptr; + + for (isize lhs_idx = 0; lhs_idx < lhs_count; lhs_idx++) { + Entity *e = lhs[lhs_idx]; + if (e != nullptr && e->token.string == key && + !is_type_polymorphic(e->type)) { + type_hint = e->type; + break; + } + } + Operand o = {}; + check_expr_with_type_hint(c, &o, value, type_hint); + array_add(&named_operands, o); + } + + gb_free(heap_allocator(), lhs); + + auto valids = array_make(heap_allocator(), 0, procs.count); + defer (array_free(&valids)); + + auto proc_entities = array_make(heap_allocator(), 0, procs.count*2 + 1); + defer (array_free(&proc_entities)); + for (Entity *proc : procs) { + array_add(&proc_entities, proc); + } + + + gbString expr_name = expr_to_string(operand->expr); + defer (gb_string_free(expr_name)); + + for_array(i, procs) { + Entity *p = procs[i]; + Type *pt = base_type(p->type); + if (pt != nullptr && is_type_proc(pt)) { + CallArgumentError err = CallArgumentError_None; + CallArgumentData data = {}; + CheckerContext ctx = *c; + + ctx.no_polymorphic_errors = true; + ctx.allow_polymorphic_types = is_type_polymorphic(pt); + ctx.hide_polymorphic_errors = true; + + err = check_call_arguments_internal(&ctx, call, pt, p, positional_operands, named_operands, CallArgumentMode_NoErrors, &data); + if (err != CallArgumentError_None) { + continue; + } + isize index = i; + + if (data.gen_entity != nullptr) { + Entity *e = data.gen_entity; + DeclInfo *decl = data.gen_entity->decl_info; + ctx.scope = decl->scope; + ctx.decl = decl; + ctx.proc_name = e->token.string; + ctx.curr_proc_decl = decl; + ctx.curr_proc_sig = e->type; + + GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); + if (!evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, false)) { + continue; + } + + array_add(&proc_entities, data.gen_entity); + index = proc_entities.count-1; + } + + ValidIndexAndScore item = {}; + item.index = index; + item.score = data.score; + array_add(&valids, item); + } + } + + if (valids.count > 1) { + gb_sort_array(valids.data, valids.count, valid_index_and_score_cmp); + i64 best_score = valids[0].score; + Entity *best_entity = proc_entities[valids[0].index]; + GB_ASSERT(best_entity != nullptr); + for (isize i = 1; i < valids.count; i++) { + if (best_score > valids[i].score) { + valids.count = i; + break; + } + if (best_entity == proc_entities[valids[i].index]) { + valids.count = i; + break; + } + } + } + + auto print_argument_types = [&]() { + error_line("\tGiven argument types: ("); + isize i = 0; + for (Operand const &o : positional_operands) { + if (i++ > 0) error_line(", "); + gbString type = type_to_string(o.type); + defer (gb_string_free(type)); + error_line("%s", type); + } + for (Operand const &o : named_operands) { + if (i++ > 0) error_line(", "); + + gbString type = type_to_string(o.type); + defer (gb_string_free(type)); + + if (i < ce->split_args->named.count) { + Ast *named_field = ce->split_args->named[i]; + ast_node(fv, FieldValue, named_field); + + gbString field = expr_to_string(fv->field); + defer (gb_string_free(field)); + + error_line("%s = %s", field, type); + } else { + error_line("%s", type); + } + } + error_line(")\n"); + }; + + if (valids.count == 0) { + begin_error_block(); + defer (end_error_block()); + + error(operand->expr, "No procedures or ambiguous call for procedure group '%s' that match with the given arguments", expr_name); + if (positional_operands.count == 0 && named_operands.count == 0) { + error_line("\tNo given arguments\n"); + } else { + print_argument_types(); + } + + if (procs.count > 0) { + error_line("Did you mean to use one of the following:\n"); + } + for (Entity *proc : procs) { + TokenPos pos = proc->token.pos; + Type *t = base_type(proc->type); + if (t == t_invalid) continue; + GB_ASSERT(t->kind == Type_Proc); + gbString pt; + defer (gb_string_free(pt)); + if (t->Proc.node != nullptr) { + pt = expr_to_string(t->Proc.node); + } else { + pt = type_to_string(t); + } + String prefix = {}; + String prefix_sep = {}; + if (proc->pkg) { + prefix = proc->pkg->name; + prefix_sep = str_lit("."); + } + String name = proc->token.string; + + char const *sep = "::"; + if (proc->kind == Entity_Variable) { + sep = ":="; + } + error_line("\t%.*s%.*s%.*s %s %s at %s\n", LIT(prefix), LIT(prefix_sep), LIT(name), sep, pt, token_pos_to_string(pos)); + } + if (procs.count > 0) { + error_line("\n"); + } + + data.result_type = t_invalid; + } else if (valids.count > 1) { + begin_error_block(); + defer (end_error_block()); + + error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name); + print_argument_types(); + + for (isize i = 0; i < valids.count; i++) { + Entity *proc = proc_entities[valids[i].index]; + GB_ASSERT(proc != nullptr); + TokenPos pos = proc->token.pos; + Type *t = base_type(proc->type); GB_ASSERT(t->kind == Type_Proc); + gbString pt = nullptr; + defer (gb_string_free(pt)); + if (t->Proc.node != nullptr) { + pt = expr_to_string(t->Proc.node); + } else { + pt = type_to_string(t); + } + String name = proc->token.string; + char const *sep = "::"; + if (proc->kind == Entity_Variable) { + sep = ":="; + } + error_line("\t%.*s %s %s ", LIT(name), sep, pt); + if (proc->decl_info->proc_lit != nullptr) { + GB_ASSERT(proc->decl_info->proc_lit->kind == Ast_ProcLit); + auto *pl = &proc->decl_info->proc_lit->ProcLit; + if (pl->where_token.kind != Token_Invalid) { + error_line("\n\t\twhere "); + for_array(j, pl->where_clauses) { + Ast *clause = pl->where_clauses[j]; + if (j != 0) { + error_line("\t\t "); + } + gbString str = expr_to_string(clause); + error_line("%s", str); + gb_string_free(str); + + if (j != pl->where_clauses.count-1) { + error_line(","); + } + } + error_line("\n\t"); + } + } + error_line("at %s\n", token_pos_to_string(pos)); + } + data.result_type = t_invalid; + } else { + GB_ASSERT(valids.count == 1); + Ast *ident = operand->expr; + while (ident->kind == Ast_SelectorExpr) { + Ast *s = ident->SelectorExpr.selector; + ident = s; + } + + Entity *e = proc_entities[valids[0].index]; + GB_ASSERT(e != nullptr); + + Array named_operands = {}; + + Type *proc_type = e->type; + CallArgumentData data = {}; + CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, positional_operands, named_operands, CallArgumentMode_ShowErrors, &data); + gb_unused(err); + Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; + add_entity_use(c, ident, entity_to_use); + if (entity_to_use != nullptr) { + update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); + } + + if (data.gen_entity != nullptr) { + Entity *e = data.gen_entity; + DeclInfo *decl = data.gen_entity->decl_info; + CheckerContext ctx = *c; + ctx.scope = decl->scope; + ctx.decl = decl; + ctx.proc_name = e->token.string; + ctx.curr_proc_decl = decl; + ctx.curr_proc_sig = e->type; + + GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); + bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); + decl->where_clauses_evaluated = true; + + if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { + check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); + } + } + return data; + } + + return data; +} + + +gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContext *c, Operand *operand, Ast *call) { Type *proc_type = nullptr; CallArgumentData data = {}; @@ -5978,23 +6578,6 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex proc_type = base_type(operand->type); - if (operand->mode == Addressing_ProcGroup) { - Array procs = proc_group_entities(c, *operand); - if (procs.count == 1) { - proc_type = base_type(procs[0]->type); - } else { - error(call, "Procedure groups >1 not yet supported"); - return data; - } - } - if (proc_type == nullptr || proc_type->kind != Type_Proc) { - gbString s = type_to_string(proc_type); - error(call, "Expected a procedure to call, got %s", s); - gb_string_free(s); - - return data; - } - if (proc_type != nullptr && is_type_polymorphic(proc_type)) { error(call, "Polymorphic procedures not yet supported"); return data; @@ -6012,19 +6595,38 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex bool vari_expand = (ce->ellipsis.pos.line != 0); - auto positional_args = ce->args; - for (isize i = 0; i < ce->args.count; i++) { - Ast *arg = ce->args.data[i]; - if (arg->kind == Ast_FieldValue) { - positional_args.count = i; - break; + // Split positional and named args into separate arrays/slices + Slice positional_args = {}; + Slice named_args = {}; + + if (ce->split_args == nullptr) { + positional_args = ce->args; + for (isize i = 0; i < ce->args.count; i++) { + Ast *arg = ce->args.data[i]; + if (arg->kind == Ast_FieldValue) { + positional_args.count = i; + break; + } } + named_args = slice(ce->args, positional_args.count, ce->args.count); + + auto split_args = gb_alloc_item(permanent_allocator(), AstSplitArgs); + split_args->positional = positional_args; + split_args->named = named_args; + ce->split_args = split_args; + } else { + positional_args = ce->split_args->positional; + named_args = ce->split_args->named; } - auto named_args = slice(ce->args, positional_args.count, ce->args.count); + + if (operand->mode == Addressing_ProcGroup) { + return check_call_arguments_new_and_improved_proc_group(c, operand, call); + } + auto positional_operands = array_make(heap_allocator(), 0, positional_args.count); - auto variadic_operands = array_make(heap_allocator(), 0, 0); - auto named_operands = array_make(heap_allocator(), 0, 0); + auto variadic_operands = array_make(heap_allocator(), 0, 0); + auto named_operands = array_make(heap_allocator(), 0, 0); defer (array_free(&positional_operands)); defer (array_free(&variadic_operands)); @@ -6038,14 +6640,16 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex isize lhs_count = -1; bool is_variadic = false; Entity **lhs = nullptr; - if (proc_type != nullptr && is_type_proc(proc_type)) { + if (pt != nullptr) { lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic); + } else if (operand->mode == Addressing_ProcGroup) { + // TODO } check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); } positional_operand_count = positional_operands.count; - if (proc_type != nullptr) { + if (pt != nullptr) { visited = slice_make(temporary_allocator(), pt->param_count); if (!pt->variadic) { @@ -6161,7 +6765,7 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex string_map_init(&type_hint_map, 2*named_args.count); defer (string_map_destroy(&type_hint_map)); - if (proc_type != nullptr) { + if (pt != nullptr) { TypeTuple *param_tuple = &pt->params->Tuple; for (Entity *e : param_tuple->variables) { if (is_blank_ident(e->token)) { @@ -6252,7 +6856,7 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex } - if (proc_type != nullptr) { + if (pt != nullptr) { if (pt->variadic) { visited[pt->variadic_index] = true; } @@ -6293,14 +6897,45 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex return data; } + { + Ast *ident = operand->expr; + while (ident->kind == Ast_SelectorExpr) { + Ast *s = ident->SelectorExpr.selector; + ident = s; + } - auto ordered_args = gb_alloc_item(permanent_allocator(), AstOrderedArgs); - ordered_args->positional = positional_args; - ordered_args->named = named_args; + Entity *e = entity_of_node(ident); - ce->ordered_args = ordered_args; + Array operands = array_clone(heap_allocator(), positional_operands); - if (proc_type != nullptr) { + CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, operands, named_operands, CallArgumentMode_ShowErrors, &data); + gb_unused(err); + Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; + add_entity_use(c, ident, entity_to_use); + if (entity_to_use != nullptr) { + update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); + } + if (data.gen_entity != nullptr) { + Entity *e = data.gen_entity; + DeclInfo *decl = data.gen_entity->decl_info; + CheckerContext ctx = *c; + ctx.scope = decl->scope; + ctx.decl = decl; + ctx.proc_name = e->token.string; + ctx.curr_proc_decl = decl; + ctx.curr_proc_sig = e->type; + + GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); + bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); + decl->where_clauses_evaluated = true; + + if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { + check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); + } + } + } + + if (pt != nullptr) { data.result_type = pt->results; } return data; @@ -6438,13 +7073,13 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op break; } } - if (all_non_poly && (procs.count == 1 || c->pkg->name == "bug")) { - return check_call_arguments_single_procedure(c, operand, call); + if (all_non_poly) { + return check_call_arguments_new_and_improved(c, operand, call); } } if (operand->mode != Addressing_ProcGroup && is_type_proc(operand->type) && !is_type_polymorphic(operand->type)) { - return check_call_arguments_single_procedure(c, operand, call); + return check_call_arguments_new_and_improved(c, operand, call); } ast_node(ce, CallExpr, call); @@ -6635,8 +7270,9 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic); check_unpack_arguments(c, lhs, lhs_count, &operands, args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); + Array named_operands = {}; CallArgumentData data = {}; - CallArgumentError err = call_checker(c, call, e->type, e, operands, CallArgumentMode_ShowErrors, &data); + CallArgumentError err = call_checker(c, call, e->type, e, operands, named_operands, CallArgumentMode_ShowErrors, &data); if (err != CallArgumentError_None) { // handle error } @@ -6740,7 +7376,8 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op ctx.allow_polymorphic_types = is_type_polymorphic(pt); ctx.hide_polymorphic_errors = true; - err = call_checker(&ctx, call, pt, p, operands, CallArgumentMode_NoErrors, &data); + Array named_operands = {}; + err = call_checker(&ctx, call, pt, p, operands, named_operands, CallArgumentMode_NoErrors, &data); if (err != CallArgumentError_None) { continue; } @@ -6910,9 +7547,11 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op Entity *e = proc_entities[valids[0].index]; GB_ASSERT(e != nullptr); + Array named_operands = {}; + proc_type = e->type; CallArgumentData data = {}; - CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data); + CallArgumentError err = call_checker(c, call, proc_type, e, operands, named_operands, CallArgumentMode_ShowErrors, &data); gb_unused(err); Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; add_entity_use(c, ident, entity_to_use); @@ -6949,9 +7588,10 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op Entity *e = entity_of_node(ident); + Array named_operands = {}; CallArgumentData data = {}; - CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data); + CallArgumentError err = call_checker(c, call, proc_type, e, operands, named_operands, CallArgumentMode_ShowErrors, &data); gb_unused(err); Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; add_entity_use(c, ident, entity_to_use); diff --git a/src/error.cpp b/src/error.cpp index defc2593f..eb010eb36 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -265,7 +265,8 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { defer (gb_string_free(the_line)); if (the_line != nullptr) { - String line = make_string(cast(u8 const *)the_line, gb_string_length(the_line)); + char const *line_text = the_line; + isize line_len = gb_string_length(the_line); // TODO(bill): This assumes ASCII @@ -285,21 +286,27 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { isize squiggle_extra = 0; - if (line.len > MAX_LINE_LENGTH_PADDED) { + if (line_len > MAX_LINE_LENGTH_PADDED) { i32 left = MAX_TAB_WIDTH; - line.text += offset-left; - line.len -= offset-left; - offset = left+MAX_TAB_WIDTH/2; - if (line.len > MAX_LINE_LENGTH_PADDED) { - line.len = MAX_LINE_LENGTH_PADDED; - if (error_length > line.len-left) { - error_length = cast(i32)line.len - left; + if (offset > 0) { + line_text += offset-left; + line_len -= offset-left; + offset = left+MAX_TAB_WIDTH/2; + } + if (line_len > MAX_LINE_LENGTH_PADDED) { + line_len = MAX_LINE_LENGTH_PADDED; + if (error_length > line_len-left) { + error_length = cast(i32)line_len - left; squiggle_extra = 1; } } - error_out("... %.*s ...", LIT(line)); + if (offset > 0) { + error_out("... %.*s ...", cast(i32)line_len, line_text); + } else { + error_out("%.*s ...", cast(i32)line_len, line_text); + } } else { - error_out("%.*s", LIT(line)); + error_out("%.*s", cast(i32)line_len, line_text); } error_out("\n\t"); @@ -312,7 +319,7 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { error_out("^"); if (end.file_id == pos.file_id) { if (end.line > pos.line) { - for (i32 i = offset; i < line.len; i++) { + for (i32 i = offset; i < line_len; i++) { error_out("~"); } } else if (end.line == pos.line && end.column > pos.column) { diff --git a/src/gb/gb.h b/src/gb/gb.h index bc4c1f27d..3d4bff9b4 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -3299,12 +3299,39 @@ void const *gb_memrchr(void const *data, u8 c, isize n) { -gb_inline void *gb_alloc_align (gbAllocator a, isize size, isize alignment) { return a.proc(a.data, gbAllocation_Alloc, size, alignment, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); } -gb_inline void *gb_alloc (gbAllocator a, isize size) { return gb_alloc_align(a, size, GB_DEFAULT_MEMORY_ALIGNMENT); } -gb_inline void gb_free (gbAllocator a, void *ptr) { if (ptr != NULL) a.proc(a.data, gbAllocation_Free, 0, 0, ptr, 0, GB_DEFAULT_ALLOCATOR_FLAGS); } -gb_inline void gb_free_all (gbAllocator a) { a.proc(a.data, gbAllocation_FreeAll, 0, 0, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); } -gb_inline void *gb_resize (gbAllocator a, void *ptr, isize old_size, isize new_size) { return gb_resize_align(a, ptr, old_size, new_size, GB_DEFAULT_MEMORY_ALIGNMENT); } -gb_inline void *gb_resize_align(gbAllocator a, void *ptr, isize old_size, isize new_size, isize alignment) { return a.proc(a.data, gbAllocation_Resize, new_size, alignment, ptr, old_size, GB_DEFAULT_ALLOCATOR_FLAGS); } +gb_inline void *gb_alloc_align (gbAllocator a, isize size, isize alignment) { + if (size == 0) { + return NULL; + } + return a.proc(a.data, gbAllocation_Alloc, size, alignment, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); +} +gb_inline void *gb_alloc(gbAllocator a, isize size) { + return gb_alloc_align(a, size, GB_DEFAULT_MEMORY_ALIGNMENT); +} +gb_inline void gb_free(gbAllocator a, void *ptr) { + if (ptr != NULL) { + a.proc(a.data, gbAllocation_Free, 0, 0, ptr, 0, GB_DEFAULT_ALLOCATOR_FLAGS); + } +} +gb_inline void gb_free_all(gbAllocator a) { + a.proc(a.data, gbAllocation_FreeAll, 0, 0, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); +} +gb_inline void *gb_resize(gbAllocator a, void *ptr, isize old_size, isize new_size) { + return gb_resize_align(a, ptr, old_size, new_size, GB_DEFAULT_MEMORY_ALIGNMENT); +} +gb_inline void *gb_resize_align(gbAllocator a, void *ptr, isize old_size, isize new_size, isize alignment) { + if (new_size == 0) { + if (ptr != NULL) { + return a.proc(a.data, gbAllocation_Free, 0, 0, ptr, old_size, GB_DEFAULT_ALLOCATOR_FLAGS); + } + return NULL; + } else if (ptr == NULL) { + return a.proc(a.data, gbAllocation_Alloc, new_size, alignment, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); + } else if (old_size == new_size && ((uintptr)ptr % (uintptr)alignment) == 0) { + return ptr; + } + return a.proc(a.data, gbAllocation_Resize, new_size, alignment, ptr, old_size, GB_DEFAULT_ALLOCATOR_FLAGS); +} gb_inline void *gb_alloc_copy (gbAllocator a, void const *src, isize size) { return gb_memcopy(gb_alloc(a, size), src, size); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 3c1cfe47c..046016c75 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3156,20 +3156,20 @@ gb_internal void lb_add_values_to_array(lbProcedure *p, Array *args, lb array_add(args, value); } } -gb_internal lbValue lb_build_call_expr_internal_with_arg_ordered_args(lbProcedure *p, lbValue value, TypeProc *pt, Ast *call, AstOrderedArgs *ordered_args) { +gb_internal lbValue lb_build_call_expr_internal_with_arg_split_args(lbProcedure *p, lbValue value, TypeProc *pt, Ast *call, AstSplitArgs *split_args) { ast_node(ce, CallExpr, call); auto args = array_make(permanent_allocator(), 0, pt->param_count); bool vari_expand = (ce->ellipsis.pos.line != 0); - for_array(i, ordered_args->positional) { + for_array(i, split_args->positional) { Entity *e = pt->params->Tuple.variables[i]; GB_ASSERT(e->kind == Entity_Variable); if (pt->variadic && pt->variadic_index == i) { lbValue variadic_args = lb_const_nil(p->module, e->type); - auto variadic = slice(ordered_args->positional, pt->variadic_index, ordered_args->positional.count); + auto variadic = slice(split_args->positional, pt->variadic_index, split_args->positional.count); if (variadic.count != 0) { // variadic call argument generation Type *slice_type = e->type; @@ -3211,14 +3211,14 @@ gb_internal lbValue lb_build_call_expr_internal_with_arg_ordered_args(lbProcedur break; } else { - lbValue value = lb_build_expr(p, ordered_args->positional[i]); + lbValue value = lb_build_expr(p, split_args->positional[i]); lb_add_values_to_array(p, &args, value); } } array_resize(&args, pt->param_count); - for (Ast *arg : ordered_args->named) { + for (Ast *arg : split_args->named) { ast_node(fv, FieldValue, arg); GB_ASSERT(fv->field->kind == Ast_Ident); String name = fv->field->Ident.token.string; @@ -3331,8 +3331,8 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { GB_ASSERT(proc_type_->kind == Type_Proc); TypeProc *pt = &proc_type_->Proc; - if (ce->ordered_args) { - return lb_build_call_expr_internal_with_arg_ordered_args(p, value, pt, expr, ce->ordered_args); + if (ce->split_args) { + return lb_build_call_expr_internal_with_arg_split_args(p, value, pt, expr, ce->split_args); } if (is_call_expr_field_value(ce)) { diff --git a/src/parser.hpp b/src/parser.hpp index e0e78fa1d..900fddbab 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -367,7 +367,7 @@ gb_global char const *union_type_kind_strings[UnionType_COUNT] = { "#shared_nil", }; -struct AstOrderedArgs { +struct AstSplitArgs { Slice positional; Slice named; }; @@ -447,7 +447,7 @@ AST_KIND(_ExprBegin, "", bool) \ ProcInlining inlining; \ bool optional_ok_one; \ bool was_selector; \ - AstOrderedArgs *ordered_args; \ + AstSplitArgs *split_args; \ }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ AST_KIND(EnumFieldValue, "enum field value", struct { \ diff --git a/src/types.cpp b/src/types.cpp index 3cc077f84..f3b7f5bab 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -3326,6 +3326,9 @@ gb_internal bool are_struct_fields_reordered(Type *type) { type = base_type(type); GB_ASSERT(type->kind == Type_Struct); type_set_offsets(type); + if (type->Struct.fields.count == 0) { + return false; + } GB_ASSERT(type->Struct.offsets != nullptr); i64 prev_offset = 0; @@ -3344,6 +3347,9 @@ gb_internal Slice struct_fields_index_by_increasing_offset(gbAllocator allo type = base_type(type); GB_ASSERT(type->kind == Type_Struct); type_set_offsets(type); + if (type->Struct.fields.count == 0) { + return {}; + } GB_ASSERT(type->Struct.offsets != nullptr); auto indices = slice_make(allocator, type->Struct.fields.count);