From 1ced92be473ef6f1c6aa0058bcf89c4ded684379 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sun, 25 Jun 2017 22:15:30 +0100 Subject: [PATCH] Rudimentary para-poly procedures --- code/demo.odin | 5 +- src/check_decl.cpp | 11 +++- src/check_expr.cpp | 154 +++++++++++++++++++++++++++++++-------------- src/checker.cpp | 54 ++++++++++------ src/gb/gb.h | 2 +- src/ir.cpp | 19 +++++- src/ir_print.cpp | 14 +++-- src/types.cpp | 4 ++ 8 files changed, 186 insertions(+), 77 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index 962b25db1..4639588e1 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -3,11 +3,14 @@ import ( ) proc new_type(T: type) -> ^T { - return ^T(alloc_align(size_of(T), align_of(T))); + return ^T(alloc(size_of(T), align_of(T))); } proc main() { var ptr = new_type(int); + ptr^ = 123; + + fmt.println(ptr^); } /* diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 672673faf..602e09a0a 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -307,7 +307,12 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { return; } - Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0, false, ProcCC_Odin); + Type *proc_type = e->type; + if (d->gen_proc_type != NULL) { + proc_type = d->gen_proc_type; + } else { + proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0, false, ProcCC_Odin); + } e->type = proc_type; ast_node(pd, ProcDecl, d->proc_decl); @@ -575,7 +580,9 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod TypeTuple *params = &type->Proc.params->Tuple; for (isize i = 0; i < params->variable_count; i++) { Entity *e = params->variables[i]; - GB_ASSERT(e->kind == Entity_Variable); + if (e->kind != Entity_Variable) { + continue; + } if (!(e->flags & EntityFlag_Using)) { continue; } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 6696af6a4..f61809078 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1,3 +1,37 @@ +enum CallArgumentError { + CallArgumentError_None, + CallArgumentError_NoneProcedureType, + CallArgumentError_WrongTypes, + CallArgumentError_NonVariadicExpand, + CallArgumentError_VariadicTuple, + CallArgumentError_MultipleVariadicExpand, + CallArgumentError_ArgumentCount, + CallArgumentError_TooFewArguments, + CallArgumentError_TooManyArguments, + CallArgumentError_InvalidFieldValue, + CallArgumentError_ParameterNotFound, + CallArgumentError_ParameterMissing, + CallArgumentError_DuplicateParameter, + CallArgumentError_GenericProcedureNotSupported, +}; + +enum CallArgumentErrorMode { + CallArgumentMode_NoErrors, + CallArgumentMode_ShowErrors, +}; + +struct CallArgumentData { + Entity *gen_entity; + i64 score; + Type * result_type; +}; + + +#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(Checker *c, AstNode *call, Type *proc_type, Entity *entity, Array operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data) +typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType); + + + void check_expr (Checker *c, Operand *operand, AstNode *expression); void check_multi_expr (Checker *c, Operand *operand, AstNode *expression); void check_expr_or_type (Checker *c, Operand *operand, AstNode *expression); @@ -19,7 +53,7 @@ void check_stmt (Checker *c, AstNode *node, u32 flags); void check_stmt_list (Checker *c, Array stmts, u32 flags); void check_init_constant (Checker *c, Entity *e, Operand *operand); bool check_representable_as_constant(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value); -Type * check_call_arguments (Checker *c, Operand *operand, Type *proc_type, AstNode *call); +CallArgumentData check_call_arguments (Checker *c, Operand *operand, Type *proc_type, AstNode *call); void error_operand_not_expression(Operand *o) { @@ -1173,13 +1207,16 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari if (ast_node_expect(name, AstNode_Ident)) { Entity *param = NULL; bool is_generic = type->kind == Type_Generic; + Type *gen_type = type; if (operands != NULL) { Operand o = (*operands)[j]; is_generic = o.mode == Addressing_Type && o.type == type; + if (is_generic) gen_type = o.type; } if (is_generic) { - param = make_entity_type_name(c->allocator, scope, name->Ident, type); + param = make_entity_type_name(c->allocator, scope, name->Ident, gen_type); + param->TypeName.is_type_alias = true; } else { param = make_entity_param(c->allocator, scope, name->Ident, type, (p->flags&FieldFlag_using) != 0, false); @@ -4858,28 +4895,6 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id return true; } -enum CallArgumentError { - CallArgumentError_None, - CallArgumentError_NoneProcedureType, - CallArgumentError_WrongTypes, - CallArgumentError_NonVariadicExpand, - CallArgumentError_VariadicTuple, - CallArgumentError_MultipleVariadicExpand, - CallArgumentError_ArgumentCount, - CallArgumentError_TooFewArguments, - CallArgumentError_TooManyArguments, - CallArgumentError_InvalidFieldValue, - CallArgumentError_ParameterNotFound, - CallArgumentError_ParameterMissing, - CallArgumentError_DuplicateParameter, - CallArgumentError_GenericProcedureNotSupported, -}; - -enum CallArgumentErrorMode { - CallArgumentMode_NoErrors, - CallArgumentMode_ShowErrors, -}; - struct ValidProcAndScore { isize index; @@ -4934,8 +4949,7 @@ bool check_unpack_arguments(Checker *c, isize lhs_count, Array *operand return optional_ok; } -#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(Checker *c, AstNode *call, Type *proc_type, Array operands, CallArgumentErrorMode show_error_mode, i64 *score_, Type **result_type_) -typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType); + CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { @@ -4984,6 +4998,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { CallArgumentError err = CallArgumentError_None; Type *final_proc_type = proc_type; + Entity *gen_entity = NULL; if (vari_expand && !variadic) { if (show_error) { @@ -5022,14 +5037,44 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { gb_string_free(proc_str); } } else { + // NOTE(bill): Generate the procedure type for this generic instance + // TODO(bill): Clean this shit up! if (pt->is_generic) { - Scope *scope = make_scope(pt->scope->parent, c->allocator); - CheckerContext prev = c->context; - defer (c->context = prev); - c->context.scope = scope; + GB_ASSERT(entity != NULL); + DeclInfo *old_decl = decl_info_of_entity(&c->info, entity); + GB_ASSERT(old_decl != NULL); - final_proc_type = alloc_type(c->allocator, Type_Proc); + gbAllocator a = heap_allocator(); + + Scope *scope = entity->scope; + + AstNode *proc_decl = clone_ast_node(a, old_decl->proc_decl); + ast_node(pd, ProcDecl, proc_decl); + + check_open_scope(c, pd->type); + defer (check_close_scope(c)); + + final_proc_type = make_type_proc(c->allocator, c->context.scope, NULL, 0, NULL, 0, false, pt->calling_convention); check_procedure_type(c, final_proc_type, pt->node, &operands); + + u64 tags = entity->Procedure.tags; + AstNode *ident = clone_ast_node(a, entity->identifier); + Token token = ident->Ident; + DeclInfo *d = make_declaration_info(c->allocator, c->context.scope, old_decl->parent); + d->gen_proc_type = final_proc_type; + d->type_expr = pd->type; + d->proc_decl = proc_decl; + + gen_entity = make_entity_procedure(c->allocator, entity->scope, token, final_proc_type, tags); + gen_entity->identifier = ident; + + add_entity_and_decl_info(c, ident, gen_entity, d); + add_entity_definition(&c->info, ident, gen_entity); + + add_entity_use(c, ident, gen_entity); + add_entity_use(c, ce->proc, gen_entity); + + check_procedure_later(c, c->curr_ast_file, token, d, final_proc_type, pd->body, tags); } @@ -5082,7 +5127,11 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { if (show_error) { error(o.expr, "`..` in a variadic procedure can only have one variadic argument at the end"); } - if (score_) *score_ = score; + if (data) { + data->score = score; + data->result_type = final_proc_type->Proc.results; + data->gen_entity = gen_entity; + } return CallArgumentError_MultipleVariadicExpand; } } @@ -5099,8 +5148,11 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { } } - if (score_) *score_ = score; - if (result_type_) *result_type_ = final_proc_type->Proc.results; + if (data) { + data->score = score; + data->result_type = final_proc_type->Proc.results; + data->gen_entity = gen_entity; + } return err; } @@ -5250,14 +5302,17 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) { err = CallArgumentError_GenericProcedureNotSupported; } - if (score_) *score_ = score; - if (result_type_) *result_type_ = pt->results; + if (data) { + data->score = score; + data->result_type = pt->results; + data->gen_entity = NULL; + } return err; } -Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode *call) { +CallArgumentData check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode *call) { ast_node(ce, CallExpr, call); CallArgumentCheckerType *call_checker = check_call_arguments_internal; @@ -5310,11 +5365,11 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod Entity *p = procs[i]; Type *pt = base_type(p->type); if (pt != NULL && is_type_proc(pt)) { - i64 score = 0; - CallArgumentError err = call_checker(c, call, pt, operands, CallArgumentMode_NoErrors, &score, &result_type); + CallArgumentData data = {}; + CallArgumentError err = call_checker(c, call, pt, p, operands, CallArgumentMode_NoErrors, &data); if (err == CallArgumentError_None) { valids[valid_count].index = i; - valids[valid_count].score = score; + valids[valid_count].score = data.score; valid_count++; } } @@ -5355,15 +5410,20 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod Entity *e = procs[valids[0].index]; add_entity_use(c, expr, e); proc_type = e->type; - i64 score = 0; - CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_ShowErrors, &score, &result_type); + CallArgumentData data = {}; + CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data); + return data; } } else { - i64 score = 0; - CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_ShowErrors, &score, &result_type); + Entity *e = entity_of_ident(&c->info, operand->expr); + CallArgumentData data = {}; + CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data); + return data; } - return result_type; + CallArgumentData data = {}; + data.result_type = t_invalid; + return data; } @@ -5498,8 +5558,8 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) { } } - Type *result_type = check_call_arguments(c, operand, proc_type, call); - + CallArgumentData data = check_call_arguments(c, operand, proc_type, call); + Type *result_type = data.result_type; gb_zero_item(operand); operand->expr = call; diff --git a/src/checker.cpp b/src/checker.cpp index 5fe030331..10afa06fd 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -188,6 +188,7 @@ struct DeclInfo { AstNode * type_expr; AstNode * init_expr; AstNode * proc_decl; // AstNode_ProcDecl + Type * gen_proc_type; // Precalculated Map deps; // Key: Entity * Array labels; @@ -202,7 +203,7 @@ struct ProcedureInfo { DeclInfo * decl; Type * type; // Type_Procedure AstNode * body; // AstNode_BlockStmt - u32 tags; + u64 tags; }; // ExprInfo stores information used for "untyped" expressions @@ -266,6 +267,7 @@ struct CheckerContext { AstNode * curr_foreign_library; }; + // CheckerInfo stores all the symbol information for a type-checked program struct CheckerInfo { Map types; // Key: AstNode * | Expression -> Type (and value) @@ -277,7 +279,6 @@ struct CheckerInfo { Map entities; // Key: Entity * Map foreigns; // Key: String Map files; // Key: String (full path) - Map type_info_map; // Key: Type * isize type_info_count; }; @@ -286,28 +287,29 @@ struct Checker { Parser * parser; CheckerInfo info; - AstFile * curr_ast_file; - Scope * global_scope; + AstFile * curr_ast_file; + Scope * global_scope; // NOTE(bill): Procedures to check - Map procs; // Key: DeclInfo * - Array delayed_imports; - Array delayed_foreign_libraries; - Array file_nodes; + Map procs; // Key: DeclInfo * + Map > gen_procs; + Array delayed_imports; + Array delayed_foreign_libraries; + Array file_nodes; - gbArena arena; - gbArena tmp_arena; - gbAllocator allocator; - gbAllocator tmp_allocator; + gbArena arena; + gbArena tmp_arena; + gbAllocator allocator; + gbAllocator tmp_allocator; - CheckerContext context; + CheckerContext context; - Array proc_stack; - bool done_preload; + Array proc_stack; + bool done_preload; }; -HashKey hash_node (AstNode *node) { return hash_ptr_and_id(node, 0); } +HashKey hash_node (AstNode *node) { return hash_pointer(node); } HashKey hash_ast_file (AstFile *file) { return hash_pointer(file); } HashKey hash_entity (Entity *e) { return hash_pointer(e); } HashKey hash_type (Type *t) { return hash_pointer(t); } @@ -741,6 +743,7 @@ void init_checker(Checker *c, Parser *parser) { array_init(&c->proc_stack, a); map_init(&c->procs, a); + map_init(&c->gen_procs, a); array_init(&c->delayed_imports, a); array_init(&c->delayed_foreign_libraries, a); array_init(&c->file_nodes, a); @@ -778,6 +781,7 @@ void destroy_checker(Checker *c) { destroy_scope(c->global_scope); array_free(&c->proc_stack); map_destroy(&c->procs); + map_destroy(&c->gen_procs); array_free(&c->delayed_imports); array_free(&c->delayed_foreign_libraries); array_free(&c->file_nodes); @@ -946,7 +950,7 @@ void add_entity_definition(CheckerInfo *i, AstNode *identifier, Entity *entity) HashKey key = hash_node(identifier); map_set(&i->definitions, key, entity); } else { - // NOTE(bill): Error should handled elsewhere + // NOTE(bill): Error should be handled elsewhere } } @@ -1161,7 +1165,7 @@ void add_type_info_type(Checker *c, Type *t) { } -void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *decl, Type *type, AstNode *body, u32 tags) { +void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *decl, Type *type, AstNode *body, u64 tags) { ProcedureInfo info = {}; info.file = file; info.token = token; @@ -1204,6 +1208,14 @@ void add_dependency_to_map(Map *map, CheckerInfo *info, Entity *entity if (entity == NULL) { return; } + if (entity->type != NULL && + is_type_gen_proc(entity->type)) { + DeclInfo *decl = decl_info_of_entity(info, entity); + if (decl->gen_proc_type == NULL) { + return; + } + } + if (map_get(map, hash_entity(entity)) != NULL) { return; } @@ -2241,8 +2253,10 @@ void check_parsed_files(Checker *c) { TypeProc *pt = &pi->type->Proc; if (pt->is_generic) { - error(pi->token, "Generic procedures are not yet supported"); - continue; + if (pi->decl->gen_proc_type == NULL) { + continue; + } + // gb_printf_err("Generic procedure `%.*s` -> %s\n", LIT(pi->token.string), type_to_string(pi->decl->gen_proc_type)); } add_curr_ast_file(c, pi->file); diff --git a/src/gb/gb.h b/src/gb/gb.h index 2ef21c7da..457e5306c 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -3602,7 +3602,7 @@ extern "C" { #endif void gb_assert_handler(char const *condition, char const *file, i32 line, char const *msg, ...) { - gb_printf_err("%s:%d: Assert Failure: ", file, line); + gb_printf_err("%s(%d): Assert Failure: ", file, line); if (condition) gb_printf_err( "`%s` ", condition); if (msg) { diff --git a/src/ir.cpp b/src/ir.cpp index cd611ea44..313cb2a3c 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1510,7 +1510,11 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, irValue **args, isize arg_ GB_ASSERT(param_count == arg_count); } for (isize i = 0; i < param_count; i++) { - Type *original_type = pt->Proc.params->Tuple.variables[i]->type; + Entity *e = pt->Proc.params->Tuple.variables[i]; + if (e->kind != Entity_Variable) { + continue; + } + Type *original_type = e->type; Type *new_type = pt->Proc.abi_compat_params[i]; if (original_type != new_type) { if (is_type_pointer(new_type)) { @@ -4360,6 +4364,11 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { TypeAndValue tv = type_and_value_of_expr(proc->module->info, expr); GB_ASSERT(tv.mode != Addressing_Invalid); + if (tv.mode == Addressing_Type) { + // TODO(bill): Handle this correctly + return ir_value_nil(proc->module->allocator, tv.type); + } + if (tv.value.kind != ExactValue_Invalid) { // NOTE(bill): Edge case if (tv.value.kind != ExactValue_Compound && @@ -4417,7 +4426,7 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { } else if (e != NULL && e->kind == Entity_Variable) { return ir_addr_load(proc, ir_build_addr(proc, expr)); } - GB_PANIC("NULL value for expression from identifier: %.*s", LIT(i->string)); + GB_PANIC("NULL value for expression from identifier: %.*s @ %p", LIT(i->string), expr); return NULL; case_end; @@ -6911,6 +6920,10 @@ void ir_begin_procedure_body(irProcedure *proc) { AstNode *name = field->names[q_index++]; Entity *e = params->variables[i]; + if (e->kind != Entity_Variable) { + continue; + } + Type *abi_type = proc->type->Proc.abi_compat_params[i]; if (e->token.string != "" && e->token.string != "_") { @@ -7385,6 +7398,8 @@ void ir_gen_tree(irGen *s) { ast_node(pd, ProcDecl, decl->proc_decl); String original_name = name; AstNode *body = pd->body; + + if (e->Procedure.is_foreign) { name = e->token.string; // NOTE(bill): Don't use the mangled name ir_add_foreign_library_path(m, e->Procedure.foreign_library); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 29d9fac7d..195e61e84 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1280,6 +1280,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { } + isize param_index = 0; if (call->arg_count > 0) { TypeTuple *params = &proc_type->Proc.params->Tuple; if (proc_type->Proc.c_vararg) { @@ -1287,8 +1288,9 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { for (; i < params->variable_count-1; i++) { Entity *e = params->variables[i]; GB_ASSERT(e != NULL); + if (e->kind != Entity_Variable) continue; Type *t = proc_type->Proc.abi_compat_params[i]; - if (i > 0) { + if (param_index > 0) { ir_fprintf(f, ", "); } ir_print_type(f, m, t); @@ -1298,9 +1300,10 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { ir_fprintf(f, " "); irValue *arg = call->args[i]; ir_print_value(f, m, arg, t); + param_index++; } for (; i < call->arg_count; i++) { - if (i > 0) { + if (param_index > 0) { ir_fprintf(f, ", "); } @@ -1309,6 +1312,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { ir_print_type(f, m, t); ir_fprintf(f, " "); ir_print_value(f, m, arg, t); + param_index++; } } else { GB_ASSERT(call->arg_count == params->variable_count); @@ -1316,9 +1320,10 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { for (isize i = 0; i < param_count; i++) { Entity *e = params->variables[i]; GB_ASSERT(e != NULL); + if (e->kind != Entity_Variable) continue; irValue *arg = call->args[i]; Type *t = proc_type->Proc.abi_compat_params[i]; - if (i > 0) { + if (param_index > 0) { ir_fprintf(f, ", "); } ir_print_type(f, m, t); @@ -1327,11 +1332,12 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { } ir_fprintf(f, " "); ir_print_value(f, m, arg, t); + param_index++; } } } if (proc_type->Proc.calling_convention == ProcCC_Odin) { - if (proc_type->Proc.param_count > 0) { + if (param_index > 0) { ir_fprintf(f, ", "); } ir_print_type(f, m, t_context_ptr); diff --git a/src/types.cpp b/src/types.cpp index f6399a7a0..8f645b431 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2234,6 +2234,10 @@ gbString write_type_to_string(gbString str, Type *type) { str = gb_string_append_length(str, type->Basic.name.text, type->Basic.name.len); break; + case Type_Generic: + str = gb_string_appendc(str, "type"); + break; + case Type_Pointer: str = gb_string_appendc(str, "^"); str = write_type_to_string(str, type->Pointer.elem);