massive clean up of polymorphic procedure generation
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 24 Dec 2020 20:04:46 +0000 (14:04 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 24 Dec 2020 20:04:46 +0000 (14:04 -0600)
include/onyxastnodes.h
include/onyxsempass.h
onyx
src/onyxsymres.c
src/onyxutils.c

index 7b2d5c74404dea3be6947764c1617c830fe2113a..1ecbee00ad17dd51c47c8325c376792fb9b21afd 100644 (file)
@@ -65,6 +65,7 @@ typedef struct AstFunction AstFunction;
 typedef struct AstOverloadedFunction AstOverloadedFunction;
 
 typedef struct AstPolyParam AstPolyParam;
+typedef struct AstPolySolution AstPolySolution;
 typedef struct AstPolyProc AstPolyProc;
 
 typedef struct AstPackage AstPackage;
@@ -712,7 +713,23 @@ struct AstFunction {
         };
     };
 };
-struct AstPolyParam { AstNode* poly_sym; AstType* type_expr; u64 idx; };
+struct AstPolyParam {
+    // The symbol node that represents the polymorphic variable.
+    AstNode* poly_sym;
+
+    // The type expression that contains `poly_sym` in it somewhere.
+    // Matching polymorphic variables does a parallel tree traversal
+    // to find the pairing of the actual type and polymorphic variable
+    // symbol.
+    AstType* type_expr;
+
+    // The parameter index where the polymorphic variable occurs.
+    u64 idx;
+};
+struct AstPolySolution {
+    AstNode* poly_sym;
+    Type*    type;
+};
 struct AstPolyProc {
     AstNode_base;
 
@@ -811,6 +828,7 @@ Entity entity_heap_top(EntityHeap* entities);
 void entity_heap_change_top(EntityHeap* entities, Entity new_top);
 void entity_heap_remove_top(EntityHeap* entities);
 
+void entity_bring_to_state(Entity* ent, EntityState state);
 void symres_entity(Entity* ent);
 void check_entity(Entity* ent);
 void emit_entity(Entity* ent);
@@ -892,6 +910,7 @@ typedef enum PolyProcLookupMethod {
     PPLM_By_Function_Type,
 } PolyProcLookupMethod;
 AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxFilePos pos);
+AstFunction* polymorphic_proc_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxFilePos pos);
 
 AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(Type *) params, OnyxFilePos pos);
 
index 9293353b8ed91dc3f9354d2cc734967359858409..9897a55bf854994a7a48540ceda9945c0ff2d7ec 100644 (file)
@@ -31,11 +31,8 @@ typedef struct SemState {
 extern SemState semstate;
 
 AstType* symres_type(AstType* type);
-void symres_function(AstFunction* func);
 
 b32 check_expression(AstTyped** expr);
-b32 check_function_header(AstFunction* func);
-b32 check_function(AstFunction* func);
 
 // TODO: This should be moved elsewhere.
 void onyx_sempass_init(bh_allocator alloc, bh_allocator node_alloc);
diff --git a/onyx b/onyx
index c8f1d3782fdac06e86a8806b4983b65ab7dc0940..722bd518f0a25854d28a426c17642eea3dac1bbd 100755 (executable)
Binary files a/onyx and b/onyx differ
index 4639913e61db0db1a246eed7efda32a79c5c1b46..185480c75c91113e1597dc3534b134f1117f3281 100644 (file)
@@ -24,6 +24,7 @@ static void symres_use(AstUse* use);
 static void symres_statement_chain(AstNode** walker);
 static b32  symres_statement(AstNode** stmt);
 static void symres_block(AstBlock* block);
+void symres_function_header(AstFunction* func);
 void symres_function(AstFunction* func);
 static void symres_global(AstGlobal* global);
 static void symres_overloaded_function(AstOverloadedFunction* ofunc);
@@ -40,8 +41,6 @@ static AstFieldAccess* make_field_access(AstTyped* node, char* field) {
 }
 
 static void scope_enter(Scope* new_scope) {
-    // if (new_scope->parent == NULL)
-    //     new_scope->parent = semstate.curr_scope;
     semstate.curr_scope = new_scope;
 }
 
@@ -642,7 +641,7 @@ static void symres_block(AstBlock* block) {
     scope_leave();
 }
 
-void symres_function(AstFunction* func) {
+void symres_function_header(AstFunction* func) {
     if (func->scope == NULL)
         func->scope = scope_create(semstate.node_allocator, semstate.curr_scope, func->token->pos);
 
@@ -651,6 +650,13 @@ void symres_function(AstFunction* func) {
             symres_expression(&param->default_value);
             if (onyx_has_errors()) return;
 
+            // HACK: It shouldn't be necessary to do this twice, but right now
+            // if `null` is the default parameter and it hasn't been used anywhere in
+            // code yet, it doesn't resolve properly. So for now I am just checking symbols twice.
+            //                                                      -brendanfh 2020/12/24
+            symres_expression(&param->default_value);
+            if (onyx_has_errors()) return;
+
             if (check_expression(&param->default_value)) return;
         }
     }
@@ -744,9 +750,11 @@ void symres_function(AstFunction* func) {
         }
     }
 
-    if (func->type_node != NULL) {
-        func->type_node = symres_type(func->type_node);
-    }
+    scope_leave();
+}
+
+void symres_function(AstFunction* func) {
+    scope_enter(func->scope);
 
     semstate.curr_function = func;
     symres_block(func->body);
@@ -864,20 +872,24 @@ static void symres_memres(AstMemRes** memres) {
 }
 
 static void symres_polyproc(AstPolyProc* pp) {
-    pp->poly_scope = scope_create(semstate.node_allocator, semstate.curr_scope, pp->token->pos);
+    pp->poly_scope = semstate.curr_scope;
 }
 
 void symres_entity(Entity* ent) {
-    if (ent->package) {
+    if (ent->package) semstate.curr_package = ent->package;
+
+    Scope* old_scope;
+    if (ent->scope) {
+        old_scope = semstate.curr_scope;
         scope_enter(ent->scope);
-        semstate.curr_package = ent->package;
     }
 
     EntityState next_state = Entity_State_Check_Types;
 
     switch (ent->type) {
         case Entity_Type_Foreign_Function_Header:
-        case Entity_Type_Function:            symres_function(ent->function); break;
+        case Entity_Type_Function_Header:         symres_function_header(ent->function); break;
+        case Entity_Type_Function:                symres_function(ent->function);        break;
 
         case Entity_Type_Foreign_Global_Header:
         case Entity_Type_Global_Header:       symres_global(ent->global); break;
@@ -900,5 +912,7 @@ void symres_entity(Entity* ent) {
 
     ent->state = next_state;
 
-    if (ent->package) scope_leave();
+    if (ent->scope) {
+        semstate.curr_scope = old_scope;
+    }
 }
index 85bb259d2d62041720ee2a4636e5eeb360c36801..86562e25f94c717f7b29abedb9097f4f282672ae 100644 (file)
@@ -533,10 +533,12 @@ AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lo
         bh_table_init(global_heap_allocator, pp->concrete_funcs, 8);
     }
 
-    scope_clear(pp->poly_scope);
+    bh_arr(AstPolySolution) slns = NULL;
+    bh_arr_new(global_heap_allocator, slns, bh_arr_length(pp->poly_params));
 
     bh_arr_each(AstPolyParam, param, pp->poly_params) {
         Type* actual_type;
+
         if (pp_lookup == PPLM_By_Call) {
             AstArgument* arg = ((AstCall *) actual)->arguments;
             if (param->idx >= ((AstCall *) actual)->arg_count) {
@@ -570,71 +572,89 @@ AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lo
             return NULL;
         }
 
-        AstTypeRawAlias* raw = onyx_ast_node_new(semstate.node_allocator, sizeof(AstTypeRawAlias), Ast_Kind_Type_Raw_Alias);
-        raw->to = resolved_type;
-
-        symbol_introduce(pp->poly_scope, param->poly_sym->token, (AstNode *) raw);
+        bh_arr_push(slns, ((AstPolySolution) {
+            .poly_sym = param->poly_sym,
+            .type     = resolved_type
+        }));
     }
 
-    // HACK(Brendan): Maybe each type should be given a unique id upon creation?
-    // Then that could be used to uniquely identify the type, instead of relying
-    // on the name being unique.                     - brendanfh 2020/12/14
+    AstFunction* result = polymorphic_proc_solidify(pp, slns, pos);
+    
+    bh_arr_free(slns);
+    return result;
+}
+
+// NOTE: This returns a volatile string. Do not store it without copying it.
+static char* build_polyproc_unique_key(bh_arr(AstPolySolution) slns) {
     static char key_buf[1024];
     fori (i, 0, 1024) key_buf[i] = 0;
-    bh_table_each_start(AstNode *, pp->poly_scope->symbols);
-        strncat(key_buf, key, 1023);
+
+    bh_arr_each(AstPolySolution, sln, slns) {
+        token_toggle_end(sln->poly_sym->token);
+
+        strncat(key_buf, sln->poly_sym->token->text, 1023);
         strncat(key_buf, "=", 1023);
-        strncat(key_buf, type_get_unique_name(((AstTypeRawAlias *) value)->to), 1023);
+        strncat(key_buf, type_get_unique_name(sln->type), 1023);
         strncat(key_buf, ";", 1023);
-    bh_table_each_end;
 
-    if (bh_table_has(AstFunction *, pp->concrete_funcs, key_buf)) {
-        return bh_table_get(AstFunction *, pp->concrete_funcs, key_buf);
+        token_toggle_end(sln->poly_sym->token);
     }
 
-    Type* old_return_type = semstate.expected_return_type;
-    Scope* old_scope = semstate.curr_scope;
-    semstate.curr_scope = pp->poly_scope;
+    return key_buf;
+}
 
-    AstFunction* func = (AstFunction *) ast_clone(semstate.node_allocator, pp->base_func);
-    bh_table_put(AstFunction *, pp->concrete_funcs, key_buf, func);
+AstFunction* polymorphic_proc_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxFilePos pos) {
+    if (pp->concrete_funcs == NULL) {
+        bh_table_init(global_heap_allocator, pp->concrete_funcs, 8);
+    }
 
-    symres_function(func);
-    semstate.curr_scope = old_scope;
+    // NOTE: Check if a version of this polyproc has already been created.
+    char* unique_key = build_polyproc_unique_key(slns);
+    if (bh_table_has(AstFunction *, pp->concrete_funcs, unique_key)) {
+        return bh_table_get(AstFunction *, pp->concrete_funcs, unique_key);
+    }
 
-    if (onyx_has_errors()) goto has_error;
-    if (check_function_header(func)) goto has_error;
-    goto no_errors;
+    Scope* poly_scope = scope_create(semstate.node_allocator, pp->poly_scope, pos);
+    bh_arr_each(AstPolySolution, sln, slns) {
+        AstTypeRawAlias* raw = onyx_ast_node_new(semstate.node_allocator, sizeof(AstTypeRawAlias), Ast_Kind_Type_Raw_Alias);
+        raw->to = sln->type;
 
-has_error:
-    onyx_report_error(pos, "Error in polymorphic procedure generated from this call site.");
-    semstate.expected_return_type = old_return_type;
-    return NULL;
+        symbol_introduce(poly_scope, sln->poly_sym->token, (AstNode *) raw);
+    }
 
-no_errors:
-    semstate.expected_return_type = old_return_type;
+    AstFunction* func = (AstFunction *) ast_clone(semstate.node_allocator, pp->base_func);
+    bh_table_put(AstFunction *, pp->concrete_funcs, unique_key, func);
+
+    func->flags |= Ast_Flag_Function_Used;
 
-    entity_heap_insert(&semstate.program->entities, (Entity) {
-        .state = Entity_State_Code_Gen,
+    Entity func_header_entity = {
+        .state = Entity_State_Resolve_Symbols,
         .type = Entity_Type_Function_Header,
         .function = func,
         .package = NULL,
-    });
+        .scope = poly_scope,
+    };
+
+    entity_bring_to_state(&func_header_entity, Entity_State_Code_Gen);
+    if (onyx_has_errors()) {
+        onyx_report_error(pos, "Error in polymorphic procedure header generated from this call site.");
+        return NULL;
+    }
 
-    entity_heap_insert(&semstate.program->entities, (Entity) {
-        .state = Entity_State_Check_Types,
+    Entity func_entity = {
+        .state = Entity_State_Resolve_Symbols,
         .type = Entity_Type_Function,
         .function = func,
         .package = NULL,
-    });
-
-    func->flags |= Ast_Flag_Function_Used;
+        .scope = poly_scope,
+    };
 
+    entity_heap_insert(&semstate.program->entities, func_header_entity);
+    entity_heap_insert(&semstate.program->entities, func_entity);
     return func;
 }
 
 
-
 AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(Type *) params, OnyxFilePos pos) {
     // @Cleanup
     assert(bh_arr_length(ps_type->poly_params) == bh_arr_length(params));
@@ -887,3 +907,17 @@ char* get_function_name(AstFunction* func) {
 
     return "<anonymous procedure>";
 }
+
+void entity_bring_to_state(Entity* ent, EntityState state) {
+    while (ent->state != state) {
+        switch (ent->state) {
+            case Entity_State_Resolve_Symbols: symres_entity(ent); break;
+            case Entity_State_Check_Types:     check_entity(ent);  break;
+            case Entity_State_Code_Gen:        emit_entity(ent);   break;
+
+            default: return;
+        }
+
+        if (onyx_has_errors()) return;
+    }
+}