cleaned up code relating to overloaded procedures
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 8 Mar 2021 03:52:54 +0000 (21:52 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 8 Mar 2021 03:52:54 +0000 (21:52 -0600)
bin/onyx
include/onyxastnodes.h
src/onyxastnodes.c
src/onyxchecker.c
src/onyxentities.c
src/onyxparser.c
src/onyxutils.c

index 7e78bd707d604bdd9e6d4336f8158b9eb5efc68f..8aa4189de856ccd4f443ae3c6bf309084a9db09d 100755 (executable)
Binary files a/bin/onyx and b/bin/onyx differ
index 7303159bc75a7165e80e5f06dfed907b1b127e97..827072bbc033c90635e8fca4d00cc692cfa4d0e1 100644 (file)
@@ -1159,6 +1159,7 @@ AstNode*         make_symbol(bh_allocator a, OnyxToken* sym);
 void arguments_initialize(Arguments* args);
 b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg);
 void arguments_ensure_length(Arguments* args, u32 count);
+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);
@@ -1175,6 +1176,8 @@ AstFunction* polymorphic_proc_solidify(AstPolyProc* pp, bh_arr(AstPolySolution)
 AstNode* polymorphic_proc_try_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
 AstFunction* polymorphic_proc_build_only_header(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual);
 
+AstTyped* find_matching_overload_by_arguments(bh_arr(AstTyped *) overloads, Arguments* args);
+void report_unable_to_match_overload(AstCall* call);
 
 AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos);
 
index f12047159abc4f8c176e9bb58568b0688a28a866..52c7ecc718f73726fa3b06214b57e37924f2106f 100644 (file)
@@ -762,7 +762,15 @@ void arguments_ensure_length(Arguments* args, u32 count) {
     bh_arr_set_length(args->values, bh_max(count, (u32) bh_arr_length(args->values)));
 }
 
-// In clone, the named_values are not copied. This is used in match_overloaded_function since it doesn't need them to be copied.
+void arguments_copy(Arguments* dest, Arguments* src) {
+    dest->named_values = src->named_values;
+    
+    arguments_ensure_length(dest, 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->named_values = src->named_values;
     dest->values = bh_arr_copy(global_heap_allocator, src->values);
index 3b3b23c6a9a8583e4c32d07e78de73aa61717c26..12898e2ba707fb112051f650e3ce8be891a02010 100644 (file)
@@ -307,99 +307,6 @@ CheckStatus check_switch(AstSwitch* switchnode) {
     return 0;
 }
 
-static AstTyped* match_overloaded_function(Arguments* args, bh_arr(AstTyped *) overloads) {
-    bh_arr_each(AstTyped *, node, overloads) {
-        AstFunction* overload = NULL;
-        if ((*node)->kind == Ast_Kind_Function) {
-            overload = (AstFunction *) *node;
-        }
-        else if ((*node)->kind == Ast_Kind_Polymorphic_Proc) {
-            overload = polymorphic_proc_build_only_header((AstPolyProc *) *node, PPLM_By_Arguments, args);
-        }
-
-        if (overload == NULL) continue;
-
-        Arguments* args_to_use = args;
-        if (args->named_values != NULL && bh_arr_length(args->named_values) > 0) {
-            args_to_use = bh_alloc_item(global_scratch_allocator, Arguments);
-
-            arguments_clone(args_to_use, args);
-            arguments_ensure_length(args_to_use, bh_arr_length(args->values) + bh_arr_length(args->named_values));
-
-            b32 values_place_correctly = fill_in_arguments(args_to_use, (AstNode *) overload, NULL);
-
-            if (!values_place_correctly) goto no_match;
-        }
-
-        fill_in_type((AstTyped *) overload);
-
-        TypeFunction* ol_type = &overload->type->Function;
-        if (bh_arr_length(args_to_use->values) < (i32) ol_type->needed_param_count) continue;
-
-        i32 param_left = ol_type->param_count;
-        Type** param_type = ol_type->params;
-        bh_arr_each(AstTyped*, arg, args_to_use->values) {
-            if (param_left == 0) goto no_match;
-            param_left--;
-
-            fill_in_type(*arg);
-
-            Type* type_to_match = *param_type;
-            if ((*param_type)->kind == Type_Kind_VarArgs)
-                type_to_match = (*param_type)->VarArgs.ptr_to_data->Pointer.elem;
-
-            AstTyped** value = arg;
-            if ((*arg)->kind == Ast_Kind_Argument)
-                value = &((AstArgument *) *arg)->value;
-
-            if (!type_check_or_auto_cast(value, type_to_match)) goto no_match;
-
-            param_type++;
-        }
-
-        return (AstTyped *) *node;
-
-no_match:
-        if (args->named_values != NULL && bh_arr_length(args->named_values) > 0) {
-            bh_arr_free(args_to_use->values);
-        }
-
-        continue;
-    }
-
-    return NULL;
-}
-
-static void report_unable_to_match_overload(AstCall* call) {
-    char* arg_str = bh_alloc(global_scratch_allocator, 1024);
-    arg_str[0] = '\0';
-
-    bh_arr_each(AstTyped *, arg, call->args.values) {
-        strncat(arg_str, node_get_type_name(*arg), 1023);
-
-        if (arg != &bh_arr_last(call->args.values))
-            strncat(arg_str, ", ", 1023);
-    }
-
-    if (bh_arr_length(call->args.named_values) > 0) {
-        bh_arr_each(AstNamedValue *, named_value, call->args.named_values) { 
-            token_toggle_end((*named_value)->token);
-            strncat(arg_str, (*named_value)->token->text, 1023);
-            token_toggle_end((*named_value)->token);
-
-            strncat(arg_str, "=", 1023);
-            strncat(arg_str, node_get_type_name((*named_value)->value), 1023); // CHECK: this might say 'unknown'.
-
-            if (named_value != &bh_arr_last(call->args.named_values))
-                strncat(arg_str, ", ", 1023);
-        }
-    }
-
-    onyx_report_error(call->token->pos, "unable to match overloaded function with provided argument types: (%s)", arg_str);
-
-    bh_free(global_scratch_allocator, arg_str);
-}
-
 CheckStatus check_arguments(Arguments* args) {
     bh_arr_each(AstTyped *, actual, args->values)
         CHECK(expression, actual);
@@ -451,12 +358,10 @@ CheckStatus check_call(AstCall* call) {
     //      1. Ensure the callee is not a symbol
     //      2. Check the callee expression (since it could be a variable or a field access, etc)
     //      3. Check all arguments
-    //          * Cannot pass overloaded functions
-    //          * Cannot pass a non-simple struct
+    //          * Cannot pass overloaded functions (ROBUSTNESS)
     //      4. If callee is an overloaded function, use the argument types to determine which overload is used.
     //      5. If callee is polymorphic, use the arguments type to generate a polymorphic function.
-    //      6. If an argument is polymorphic, generate the correct polymorphic function.
-    //      7. Fill in default arguments
+    //      7. Fill in arguments
     //      8. If callee is an intrinsic, turn call into an Intrinsic_Call node
     //      9. Check types of formal and actual params against each other, handling varargs
 
@@ -464,7 +369,7 @@ CheckStatus check_call(AstCall* call) {
     check_arguments(&call->args);
 
     if (call->callee->kind == Ast_Kind_Overloaded_Function) {
-        call->callee = match_overloaded_function(&call->args, ((AstOverloadedFunction *) call->callee)->overloads);
+        call->callee = find_matching_overload_by_arguments(((AstOverloadedFunction *) call->callee)->overloads, &call->args);
         if (call->callee == NULL) {
             report_unable_to_match_overload(call);
             return Check_Error;
@@ -716,13 +621,6 @@ CheckStatus check_binop_assignment(AstBinaryOp* binop, b32 assignment_is_ok) {
         return Check_Error;
     }
 
-    // CLEANUP: This seems like it should be here. But I don't know where
-    // or what the right place is for it.
-    // if (binop->right->type == NULL) {
-    //     onyx_report_error(binop->right->token->pos, "Unable to resolve type for this expression.");
-    //     return Check_Error;
-    // }
-
     binop->type = &basic_types[Basic_Kind_Void];
 
     return Check_Success;
@@ -803,7 +701,7 @@ static AstCall* binaryop_try_operator_overload(AstBinaryOp* binop) {
     bh_arr_push(args.values, binop->left);
     bh_arr_push(args.values, binop->right);
 
-    AstTyped* overload = match_overloaded_function(&args, operator_overloads[binop->operation]);
+    AstTyped* overload = find_matching_overload_by_arguments(operator_overloads[binop->operation], &args);
     if (overload == NULL) {
         bh_arr_free(args.values);
         return NULL;
index bf3e88c40ccea67cb3a11cfba1ac0a38b4d15dd6..d415b62d10689a62f4ac10784ef30414658c6003 100644 (file)
@@ -68,7 +68,6 @@ void entity_heap_init(EntityHeap* entities) {
     bh_arena_init(&entities->entity_arena, global_heap_allocator, 32 * 1024);
 }
 
-// nocheckin
 // Allocates the entity in the entity heap. Don't quite feel this is necessary...
 Entity* entity_heap_register(EntityHeap* entities, Entity e) {
     bh_allocator alloc = bh_arena_allocator(&entities->entity_arena);
@@ -254,7 +253,6 @@ void add_entities_for_node(bh_arr(Entity *) *target_arr, AstNode* node, Scope* s
         }
         
         case Ast_Kind_Use_Package: {
-            // nocheckin
             ent.state = Entity_State_Comptime_Resolve_Symbols;
             ent.type = Entity_Type_Use_Package;
             ent.use_package = (AstUsePackage *) node;
index e3e8f70f96920762f531f7d783acfdd1d41c6e32..7e53af02fd973b16b9e412f01a0014b2b9a4ccda 100644 (file)
@@ -26,8 +26,6 @@ void submit_entity_in_scope(OnyxParser* parser, AstNode* node, Scope* scope, Pac
 
     } else {
         bh_arr(Entity *) *entity_array = bh_arr_last(parser->alternate_entity_placement_stack);
-
-        // nocheckin This should also be able to place them in the false entities
         add_entities_for_node(entity_array, node, scope, package);
     }
 }
index f176a1b224d7f46d99b2c28a00ddd8de21987f0b..91de1ac0c6a4d7dc6bf803d78636769a517da24e 100644 (file)
@@ -211,7 +211,7 @@ void scope_clear(Scope* scope) {
 
 
 //
-// Polymorphic things
+// Polymorphic Procedures
 //
 static void ensure_polyproc_cache_is_created(AstPolyProc* pp) {
     if (pp->concrete_funcs == NULL) {
@@ -913,6 +913,123 @@ AstFunction* polymorphic_proc_build_only_header(AstPolyProc* pp, PolyProcLookupM
     return solidified_func.func;
 }
 
+//
+// Overloaded Procedures
+//
+
+//
+// @Cleanup: Everything having to do with overload resolving!
+// Things that need to be available:
+//  * A copy of the arguments list that can be mutated
+//      - The named_values do not need to be copied, because they are not modified when fill_in_arguments happens.
+//      - Only values needs to have a copy available
+//      - This copy needs to be reset after checking every option
+//
+// Steps needed to check if an overload option is "the one":
+//  1. Figure out what the overload is
+//      a. If polymorphic, generate the header for the procedure only
+//  2. Place the arguments in the copy, according to the overload option
+//  3. Ensure the option has a type filled out
+//  4. For each argument
+//      a. Ensure it has a place to go (not too many arguments)
+//      b. Ensure it has a type
+//      c. Ensure the types match (currently there could be a problem if an option is attempted and doesn't work all the way that polymorphic procedures as arguments could still be solidified)
+//
+// Additional features that this needs to account for:
+//  * Resolving an overload from a list of parameter types
+//  * Resolving an overload from a TypeFunction (so an overloaded procedure can be passed as a parameter)
+//
+
+AstTyped* find_matching_overload_by_arguments(bh_arr(AstTyped *) overloads, Arguments* param_args) {
+    Arguments args;
+    arguments_clone(&args, param_args);
+    arguments_ensure_length(&args, bh_arr_length(args.values) + bh_arr_length(args.named_values));
+
+    AstTyped *matched_overload = NULL;
+
+    bh_arr_each(AstTyped *, node, overloads) {
+        arguments_copy(&args, param_args);
+
+        AstFunction* overload = NULL;
+        switch ((*node)->kind) {
+            case Ast_Kind_Function:         overload = (AstFunction *) *node; break;
+            case Ast_Kind_Polymorphic_Proc: overload = polymorphic_proc_build_only_header((AstPolyProc *) *node, PPLM_By_Arguments, param_args); break;
+        }
+
+        // NOTE: Overload is not something that is known to be overloadable.
+        if (overload == NULL) continue;
+        if (overload->kind != Ast_Kind_Function) continue;
+
+        // 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.ptr_to_data->Pointer.elem;
+            if ((*value)->kind == Ast_Kind_Argument)      value = &((AstArgument *) *value)->value;
+
+            if (!type_check_or_auto_cast(value, type_to_match)) {
+                all_arguments_work = 0;
+                break;
+            }
+        }
+
+        if (all_arguments_work) {
+            matched_overload = *node;
+            break;
+        }
+    }
+
+    bh_arr_free(args.values);
+    return matched_overload;
+}
+
+void report_unable_to_match_overload(AstCall* call) {
+    char* arg_str = bh_alloc(global_scratch_allocator, 1024);
+    arg_str[0] = '\0';
+
+    bh_arr_each(AstTyped *, arg, call->args.values) {
+        strncat(arg_str, node_get_type_name(*arg), 1023);
+
+        if (arg != &bh_arr_last(call->args.values))
+            strncat(arg_str, ", ", 1023);
+    }
+
+    if (bh_arr_length(call->args.named_values) > 0) {
+        bh_arr_each(AstNamedValue *, named_value, call->args.named_values) { 
+            token_toggle_end((*named_value)->token);
+            strncat(arg_str, (*named_value)->token->text, 1023);
+            token_toggle_end((*named_value)->token);
+
+            strncat(arg_str, "=", 1023);
+            strncat(arg_str, node_get_type_name((*named_value)->value), 1023); // CHECK: this might say 'unknown'.
+
+            if (named_value != &bh_arr_last(call->args.named_values))
+                strncat(arg_str, ", ", 1023);
+        }
+    }
+
+    onyx_report_error(call->token->pos, "unable to match overloaded function with provided argument types: (%s)", arg_str);
+
+    bh_free(global_scratch_allocator, arg_str);
+}
+
+
+
+//
+// Polymorphic Structures
+//
 char* build_poly_struct_name(AstPolyStructType* ps_type, Type* cs_type) {
     char name_buf[256];
     fori (i, 0, 256) name_buf[i] = 0;
@@ -1042,7 +1159,7 @@ AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstP
     entity_bring_to_state(&struct_default_entity, Entity_State_Code_Gen);
  
     if (onyx_has_errors()) {
-        onyx_report_error(pos, "Error in creating polymoprhic struct instantiation here.");
+        onyx_report_error(pos, "Error in creating polymorphic struct instantiation here.");
         bh_table_put(AstStructType *, ps_type->concrete_structs, unique_key, NULL);
         return NULL;
     }