From: Brendan Hansen Date: Sat, 16 Jan 2021 18:52:52 +0000 (-0600) Subject: very hacky implementation of baked compile-time procedures X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=bcaae74df1a69c3f7b9cd1020870444479b4dc5a;p=onyx.git very hacky implementation of baked compile-time procedures --- diff --git a/bin/onyx b/bin/onyx index ea7ee4c9..29aee206 100755 Binary files a/bin/onyx and b/bin/onyx differ diff --git a/include/onyxastnodes.h b/include/onyxastnodes.h index dd93a7f7..d290e501 100644 --- a/include/onyxastnodes.h +++ b/include/onyxastnodes.h @@ -474,7 +474,7 @@ 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 AstArgument { AstTyped_base; AstTyped *value; VarArgKind va_kind; }; +struct AstArgument { AstTyped_base; AstTyped *value; VarArgKind va_kind; b32 is_baked : 1; }; struct AstAddressOf { AstTyped_base; AstTyped *expr; }; struct AstDereference { AstTyped_base; AstTyped *expr; }; struct AstArrayAccess { AstTyped_base; AstTyped *addr; AstTyped *expr; u64 elem_size; }; @@ -748,6 +748,11 @@ struct AstFunction { OnyxToken* name; + + // NOTE: This is NULL, unless this function was generated from a polymorphic + // procedure call. Then it is set to the token of the call node. + OnyxToken* generated_from; + union { // NOTE: Used when a function is exported with a specific name OnyxToken* exported_name; @@ -760,7 +765,42 @@ struct AstFunction { }; }; }; + +struct AstOverloadedFunction { + AstTyped_base; + + bh_arr(AstTyped *) overloads; +}; + +struct AstPackage { + AstNode_base; + + Package* package; +}; + +// +// Polymorphic procedures +// + +typedef enum PolyParamKind { + // Dont love these names + PPK_Undefined, + PPK_Poly_Type, + PPK_Baked_Value, +} PolyParamKind; + +typedef enum PolySolutionKind { + PSK_Undefined, + PSK_Type, + PSK_Value, +} PolySolutionKind; + struct AstPolyParam { + PolyParamKind kind; + + // The parameter index where the polymorphic variable occurs. + u32 idx; + // The symbol node that represents the polymorphic variable. AstNode* poly_sym; @@ -770,16 +810,10 @@ struct AstPolyParam { // symbol. AstType* type_expr; - // The parameter index where the polymorphic variable occurs. - u64 idx; + // Used for baked values. The expected type of the parameter. + Type* type; }; -typedef enum PolySolutionKind { - PSK_Undefined, - PSK_Type, - PSK_Value, -} PolySolutionKind; - struct AstPolySolution { PolySolutionKind kind; AstNode* poly_sym; @@ -794,10 +828,12 @@ struct AstPolySolution { AstTyped* value; }; }; + struct AstSolidifiedFunction { AstFunction* func; Scope* poly_scope; }; + struct AstPolyProc { AstNode_base; @@ -809,16 +845,6 @@ struct AstPolyProc { AstFunction* base_func; bh_table(AstSolidifiedFunction) concrete_funcs; }; -struct AstOverloadedFunction { - AstTyped_base; - - bh_arr(AstTyped *) overloads; -}; -struct AstPackage { - AstNode_base; - - Package* package; -}; extern AstNode empty_node; @@ -1003,6 +1029,7 @@ b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg); void arguments_ensure_length(Arguments* args, u32 count); void arguments_clone(Arguments* dest, Arguments* src); void arguments_deep_clone(bh_allocator a, Arguments* dest, Arguments* src); +void arguments_removed_baked(Arguments* args); typedef enum PolyProcLookupMethod { PPLM_By_Call, diff --git a/onyx.exe b/onyx.exe index c405dd6f..e409689d 100644 Binary files a/onyx.exe and b/onyx.exe differ diff --git a/src/onyxastnodes.c b/src/onyxastnodes.c index b2a2eb6e..85db0d23 100644 --- a/src/onyxastnodes.c +++ b/src/onyxastnodes.c @@ -706,4 +706,22 @@ void arguments_deep_clone(bh_allocator a, Arguments* dest, Arguments* src) { bh_arr_each(AstTyped *, val, src->values) bh_arr_push(dest->values, (AstTyped *) ast_clone(a, (AstNode *) *val)); +} + +void arguments_removed_baked(Arguments* args) { + fori (i, 0, bh_arr_length(args->values)) { + if (args->values[i]->kind != Ast_Kind_Argument) continue; + if (!((AstArgument *) args->values[i])->is_baked) continue; + + bh_arr_deleten(args->values, i, 1); + i--; + } + + fori (i, 0, bh_arr_length(args->named_values)) { + if (args->named_values[i]->value->kind != Ast_Kind_Argument) continue; + if (!((AstArgument *) args->named_values[i]->value)->is_baked) continue; + + bh_arr_deleten(args->named_values, i, 1); + i--; + } } \ No newline at end of file diff --git a/src/onyxchecker.c b/src/onyxchecker.c index b6f90a59..8e5ed61d 100644 --- a/src/onyxchecker.c +++ b/src/onyxchecker.c @@ -413,6 +413,22 @@ CheckStatus check_argument(AstArgument** parg) { return Check_Success; } +static i32 non_baked_argument_count(Arguments* args) { + i32 count = 0; + + bh_arr_each(AstTyped *, actual, args->values) { + assert((*actual)->kind == Ast_Kind_Argument); + if (!((AstArgument *) (*actual))->is_baked) count++; + } + + bh_arr_each(AstNamedValue *, named_value, args->named_values) { + assert((*named_value)->value->kind == Ast_Kind_Argument); + if (!((AstArgument *) (*named_value)->value)->is_baked) count++; + } + + return count; +} + typedef enum ArgState { AS_Expecting_Exact, AS_Expecting_Typed_VA, @@ -455,6 +471,7 @@ CheckStatus check_call(AstCall* call) { if (call->callee == NULL) return Check_Error; callee = (AstFunction *) call->callee; + arguments_removed_baked(&call->args); } // NOTE: Build callee's type @@ -472,26 +489,22 @@ CheckStatus check_call(AstCall* call) { } - { - // CLEANUP maybe make function_get_expected_arguments? - i32 non_vararg_param_count = (i32) callee->type->Function.param_count; - if (non_vararg_param_count > 0 && - callee->type->Function.params[callee->type->Function.param_count - 1] == builtin_vararg_type_type) - non_vararg_param_count--; + // CLEANUP maybe make function_get_expected_arguments? + i32 non_vararg_param_count = (i32) callee->type->Function.param_count; + if (non_vararg_param_count > 0 && + callee->type->Function.params[callee->type->Function.param_count - 1] == builtin_vararg_type_type) + non_vararg_param_count--; - i32 arg_count = bh_max( - bh_arr_length(call->args.values) + bh_arr_length(call->args.named_values), - non_vararg_param_count); + i32 arg_count = bh_max(non_vararg_param_count, non_baked_argument_count(&call->args)); - arguments_ensure_length(&call->args, arg_count); + arguments_ensure_length(&call->args, arg_count); - char* err_msg = NULL; - fill_in_arguments(&call->args, (AstNode *) callee, &err_msg); + char* err_msg = NULL; + fill_in_arguments(&call->args, (AstNode *) callee, &err_msg); - if (err_msg != NULL) { - onyx_report_error(call->token->pos, err_msg); - return Check_Error; - } + if (err_msg != NULL) { + onyx_report_error(call->token->pos, err_msg); + return Check_Error; } bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values; @@ -607,7 +620,7 @@ type_checking_done: return Check_Error; } - if (arg_pos < (u32) bh_arr_length(arg_arr)) { + if (arg_pos < arg_count) { onyx_report_error(call->token->pos, "Too many arguments to function call."); return Check_Error; } @@ -1345,13 +1358,14 @@ CheckStatus check_align_of(AstAlignOf* ao) { CheckStatus check_expression(AstTyped** pexpr) { AstTyped* expr = *pexpr; if (expr->kind > Ast_Kind_Type_Start && expr->kind < Ast_Kind_Type_End) { - if (expr->token) { - onyx_report_error(expr->token->pos, "Type used as part of an expression."); - } - else { - onyx_report_error((OnyxFilePos) { 0 }, "Type used as part of an expression somewhere in the program."); - } - return Check_Error; + return Check_Success; + // if (expr->token) { + // onyx_report_error(expr->token->pos, "Type used as part of an expression."); + // } + // else { + // onyx_report_error((OnyxFilePos) { 0 }, "Type used as part of an expression somewhere in the program."); + // } + // return Check_Error; } fill_in_type(expr); diff --git a/src/onyxparser.c b/src/onyxparser.c index 57745c81..7cf8789a 100644 --- a/src/onyxparser.c +++ b/src/onyxparser.c @@ -167,6 +167,22 @@ static AstNumLit* parse_float_literal(OnyxParser* parser) { return float_node; } +// e +// '#' +static b32 parse_possible_directive(OnyxParser* parser, const char* dir) { + if (parser->curr->type != '#') return 0; + + expect_token(parser, '#'); + OnyxToken* sym = expect_token(parser, Token_Type_Symbol); + + b32 match = (strlen(dir) == (u64) sym->length) && (strncmp(dir, sym->text, sym->length) == 0); + if (!match) { + unconsume_token(parser); + unconsume_token(parser); + } + return match; +} + static b32 parse_possible_struct_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret) { if (parser->curr->type != '.' || peek_token(1)->type != '{') return 0; @@ -1461,7 +1477,7 @@ static void parse_polymorphic_variable(OnyxParser* parser, AstType*** next_inser bh_arr(AstPolyParam) pv = NULL; if (parser->polymorph_context.poly_params == NULL) - onyx_report_error(parser->curr->pos, "polymorphic variable not valid here."); + onyx_report_error(parser->curr->pos, "Polymorphic variable not valid here."); else pv = *parser->polymorph_context.poly_params; @@ -1475,6 +1491,7 @@ static void parse_polymorphic_variable(OnyxParser* parser, AstType*** next_inser if (pv != NULL) { bh_arr_push(pv, ((AstPolyParam) { + .kind = PPK_Poly_Type, .poly_sym = symbol_node, // These will be filled out by function_params() @@ -1824,6 +1841,7 @@ static void parse_function_params(OnyxParser* parser, AstFunction* func) { assert(parser->polymorph_context.poly_params != NULL); b32 param_use = 0; + b32 param_is_baked = 0; OnyxToken* symbol; while (parser->curr->type != ')') { if (parser->hit_unexpected_token) return; @@ -1833,6 +1851,11 @@ static void parse_function_params(OnyxParser* parser, AstFunction* func) { param_use = 1; } + if (parser->curr->type == '$') { + consume_token(parser); + param_is_baked = 1; + } + symbol = expect_token(parser, Token_Type_Symbol); expect_token(parser, ':'); @@ -1858,6 +1881,8 @@ static void parse_function_params(OnyxParser* parser, AstFunction* func) { i32 old_len = 0, new_len = 0; if (curr_param.vararg_kind != VA_Kind_Untyped) { + // CLEANUP: This is mess and it is hard to follow what is going on here. + // I think with recent rewrites, this should be easier to do. old_len = bh_arr_length(*parser->polymorph_context.poly_params); curr_param.local->type_node = parse_type(parser); new_len = bh_arr_length(*parser->polymorph_context.poly_params); @@ -1885,6 +1910,23 @@ static void parse_function_params(OnyxParser* parser, AstFunction* func) { bh_arr_push(func->params, curr_param); + if (param_is_baked) { + param_is_baked = 0; + + assert(parser->polymorph_context.poly_params != NULL); + + bh_arr(AstPolyParam) pv = *parser->polymorph_context.poly_params; + bh_arr_push(pv, ((AstPolyParam) { + .kind = PPK_Baked_Value, + .idx = param_idx, + + .poly_sym = (AstNode *) curr_param.local, + .type_expr = curr_param.local->type_node, + })); + + *parser->polymorph_context.poly_params = pv; + } + curr_param.default_value = NULL; if (parser->curr->type != ')') @@ -1897,22 +1939,6 @@ static void parse_function_params(OnyxParser* parser, AstFunction* func) { return; } -// e -// '#' -static b32 parse_possible_directive(OnyxParser* parser, const char* dir) { - if (parser->curr->type != '#') return 0; - - expect_token(parser, '#'); - OnyxToken* sym = expect_token(parser, Token_Type_Symbol); - - b32 match = (strlen(dir) == (u64) sym->length) && (strncmp(dir, sym->text, sym->length) == 0); - if (!match) { - unconsume_token(parser); - unconsume_token(parser); - } - return match; -} - // 'proc' ('->' )? * static AstFunction* parse_function_definition(OnyxParser* parser) { OnyxToken* proc_token = expect_token(parser, Token_Type_Keyword_Proc); diff --git a/src/onyxsymres.c b/src/onyxsymres.c index 1bdd3df6..e44c4b97 100644 --- a/src/onyxsymres.c +++ b/src/onyxsymres.c @@ -848,6 +848,13 @@ static void symres_struct_defaults(AstType* t) { static void symres_polyproc(AstPolyProc* pp) { pp->poly_scope = semstate.curr_scope; + bh_arr_each(AstPolyParam, param, pp->poly_params) { + if (param->kind != PPK_Baked_Value) continue; + + param->type_expr = symres_type(param->type_expr); + } + + // CLEANUP: This was copied from symres_function_header. if (pp->base_func->operator_overload != (BinaryOp) -1) { if (bh_arr_length(pp->base_func->params) != 2) { onyx_report_error(pp->base_func->token->pos, "Expected 2 exactly arguments for binary operator overload."); diff --git a/src/onyxutils.c b/src/onyxutils.c index eec1472c..23abe8fe 100644 --- a/src/onyxutils.c +++ b/src/onyxutils.c @@ -354,7 +354,7 @@ static PolySolveResult solve_poly_type(AstNode* target, AstType* type_expr, Type return result; } -static Type* lookup_actual_type_in_arguments(AstPolyProc* pp, AstPolyParam* param, Arguments* args, char** err_msg) { +static AstTyped* lookup_param_in_arguments(AstPolyProc* pp, AstPolyParam* param, Arguments* args, char** err_msg) { bh_arr(AstTyped *) arg_arr = args->values; bh_arr(AstNamedValue *) named_values = args->named_values; @@ -363,7 +363,7 @@ static Type* lookup_actual_type_in_arguments(AstPolyProc* pp, AstPolyParam* para bh_arr_each(AstNamedValue *, named_value, named_values) { if (token_equals(param_name, (*named_value)->token)) { - return resolve_expression_type((AstTyped *) (*named_value)->value); + return (AstTyped *) (*named_value)->value; } } @@ -371,7 +371,7 @@ static Type* lookup_actual_type_in_arguments(AstPolyProc* pp, AstPolyParam* para if (err_msg) *err_msg = "Not enough arguments to polymorphic procedure. This error message may not be entirely right."; } else { - return resolve_expression_type((AstTyped *) arg_arr[param->idx]); + return (AstTyped *) arg_arr[param->idx]; } return NULL; @@ -393,46 +393,125 @@ static bh_arr(AstPolySolution) find_polymorphic_slns(AstPolyProc* pp, PolyProcLo } if (already_solved) continue; - Type* actual_type = NULL; + // CLEANUP CLEANUP CLEANUP + PolySolveResult resolved; - if (pp_lookup == PPLM_By_Call) { - AstCall* call = (AstCall *) actual; + if (param->kind == PPK_Poly_Type) { + Type* actual_type = NULL; - actual_type = lookup_actual_type_in_arguments(pp, param, &call->args, err_msg); - if (actual_type == NULL) goto sln_not_found; - } + if (pp_lookup == PPLM_By_Call) { + AstCall* call = (AstCall *) actual; - else if (pp_lookup == PPLM_By_Arguments) { - Arguments* args = (Arguments *) actual; + AstTyped* typed_param = lookup_param_in_arguments(pp, param, &call->args, err_msg); + if (typed_param == NULL) goto sln_not_found; - actual_type = lookup_actual_type_in_arguments(pp, param, args, err_msg); - if (actual_type == NULL) goto sln_not_found; - } + actual_type = resolve_expression_type(typed_param); + if (actual_type == NULL) goto sln_not_found; + } + + else if (pp_lookup == PPLM_By_Arguments) { + Arguments* args = (Arguments *) actual; + + AstTyped* typed_param = lookup_param_in_arguments(pp, param, args, err_msg); + if (typed_param == NULL) goto sln_not_found; + + actual_type = resolve_expression_type(typed_param); + if (actual_type == NULL) goto sln_not_found; + } + + else if (pp_lookup == PPLM_By_Function_Type) { + Type* ft = (Type*) actual; + if (param->idx >= ft->Function.param_count) { + if (err_msg) *err_msg = "Incompatible polymorphic argument to function parameter."; + goto sln_not_found; + } - else if (pp_lookup == PPLM_By_Function_Type) { - Type* ft = (Type*) actual; - if (param->idx >= ft->Function.param_count) { - if (err_msg) *err_msg = "Incompatible polymorphic argument to function parameter."; + actual_type = ft->Function.params[param->idx]; + } + + else { + if (err_msg) *err_msg = "Cannot resolve polymorphic function type."; goto sln_not_found; } - actual_type = ft->Function.params[param->idx]; - } + resolved = solve_poly_type(param->poly_sym, param->type_expr, actual_type); - else { - if (err_msg) *err_msg = "Cannot resolve polymorphic function type."; - goto sln_not_found; - } + } else if (param->kind == PPK_Baked_Value) { + AstTyped* value = NULL; + + if (pp_lookup == PPLM_By_Call) { + AstCall* call = (AstCall *) actual; + + value = lookup_param_in_arguments(pp, param, &call->args, err_msg); + if (value == NULL) goto sln_not_found; + } + + else if (pp_lookup == PPLM_By_Arguments) { + Arguments* args = (Arguments *) actual; + + value = lookup_param_in_arguments(pp, param, args, err_msg); + if (value == NULL) goto sln_not_found; + } + + else if (pp_lookup == PPLM_By_Function_Type) { + *err_msg = "Function type cannot be used to solved for baked parameter value."; + goto sln_not_found; + } + + else { + if (err_msg) *err_msg = "Cannot resolve polymorphic function type."; + goto sln_not_found; + } + + AstTyped* orig_value = value; + if (value->kind == Ast_Kind_Argument) { + value = ((AstArgument *) value)->value; + } + + if (param->type_expr == (AstType *) &type_expr_symbol) { + if (!node_is_type((AstNode *) value)) { + *err_msg = "Expected type expression."; + goto sln_not_found; + } - PolySolveResult resolved = solve_poly_type(param->poly_sym, param->type_expr, actual_type); + resolved = ((PolySolveResult) { + .kind = PSK_Type, + .actual = type_build_from_ast(semstate.node_allocator, (AstType *) value), + }); + + } else { + // CLEANUP + if ((value->flags & Ast_Flag_Comptime) == 0) { + *err_msg = "Expected compile-time known argument."; + goto sln_not_found; + } + + if (param->type == NULL) + param->type = type_build_from_ast(semstate.node_allocator, param->type_expr); + + if (!type_check_or_auto_cast(&value, param->type)) { + *err_msg = "Expected parameter of a different type. This error message should be wayyy better."; + goto sln_not_found; + } + + resolved = ((PolySolveResult) { + .kind = PSK_Value, + .value = value, + }); + } + + if (orig_value->kind == Ast_Kind_Argument) { + ((AstArgument *) orig_value)->is_baked = 1; + } + } switch (resolved.kind) { case PSK_Undefined: if (err_msg) *err_msg = bh_aprintf(global_heap_allocator, - "Unable to solve for polymoprhic variable '%b', using the type '%s'.", + "Unable to solve for polymoprhic variable '%b'.", param->poly_sym->token->text, - param->poly_sym->token->length, - type_get_name(actual_type)); + param->poly_sym->token->length); + // type_get_name(actual_type)); goto sln_not_found; case PSK_Type: @@ -468,6 +547,8 @@ AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lo if (slns == NULL) { 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); @@ -586,6 +667,15 @@ AstFunction* polymorphic_proc_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) solidified_func.func->generated_from = tkn; + // HACK HACK HACK + u32 removed_params = 0; + bh_arr_each(AstPolyParam, param, pp->poly_params) { + if (param->kind != PPK_Baked_Value) continue; + + bh_arr_deleten(solidified_func.func->params, param->idx - removed_params, 1); + removed_params++; + } + if (!add_solidified_function_entities(solidified_func, 0)) { onyx_report_error(tkn->pos, "Error in polymorphic procedure header generated from this call site."); return NULL; diff --git a/src/onyxwasm.c b/src/onyxwasm.c index 326c1fdb..38cc66cf 100644 --- a/src/onyxwasm.c +++ b/src/onyxwasm.c @@ -1332,6 +1332,7 @@ EMIT_FUNC(call, AstCall* call) { bh_arr_each(AstTyped *, parg, call->args.values) { AstArgument* arg = (AstArgument *) *parg; + if (arg->is_baked) continue; b32 place_on_stack = 0; b32 arg_is_compound = type_is_compound(arg->value->type);