From ba0dcf05bf179e8b7d2dbea789e14faafdf9b26b Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Sun, 14 Nov 2021 17:54:22 -0600 Subject: [PATCH] completely revamped the polymorph solving system --- CHANGELOG | 2 + include/astnodes.h | 54 ++++++++++-- src/astnodes.c | 4 +- src/checker.c | 170 ++++++++++++++++++++++++++---------- src/entities.c | 8 ++ src/polymorph.c | 210 +++++++++++++++++++++++++-------------------- src/symres.c | 48 ++++++++--- src/types.c | 9 +- src/utils.c | 5 +- 9 files changed, 343 insertions(+), 167 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 308b4ea5..a4a80bef 100644 --- 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: diff --git a/include/astnodes.h b/include/astnodes.h index ddb2f36f..097c9740 100644 --- a/include/astnodes.h +++ b/include/astnodes.h @@ -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); diff --git a/src/astnodes.c b/src/astnodes.c index b5a8d3c7..a7bd73e1 100644 --- a/src/astnodes.c +++ b/src/astnodes.c @@ -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; diff --git a/src/checker.c b/src/checker.c index 511747e5..8225cba5 100644 --- a/src/checker.c +++ b/src/checker.c @@ -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); diff --git a/src/entities.c b/src/entities.c index 6d8f94b6..a3fe241d 100644 --- a/src/entities.c +++ b/src/entities.c @@ -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; diff --git a/src/polymorph.c b/src/polymorph.c index dda7b615..eaa38e6c 100644 --- a/src/polymorph.c +++ b/src/polymorph.c @@ -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); diff --git a/src/symres.c b/src/symres.c index 6005785b..a96a0237 100644 --- a/src/symres.c +++ b/src/symres.c @@ -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, ¶m->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(¶m->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; } diff --git a/src/types.c b/src/types.c index 77360978..d0a18e47 100644 --- a/src/types.c +++ b/src/types.c @@ -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; diff --git a/src/utils.c b/src/utils.c index ddd436a5..ed541dfb 100644 --- a/src/utils.c +++ b/src/utils.c @@ -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; } -- 2.25.1