From: Brendan Hansen Date: Sat, 26 Sep 2020 02:21:24 +0000 (-0500) Subject: added completely variable type varargs X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=5b1b4c3c271d7094adcbffe036b588c90d9daf43;p=onyx.git added completely variable type varargs --- diff --git a/.vimspector.json b/.vimspector.json index ea4cd749..61caceea 100644 --- a/.vimspector.json +++ b/.vimspector.json @@ -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", diff --git a/core/builtin.onyx b/core/builtin.onyx index a867c8d1..92c28622 100644 --- a/core/builtin.onyx +++ b/core/builtin.onyx @@ -13,6 +13,11 @@ range :: struct { step : i32 = 1; } +vararg :: #type ^struct { + data: rawptr; + count: i32; +} + null :: cast(rawptr) 0; // --------------------------------- diff --git a/include/onyxastnodes.h b/include/onyxastnodes.h index fb6994f4..b937438f 100644 --- a/include/onyxastnodes.h +++ b/include/onyxastnodes.h @@ -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 2579a91e..730b313e 100755 Binary files a/onyx and b/onyx differ diff --git a/progs/vararg_test.onyx b/progs/vararg_test.onyx index 6d391825..7fd5e44e 100644 --- a/progs/vararg_test.onyx +++ b/progs/vararg_test.onyx @@ -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" diff --git a/src/onyxbuiltins.c b/src/onyxbuiltins.c index 0869e6cd..965a7c77 100644 --- a/src/onyxbuiltins.c +++ b/src/onyxbuiltins.c @@ -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); diff --git a/src/onyxchecker.c b/src/onyxchecker.c index 5f9940ae..815ee18c 100644 --- a/src/onyxchecker.c +++ b/src/onyxchecker.c @@ -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) { diff --git a/src/onyxclone.c b/src/onyxclone.c index a4e95d97..d1b7ffb7 100644 --- a/src/onyxclone.c +++ b/src/onyxclone.c @@ -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); } diff --git a/src/onyxparser.c b/src/onyxparser.c index 82e75940..a8180c3e 100644 --- a/src/onyxparser.c +++ b/src/onyxparser.c @@ -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); diff --git a/src/onyxsymres.c b/src/onyxsymres.c index 7abad056..7add1d58 100644 --- a/src/onyxsymres.c +++ b/src/onyxsymres.c @@ -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) { diff --git a/src/onyxtypes.c b/src/onyxtypes.c index 8d961b08..f18a4d0d 100644 --- a/src/onyxtypes.c +++ b/src/onyxtypes.c @@ -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; } diff --git a/src/onyxwasm.c b/src/onyxwasm.c index f98edce5..83dee10f 100644 --- a/src/onyxwasm.c +++ b/src/onyxwasm.c @@ -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);