diff options
| author | ftphikari <ftphikari@gmail.com> | 2023-07-25 15:32:18 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-07-25 15:32:18 +0300 |
| commit | 699aec331d44da58bceddfb788bf349995473ad9 (patch) | |
| tree | 3f5ce42c72c18fff1fc79f0229797be72f0e7638 /src/check_expr.cpp | |
| parent | d2375a79f29d8377c813484bce3127ae9c205974 (diff) | |
| parent | 5ac7fe453f5fbf0995c24f0c1c12ed439ae3aee9 (diff) | |
Merge branch 'odin-lang:master' into master
Diffstat (limited to 'src/check_expr.cpp')
| -rw-r--r-- | src/check_expr.cpp | 2051 |
1 files changed, 1157 insertions, 894 deletions
diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 830b5315d..98154f33d 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -14,6 +14,7 @@ enum CallArgumentError { CallArgumentError_ParameterMissing, CallArgumentError_DuplicateParameter, CallArgumentError_NoneConstantParameter, + CallArgumentError_OutOfOrderParameters, CallArgumentError_MAX, }; @@ -33,12 +34,13 @@ gb_global char const *CallArgumentError_strings[CallArgumentError_MAX] = { "ParameterMissing", "DuplicateParameter", "NoneConstantParameter", + "OutOfOrderParameters", }; -enum CallArgumentErrorMode { - CallArgumentMode_NoErrors, - CallArgumentMode_ShowErrors, +enum struct CallArgumentErrorMode { + NoErrors, + ShowErrors, }; struct CallArgumentData { @@ -65,11 +67,6 @@ 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) -typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType); - - - gb_internal void check_expr (CheckerContext *c, Operand *operand, Ast *expression); gb_internal void check_multi_expr (CheckerContext *c, Operand *operand, Ast *expression); gb_internal void check_multi_expr_or_type (CheckerContext *c, Operand *operand, Ast *expression); @@ -94,14 +91,13 @@ gb_internal void check_stmt (CheckerContext *c, Ast *nod gb_internal void check_stmt_list (CheckerContext *c, Slice<Ast *> const &stmts, u32 flags); gb_internal void check_init_constant (CheckerContext *c, Entity *e, Operand *operand); gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Type *type, ExactValue *out_value); -gb_internal bool check_procedure_type (CheckerContext *c, Type *type, Ast *proc_type_node, Array<Operand> *operands = nullptr); +gb_internal bool check_procedure_type (CheckerContext *c, Type *type, Ast *proc_type_node, Array<Operand> const *operands = nullptr); gb_internal void check_struct_type (CheckerContext *c, Type *struct_type, Ast *node, Array<Operand> *poly_operands, Type *named_type = nullptr, Type *original_type_for_poly = nullptr); gb_internal void check_union_type (CheckerContext *c, Type *union_type, Ast *node, Array<Operand> *poly_operands, Type *named_type = nullptr, Type *original_type_for_poly = nullptr); -gb_internal CallArgumentData check_call_arguments (CheckerContext *c, Operand *operand, Type *proc_type, Ast *call); -gb_internal Type * check_init_variable (CheckerContext *c, Entity *e, Operand *operand, String context_name); +gb_internal Type * check_init_variable (CheckerContext *c, Entity *e, Operand *operand, String context_name); gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type); @@ -121,6 +117,7 @@ gb_internal void check_or_return_split_types(CheckerContext *c, Operand *x, Stri gb_internal bool is_diverging_expr(Ast *expr); +gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_); enum LoadDirectiveResult { LoadDirective_Success = 0, @@ -335,7 +332,7 @@ gb_internal void check_scope_decls(CheckerContext *c, Slice<Ast *> const &nodes, } gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_entity, Type *type, - Array<Operand> *param_operands, Ast *poly_def_node, PolyProcData *poly_proc_data) { + Array<Operand> const *param_operands, Ast *poly_def_node, PolyProcData *poly_proc_data) { /////////////////////////////////////////////////////////////////////////////// // // // TODO CLEANUP(bill): This procedure is very messy and hacky. Clean this!!! // @@ -602,7 +599,7 @@ gb_internal bool check_polymorphic_procedure_assignment(CheckerContext *c, Opera return find_or_generate_polymorphic_procedure(c, base_entity, type, nullptr, poly_def_node, poly_proc_data); } -gb_internal bool find_or_generate_polymorphic_procedure_from_parameters(CheckerContext *c, Entity *base_entity, Array<Operand> *operands, Ast *poly_def_node, PolyProcData *poly_proc_data) { +gb_internal bool find_or_generate_polymorphic_procedure_from_parameters(CheckerContext *c, Entity *base_entity, Array<Operand> const *operands, Ast *poly_def_node, PolyProcData *poly_proc_data) { return find_or_generate_polymorphic_procedure(c, base_entity, nullptr, operands, poly_def_node, poly_proc_data); } @@ -667,6 +664,11 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand if (check_representable_as_constant(c, operand->value, dst, nullptr)) { if (is_type_typed(dst) && src->kind == Type_Basic) { switch (src->Basic.kind) { + case Basic_UntypedBool: + if (is_type_boolean(dst)) { + return 1; + } + break; case Basic_UntypedRune: if (is_type_integer(dst) || is_type_rune(dst)) { return 1; @@ -677,6 +679,11 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand return 1; } break; + case Basic_UntypedString: + if (is_type_string(dst)) { + return 1; + } + break; case Basic_UntypedFloat: if (is_type_float(dst)) { return 1; @@ -701,23 +708,58 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand } return -1; } - if (src->kind == Type_Basic && src->Basic.kind == Basic_UntypedRune) { - if (is_type_integer(dst) || is_type_rune(dst)) { - if (is_type_typed(type)) { - return 2; + if (src->kind == Type_Basic) { + Type *d = base_array_type(dst); + i64 score = -1; + switch (src->Basic.kind) { + case Basic_UntypedBool: + if (is_type_boolean(d)) { + score = 1; + } + break; + case Basic_UntypedRune: + if (is_type_integer(d) || is_type_rune(d)) { + score = 1; + } + break; + case Basic_UntypedInteger: + if (is_type_integer(d) || is_type_rune(d)) { + score = 1; + } + break; + case Basic_UntypedString: + if (is_type_string(d)) { + score = 1; + } + break; + case Basic_UntypedFloat: + if (is_type_float(d)) { + score = 1; + } + break; + case Basic_UntypedComplex: + if (is_type_complex(d)) { + score = 1; + } + if (is_type_quaternion(d)) { + score = 2; + } + break; + case Basic_UntypedQuaternion: + if (is_type_quaternion(d)) { + score = 1; } - return 1; + break; } - return -1; - } - if (src->kind == Type_Basic && src->Basic.kind == Basic_UntypedBool) { - if (is_type_boolean(dst)) { - if (is_type_typed(type)) { - return 2; + if (score > 0) { + if (is_type_typed(d)) { + score += 1; + } + if (d != dst) { + score += 6; } - return 1; } - return -1; + return score; } } } @@ -1677,7 +1719,7 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) { break; case Token_Not: - if (!is_type_boolean(type)) { + if (!is_type_boolean(type) || is_type_array_like(o->type)) { ERROR_BLOCK(); str = expr_to_string(o->expr); error(op, "Operator '%.*s' is only allowed on boolean expressions", LIT(op.string)); @@ -2218,6 +2260,37 @@ gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) { return o->mode != Addressing_Variable && o->mode != Addressing_SoaVariable; } +gb_internal void check_old_for_or_switch_value_usage(Ast *expr) { + if (!build_context.strict_style) { + return; + } + + Entity *e = entity_of_node(expr); + if (e != nullptr && (e->flags & EntityFlag_OldForOrSwitchValue) != 0) { + GB_ASSERT(e->kind == Entity_Variable); + + begin_error_block(); + defer (end_error_block()); + + if ((e->flags & EntityFlag_ForValue) != 0) { + Type *parent_type = type_deref(e->Variable.for_loop_parent_type); + + error(expr, "Assuming a for-in defined value is addressable as the iterable is passed by value has been disallowed with '-strict-style'."); + + if (is_type_map(parent_type)) { + error_line("\tSuggestion: Prefer doing 'for key, &%.*s in ...'\n", LIT(e->token.string)); + } else { + error_line("\tSuggestion: Prefer doing 'for &%.*s in ...'\n", LIT(e->token.string)); + } + } else { + GB_ASSERT((e->flags & EntityFlag_SwitchValue) != 0); + + error(expr, "Assuming a switch-in defined value is addressable as the iterable is passed by value has been disallowed with '-strict-style'."); + error_line("\tSuggestion: Prefer doing 'switch &%.*s in ...'\n", LIT(e->token.string)); + } + } +} + gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) { switch (op.kind) { case Token_And: { // Pointer address @@ -2227,7 +2300,7 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * gbString str = expr_to_string(ue->expr); defer (gb_string_free(str)); - Entity *e = entity_of_node(o->expr); + Entity *e = entity_of_node(ue->expr); if (e != nullptr && (e->flags & EntityFlag_Param) != 0) { error(op, "Cannot take the pointer address of '%s' which is a procedure parameter", str); } else { @@ -2245,7 +2318,15 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * defer (end_error_block()); error(op, "Cannot take the pointer address of '%s'", str); if (e != nullptr && (e->flags & EntityFlag_ForValue) != 0) { - error_line("\tSuggestion: Did you want to pass the iterable value to the for statement by pointer to get addressable semantics?\n"); + Type *parent_type = type_deref(e->Variable.for_loop_parent_type); + + if (parent_type != nullptr && is_type_string(parent_type)) { + error_line("\tSuggestion: Iterating over a string produces an intermediate 'rune' value which cannot be addressed.\n"); + } else if (parent_type != nullptr && is_type_tuple(parent_type)) { + error_line("\tSuggestion: Iterating over a procedure does not produce values which are addressable.\n"); + } else { + error_line("\tSuggestion: Did you want to pass the iterable value to the for statement by pointer to get addressable semantics?\n"); + } } if (e != nullptr && (e->flags & EntityFlag_SwitchValue) != 0) { error_line("\tSuggestion: Did you want to pass the value to the switch statement by pointer to get addressable semantics?\n"); @@ -2270,6 +2351,11 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * o->type = alloc_type_pointer(o->type); } } else { + if (build_context.strict_style && ast_node_expect(node, Ast_UnaryExpr)) { + ast_node(ue, UnaryExpr, node); + check_old_for_or_switch_value_usage(ue->expr); + } + o->type = alloc_type_pointer(o->type); } @@ -4688,7 +4774,10 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod if (entity == nullptr && selector->kind == Ast_Ident) { String field_name = selector->Ident.token.string; - if (is_type_dynamic_array(type_deref(operand->type))) { + Type *t = type_deref(operand->type); + if (t == nullptr) { + error(operand->expr, "Cannot use a selector expression on 0-value expression"); + } else if (is_type_dynamic_array(t)) { init_mem_allocator(c->checker); } sel = lookup_field(operand->type, field_name, operand->mode == Addressing_Type); @@ -5150,14 +5239,19 @@ enum UnpackFlag : u32 { }; -gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs, UnpackFlags flags) { +gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs_arguments, UnpackFlags flags) { bool allow_ok = (flags & UnpackFlag_AllowOk) != 0; bool is_variadic = (flags & UnpackFlag_IsVariadic) != 0; bool allow_undef = (flags & UnpackFlag_AllowUndef) != 0; bool optional_ok = false; isize tuple_index = 0; - for_array(i, rhs) { + for (Ast *rhs : rhs_arguments) { + if (rhs->kind == Ast_FieldValue) { + error(rhs, "Invalid use of 'field = value'"); + rhs = rhs->FieldValue.value; + } + CheckerContext c_ = *ctx; CheckerContext *c = &c_; @@ -5165,12 +5259,11 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize Type *type_hint = nullptr; + if (lhs != nullptr && tuple_index < lhs_count) { // NOTE(bill): override DeclInfo for dependency Entity *e = lhs[tuple_index]; if (e != nullptr) { - // DeclInfo *decl = decl_info_of_entity(e); - // if (decl) c->decl = decl; type_hint = e->type; if (e->flags & EntityFlag_Ellipsis) { GB_ASSERT(is_type_slice(e->type)); @@ -5182,8 +5275,6 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize // NOTE(bill): override DeclInfo for dependency Entity *e = lhs[lhs_count-1]; if (e != nullptr) { - // DeclInfo *decl = decl_info_of_entity(e); - // if (decl) c->decl = decl; type_hint = e->type; if (e->flags & EntityFlag_Ellipsis) { GB_ASSERT(is_type_slice(e->type)); @@ -5193,15 +5284,15 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize } } - Ast *rhs_expr = unparen_expr(rhs[i]); + Ast *rhs_expr = unparen_expr(rhs); if (allow_undef && rhs_expr != nullptr && rhs_expr->kind == Ast_Uninit) { // NOTE(bill): Just handle this very specific logic here o.type = t_untyped_uninit; o.mode = Addressing_Value; - o.expr = rhs[i]; - add_type_and_value(c, rhs[i], o.mode, o.type, o.value); + o.expr = rhs; + add_type_and_value(c, rhs, o.mode, o.type, o.value); } else { - check_expr_base(c, &o, rhs[i], type_hint); + check_expr_base(c, &o, rhs, type_hint); } if (o.mode == Addressing_NoValue) { error_operand_no_value(&o); @@ -5209,7 +5300,7 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize } if (o.type == nullptr || o.type->kind != Type_Tuple) { - if (allow_ok && lhs_count == 2 && rhs.count == 1 && + if (allow_ok && lhs_count == 2 && rhs_arguments.count == 1 && (o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk || o.mode == Addressing_OptionalOkPtr)) { Ast *expr = unparen_expr(o.expr); @@ -5309,7 +5400,36 @@ gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize * } -gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { +gb_internal isize lookup_procedure_parameter(TypeProc *pt, String const ¶meter_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 isize lookup_procedure_parameter(Type *type, String const ¶meter_name) { + type = base_type(type); + GB_ASSERT(type->kind == Type_Proc); + return lookup_procedure_parameter(&type->Proc, parameter_name); +} + +gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, Ast *call, + Entity *entity, Type *proc_type, + Array<Operand> positional_operands, Array<Operand> const &named_operands, + CallArgumentErrorMode show_error_mode, + CallArgumentData *data) { + TEMPORARY_ALLOCATOR_GUARD(); + + CallArgumentError err = CallArgumentError_None; + ast_node(ce, CallExpr, call); GB_ASSERT(is_type_proc(proc_type)); proc_type = base_type(proc_type); @@ -5320,16 +5440,8 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { bool variadic = pt->variadic; bool vari_expand = (ce->ellipsis.pos.line != 0); i64 score = 0; - bool show_error = show_error_mode == CallArgumentMode_ShowErrors; - - - TypeTuple *param_tuple = nullptr; - if (pt->params != nullptr) { - param_tuple = &pt->params->Tuple; - } - + bool show_error = show_error_mode == CallArgumentErrorMode::ShowErrors; - CallArgumentError err = CallArgumentError_None; Type *final_proc_type = proc_type; Entity *gen_entity = nullptr; @@ -5347,302 +5459,135 @@ 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) { - err = CallArgumentError_None; + } - if (variadic) { - GB_ASSERT(param_tuple != nullptr && param_tuple->variables.count > 0); - Type *t = param_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; + GB_ASSERT(ce->split_args); + auto visited = slice_make<bool>(temporary_allocator(), pt->param_count); + auto ordered_operands = array_make<Operand>(temporary_allocator(), pt->param_count); + defer ({ + for (Operand const &o : ordered_operands) { + if (o.expr != nullptr) { + call->viral_state_flags |= o.expr->viral_state_flags; } } - } else { - i32 error_code = 0; - if (operands.count < param_count_excluding_defaults) { - error_code = -1; - } else if (!variadic && operands.count > param_count) { - error_code = +1; - } - if (error_code != 0) { - err = CallArgumentError_TooManyArguments; - char const *err_fmt = "Too many arguments for '%s', expected %td arguments, got %td"; - if (error_code < 0) { - err = CallArgumentError_TooFewArguments; - err_fmt = "Too few 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, operands.count); - - #if 0 - error_line("\t"); - for_array(i, operands) { - if (i > 0) { - error_line(", "); - } - gbString s = expr_to_string(operands[i].expr); - error_line("%s", s); - gb_string_free(s); - } - error_line("\n"); - #endif - } - } else { - // NOTE(bill): Generate the procedure type for this generic instance - if (pt->is_polymorphic && !pt->is_poly_specialized) { - PolyProcData poly_proc_data = {}; - if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &operands, call, &poly_proc_data)) { - gen_entity = poly_proc_data.gen_entity; - GB_ASSERT(is_type_proc(gen_entity->type)); - final_proc_type = gen_entity->type; - } else { - err = CallArgumentError_WrongTypes; - } - } - - GB_ASSERT(is_type_proc(final_proc_type)); - TypeProc *pt = &final_proc_type->Proc; - - GB_ASSERT(pt->params != nullptr); - auto sig_params = pt->params->Tuple.variables; - isize operand_index = 0; - isize max_operand_count = gb_min(param_count, operands.count); - for (; operand_index < max_operand_count; operand_index++) { - Entity *e = sig_params[operand_index]; - Type *t = e->type; - Operand o = operands[operand_index]; - if (o.expr != nullptr) { - call->viral_state_flags |= o.expr->viral_state_flags; - } - - if (e->kind == Entity_TypeName) { - // GB_ASSERT(!variadic); - if (o.mode == Addressing_Invalid) { - continue; - } else 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); - } + }); - continue; - } + 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); - bool param_is_variadic = pt->variadic && pt->variadic_index == operand_index; + for (isize i = 0; i < positional_operand_count; i++) { + ordered_operands[i] = positional_operands[i]; + visited[i] = true; + } - i64 s = 0; - if (!check_is_assignable_to_with_score(c, &o, t, &s, param_is_variadic)) { - bool ok = false; - if (e->flags & EntityFlag_AnyInt) { - if (is_type_integer(t)) { - ok = check_is_castable_to(c, &o, t); - } - } - if (ok) { - s = assign_score_function(MAXIMUM_TYPE_DISTANCE); - } else { - if (show_error) { - check_assignment(c, &o, t, str_lit("argument")); - } - // TODO(bill, 2021-05-05): Is this incorrect logic to only fail if there is ambiguity for definite? - if (o.mode == Addressing_Invalid) { - err = CallArgumentError_WrongTypes; - } - } - } else if (show_error) { - check_assignment(c, &o, t, str_lit("argument")); - } - score += s; + auto variadic_operands = slice(slice_from_array(positional_operands), positional_operand_count, positional_operands.count); - 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; - } - } + 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]; - 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)); - } else if (show_error && is_type_untyped(o.type)) { - update_untyped_expr_type(c, o.expr, t, true); + 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; } - - if (variadic) { - bool variadic_expand = false; - Type *slice = sig_params[param_count]->type; - GB_ASSERT(is_type_slice(slice)); - Type *elem = base_type(slice)->Slice.elem; - Type *t = elem; - - if (is_type_polymorphic(t)) { - error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input"); - err = CallArgumentError_AmbiguousPolymorphicVariadic; - } - - for (; operand_index < operands.count; operand_index++) { - Operand o = operands[operand_index]; - if (vari_expand) { - variadic_expand = true; - 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"); - } - if (data) { - data->score = score; - data->result_type = final_proc_type->Proc.results; - data->gen_entity = gen_entity; - } - return CallArgumentError_MultipleVariadicExpand; - } - } - i64 s = 0; - if (!check_is_assignable_to_with_score(c, &o, t, &s, true)) { - if (show_error) { - check_assignment(c, &o, t, str_lit("argument")); - } - err = CallArgumentError_WrongTypes; - } else if (show_error) { - check_assignment(c, &o, t, str_lit("argument")); - } - score += s; - if (is_type_any(elem)) { - add_type_info_type(c, o.type); - } - if (o.mode == Addressing_Type && is_type_typeid(t)) { - add_type_info_type(c, o.type); - add_type_and_value(c, o.expr, Addressing_Value, t, exact_value_typeid(o.type)); - } else if (show_error && is_type_untyped(o.type)) { - update_untyped_expr_type(c, o.expr, t, true); - } + 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 (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 (visited[param_index]) { + if (show_error) { + error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name)); + } + err = CallArgumentError_DuplicateParameter; + continue; } - } - if (proc_lit == nullptr) { - add_type_and_value(c, ce->proc, Addressing_Value, final_proc_type, {}); - } - } - - return err; -} - -gb_internal bool is_call_expr_field_value(AstCallExpr *ce) { - GB_ASSERT(ce != nullptr); - - if (ce->args.count == 0) { - return false; - } - 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; + visited[param_index] = true; + ordered_operands[param_index] = operand; } } - return -1; -} -gb_internal CALL_ARGUMENT_CHECKER(check_named_call_arguments) { - ast_node(ce, CallExpr, call); - GB_ASSERT(is_type_proc(proc_type)); - proc_type = base_type(proc_type); - TypeProc *pt = &proc_type->Proc; + isize dummy_argument_count = 0; + bool actually_variadic = false; - i64 score = 0; - bool show_error = show_error_mode == CallArgumentMode_ShowErrors; - CallArgumentError err = CallArgumentError_None; + 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 if (!visited[pt->variadic_index]) { + visited[pt->variadic_index] = true; - TEMPORARY_ALLOCATOR_GUARD(); + Operand *variadic_operand = &ordered_operands[pt->variadic_index]; - isize param_count = pt->param_count; - bool *visited = gb_alloc_array(temporary_allocator(), bool, param_count); - auto ordered_operands = array_make<Operand>(temporary_allocator(), param_count); - defer ({ - for (Operand const &o : ordered_operands) { - if (o.expr != nullptr) { - call->viral_state_flags |= o.expr->viral_state_flags; - } - } - }); + if (vari_expand) { + GB_ASSERT(variadic_operands.count != 0); + *variadic_operand = variadic_operands[0]; + variadic_operand->type = default_type(variadic_operand->type); + actually_variadic = true; + } else { + AstFile *f = call->file(); - for_array(i, ce->args) { - Ast *arg = ce->args[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 index = lookup_procedure_parameter(pt, name); - if (index < 0) { - if (show_error) { - error(arg, "No parameter named '%.*s' for this procedure type", LIT(name)); + // HACK(bill): this is an awful hack + Operand o = {}; + o.mode = Addressing_Value; + o.expr = ast_ident(f, make_token_ident("nil")); + o.expr->Ident.token.pos = ast_token(call).pos; + if (variadic_operands.count != 0) { + actually_variadic = true; + o.expr->Ident.token.pos = ast_token(variadic_operands[0].expr).pos; + + Entity *vt = pt->params->Tuple.variables[pt->variadic_index]; + if (is_type_polymorphic(vt->type)) { + o.type = alloc_type_slice(default_type(variadic_operands[0].type)); + } else { + o.type = vt->type; + } + } else { + dummy_argument_count += 1; + o.type = t_untyped_nil; + } + *variadic_operand = o; } - err = CallArgumentError_ParameterNotFound; - continue; - } - if (visited[index]) { - if (show_error) { - error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name)); - } - err = CallArgumentError_DuplicateParameter; - continue; } - visited[index] = true; - ordered_operands[index] = operands[i]; } - // NOTE(bill): Check for default values and missing parameters - isize param_count_to_check = param_count; - if (pt->variadic) { - param_count_to_check--; + 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 < param_count_to_check; i++) { + + 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)) { @@ -5650,6 +5595,11 @@ gb_internal CALL_ARGUMENT_CHECKER(check_named_call_arguments) { } if (e->kind == Entity_Variable) { if (e->Variable.param_value.kind != ParameterValue_Invalid) { + ordered_operands[i].mode = Addressing_Value; + ordered_operands[i].type = e->type; + ordered_operands[i].expr = e->Variable.param_value.original_ast_expr; + + dummy_argument_count += 1; score += assign_score_function(1); continue; } @@ -5672,97 +5622,174 @@ gb_internal CALL_ARGUMENT_CHECKER(check_named_call_arguments) { } } - Entity *gen_entity = nullptr; - 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; + auto eval_param_and_score = [](CheckerContext *c, Operand *o, Type *param_type, CallArgumentError &err, bool param_is_variadic, Entity *e, bool show_error) -> i64 { + i64 s = 0; + if (!check_is_assignable_to_with_score(c, o, param_type, &s, param_is_variadic)) { + bool ok = false; + if (e && e->flags & EntityFlag_AnyInt) { + if (is_type_integer(param_type)) { + ok = check_is_castable_to(c, o, param_type); + } + } + if (ok) { + s = assign_score_function(MAXIMUM_TYPE_DISTANCE); + } else { + if (show_error) { + check_assignment(c, o, param_type, str_lit("procedure argument")); + } + err = CallArgumentError_WrongTypes; + } + + } else if (show_error) { + check_assignment(c, o, param_type, str_lit("procedure argument")); } - } + if (e && 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; + } + } - for (isize i = 0; i < 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 (!err && is_type_any(param_type)) { + add_type_info_type(c, o->type); + } + if (o->mode == Addressing_Type && is_type_typeid(param_type)) { + add_type_info_type(c, o->type); + add_type_and_value(c, o->expr, Addressing_Value, param_type, exact_value_typeid(o->type)); + } else if (show_error && is_type_untyped(o->type)) { + update_untyped_expr_type(c, o->expr, param_type, true); + } - 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 (is_type_polymorphic(elem)) { + return s; + }; + + + 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)) { + if (show_error) { error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input"); - err = CallArgumentError_AmbiguousPolymorphicVariadic; - return err; } + err = CallArgumentError_AmbiguousPolymorphicVariadic; } - continue; } + } 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)); + final_proc_type = gen_entity->type; + pt = &gept->Proc; - 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)); - } + } else { err = CallArgumentError_WrongTypes; } - if (are_types_identical(e->type, o->type)) { - score += assign_score_function(1); - } else { - score += assign_score_function(MAXIMUM_TYPE_DISTANCE); + } + + for (isize i = 0; i < pt->param_count; i++) { + Operand *o = &ordered_operands[i]; + if (o->mode == Addressing_Invalid) { + continue; } - } 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 { + + Entity *e = pt->params->Tuple.variables[i]; + bool param_is_variadic = pt->variadic && pt->variadic_index == i; + + if (e->kind == Entity_TypeName) { + GB_ASSERT(pt->is_polymorphic); + if (o->mode != Addressing_Type) { if (show_error) { - check_assignment(c, o, e->type, str_lit("procedure argument")); + error(o->expr, "Expected a type for the argument '%.*s'", LIT(e->token.string)); } 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; - } + if (are_types_identical(e->type, o->type)) { + score += assign_score_function(1); + } else { + score += assign_score_function(MAXIMUM_TYPE_DISTANCE); } - } else if (show_error) { - check_assignment(c, o, e->type, str_lit("procedure argument")); + continue; } - score += s; + + if (param_is_variadic) { + continue; + } + + score += eval_param_and_score(c, o, e->type, err, param_is_variadic, e, show_error); } + } - 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 (variadic) { + Type *slice = pt->params->Tuple.variables[pt->variadic_index]->type; + GB_ASSERT(is_type_slice(slice)); + Type *elem = base_type(slice)->Slice.elem; + Type *t = elem; + + if (is_type_polymorphic(t)) { + error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input %s", type_to_string(final_proc_type)); + err = CallArgumentError_AmbiguousPolymorphicVariadic; + } + + for_array(operand_index, variadic_operands) { + Operand *o = &variadic_operands[operand_index]; + if (vari_expand) { + t = slice; + if (operand_index > 0) { + if (show_error) { + error(o->expr, "'..' in a variadic procedure can only have one variadic argument at the end"); + } + if (data) { + data->score = score; + data->result_type = final_proc_type->Proc.results; + data->gen_entity = gen_entity; + } + return CallArgumentError_MultipleVariadicExpand; + } + } + score += eval_param_and_score(c, o, t, err, true, nullptr, show_error); } } if (data) { data->score = score; - data->result_type = pt->results; + data->result_type = final_proc_type->Proc.results; data->gen_entity = gen_entity; - add_type_and_value(c, ce->proc, Addressing_Value, proc_type, {}); + + + 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; } +gb_internal bool is_call_expr_field_value(AstCallExpr *ce) { + GB_ASSERT(ce != nullptr); + + if (ce->args.count == 0) { + return false; + } + return ce->args[0]->kind == Ast_FieldValue; +} + gb_internal Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize *lhs_count_, bool *is_variadic) { Entity **lhs = nullptr; isize lhs_count = -1; @@ -5867,535 +5894,704 @@ gb_internal bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Sco return true; } +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(CheckerContext *c, Operand *operand, Type *proc_type, Ast *call, Slice<Ast *> const &args) { - ast_node(ce, CallExpr, call); - - CallArgumentCheckerType *call_checker = check_call_arguments_internal; - Array<Operand> operands = {}; - defer (array_free(&operands)); - - Type *result_type = t_invalid; - - if (is_call_expr_field_value(ce)) { - call_checker = check_named_call_arguments; - - operands = array_make<Operand>(heap_allocator(), args.count); - - // NOTE(bill): This is give type hints for the named parameters - // in order to improve the type inference system - - StringMap<Type *> type_hint_map = {}; // Key: String - string_map_init(&type_hint_map, 2*args.count); - defer (string_map_destroy(&type_hint_map)); - - Type *ptype = nullptr; - bool single_case = true; - - if (operand->mode == Addressing_ProcGroup) { - single_case = false; - Array<Entity *> procs = proc_group_entities(c, *operand); - if (procs.count == 1) { - ptype = procs[0]->type; - single_case = true; - } - } else { - ptype = proc_type; + type = base_type(type); + if (named_args.count > 0) { + TypeProc *pt = nullptr; + if (is_type_proc(type)) { + pt = &type->Proc; } - if (single_case) { - Type *bptype = base_type(ptype); - if (is_type_proc(bptype)) { - TypeProc *pt = &bptype->Proc; - TypeTuple *param_tuple = nullptr; - if (pt->params != nullptr) { - param_tuple = &pt->params->Tuple; + for_array(i, named_args) { + Ast *arg = named_args[i]; + if (arg->kind != Ast_FieldValue) { + if (show_error) { + error(arg, "Expected a 'field = value'"); } - if (param_tuple != nullptr) { - for (Entity *e : param_tuple->variables) { - if (is_blank_ident(e->token)) { - continue; - } - string_map_set(&type_hint_map, e->token.string, e->type); - } + 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; } - } else { - Array<Entity *> 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); - } + 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", LIT(key)); } + success = false; + continue; } + + 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 bool check_call_arguments_single(CheckerContext *c, Ast *call, Operand *operand, + Entity *e, Type *proc_type, + Array<Operand> const &positional_operands, Array<Operand> const &named_operands, + CallArgumentErrorMode show_error_mode, + CallArgumentData *data) { + + bool return_on_failure = show_error_mode == CallArgumentErrorMode::NoErrors; + + Ast *ident = operand->expr; + while (ident->kind == Ast_SelectorExpr) { + Ast *s = ident->SelectorExpr.selector; + ident = s; + } + + if (e == nullptr) { + e = entity_of_node(ident); + if (e != nullptr) { + proc_type = e->type; } + } + GB_ASSERT(proc_type != nullptr); + proc_type = base_type(proc_type); + GB_ASSERT(proc_type->kind == Type_Proc); - for_array(i, args) { - Ast *arg = args[i]; - ast_node(fv, FieldValue, arg); - Ast *field = fv->field; + CallArgumentError err = check_call_arguments_internal(c, call, e, proc_type, positional_operands, named_operands, show_error_mode, data); + if (return_on_failure && err != CallArgumentError_None) { + return false; + } - Type *type_hint = nullptr; + Entity *entity_to_use = data->gen_entity != nullptr ? data->gen_entity : e; + if (!return_on_failure && entity_to_use != nullptr) { + add_entity_use(c, ident, entity_to_use); + update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); + add_type_and_value(c, operand->expr, operand->mode, entity_to_use->type, operand->value); + } - if (field != nullptr && field->kind == Ast_Ident) { - String key = field->Ident.token.string; - Type **found = string_map_get(&type_hint_map, key); - if (found) { - type_hint = *found; - } + 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, !return_on_failure); + if (return_on_failure) { + if (!ok) { + return false; + } + + } else { + 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 (is_type_proc(data->gen_entity->type)) { + Type *t = base_type(entity_to_use->type); + data->result_type = t->Proc.results; } - check_expr_or_type(c, &operands[i], fv->value, type_hint); - } - } else { - operands = array_make<Operand>(heap_allocator(), 0, 2*args.count); - Entity **lhs = nullptr; - isize lhs_count = -1; - bool is_variadic = false; - if (proc_type != nullptr && is_type_proc(proc_type)) { - lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic); - } - if (operand->mode != Addressing_ProcGroup) { - check_unpack_arguments(c, lhs, lhs_count, &operands, args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); } } - for (Operand const &o : operands) { - check_no_copy_assignment(o, str_lit("call expression")); - } + return true; +} - if (operand->mode == Addressing_ProcGroup) { - check_entity_decl(c, operand->proc_group, nullptr, nullptr); - auto procs = proc_group_entities_cloned(c, *operand); +gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, Operand *operand, Ast *call) { + ast_node(ce, CallExpr, call); + GB_ASSERT(ce->split_args != nullptr); - if (procs.count > 1) { - isize max_arg_count = args.count; - for (Ast *arg : 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); + 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); - } else { - proc_index++; + // ignore named arguments first + for (Ast *arg : named_args) { + if (arg->kind != Ast_FieldValue) { + continue; + } + ast_node(fv, FieldValue, arg); + if (fv->field->kind != Ast_Ident) { + continue; + } + String key = fv->field->Ident.token.string; + for (isize proc_index = procs.count-1; proc_index >= 0; proc_index--) { + Type *t = procs[proc_index]->type; + if (is_type_proc(t)) { + isize param_index = lookup_procedure_parameter(t, key); + if (param_index < 0) { + array_unordered_remove(&procs, proc_index); + } } } } - if (procs.count == 1) { - Ast *ident = operand->expr; - while (ident->kind == Ast_SelectorExpr) { - Ast *s = ident->SelectorExpr.selector; - ident = s; - } + if (procs.count == 0) { + // if any of the named arguments are wrong, the `procs` will be empty + // just start from scratch + array_free(&procs); + procs = proc_group_entities_cloned(c, *operand); + } - Entity *e = procs[0]; + // filter by positional argument length + 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; + } - Entity **lhs = nullptr; - isize lhs_count = -1; - bool is_variadic = false; - 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); + isize param_count = 0; + isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(pt, ¶m_count); - CallArgumentData data = {}; - CallArgumentError err = call_checker(c, call, e->type, e, operands, CallArgumentMode_ShowErrors, &data); - if (err != CallArgumentError_None) { - // handle error - } - 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 (param_count_excluding_defaults > max_arg_count) { + array_unordered_remove(&procs, proc_index); + continue; } - return data; + proc_index++; } + } + Entity **lhs = nullptr; + isize lhs_count = -1; + bool is_variadic = false; - Entity **lhs = nullptr; - isize lhs_count = -1; + 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)); - { - // 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 (procs.count == 1) { + 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)) { + check_call_arguments_single(c, call, operand, + e, e->type, + positional_operands, named_operands, + CallArgumentErrorMode::ShowErrors, + &data); + } + 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) + 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 { + proc_arg_count = gb_min(proc_arg_count, pt->Proc.param_count); } } + } + if (proc_arg_count >= 0) { + 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 (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)) { - 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; + 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; } + lhs[param_index] = e; } } } + } + check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); - check_unpack_arguments(c, lhs, lhs_count, &operands, args, UnpackFlag_None); - - if (lhs != nullptr) { - gb_free(heap_allocator(), lhs); + for_array(i, named_args) { + Ast *arg = named_args[i]; + if (arg->kind != Ast_FieldValue) { + error(arg, "Expected a 'field = value'"); + return data; } - - 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); + 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; - gbString expr_name = expr_to_string(operand->expr); - defer (gb_string_free(expr_name)); + 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); + } - 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; + gb_free(heap_allocator(), lhs); - ctx.no_polymorphic_errors = true; - ctx.allow_polymorphic_types = is_type_polymorphic(pt); - ctx.hide_polymorphic_errors = true; + auto valids = array_make<ValidIndexAndScore>(heap_allocator(), 0, procs.count); + defer (array_free(&valids)); - err = call_checker(&ctx, call, pt, p, operands, CallArgumentMode_NoErrors, &data); - if (err != CallArgumentError_None) { - continue; - } - isize index = i; + 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); + } - 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; - } + gbString expr_name = expr_to_string(operand->expr); + defer (gb_string_free(expr_name)); - array_add(&proc_entities, data.gen_entity); - index = proc_entities.count-1; - } + for_array(i, procs) { + Entity *p = procs[i]; + Type *pt = base_type(p->type); + if (pt != nullptr && is_type_proc(pt)) { + CallArgumentData data = {}; + CheckerContext ctx = *c; - ValidIndexAndScore item = {}; - item.index = index; - item.score = data.score; - array_add(&valids, item); - } - } + ctx.no_polymorphic_errors = true; + ctx.allow_polymorphic_types = is_type_polymorphic(pt); + ctx.hide_polymorphic_errors = true; - 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; - } + bool is_a_candidate = check_call_arguments_single(&ctx, call, operand, + p, pt, + positional_operands, named_operands, + CallArgumentErrorMode::NoErrors, + &data); + if (!is_a_candidate) { + continue; } - } + isize index = i; + ValidIndexAndScore item = {}; + item.score = data.score; - if (valids.count == 0) { - begin_error_block(); - defer (end_error_block()); + if (data.gen_entity != nullptr) { + array_add(&proc_entities, data.gen_entity); + index = proc_entities.count-1; - error(operand->expr, "No procedures or ambiguous call for procedure group '%s' that match with the given arguments", expr_name); - if (operands.count == 0) { - error_line("\tNo given arguments\n"); - } else { - error_line("\tGiven argument types: ("); - for_array(i, operands) { - Operand o = operands[i]; - if (i > 0) error_line(", "); - gbString type = type_to_string(o.type); - defer (gb_string_free(type)); - error_line("%s", type); - } - error_line(")\n"); + // prefer non-polymorphic procedures over polymorphic + item.score += assign_score_function(1); } - 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; + item.index = index; + array_add(&valids, item); + } + } - 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 (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 (procs.count > 0) { - error_line("\n"); + 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); - result_type = t_invalid; - } else if (valids.count > 1) { - begin_error_block(); - defer (end_error_block()); + gbString field = expr_to_string(fv->field); + defer (gb_string_free(field)); - error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name); - error_line("\tGiven argument types: ("); - for_array(i, operands) { - Operand o = operands[i]; - if (i > 0) error_line(", "); - gbString type = type_to_string(o.type); - defer (gb_string_free(type)); + error_line("%s = %s", field, type); + } else { error_line("%s", type); } - error_line(")\n"); - - 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); + } + error_line(")\n"); + }; - if (j != pl->where_clauses.count-1) { - error_line(","); - } - } - error_line("\n\t"); - } - } - error_line("at %s\n", token_pos_to_string(pos)); - } - result_type = t_invalid; + 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 { - GB_ASSERT(valids.count == 1); - Ast *ident = operand->expr; - while (ident->kind == Ast_SelectorExpr) { - Ast *s = ident->SelectorExpr.selector; - ident = s; + print_argument_types(); + } + + if (procs.count == 0) { + procs = proc_group_entities_cloned(c, *operand); + } + if (procs.count > 0) { + error_line("Did you mean to use one of the following:\n"); + } + isize max_name_length = 0; + isize max_type_length = 0; + for (Entity *proc : procs) { + Type *t = base_type(proc->type); + if (t == t_invalid) continue; + String prefix = {}; + String prefix_sep = {}; + if (proc->pkg) { + prefix = proc->pkg->name; + prefix_sep = str_lit("."); + } + String name = proc->token.string; + max_name_length = gb_max(max_name_length, prefix.len + prefix_sep.len + name.len); + + gbString pt; + if (t->Proc.node != nullptr) { + pt = expr_to_string(t->Proc.node); + } else { + pt = type_to_string(t); } - Entity *e = proc_entities[valids[0].index]; - GB_ASSERT(e != nullptr); + max_type_length = gb_max(max_type_length, gb_string_length(pt)); + gb_string_free(pt); + } - proc_type = e->type; - CallArgumentData data = {}; - CallArgumentError err = call_checker(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); - } + isize max_spaces = gb_max(max_name_length, max_type_length); + char *spaces = gb_alloc_array(temporary_allocator(), char, max_spaces+1); + for (isize i = 0; i < max_spaces; i++) { + spaces[i] = ' '; + } + spaces[max_spaces] = 0; - 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; + 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; + isize len = prefix.len + prefix_sep.len + name.len; + + int name_padding = cast(int)gb_max(max_name_length - len, 0); + int type_padding = cast(int)gb_max(max_type_length - gb_string_length(pt), 0); + + char const *sep = "::"; + if (proc->kind == Entity_Variable) { + sep = ":="; + } + error_line("\t%.*s%.*s%.*s %.*s%s %s %.*sat %s\n", + LIT(prefix), LIT(prefix_sep), LIT(name), + name_padding, spaces, + sep, + pt, + type_padding, spaces, + token_pos_to_string(pos) + ); + } + if (procs.count > 0) { + error_line("\n"); + } - 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; + 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 (auto const &valid : valids) { + Entity *proc = proc_entities[valid.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 (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 (j != pl->where_clauses.count-1) { + error_line(","); + } + } + error_line("\n\t"); } } - return data; + 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 = entity_of_node(ident); + Entity *e = proc_entities[valids[0].index]; + GB_ASSERT(e != nullptr); + Array<Operand> named_operands = {}; - CallArgumentData data = {}; - CallArgumentError err = call_checker(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); + check_call_arguments_single(c, call, operand, + e, e->type, + positional_operands, named_operands, + CallArgumentErrorMode::ShowErrors, + &data); + return data; + } + + return data; +} + + +gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Ast *call) { + Type *proc_type = nullptr; + + CallArgumentData data = {}; + data.result_type = t_invalid; + + proc_type = base_type(operand->type); + + TypeProc *pt = nullptr; + if (proc_type) { + pt = &proc_type->Proc; + } + + TEMPORARY_ALLOCATOR_GUARD(); + ast_node(ce, CallExpr, call); + + bool any_failure = false; + + // 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; + } } - 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; + named_args = slice(ce->args, positional_args.count, ce->args.count); - 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; + 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; + } - 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 (operand->mode == Addressing_ProcGroup) { + return check_call_arguments_proc_group(c, operand, call); + } + + auto positional_operands = array_make<Operand>(heap_allocator(), 0, positional_args.count); + auto named_operands = array_make<Operand>(heap_allocator(), 0, 0); + + defer (array_free(&positional_operands)); + defer (array_free(&named_operands)); + + if (positional_args.count > 0) { + isize lhs_count = -1; + bool is_variadic = false; + Entity **lhs = nullptr; + if (pt != nullptr) { + lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic); + } + check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); + } + + if (named_args.count > 0) { + 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); + any_failure = true; + gb_string_free(expr_str); + continue; } + String key = fv->field->Ident.token.string; + Ast *value = fv->value; + + isize param_index = lookup_procedure_parameter(pt, key); + Type *type_hint = nullptr; + if (param_index >= 0) { + Entity *e = pt->params->Tuple.variables[param_index]; + type_hint = e->type; + } + + Operand o = {}; + check_expr_with_type_hint(c, &o, value, type_hint); + if (o.mode == Addressing_Invalid) { + any_failure = true; + } + array_add(&named_operands, o); } - return data; + } + if (!any_failure) { + check_call_arguments_single(c, call, operand, + nullptr, proc_type, + positional_operands, named_operands, + CallArgumentErrorMode::ShowErrors, + &data); + } else if (pt) { + data.result_type = pt->results; + } - CallArgumentData data = {}; - data.result_type = t_invalid; return data; } - gb_internal isize lookup_polymorphic_record_parameter(Type *t, String parameter_name) { if (!is_type_polymorphic_record(t)) { return -1; @@ -6699,15 +6895,15 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O return err; } - String generated_name = make_string_c(expr_to_string(call)); - CheckerContext ctx = *c; // NOTE(bill): We need to make sure the lookup scope for the record is the same as where it was created ctx.scope = polymorphic_record_parent_scope(original_type); GB_ASSERT(ctx.scope != nullptr); - Type *named_type = alloc_type_named(generated_name, nullptr, nullptr); Type *bt = base_type(original_type); + String generated_name = make_string_c(expr_to_string(call)); + + Type *named_type = alloc_type_named(generated_name, nullptr, nullptr); if (bt->kind == Type_Struct) { Ast *node = clone_ast(bt->Struct.node); Type *struct_type = alloc_type_struct(); @@ -6732,6 +6928,49 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O GB_PANIC("Unsupported parametric polymorphic record type"); } + + bt = base_type(named_type); + if (bt->kind == Type_Struct || bt->kind == Type_Union) { + GB_ASSERT(original_type->kind == Type_Named); + Entity *e = original_type->Named.type_name; + GB_ASSERT(e->kind == Entity_TypeName); + + gbString s = gb_string_make_reserve(heap_allocator(), e->token.string.len+3); + s = gb_string_append_fmt(s, "%.*s(", LIT(e->token.string)); + + Type *params = nullptr; + switch (bt->kind) { + case Type_Struct: params = bt->Struct.polymorphic_params; break; + case Type_Union: params = bt->Union.polymorphic_params; break; + } + + if (params != nullptr) for_array(i, params->Tuple.variables) { + Entity *v = params->Tuple.variables[i]; + String name = v->token.string; + if (i > 0) { + s = gb_string_append_fmt(s, ", "); + } + s = gb_string_append_fmt(s, "$%.*s", LIT(name)); + + if (v->kind == Entity_TypeName) { + if (v->type->kind != Type_Generic) { + s = gb_string_append_fmt(s, "="); + s = write_type_to_string(s, v->type, false); + } + } else if (v->kind == Entity_Constant) { + s = gb_string_append_fmt(s, "="); + s = write_exact_value_to_string(s, v->Constant.value); + } + } + s = gb_string_append_fmt(s, ")"); + + String new_name = make_string_c(s); + named_type->Named.name = new_name; + if (named_type->Named.type_name) { + named_type->Named.type_name->token.string = new_name; + } + } + operand->mode = Addressing_Type; operand->type = named_type; } @@ -6740,6 +6979,46 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O +// returns true on success +gb_internal bool check_call_parameter_mixture(Slice<Ast *> const &args, char const *context, bool allow_mixed=false) { + bool success = true; + if (args.count > 0) { + if (allow_mixed) { + bool was_named = false; + for (Ast *arg : args) { + if (was_named && arg->kind != Ast_FieldValue) { + error(arg, "Non-named parameter is not allowed to follow named parameter i.e. 'field = value' in a %s", context); + success = false; + break; + } + was_named = was_named || arg->kind == Ast_FieldValue; + } + } else { + bool first_is_field_value = (args[0]->kind == Ast_FieldValue); + for (Ast *arg : args) { + bool mix = false; + if (first_is_field_value) { + mix = arg->kind != Ast_FieldValue; + } else { + mix = arg->kind == Ast_FieldValue; + } + if (mix) { + error(arg, "Mixture of 'field = value' and value elements in a %s is not allowed", context); + success = false; + } + } + } + + } + return success; +} + +#define CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN(context_, ...) if (!check_call_parameter_mixture(args, context_, ##__VA_ARGS__)) { \ + operand->mode = Addressing_Invalid; \ + operand->expr = call; \ + return Expr_Stmt; \ +} + gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice<Ast *> const &args, ProcInlining inlining, Type *type_hint) { if (proc != nullptr && @@ -6779,30 +7058,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } } - if (args.count > 0) { - bool fail = false; - bool first_is_field_value = (args[0]->kind == Ast_FieldValue); - for (Ast *arg : args) { - bool mix = false; - if (first_is_field_value) { - mix = arg->kind != Ast_FieldValue; - } else { - mix = arg->kind == Ast_FieldValue; - } - if (mix) { - error(arg, "Mixture of 'field = value' and value elements in a procedure call is not allowed"); - fail = true; - } - } - - if (fail) { - operand->mode = Addressing_Invalid; - operand->expr = call; - return Expr_Stmt; - } - } - if (operand->mode == Addressing_Invalid) { + CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("procedure call"); for (Ast *arg : args) { if (arg->kind == Ast_FieldValue) { arg = arg->FieldValue.value; @@ -6817,6 +7074,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c if (operand->mode == Addressing_Type) { Type *t = operand->type; if (is_type_polymorphic_record(t)) { + CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("polymorphic type construction"); + if (!is_type_named(t)) { gbString s = expr_to_string(operand->expr); error(call, "Illegal use of an unnamed polymorphic record, %s", s); @@ -6842,6 +7101,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c operand->type = t_invalid; } } else { + CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("type conversion"); + operand->mode = Addressing_Invalid; isize arg_count = args.count; switch (arg_count) { @@ -6887,6 +7148,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } if (operand->mode == Addressing_Builtin) { + CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("builtin call"); + i32 id = operand->builtin_id; Entity *e = entity_of_node(operand->expr); if (e != nullptr && e->token.string == "expand_to_tuple") { @@ -6900,6 +7163,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c return builtin_procs[id].kind; } + CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN(operand->mode == Addressing_ProcGroup ? "procedure group call": "procedure call", true); + Entity *initial_entity = entity_of_node(operand->expr); if (initial_entity != nullptr && initial_entity->kind == Entity_Procedure) { @@ -6911,8 +7176,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } } - Type *proc_type = base_type(operand->type); if (operand->mode != Addressing_ProcGroup) { + Type *proc_type = base_type(operand->type); bool valid_type = (proc_type != nullptr) && is_type_proc(proc_type); bool valid_mode = is_operand_value(*operand); if (!valid_type || !valid_mode) { @@ -6930,7 +7195,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } } - CallArgumentData data = check_call_arguments(c, operand, proc_type, call, args); + CallArgumentData data = check_call_arguments(c, operand, call); Type *result_type = data.result_type; gb_zero_item(operand); operand->expr = call; @@ -6941,7 +7206,10 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c return Expr_Stmt; } - Type *pt = base_type(proc_type); + Type *pt = base_type(operand->type); + if (pt == nullptr) { + pt = t_invalid; + } if (pt == t_invalid) { if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) { pt = type_of_expr(operand->expr->CallExpr.proc); @@ -6986,7 +7254,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c if (decl->proc_lit) { ast_node(pl, ProcLit, decl->proc_lit); if (pl->inlining == ProcInlining_no_inline) { - error(call, "'inline' cannot be applied to a procedure that has be marked as 'no_inline'"); + error(call, "'#force_inline' cannot be applied to a procedure that has be marked as '#force_no_inline'"); } } } @@ -6999,9 +7267,6 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c operand->expr = call; { - if (proc_type == t_invalid) { - // gb_printf_err("%s\n", expr_to_string(operand->expr)); - } Type *type = nullptr; if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) { type = type_of_expr(operand->expr->CallExpr.proc); @@ -7019,8 +7284,6 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } } - // add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value); - return Expr_Expr; } @@ -9098,13 +9361,13 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast ExprKind kind = check_expr_base(c, &x, se->expr, nullptr); c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr; - if (x.mode == Addressing_Invalid || x.type == t_invalid) { + if (x.mode == Addressing_Invalid || (x.type == t_invalid && x.mode != Addressing_ProcGroup)) { o->mode = Addressing_Invalid; o->type = t_invalid; o->expr = node; return kind; } - if (!is_type_proc(x.type)) { + if (!is_type_proc(x.type) && x.mode != Addressing_ProcGroup) { gbString type_str = type_to_string(x.type); error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str); gb_string_free(type_str); @@ -9127,76 +9390,76 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast first_arg->state_flags |= StateFlag_SelectorCallExpr; } - Type *pt = base_type(x.type); - GB_ASSERT(pt->kind == Type_Proc); - Type *first_type = nullptr; - String first_arg_name = {}; - if (pt->Proc.param_count > 0) { - Entity *f = pt->Proc.params->Tuple.variables[0]; - first_type = f->type; - first_arg_name = f->token.string; - } - if (first_arg_name.len == 0) { - first_arg_name = str_lit("_"); - } + if (e->kind != Entity_ProcGroup) { + Type *pt = base_type(x.type); + GB_ASSERT_MSG(pt->kind == Type_Proc, "%.*s %.*s %s", LIT(e->token.string), LIT(entity_strings[e->kind]), type_to_string(x.type)); + Type *first_type = nullptr; + String first_arg_name = {}; + if (pt->Proc.param_count > 0) { + Entity *f = pt->Proc.params->Tuple.variables[0]; + first_type = f->type; + first_arg_name = f->token.string; + } + if (first_arg_name.len == 0) { + first_arg_name = str_lit("_"); + } - if (first_type == nullptr) { - error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter"); - o->mode = Addressing_Invalid; - o->type = t_invalid; - o->expr = node; - return Expr_Stmt; - } + if (first_type == nullptr) { + error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter"); + o->mode = Addressing_Invalid; + o->type = t_invalid; + o->expr = node; + return Expr_Stmt; + } - Operand y = {}; - y.mode = first_arg->tav.mode; - y.type = first_arg->tav.type; - y.value = first_arg->tav.value; + Operand y = {}; + y.mode = first_arg->tav.mode; + y.type = first_arg->tav.type; + y.value = first_arg->tav.value; - if (check_is_assignable_to(c, &y, first_type)) { - // Do nothing, it's valid - } else { - Operand z = y; - z.type = type_deref(y.type); - if (check_is_assignable_to(c, &z, first_type)) { - // NOTE(bill): AST GENERATION HACK! - Token op = {Token_Pointer}; - first_arg = ast_deref_expr(first_arg->file(), first_arg, op); - } else if (y.mode == Addressing_Variable) { - Operand w = y; - w.type = alloc_type_pointer(y.type); - if (check_is_assignable_to(c, &w, first_type)) { + if (check_is_assignable_to(c, &y, first_type)) { + // Do nothing, it's valid + } else { + Operand z = y; + z.type = type_deref(y.type); + if (check_is_assignable_to(c, &z, first_type)) { // NOTE(bill): AST GENERATION HACK! - Token op = {Token_And}; - first_arg = ast_unary_expr(first_arg->file(), op, first_arg); + Token op = {Token_Pointer}; + first_arg = ast_deref_expr(first_arg->file(), first_arg, op); + } else if (y.mode == Addressing_Variable) { + Operand w = y; + w.type = alloc_type_pointer(y.type); + if (check_is_assignable_to(c, &w, first_type)) { + // NOTE(bill): AST GENERATION HACK! + Token op = {Token_And}; + first_arg = ast_unary_expr(first_arg->file(), op, first_arg); + } } } - } - if (ce->args.count > 0) { - bool fail = false; - bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue); - for (Ast *arg : ce->args) { - bool mix = false; - if (first_is_field_value) { - mix = arg->kind != Ast_FieldValue; - } else { - mix = arg->kind == Ast_FieldValue; + if (ce->args.count > 0) { + bool fail = false; + bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue); + for (Ast *arg : ce->args) { + bool mix = false; + if (first_is_field_value) { + mix = arg->kind != Ast_FieldValue; + } else { + mix = arg->kind == Ast_FieldValue; + } + if (mix) { + fail = true; + break; + } } - if (mix) { - fail = true; - break; + if (!fail && first_is_field_value) { + Token op = {Token_Eq}; + AstFile *f = first_arg->file(); + first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op); } } - if (!fail && first_is_field_value) { - Token op = {Token_Eq}; - AstFile *f = first_arg->file(); - first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op); - } } - - auto modified_args = slice_make<Ast *>(heap_allocator(), ce->args.count+1); modified_args[0] = first_arg; slice_copy(&modified_args, ce->args, 1); |