mirror of
https://github.com/odin-lang/Odin.git
synced 2026-01-05 04:27:51 +00:00
Fix line error printing for error messages
This commit is contained in:
@@ -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<Operand> operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data)
|
||||
#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(CheckerContext *c, Ast *call, Type *proc_type, Entity *entity, Array<Operand> operands, Array<Operand> 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<Operand>(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<Ast *> const &named_args, Array<Operand> *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<Ast *> const &positional_args = ce->split_args->positional;
|
||||
Slice<Ast *> 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<Operand>(heap_allocator(), 0, 0);
|
||||
auto named_operands = array_make<Operand>(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<ValidIndexAndScore>(heap_allocator(), 0, procs.count);
|
||||
defer (array_free(&valids));
|
||||
|
||||
auto proc_entities = array_make<Entity *>(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<Operand> 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<Entity *> 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<Ast *> positional_args = {};
|
||||
Slice<Ast *> 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<Operand>(heap_allocator(), 0, positional_args.count);
|
||||
auto variadic_operands = array_make<Operand>(heap_allocator(), 0, 0);
|
||||
auto named_operands = array_make<Operand>(heap_allocator(), 0, 0);
|
||||
auto variadic_operands = array_make<Operand>(heap_allocator(), 0, 0);
|
||||
auto named_operands = array_make<Operand>(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<bool>(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<Operand> 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<Operand> 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<Operand> 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<Operand> 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<Operand> 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);
|
||||
|
||||
Reference in New Issue
Block a user