aboutsummaryrefslogtreecommitdiff
path: root/src/check_expr.cpp
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2023-06-23 12:11:46 +0100
committerGitHub <noreply@github.com>2023-06-23 12:11:46 +0100
commit9841b11a5423e2cba67c19fbd06e28732d36109c (patch)
tree7c067cbc1501c4a044a80944ca282dd7da974074 /src/check_expr.cpp
parent5a6d5374d780e726be82f3576b4f647d9096d012 (diff)
parentc48057081e451c81524c7727ec3ccf434a45726f (diff)
Merge pull request #2597 from odin-lang/ordered-named-arguments
Allowing for Positional and Named Arguments in Procedure Calls
Diffstat (limited to 'src/check_expr.cpp')
-rw-r--r--src/check_expr.cpp1765
1 files changed, 939 insertions, 826 deletions
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 830b5315d..83e44b39f 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);
}
@@ -677,6 +674,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 +703,49 @@ 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) {
+ i64 score = -1;
+ switch (src->Basic.kind) {
+ case Basic_UntypedRune:
+ if (is_type_integer(dst) || is_type_rune(dst)) {
+ score = 1;
}
- return 1;
+ break;
+ case Basic_UntypedInteger:
+ if (is_type_integer(dst) || is_type_rune(dst)) {
+ score = 1;
+ }
+ break;
+ case Basic_UntypedString:
+ if (is_type_string(dst)) {
+ score = 1;
+ }
+ break;
+ case Basic_UntypedFloat:
+ if (is_type_float(dst)) {
+ score = 1;
+ }
+ break;
+ case Basic_UntypedComplex:
+ if (is_type_complex(dst)) {
+ score = 1;
+ }
+ if (is_type_quaternion(dst)) {
+ score = 2;
+ }
+ break;
+ case Basic_UntypedQuaternion:
+ if (is_type_quaternion(dst)) {
+ score = 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(dst)) {
+ score += 1;
}
- return 1;
}
- return -1;
+ return score;
}
}
}
@@ -5150,14 +5178,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 +5198,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 +5214,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 +5223,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 +5239,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 +5339,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 &parameter_name) {
+ isize param_count = pt->param_count;
+ for (isize i = 0; i < param_count; i++) {
+ Entity *e = pt->params->Tuple.variables[i];
+ String name = e->token.string;
+ if (is_blank_ident(name)) {
+ continue;
+ }
+ if (name == parameter_name) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+gb_internal isize lookup_procedure_parameter(Type *type, String const &parameter_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 +5379,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 +5398,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;
+ 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));
}
-
- 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);
- }
+ 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;
}
- }
- }
- 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, {});
+ visited[param_index] = true;
+ ordered_operands[param_index] = operand;
}
}
- 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;
-}
+ isize dummy_argument_count = 0;
+ bool actually_variadic = false;
-gb_internal isize lookup_procedure_parameter(TypeProc *pt, String parameter_name) {
- isize param_count = pt->param_count;
- for (isize i = 0; i < param_count; i++) {
- Entity *e = pt->params->Tuple.variables[i];
- String name = e->token.string;
- if (is_blank_ident(name)) {
- continue;
- }
- if (name == parameter_name) {
- return i;
- }
- }
- return -1;
-}
-
-gb_internal CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
- ast_node(ce, CallExpr, call);
- GB_ASSERT(is_type_proc(proc_type));
- proc_type = base_type(proc_type);
- TypeProc *pt = &proc_type->Proc;
-
- 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));
- }
- err = CallArgumentError_ParameterNotFound;
- continue;
- }
- if (visited[index]) {
- if (show_error) {
- error(arg, "Duplicate parameter '%.*s' in procedure call", 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_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 +5534,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 +5561,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 +5833,658 @@ 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, &param_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, &param_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;
+ 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));
- Entity **lhs = nullptr;
- isize lhs_count = -1;
+ if (procs.count == 1) {
+ Entity *e = procs[0];
- {
- // 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;
- }
+ 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)
+ bool proc_arg_count_all_equal = true;
+ isize proc_arg_count = -1;
+ for (Entity *p : procs) {
+ Type *pt = base_type(p->type);
+ if (pt != nullptr && is_type_proc(pt)) {
+ if (proc_arg_count < 0) {
+ proc_arg_count = pt->Proc.param_count;
+ } else {
+ if (proc_arg_count != pt->Proc.param_count) {
+ proc_arg_count_all_equal = false;
+ break;
}
}
}
+ }
+ if (proc_arg_count >= 0 && proc_arg_count_all_equal) {
+ lhs_count = proc_arg_count;
+ if (lhs_count > 0) {
+ lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count);
+ for (isize param_index = 0; param_index < lhs_count; param_index++) {
+ Entity *e = nullptr;
+ for (Entity *p : procs) {
+ Type *pt = base_type(p->type);
+ if (!(pt != nullptr && is_type_proc(pt))) {
+ continue;
+ }
-
- if (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;
}
+ 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;
- auto valids = array_make<ValidIndexAndScore>(heap_allocator(), 0, procs.count);
- defer (array_free(&valids));
+ Type *type_hint = nullptr;
- 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);
+ for (isize lhs_idx = 0; lhs_idx < lhs_count; lhs_idx++) {
+ Entity *e = lhs[lhs_idx];
+ if (e != nullptr && e->token.string == key &&
+ !is_type_polymorphic(e->type)) {
+ type_hint = e->type;
+ break;
+ }
}
+ Operand o = {};
+ check_expr_with_type_hint(c, &o, value, type_hint);
+ array_add(&named_operands, o);
+ }
+ gb_free(heap_allocator(), lhs);
- gbString expr_name = expr_to_string(operand->expr);
- defer (gb_string_free(expr_name));
+ auto valids = array_make<ValidIndexAndScore>(heap_allocator(), 0, procs.count);
+ defer (array_free(&valids));
- 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;
+ 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);
+ }
- ctx.no_polymorphic_errors = true;
- ctx.allow_polymorphic_types = is_type_polymorphic(pt);
- ctx.hide_polymorphic_errors = true;
- err = call_checker(&ctx, call, pt, p, operands, CallArgumentMode_NoErrors, &data);
- if (err != CallArgumentError_None) {
- continue;
- }
- isize index = i;
+ gbString expr_name = expr_to_string(operand->expr);
+ defer (gb_string_free(expr_name));
- 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;
+ 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;
- GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
- if (!evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, false)) {
- continue;
- }
+ ctx.no_polymorphic_errors = true;
+ ctx.allow_polymorphic_types = is_type_polymorphic(pt);
+ ctx.hide_polymorphic_errors = true;
- array_add(&proc_entities, data.gen_entity);
- index = proc_entities.count-1;
- }
+ 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.index = index;
- item.score = data.score;
- array_add(&valids, item);
+ if (data.gen_entity != nullptr) {
+ array_add(&proc_entities, data.gen_entity);
+ index = proc_entities.count-1;
}
+
+ ValidIndexAndScore item = {};
+ item.index = index;
+ item.score = data.score;
+ array_add(&valids, item);
}
+ }
- if (valids.count > 1) {
- gb_sort_array(valids.data, valids.count, valid_index_and_score_cmp);
- i64 best_score = valids[0].score;
- Entity *best_entity = proc_entities[valids[0].index];
- GB_ASSERT(best_entity != nullptr);
- for (isize i = 1; i < valids.count; i++) {
- if (best_score > valids[i].score) {
- valids.count = i;
- break;
- }
- if (best_entity == proc_entities[valids[i].index]) {
- valids.count = i;
- break;
- }
+ if (valids.count > 1) {
+ gb_sort_array(valids.data, valids.count, valid_index_and_score_cmp);
+ i64 best_score = valids[0].score;
+ Entity *best_entity = proc_entities[valids[0].index];
+ GB_ASSERT(best_entity != nullptr);
+ for (isize i = 1; i < valids.count; i++) {
+ if (best_score > valids[i].score) {
+ valids.count = i;
+ break;
+ }
+ if (best_entity == proc_entities[valids[i].index]) {
+ valids.count = i;
+ break;
}
}
+ }
+ auto print_argument_types = [&]() {
+ error_line("\tGiven argument types: (");
+ isize i = 0;
+ for (Operand const &o : positional_operands) {
+ if (i++ > 0) error_line(", ");
+ gbString type = type_to_string(o.type);
+ defer (gb_string_free(type));
+ error_line("%s", type);
+ }
+ for (Operand const &o : named_operands) {
+ if (i++ > 0) error_line(", ");
- if (valids.count == 0) {
- begin_error_block();
- defer (end_error_block());
+ gbString type = type_to_string(o.type);
+ defer (gb_string_free(type));
- 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");
- }
+ if (i < ce->split_args->named.count) {
+ Ast *named_field = ce->split_args->named[i];
+ ast_node(fv, FieldValue, named_field);
- 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;
+ gbString field = expr_to_string(fv->field);
+ defer (gb_string_free(field));
- char const *sep = "::";
- if (proc->kind == Entity_Variable) {
- sep = ":=";
- }
- error_line("\t%.*s%.*s%.*s %s %s at %s\n", LIT(prefix), LIT(prefix_sep), LIT(name), sep, pt, token_pos_to_string(pos));
- }
- if (procs.count > 0) {
- error_line("\n");
+ error_line("%s = %s", field, type);
+ } else {
+ error_line("%s", type);
}
+ }
+ error_line(")\n");
+ };
- result_type = t_invalid;
- } else if (valids.count > 1) {
- begin_error_block();
- defer (end_error_block());
+ if (valids.count == 0) {
+ begin_error_block();
+ defer (end_error_block());
- 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", 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(operand->expr, "No procedures or ambiguous call for procedure group '%s' that match with the given arguments", expr_name);
+ if (positional_operands.count == 0 && named_operands.count == 0) {
+ error_line("\tNo given arguments\n");
+ } else {
+ print_argument_types();
+ }
- if (j != pl->where_clauses.count-1) {
- error_line(",");
- }
- }
- error_line("\n\t");
- }
- }
- error_line("at %s\n", token_pos_to_string(pos));
+ 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);
}
- 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;
+ String prefix = {};
+ String prefix_sep = {};
+ if (proc->pkg) {
+ prefix = proc->pkg->name;
+ prefix_sep = str_lit(".");
}
+ String name = proc->token.string;
- Entity *e = proc_entities[valids[0].index];
- GB_ASSERT(e != nullptr);
-
- 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);
+ char const *sep = "::";
+ if (proc->kind == Entity_Variable) {
+ sep = ":=";
}
+ error_line("\t%.*s%.*s%.*s %s %s at %s\n", LIT(prefix), LIT(prefix_sep), LIT(name), sep, pt, token_pos_to_string(pos));
+ }
+ if (procs.count > 0) {
+ error_line("\n");
+ }
- 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;
+ data.result_type = t_invalid;
+ } else if (valids.count > 1) {
+ begin_error_block();
+ defer (end_error_block());
+
+ error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name);
+ print_argument_types();
+
+ for (isize i = 0; i < valids.count; i++) {
+ Entity *proc = proc_entities[valids[i].index];
+ GB_ASSERT(proc != nullptr);
+ TokenPos pos = proc->token.pos;
+ Type *t = base_type(proc->type); GB_ASSERT(t->kind == Type_Proc);
+ gbString pt = nullptr;
+ defer (gb_string_free(pt));
+ if (t->Proc.node != nullptr) {
+ pt = expr_to_string(t->Proc.node);
+ } else {
+ pt = type_to_string(t);
+ }
+ String name = proc->token.string;
+ char const *sep = "::";
+ if (proc->kind == Entity_Variable) {
+ sep = ":=";
+ }
+ error_line("\t%.*s %s %s ", LIT(name), sep, pt);
+ if (proc->decl_info->proc_lit != nullptr) {
+ GB_ASSERT(proc->decl_info->proc_lit->kind == Ast_ProcLit);
+ auto *pl = &proc->decl_info->proc_lit->ProcLit;
+ if (pl->where_token.kind != Token_Invalid) {
+ error_line("\n\t\twhere ");
+ for_array(j, pl->where_clauses) {
+ Ast *clause = pl->where_clauses[j];
+ if (j != 0) {
+ error_line("\t\t ");
+ }
+ gbString str = expr_to_string(clause);
+ error_line("%s", str);
+ gb_string_free(str);
- if (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;
@@ -6740,6 +6829,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 +6908,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 +6924,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 +6951,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 +6998,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 +7013,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 +7026,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 +7045,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 +7056,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 +7104,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 +7117,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 +7134,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;
}