From: Brendan Hansen Date: Wed, 8 Sep 2021 23:37:36 +0000 (-0500) Subject: overloaded procedures are more powerful; bugfixes X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=cdb2adfe39bdf78e471bb4683686f17f2db87b75;p=onyx.git overloaded procedures are more powerful; bugfixes --- diff --git a/bin/onyx b/bin/onyx index 87873810..89f94976 100755 Binary files a/bin/onyx and b/bin/onyx differ diff --git a/include/astnodes.h b/include/astnodes.h index 9aa31d08..c11c782f 100644 --- a/include/astnodes.h +++ b/include/astnodes.h @@ -3,6 +3,7 @@ #include "lex.h" #include "types.h" +#include "errors.h" #define AST_NODES \ NODE(Node) \ @@ -507,6 +508,9 @@ typedef struct Arguments Arguments; struct Arguments { bh_arr(AstTyped *) values; bh_arr(AstNamedValue *) named_values; + + // How many arguments were not baked. + i32 used_argument_count; }; @@ -1364,6 +1368,9 @@ void arguments_copy(Arguments* dest, Arguments* src); void arguments_clone(Arguments* dest, Arguments* src); void arguments_deep_clone(bh_allocator a, Arguments* dest, Arguments* src); void arguments_remove_baked(Arguments* args); +b32 check_arguments_against_type(Arguments* args, TypeFunction* func_type, VarArgKind* va_kind, + OnyxToken* location, char* func_name, struct OnyxError* error); +i32 function_get_minimum_argument_count(TypeFunction* type, Arguments* args); // GROSS: Using void* to avoid having to cast everything. const char* node_get_type_name(void* node); diff --git a/include/errors.h b/include/errors.h index 41aebdde..6d85fe6b 100644 --- a/include/errors.h +++ b/include/errors.h @@ -24,7 +24,9 @@ typedef struct OnyxErrors { extern OnyxErrors msgs; void onyx_errors_init(bh_arr(bh_file_contents)* files); +void onyx_submit_error(OnyxError error); void onyx_report_error(OnyxFilePos pos, char * format, ...); +void onyx_submit_warning(OnyxError error); void onyx_report_warning(OnyxFilePos pos, char* format, ...); void onyx_errors_print(); b32 onyx_has_errors(); diff --git a/src/astnodes.c b/src/astnodes.c index b8ae2c6c..109ddf3b 100644 --- a/src/astnodes.c +++ b/src/astnodes.c @@ -969,6 +969,8 @@ void arguments_initialize(Arguments* args) { args->values[i] = NULL; args->named_values[i] = NULL; } + + args->used_argument_count = -1; } void arguments_ensure_length(Arguments* args, u32 count) { @@ -983,20 +985,24 @@ void arguments_ensure_length(Arguments* args, u32 count) { } void arguments_copy(Arguments* dest, Arguments* src) { + dest->used_argument_count = -1; dest->named_values = src->named_values; - arguments_ensure_length(dest, bh_arr_length(src->values)); + bh_arr_grow(dest->values, (u32) bh_arr_length(src->values)); + bh_arr_set_length(dest->values, (u32) bh_arr_length(src->values)); bh_arr_each(AstTyped*, arg, dest->values) *arg = NULL; fori (i, 0, bh_arr_length(src->values)) dest->values[i] = src->values[i]; } // In clone, the named_values are not copied. This is used in find_matching_overload_by_arguments since it doesn't need them to be copied. void arguments_clone(Arguments* dest, Arguments* src) { + dest->used_argument_count = -1; dest->named_values = src->named_values; dest->values = bh_arr_copy(global_heap_allocator, src->values); } void arguments_deep_clone(bh_allocator a, Arguments* dest, Arguments* src) { + dest->used_argument_count = -1; dest->values = NULL; dest->named_values = NULL; diff --git a/src/checker.c b/src/checker.c index becae7ad..3340b2e6 100644 --- a/src/checker.c +++ b/src/checker.c @@ -419,22 +419,6 @@ CheckStatus check_argument(AstArgument** parg) { return Check_Success; } -static i32 non_baked_argument_count(Arguments* args) { - i32 count = 0; - - bh_arr_each(AstTyped *, actual, args->values) { - assert((*actual)->kind == Ast_Kind_Argument); - if (!((AstArgument *) (*actual))->is_baked) count++; - } - - bh_arr_each(AstNamedValue *, named_value, args->named_values) { - assert((*named_value)->value->kind == Ast_Kind_Argument); - if (!((AstArgument *) (*named_value)->value)->is_baked) count++; - } - - return count; -} - static CheckStatus check_resolve_callee(AstCall* call, AstTyped** effective_callee) { if (call->kind == Ast_Kind_Intrinsic_Call) return Check_Success; @@ -505,12 +489,6 @@ static CheckStatus check_resolve_callee(AstCall* call, AstTyped** effective_call return Check_Success; } -typedef enum ArgState { - AS_Expecting_Exact, - AS_Expecting_Typed_VA, - AS_Expecting_Untyped_VA, -} ArgState; - CheckStatus check_call(AstCall** pcall) { // All the things that need to be done when checking a call node. // 1. Ensure the callee is not a symbol @@ -540,13 +518,7 @@ CheckStatus check_call(AstCall** pcall) { AstFunction* callee=NULL; CHECK(resolve_callee, call, (AstTyped **) &callee); - // CLEANUP maybe make function_get_expected_arguments? - i32 non_vararg_param_count = (i32) callee->type->Function.param_count; - if (non_vararg_param_count > 0 && - callee->type->Function.params[callee->type->Function.param_count - 1] == builtin_vararg_type_type) - non_vararg_param_count--; - - i32 arg_count = bh_max(non_vararg_param_count, non_baked_argument_count(&call->args)); + i32 arg_count = function_get_minimum_argument_count(&callee->type->Function, &call->args); arguments_ensure_length(&call->args, arg_count); char* err_msg = NULL; @@ -622,103 +594,14 @@ CheckStatus check_call(AstCall** pcall) { YIELD(call->token->pos, "Waiting for auto-return type to be solved."); } - Type **formal_params = callee->type->Function.params; - - Type* variadic_type = NULL; - AstParam* variadic_param = NULL; - - // SPEED CLEANUP: Caching the any type here. - Type* any_type = type_build_from_ast(context.ast_alloc, builtin_any_type); - - ArgState arg_state = AS_Expecting_Exact; - u32 arg_pos = 0; - while (1) { - switch (arg_state) { - case AS_Expecting_Exact: { - if (arg_pos >= callee->type->Function.param_count) goto type_checking_done; - - if (formal_params[arg_pos]->kind == Type_Kind_VarArgs) { - variadic_type = formal_params[arg_pos]->VarArgs.elem; - variadic_param = &callee->params[arg_pos]; - arg_state = AS_Expecting_Typed_VA; - continue; - } - - if ((i16) arg_pos == callee->type->Function.vararg_arg_pos) { - arg_state = AS_Expecting_Untyped_VA; - continue; - } - - if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done; - if (!unify_node_and_type(&arg_arr[arg_pos]->value, formal_params[arg_pos])) { - ERROR_(arg_arr[arg_pos]->token->pos, - "The procedure '%s' expects a value of type '%s' for %d%s parameter, got '%s'.", - get_function_name(callee), - type_get_name(formal_params[arg_pos]), - arg_pos + 1, - bh_num_suffix(arg_pos + 1), - node_get_type_name(arg_arr[arg_pos]->value)); - } - - arg_arr[arg_pos]->va_kind = VA_Kind_Not_VA; - break; - } - - case AS_Expecting_Typed_VA: { - if (variadic_type->id == any_type->id) call->va_kind = VA_Kind_Any; - if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done; - - if (variadic_type->id == any_type->id) { - resolve_expression_type(arg_arr[arg_pos]->value); - arg_arr[arg_pos]->va_kind = VA_Kind_Any; - break; - } - - call->va_kind = VA_Kind_Typed; - - if (!unify_node_and_type(&arg_arr[arg_pos]->value, variadic_type)) { - onyx_report_error(arg_arr[arg_pos]->token->pos, - "The procedure '%s' expects a value of type '%s' for the variadic parameter, '%b', got '%s'.", - get_function_name(callee), - type_get_name(variadic_type), - variadic_param->local->token->text, - variadic_param->local->token->length, - node_get_type_name(arg_arr[arg_pos]->value)); - return Check_Error; - } - - arg_arr[arg_pos]->va_kind = VA_Kind_Typed; - break; - } - - case AS_Expecting_Untyped_VA: { - call->va_kind = VA_Kind_Untyped; - - if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done; - - resolve_expression_type(arg_arr[arg_pos]->value); - if (arg_arr[arg_pos]->value->type == NULL) { - ERROR(arg_arr[arg_pos]->token->pos, "Unable to resolve type for argument."); - } - - arg_arr[arg_pos]->va_kind = VA_Kind_Untyped; - break; - } - } - - arg_pos++; + OnyxError error; + if (!check_arguments_against_type(&call->args, &callee->type->Function, &call->va_kind, + call->token, get_function_name(callee), &error)) { + onyx_submit_error(error); + return Check_Error; } -type_checking_done: - - call->flags |= Ast_Flag_Has_Been_Checked; - - if (arg_pos < callee->type->Function.needed_param_count) - ERROR(call->token->pos, "Too few arguments to function call."); - - if (arg_pos < (u32) arg_count) - ERROR(call->token->pos, "Too many arguments to function call."); - + call->flags |= Ast_Flag_Has_Been_Checked; callee->flags |= Ast_Flag_Function_Used; if (call->kind == Ast_Kind_Call && call->callee->kind == Ast_Kind_Macro) { @@ -741,8 +624,13 @@ static AstCall* binaryop_try_operator_overload(AstBinaryOp* binop) { Arguments args = ((Arguments) { NULL, NULL }); bh_arr_new(global_heap_allocator, args.values, 2); - bh_arr_push(args.values, binop->left); - bh_arr_push(args.values, binop->right); + bh_arr_push(args.values, (AstTyped *) make_argument(context.ast_alloc, binop->left)); + bh_arr_push(args.values, (AstTyped *) make_argument(context.ast_alloc, binop->right)); + + u32 current_checking_level_store = current_checking_level; + check_argument((AstArgument **) &args.values[0]); + check_argument((AstArgument **) &args.values[1]); + current_checking_level = current_checking_level_store; if (binop_is_assignment(binop->operation)) { args.values[0] = (AstTyped *) make_address_of(context.ast_alloc, binop->left); @@ -755,6 +643,11 @@ static AstCall* binaryop_try_operator_overload(AstBinaryOp* binop) { if (cs == Check_Error) { return NULL; } + + args.values[0] = (AstTyped *) make_argument(context.ast_alloc, args.values[0]); + current_checking_level_store = current_checking_level; + check_argument((AstArgument **) &args.values[0]); + current_checking_level = current_checking_level_store; } b32 should_yield = 0; @@ -774,9 +667,6 @@ static AstCall* binaryop_try_operator_overload(AstBinaryOp* binop) { implicit_call->callee = overload; implicit_call->va_kind = VA_Kind_Not_VA; - bh_arr_each(AstTyped *, arg, args.values) - *arg = (AstTyped *) make_argument(context.ast_alloc, *arg); - implicit_call->args = args; return implicit_call; } diff --git a/src/errors.c b/src/errors.c index 0a074521..0b9f16d3 100644 --- a/src/errors.c +++ b/src/errors.c @@ -79,6 +79,10 @@ void onyx_clear_errors() { bh_arr_set_length(errors.errors, 0); } +void onyx_submit_error(OnyxError error) { + bh_arr_push(errors.errors, error); +} + void onyx_report_error(OnyxFilePos pos, char * format, ...) { va_list vargs; @@ -94,6 +98,19 @@ void onyx_report_error(OnyxFilePos pos, char * format, ...) { bh_arr_push(errors.errors, err); } +void onyx_submit_warning(OnyxError error) { + bh_file_contents file_contents = { 0 }; + bh_arr_each(bh_file_contents, fc, *errors.file_contents) { + if (!strcmp(fc->filename, error.pos.filename)) { + file_contents = *fc; + break; + } + } + + print_detailed_message(&error, &file_contents); +} + +// This definitely doesn't do what I thought it did? void onyx_report_warning(OnyxFilePos pos, char* format, ...) { va_list vargs; va_start(vargs, format); diff --git a/src/utils.c b/src/utils.c index 017ed8c5..7c63d9c9 100644 --- a/src/utils.c +++ b/src/utils.c @@ -6,6 +6,7 @@ #include "errors.h" #include "parser.h" #include "astnodes.h" +#include "errors.h" bh_scratch global_scratch; bh_allocator global_scratch_allocator; @@ -366,36 +367,14 @@ AstTyped* find_matching_overload_by_arguments(bh_arr(OverloadOption) overloads, } assert(overload->type->kind == Type_Kind_Function); + arguments_remove_baked(&args); + arguments_ensure_length(&args, function_get_minimum_argument_count(&overload->type->Function, &args)); + // NOTE: If the arguments cannot be placed successfully in the parameters list if (!fill_in_arguments(&args, (AstNode *) overload, NULL)) continue; - - TypeFunction* ol_type = &overload->type->Function; - if (bh_arr_length(args.values) < (i32) ol_type->needed_param_count) continue; - - b32 all_arguments_work = 1; - fori (i, 0, bh_arr_length(args.values)) { - if (i >= ol_type->param_count) { - all_arguments_work = 0; - break; - } - - Type* type_to_match = ol_type->params[i]; - AstTyped** value = &args.values[i]; - - if (type_to_match->kind == Type_Kind_VarArgs) type_to_match = type_to_match->VarArgs.elem; - if ((*value)->kind == Ast_Kind_Argument) { - // :ArgumentResolvingIsComplicated - if (((AstArgument *) (*value))->is_baked) continue; - value = &((AstArgument *) *value)->value; - } - - if (!unify_node_and_type_(value, type_to_match, 0)) { - all_arguments_work = 0; - break; - } - } - - if (all_arguments_work) { + + VarArgKind va_kind; + if (check_arguments_against_type(&args, &overload->type->Function, &va_kind, NULL, NULL, NULL)) { matched_overload = node; break; } @@ -692,6 +671,33 @@ static i32 maximum_argument_count(AstNode* provider) { return 0x7fffffff; } +static i32 non_baked_argument_count(Arguments* args) { + if (args->used_argument_count >= 0) return args->used_argument_count; + + i32 count = 0; + + bh_arr_each(AstTyped *, actual, args->values) { + if ((*actual)->kind != Ast_Kind_Argument) count++; + else if (!((AstArgument *) (*actual))->is_baked) count++; + } + + bh_arr_each(AstNamedValue *, named_value, args->named_values) { + if ((*named_value)->value->kind != Ast_Kind_Argument) count++; + else if (!((AstArgument *) (*named_value)->value)->is_baked) count++; + } + + args->used_argument_count = count; + return count; +} + +i32 function_get_minimum_argument_count(TypeFunction* type, Arguments* args) { + i32 non_vararg_param_count = (i32) type->param_count; + if (non_vararg_param_count > 0 && type->params[type->param_count - 1] == builtin_vararg_type_type) + non_vararg_param_count--; + + return bh_max(non_vararg_param_count, non_baked_argument_count(args)); +} + // NOTE: The values array can be partially filled out, and is the resulting array. // Returns if all the values were filled in. b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg) { @@ -735,6 +741,7 @@ b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg) { // assert(idx < bh_arr_length(args->values)); if (idx >= bh_arr_length(args->values)) { + if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "Error placing value with name '%s' at index '%d'.", named_value->token->text, idx); token_toggle_end(named_value->token); return 0; } @@ -754,20 +761,159 @@ b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg) { fori (idx, 0, bh_arr_length(args->values)) { if (args->values[idx] == NULL) args->values[idx] = (AstTyped *) lookup_default_value_by_idx(provider, idx); if (args->values[idx] == NULL) { - *err_msg = bh_aprintf(global_scratch_allocator, "No value given for %d%s argument.", idx + 1, bh_num_suffix(idx + 1)); + if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "No value given for %d%s argument.", idx + 1, bh_num_suffix(idx + 1)); success = 0; } } i32 maximum_arguments = maximum_argument_count(provider); if (bh_arr_length(args->values) > maximum_arguments) { - *err_msg = bh_aprintf(global_scratch_allocator, "Too many values provided. Expected at most %d.", maximum_arguments); + if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "Too many values provided. Expected at most %d.", maximum_arguments); success = 0; } return success; } + +// +// Argument checking +// + +typedef enum ArgState { + AS_Expecting_Exact, + AS_Expecting_Typed_VA, + AS_Expecting_Untyped_VA, +} ArgState; + +b32 check_arguments_against_type(Arguments* args, TypeFunction* func_type, VarArgKind* va_kind, + OnyxToken* location, char* func_name, OnyxError* error) { + b32 permanent = location != NULL; + if (func_name == NULL) func_name = "UNKNOWN FUNCTION"; + + bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) args->values; + i32 arg_count = function_get_minimum_argument_count(func_type, args); + + Type **formal_params = func_type->params; + Type* variadic_type = NULL; + i64 any_type_id = type_build_from_ast(context.ast_alloc, builtin_any_type)->id; + + ArgState arg_state = AS_Expecting_Exact; + u32 arg_pos = 0; + while (1) { + switch (arg_state) { + case AS_Expecting_Exact: { + if (arg_pos >= func_type->param_count) goto type_checking_done; + + if (formal_params[arg_pos]->kind == Type_Kind_VarArgs) { + variadic_type = formal_params[arg_pos]->VarArgs.elem; + arg_state = AS_Expecting_Typed_VA; + continue; + } + + if ((i16) arg_pos == func_type->vararg_arg_pos) { + arg_state = AS_Expecting_Untyped_VA; + continue; + } + + if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done; + + assert(arg_arr[arg_pos]->kind == Ast_Kind_Argument); + if (!unify_node_and_type_(&arg_arr[arg_pos]->value, formal_params[arg_pos], permanent)) { + if (error != NULL) { + error->pos = arg_arr[arg_pos]->token->pos, + error->text = bh_aprintf(global_heap_allocator, + "The procedure '%s' expects a value of type '%s' for %d%s parameter, got '%s'.", + func_name, + type_get_name(formal_params[arg_pos]), + arg_pos + 1, + bh_num_suffix(arg_pos + 1), + node_get_type_name(arg_arr[arg_pos]->value)); + } + return 0; + } + + arg_arr[arg_pos]->va_kind = VA_Kind_Not_VA; + break; + } + + case AS_Expecting_Typed_VA: { + if (variadic_type->id == any_type_id) *va_kind = VA_Kind_Any; + + if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done; + + if (variadic_type->id == any_type_id) { + resolve_expression_type(arg_arr[arg_pos]->value); + arg_arr[arg_pos]->va_kind = VA_Kind_Any; + break; + } + + *va_kind = VA_Kind_Typed; + + assert(arg_arr[arg_pos]->kind == Ast_Kind_Argument); + if (!unify_node_and_type_(&arg_arr[arg_pos]->value, variadic_type, permanent)) { + if (error != NULL) { + error->pos = arg_arr[arg_pos]->token->pos, + error->text = bh_aprintf(global_heap_allocator, + "The procedure '%s' expects a value of type '%s' for the variadic parameter, got '%s'.", + func_name, + type_get_name(variadic_type), + node_get_type_name(arg_arr[arg_pos]->value)); + } + return 0; + } + + arg_arr[arg_pos]->va_kind = VA_Kind_Typed; + break; + } + + case AS_Expecting_Untyped_VA: { + *va_kind = VA_Kind_Untyped; + + if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done; + + assert(arg_arr[arg_pos]->kind == Ast_Kind_Argument); + resolve_expression_type(arg_arr[arg_pos]->value); + if (arg_arr[arg_pos]->value->type == NULL) { + if (error != NULL) { + error->pos = arg_arr[arg_pos]->token->pos; + error->text = "Unable to resolve type for argument."; + } + return 0; + } + + arg_arr[arg_pos]->va_kind = VA_Kind_Untyped; + break; + } + } + + arg_pos++; + } + +type_checking_done: + if (arg_pos < func_type->needed_param_count) { + if (error != NULL) { + error->pos = location->pos; + error->text = "Too few arguments to function call."; + } + return 0; + } + + if (arg_pos < (u32) arg_count) { + if (error != NULL) { + error->pos = location->pos; + error->text = bh_aprintf(global_heap_allocator, "Too many arguments to function call. %d %d", arg_pos, arg_count); + } + return 0; + } + + return 1; +} + + + + + // // String parsing helpers // diff --git a/src/wasm.c b/src/wasm.c index 144deaba..43125c52 100644 --- a/src/wasm.c +++ b/src/wasm.c @@ -2845,20 +2845,20 @@ EMIT_FUNC(return, AstReturn* ret) { // Clear the normal deferred statements emit_deferred_stmts(mod, &code); - // Clear the rest of the deferred statements - if (bh_arr_length(mod->deferred_stmts) > 0) { - i32 i = bh_arr_length(mod->deferred_stmts) - 1; - while (i >= 0) { - emit_deferred_stmt(mod, &code, mod->deferred_stmts[i]); - i--; - } - } - i64 jump_label = get_structured_jump_label(mod, Jump_Type_Return, 1); if (jump_label >= 0) { WIL(WI_JUMP, jump_label); } else { + // Clear the rest of the deferred statements + if (bh_arr_length(mod->deferred_stmts) > 0) { + i32 i = bh_arr_length(mod->deferred_stmts) - 1; + while (i >= 0) { + emit_deferred_stmt(mod, &code, mod->deferred_stmts[i]); + i--; + } + } + // Make a patch for the two instructions needed to restore the stack pointer SUBMIT_PATCH(mod->stack_leave_patches, 0); WI(WI_NOP); diff --git a/tests/named_arguments_test b/tests/named_arguments_test index 2942784d..e7cf58fb 100644 --- a/tests/named_arguments_test +++ b/tests/named_arguments_test @@ -26,3 +26,6 @@ MATCHED Z: 10 ========================= false true false true true false true true false false true true false true +MATCHED X +MATCHED Y +MATCHED Z [ 10, 40, 90 ] diff --git a/tests/named_arguments_test.onyx b/tests/named_arguments_test.onyx index 7b1bcc26..a2a7d58d 100644 --- a/tests/named_arguments_test.onyx +++ b/tests/named_arguments_test.onyx @@ -73,13 +73,13 @@ main :: (args: [] cstr) { // This currently does not work and would require a lot of rewriting of compiler internals to make work. - // named_baked_overloaded_parameters :: proc { - // ($T: type_expr, x: T) do { println("MATCHED X"); }, - // ($T: type_expr, y: [$N] T) do { println("MATCHED X"); }, - // ($T: type_expr, z: T) do { println("MATCHED X"); }, - // } - - // named_baked_overloaded_parameters(i32, 10); - // named_baked_overloaded_parameters(i32, u32.[ 10, 20, 30 ]); - // named_baked_overloaded_parameters(i32, z = u32.[ 10, 20, 30 ]); + named_baked_overloaded_parameters :: #match { + ($T: type_expr, x: T) { println("MATCHED X"); }, + ($T: type_expr, y: [$N] T) { println("MATCHED Y"); }, + ($T: type_expr, z: [$N] T) { printf("MATCHED Z {}\n", z); }, + } + + named_baked_overloaded_parameters(i32, 10); + named_baked_overloaded_parameters(i32, u32.[ 10, 20, 30 ]); + named_baked_overloaded_parameters(i32, z = u32.[ 10, 40, 90 ]); }