mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-04 22:58:46 +00:00
Typesafe variadic procedures
This commit is contained in:
+19
-8
@@ -3,17 +3,28 @@
|
|||||||
#load "game.odin"
|
#load "game.odin"
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
print_int(min(1, 2)); nl()
|
|
||||||
print_int(max(1, 2)); nl()
|
|
||||||
print_int(abs(-1337)); nl()
|
|
||||||
|
|
||||||
a, b, c := 1, 2, -1337
|
print_ints :: proc(args: ..int) {
|
||||||
|
for i := 0; i < len(args); i++ {
|
||||||
|
print_int(args[i])
|
||||||
|
nl()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// print_ints()
|
||||||
|
// print_ints(1)
|
||||||
|
print_ints(1, 2, 3, 4, 5)
|
||||||
|
|
||||||
print_int(min(a, b)); nl()
|
// print_int(min(1, 2)); nl()
|
||||||
print_int(max(a, b)); nl()
|
// print_int(max(1, 2)); nl()
|
||||||
print_int(abs(c) as int); nl()
|
// print_int(abs(-1337)); nl()
|
||||||
|
|
||||||
nl()
|
// a, b, c := 1, 2, -1337
|
||||||
|
|
||||||
|
// print_int(min(a, b)); nl()
|
||||||
|
// print_int(max(a, b)); nl()
|
||||||
|
// print_int(abs(c) as int); nl()
|
||||||
|
|
||||||
|
// nl()
|
||||||
/*
|
/*
|
||||||
Vec3 :: type struct { x, y, z: f32 }
|
Vec3 :: type struct { x, y, z: f32 }
|
||||||
Entity :: type struct {
|
Entity :: type struct {
|
||||||
|
|||||||
@@ -110,6 +110,8 @@ GetQueryPerformanceFrequency :: proc() -> i64 {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GetCommandLineA :: proc() -> ^u8 #foreign
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// File Stuff
|
// File Stuff
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ enum BuiltinProcId {
|
|||||||
BuiltinProc_align_of_val,
|
BuiltinProc_align_of_val,
|
||||||
BuiltinProc_offset_of,
|
BuiltinProc_offset_of,
|
||||||
BuiltinProc_offset_of_val,
|
BuiltinProc_offset_of_val,
|
||||||
|
BuiltinProc_type_of_val,
|
||||||
BuiltinProc_assert,
|
BuiltinProc_assert,
|
||||||
|
|
||||||
BuiltinProc_len,
|
BuiltinProc_len,
|
||||||
@@ -170,6 +171,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
|
|||||||
{STR_LIT("align_of_val"), 1, false, Expr_Expr},
|
{STR_LIT("align_of_val"), 1, false, Expr_Expr},
|
||||||
{STR_LIT("offset_of"), 2, false, Expr_Expr},
|
{STR_LIT("offset_of"), 2, false, Expr_Expr},
|
||||||
{STR_LIT("offset_of_val"), 1, false, Expr_Expr},
|
{STR_LIT("offset_of_val"), 1, false, Expr_Expr},
|
||||||
|
{STR_LIT("type_of_val"), 1, false, Expr_Expr},
|
||||||
{STR_LIT("assert"), 1, false, Expr_Stmt},
|
{STR_LIT("assert"), 1, false, Expr_Stmt},
|
||||||
|
|
||||||
{STR_LIT("len"), 1, false, Expr_Expr},
|
{STR_LIT("len"), 1, false, Expr_Expr},
|
||||||
|
|||||||
+85
-17
@@ -459,10 +459,12 @@ void check_enum_type(Checker *c, Type *enum_type, AstNode *node) {
|
|||||||
enum_type->Record.other_field_count = et->field_count;
|
enum_type->Record.other_field_count = et->field_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize field_count) {
|
Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize field_count, b32 *is_variadic_) {
|
||||||
if (field_list == NULL || field_count == 0)
|
if (field_list == NULL || field_count == 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
b32 is_variadic = false;
|
||||||
|
|
||||||
Type *tuple = make_type_tuple(c->allocator);
|
Type *tuple = make_type_tuple(c->allocator);
|
||||||
|
|
||||||
Entity **variables = gb_alloc_array(c->allocator, Entity *, field_count);
|
Entity **variables = gb_alloc_array(c->allocator, Entity *, field_count);
|
||||||
@@ -471,6 +473,15 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize fiel
|
|||||||
ast_node(f, Field, field);
|
ast_node(f, Field, field);
|
||||||
AstNode *type_expr = f->type;
|
AstNode *type_expr = f->type;
|
||||||
if (type_expr) {
|
if (type_expr) {
|
||||||
|
if (type_expr->kind == AstNode_Ellipsis) {
|
||||||
|
type_expr = type_expr->Ellipsis.expr;
|
||||||
|
if (field->next == NULL) {
|
||||||
|
is_variadic = true;
|
||||||
|
} else {
|
||||||
|
error(&c->error_collector, ast_node_token(field), "Invalid AST: Invalid variadic parameter");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Type *type = check_type(c, type_expr);
|
Type *type = check_type(c, type_expr);
|
||||||
for (AstNode *name = f->name_list; name != NULL; name = name->next) {
|
for (AstNode *name = f->name_list; name != NULL; name = name->next) {
|
||||||
if (name->kind == AstNode_Ident) {
|
if (name->kind == AstNode_Ident) {
|
||||||
@@ -478,14 +489,24 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize fiel
|
|||||||
add_entity(c, scope, name, param);
|
add_entity(c, scope, name, param);
|
||||||
variables[variable_index++] = param;
|
variables[variable_index++] = param;
|
||||||
} else {
|
} else {
|
||||||
error(&c->error_collector, ast_node_token(name), "Invalid parameter (invalid AST)");
|
error(&c->error_collector, ast_node_token(name), "Invalid AST: Invalid parameter");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_variadic && field_count > 0) {
|
||||||
|
// NOTE(bill): Change last variadic parameter to be a slice
|
||||||
|
// Custom Calling convention for variadic parameters
|
||||||
|
Entity *end = variables[field_count-1];
|
||||||
|
end->type = make_type_slice(c->allocator, end->type);
|
||||||
|
}
|
||||||
|
|
||||||
tuple->Tuple.variables = variables;
|
tuple->Tuple.variables = variables;
|
||||||
tuple->Tuple.variable_count = field_count;
|
tuple->Tuple.variable_count = field_count;
|
||||||
|
|
||||||
|
if (is_variadic_) *is_variadic_ = is_variadic;
|
||||||
|
|
||||||
return tuple;
|
return tuple;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -520,7 +541,8 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
|
|||||||
|
|
||||||
// gb_printf("%td -> %td\n", param_count, result_count);
|
// gb_printf("%td -> %td\n", param_count, result_count);
|
||||||
|
|
||||||
Type *params = check_get_params(c, c->context.scope, pt->param_list, param_count);
|
b32 variadic = false;
|
||||||
|
Type *params = check_get_params(c, c->context.scope, pt->param_list, param_count, &variadic);
|
||||||
Type *results = check_get_results(c, c->context.scope, pt->result_list, result_count);
|
Type *results = check_get_results(c, c->context.scope, pt->result_list, result_count);
|
||||||
|
|
||||||
type->Proc.scope = c->context.scope;
|
type->Proc.scope = c->context.scope;
|
||||||
@@ -528,6 +550,7 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
|
|||||||
type->Proc.param_count = pt->param_count;
|
type->Proc.param_count = pt->param_count;
|
||||||
type->Proc.results = results;
|
type->Proc.results = results;
|
||||||
type->Proc.result_count = pt->result_count;
|
type->Proc.result_count = pt->result_count;
|
||||||
|
type->Proc.variadic = variadic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -772,10 +795,19 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_c
|
|||||||
goto end;
|
goto end;
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
default:
|
default: {
|
||||||
|
if (e->kind == AstNode_CallExpr) {
|
||||||
|
Operand o = {};
|
||||||
|
check_expr_or_type(c, &o, e);
|
||||||
|
if (o.mode == Addressing_Type) {
|
||||||
|
type = o.type;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err_str = expr_to_string(e);
|
err_str = expr_to_string(e);
|
||||||
error(&c->error_collector, ast_node_token(e), "`%s` is not a type", err_str);
|
error(&c->error_collector, ast_node_token(e), "`%s` is not a type", err_str);
|
||||||
break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|
||||||
type = t_invalid;
|
type = t_invalid;
|
||||||
@@ -1894,7 +1926,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
|||||||
|
|
||||||
case BuiltinProc_size_of_val:
|
case BuiltinProc_size_of_val:
|
||||||
// size_of_val :: proc(val: Type) -> int
|
// size_of_val :: proc(val: Type) -> int
|
||||||
check_assignment(c, operand, NULL, make_string("argument of `size_of`"));
|
check_assignment(c, operand, NULL, make_string("argument of `size_of_val`"));
|
||||||
if (operand->mode == Addressing_Invalid)
|
if (operand->mode == Addressing_Invalid)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -1917,7 +1949,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
|||||||
|
|
||||||
case BuiltinProc_align_of_val:
|
case BuiltinProc_align_of_val:
|
||||||
// align_of_val :: proc(val: Type) -> int
|
// align_of_val :: proc(val: Type) -> int
|
||||||
check_assignment(c, operand, NULL, make_string("argument of `align_of`"));
|
check_assignment(c, operand, NULL, make_string("argument of `align_of_val`"));
|
||||||
if (operand->mode == Addressing_Invalid)
|
if (operand->mode == Addressing_Invalid)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -1996,6 +2028,14 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
|||||||
operand->type = t_int;
|
operand->type = t_int;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case BuiltinProc_type_of_val:
|
||||||
|
// type_of_val :: proc(val: Type) -> type(Type)
|
||||||
|
check_assignment(c, operand, NULL, make_string("argument of `type_of_val`"));
|
||||||
|
if (operand->mode == Addressing_Invalid)
|
||||||
|
return false;
|
||||||
|
operand->mode = Addressing_Type;
|
||||||
|
break;
|
||||||
|
|
||||||
case BuiltinProc_assert:
|
case BuiltinProc_assert:
|
||||||
// assert :: proc(cond: bool)
|
// assert :: proc(cond: bool)
|
||||||
|
|
||||||
@@ -2519,14 +2559,20 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode
|
|||||||
isize error_code = 0;
|
isize error_code = 0;
|
||||||
isize param_index = 0;
|
isize param_index = 0;
|
||||||
isize param_count = 0;
|
isize param_count = 0;
|
||||||
|
b32 variadic = proc_type->Proc.variadic;
|
||||||
|
|
||||||
if (proc_type->Proc.params)
|
if (proc_type->Proc.params) {
|
||||||
param_count = proc_type->Proc.params->Tuple.variable_count;
|
param_count = proc_type->Proc.params->Tuple.variable_count;
|
||||||
|
}
|
||||||
|
|
||||||
if (ce->arg_list_count == 0 && param_count == 0)
|
if (ce->arg_list_count == 0) {
|
||||||
return;
|
if (variadic && param_count-1 == 0)
|
||||||
|
return;
|
||||||
|
if (param_count == 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (ce->arg_list_count > param_count) {
|
if (ce->arg_list_count > param_count && !variadic) {
|
||||||
error_code = +1;
|
error_code = +1;
|
||||||
} else {
|
} else {
|
||||||
Entity **sig_params = proc_type->Proc.params->Tuple.variables;
|
Entity **sig_params = proc_type->Proc.params->Tuple.variables;
|
||||||
@@ -2537,19 +2583,40 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode
|
|||||||
continue;
|
continue;
|
||||||
if (operand->type->kind != Type_Tuple) {
|
if (operand->type->kind != Type_Tuple) {
|
||||||
check_not_tuple(c, operand);
|
check_not_tuple(c, operand);
|
||||||
check_assignment(c, operand, sig_params[param_index]->type, make_string("argument"), true);
|
isize index = param_index;
|
||||||
|
b32 end_variadic = false;
|
||||||
|
if (variadic && param_index >= param_count-1) {
|
||||||
|
index = param_count-1;
|
||||||
|
end_variadic = true;
|
||||||
|
}
|
||||||
|
Type *arg_type = sig_params[index]->type;
|
||||||
|
if (end_variadic && is_type_slice(arg_type)) {
|
||||||
|
arg_type = get_base_type(arg_type)->Slice.elem;
|
||||||
|
}
|
||||||
|
check_assignment(c, operand, arg_type, make_string("argument"), true);
|
||||||
param_index++;
|
param_index++;
|
||||||
} else {
|
} else {
|
||||||
auto *tuple = &operand->type->Tuple;
|
auto *tuple = &operand->type->Tuple;
|
||||||
isize i = 0;
|
isize i = 0;
|
||||||
for (;
|
for (;
|
||||||
i < tuple->variable_count && param_index < param_count;
|
i < tuple->variable_count && (param_index < param_count && !variadic);
|
||||||
i++, param_index++) {
|
i++) {
|
||||||
Entity *e = tuple->variables[i];
|
Entity *e = tuple->variables[i];
|
||||||
operand->type = e->type;
|
operand->type = e->type;
|
||||||
operand->mode = Addressing_Value;
|
operand->mode = Addressing_Value;
|
||||||
check_not_tuple(c, operand);
|
check_not_tuple(c, operand);
|
||||||
check_assignment(c, operand, sig_params[param_index]->type, make_string("argument"), true);
|
isize index = param_index;
|
||||||
|
b32 end_variadic = false;
|
||||||
|
if (variadic && param_index >= param_count-1) {
|
||||||
|
index = param_count-1;
|
||||||
|
end_variadic = true;
|
||||||
|
}
|
||||||
|
Type *arg_type = sig_params[index]->type;
|
||||||
|
if (end_variadic && is_type_slice(arg_type)) {
|
||||||
|
arg_type = get_base_type(arg_type)->Slice.elem;
|
||||||
|
}
|
||||||
|
check_assignment(c, operand, arg_type, make_string("argument"), true);
|
||||||
|
param_index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < tuple->variable_count && param_index == param_count) {
|
if (i < tuple->variable_count && param_index == param_count) {
|
||||||
@@ -2558,12 +2625,13 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (param_index >= param_count)
|
if (!variadic && param_index >= param_count)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (param_index < param_count) {
|
if ((!variadic && param_index < param_count) ||
|
||||||
|
(variadic && param_index < param_count-1)) {
|
||||||
error_code = -1;
|
error_code = -1;
|
||||||
} else if (call_arg != NULL && call_arg->next != NULL) {
|
} else if (call_arg != NULL && call_arg->next != NULL) {
|
||||||
error_code = +1;
|
error_code = +1;
|
||||||
|
|||||||
@@ -421,7 +421,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
|
|||||||
void check_proc_decl(Checker *c, Entity *e, DeclInfo *d, b32 check_body_later) {
|
void check_proc_decl(Checker *c, Entity *e, DeclInfo *d, b32 check_body_later) {
|
||||||
GB_ASSERT(e->type == NULL);
|
GB_ASSERT(e->type == NULL);
|
||||||
|
|
||||||
Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0);
|
Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0, false);
|
||||||
e->type = proc_type;
|
e->type = proc_type;
|
||||||
ast_node(pd, ProcDecl, d->proc_decl);
|
ast_node(pd, ProcDecl, d->proc_decl);
|
||||||
check_open_scope(c, pd->type);
|
check_open_scope(c, pd->type);
|
||||||
|
|||||||
+20
-5
@@ -149,6 +149,7 @@ struct Type {
|
|||||||
Type * results; // Type_Tuple
|
Type * results; // Type_Tuple
|
||||||
isize param_count;
|
isize param_count;
|
||||||
isize result_count;
|
isize result_count;
|
||||||
|
b32 variadic;
|
||||||
} Proc;
|
} Proc;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -240,13 +241,27 @@ Type *make_type_tuple(gbAllocator a) {
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
Type *make_type_proc(gbAllocator a, Scope *scope, Type *params, isize param_count, Type *results, isize result_count) {
|
Type *make_type_proc(gbAllocator a, Scope *scope, Type *params, isize param_count, Type *results, isize result_count, b32 variadic) {
|
||||||
Type *t = alloc_type(a, Type_Proc);
|
Type *t = alloc_type(a, Type_Proc);
|
||||||
t->Proc.scope = scope;
|
|
||||||
t->Proc.params = params;
|
if (variadic) {
|
||||||
t->Proc.param_count = param_count;
|
if (param_count == 0) {
|
||||||
t->Proc.results = results;
|
GB_PANIC("variadic procedure must have at least one parameter");
|
||||||
|
}
|
||||||
|
GB_ASSERT(params != NULL && params->kind == Type_Tuple);
|
||||||
|
Entity *e = params->Tuple.variables[param_count-1];
|
||||||
|
if (get_base_type(e->type)->kind != Type_Slice) {
|
||||||
|
// NOTE(bill): For custom calling convention
|
||||||
|
GB_PANIC("variadic parameter must be of type slice");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t->Proc.scope = scope;
|
||||||
|
t->Proc.params = params;
|
||||||
|
t->Proc.param_count = param_count;
|
||||||
|
t->Proc.results = results;
|
||||||
t->Proc.result_count = result_count;
|
t->Proc.result_count = result_count;
|
||||||
|
t->Proc.variadic = variadic;
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ void ssa_gen_code(ssaGen *s) {
|
|||||||
String name = make_string(SSA_STARTUP_RUNTIME_PROC_NAME);
|
String name = make_string(SSA_STARTUP_RUNTIME_PROC_NAME);
|
||||||
Type *proc_type = make_type_proc(a, gb_alloc_item(a, Scope),
|
Type *proc_type = make_type_proc(a, gb_alloc_item(a, Scope),
|
||||||
NULL, 0,
|
NULL, 0,
|
||||||
NULL, 0);
|
NULL, 0, false);
|
||||||
AstNode *body = gb_alloc_item(a, AstNode);
|
AstNode *body = gb_alloc_item(a, AstNode);
|
||||||
ssaValue *p = ssa_make_value_procedure(a, m, proc_type, NULL, body, name);
|
ssaValue *p = ssa_make_value_procedure(a, m, proc_type, NULL, body, name);
|
||||||
Token token = {};
|
Token token = {};
|
||||||
|
|||||||
+62
-6
@@ -2159,10 +2159,23 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
|||||||
auto *type = &proc_type_->Proc;
|
auto *type = &proc_type_->Proc;
|
||||||
|
|
||||||
isize arg_index = 0;
|
isize arg_index = 0;
|
||||||
isize arg_count = type->param_count;
|
|
||||||
ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, arg_count);
|
|
||||||
|
|
||||||
for (AstNode *arg = ce->arg_list; arg != NULL; arg = arg->next) {
|
isize arg_count = 0;
|
||||||
|
for (AstNode *a = ce->arg_list; a != NULL; a = a->next) {
|
||||||
|
Type *at = get_base_type(type_of_expr(proc->module->info, a));
|
||||||
|
if (at->kind == Type_Tuple) {
|
||||||
|
arg_count += at->Tuple.variable_count;
|
||||||
|
} else {
|
||||||
|
arg_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, arg_count);
|
||||||
|
b32 variadic = proc_type_->Proc.variadic;
|
||||||
|
|
||||||
|
AstNode *arg = ce->arg_list;
|
||||||
|
for (;
|
||||||
|
arg != NULL;
|
||||||
|
arg = arg->next) {
|
||||||
ssaValue *a = ssa_build_expr(proc, arg);
|
ssaValue *a = ssa_build_expr(proc, arg);
|
||||||
Type *at = ssa_type(a);
|
Type *at = ssa_type(a);
|
||||||
if (at->kind == Type_Tuple) {
|
if (at->kind == Type_Tuple) {
|
||||||
@@ -2176,11 +2189,54 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *pt = &proc_type_->Proc.params->Tuple;
|
auto *pt = &type->params->Tuple;
|
||||||
for (isize i = 0; i < arg_count; i++) {
|
|
||||||
args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type, true);
|
if (variadic) {
|
||||||
|
isize i = 0;
|
||||||
|
for (; i < type->param_count-1; i++) {
|
||||||
|
args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type, true);
|
||||||
|
}
|
||||||
|
Type *variadic_type = pt->variables[i]->type;
|
||||||
|
GB_ASSERT(is_type_slice(variadic_type));
|
||||||
|
variadic_type = get_base_type(variadic_type)->Slice.elem;
|
||||||
|
for (; i < arg_count; i++) {
|
||||||
|
args[i] = ssa_emit_conv(proc, args[i], variadic_type, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (isize i = 0; i < arg_count; i++) {
|
||||||
|
args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (variadic) {
|
||||||
|
gbAllocator allocator = proc->module->allocator;
|
||||||
|
Type *slice_type = pt->variables[type->param_count-1]->type;
|
||||||
|
Type *elem_type = get_base_type(slice_type)->Slice.elem;
|
||||||
|
Type *elem_ptr_type = make_type_pointer(allocator, elem_type);
|
||||||
|
ssaValue *slice = ssa_add_local_generated(proc, slice_type);
|
||||||
|
isize slice_len = arg_count+1 - type->param_count;
|
||||||
|
|
||||||
|
if (slice_len > 0) {
|
||||||
|
ssaValue *base_array = ssa_add_local_generated(proc, make_type_array(allocator, elem_type, slice_len));
|
||||||
|
|
||||||
|
for (isize i = type->param_count-1, j = 0; i < arg_count; i++, j++) {
|
||||||
|
ssaValue *addr = ssa_emit_struct_gep(proc, base_array, j, elem_type);
|
||||||
|
ssa_emit_store(proc, addr, args[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssaValue *base_elem = ssa_emit_struct_gep(proc, base_array, v_zero32, elem_type);
|
||||||
|
ssa_emit_store(proc, ssa_emit_struct_gep(proc, slice, v_zero32, elem_ptr_type), base_elem);
|
||||||
|
ssaValue *len = ssa_make_value_constant(allocator, t_int, make_exact_value_integer(slice_len));
|
||||||
|
ssa_emit_store(proc, ssa_emit_struct_gep(proc, slice, v_one32, t_int), len);
|
||||||
|
ssa_emit_store(proc, ssa_emit_struct_gep(proc, slice, v_two32, t_int), len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
arg_count = type->param_count;
|
||||||
|
args[arg_count-1] = ssa_emit_load(proc, slice);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return ssa_emit_call(proc, value, args, arg_count);
|
return ssa_emit_call(proc, value, args, arg_count);
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
|
|||||||
+105
-74
@@ -77,6 +77,10 @@ enum CallExprKind {
|
|||||||
AST_NODE_KIND(Invalid, "invalid node", struct{}) \
|
AST_NODE_KIND(Invalid, "invalid node", struct{}) \
|
||||||
AST_NODE_KIND(BasicLit, "basic literal", Token) \
|
AST_NODE_KIND(BasicLit, "basic literal", Token) \
|
||||||
AST_NODE_KIND(Ident, "identifier", Token) \
|
AST_NODE_KIND(Ident, "identifier", Token) \
|
||||||
|
AST_NODE_KIND(Ellipsis, "ellipsis", struct { \
|
||||||
|
Token token; \
|
||||||
|
AstNode *expr; \
|
||||||
|
}) \
|
||||||
AST_NODE_KIND(ProcLit, "procedure literal", struct { \
|
AST_NODE_KIND(ProcLit, "procedure literal", struct { \
|
||||||
AstNode *type; \
|
AstNode *type; \
|
||||||
AstNode *body; \
|
AstNode *body; \
|
||||||
@@ -110,7 +114,6 @@ AST_NODE_KIND(_ExprBegin, "", struct{}) \
|
|||||||
b32 triple_indexed; \
|
b32 triple_indexed; \
|
||||||
}) \
|
}) \
|
||||||
AST_NODE_KIND(FieldValue, "field value", struct { Token eq; AstNode *field, *value; }) \
|
AST_NODE_KIND(FieldValue, "field value", struct { Token eq; AstNode *field, *value; }) \
|
||||||
AST_NODE_KIND(Ellipsis, "ellipsis", struct { Token token; }) \
|
|
||||||
AST_NODE_KIND(_ExprEnd, "", struct{}) \
|
AST_NODE_KIND(_ExprEnd, "", struct{}) \
|
||||||
AST_NODE_KIND(_StmtBegin, "", struct{}) \
|
AST_NODE_KIND(_StmtBegin, "", struct{}) \
|
||||||
AST_NODE_KIND(BadStmt, "bad statement", struct { Token begin, end; }) \
|
AST_NODE_KIND(BadStmt, "bad statement", struct { Token begin, end; }) \
|
||||||
@@ -542,11 +545,6 @@ gb_inline AstNode *make_deref_expr(AstFile *f, AstNode *expr, Token op) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
gb_inline AstNode *make_ellipsis(AstFile *f, Token token) {
|
|
||||||
AstNode *result = make_node(f, AstNode_Ellipsis);
|
|
||||||
result->Ellipsis.token = token;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
gb_inline AstNode *make_basic_lit(AstFile *f, Token basic_lit) {
|
gb_inline AstNode *make_basic_lit(AstFile *f, Token basic_lit) {
|
||||||
AstNode *result = make_node(f, AstNode_BasicLit);
|
AstNode *result = make_node(f, AstNode_BasicLit);
|
||||||
result->BasicLit = basic_lit;
|
result->BasicLit = basic_lit;
|
||||||
@@ -559,6 +557,14 @@ gb_inline AstNode *make_ident(AstFile *f, Token token) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gb_inline AstNode *make_ellipsis(AstFile *f, Token token, AstNode *expr) {
|
||||||
|
AstNode *result = make_node(f, AstNode_Ellipsis);
|
||||||
|
result->Ellipsis.token = token;
|
||||||
|
result->Ellipsis.expr = expr;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
gb_inline AstNode *make_proc_lit(AstFile *f, AstNode *type, AstNode *body, u64 tags) {
|
gb_inline AstNode *make_proc_lit(AstFile *f, AstNode *type, AstNode *body, u64 tags) {
|
||||||
AstNode *result = make_node(f, AstNode_ProcLit);
|
AstNode *result = make_node(f, AstNode_ProcLit);
|
||||||
result->ProcLit.type = type;
|
result->ProcLit.type = type;
|
||||||
@@ -1253,6 +1259,37 @@ b32 is_literal_type(AstNode *node) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AstNode *parse_call_expr(AstFile *f, AstNode *operand) {
|
||||||
|
AstNode *arg_list = NULL;
|
||||||
|
AstNode *arg_list_curr = NULL;
|
||||||
|
isize arg_list_count = 0;
|
||||||
|
Token open_paren, close_paren;
|
||||||
|
|
||||||
|
f->expr_level++;
|
||||||
|
open_paren = expect_token(f, Token_OpenParen);
|
||||||
|
|
||||||
|
while (f->cursor[0].kind != Token_CloseParen &&
|
||||||
|
f->cursor[0].kind != Token_EOF) {
|
||||||
|
if (f->cursor[0].kind == Token_Comma)
|
||||||
|
ast_file_err(f, f->cursor[0], "Expected an expression not a ,");
|
||||||
|
|
||||||
|
DLIST_APPEND(arg_list, arg_list_curr, parse_expr(f, false));
|
||||||
|
arg_list_count++;
|
||||||
|
|
||||||
|
if (f->cursor[0].kind != Token_Comma) {
|
||||||
|
if (f->cursor[0].kind == Token_CloseParen)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_token(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
f->expr_level--;
|
||||||
|
close_paren = expect_token(f, Token_CloseParen);
|
||||||
|
|
||||||
|
return make_call_expr(f, operand, arg_list, arg_list_count, open_paren, close_paren);
|
||||||
|
}
|
||||||
|
|
||||||
AstNode *parse_atom_expr(AstFile *f, b32 lhs) {
|
AstNode *parse_atom_expr(AstFile *f, b32 lhs) {
|
||||||
AstNode *operand = parse_operand(f, lhs);
|
AstNode *operand = parse_operand(f, lhs);
|
||||||
|
|
||||||
@@ -1273,34 +1310,7 @@ AstNode *parse_atom_expr(AstFile *f, b32 lhs) {
|
|||||||
if (lhs) {
|
if (lhs) {
|
||||||
// TODO(bill): Handle this shit! Is this even allowed in this language?!
|
// TODO(bill): Handle this shit! Is this even allowed in this language?!
|
||||||
}
|
}
|
||||||
AstNode *arg_list = NULL;
|
operand = parse_call_expr(f, operand);
|
||||||
AstNode *arg_list_curr = NULL;
|
|
||||||
isize arg_list_count = 0;
|
|
||||||
Token open_paren, close_paren;
|
|
||||||
|
|
||||||
f->expr_level++;
|
|
||||||
open_paren = expect_token(f, Token_OpenParen);
|
|
||||||
|
|
||||||
while (f->cursor[0].kind != Token_CloseParen &&
|
|
||||||
f->cursor[0].kind != Token_EOF) {
|
|
||||||
if (f->cursor[0].kind == Token_Comma)
|
|
||||||
ast_file_err(f, f->cursor[0], "Expected an expression not a ,");
|
|
||||||
|
|
||||||
DLIST_APPEND(arg_list, arg_list_curr, parse_expr(f, false));
|
|
||||||
arg_list_count++;
|
|
||||||
|
|
||||||
if (f->cursor[0].kind != Token_Comma) {
|
|
||||||
if (f->cursor[0].kind == Token_CloseParen)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
next_token(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
f->expr_level--;
|
|
||||||
close_paren = expect_token(f, Token_CloseParen);
|
|
||||||
|
|
||||||
operand = make_call_expr(f, operand, arg_list, arg_list_count, open_paren, close_paren);
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Token_Period: {
|
case Token_Period: {
|
||||||
@@ -1617,34 +1627,6 @@ AstNode *parse_type(AstFile *f) {
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNode *parse_field_decl(AstFile *f, b32 allow_using) {
|
|
||||||
b32 is_using = false;
|
|
||||||
AstNode *name_list = NULL;
|
|
||||||
isize name_count = 0;
|
|
||||||
if (allow_using) {
|
|
||||||
if (allow_token(f, Token_using)) {
|
|
||||||
is_using = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
name_list = parse_lhs_expr_list(f, &name_count);
|
|
||||||
if (name_count == 0)
|
|
||||||
ast_file_err(f, f->cursor[0], "Empty field declaration");
|
|
||||||
|
|
||||||
if (name_count > 1 && is_using) {
|
|
||||||
ast_file_err(f, f->cursor[0], "Cannot apply `using` to more than one of the same type");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
expect_token(f, Token_Colon);
|
|
||||||
|
|
||||||
AstNode *type = parse_type_attempt(f);
|
|
||||||
if (type == NULL)
|
|
||||||
ast_file_err(f, f->cursor[0], "Expected a type for this field declaration");
|
|
||||||
|
|
||||||
AstNode *field = make_field(f, name_list, name_count, type, is_using);
|
|
||||||
return field;
|
|
||||||
}
|
|
||||||
|
|
||||||
Token parse_procedure_signature(AstFile *f,
|
Token parse_procedure_signature(AstFile *f,
|
||||||
AstNode **param_list, isize *param_count,
|
AstNode **param_list, isize *param_count,
|
||||||
@@ -1661,20 +1643,65 @@ AstNode *parse_proc_type(AstFile *f) {
|
|||||||
return make_proc_type(f, proc_token, params, param_count, results, result_count);
|
return make_proc_type(f, proc_token, params, param_count, results, result_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AstNode *parse_field_decl(AstFile *f) {
|
||||||
|
AstNode *name_list = NULL;
|
||||||
|
isize name_count = 0;
|
||||||
|
b32 is_using = false;
|
||||||
|
if (allow_token(f, Token_using)) {
|
||||||
|
is_using = true;
|
||||||
|
}
|
||||||
|
|
||||||
AstNode *parse_parameter_list(AstFile *f, isize *param_count_, TokenKind separator, b32 allow_using) {
|
name_list = parse_lhs_expr_list(f, &name_count);
|
||||||
|
if (name_count == 0) {
|
||||||
|
ast_file_err(f, f->cursor[0], "Empty field declaration");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name_count > 1 && is_using) {
|
||||||
|
ast_file_err(f, f->cursor[0], "Cannot apply `using` to more than one of the same type");
|
||||||
|
is_using = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
expect_token(f, Token_Colon);
|
||||||
|
|
||||||
|
AstNode *type = NULL;
|
||||||
|
if (f->cursor[0].kind == Token_Ellipsis) {
|
||||||
|
Token ellipsis = f->cursor[0];
|
||||||
|
next_token(f);
|
||||||
|
type = parse_type_attempt(f);
|
||||||
|
if (type == NULL) {
|
||||||
|
ast_file_err(f, f->cursor[0], "variadic parameter is missing a type after `..`");
|
||||||
|
type = make_bad_expr(f, ellipsis, f->cursor[0]);
|
||||||
|
} else {
|
||||||
|
if (name_count > 1) {
|
||||||
|
ast_file_err(f, f->cursor[0], "mutliple variadic parameters, only 1 is allowed");
|
||||||
|
type = make_bad_expr(f, ellipsis, f->cursor[0]);
|
||||||
|
} else {
|
||||||
|
type = make_ellipsis(f, ellipsis, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
type = parse_type_attempt(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == NULL) {
|
||||||
|
ast_file_err(f, f->cursor[0], "Expected a type for this field declaration");
|
||||||
|
}
|
||||||
|
|
||||||
|
AstNode *field = make_field(f, name_list, name_count, type, is_using);
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
AstNode *parse_parameter_list(AstFile *f, isize *param_count_) {
|
||||||
AstNode *param_list = NULL;
|
AstNode *param_list = NULL;
|
||||||
AstNode *param_list_curr = NULL;
|
AstNode *param_list_curr = NULL;
|
||||||
isize param_count = 0;
|
isize param_count = 0;
|
||||||
while (f->cursor[0].kind == Token_Identifier ||
|
while (f->cursor[0].kind == Token_Identifier ||
|
||||||
(allow_using && f->cursor[0].kind == Token_using)) {
|
f->cursor[0].kind == Token_using) {
|
||||||
if (!allow_using && allow_token(f, Token_using)) {
|
AstNode *field = parse_field_decl(f);
|
||||||
ast_file_err(f, f->cursor[-1], "`using` is only allowed within structures (at the moment)");
|
|
||||||
}
|
|
||||||
AstNode *field = parse_field_decl(f, allow_using);
|
|
||||||
DLIST_APPEND(param_list, param_list_curr, field);
|
DLIST_APPEND(param_list, param_list_curr, field);
|
||||||
param_count += field->Field.name_count;
|
param_count += field->Field.name_count;
|
||||||
if (f->cursor[0].kind != separator)
|
if (f->cursor[0].kind != Token_Comma)
|
||||||
break;
|
break;
|
||||||
next_token(f);
|
next_token(f);
|
||||||
}
|
}
|
||||||
@@ -1746,14 +1773,18 @@ AstNode *parse_struct_params(AstFile *f, isize *decl_count_) {
|
|||||||
AstNode *parse_identifier_or_type(AstFile *f) {
|
AstNode *parse_identifier_or_type(AstFile *f) {
|
||||||
switch (f->cursor[0].kind) {
|
switch (f->cursor[0].kind) {
|
||||||
case Token_Identifier: {
|
case Token_Identifier: {
|
||||||
AstNode *ident = parse_identifier(f);
|
AstNode *e = parse_identifier(f);
|
||||||
while (f->cursor[0].kind == Token_Period) {
|
while (f->cursor[0].kind == Token_Period) {
|
||||||
Token token = f->cursor[0];
|
Token token = f->cursor[0];
|
||||||
next_token(f);
|
next_token(f);
|
||||||
AstNode *sel = parse_identifier(f);
|
AstNode *sel = parse_identifier(f);
|
||||||
ident = make_selector_expr(f, token, ident, sel);
|
e = make_selector_expr(f, token, e, sel);
|
||||||
}
|
}
|
||||||
return ident;
|
if (f->cursor[0].kind == Token_OpenParen) {
|
||||||
|
// HACK NOTE(bill): For type_of_val(expr)
|
||||||
|
e = parse_call_expr(f, e);
|
||||||
|
}
|
||||||
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Token_Pointer:
|
case Token_Pointer:
|
||||||
@@ -1765,7 +1796,7 @@ AstNode *parse_identifier_or_type(AstFile *f) {
|
|||||||
AstNode *count_expr = NULL;
|
AstNode *count_expr = NULL;
|
||||||
|
|
||||||
if (f->cursor[0].kind == Token_Ellipsis) {
|
if (f->cursor[0].kind == Token_Ellipsis) {
|
||||||
count_expr = make_ellipsis(f, f->cursor[0]);
|
count_expr = make_ellipsis(f, f->cursor[0], NULL);
|
||||||
next_token(f);
|
next_token(f);
|
||||||
} else if (f->cursor[0].kind != Token_CloseBracket) {
|
} else if (f->cursor[0].kind != Token_CloseBracket) {
|
||||||
count_expr = parse_expr(f, false);
|
count_expr = parse_expr(f, false);
|
||||||
@@ -1922,7 +1953,7 @@ Token parse_procedure_signature(AstFile *f,
|
|||||||
AstNode **result_list, isize *result_count) {
|
AstNode **result_list, isize *result_count) {
|
||||||
Token proc_token = expect_token(f, Token_proc);
|
Token proc_token = expect_token(f, Token_proc);
|
||||||
expect_token(f, Token_OpenParen);
|
expect_token(f, Token_OpenParen);
|
||||||
*param_list = parse_parameter_list(f, param_count, Token_Comma, true);
|
*param_list = parse_parameter_list(f, param_count);
|
||||||
expect_token(f, Token_CloseParen);
|
expect_token(f, Token_CloseParen);
|
||||||
*result_list = parse_results(f, result_count);
|
*result_list = parse_results(f, result_count);
|
||||||
return proc_token;
|
return proc_token;
|
||||||
|
|||||||
Reference in New Issue
Block a user