very hacky implementation of baked compile-time procedures
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 16 Jan 2021 18:52:52 +0000 (12:52 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 16 Jan 2021 18:52:52 +0000 (12:52 -0600)
bin/onyx
include/onyxastnodes.h
onyx.exe
src/onyxastnodes.c
src/onyxchecker.c
src/onyxparser.c
src/onyxsymres.c
src/onyxutils.c
src/onyxwasm.c

index ea7ee4c95d7f3dd46378573cee73c2325f8ef518..29aee206618053d8d1fd64ee00204155b9f950c6 100755 (executable)
Binary files a/bin/onyx and b/bin/onyx differ
index dd93a7f7fd270bb2fa66e62e811076a2cd3f26ae..d290e50170d295d6e522ebdfd6e77bbcacf6fc8f 100644 (file)
@@ -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,
index c405dd6fd0d0dc5741b099274e0a19e84d9ae724..e409689db0df3bcbaa668f0bf43f588425c71566 100644 (file)
Binary files a/onyx.exe and b/onyx.exe differ
index b2a2eb6e5072228389037008d93418d5d073d691..85db0d236ba74220014b3a160bfc82de4a2f34d9 100644 (file)
@@ -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
index b6f90a593309712488efd74d3521b3b578e969e8..8e5ed61dd93af6b557056cc31f3910faff4e9c02 100644 (file)
@@ -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);
index 57745c815582017f78be16be4318cedea20414f8..7cf8789a9eca46e2bf500d459dccc3aba0135022 100644 (file)
@@ -167,6 +167,22 @@ static AstNumLit* parse_float_literal(OnyxParser* parser) {
     return float_node;
 }
 
+// e
+// '#' <symbol>
+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
-// '#' <symbol>
-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' <func_params> ('->' <type>)? <directive>* <block>
 static AstFunction* parse_function_definition(OnyxParser* parser) {
     OnyxToken* proc_token = expect_token(parser, Token_Type_Keyword_Proc);
index 1bdd3df6ccbd0da006257515099db2e17e975963..e44c4b9706be6a5fe90606a960a2adaab942a429 100644 (file)
@@ -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.");
index eec1472ce54ed4b7a65a4bf1d64a069df0901cfc..23abe8fec78c795a18adfe40e6b096589febe99e 100644 (file)
@@ -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;
index 326c1fdbef86ba0c7360f943888d5777297f1515..38cc66cf265b42f3ae950cad61130240432d355c 100644 (file)
@@ -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);