added completely variable type varargs
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 26 Sep 2020 02:21:24 +0000 (21:21 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 26 Sep 2020 02:21:24 +0000 (21:21 -0500)
12 files changed:
.vimspector.json
core/builtin.onyx
include/onyxastnodes.h
onyx
progs/vararg_test.onyx
src/onyxbuiltins.c
src/onyxchecker.c
src/onyxclone.c
src/onyxparser.c
src/onyxsymres.c
src/onyxtypes.c
src/onyxwasm.c

index ea4cd749f4be96898328375528453ef648a5f544..61caceeadb9b4d0837167a9a277d209f401dfc48 100644 (file)
@@ -6,9 +6,9 @@
                 "type": "cppdbg",
                 "request": "launch",
                 "program": "${workspaceFolder}/onyx",
-                "args": ["-verbose", "progs/poly_struct.onyx"],
+                "args": ["-verbose", "progs/vararg_test.onyx"],
                 "stopAtEntry": true,
-                "cwd": "${workspaceFolder}",
+                "cwd": "~/dev/onyx",
                 "environment": [],
                 "externalConsole": false,
                 "MIMode": "gdb",
index a867c8d1f55ae62cbbf7b83785863130f312ca06..92c286225784896e9270caa572477bbcd9273312 100644 (file)
@@ -13,6 +13,11 @@ range :: struct {
     step : i32 = 1;
 }
 
+vararg :: #type ^struct {
+    data:  rawptr;
+    count: i32;
+}
+
 null :: cast(rawptr) 0;
 
 // ---------------------------------
index fb6994f4862904f13f8e9baabb99f3703c0497c3..b937438f9321b7e124a97b06ebeb66e951b5e581 100644 (file)
@@ -155,42 +155,44 @@ typedef enum AstKind {
 // only 32-bits of flags to play with
 typedef enum AstFlags {
     // Top-level flags
-    Ast_Flag_Exported          = BH_BIT(1),
-    Ast_Flag_Foreign           = BH_BIT(2),
-    Ast_Flag_Const             = BH_BIT(3),
-    Ast_Flag_Comptime          = BH_BIT(4),
-    Ast_Flag_Private_Package   = BH_BIT(5),
-    Ast_Flag_Private_File      = BH_BIT(6),
+    Ast_Flag_Exported              = BH_BIT(1),
+    Ast_Flag_Foreign               = BH_BIT(2),
+    Ast_Flag_Const                 = BH_BIT(3),
+    Ast_Flag_Comptime              = BH_BIT(4),
+    Ast_Flag_Private_Package       = BH_BIT(5),
+    Ast_Flag_Private_File          = BH_BIT(6),
 
     // Global flags
-    Ast_Flag_Global_Stack_Top  = BH_BIT(7),
-    Ast_Flag_Global_Stack_Base = BH_BIT(8),
+    Ast_Flag_Global_Stack_Top      = BH_BIT(7),
+    Ast_Flag_Global_Stack_Base     = BH_BIT(8),
 
     // Function flags
-    Ast_Flag_Intrinsic         = BH_BIT(10),
-    Ast_Flag_Function_Used     = BH_BIT(11),
+    Ast_Flag_Intrinsic             = BH_BIT(10),
+    Ast_Flag_Function_Used         = BH_BIT(11),
 
     // Expression flags
-    Ast_Flag_Expr_Ignored      = BH_BIT(13),
-    Ast_Flag_Param_Use         = BH_BIT(14),
-    Ast_Flag_Address_Taken     = BH_BIT(15),
+    Ast_Flag_Expr_Ignored          = BH_BIT(13),
+    Ast_Flag_Param_Use             = BH_BIT(14),
+    Ast_Flag_Address_Taken         = BH_BIT(15),
 
     // Type flags
-    Ast_Flag_Type_Is_Resolved  = BH_BIT(16),
+    Ast_Flag_Type_Is_Resolved      = BH_BIT(16),
 
     // Enum flags
-    Ast_Flag_Enum_Is_Flags     = BH_BIT(17),
+    Ast_Flag_Enum_Is_Flags         = BH_BIT(17),
 
     // Struct flags
-    Ast_Flag_Struct_Is_Union   = BH_BIT(18),
+    Ast_Flag_Struct_Is_Union       = BH_BIT(18),
 
-    Ast_Flag_No_Clone          = BH_BIT(19),
+    Ast_Flag_No_Clone              = BH_BIT(19),
 
-    Ast_Flag_Cannot_Take_Addr  = BH_BIT(20),
+    Ast_Flag_Cannot_Take_Addr      = BH_BIT(20),
 
-    Ast_Flag_Arg_Is_VarArg     = BH_BIT(21),
+    Ast_Flag_Arg_Is_VarArg         = BH_BIT(21),
 
-    Ast_Flag_Struct_Mem_Used   = BH_BIT(22),
+    Ast_Flag_Arg_Is_Untyped_VarArg = BH_BIT(22),
+
+    Ast_Flag_Struct_Mem_Used       = BH_BIT(23),
 } AstFlags;
 
 typedef enum UnaryOp {
@@ -404,6 +406,12 @@ typedef enum ParamPassType {
     Param_Pass_By_Implicit_Pointer,
 } ParamPassType;
 
+typedef enum VarArgKind {
+    VA_Kind_Not_VA,
+    VA_Kind_Typed,
+    VA_Kind_Untyped,
+} VarArgKind;
+
 // Base Nodes
 #define AstNode_base \
     AstKind kind;             \
@@ -616,12 +624,7 @@ struct AstParam {
     AstLocal *local;
     AstTyped *default_value;
 
-    // HACK: There is a little too much complexity here. If a parameter is
-    // a vararg, not only is the flag below set, but the type_node of the
-    // local is also a AstVarArgType. I think this redudancy should be able
-    // to be cleaned up.                        -brendanfh   2020/09/07
-
-    b32 is_vararg : 1;
+    VarArgKind vararg_kind;
 };
 struct AstFunction {
     AstTyped_base;
@@ -751,6 +754,8 @@ extern AstGlobal builtin_stack_top;
 extern AstType  *builtin_string_type;
 extern AstType  *builtin_range_type;
 extern Type     *builtin_range_type_type;
+extern AstType  *builtin_vararg_type;
+extern Type     *builtin_vararg_type_type;
 
 typedef struct BuiltinSymbol {
     char*    package;
diff --git a/onyx b/onyx
index 2579a91eb8674956fb5d1e13956a91bd4dfd259b..730b313e12ee5142c888724334bab81dcc50d37b 100755 (executable)
Binary files a/onyx and b/onyx differ
index 6d391825a5353fb88e00d501f940def3cf27a2ce..7fd5e44e9073b3348e109333363711a14f493f48 100644 (file)
@@ -8,23 +8,31 @@ old_va_test :: proc (prefix: string, va: ..i32) {
     for v: va do println(v);
 }
 
-vararg_get :: proc (va: ^rawptr, ret: ^$T) {
-    *ret = *cast(^T) *va;
-    *va = cast(rawptr) (cast(^u8) *va + sizeof T);
+vararg_get :: proc (va: vararg, ret: ^$T) {
+    *ret = *cast(^T) va.data;
+    va.data = cast(rawptr) (cast(^u8) va.data + sizeof T);
+    // *va = cast(rawptr) (cast(^u8) *va + sizeof T);
 }
 
 // va is just a rawptr with no bounds checking
 new_va_test :: proc (prefix: string, va: ...) {
-    println(string);
-
-    x : i32;
-    vararg_get(^va, ^x);
-
-    println(x);
+    println(prefix);
+
+    for i: 0 .. 2 {
+        x : i32;
+        vararg_get(va, ^x);
+        println(x);
+    }
+
+    for i: 0 .. 2 {
+        x : f32;
+        vararg_get(va, ^x);
+        println(x);
+    }
 }
 
 main :: proc (args: [] cstring) {
-    va_test("foo", 1, 2, 3, 4);
+    new_va_test("foo", 1, 2, 3.0f, 5.0f);
 }
 
 // Things needed to make the above work"
index 0869e6cd8e2fc10e41796d5f3cbc00f34d416591..965a7c77893ac3ae747e131072caecc9fa18df52 100644 (file)
@@ -37,6 +37,8 @@ AstGlobal builtin_stack_top   = { Ast_Kind_Global, Ast_Flag_Const | Ast_Flag_Glo
 AstType  *builtin_string_type;
 AstType  *builtin_range_type;
 Type     *builtin_range_type_type;
+AstType  *builtin_vararg_type;
+Type     *builtin_vararg_type_type;
 
 const BuiltinSymbol builtin_symbols[] = {
     { NULL, "void",       (AstNode *) &basic_type_void },
@@ -345,6 +347,12 @@ void initialize_builtins(bh_allocator a, ProgramInfo* prog) {
         return;
     }
 
+    builtin_vararg_type = (AstType *) symbol_raw_resolve(p->scope, "vararg");
+    if (builtin_range_type == NULL) {
+        onyx_report_error((OnyxFilePos) { 0 }, "'vararg' struct not found in builtin package.");
+        return;
+    }
+
 
 
     bh_table_init(global_heap_allocator, intrinsic_table, 128);
index 5f9940ae97c771de5737c588c6eb7a99fe97148c..815ee18c199d75ef652baefe9913a7fe8344de14 100644 (file)
@@ -320,16 +320,6 @@ b32 check_call(AstCall* call) {
             return 1;
         }
 
-        // NOTE: This restricts you to only passing simple structures as parameters.
-        // At one point in time it was okay, but there are some necessary complexities.
-        // if (type_is_structlike_strict(actual->value->type)) {
-        //     if (!type_structlike_is_simple(actual->value->type)) {
-        //         onyx_report_error(actual->token->pos,
-        //             "Can only pass simple structs as parameters (no nested structures). passing by pointer is the only way for now.");
-        //         return 1;
-        //     }
-        // }
-
         if (actual->value->kind == Ast_Kind_Polymorphic_Proc)
             has_polymorphic_args = 1;
 
@@ -440,45 +430,71 @@ b32 check_call(AstCall* call) {
     Type* variadic_type = NULL;
     AstParam* variadic_param = NULL;
 
+    i32 arg_state = 0;
+
     i32 arg_pos = 0;
     while (1) {
         if (actual == NULL) break;
-        if (arg_pos >= callee->type->Function.param_count && variadic_type == NULL) break;
 
-        if (variadic_type == NULL && formal_params[arg_pos]->kind == Type_Kind_VarArgs) {
-            variadic_type = formal_params[arg_pos]->VarArgs.ptr_to_data->Pointer.elem;
-            variadic_param = &callee->params[arg_pos];
-        }
+        switch (arg_state) {
+            case 0: {
+                if (arg_pos >= callee->type->Function.param_count) goto type_checking_done;
+
+                if (formal_params[arg_pos]->kind == Type_Kind_VarArgs) {
+                    variadic_type = formal_params[arg_pos]->VarArgs.ptr_to_data->Pointer.elem;
+                    variadic_param = &callee->params[arg_pos];
+                    arg_state = 1;
+                    continue;
+                }
+
+                if (formal_params[arg_pos] == builtin_vararg_type_type) {
+                    arg_state = 2; 
+                    continue;
+                }
+
+                if (!type_check_or_auto_cast(actual->value, formal_params[arg_pos])) {
+                    onyx_report_error(actual->token->pos,
+                            "The function '%b' expects a value of type '%s' for %d%s parameter, got '%s'.",
+                            callee->token->text, callee->token->length,
+                            type_get_name(formal_params[arg_pos]),
+                            arg_pos + 1,
+                            bh_num_suffix(arg_pos + 1),
+                            type_get_name(actual->value->type));
+                    return 1;
+                }
 
-        if (variadic_type != NULL) {
-            if (!type_check_or_auto_cast(actual->value, variadic_type)) {
-                onyx_report_error(actual->token->pos,
-                        "The function '%b' expects a value of type '%s' for the variadic parameter, '%b', got '%s'.",
-                        callee->token->text, callee->token->length,
-                        type_get_name(variadic_type),
-                        variadic_param->local->token->text,
-                        variadic_param->local->token->length,
-                        type_get_name(actual->value->type));
-                return 1;
+                break;
             }
 
-            actual->flags |= Ast_Flag_Arg_Is_VarArg;
+            case 1: {
+                if (!type_check_or_auto_cast(actual->value, variadic_type)) {
+                    onyx_report_error(actual->token->pos,
+                            "The function '%b' expects a value of type '%s' for the variadic parameter, '%b', got '%s'.",
+                            callee->token->text, callee->token->length,
+                            type_get_name(variadic_type),
+                            variadic_param->local->token->text,
+                            variadic_param->local->token->length,
+                            type_get_name(actual->value->type));
+                    return 1;
+                }
+
+                actual->flags |= Ast_Flag_Arg_Is_VarArg;
+                break;
+            }
 
-        } else if (!type_check_or_auto_cast(actual->value, formal_params[arg_pos])) {
-            onyx_report_error(actual->token->pos,
-                    "The function '%b' expects a value of type '%s' for %d%s parameter, got '%s'.",
-                    callee->token->text, callee->token->length,
-                    type_get_name(formal_params[arg_pos]),
-                    arg_pos + 1,
-                    bh_num_suffix(arg_pos + 1),
-                    type_get_name(actual->value->type));
-            return 1;
+            case 2: {
+                // Untyped varargs
+                actual->flags |= Ast_Flag_Arg_Is_Untyped_VarArg;
+                break;
+            }
         }
 
         arg_pos++;
         actual = (AstArgument *) actual->next;
     }
 
+type_checking_done:
+
     if (arg_pos < callee->type->Function.param_count) {
         onyx_report_error(call->token->pos, "Too few arguments to function call.");
         return 1;
@@ -1295,13 +1311,13 @@ b32 check_function_header(AstFunction* func) {
             return 1;
         }
 
-        if (has_had_varargs && param->is_vararg) {
+        if (has_had_varargs && param->vararg_kind != VA_Kind_Not_VA) {
             onyx_report_error(local->token->pos,
                     "Can only have one param that is of variable argument type.");
             return 1;
         }
 
-        if (has_had_varargs && !param->is_vararg) {
+        if (has_had_varargs && param->vararg_kind != VA_Kind_Not_VA) {
             onyx_report_error(local->token->pos,
                     "Variable arguments must be last in parameter list");
             return 1;
@@ -1316,7 +1332,7 @@ b32 check_function_header(AstFunction* func) {
             return 1;
         }
 
-        if (param->is_vararg) has_had_varargs = 1;
+        if (param->vararg_kind != VA_Kind_Not_VA) has_had_varargs = 1;
 
         if (local->type->kind != Type_Kind_Array
             && type_size_of(local->type) == 0) {
index a4e95d97620e7d9574af42b6e06f52d7fcc36998..d1b7ffb77597a2ee04bccc15c9a800a080d47129 100644 (file)
@@ -342,7 +342,7 @@ AstNode* ast_clone(bh_allocator a, void* n) {
                                AstParam new_param;
                                new_param.local = (AstLocal *) ast_clone(a, param->local);
                                new_param.default_value = (AstTyped *) ast_clone(a, param->default_value);
-                new_param.is_vararg = param->is_vararg;
+                new_param.vararg_kind = param->vararg_kind;
                                bh_arr_push(df->params, new_param);
                        }
 
index 82e7594002f443c08cbff9b4d2f5e26cf2374d60..a8180c3ed10248634e6f20e29134ab14b295aeb8 100644 (file)
@@ -1507,6 +1507,7 @@ static void parse_function_params(OnyxParser* parser, AstFunction* func) {
         symbol = expect_token(parser, Token_Type_Symbol);
         expect_token(parser, ':');
 
+        curr_param.vararg_kind = VA_Kind_Not_VA;
         curr_param.local = make_node(AstLocal, Ast_Kind_Param);
         curr_param.local->token = symbol;
         curr_param.local->flags |= Ast_Flag_Const;
@@ -1519,29 +1520,36 @@ static void parse_function_params(OnyxParser* parser, AstFunction* func) {
         if (parser->curr->type != '=') {
             if (parser->curr->type == Token_Type_Dot_Dot) {
                 consume_token(parser);
-                curr_param.is_vararg = 1;
-            }
-
-            i32 old_len = bh_arr_length(*parser->polymorph_context.poly_params);
-            curr_param.local->type_node = parse_type(parser);
+                curr_param.vararg_kind = VA_Kind_Typed;
 
-            i32 new_len = bh_arr_length(*parser->polymorph_context.poly_params);
-            i32 new_poly_params = new_len - old_len;
+                if (parser->curr->type == '.') {
+                    consume_token(parser);
+                    curr_param.vararg_kind = VA_Kind_Untyped;
+                }
+            }
 
-            if (curr_param.is_vararg) {
-                AstVarArgType* va_type = make_node(AstVarArgType, Ast_Kind_VarArg_Type);
-                va_type->elem = curr_param.local->type_node;
-                va_type->token = curr_param.local->type_node->token;
-                curr_param.local->type_node = (AstType *) va_type;
+            i32 old_len = 0, new_len = 0;
+            if (curr_param.vararg_kind != VA_Kind_Untyped) {
+                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);
+
+                if (curr_param.vararg_kind == VA_Kind_Typed) {
+                    AstVarArgType* va_type = make_node(AstVarArgType, Ast_Kind_VarArg_Type);
+                    va_type->elem = curr_param.local->type_node;
+                    va_type->token = curr_param.local->type_node->token;
+                    curr_param.local->type_node = (AstType *) va_type;
+                }
             }
 
+            i32 new_poly_params = new_len - old_len;
             fori (i, 0, new_poly_params) {
                 (*parser->polymorph_context.poly_params)[old_len + i].type_expr = curr_param.local->type_node;
                 (*parser->polymorph_context.poly_params)[old_len + i].idx = param_idx;
             }
         }
 
-        if (parser->curr->type == '=') {
+        if (parser->curr->type == '=' && curr_param.vararg_kind == VA_Kind_Not_VA) {
             consume_token(parser);
 
             curr_param.default_value = parse_expression(parser);
index 7abad05695b8a6af463bf489d1e383201ed909b8..7add1d583f3fb356435a7d7d3f12e6ef9f5ce305 100644 (file)
@@ -582,7 +582,7 @@ void symres_function(AstFunction* func) {
     bh_arr_each(AstParam, param, func->params) {
         if (param->local->type_node != NULL) {
             param->local->type_node = symres_type(param->local->type_node);
-            param->local->type = type_build_from_ast(semstate.allocator, param->local->type_node);
+            param->local->type = type_build_from_ast(semstate.node_allocator, param->local->type_node);
 
             if (param->default_value != NULL) {
                 if (!types_are_compatible(param->local->type, param->default_value->type)) {
@@ -593,8 +593,15 @@ void symres_function(AstFunction* func) {
                     return;
                 }
             }
-        } else {
+
+        } else if (param->default_value != NULL) {
             param->local->type = param->default_value->type;
+
+        } else if (param->vararg_kind == VA_Kind_Untyped) {
+            // HACK
+            builtin_vararg_type_type = type_build_from_ast(semstate.node_allocator, builtin_vararg_type);
+
+            param->local->type = builtin_vararg_type_type;
         }
 
         if (param->local->type == NULL) {
index 8d961b086637f4ca23e40c438c52565d34830fea..f18a4d0dd2df9cacaae6f3eabf42a4a85670c2c3 100644 (file)
@@ -474,7 +474,7 @@ Type* type_build_function_type(bh_allocator alloc, AstFunction* func) {
     if (param_count > 0) {
         i32 i = 0;
         bh_arr_each(AstParam, param, func->params) {
-            if (param->default_value == NULL && !param->is_vararg)
+            if (param->default_value == NULL && param->vararg_kind == VA_Kind_Not_VA)
                 func_type->Function.needed_param_count++;
             func_type->Function.params[i++] = param->local->type;
         }
index f98edce5e1c33f8e539b877a5c3631ba54881741..83dee10f3dca4636342b70f4ed36d7a19829d6f3 100644 (file)
@@ -1325,6 +1325,8 @@ EMIT_FUNC(call, AstCall* call) {
     u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
 
     u32 vararg_count = 0;
+    u32 vararg_offset = -1;
+    VarArgKind vararg_type = VA_Kind_Not_VA;
 
     for (AstArgument *arg = call->arguments;
             arg != NULL;
@@ -1333,7 +1335,16 @@ EMIT_FUNC(call, AstCall* call) {
         b32 place_on_stack = 0;
         b32 arg_is_struct  = type_is_structlike(arg->value->type);
 
-        if (arg->flags & Ast_Flag_Arg_Is_VarArg) place_on_stack = 1;
+        if (arg->flags & Ast_Flag_Arg_Is_VarArg) {
+            vararg_type = VA_Kind_Typed;
+            if (vararg_offset == -1) vararg_offset = stack_grow_amm;
+            place_on_stack = 1;
+        }
+        if (arg->flags & Ast_Flag_Arg_Is_Untyped_VarArg) {
+            vararg_type = VA_Kind_Untyped;
+            if (vararg_offset == -1) vararg_offset = stack_grow_amm;
+            place_on_stack = 1;
+        }
         if (type_get_param_pass(arg->value->type) == Param_Pass_By_Implicit_Pointer) place_on_stack = 1;
 
         if (place_on_stack && !arg_is_struct) WID(WI_GLOBAL_GET, stack_top_idx);
@@ -1344,7 +1355,7 @@ EMIT_FUNC(call, AstCall* call) {
             if (arg_is_struct) WID(WI_GLOBAL_GET, stack_top_idx);
             emit_store_instruction(mod, &code, arg->value->type, stack_grow_amm);
 
-            if (arg->flags & Ast_Flag_Arg_Is_VarArg) vararg_count += 1;
+            if (vararg_type != VA_Kind_Not_VA) vararg_count += 1;
             else {
                 WID(WI_GLOBAL_GET, stack_top_idx);
                 WID(WI_I32_CONST, stack_grow_amm);
@@ -1355,9 +1366,35 @@ EMIT_FUNC(call, AstCall* call) {
         }
     }
 
-    if (vararg_count > 0) {
-        WID(WI_GLOBAL_GET, stack_top_idx);
-        WID(WI_I32_CONST, vararg_count);
+    if (vararg_type != VA_Kind_Not_VA) {
+        switch (vararg_type) {
+            case VA_Kind_Typed: {
+                WID(WI_GLOBAL_GET, vararg_offset);
+                WID(WI_I32_CONST, vararg_count);
+                break;
+            }
+
+            case VA_Kind_Untyped: {
+                WID(WI_GLOBAL_GET, stack_top_idx);
+                WID(WI_GLOBAL_GET, stack_top_idx);
+                WID(WI_I32_CONST, vararg_offset);
+                WI(WI_I32_ADD);
+                emit_store_instruction(mod, &code, &basic_types[Basic_Kind_I32], stack_grow_amm);
+
+                WID(WI_GLOBAL_GET, stack_top_idx);
+                WID(WI_I32_CONST, vararg_count);
+                emit_store_instruction(mod, &code, &basic_types[Basic_Kind_I32], stack_grow_amm + 4);
+
+                WID(WI_GLOBAL_GET, stack_top_idx);
+                WID(WI_I32_CONST, stack_grow_amm);
+                WI(WI_I32_ADD);
+
+                stack_grow_amm += 8;
+                break;
+            }
+
+            default: break;
+        }
     }
 
     CallingConvention cc = type_function_get_cc(call->callee->type);