Typesafe variadic procedures

This commit is contained in:
Ginger Bill
2016-09-03 12:41:03 +01:00
parent e1a6775661
commit 11205f968a
9 changed files with 297 additions and 112 deletions

View File

@@ -459,10 +459,12 @@ void check_enum_type(Checker *c, Type *enum_type, AstNode *node) {
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)
return NULL;
b32 is_variadic = false;
Type *tuple = make_type_tuple(c->allocator);
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);
AstNode *type_expr = f->type;
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);
for (AstNode *name = f->name_list; name != NULL; name = name->next) {
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);
variables[variable_index++] = param;
} 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.variable_count = field_count;
if (is_variadic_) *is_variadic_ = is_variadic;
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);
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->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.results = results;
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;
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);
error(&c->error_collector, ast_node_token(e), "`%s` is not a type", err_str);
break;
} break;
}
type = t_invalid;
@@ -1894,7 +1926,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
case BuiltinProc_size_of_val:
// 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)
return false;
@@ -1917,7 +1949,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
case BuiltinProc_align_of_val:
// 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)
return false;
@@ -1996,6 +2028,14 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
operand->type = t_int;
} 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:
// 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 param_index = 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;
}
if (ce->arg_list_count == 0 && param_count == 0)
return;
if (ce->arg_list_count == 0) {
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;
} else {
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;
if (operand->type->kind != Type_Tuple) {
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++;
} else {
auto *tuple = &operand->type->Tuple;
isize i = 0;
for (;
i < tuple->variable_count && param_index < param_count;
i++, param_index++) {
i < tuple->variable_count && (param_index < param_count && !variadic);
i++) {
Entity *e = tuple->variables[i];
operand->type = e->type;
operand->mode = Addressing_Value;
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) {
@@ -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;
}
if (param_index < param_count) {
if ((!variadic && param_index < param_count) ||
(variadic && param_index < param_count-1)) {
error_code = -1;
} else if (call_arg != NULL && call_arg->next != NULL) {
error_code = +1;