From 15a0d9f9008362882aed152ba3fecd09f4ce7b28 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 19 Jun 2023 23:46:06 +0100 Subject: [PATCH] Begin to handle split arguments correctly --- src/check_expr.cpp | 560 ++++++++++++++++++++++----------------------- 1 file changed, 279 insertions(+), 281 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index eb3713d82..92b572352 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, Array const &named_operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data) +#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(CheckerContext *c, Ast *call, Type *proc_type, Entity *entity, Array positional_operands, Array const &named_operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data) typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType); @@ -5351,59 +5351,6 @@ 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) { - param_tuple = &pt->params->Tuple; - } - - Type *final_proc_type = proc_type; Entity *gen_entity = nullptr; @@ -5421,7 +5368,263 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { LIT(ce->proc->Ident.token.string)); } err = CallArgumentError_NonVariadicExpand; - } else if (operands.count == 0 && param_count_excluding_defaults == 0) { + } + + if (ce->split_args) { + auto ordered_operands = array_make(temporary_allocator(), pt->param_count); + auto visited = slice_make(temporary_allocator(), pt->param_count); + + isize positional_operand_count = positional_operands.count; + if (variadic) { + positional_operand_count = gb_min(positional_operands.count, pt->variadic_index); + } else if (positional_operand_count > pt->param_count) { + err = CallArgumentError_TooManyArguments; + char const *err_fmt = "Too many arguments for '%s', expected %td arguments, got %td"; + if (show_error) { + gbString proc_str = expr_to_string(ce->proc); + defer (gb_string_free(proc_str)); + error(call, err_fmt, proc_str, param_count_excluding_defaults, positional_operands.count); + } + return err; + } + positional_operand_count = gb_min(positional_operand_count, pt->param_count); + + for (isize i = 0; i < positional_operand_count; i++) { + ordered_operands[i] = positional_operands[i]; + visited[i] = true; + } + + auto variadic_operands = slice(slice_from_array(positional_operands), positional_operand_count, positional_operands.count); + + if (named_operands.count != 0) { + 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; + } + Entity *e = pt->params->Tuple.variables[param_index]; + + check_assignment(c, &operand, e->type, str_lit("named argument")); + + visited[param_index] = true; + ordered_operands[param_index] = operand; + + err = CallArgumentError_DuplicateParameter; + } + } + + if (variadic) { + if (visited[pt->variadic_index] && + positional_operand_count < positional_operands.count) { + if (show_error) { + String name = pt->params->Tuple.variables[pt->variadic_index]->token.string; + error(call, "Variadic parameters already handled with a named argument '%.*s' in procedure call", LIT(name)); + } + err = CallArgumentError_DuplicateParameter; + } else { + visited[pt->variadic_index] = true; + } + + } + + + for (Operand const &o : ordered_operands) { + if (o.mode != Addressing_Invalid) { + check_no_copy_assignment(o, str_lit("procedure call expression")); + } + } + + for (isize i = 0; i < pt->param_count; i++) { + if (!visited[i]) { + Entity *e = pt->params->Tuple.variables[i]; + if (is_blank_ident(e->token)) { + continue; + } + if (e->kind == Entity_Variable) { + if (e->Variable.param_value.kind != ParameterValue_Invalid) { + score += assign_score_function(1); + continue; + } + } + + if (show_error) { + if (e->kind == Entity_TypeName) { + error(call, "Type parameter '%.*s' is missing in procedure call", + LIT(e->token.string)); + } else if (e->kind == Entity_Constant && e->Constant.value.kind != ExactValue_Invalid) { + // Ignore + } else { + gbString str = type_to_string(e->type); + error(call, "Parameter '%.*s' of type '%s' is missing in procedure call", + LIT(e->token.string), str); + gb_string_free(str); + } + } + err = CallArgumentError_ParameterMissing; + } + } + + if (ordered_operands.count == 0 && param_count_excluding_defaults == 0) { + err = CallArgumentError_None; + + if (variadic) { + GB_ASSERT(pt->params != nullptr && pt->params->Tuple.variables.count > 0); + Type *t = pt->params->Tuple.variables[0]->type; + if (is_type_polymorphic(t)) { + error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input"); + err = CallArgumentError_AmbiguousPolymorphicVariadic; + } + } + } else { + if (pt->is_polymorphic && !pt->is_poly_specialized && err == CallArgumentError_None) { + 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; + Type *gept = base_type(gen_entity->type); + GB_ASSERT(is_type_proc(gept)); + proc_type = gept; + pt = &gept->Proc; + } else { + err = CallArgumentError_WrongTypes; + } + } + + for (isize i = 0; i < pt->param_count; i++) { + Entity *e = pt->params->Tuple.variables[i]; + Operand *o = &ordered_operands[i]; + bool param_is_variadic = pt->variadic && pt->variadic_index == i; + + if (o->mode == Addressing_Invalid) { + if (param_is_variadic) { + Type *slice = e->type; + GB_ASSERT(is_type_slice(slice)); + Type *elem = base_type(slice)->Slice.elem; + + if (variadic_operands.count == 0) { + if (is_type_polymorphic(elem)) { + error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input"); + err = CallArgumentError_AmbiguousPolymorphicVariadic; + return err; + } + } else { + if (vari_expand) { + GB_ASSERT(variadic_operands.count == 1); + check_assignment(c, &variadic_operands[0], slice, str_lit("variadic expanded argument")); + } else { + for (Operand &vo : variadic_operands) { + check_assignment(c, &vo, elem, str_lit("variadic argument")); + } + } + } + } + continue; + } + + if (e->kind == Entity_TypeName) { + GB_ASSERT(pt->is_polymorphic); + if (o->mode != Addressing_Type) { + if (show_error) { + error(o->expr, "Expected a type for the argument '%.*s'", LIT(e->token.string)); + } + err = CallArgumentError_WrongTypes; + } + if (are_types_identical(e->type, o->type)) { + score += assign_score_function(1); + } else { + score += assign_score_function(MAXIMUM_TYPE_DISTANCE); + } + } else { + i64 s = 0; + if (!check_is_assignable_to_with_score(c, o, e->type, &s, param_is_variadic)) { + bool ok = false; + if (ok) { + s = assign_score_function(MAXIMUM_TYPE_DISTANCE); + } else { + if (show_error) { + check_assignment(c, o, e->type, str_lit("procedure argument")); + } + err = CallArgumentError_WrongTypes; + } + + if (e->flags & EntityFlag_ConstInput) { + if (o->mode != Addressing_Constant) { + if (show_error) { + error(o->expr, "Expected a constant value for the argument '%.*s'", LIT(e->token.string)); + } + err = CallArgumentError_NoneConstantParameter; + } + } + } else if (show_error) { + check_assignment(c, o, e->type, str_lit("procedure argument")); + } + score += s; + } + + if (o->mode == Addressing_Type && is_type_typeid(e->type)) { + add_type_info_type(c, o->type); + add_type_and_value(c, o->expr, Addressing_Value, e->type, exact_value_typeid(o->type)); + } + } + } + + if (data) { + data->score = score; + data->result_type = final_proc_type->Proc.results; + data->gen_entity = gen_entity; + + + Ast *proc_lit = nullptr; + if (ce->proc->tav.value.kind == ExactValue_Procedure) { + Ast *vp = unparen_expr(ce->proc->tav.value.value_procedure); + if (vp && vp->kind == Ast_ProcLit) { + proc_lit = vp; + } + } + if (proc_lit == nullptr) { + add_type_and_value(c, ce->proc, Addressing_Value, final_proc_type, {}); + } + } + + return err; + } + + if (err) { + return err; + } + + auto operands = positional_operands; + TypeTuple *param_tuple = nullptr; + if (pt->params != nullptr) { + param_tuple = &pt->params->Tuple; + } + + if (operands.count == 0 && param_count_excluding_defaults == 0) { err = CallArgumentError_None; if (variadic) { @@ -5641,6 +5844,7 @@ gb_internal bool is_call_expr_field_value(AstCallExpr *ce) { } gb_internal CALL_ARGUMENT_CHECKER(check_named_call_arguments) { + auto operands = positional_operands; ast_node(ce, CallExpr, call); GB_ASSERT(is_type_proc(proc_type)); proc_type = base_type(proc_type); @@ -6705,37 +6909,26 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex return data; } + // if (pt->variadic) { + // isize param_index = pt->variadic_index; - for_array(i, positional_operands) { - Operand &o = positional_operands[i]; - isize param_index = i; + // Entity *e = pt->params->Tuple.variables[param_index]; + // GB_ASSERT(e->kind == Entity_Variable); + // GB_ASSERT(e->flags & EntityFlag_Ellipsis); + // GB_ASSERT(is_type_slice(e->type)); - Entity *e = pt->params->Tuple.variables[param_index]; - GB_ASSERT(e->kind == Entity_Variable); - - check_assignment(c, &o, e->type, str_lit("procedure call expression")); - } - - if (pt->variadic) { - isize param_index = pt->variadic_index; - - Entity *e = pt->params->Tuple.variables[param_index]; - GB_ASSERT(e->kind == Entity_Variable); - GB_ASSERT(e->flags & EntityFlag_Ellipsis); - GB_ASSERT(is_type_slice(e->type)); - - if (vari_expand) { - if (variadic_operands.count != 0) { - GB_ASSERT(variadic_operands.count == 1); - check_assignment(c, &variadic_operands[0], e->type, str_lit("procedure call expression")); - } - } else { - Type *elem = e->type->Slice.elem; - for (Operand &o : variadic_operands) { - check_assignment(c, &o, elem, str_lit("procedure call expression")); - } - } - } + // if (vari_expand) { + // if (variadic_operands.count != 0) { + // GB_ASSERT(variadic_operands.count == 1); + // check_assignment(c, &variadic_operands[0], e->type, str_lit("procedure call expression")); + // } + // } else { + // Type *elem = e->type->Slice.elem; + // for (Operand &o : variadic_operands) { + // check_assignment(c, &o, elem, str_lit("procedure call expression")); + // } + // } + // } array_resize(&positional_operands, pt->param_count); @@ -6770,68 +6963,6 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex array_add(&named_parameters, NamedParameter{name, fv->value}); } - StringMap type_hint_map = {}; // Key: String - string_map_init(&type_hint_map, 2*named_args.count); - defer (string_map_destroy(&type_hint_map)); - - if (pt != nullptr) { - TypeTuple *param_tuple = &pt->params->Tuple; - for (Entity *e : param_tuple->variables) { - if (is_blank_ident(e->token)) { - continue; - } - string_map_set(&type_hint_map, e->token.string, e->type); - } - } else if (operand->mode == Addressing_ProcGroup) { - Array procs = proc_group_entities(c, *operand); - for (Entity *proc : procs) { - Type *proc_type = base_type(proc->type); - if (is_type_proc(proc_type)) { - TypeProc *pt = &proc_type->Proc; - TypeTuple *param_tuple = nullptr; - if (pt->params != nullptr) { - param_tuple = &pt->params->Tuple; - } - if (param_tuple == nullptr) { - continue; - } - for (Entity *e : param_tuple->variables) { - if (is_blank_ident(e->token)) { - continue; - } - StringHashKey key = string_hash_string(e->token.string); - Type **found = string_map_get(&type_hint_map, key); - if (found) { - Type *t = *found; - if (t == nullptr) { - // NOTE(bill): Ambiguous named parameter across all types - continue; - } - if (are_types_identical(t, e->type)) { - // NOTE(bill): No need to set again - } else { - // NOTE(bill): Ambiguous named parameter across all types so set it to a nullptr - string_map_set(&type_hint_map, key, cast(Type *)nullptr); - } - } else { - string_map_set(&type_hint_map, key, e->type); - } - } - } - } - } - - for (NamedParameter ¶m : named_parameters) { - Type *type_hint = nullptr; - if (Type **found = string_map_get(&type_hint_map, param.key)) { - type_hint = *found; - } - - Operand operand = {}; - check_expr_or_type(c, &operand, param.value, type_hint); - array_add(&named_operands, operand); - } - for (NamedParameter const ¶m : named_parameters) { isize param_index = lookup_procedure_parameter(pt, param.key); if (param_index < 0) { @@ -6856,11 +6987,10 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex any_failure = true; } - check_assignment(c, &o, e->type, str_lit("procedure call expression")); - if (!prev_visited) { positional_operands[param_index] = o; } + array_add(&named_operands, o); } } @@ -6891,17 +7021,6 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex } } - for (Operand const &o : positional_operands) { - check_no_copy_assignment(o, str_lit("procedure call expression")); - } - for (Operand const &o : variadic_operands) { - check_no_copy_assignment(o, str_lit("procedure call expression")); - } - for (Operand const &o : named_operands) { - check_no_copy_assignment(o, str_lit("procedure call expression")); - } - - if (any_failure) { return data; } @@ -6917,7 +7036,7 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex Array operands = array_clone(heap_allocator(), positional_operands); - CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, operands, named_operands, CallArgumentMode_ShowErrors, &data); + CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, 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); @@ -6948,127 +7067,6 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex data.result_type = pt->results; } return data; -#if 0 - - - - /* - auto visited = slice_make(temporary_allocator(), pt->param_count); - auto ordered_values = slice_make(temporary_allocator(), pt->param_count); - - - for_array(i, ce->args) { - Ast *arg = ce->args[i]; - - isize param_index = i; - String param_name = {}; - - if (arg->kind == Ast_FieldValue) { - 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); - any_failure = true; - gb_string_free(expr_str); - continue; - } - param_name = fv->field->Ident.token.string; - param_index = lookup_procedure_parameter(pt, param_name); - if (param_index < 0) { - error(arg, "No parameter named '%.*s' for this procedure type", LIT(param_name)); - any_failure = true; - continue; - } - arg = fv->value; - } else { - if (param_index >= pt->param_count) { - error(arg, "Too many parameters in procedure call, expected a maximum of %td, got %td", pt->param_count, ce->args.count); - any_failure = true; - break; - } - - param_name = pt->params->Tuple.variables[param_index]->token.string; - } - if (visited[param_index]) { - error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(param_name)); - any_failure = true; - } else { - ParameterValue value = {}; - value.kind = ParameterValue_Value; - value.original_ast_expr = arg; - value.ast_value = arg; - ordered_values[param_index] = value; - } - visited[param_index] = true; - } - - for (isize i = 0; i < pt->param_count; i++) { - if (!visited[i]) { - Entity *e = pt->params->Tuple.variables[i]; - if (e->kind == Entity_Variable && - has_parameter_value(e->Variable.param_value)) { - ordered_values[i] = e->Variable.param_value; - visited[i] = true; - } - } - } - - for (isize i = 0; i < visited.count; i++) { - if (!visited[i]) { - Entity *e = pt->params->Tuple.variables[i]; - gbString t = type_to_string(e->type); - defer (gb_string_free(t)); - if (is_blank_ident(e->token)) { - error(call, "Missing parameter of type '%s' at index %td", t, i); - } else { - error(call, "Missing parameter '%.*s' of type '%s'", LIT(e->token.string), t, i); - } - return data; - } - } - */ - if (any_failure) { - return data; - } - - Array operands = {}; - defer (array_free(&operands)); - - Ast *ident = unparen_expr(operand->expr); - while (ident->kind == Ast_SelectorExpr) { - Ast *s = ident->SelectorExpr.selector; - ident = unparen_expr(s); - } - - Entity *e = entity_of_node(ident); - - CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, 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; -#endif }