diff options
| author | Ginger Bill <bill@gingerbill.org> | 2016-09-03 12:41:03 +0100 |
|---|---|---|
| committer | Ginger Bill <bill@gingerbill.org> | 2016-09-03 12:41:03 +0100 |
| commit | 11205f968ad2bf4893fa75df3fc3331f960039d1 (patch) | |
| tree | 9f6504a674e1fc59346b98df961c20c773db2227 /src/checker | |
| parent | e1a6775661d1ef57b84effa9b4c567c030b87556 (diff) | |
Typesafe variadic procedures
Diffstat (limited to 'src/checker')
| -rw-r--r-- | src/checker/checker.cpp | 2 | ||||
| -rw-r--r-- | src/checker/expr.cpp | 102 | ||||
| -rw-r--r-- | src/checker/stmt.cpp | 2 | ||||
| -rw-r--r-- | src/checker/type.cpp | 25 |
4 files changed, 108 insertions, 23 deletions
diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp index 50d122209..8c95281fc 100644 --- a/src/checker/checker.cpp +++ b/src/checker/checker.cpp @@ -132,6 +132,7 @@ enum BuiltinProcId { BuiltinProc_align_of_val, BuiltinProc_offset_of, BuiltinProc_offset_of_val, + BuiltinProc_type_of_val, BuiltinProc_assert, BuiltinProc_len, @@ -170,6 +171,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { {STR_LIT("align_of_val"), 1, false, Expr_Expr}, {STR_LIT("offset_of"), 2, 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("len"), 1, false, Expr_Expr}, diff --git a/src/checker/expr.cpp b/src/checker/expr.cpp index ca3667108..dd49e5452 100644 --- a/src/checker/expr.cpp +++ b/src/checker/expr.cpp @@ -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; diff --git a/src/checker/stmt.cpp b/src/checker/stmt.cpp index 3508321c7..803e777de 100644 --- a/src/checker/stmt.cpp +++ b/src/checker/stmt.cpp @@ -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) { 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; ast_node(pd, ProcDecl, d->proc_decl); check_open_scope(c, pd->type); diff --git a/src/checker/type.cpp b/src/checker/type.cpp index 2ff004636..16453b4dc 100644 --- a/src/checker/type.cpp +++ b/src/checker/type.cpp @@ -149,6 +149,7 @@ struct Type { Type * results; // Type_Tuple isize param_count; isize result_count; + b32 variadic; } Proc; }; }; @@ -240,13 +241,27 @@ Type *make_type_tuple(gbAllocator a) { 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); - t->Proc.scope = scope; - t->Proc.params = params; - t->Proc.param_count = param_count; - t->Proc.results = results; + + if (variadic) { + if (param_count == 0) { + 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.variadic = variadic; return t; } |