From b2fdb69b4dd7f52f42414139a257b3800eb51a90 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sun, 11 Jun 2017 12:01:40 +0100 Subject: Named procedure calls --- src/check_expr.cpp | 317 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 242 insertions(+), 75 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 5043f9b38..d967ff504 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2,8 +2,7 @@ void check_expr (Checker *c, Operand *operand, AstNode * void check_multi_expr (Checker *c, Operand *operand, AstNode *expression); void check_expr_or_type (Checker *c, Operand *operand, AstNode *expression); ExprKind check_expr_base (Checker *c, Operand *operand, AstNode *expression, Type *type_hint); -Type * check_type_extra (Checker *c, AstNode *expression, Type *named_type); -Type * check_type (Checker *c, AstNode *expression); +Type * check_type (Checker *c, AstNode *expression, Type *named_type = NULL); void check_type_decl (Checker *c, Entity *e, AstNode *type_expr, Type *def); Entity * check_selector (Checker *c, Operand *operand, AstNode *node, Type *type_hint); void check_not_tuple (Checker *c, Operand *operand); @@ -22,11 +21,6 @@ bool check_representable_as_constant(Checker *c, ExactValue in_value, Type * Type * check_call_arguments (Checker *c, Operand *operand, Type *proc_type, AstNode *call); -gb_inline Type *check_type(Checker *c, AstNode *expression) { - return check_type_extra(c, expression, NULL); -} - - void error_operand_not_expression(Operand *o) { if (o->mode == Addressing_Type) { gbString err = expr_to_string(o->expr); @@ -326,7 +320,7 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n // TODO(bill): is this a good enough error message? // TODO(bill): Actually allow built in procedures to be passed around and thus be created on use error_node(operand->expr, - "Cannot assign builtin procedure `%s` in %.*s", + "Cannot assign built-in procedure `%s` in %.*s", expr_str, LIT(context_name)); } else { @@ -1605,8 +1599,8 @@ void check_map_type(Checker *c, Type *type, AstNode *node) { ast_node(mt, MapType, node); i64 count = check_array_or_map_count(c, mt->count, true); - Type *key = check_type_extra(c, mt->key, NULL); - Type *value = check_type_extra(c, mt->value, NULL); + Type *key = check_type(c, mt->key); + Type *value = check_type(c, mt->value); if (!is_type_valid_for_keys(key)) { if (is_type_boolean(key)) { @@ -1702,7 +1696,7 @@ void check_map_type(Checker *c, Type *type, AstNode *node) { // error_node(node, "`map` types are not yet implemented"); } -bool check_type_extra_internal(Checker *c, AstNode *e, Type **type, Type *named_type) { +bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type) { GB_ASSERT_NOT_NULL(type); if (e == NULL) { *type = t_invalid; @@ -1759,7 +1753,7 @@ bool check_type_extra_internal(Checker *c, AstNode *e, Type **type, Type *named_ case_end; case_ast_node(pe, ParenExpr, e); - *type = check_type_extra(c, pe->expr, named_type); + *type = check_type(c, pe->expr, named_type); return true; case_end; @@ -1794,7 +1788,7 @@ bool check_type_extra_internal(Checker *c, AstNode *e, Type **type, Type *named_ case_ast_node(at, ArrayType, e); if (at->count != NULL) { - Type *elem = check_type_extra(c, at->elem, NULL); + Type *elem = check_type(c, at->elem, NULL); i64 count = check_array_or_map_count(c, at->count, false); if (count < 0) { error_node(at->count, ".. can only be used in conjuction with compound literals"); @@ -1825,7 +1819,7 @@ bool check_type_extra_internal(Checker *c, AstNode *e, Type **type, Type *named_ case_end; case_ast_node(dat, DynamicArrayType, e); - Type *elem = check_type_extra(c, dat->elem, NULL); + Type *elem = check_type(c, dat->elem); i64 esz = type_size_of(c->allocator, elem); #if 0 if (esz == 0) { @@ -1934,9 +1928,9 @@ bool check_type_extra_internal(Checker *c, AstNode *e, Type **type, Type *named_ -Type *check_type_extra(Checker *c, AstNode *e, Type *named_type) { +Type *check_type(Checker *c, AstNode *e, Type *named_type) { Type *type = NULL; - bool ok = check_type_extra_internal(c, e, &type, named_type); + bool ok = check_type_internal(c, e, &type, named_type); if (!ok) { gbString err_str = expr_to_string(e); @@ -3533,6 +3527,14 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id } } + if (ce->args.count > 0) { + if (ce->args[0]->kind == AstNode_FieldValue) { + error_node(call, "`field = value` calling is not allowed on built-in procedures"); + return false; + } + } + + bool vari_expand = (ce->ellipsis.pos.line != 0); if (vari_expand && id != BuiltinProc_append) { error(ce->ellipsis, "Invalid use of `..` with built-in procedure `append`"); @@ -3556,7 +3558,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id switch (id) { default: - GB_PANIC("Implement builtin procedure: %.*s", LIT(builtin_procs[id].name)); + GB_PANIC("Implement built-in procedure: %.*s", LIT(builtin_procs[id].name)); break; case BuiltinProc_len: @@ -4707,6 +4709,10 @@ enum CallArgumentError { CallArgumentError_ArgumentCount, CallArgumentError_TooFewArguments, CallArgumentError_TooManyArguments, + CallArgumentError_InvalidFieldValue, + CallArgumentError_ParameterNotFound, + CallArgumentError_ParameterMissing, + CallArgumentError_DuplicateParameter, }; enum CallArgumentErrorMode { @@ -4714,8 +4720,59 @@ enum CallArgumentErrorMode { CallArgumentMode_ShowErrors, }; -CallArgumentError check_call_arguments_internal(Checker *c, AstNode *call, Type *proc_type, Operand *operands, isize operand_count, - CallArgumentErrorMode show_error_mode, i64 *score_) { + +struct ValidProcAndScore { + isize index; + i64 score; +}; + +int valid_proc_and_score_cmp(void const *a, void const *b) { + i64 si = (cast(ValidProcAndScore const *)a)->score; + i64 sj = (cast(ValidProcAndScore const *)b)->score; + return sj < si ? -1 : sj > si; +} + +bool check_unpack_arguments(Checker *c, isize lhs_count, Array *operands, Array rhs, bool allow_ok) { + bool optional_ok = false; + for_array(i, rhs) { + Operand o = {}; + check_multi_expr(c, &o, rhs[i]); + + if (o.type == NULL || o.type->kind != Type_Tuple) { + if (allow_ok && lhs_count == 2 && rhs.count == 1 && + (o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk)) { + Type *tuple = make_optional_ok_type(c->allocator, o.type); + add_type_and_value(&c->info, o.expr, o.mode, tuple, o.value); + + Operand val = o; + Operand ok = o; + val.mode = Addressing_Value; + ok.mode = Addressing_Value; + ok.type = t_bool; + array_add(operands, val); + array_add(operands, ok); + + optional_ok = true; + } else { + array_add(operands, o); + } + } else { + TypeTuple *tuple = &o.type->Tuple; + for (isize j = 0; j < tuple->variable_count; j++) { + o.type = tuple->variables[j]->type; + array_add(operands, o); + } + } + } + + return optional_ok; +} + +#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(Checker *c, AstNode *call, Type *proc_type, Array operands, CallArgumentErrorMode show_error_mode, i64 *score_) +typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType); + + +CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { ast_node(ce, CallExpr, call); isize param_count = 0; bool variadic = proc_type->Proc.variadic; @@ -4740,15 +4797,15 @@ CallArgumentError check_call_arguments_internal(Checker *c, AstNode *call, Type return CallArgumentError_NonVariadicExpand; } - if (operand_count == 0 && param_count == 0) { + if (operands.count == 0 && param_count == 0) { if (score_) *score_ = score; return CallArgumentError_None; } i32 error_code = 0; - if (operand_count < param_count) { + if (operands.count < param_count) { error_code = -1; - } else if (!variadic && operand_count > param_count) { + } else if (!variadic && operands.count > param_count) { error_code = +1; } if (error_code != 0) { @@ -4795,7 +4852,7 @@ CallArgumentError check_call_arguments_internal(Checker *c, AstNode *call, Type GB_ASSERT(is_type_slice(slice)); Type *elem = base_type(slice)->Slice.elem; Type *t = elem; - for (; operand_index < operand_count; operand_index++) { + for (; operand_index < operands.count; operand_index++) { Operand o = operands[operand_index]; if (vari_expand) { variadic_expand = true; @@ -4823,61 +4880,136 @@ CallArgumentError check_call_arguments_internal(Checker *c, AstNode *call, Type return err; } -struct ValidProcAndScore { - isize index; - i64 score; -}; +bool is_call_expr_field_value(AstNodeCallExpr *ce) { + GB_ASSERT(ce != NULL); -int valid_proc_and_score_cmp(void const *a, void const *b) { - i64 si = (cast(ValidProcAndScore const *)a)->score; - i64 sj = (cast(ValidProcAndScore const *)b)->score; - return sj < si ? -1 : sj > si; + if (ce->args.count == 0) { + return false; + } + return ce->args[0]->kind == AstNode_FieldValue; } -bool check_unpack_arguments(Checker *c, isize lhs_count, Array *operands, Array rhs, bool allow_ok) { - bool optional_ok = false; - for_array(i, rhs) { - Operand o = {}; - check_multi_expr(c, &o, rhs[i]); +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 (name == "_") { + continue; + } + if (name == parameter_name) { + return i; + } + } + return -1; +} - if (o.type == NULL || o.type->kind != Type_Tuple) { - if (allow_ok && lhs_count == 2 && rhs.count == 1 && - (o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk)) { - Type *tuple = make_optional_ok_type(c->allocator, o.type); - add_type_and_value(&c->info, o.expr, o.mode, tuple, o.value); +CALL_ARGUMENT_CHECKER(check_named_call_arguments) { + ast_node(ce, CallExpr, call); + GB_ASSERT(is_type_proc(proc_type)); + TypeProc *pt = &base_type(proc_type)->Proc; - Operand val = o; - Operand ok = o; - val.mode = Addressing_Value; - ok.mode = Addressing_Value; - ok.type = t_bool; - array_add(operands, val); - array_add(operands, ok); + i64 score = 0; + bool show_error = show_error_mode == CallArgumentMode_ShowErrors; + CallArgumentError err = CallArgumentError_None; - optional_ok = true; - } else { - array_add(operands, o); + isize param_count = pt->param_count; + bool *params_visited = gb_alloc_array(c->allocator, bool, param_count); + + for_array(i, ce->args) { + AstNode *arg = ce->args[i]; + ast_node(fv, FieldValue, arg); + if (fv->field->kind != AstNode_Ident) { + if (show_error) { + gbString expr_str = expr_to_string(fv->field); + error_node(arg, "Invalid parameter name `%s` in procedure call", expr_str); + gb_string_free(expr_str); } - } else { - TypeTuple *tuple = &o.type->Tuple; - for (isize j = 0; j < tuple->variable_count; j++) { - o.type = tuple->variables[j]->type; - array_add(operands, o); + err = CallArgumentError_InvalidFieldValue; + continue; + } + String name = fv->field->Ident.string; + isize index = lookup_procedure_parameter(pt, name); + if (index < 0) { + if (show_error) { + error_node(arg, "No parameter named `%.*s` for this procedure type", LIT(name)); } + err = CallArgumentError_ParameterNotFound; + continue; } + if (params_visited[index]) { + if (show_error) { + error_node(arg, "Duplicate parameter `%.*s` in procedure call", LIT(name)); + } + err = CallArgumentError_DuplicateParameter; + continue; + } + + params_visited[index] = true; + Operand *o = &operands[i]; + + Type *param_type = pt->params->Tuple.variables[index]->type; + + i64 s = 0; + if (!check_is_assignable_to_with_score(c, o, param_type, &s)) { + if (show_error) { + check_assignment(c, o, param_type, str_lit("procedure argument")); + } + err = CallArgumentError_WrongTypes; + } + score += s; } - return optional_ok; + +#if 1 + isize param_count_to_check = param_count; + if (pt->variadic) { + param_count_to_check--; + } + for (isize i = 0; i < param_count_to_check; i++) { + if (!params_visited[i]) { + if (show_error) { + Entity *e = pt->params->Tuple.variables[i]; + gbString str = type_to_string(e->type); + error_node(call, "Parameter `%.*s` of type `%s` is missing in procedure call", + LIT(e->token.string), str); + gb_string_free(str); + } + err = CallArgumentError_ParameterMissing; + } + } +#endif + + if (score_) *score_ = score; + + return err; } -Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode *call) { - GB_ASSERT(call->kind == AstNode_CallExpr); +Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode *call) { ast_node(ce, CallExpr, call); - Array operands; - array_init(&operands, heap_allocator(), 2*ce->args.count); - check_unpack_arguments(c, -1, &operands, ce->args, false); + CallArgumentCheckerType *call_checker = NULL; + Array operands = {}; + defer (array_free(&operands)); + + if (is_call_expr_field_value(ce)) { + call_checker = check_named_call_arguments; + + array_init_count(&operands, heap_allocator(), ce->args.count); + for_array(i, ce->args) { + AstNode *arg = ce->args[i]; + ast_node(fv, FieldValue, arg); + check_expr(c, &operands[i], fv->value); + } + } else { + call_checker = check_call_arguments_internal; + + array_init(&operands, heap_allocator(), 2*ce->args.count); + check_unpack_arguments(c, -1, &operands, ce->args, false); + } + + GB_ASSERT(call_checker != NULL); if (operand->mode == Addressing_Overload) { GB_ASSERT(operand->overload_entities != NULL && @@ -4888,6 +5020,9 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod ValidProcAndScore *valids = gb_alloc_array(heap_allocator(), ValidProcAndScore, overload_count); isize valid_count = 0; + defer (gb_free(heap_allocator(), procs)); + defer (gb_free(heap_allocator(), valids)); + String name = procs[0]->token.string; for (isize i = 0; i < overload_count; i++) { @@ -4903,7 +5038,7 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod Type *proc_type = base_type(p->type); if (proc_type != NULL && is_type_proc(proc_type)) { i64 score = 0; - CallArgumentError err = check_call_arguments_internal(c, call, proc_type, operands.data, operands.count, CallArgumentMode_NoErrors, &score); + CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_NoErrors, &score); if (err == CallArgumentError_None) { valids[valid_count].index = i; valids[valid_count].score = score; @@ -4948,16 +5083,14 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod add_entity_use(c, expr, e); proc_type = e->type; i64 score = 0; - CallArgumentError err = check_call_arguments_internal(c, call, proc_type, operands.data, operands.count, CallArgumentMode_ShowErrors, &score); + CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_ShowErrors, &score); } - - gb_free(heap_allocator(), valids); - gb_free(heap_allocator(), procs); } else { i64 score = 0; - CallArgumentError err = check_call_arguments_internal(c, call, proc_type, operands.data, operands.count, CallArgumentMode_ShowErrors, &score); - array_free(&operands); + CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_ShowErrors, &score); } + + return proc_type; } @@ -4990,9 +5123,37 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) { ast_node(ce, CallExpr, call); check_expr_or_type(c, operand, ce->proc); + if (ce->args.count > 0) { + bool fail = false; + bool first_is_field_value = (ce->args[0]->kind == AstNode_FieldValue); + for_array(i, ce->args) { + AstNode *arg = ce->args[i]; + bool mix = false; + if (first_is_field_value) { + mix = arg->kind != AstNode_FieldValue; + } else { + mix = arg->kind == AstNode_FieldValue; + } + if (mix) { + error_node(arg, "Mixture of `field = value` and value elements in a procedure all is not allowed"); + fail = true; + } + } + + if (fail) { + operand->mode = Addressing_Invalid; + operand->expr = call; + return Expr_Stmt; + } + } + if (operand->mode == Addressing_Invalid) { for_array(i, ce->args) { - check_expr_base(c, operand, ce->args[i], NULL); + AstNode *arg = ce->args[i]; + if (arg->kind == AstNode_FieldValue) { + arg = arg->FieldValue.value; + } + check_expr_base(c, operand, arg, NULL); } operand->mode = Addressing_Invalid; operand->expr = call; @@ -5005,14 +5166,20 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) { operand->mode = Addressing_Invalid; isize arg_count = ce->args.count; switch (arg_count) { - case 0: error_node(call, "Missing argument in convertion to `%s`", str); break; - default: error_node(call, "Too many arguments in convertion to `%s`", str); break; - case 1: - check_expr(c, operand, ce->args[0]); + case 0: error_node(call, "Missing argument in conversion to `%s`", str); break; + default: error_node(call, "Too many arguments in conversion to `%s`", str); break; + case 1: { + AstNode *arg = ce->args[0]; + if (arg->kind == AstNode_FieldValue) { + error_node(call, "`field = value` cannot be used in a type conversion"); + arg = arg->FieldValue.value; + // NOTE(bill): Carry on the cast regardless + } + check_expr(c, operand, arg); if (operand->mode != Addressing_Invalid) { check_cast(c, operand, t); } - break; + } break; } gb_string_free(str); -- cgit v1.2.3