From: Brendan Hansen Date: Mon, 15 Nov 2021 02:04:57 +0000 (-0600) Subject: polymorphism is even more powerful now! X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=b6be7b1e33e42fe6d399eaf5b896c4b1c3a791ce;p=onyx.git polymorphism is even more powerful now! --- diff --git a/include/astnodes.h b/include/astnodes.h index 097c9740..277a0186 100644 --- a/include/astnodes.h +++ b/include/astnodes.h @@ -809,11 +809,21 @@ struct AstType { AstType_base; }; struct AstBasicType { AstType_base; Type* basic_type; }; struct AstPointerType { AstType_base; AstType* elem; }; -struct AstFunctionType { AstType_base; AstType* return_type; u64 param_count; AstType* params[]; }; struct AstArrayType { AstType_base; AstType* elem; AstTyped *count_expr; }; struct AstSliceType { AstType_base; AstType* elem; }; struct AstDynArrType { AstType_base; AstType* elem; }; struct AstVarArgType { AstType_base; AstType* elem; }; +struct AstFunctionType { + AstType_base; + + // Used in a rare case in solve_for_polymorphic_param_type. + Type *partial_function_type; + + AstType* return_type; + + u64 param_count; + AstType* params[]; +}; struct AstStructType { AstType_base; char *name; @@ -1512,6 +1522,7 @@ typedef enum TypeMatch { TYPE_MATCH_SUCCESS, TYPE_MATCH_FAILED, TYPE_MATCH_YIELD, + TYPE_MATCH_SPECIAL, // Only used for nest polymorph function lookups } TypeMatch; #define unify_node_and_type(node, type) (unify_node_and_type_((node), (type), 1)) TypeMatch unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent); diff --git a/src/checker.c b/src/checker.c index 8225cba5..bb586666 100644 --- a/src/checker.c +++ b/src/checker.c @@ -2609,9 +2609,12 @@ CheckStatus check_polyquery(AstPolyQuery *query) { case TYPE_MATCH_SUCCESS: goto poly_var_solved; + case TYPE_MATCH_SPECIAL: + return Check_Yield_Macro; + case TYPE_MATCH_YIELD: case TYPE_MATCH_FAILED: { - if (query->successful_symres) continue; + if (query->successful_symres || solved_something) 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); @@ -2699,12 +2702,14 @@ void check_entity(Entity* ent) { default: break; } - if (cs == Check_Success) ent->state = Entity_State_Code_Gen; - if (cs == Check_Complete) ent->state = Entity_State_Finalized; - if (cs == Check_Return_To_Symres) ent->state = Entity_State_Resolve_Symbols; - if (cs == Check_Failed) ent->state = Entity_State_Failed; - if (cs == Check_Yield_Macro) ent->macro_attempts++; - else { + switch (cs) { + case Check_Yield_Macro: ent->macro_attempts++; break; + case Check_Success: ent->state = Entity_State_Code_Gen; goto clear_attempts; + case Check_Complete: ent->state = Entity_State_Finalized; goto clear_attempts; + case Check_Return_To_Symres: ent->state = Entity_State_Resolve_Symbols; goto clear_attempts; + case Check_Failed: ent->state = Entity_State_Failed; goto clear_attempts; + + clear_attempts: ent->macro_attempts = 0; ent->micro_attempts = 0; } diff --git a/src/polymorph.c b/src/polymorph.c index eaa38e6c..51f6da59 100644 --- a/src/polymorph.c +++ b/src/polymorph.c @@ -9,6 +9,11 @@ // checker ever gets multi-threaded, this would have to become a threadlocal variable. static b32 flag_to_yield = 0; +// This flag is used in the very special case that you are passing a polymorphic procedure +// to a polymorphic procedure, and you have enough information to instantiate said procedure +// in order to resolve the type of one of the return values. +static b32 doing_nested_polymorph_lookup = 0; + // The name is pretty self-descriptive, but this is a node that is returned from things // like polymorphic_proc_lookup when it is determined that everything works so far, but // the caller must yield in order to finish checking this polymorphic procedure. @@ -413,6 +418,28 @@ static AstTyped* lookup_param_in_arguments(AstFunction* func, AstPolyParam* para return NULL; } +static AstTyped* try_lookup_based_on_partial_function_type(AstPolyProc *pp, AstFunctionType *ft) { + if (ft->partial_function_type == NULL) { + AstType *old_return_type = ft->return_type; + ft->return_type = (AstType *) &basic_type_void; + ft->partial_function_type = type_build_from_ast(context.ast_alloc, (AstType *) ft); + assert(ft->partial_function_type); + ft->return_type = old_return_type; + } + + AstTyped *result = (AstTyped *) polymorphic_proc_lookup(pp, PPLM_By_Function_Type, ft->partial_function_type, pp->token); + if (result && result->type == NULL) { + doing_nested_polymorph_lookup = 1; + result = NULL; + } + if (result == &node_that_signals_a_yield) { + doing_nested_polymorph_lookup = 1; + result = NULL; + } + + return result; +} + // NOTE: The job of this function is to solve for type of AstPolySolution using the provided // 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 @@ -427,6 +454,30 @@ static void solve_for_polymorphic_param_type(PolySolveResult* resolved, AstFunct AstTyped* typed_param = lookup_param_in_arguments(func, param, args, err_msg); if (typed_param == NULL) return; + // CLEANUP FIXME HACK TODO GROSS + if (typed_param->kind == Ast_Kind_Argument) { + AstTyped* potential = ((AstArgument *) typed_param)->value; + if (potential->kind == Ast_Kind_Polymorphic_Proc) { + if (param->idx < (u32) bh_arr_length(func->params)) { + AstType *param_type = func->params[param->idx].local->type_node; + if (param_type->kind == Ast_Kind_Function_Type) { + AstFunctionType *ft = (AstFunctionType *) param_type; + b32 all_types = 1; + fori (i, 0, (i32) ft->param_count) { + if (!node_is_type((AstNode *) ft->params[i])) { + all_types = 0; + break; + } + } + + if (all_types) { + typed_param = try_lookup_based_on_partial_function_type((AstPolyProc *) potential, ft); + } + } + } + } + } + actual_type = resolve_expression_type(typed_param); if (actual_type == NULL) return; @@ -475,6 +526,7 @@ static void solve_for_polymorphic_param_value(PolySolveResult* resolved, AstFunc value = ((AstArgument *) value)->value; } + Type* param_type = NULL; 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)) { @@ -495,10 +547,8 @@ static void solve_for_polymorphic_param_value(PolySolveResult* resolved, AstFunc return; } - if (param->type == NULL) - param->type = type_build_from_ast(context.ast_alloc, param_type_expr); - - if (param->type == NULL) { + 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; @@ -509,12 +559,12 @@ static void solve_for_polymorphic_param_value(PolySolveResult* resolved, AstFunc value_to_use = (AstTyped *) get_function_from_node((AstNode *) value); } - TypeMatch tm = unify_node_and_type(&value_to_use, param->type); + TypeMatch tm = unify_node_and_type(&value_to_use, param_type); 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(func), - type_get_name(param->type), + type_get_name(param_type), param->idx + 1, bh_num_suffix(param->idx + 1), node_get_type_name(value_to_use)); @@ -541,6 +591,11 @@ TypeMatch find_polymorphic_sln(AstPolySolution *out, AstPolyParam *param, AstFun default: if (err_msg) *err_msg = "Invalid polymorphic parameter kind. This is a compiler bug."; } + if (doing_nested_polymorph_lookup) { + doing_nested_polymorph_lookup = 0; + return TYPE_MATCH_SPECIAL; + } + if (flag_to_yield) { flag_to_yield = 0; return TYPE_MATCH_YIELD; @@ -627,7 +682,6 @@ AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lo if (slns == NULL) { if (flag_to_yield) { flag_to_yield = 0; - bh_arr_free(slns); return (AstFunction *) &node_that_signals_a_yield; } diff --git a/src/symres.c b/src/symres.c index a96a0237..85690d2e 100644 --- a/src/symres.c +++ b/src/symres.c @@ -181,13 +181,13 @@ static SymresStatus symres_type(AstType** type) { case Ast_Kind_Function_Type: { AstFunctionType* ftype = (AstFunctionType *) *type; - SYMRES(type, &ftype->return_type); - if (ftype->param_count > 0) { fori (i, 0, (i64) ftype->param_count) { SYMRES(type, &ftype->params[i]); } } + + SYMRES(type, &ftype->return_type); break; } diff --git a/tests/lazy_iterators b/tests/lazy_iterators index 47af0b1f..2c7ab48d 100644 --- a/tests/lazy_iterators +++ b/tests/lazy_iterators @@ -1,3 +1,9 @@ +Closing the count iterator... +54 +56 +58 +60 +62 Starting the iteration... 54 56 diff --git a/tests/lazy_iterators.onyx b/tests/lazy_iterators.onyx index 98d08cc3..700bf1b5 100644 --- a/tests/lazy_iterators.onyx +++ b/tests/lazy_iterators.onyx @@ -36,7 +36,6 @@ count_iterator :: (lo: i32, hi: i32, step := 1) -> Iterator(i32) { main :: (args: [] cstr) { // Hopefully soon, the following will be possible. -/* quick_iterator := count_iterator(1, 10) |> iter.map((x) => x * 2) @@ -48,7 +47,6 @@ main :: (args: [] cstr) { for v: quick_iterator { println(v); } -*/ iterator := count_iterator(1, 10) |> iter.map((x: i32) -> i32 { return x * 2; })