completely revamped the polymorph solving system
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 14 Nov 2021 23:54:22 +0000 (17:54 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 14 Nov 2021 23:54:22 +0000 (17:54 -0600)
CHANGELOG
include/astnodes.h
src/astnodes.c
src/checker.c
src/entities.c
src/polymorph.c
src/symres.c
src/types.c
src/utils.c

index 308b4ea5b56118ad2aef7a92f7e46a220185f363..a4a80bef7d8bb59be651804dd5c2822f3b3337e6 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -16,6 +16,7 @@ Additions:
 * Quick-Functions
 * do-expressions
 * typeof
+* interfaces and where clauses
 * '#auto' return type
 * Macros and paste-able code blocks
 * overloaded procedures can have polymorphic and overloaded procedures as overloads.  This makes them MUCH more powerful.
@@ -45,6 +46,7 @@ Changes:
 * operator overloading is now done as a top level declaration handled through the entity
     system, instead of relying on creating a procedure. This lets you use an existing
     procedure as an operator overload. Take a look at '#operator ==' in string.onyx.
+* easier to write types in expressions
 * Tons of internal code cleanup relating to structures and code generation.
 
 Bug fixes:
index ddb2f36f64fdda1bdf4bfbfd759a9d0c93332ad1..097c97400016031289f9a8d66ea94dd8d7f19f84 100644 (file)
@@ -88,6 +88,7 @@
     NODE(PolySolution)         \
     NODE(SolidifiedFunction)   \
     NODE(PolyProc)             \
+    NODE(PolyQuery)            \
                                \
     NODE(Note)                 \
     NODE(CallSite)             \
@@ -125,6 +126,7 @@ typedef enum AstKind {
     Ast_Kind_Function,
     Ast_Kind_Overloaded_Function,
     Ast_Kind_Polymorphic_Proc,
+    Ast_Kind_Polymorph_Query,
     Ast_Kind_Interface,
     Ast_Kind_Constraint,
     Ast_Kind_Constraint_Sentinel,
@@ -567,15 +569,28 @@ struct AstTyped { AstTyped_base; };
 
 // Expression Nodes
 struct AstNamedValue    { AstTyped_base; AstTyped* value; };
-struct AstBinaryOp      { AstTyped_base; BinaryOp operation; AstTyped *left, *right; };
 struct AstUnaryOp       { AstTyped_base; UnaryOp operation; AstTyped *expr; };
 struct AstNumLit        { AstTyped_base; union { i32 i; i64 l; f32 f; f64 d; } value; };
 struct AstStrLit        { AstTyped_base; u64 addr; u64 length; };
 struct AstLocal         { AstTyped_base; };
-struct AstAddressOf     { AstTyped_base; AstTyped *expr; };
 struct AstDereference   { AstTyped_base; AstTyped *expr; };
 struct AstSizeOf        { AstTyped_base; AstType *so_ast_type; Type *so_type; u64 size; };
 struct AstAlignOf       { AstTyped_base; AstType *ao_ast_type; Type *ao_type; u64 alignment; };
+struct AstBinaryOp      {
+    AstTyped_base;
+    BinaryOp operation;
+    AstTyped *left, *right;
+
+    Arguments *overload_args; // This is set of the binary operator is attempted to be overloaded
+                              // but isnt successful yet.
+    AstBinaryOp *potential_substitute;
+};
+struct AstAddressOf     {
+    AstTyped_base;
+    AstTyped *expr;
+
+    AstBinaryOp *potential_substitute;
+};
 struct AstArgument      {
     AstTyped_base;
 
@@ -590,6 +605,11 @@ struct AstSubscript   {
     BinaryOp __unused_operation; // This will be set to Binary_Op_Subscript
     AstTyped *addr;
     AstTyped *expr;
+
+    Arguments *overload_args; // This is set of the binary operator is attempted to be overloaded
+                              // but isnt successful yet.
+    AstBinaryOp *potential_substitute;
+
     u64 elem_size;
 };
 struct AstFieldAccess   {
@@ -1040,6 +1060,11 @@ typedef enum PolySolutionKind {
     PSK_Value,
 } PolySolutionKind;
 
+typedef enum PolyProcLookupMethod {
+    PPLM_By_Arguments,
+    PPLM_By_Function_Type,
+} PolyProcLookupMethod;
+
 struct AstPolyParam {
     PolyParamKind kind;
 
@@ -1091,6 +1116,23 @@ struct AstPolyProc {
 
     AstFunction* base_func;
     bh_table(AstSolidifiedFunction) concrete_funcs;
+    bh_imap active_queries;
+};
+
+struct AstPolyQuery {
+    AstNode_base;
+
+    AstPolyProc *proc;
+    PolyProcLookupMethod pp_lookup;
+    ptr given;
+    OnyxToken *error_loc;
+
+    bh_arr(AstPolySolution) slns;
+
+    AstFunction *function_header;
+
+    b32 error_on_fail : 1;     // Whether or not to report errors on failing to match.
+    b32 successful_symres : 1; // If something successful happened in symbol resolution
 };
 
 
@@ -1218,6 +1260,7 @@ typedef enum EntityType {
     Entity_Type_Interface,
     Entity_Type_Constraint_Check,
     Entity_Type_Polymorphic_Proc,
+    Entity_Type_Polymorph_Query,
     Entity_Type_Macro,
     Entity_Type_Foreign_Function_Header,
     Entity_Type_Temp_Function_Header,    // Same as a Function_Header, except it disappears after it checks completely.
@@ -1269,6 +1312,7 @@ typedef struct Entity {
         AstEnumType           *enum_type;
         AstMemRes             *mem_res;
         AstPolyProc           *poly_proc;
+        AstPolyQuery          *poly_query;
         AstMacro              *macro;
         AstUse                *use;
         AstInterface          *interface;
@@ -1507,10 +1551,8 @@ const char* node_get_type_name(void* node);
 
 b32 static_if_resolution(AstIf* static_if);
 
-typedef enum PolyProcLookupMethod {
-    PPLM_By_Arguments,
-    PPLM_By_Function_Type,
-} PolyProcLookupMethod;
+void insert_poly_sln_into_scope(Scope* scope, AstPolySolution *sln);
+TypeMatch find_polymorphic_sln(AstPolySolution *out, AstPolyParam *param, AstFunction *func, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg);
 AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken* tkn);
 AstFunction* polymorphic_proc_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
 AstNode* polymorphic_proc_try_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
index b5a8d3c7f85fa7981c33629740853410dbbbb2ba..a7bd73e17ed6f638676a4a635b5af6ba00322591 100644 (file)
@@ -14,6 +14,7 @@ static const char* ast_node_names[] = {
     "FUNCTION",
     "OVERLOADED_FUNCTION",
     "POLYMORPHIC PROC",
+    "POLYMORPH QUERY",
     "INTERFACE",
     "CONSTRAINT",
     "CONSTRAINT SENTITEL",
@@ -153,6 +154,7 @@ const char* entity_type_strings[Entity_Type_Count] = {
     "Interface",
     "Constraint Check",
     "Polymorphic Proc",
+    "Polymorph Query",
     "Macro",
     "Foreign_Function Header",
     "Temporary Function Header",
@@ -588,8 +590,6 @@ TypeMatch unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent) {
     if (node->kind == Ast_Kind_Polymorphic_Proc) {
         AstFunction* func = polymorphic_proc_lookup((AstPolyProc *) node, PPLM_By_Function_Type, type, node->token);
         if (func == NULL) return TYPE_MATCH_FAILED;
-
-        // FIXME: This is incorrect. It should actually yield and not return a failure.
         if (func == (AstFunction *) &node_that_signals_a_yield) return TYPE_MATCH_YIELD;
 
         *pnode = (AstTyped *) func;
index 511747e5b2299213296d20d2a54c51a49464a636..8225cba526823c8e3f9f7aacd89edd8196910183 100644 (file)
@@ -97,6 +97,7 @@ CheckStatus check_insert_directive(AstDirectiveInsert** pinsert);
 CheckStatus check_do_block(AstDoBlock** pdoblock);
 CheckStatus check_constraint(AstConstraint *constraint);
 CheckStatus check_constraint_context(ConstraintContext *cc, OnyxFilePos pos);
+CheckStatus check_polyquery(AstPolyQuery *query);
 
 // HACK HACK HACK
 b32 expression_types_must_be_known = 0;
@@ -632,35 +633,36 @@ static void report_bad_binaryop(AstBinaryOp* binop) {
 static AstCall* binaryop_try_operator_overload(AstBinaryOp* binop, AstTyped* third_argument) {
     if (bh_arr_length(operator_overloads[binop->operation]) == 0) return NULL;
 
-    u8 value_buffer[sizeof(bh__arr) + sizeof(AstTyped *) * 3];
-    Arguments args = ((Arguments) { NULL, NULL });
-    args.values = (AstTyped **) &value_buffer[sizeof(bh__arr)];
-    bh_arr_set_length(args.values, third_argument ? 3 : 2);
+    if (binop->overload_args == NULL) {
+        binop->overload_args = bh_alloc_item(context.ast_alloc, Arguments);
+        bh_arr_new(context.ast_alloc, binop->overload_args->values, 3);
+        bh_arr_set_length(binop->overload_args->values, third_argument ? 3 : 2);
 
-    if (binop_is_assignment(binop->operation)) {
-        args.values[0] = (AstTyped *) make_address_of(context.ast_alloc, binop->left);
+        if (binop_is_assignment(binop->operation)) {
+            binop->overload_args->values[0] = (AstTyped *) make_address_of(context.ast_alloc, binop->left);
 
-        u32 current_all_checks_are_final = all_checks_are_final;
-        all_checks_are_final = 0;
-        u32 current_checking_level_store = current_checking_level;
-        CheckStatus cs = check_address_of((AstAddressOf **) &args.values[0]);
-        current_checking_level = current_checking_level_store;
-        all_checks_are_final   = current_all_checks_are_final;
+            u32 current_all_checks_are_final = all_checks_are_final;
+            all_checks_are_final = 0;
+            u32 current_checking_level_store = current_checking_level;
+            CheckStatus cs = check_address_of((AstAddressOf **) &binop->overload_args->values[0]);
+            current_checking_level = current_checking_level_store;
+            all_checks_are_final   = current_all_checks_are_final;
 
-        if (cs == Check_Yield_Macro)      return (AstCall *) &node_that_signals_a_yield;
-        if (cs == Check_Error)            return NULL;
+            if (cs == Check_Yield_Macro)      return (AstCall *) &node_that_signals_a_yield;
+            if (cs == Check_Error)            return NULL;
 
-        args.values[0] = (AstTyped *) make_argument(context.ast_alloc, args.values[0]);
+            binop->overload_args->values[0] = (AstTyped *) make_argument(context.ast_alloc, binop->overload_args->values[0]);
 
-    } else {
-        args.values[0] = (AstTyped *) make_argument(context.ast_alloc, binop->left);
-    }
+        } else {
+            binop->overload_args->values[0] = (AstTyped *) make_argument(context.ast_alloc, binop->left);
+        }
 
 
-    args.values[1] = (AstTyped *) make_argument(context.ast_alloc, binop->right);
-    if (third_argument != NULL) args.values[2] = (AstTyped *) make_argument(context.ast_alloc, third_argument);
+        binop->overload_args->values[1] = (AstTyped *) make_argument(context.ast_alloc, binop->right);
+        if (third_argument != NULL) binop->overload_args->values[2] = (AstTyped *) make_argument(context.ast_alloc, third_argument);
+    }
 
-    AstTyped* overload = find_matching_overload_by_arguments(operator_overloads[binop->operation], &args);
+    AstTyped* overload = find_matching_overload_by_arguments(operator_overloads[binop->operation], binop->overload_args);
     if (overload == NULL || overload == (AstTyped *) &node_that_signals_a_yield) return (AstCall *) overload;
 
     AstCall* implicit_call = onyx_ast_node_new(context.ast_alloc, sizeof(AstCall), Ast_Kind_Call);
@@ -668,7 +670,7 @@ static AstCall* binaryop_try_operator_overload(AstBinaryOp* binop, AstTyped* thi
     implicit_call->callee = overload;
     implicit_call->va_kind = VA_Kind_Not_VA;
 
-    arguments_clone(&implicit_call->args, &args);
+    arguments_clone(&implicit_call->args, binop->overload_args);
     return implicit_call;
 }
 
@@ -898,21 +900,24 @@ CheckStatus check_binaryop(AstBinaryOp** pbinop) {
     if (binop->operation == Binary_Op_Assign && binop->left->kind == Ast_Kind_Subscript && bh_arr_length(operator_overloads[Binary_Op_Subscript_Equals]) > 0) {
         AstSubscript* sub = (AstSubscript *) binop->left;
         
-        u32 current_checking_level_store = current_checking_level;
-        CHECK(expression, &sub->addr);
-        CHECK(expression, &sub->expr);
-        CHECK(expression, &binop->right);
-        current_checking_level = current_checking_level_store;
-
-        AstBinaryOp op;
-        op.kind = Ast_Kind_Binary_Op;
-        op.token = binop->token;
-        op.operation = Binary_Op_Subscript_Equals;
-        op.left  = ((AstSubscript *) binop->left)->addr;
-        op.right = ((AstSubscript *) binop->left)->expr;
-
-        AstCall* call = binaryop_try_operator_overload(&op, binop->right);
-        if (call == (AstCall *) &node_that_signals_a_yield) YIELD(op.token->pos, "Waiting on potential operator overload.");
+        if (binop->potential_substitute == NULL) {
+            u32 current_checking_level_store = current_checking_level;
+            CHECK(expression, &sub->addr);
+            CHECK(expression, &sub->expr);
+            CHECK(expression, &binop->right);
+            current_checking_level = current_checking_level_store;
+
+            AstBinaryOp *op = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinaryOp), Ast_Kind_Binary_Op);
+            op->token = binop->token;
+            op->operation = Binary_Op_Subscript_Equals;
+            op->left  = ((AstSubscript *) binop->left)->addr;
+            op->right = ((AstSubscript *) binop->left)->expr;
+
+            binop->potential_substitute = op;
+        }
+
+        AstCall* call = binaryop_try_operator_overload(binop->potential_substitute, binop->right);
+        if (call == (AstCall *) &node_that_signals_a_yield) YIELD(binop->token->pos, "Waiting on potential operator overload.");
         if (call != NULL) {
             call->next = binop->next;
             *(AstCall **) pbinop = call;
@@ -1238,6 +1243,8 @@ CheckStatus check_array_literal(AstArrayLiteral* al) {
 
 CheckStatus check_range_literal(AstRangeLiteral** prange) {
     AstRangeLiteral* range = *prange;
+    if (range->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
+
     CHECK(expression, &range->low);
     CHECK(expression, &range->high);
 
@@ -1266,6 +1273,7 @@ CheckStatus check_range_literal(AstRangeLiteral** prange) {
         range->step = *smem.initial_value;
     }
 
+    range->flags |= Ast_Flag_Has_Been_Checked;
     return Check_Success;
 }
 
@@ -1325,17 +1333,20 @@ CheckStatus check_address_of(AstAddressOf** paof) {
 
     AstTyped* expr = (AstTyped *) strip_aliases((AstNode *) aof->expr);
     if (expr->kind == Ast_Kind_Subscript && bh_arr_length(operator_overloads[Binary_Op_Ptr_Subscript]) > 0) {
-        CHECK(expression, &((AstSubscript *) expr)->addr);
-        CHECK(expression, &((AstSubscript *) expr)->expr);
+        if (aof->potential_substitute == NULL) {
+            CHECK(expression, &((AstSubscript *) expr)->addr);
+            CHECK(expression, &((AstSubscript *) expr)->expr);
 
-        AstBinaryOp op;
-        op.kind = Ast_Kind_Binary_Op;
-        op.operation = Binary_Op_Ptr_Subscript;
-        op.left  = ((AstSubscript *) expr)->addr;
-        op.right = ((AstSubscript *) expr)->expr;
-        op.token = aof->token;
+            AstBinaryOp *op = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinaryOp), Ast_Kind_Binary_Op);
+            op->operation = Binary_Op_Ptr_Subscript;
+            op->left  = ((AstSubscript *) expr)->addr;
+            op->right = ((AstSubscript *) expr)->expr;
+            op->token = aof->token;
 
-        AstCall* call = binaryop_try_operator_overload(&op, NULL);
+            aof->potential_substitute = op;
+        }
+
+        AstCall* call = binaryop_try_operator_overload(aof->potential_substitute, NULL);
         if (call == (AstCall *) &node_that_signals_a_yield) YIELD(aof->token->pos, "Waiting for operator overload to possibly resolve.");
         if (call != NULL) {
             call->next = aof->next;
@@ -2566,6 +2577,72 @@ CheckStatus check_constraint_context(ConstraintContext *cc, OnyxFilePos pos) {
     }
 }
 
+CheckStatus check_polyquery(AstPolyQuery *query) {
+    if (query->function_header->scope == NULL)
+        query->function_header->scope = scope_create(context.ast_alloc, query->proc->poly_scope, query->token->pos);
+
+    CheckStatus header_check = check_temp_function_header(query->function_header);
+    if (header_check == Check_Return_To_Symres) return Check_Return_To_Symres;
+
+    b32 solved_something = 0;
+    i32 solved_count = 0; 
+    char *err_msg = NULL;
+    bh_arr_each(AstPolyParam, param, query->proc->poly_params) {
+        AstPolySolution sln;
+        bh_arr_each(AstPolySolution, solved_sln, query->slns) {
+            if (token_equals(param->poly_sym->token, solved_sln->poly_sym->token)) {
+                goto poly_query_done;
+            }
+        }
+
+        // CLEANUP: I think this can go away because it is already done in polymorph.c
+        bh_arr_each(AstPolySolution, known_sln, query->proc->known_slns) {
+            if (token_equals(param->poly_sym->token, known_sln->poly_sym->token)) {
+                sln = *known_sln;
+                goto poly_var_solved;
+            }
+        }
+
+        TypeMatch result = find_polymorphic_sln(&sln, param, query->function_header, query->pp_lookup, query->given, &err_msg);
+
+        switch (result) {
+            case TYPE_MATCH_SUCCESS:
+                goto poly_var_solved;
+
+            case TYPE_MATCH_YIELD:
+            case TYPE_MATCH_FAILED: {
+                if (query->successful_symres) continue;
+
+                if (query->error_on_fail || context.cycle_detected) {
+                    onyx_report_error(query->token->pos, "Error solving for polymorphic variable '%b'.", param->poly_sym->token->text, param->poly_sym->token->length);
+                    if (err_msg != NULL)  onyx_report_error(query->token->pos, "%s", err_msg);
+                    if (query->error_loc) onyx_report_error(query->error_loc->pos, "Here is where the call is located."); // :ErrorMessage
+                }
+
+                return Check_Failed;
+            }
+        }
+
+poly_var_solved:
+        solved_something = 1;
+        bh_arr_push(query->slns, sln);
+        insert_poly_sln_into_scope(query->function_header->scope, &sln);
+
+poly_query_done:
+        solved_count += 1;
+    }
+
+    if (solved_count != bh_arr_length(query->proc->poly_params)) {
+        if (solved_something || query->successful_symres) {
+            return Check_Return_To_Symres;
+        } else {
+            return Check_Failed;
+        }
+    }
+
+    return Check_Complete;
+}
+
 CheckStatus check_node(AstNode* node) {
     switch (node->kind) {
         case Ast_Kind_Function:             return check_function((AstFunction *) node);
@@ -2597,6 +2674,7 @@ void check_entity(Entity* ent) {
         case Entity_Type_Static_If:                cs = check_static_if(ent->static_if); break;
         case Entity_Type_Macro:                    cs = check_macro(ent->macro); break;
         case Entity_Type_Constraint_Check:         cs = check_constraint(ent->constraint); break;
+        case Entity_Type_Polymorph_Query:          cs = check_polyquery(ent->poly_query); break;
 
         case Entity_Type_Expression:
             cs = check_expression(&ent->expr);
index 6d8f94b68bc408cb8653fab375376e75d4bdbc5a..a3fe241d5aca4355e05c106a321f18ae709dad78 100644 (file)
@@ -303,6 +303,14 @@ void add_entities_for_node(bh_arr(Entity *) *target_arr, AstNode* node, Scope* s
             break;
         }
 
+        case Ast_Kind_Polymorph_Query: {
+            ent.type = Entity_Type_Polymorph_Query;
+            ent.state = Entity_State_Check_Types;
+            ent.poly_query = (AstPolyQuery *) node;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
         case Ast_Kind_Static_If: {
             ent.state = Entity_State_Resolve_Symbols;
             ent.type = Entity_Type_Static_If;
index dda7b615f67bf4df9abd4f3b621adff3481338a9..eaa38e6c58118ed06c0e9174ce1c75341fbce99e 100644 (file)
@@ -17,28 +17,34 @@ AstTyped node_that_signals_a_yield = { Ast_Kind_Function, 0 };
 static void ensure_polyproc_cache_is_created(AstPolyProc* pp) {
     if (pp->concrete_funcs == NULL) {
         bh_table_init(global_heap_allocator, pp->concrete_funcs, 16);
+
+        bh_imap_init(&pp->active_queries, global_heap_allocator, 31);
     }
 }
 
-static void insert_poly_slns_into_scope(Scope* scope, bh_arr(AstPolySolution) slns) {
-    bh_arr_each(AstPolySolution, sln, slns) {
-        AstNode *node = NULL;
+void insert_poly_sln_into_scope(Scope* scope, AstPolySolution *sln) {
+    AstNode *node = NULL;
 
-        switch (sln->kind) {
-            case PSK_Type:
-                node = onyx_ast_node_new(context.ast_alloc, sizeof(AstTypeRawAlias), Ast_Kind_Type_Raw_Alias);
-                ((AstTypeRawAlias *) node)->token = sln->poly_sym->token;
-                ((AstTypeRawAlias *) node)->to = sln->type;
-                break;
+    switch (sln->kind) {
+        case PSK_Type:
+            node = onyx_ast_node_new(context.ast_alloc, sizeof(AstTypeRawAlias), Ast_Kind_Type_Raw_Alias);
+            ((AstTypeRawAlias *) node)->token = sln->poly_sym->token;
+            ((AstTypeRawAlias *) node)->to = sln->type;
+            break;
 
-            case PSK_Value:
-                // CLEANUP: Maybe clone this?
-                assert(sln->value->flags & Ast_Flag_Comptime);
-                node = (AstNode *) sln->value;
-                break;
-        }
+        case PSK_Value:
+            // CLEANUP: Maybe clone this?
+            assert(sln->value->flags & Ast_Flag_Comptime);
+            node = (AstNode *) sln->value;
+            break;
+    }
+
+    symbol_introduce(scope, sln->poly_sym->token, node);
+}
 
-        symbol_introduce(scope, sln->poly_sym->token, node);
+static void insert_poly_slns_into_scope(Scope* scope, bh_arr(AstPolySolution) slns) {
+    bh_arr_each(AstPolySolution, sln, slns) {
+        insert_poly_sln_into_scope(scope, sln);
     }
 }
 
@@ -382,14 +388,14 @@ static PolySolveResult solve_poly_type(AstNode* target, AstType* type_expr, Type
 // and solve for the argument that matches the parameter. This is needed because polymorphic
 // procedure resolution has to happen before the named arguments are placed in their correct
 // positions.
-static AstTyped* lookup_param_in_arguments(AstPolyProc* pp, AstPolyParam* param, Arguments* args, char** err_msg) {
+static AstTyped* lookup_param_in_arguments(AstFunction* func, AstPolyParam* param, Arguments* args, char** err_msg) {
     bh_arr(AstTyped *) arg_arr = args->values;
     bh_arr(AstNamedValue *) named_values = args->named_values;
 
     // NOTE: This check is safe because currently the arguments given without a name
     // always map to the beginning indidies of the argument array.
     if (param->idx >= (u64) bh_arr_length(arg_arr)) {
-        OnyxToken* param_name = pp->base_func->params[param->idx].local->token;
+        OnyxToken* param_name = func->params[param->idx].local->token;
 
         bh_arr_each(AstNamedValue *, named_value, named_values) {
             if (token_equals(param_name, (*named_value)->token)) {
@@ -411,14 +417,14 @@ static AstTyped* lookup_param_in_arguments(AstPolyProc* pp, AstPolyParam* param,
 // information. It is asssumed that the "param" is of kind PPK_Poly_Type. This function uses
 // either the arguments provided, or a function type to compare against to pattern match for
 // the type that the parameter but be.
-static void solve_for_polymorphic_param_type(PolySolveResult* resolved, AstPolyProc* pp, AstPolyParam* param, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
+static void solve_for_polymorphic_param_type(PolySolveResult* resolved, AstFunction* func, AstPolyParam* param, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
     Type* actual_type = NULL;
 
     switch (pp_lookup) {
         case PPLM_By_Arguments: {
             Arguments* args = (Arguments *) actual;
 
-            AstTyped* typed_param = lookup_param_in_arguments(pp, param, args, err_msg);
+            AstTyped* typed_param = lookup_param_in_arguments(func, param, args, err_msg);
             if (typed_param == NULL) return;
 
             actual_type = resolve_expression_type(typed_param);
@@ -451,14 +457,14 @@ static void solve_for_polymorphic_param_type(PolySolveResult* resolved, AstPolyP
 // CLEANUP: This function is kind of gross at the moment, because it handles different cases for
 // the argument kind. When type expressions (type_expr) become first-class types in the type
 // system, this code should be able to be a lot cleaner.
-static void solve_for_polymorphic_param_value(PolySolveResult* resolved, AstPolyProc* pp, AstPolyParam* param, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
+static void solve_for_polymorphic_param_value(PolySolveResult* resolved, AstFunction* func, AstPolyParam* param, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
     if (pp_lookup != PPLM_By_Arguments) {
         *err_msg = "Function type cannot be used to solved for baked parameter value.";
         return;
     }
 
     Arguments* args = (Arguments *) actual;
-    AstTyped* value = lookup_param_in_arguments(pp, param, args, err_msg);
+    AstTyped* value = lookup_param_in_arguments(func, param, args, err_msg);
     if (value == NULL) return;
 
     // HACK: Storing the original value because if this was an AstArgument, we need to flag
@@ -469,7 +475,8 @@ static void solve_for_polymorphic_param_value(PolySolveResult* resolved, AstPoly
         value = ((AstArgument *) value)->value;
     }
 
-    if (param->type_expr == (AstType *) &basic_type_type_expr) {
+    AstType *param_type_expr = func->params[param->idx].local->type_node;
+    if (param_type_expr == (AstType *) &basic_type_type_expr) {
         if (!node_is_type((AstNode *) value)) {
             if (err_msg) *err_msg = "Expected type expression.";
             return;
@@ -489,8 +496,13 @@ static void solve_for_polymorphic_param_value(PolySolveResult* resolved, AstPoly
         }
 
         if (param->type == NULL)
-            param->type = type_build_from_ast(context.ast_alloc, param->type_expr);
-        assert(param->type);
+            param->type = type_build_from_ast(context.ast_alloc, param_type_expr);
+
+        if (param->type == NULL) {
+            flag_to_yield = 1;
+            *err_msg = "Waiting to know type for polymorphic value.";
+            return;
+        }
 
         AstTyped* value_to_use = value;
         if (value->kind == Ast_Kind_Macro) {
@@ -501,7 +513,7 @@ static void solve_for_polymorphic_param_value(PolySolveResult* resolved, AstPoly
         if (tm == TYPE_MATCH_FAILED) {
             if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator,
                     "The procedure '%s' expects a value of type '%s' for baked %d%s parameter, got '%s'.",
-                    get_function_name(pp->base_func),
+                    get_function_name(func),
                     type_get_name(param->type),
                     param->idx + 1,
                     bh_num_suffix(param->idx + 1),
@@ -519,10 +531,65 @@ static void solve_for_polymorphic_param_value(PolySolveResult* resolved, AstPoly
     }
 }
 
+TypeMatch find_polymorphic_sln(AstPolySolution *out, AstPolyParam *param, AstFunction *func, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
+    // NOTE: Solve for the polymorphic parameter's value
+    PolySolveResult resolved = { PSK_Undefined };
+    switch (param->kind) {
+        case PPK_Poly_Type:   solve_for_polymorphic_param_type (&resolved, func, param, pp_lookup, actual, err_msg); break;
+        case PPK_Baked_Value: solve_for_polymorphic_param_value(&resolved, func, param, pp_lookup, actual, err_msg); break;
+
+        default: if (err_msg) *err_msg = "Invalid polymorphic parameter kind. This is a compiler bug.";
+    }
+
+    if (flag_to_yield) {
+        flag_to_yield = 0;
+        return TYPE_MATCH_YIELD;
+    }
+
+    switch (resolved.kind) {
+        case PSK_Type:
+            out->kind = PSK_Type;
+            out->poly_sym = param->poly_sym;
+            out->type = resolved.actual;
+            return TYPE_MATCH_SUCCESS;
+
+        case PSK_Value:
+            out->kind = PSK_Value;
+            out->poly_sym = param->poly_sym;
+            out->value = resolved.value;
+            return TYPE_MATCH_SUCCESS;
+
+        case PSK_Undefined:
+        default:
+            // NOTE: If no error message has been assigned to why this polymorphic parameter
+            // resolution was unsuccessful, provide a basic dummy one.
+            if (err_msg && *err_msg == NULL)
+                *err_msg = bh_aprintf(global_scratch_allocator,
+                    "Unable to solve for polymorphic variable '%b'.",
+                    param->poly_sym->token->text,
+                    param->poly_sym->token->length);
+
+            out->kind = PSK_Undefined;
+            return TYPE_MATCH_FAILED;
+    }
+}
+
 // NOTE: The job of this function is to take a polymorphic procedure, as well as a method of
 // solving for the polymorphic variables, in order to return an array of the solutions for all
 // of the polymorphic variables.
-static bh_arr(AstPolySolution) find_polymorphic_slns(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
+static bh_arr(AstPolySolution) find_polymorphic_slns(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken *tkn, b32 necessary) {
+    if (bh_imap_has(&pp->active_queries, (u64) actual)) {
+        AstPolyQuery *query = (AstPolyQuery *) bh_imap_get(&pp->active_queries, (u64) actual);
+        assert(query->kind == Ast_Kind_Polymorph_Query);
+        assert(query->entity);
+
+        if (query->entity->state == Entity_State_Finalized) return query->slns;
+        if (query->entity->state == Entity_State_Failed)    return NULL;
+
+        flag_to_yield = 1;
+        return NULL;
+    }
+
     bh_arr(AstPolySolution) slns = NULL;
     bh_arr_new(global_heap_allocator, slns, bh_arr_length(pp->poly_params));
 
@@ -531,64 +598,22 @@ static bh_arr(AstPolySolution) find_polymorphic_slns(AstPolyProc* pp, PolyProcLo
     // empty and these solutions will be used.
     bh_arr_each(AstPolySolution, known_sln, pp->known_slns) bh_arr_push(slns, *known_sln);
 
-    bh_arr_each(AstPolyParam, param, pp->poly_params) {
-
-        // NOTE: First check to see if this polymorphic variable was already specified in the
-        // known solutions.
-        b32 already_solved = 0;
-        bh_arr_each(AstPolySolution, known_sln, pp->known_slns) {
-            if (token_equals(param->poly_sym->token, known_sln->poly_sym->token)) {
-                already_solved = 1;
-                break;
-            }
-        }
-        if (already_solved) continue;
-
-        // NOTE: Solve for the polymorphic parameter's value
-        PolySolveResult resolved = { PSK_Undefined };
-        switch (param->kind) {
-            case PPK_Poly_Type:   solve_for_polymorphic_param_type (&resolved, pp, param, pp_lookup, actual, err_msg); break;
-            case PPK_Baked_Value: solve_for_polymorphic_param_value(&resolved, pp, param, pp_lookup, actual, err_msg); break;
-
-            default: if (err_msg) *err_msg = "Invalid polymorphic parameter kind. This is a compiler bug.";
-        }
-
-        if (flag_to_yield) goto sln_not_found;
-        
-        switch (resolved.kind) {
-            case PSK_Undefined:
-                // NOTE: If no error message has been assigned to why this polymorphic parameter
-                // resolution was unsuccessful, provide a basic dummy one.
-                if (err_msg && *err_msg == NULL)
-                    *err_msg = bh_aprintf(global_scratch_allocator,
-                        "Unable to solve for polymorphic variable '%b'.",
-                        param->poly_sym->token->text,
-                        param->poly_sym->token->length);
-
-                goto sln_not_found;
-
-            case PSK_Type:
-                bh_arr_push(slns, ((AstPolySolution) {
-                    .kind     = PSK_Type,
-                    .poly_sym = param->poly_sym,
-                    .type     = resolved.actual,
-                }));
-                break;
-
-            case PSK_Value:
-                bh_arr_push(slns, ((AstPolySolution) {
-                    .kind     = PSK_Value,
-                    .poly_sym = param->poly_sym,
-                    .value    = resolved.value,
-                }));
-                break;
-        }
-    }
-
-    return slns;
-
-    sln_not_found:
-    bh_arr_free(slns);
+    AstPolyQuery *query = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyQuery), Ast_Kind_Polymorph_Query);
+    query->token = pp->token;
+    query->proc = pp;
+    query->pp_lookup = pp_lookup;
+    query->given = actual;
+    query->error_loc = tkn;
+    query->slns = slns;
+    query->function_header = clone_function_header(context.ast_alloc, pp->base_func);
+    query->function_header->flags |= Ast_Flag_Header_Check_No_Error;
+    query->error_on_fail = necessary;
+    query->successful_symres = 1;
+
+    bh_imap_put(&pp->active_queries, (u64) actual, (u64) query);
+    add_entities_for_node(NULL, (AstNode *) query, NULL, NULL);
+
+    flag_to_yield = 1;
     return NULL;
 }
 
@@ -598,8 +623,7 @@ static bh_arr(AstPolySolution) find_polymorphic_slns(AstPolyProc* pp, PolyProcLo
 AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken* tkn) {
     ensure_polyproc_cache_is_created(pp);
 
-    char *err_msg = NULL;
-    bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, &err_msg);
+    bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, tkn, 1);
     if (slns == NULL) {
         if (flag_to_yield) {
             flag_to_yield = 0;
@@ -607,15 +631,10 @@ AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lo
             return (AstFunction *) &node_that_signals_a_yield;
         }
 
-        if (err_msg != NULL) onyx_report_error(tkn->pos, err_msg);
-        else                 onyx_report_error(tkn->pos, "Some kind of error occured when generating a polymorphic procedure. You hopefully will not see this");
-
         return NULL;
     }
 
     AstFunction* result = polymorphic_proc_solidify(pp, slns, tkn);
-    
-    bh_arr_free(slns);
     return result;
 }
 
@@ -703,7 +722,12 @@ AstNode* polymorphic_proc_try_solidify(AstPolyProc* pp, bh_arr(AstPolySolution)
 }
 
 AstFunction* polymorphic_proc_build_only_header(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual) {
-    bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, NULL);
+    ensure_polyproc_cache_is_created(pp);
+    bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, NULL, 0);
+    if (flag_to_yield) {
+        flag_to_yield = 0;
+        return (AstFunction *) &node_that_signals_a_yield;
+    }
     if (slns == NULL) return NULL;
 
     ensure_polyproc_cache_is_created(pp);
index 6005785b8dcdcfe103292cfc376d2c45ca8af2f4..a96a0237bb1a66795af295d22ccdf763adcfe26a 100644 (file)
@@ -7,6 +7,7 @@
 // Variables used during the symbol resolution phase.
 static Scope*       curr_scope    = NULL;
 static b32 report_unresolved_symbols = 1;
+static b32 resolved_a_symbol         = 0;
 
 // Everything related to waiting on is imcomplete at the moment.
 static Entity* waiting_on         = NULL;
@@ -58,6 +59,7 @@ static SymresStatus symres_struct_defaults(AstType* st);
 static SymresStatus symres_static_if(AstIf* static_if);
 static SymresStatus symres_macro(AstMacro* macro);
 static SymresStatus symres_constraint(AstConstraint* constraint);
+static SymresStatus symres_polyquery(AstPolyQuery *query);
 
 static void scope_enter(Scope* new_scope) {
     curr_scope = new_scope;
@@ -85,6 +87,7 @@ static SymresStatus symres_symbol(AstNode** symbol_node) {
 
     } else {
         *symbol_node = res;
+        resolved_a_symbol = 1;
     }
 
     return Symres_Success;
@@ -1134,19 +1137,6 @@ static SymresStatus symres_struct_defaults(AstType* t) {
 static SymresStatus symres_polyproc(AstPolyProc* pp) {
     pp->flags |= Ast_Flag_Comptime;
     pp->poly_scope = curr_scope;
-
-    bh_arr_each(AstPolyParam, param, pp->poly_params) {
-        if (param->kind != PPK_Baked_Value) continue;
-
-        // FIX: Looking up symbols immediately in the type of the baked value does not always work
-        // because I think the following should be possible:
-        //
-        //     baked_proc :: (x: $T, $f: (T) -> T) -> T ...
-        //
-        // The type of 'f' depends on resolving the value for the polyvar 'T'.
-        SYMRES(type, &param->type_expr);
-    }
-
     return Symres_Success;
 }
 
@@ -1297,6 +1287,37 @@ static SymresStatus symres_constraint(AstConstraint* constraint) {
     return Symres_Success;
 }
 
+static SymresStatus symres_polyquery(AstPolyQuery *query) {
+    query->successful_symres = 0;
+
+    if (query->function_header->scope == NULL)
+        query->function_header->scope = scope_create(context.ast_alloc, query->proc->poly_scope, query->token->pos);
+
+    scope_enter(query->function_header->scope);
+
+    u32 idx = 0;
+    bh_arr_each(AstParam, param, query->function_header->params) {
+        bh_arr_each(AstPolyParam, pp, query->proc->poly_params) {
+            if (pp->kind == PPK_Baked_Value && pp->idx == idx) goto skip_introducing_symbol;
+        }
+        symbol_introduce(curr_scope, param->local->token, (AstNode *) param->local);
+
+    skip_introducing_symbol:
+        if (param->local->type_node != NULL) {
+            resolved_a_symbol = 0;
+            symres_type(&param->local->type_node);
+            onyx_clear_errors();
+
+            if (resolved_a_symbol) query->successful_symres = 1;
+        }
+
+        idx++;
+    }
+
+    scope_leave();
+    return Symres_Success;
+}
+
 void symres_entity(Entity* ent) {
     Scope* old_scope = NULL;
     if (ent->scope) {
@@ -1346,6 +1367,7 @@ void symres_entity(Entity* ent) {
         case Entity_Type_Process_Directive:       ss = symres_process_directive((AstNode *) ent->expr); break;
         case Entity_Type_Macro:                   ss = symres_macro(ent->macro); break;
         case Entity_Type_Constraint_Check:        ss = symres_constraint(ent->constraint); break;
+        case Entity_Type_Polymorph_Query:         ss = symres_polyquery(ent->poly_query); break;
 
         default: break;
     }
index 77360978af0555f65996a6cf6d0bb96cd95ab965..d0a18e473c876edba04e16b7d0a49a68534011d7 100644 (file)
@@ -291,8 +291,11 @@ Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) {
         case Ast_Kind_Array_Type: {
             AstArrayType* a_node = (AstArrayType *) type_node;
 
+            Type *elem_type = type_build_from_ast(alloc, a_node->elem);
+            if (elem_type == NULL)  return NULL;
+
             Type* a_type = type_create(Type_Kind_Array, alloc, 0);
-            a_type->Array.elem = type_build_from_ast(alloc, a_node->elem);
+            a_type->Array.elem = elem_type;
 
             u32 count = 0;
             if (a_node->count_expr) {
@@ -303,7 +306,7 @@ Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) {
                     a_node->count_expr = ((AstUnaryOp *) a_node)->expr;
                 }
 
-                resolve_expression_type((AstTyped *) a_node->count_expr);
+                resolve_expression_type(a_node->count_expr);
 
                 // NOTE: Currently, the count_expr has to be an I32 literal
                 if (a_node->count_expr->type->kind != Type_Kind_Basic
@@ -313,7 +316,7 @@ Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) {
                     return NULL;
                 }
 
-                count = get_expression_integer_value((AstTyped *) a_node->count_expr);
+                count = get_expression_integer_value(a_node->count_expr);
             }
 
             a_type->Array.count = count;
index ddd436a58b3b5b230f13d898d7372f9ec0c083b7..ed541dfbd88ffa4994b36ca172b0a92eab434097 100644 (file)
@@ -522,8 +522,7 @@ AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* c
             AstPolyProc* pp = (AstPolyProc *) macro->body;
             ensure_polyproc_cache_is_created(pp);
 
-            char* err_msg=NULL;
-            bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, PPLM_By_Arguments, args, &err_msg);
+            bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, PPLM_By_Arguments, args, callsite, error_if_failed);
 
             if (slns == NULL) {
                 if (flag_to_yield) {
@@ -531,8 +530,6 @@ AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* c
                     return (AstFunction *) &node_that_signals_a_yield;
                 }
 
-                if (callsite) onyx_report_error(callsite->pos, err_msg);
-
                 return NULL;
             }