"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",
step : i32 = 1;
}
+vararg :: #type ^struct {
+ data: rawptr;
+ count: i32;
+}
+
null :: cast(rawptr) 0;
// ---------------------------------
// 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 {
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; \
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;
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;
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"
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 },
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);
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;
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;
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;
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) {
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);
}
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;
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);
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)) {
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) {
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;
}
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;
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);
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);
}
}
- 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);