From d2f9d208330e692f29a2b48dce62a9c5bcab6031 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sat, 18 Feb 2017 10:41:48 +0000 Subject: [PATCH 1/8] Change ternary expression precedence --- code/demo.odin | 5 +- core/fmt.odin | 12 +- src/ir.c | 308 +++++++++++++++++++++++++++++-------------------- src/ir_print.c | 54 +++++++-- src/main.c | 9 +- src/parser.c | 80 ++++++------- 6 files changed, 271 insertions(+), 197 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index a3fee1555..34274cb22 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -5,12 +5,10 @@ #import "mem.odin"; #import "opengl.odin"; #import "os.odin"; -#import "utf8.odin"; +#import "halloc.odin"; main :: proc() { - x: f32 = false ? 123 : 55; - fmt.println("Ternary:", x); /* /* Version 0.1.1 @@ -29,6 +27,7 @@ main :: proc() { * immutable variables are "completely immutable" - rules need a full explanation * `slice_to_bytes` - convert any slice to a slice of bytes * `union_cast` allows for optional ok check + * ?: ternary operator Removed: * Maybe/option types diff --git a/core/fmt.odin b/core/fmt.odin index 22e7eddd4..6c6beb3ab 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -116,11 +116,7 @@ buffer_write_type :: proc(buf: ^Buffer, ti: ^Type_Info) { case ti == type_info(int): buffer_write_string(buf, "int"); case ti == type_info(uint): buffer_write_string(buf, "uint"); default: - if info.signed { - buffer_write_string(buf, "i"); - } else { - buffer_write_string(buf, "u"); - } + buffer_write_string(buf, info.signed ? "i" : "u"); fi := Fmt_Info{buf = buf}; fmt_int(^fi, cast(u64)(8*info.size), false, 'd'); } @@ -396,11 +392,7 @@ fmt_bad_verb :: proc(using fi: ^Fmt_Info, verb: rune) { fmt_bool :: proc(using fi: ^Fmt_Info, b: bool, verb: rune) { match verb { case 't', 'v': - if b { - buffer_write_string(buf, "true"); - } else { - buffer_write_string(buf, "false"); - } + buffer_write_string(buf, b ? "true" : "false"); default: fmt_bad_verb(fi, verb); } diff --git a/src/ir.c b/src/ir.c index ad6f65fea..18832a8b6 100644 --- a/src/ir.c +++ b/src/ir.c @@ -15,13 +15,14 @@ typedef Array(irValue *) irValueArray; #define MAP_NAME MapIrDebugInfo #include "map.c" + typedef struct irModule { CheckerInfo * info; gbArena arena; gbArena tmp_arena; gbAllocator allocator; gbAllocator tmp_allocator; - bool generate_debug_info; + // bool generate_debug_info; u32 stmt_state_flags; @@ -29,20 +30,20 @@ typedef struct irModule { String layout; // String triple; - MapEntity min_dep_map; // Key: Entity * - MapIrValue values; // Key: Entity * - MapIrValue members; // Key: String - MapString type_names; // Key: Type * - MapIrDebugInfo debug_info; // Key: Unique pointer - i32 global_string_index; - i32 global_array_index; // For ConstantSlice + MapEntity min_dep_map; // Key: Entity * + MapIrValue values; // Key: Entity * + MapIrValue members; // Key: String + MapString type_names; // Key: Type * + MapIrDebugInfo debug_info; // Key: Unique pointer + i32 global_string_index; + i32 global_array_index; // For ConstantSlice - Entity * entry_point_entity; + Entity * entry_point_entity; - Array(irProcedure *) procs; // NOTE(bill): All procedures with bodies - irValueArray procs_to_generate; // NOTE(bill): Procedures to generate + Array(irProcedure *) procs; // NOTE(bill): All procedures with bodies + irValueArray procs_to_generate; // NOTE(bill): Procedures to generate - Array(String) foreign_library_paths; // Only the ones that were used + Array(String) foreign_library_paths; // Only the ones that were used } irModule; // NOTE(bill): For more info, see https://en.wikipedia.org/wiki/Dominator_(graph_theory) @@ -121,6 +122,7 @@ struct irProcedure { irTargetList * target_list; irValueArray referrers; + i32 local_count; i32 instr_count; i32 block_count; @@ -134,90 +136,99 @@ struct irProcedure { #define IR_INSTR_KINDS \ - IR_INSTR_KIND(Comment, struct { String text; }) \ - IR_INSTR_KIND(Local, struct { \ - Entity * entity; \ - Type * type; \ - bool zero_initialized; \ - irValueArray referrers; \ - }) \ - IR_INSTR_KIND(ZeroInit, struct { irValue *address; }) \ - IR_INSTR_KIND(Store, struct { irValue *address, *value; }) \ + IR_INSTR_KIND(Comment, struct { String text; }) \ + IR_INSTR_KIND(Local, struct { \ + Entity * entity; \ + Type * type; \ + bool zero_initialized; \ + irValueArray referrers; \ + }) \ + IR_INSTR_KIND(ZeroInit, struct { irValue *address; }) \ + IR_INSTR_KIND(Store, struct { irValue *address, *value; }) \ IR_INSTR_KIND(Load, struct { Type *type; irValue *address; }) \ - IR_INSTR_KIND(PtrOffset, struct { \ - irValue *address; \ - irValue *offset; \ + IR_INSTR_KIND(PtrOffset, struct { \ + irValue *address; \ + irValue *offset; \ + }) \ + IR_INSTR_KIND(ArrayElementPtr, struct { \ + irValue *address; \ + Type * result_type; \ + irValue *elem_index; \ + }) \ + IR_INSTR_KIND(StructElementPtr, struct { \ + irValue *address; \ + Type * result_type; \ + i32 elem_index; \ + }) \ + IR_INSTR_KIND(StructExtractValue, struct { \ + irValue *address; \ + Type * result_type; \ + i32 index; \ + }) \ + IR_INSTR_KIND(UnionTagPtr, struct { \ + irValue *address; \ + Type *type; /* ^int */ \ + }) \ + IR_INSTR_KIND(UnionTagValue, struct { \ + irValue *address; \ + Type *type; /* int */ \ + }) \ + IR_INSTR_KIND(Conv, struct { \ + irConvKind kind; \ + irValue *value; \ + Type *from, *to; \ + }) \ + IR_INSTR_KIND(Jump, struct { irBlock *block; }) \ + IR_INSTR_KIND(If, struct { \ + irValue *cond; \ + irBlock *true_block; \ + irBlock *false_block; \ + }) \ + IR_INSTR_KIND(Return, struct { irValue *value; }) \ + IR_INSTR_KIND(Select, struct { \ + irValue *cond; \ + irValue *true_value; \ + irValue *false_value; \ + }) \ + IR_INSTR_KIND(Phi, struct { irValueArray edges; Type *type; }) \ + IR_INSTR_KIND(Unreachable, i32) \ + IR_INSTR_KIND(UnaryOp, struct { \ + Type * type; \ + TokenKind op; \ + irValue * expr; \ + }) \ + IR_INSTR_KIND(BinaryOp, struct { \ + Type * type; \ + TokenKind op; \ + irValue * left, *right; \ + }) \ + IR_INSTR_KIND(Call, struct { \ + Type * type; /* return type */ \ + irValue * value; \ + irValue **args; \ + isize arg_count; \ + }) \ + IR_INSTR_KIND(StartupRuntime, i32) \ + IR_INSTR_KIND(BoundsCheck, struct { \ + TokenPos pos; \ + irValue *index; \ + irValue *len; \ + }) \ + IR_INSTR_KIND(SliceBoundsCheck, struct { \ + TokenPos pos; \ + irValue *low; \ + irValue *high; \ + bool is_substring; \ + }) \ + IR_INSTR_KIND(DebugDeclare, struct { \ + irDebugInfo *debug_info; \ + AstNode * expr; \ + Entity * entity; \ + bool is_addr; \ + irValue * value; \ }) \ - IR_INSTR_KIND(ArrayElementPtr, struct { \ - irValue *address; \ - Type * result_type; \ - irValue *elem_index; \ - }) \ - IR_INSTR_KIND(StructElementPtr, struct { \ - irValue *address; \ - Type * result_type; \ - i32 elem_index; \ - }) \ - IR_INSTR_KIND(StructExtractValue, struct { \ - irValue *address; \ - Type * result_type; \ - i32 index; \ - }) \ - IR_INSTR_KIND(UnionTagPtr, struct { \ - irValue *address; \ - Type *type; /* ^int */ \ - }) \ - IR_INSTR_KIND(UnionTagValue, struct { \ - irValue *address; \ - Type *type; /* int */ \ - }) \ - IR_INSTR_KIND(Conv, struct { \ - irConvKind kind; \ - irValue *value; \ - Type *from, *to; \ - }) \ - IR_INSTR_KIND(Jump, struct { irBlock *block; }) \ - IR_INSTR_KIND(If, struct { \ - irValue *cond; \ - irBlock *true_block; \ - irBlock *false_block; \ - }) \ - IR_INSTR_KIND(Return, struct { irValue *value; }) \ - IR_INSTR_KIND(Select, struct { \ - irValue *cond; \ - irValue *true_value; \ - irValue *false_value; \ - }) \ - IR_INSTR_KIND(Phi, struct { irValueArray edges; Type *type; }) \ - IR_INSTR_KIND(Unreachable, i32) \ - IR_INSTR_KIND(UnaryOp, struct { \ - Type * type; \ - TokenKind op; \ - irValue *expr; \ - }) \ - IR_INSTR_KIND(BinaryOp, struct { \ - Type * type; \ - TokenKind op; \ - irValue *left, *right; \ - }) \ - IR_INSTR_KIND(Call, struct { \ - Type * type; /* return type */ \ - irValue *value; \ - irValue **args; \ - isize arg_count; \ - }) \ - IR_INSTR_KIND(StartupRuntime, i32) \ - IR_INSTR_KIND(BoundsCheck, struct { \ - TokenPos pos; \ - irValue *index; \ - irValue *len; \ - }) \ - IR_INSTR_KIND(SliceBoundsCheck, struct { \ - TokenPos pos; \ - irValue *low; \ - irValue *high; \ - bool is_substring; \ - }) + + #define IR_CONV_KINDS \ IR_CONV_KIND(trunc) \ @@ -394,7 +405,6 @@ irAddr ir_make_addr_map(irValue *addr, irValue *map_key, Type *map_type, Type *m return v; } - typedef enum irDebugEncoding { irDebugBasicEncoding_Invalid = 0, @@ -519,14 +529,19 @@ struct irDebugInfo { }; }; + + typedef struct irGen { irModule module; - gbFile output_file; - bool opt_called; + gbFile output_file; + bool opt_called; } irGen; + + + Type *ir_type(irValue *value); Type *ir_instr_type(irInstr *instr) { switch (instr->kind) { @@ -948,6 +963,16 @@ irValue *ir_make_instr_slice_bounds_check(irProcedure *p, TokenPos pos, irValue v->Instr.SliceBoundsCheck.is_substring = is_substring; return v; } +irValue *ir_make_instr_debug_declare(irProcedure *p, irDebugInfo *debug_info, AstNode *expr, Entity *entity, bool is_addr, irValue *value) { + irValue *v = ir_alloc_instr(p, irInstr_DebugDeclare); + v->Instr.DebugDeclare.debug_info = debug_info; + v->Instr.DebugDeclare.expr = expr; + v->Instr.DebugDeclare.entity = entity; + v->Instr.DebugDeclare.is_addr = is_addr; + v->Instr.DebugDeclare.value = value; + return v; + +} @@ -967,6 +992,23 @@ irValue *ir_make_value_constant_slice(gbAllocator a, Type *type, irValue *backin return v; } + + +irValue *ir_emit(irProcedure *proc, irValue *instr) { + GB_ASSERT(instr->kind == irValue_Instr); + irBlock *b = proc->curr_block; + instr->Instr.parent = b; + if (b != NULL) { + irInstr *i = ir_get_last_instr(b); + if (!ir_is_instr_terminating(i)) { + array_add(&b->instrs, instr); + } + } + return instr; +} + + + irValue *ir_make_const_int(gbAllocator a, i64 i) { return ir_make_value_constant(a, t_int, make_exact_value_integer(i)); } @@ -1166,7 +1208,7 @@ irValue *ir_add_global_string_array(irModule *m, String string) { -irValue *ir_add_local(irProcedure *proc, Entity *e) { +irValue *ir_add_local(irProcedure *proc, Entity *e, AstNode *expr) { irBlock *b = proc->decl_block; // all variables must be in the first block irValue *instr = ir_make_instr_local(proc, e, true); instr->Instr.parent = b; @@ -1178,6 +1220,11 @@ irValue *ir_add_local(irProcedure *proc, Entity *e) { ir_emit_zero_init(proc, instr); // } + if (expr != NULL) { + irDebugInfo *di = *map_ir_debug_info_get(&proc->module->debug_info, hash_pointer(proc->entity)); + ir_emit(proc, ir_make_instr_debug_declare(proc, di, expr, e, true, instr)); + } + return instr; } @@ -1186,7 +1233,7 @@ irValue *ir_add_local_for_identifier(irProcedure *proc, AstNode *name, bool zero if (found) { Entity *e = *found; ir_emit_comment(proc, e->token.string); - return ir_add_local(proc, e); + return ir_add_local(proc, e, name); } return NULL; } @@ -1202,14 +1249,14 @@ irValue *ir_add_local_generated(irProcedure *proc, Type *type) { scope, empty_token, type, false); - return ir_add_local(proc, e); + return ir_add_local(proc, e, NULL); } -irValue *ir_add_param(irProcedure *proc, Entity *e) { +irValue *ir_add_param(irProcedure *proc, Entity *e, AstNode *expr) { irValue *v = ir_make_value_param(proc->module->allocator, proc, e); #if 1 - irValue *l = ir_add_local(proc, e); + irValue *l = ir_add_local(proc, e, expr); ir_emit_store(proc, l, v); #else @@ -1227,9 +1274,9 @@ irValue *ir_add_param(irProcedure *proc, Entity *e) { //////////////////////////////////////////////////////////////// irDebugInfo *ir_add_debug_info_file(irProcedure *proc, AstFile *file) { - if (!proc->module->generate_debug_info) { - return NULL; - } + // if (!proc->module->generate_debug_info) { + // return NULL; + // } GB_ASSERT(file != NULL); irDebugInfo *di = ir_alloc_debug_info(proc->module->allocator, irDebugInfo_File); @@ -1259,9 +1306,9 @@ irDebugInfo *ir_add_debug_info_file(irProcedure *proc, AstFile *file) { irDebugInfo *ir_add_debug_info_proc(irProcedure *proc, Entity *entity, String name, irDebugInfo *file) { - if (!proc->module->generate_debug_info) { - return NULL; - } + // if (!proc->module->generate_debug_info) { + // return NULL; + // } GB_ASSERT(entity != NULL); irDebugInfo *di = ir_alloc_debug_info(proc->module->allocator, irDebugInfo_Proc); @@ -1281,18 +1328,7 @@ irDebugInfo *ir_add_debug_info_proc(irProcedure *proc, Entity *entity, String na //////////////////////////////////////////////////////////////// -irValue *ir_emit(irProcedure *proc, irValue *instr) { - GB_ASSERT(instr->kind == irValue_Instr); - irBlock *b = proc->curr_block; - instr->Instr.parent = b; - if (b != NULL) { - irInstr *i = ir_get_last_instr(b); - if (!ir_is_instr_terminating(i)) { - array_add(&b->instrs, instr); - } - } - return instr; -} + irValue *ir_emit_store(irProcedure *p, irValue *address, irValue *value) { #if 1 // NOTE(bill): Sanity check @@ -5357,7 +5393,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { irValue *tag_var = NULL; if (tag_var_entity != NULL) { - tag_var = ir_add_local(proc, tag_var_entity); + tag_var = ir_add_local(proc, tag_var_entity, NULL); } else { tag_var = ir_add_local_generated(proc, tag_var_type); } @@ -5385,7 +5421,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { irValue *tag_var = NULL; if (tag_var_entity != NULL) { - tag_var = ir_add_local(proc, tag_var_entity); + tag_var = ir_add_local(proc, tag_var_entity, NULL); } else { tag_var = ir_add_local_generated(proc, tag_var_type); } @@ -5558,12 +5594,24 @@ void ir_begin_procedure_body(irProcedure *proc) { ir_start_block(proc, proc->entry_block); if (proc->type->Proc.params != NULL) { + ast_node(pt, ProcType, proc->type_expr); + isize param_index = 0; + isize q_index = 0; + TypeTuple *params = &proc->type->Proc.params->Tuple; for (isize i = 0; i < params->variable_count; i++) { + GB_ASSERT(pt->params.e[0]->kind == AstNode_Field); + if (q_index == pt->params.e[param_index]->Field.names.count) { + q_index = 0; + param_index++; + } + ast_node(field, Field, pt->params.e[param_index]); + AstNode *name = field->names.e[q_index++]; + Entity *e = params->variables[i]; if (!str_eq(e->token.string, str_lit("")) && !str_eq(e->token.string, str_lit("_"))) { - irValue *param = ir_add_param(proc, e); + irValue *param = ir_add_param(proc, e, name); array_add(&proc->params, param); } } @@ -5790,7 +5838,7 @@ bool ir_gen_init(irGen *s, Checker *c) { } ir_init_module(&s->module, c); - s->module.generate_debug_info = false; + // s->module.generate_debug_info = false; // TODO(bill): generate appropriate output name int pos = cast(int)string_extension_position(c->parser->init_fullpath); @@ -6025,7 +6073,9 @@ void ir_gen_tree(irGen *s) { name = pd->link_name; } - irValue *p = ir_make_value_procedure(a, m, e, e->type, decl->type_expr, body, name); + AstNode *type_expr = decl->proc_lit->ProcLit.type; + + irValue *p = ir_make_value_procedure(a, m, e, e->type, type_expr, body, name); p->Proc.tags = pd->tags; ir_module_add_value(m, e, p); @@ -6067,7 +6117,6 @@ void ir_gen_tree(irGen *s) { for_array(i, m->debug_info.entries) { MapIrDebugInfoEntry *entry = &m->debug_info.entries.e[i]; irDebugInfo *di = entry->value; - di->id = i; if (di->kind == irDebugInfo_Proc) { array_add(&all_procs->AllProcs.procs, di); } @@ -6619,6 +6668,13 @@ void ir_gen_tree(irGen *s) { ir_build_proc(m->procs_to_generate.e[i], m->procs_to_generate.e[i]->Proc.parent); } + // Number debug info + for_array(i, m->debug_info.entries) { + MapIrDebugInfoEntry *entry = &m->debug_info.entries.e[i]; + irDebugInfo *di = entry->value; + di->id = i; + } + // m->layout = str_lit("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"); } diff --git a/src/ir_print.c b/src/ir_print.c index e1b29a847..a35c4eb36 100644 --- a/src/ir_print.c +++ b/src/ir_print.c @@ -1254,6 +1254,29 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { ir_fprintf(f, ")\n"); } break; + case irInstr_DebugDeclare: { + /* irInstrDebugDeclare *dd = &instr->DebugDeclare; + Type *vt = ir_type(dd->value); + irDebugInfo *di = dd->debug_info; + Entity *e = dd->entity; + String name = e->token.string; + TokenPos pos = e->token.pos; + // gb_printf("debug_declare %.*s\n", LIT(dd->entity->token.string)); + ir_fprintf(f, "call void @llvm.dbg.declare("); + ir_fprintf(f, "metadata "); + ir_print_type(f, m, vt); + ir_fprintf(f, " "); + ir_print_value(f, m, dd->value, vt); + ir_fprintf(f, ", metadata !DILocalVariable(name: \""); + ir_print_escape_string(f, name, false); + ir_fprintf(f, "\", scope: !%d, line: %td)", di->id, pos.line); + ir_fprintf(f, ", metadata !DIExpression()"); + ir_fprintf(f, ")"); + ir_fprintf(f, ", !dbg !DILocation(line: %td, column: %td, scope: !%d)", pos.line, pos.column, di->id); + + ir_fprintf(f, "\n"); */ + } break; + default: { GB_PANIC(" %d\n", instr->kind); @@ -1326,11 +1349,14 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) { } - if (proc->module->generate_debug_info && proc->entity != NULL) { + if (proc->entity != NULL) { if (proc->body != NULL) { - irDebugInfo *di = *map_ir_debug_info_get(&proc->module->debug_info, hash_pointer(proc->entity)); - GB_ASSERT(di->kind == irDebugInfo_Proc); - ir_fprintf(f, "!dbg !%d ", di->id); + irDebugInfo **di_ = map_ir_debug_info_get(&proc->module->debug_info, hash_pointer(proc->entity)); + if (di_ != NULL) { + irDebugInfo *di = *di_; + GB_ASSERT(di->kind == irDebugInfo_Proc); + // ir_fprintf(f, "!dbg !%d ", di->id); + } } } @@ -1396,6 +1422,8 @@ void print_llvm_ir(irGen *ir) { ir_print_type(f, m, t_rawptr); ir_fprintf(f, "} ; Basic_any\n"); + ir_fprintf(f, "declare void @llvm.dbg.declare(metadata, metadata, metadata) nounwind readnone \n"); + for_array(member_index, m->members.entries) { MapIrValueEntry *entry = &m->members.entries.e[member_index]; @@ -1482,9 +1510,18 @@ void print_llvm_ir(irGen *ir) { #if 0 - if (m->generate_debug_info) { + // if (m->generate_debug_info) { + { ir_fprintf(f, "\n"); + + i32 diec = m->debug_info.entries.count; + ir_fprintf(f, "!llvm.dbg.cu = !{!0}\n"); + ir_fprintf(f, "!llvm.ident = !{!%d}\n", diec+3); + ir_fprintf(f, "!%d = !{i32 2, !\"Dwarf Version\", i32 4}\n", diec+0); + ir_fprintf(f, "!%d = !{i32 2, !\"Debug Info Version\", i32 3}\n", diec+1); + ir_fprintf(f, "!%d = !{i32 1, !\"PIC Level\", i32 2}\n", diec+2); + ir_fprintf(f, "!%d = !{!\"clang version 3.9.0 (branches/release_39)\"}\n", diec+3); for_array(di_index, m->debug_info.entries) { MapIrDebugInfoEntry *entry = &m->debug_info.entries.e[di_index]; @@ -1493,19 +1530,18 @@ void print_llvm_ir(irGen *ir) { switch (di->kind) { case irDebugInfo_CompileUnit: { - auto *cu = &di->CompileUnit; - irDebugInfo *file = *map_ir_debug_info_get(&m->debug_info, hash_pointer(cu->file)); + irDebugInfo *file = *map_ir_debug_info_get(&m->debug_info, hash_pointer(di->CompileUnit.file)); ir_fprintf(f, "distinct !DICompileUnit(" "language: DW_LANG_Go, " // Is this good enough? "file: !%d, " - "producer: \"%.*s\", " + "producer: \"clang version 3.9.0 (branches/release_39)\", " "flags: \"\", " "runtimeVersion: 0, " "isOptimized: false, " "emissionKind: FullDebug" ")", - file->id, LIT(cu->producer)); + file->id); } break; case irDebugInfo_File: diff --git a/src/main.c b/src/main.c index 20da798af..233f6ba93 100644 --- a/src/main.c +++ b/src/main.c @@ -2,6 +2,7 @@ extern "C" { #endif + #include "common.c" #include "timings.c" #include "build_settings.c" @@ -216,10 +217,10 @@ int main(int argc, char **argv) { return 1; } - ssa_generate(&checker.info, &build_context); -#endif -#if 1 - + if (!ssa_generate(&checker.info)) { + return 1; + } +#else irGen ir_gen = {0}; if (!ir_gen_init(&ir_gen, &checker)) { return 1; diff --git a/src/parser.c b/src/parser.c index 52343b649..b3c6f7b83 100644 --- a/src/parser.c +++ b/src/parser.c @@ -1949,19 +1949,6 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) { } break; - case Token_Question: - if (!lhs && operand != NULL && f->expr_level >= 0) { - AstNode *cond = operand; - Token token_q = expect_token(f, Token_Question); - AstNode *x = parse_expr(f, false); - Token token_c = expect_token(f, Token_Colon); - AstNode *y = parse_expr(f, false); - operand = ast_ternary_expr(f, cond, x, y); - } else { - loop = false; - } - break; - default: loop = false; break; @@ -2016,22 +2003,24 @@ AstNode *parse_unary_expr(AstFile *f, bool lhs) { // NOTE(bill): result == priority i32 token_precedence(TokenKind t) { switch (t) { - case Token_CmpOr: + case Token_Question: return 1; - case Token_CmpAnd: + case Token_CmpOr: return 2; + case Token_CmpAnd: + return 3; case Token_CmpEq: case Token_NotEq: case Token_Lt: case Token_Gt: case Token_LtEq: case Token_GtEq: - return 3; + return 4; case Token_Add: case Token_Sub: case Token_Or: case Token_Xor: - return 4; + return 5; case Token_Mul: case Token_Quo: case Token_Mod: @@ -2039,51 +2028,42 @@ i32 token_precedence(TokenKind t) { case Token_AndNot: case Token_Shl: case Token_Shr: - return 5; - // case Token_as: - // case Token_transmute: - // case Token_down_cast: - // case Token_union_cast: - // return 6; + return 6; } return 0; } AstNode *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) { - AstNode *expression = parse_unary_expr(f, lhs); + AstNode *expr = parse_unary_expr(f, lhs); for (i32 prec = token_precedence(f->curr_token.kind); prec >= prec_in; prec--) { for (;;) { - AstNode *right; Token op = f->curr_token; i32 op_prec = token_precedence(op.kind); if (op_prec != prec) { + // NOTE(bill): This will also catch operators that are not valid "binary" operators break; } expect_operator(f); // NOTE(bill): error checks too - if (lhs) { - // TODO(bill): error checking - lhs = false; - } - switch (op.kind) { - /* case Token_as: - case Token_transmute: - case Token_down_cast: - case Token_union_cast: - right = parse_type(f); - break; */ - - default: - right = parse_binary_expr(f, false, prec+1); + if (op.kind == Token_Question) { + AstNode *cond = expr; + // Token_Question + AstNode *x = parse_expr(f, lhs); + Token token_c = expect_token(f, Token_Colon); + AstNode *y = parse_expr(f, lhs); + expr = ast_ternary_expr(f, cond, x, y); + } else { + AstNode *right = parse_binary_expr(f, false, prec+1); if (!right) { - syntax_error(op, "Expected expression on the right hand side of the binary operator"); + syntax_error(op, "Expected expression on the right-hand side of the binary operator"); } - break; + expr = ast_binary_expr(f, op, expr, right); } - expression = ast_binary_expr(f, op, expression, right); + + lhs = false; } } - return expression; + return expr; } AstNode *parse_expr(AstFile *f, bool lhs) { @@ -3458,8 +3438,18 @@ AstNode *parse_stmt(AstFile *f) { return s; } - expect_semicolon(f, s); - return ast_tag_stmt(f, hash_token, name, parse_stmt(f)); + + if (str_eq(tag, str_lit("include"))) { + syntax_error(token, "#include is not a valid import declaration kind. Use #load instead"); + s = ast_bad_stmt(f, token, f->curr_token); + } else { + syntax_error(token, "Unknown tag used: `%.*s`", LIT(tag)); + s = ast_bad_stmt(f, token, f->curr_token); + } + + fix_advance_to_next_stmt(f); + + return s; } break; case Token_OpenBrace: From 9ff474f387f8cfb2b0ee780034c584aabccb9248 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sat, 18 Feb 2017 12:02:11 +0000 Subject: [PATCH 2/8] Named return values but do not affect other declarations --- core/fmt.odin | 9 ++- src/check_expr.c | 119 ++++++++++++++++++++++++++-------- src/ir.c | 8 ++- src/parser.c | 165 +++++++++++++++++++++-------------------------- 4 files changed, 177 insertions(+), 124 deletions(-) diff --git a/core/fmt.odin b/core/fmt.odin index 6c6beb3ab..d340bbe5d 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -293,7 +293,7 @@ sprintf :: proc(buf: []byte, fmt: string, args: ...any) -> string { -parse_int :: proc(s: string, offset: int) -> (int, int, bool) { +parse_int :: proc(s: string, offset: int) -> (result: int, offset: int, ok: bool) { is_digit :: proc(r: rune) -> bool #inline { return '0' <= r && r <= '9'; } @@ -316,7 +316,12 @@ parse_int :: proc(s: string, offset: int) -> (int, int, bool) { return result, offset+i, i != 0; } -_arg_number :: proc(fi: ^Fmt_Info, arg_index: int, format: string, offset: int, arg_count: int) -> (int, int, bool) { +_arg_number :: proc(fi: ^Fmt_Info, + arg_index: int, + format: string, + offset: int, + arg_count: int, + ) -> (index: int, offset: int, ok: bool) { parse_arg_number :: proc(format: string) -> (int, int, bool) { if format.count < 3 { return 0, 1, false; diff --git a/src/check_expr.c b/src/check_expr.c index 687f4ebea..41f9ce08d 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -798,7 +798,13 @@ void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *nod } -Type *check_get_params(Checker *c, Scope *scope, AstNodeArray params, bool *is_variadic_) { +Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_variadic_) { + if (_params == NULL) { + return NULL; + } + ast_node(field_list, FieldList, _params); + AstNodeArray params = field_list->list; + if (params.count == 0) { return NULL; } @@ -808,7 +814,7 @@ Type *check_get_params(Checker *c, Scope *scope, AstNodeArray params, bool *is_v AstNode *field = params.e[i]; if (ast_node_expect(field, AstNode_Field)) { ast_node(f, Field, field); - variable_count += f->names.count; + variable_count += max(f->names.count, 1); } } @@ -877,26 +883,73 @@ Type *check_get_params(Checker *c, Scope *scope, AstNodeArray params, bool *is_v return tuple; } -Type *check_get_results(Checker *c, Scope *scope, AstNodeArray results) { +Type *check_get_results(Checker *c, Scope *scope, AstNode *_results) { + if (_results == NULL) { + return NULL; + } + ast_node(field_list, FieldList, _results); + AstNodeArray results = field_list->list; + if (results.count == 0) { return NULL; } Type *tuple = make_type_tuple(c->allocator); - Entity **variables = gb_alloc_array(c->allocator, Entity *, results.count); + isize variable_count = 0; + for_array(i, results) { + AstNode *field = results.e[i]; + if (ast_node_expect(field, AstNode_Field)) { + ast_node(f, Field, field); + variable_count += max(f->names.count, 1); + } + } + + Entity **variables = gb_alloc_array(c->allocator, Entity *, variable_count); isize variable_index = 0; for_array(i, results) { - AstNode *item = results.e[i]; - Type *type = check_type(c, item); - Token token = ast_node_token(item); - token.string = str_lit(""); // NOTE(bill): results are not named - // TODO(bill): Should I have named results? - Entity *param = make_entity_param(c->allocator, scope, token, type, false, false); - // NOTE(bill): No need to record - variables[variable_index++] = param; + ast_node(field, Field, results.e[i]); + Type *type = check_type(c, field->type); + if (field->names.count == 0) { + Token token = ast_node_token(field->type); + token.string = str_lit(""); + Entity *param = make_entity_param(c->allocator, scope, token, type, false, false); + variables[variable_index++] = param; + } else { + for_array(j, field->names) { + Token token = ast_node_token(field->type); + token.string = str_lit(""); + + AstNode *name = field->names.e[j]; + if (name->kind != AstNode_Ident) { + error_node(name, "Expected an identifer for as the field name"); + } else { + token = name->Ident; + } + + Entity *param = make_entity_param(c->allocator, scope, token, type, false, false); + variables[variable_index++] = param; + } + } } + + for (isize i = 0; i < variable_index; i++) { + String x = variables[i]->token.string; + if (x.len == 0 || str_eq(x, str_lit("_"))) { + continue; + } + for (isize j = i+1; j < variable_index; j++) { + String y = variables[j]->token.string; + if (y.len == 0 || str_eq(y, str_lit("_"))) { + continue; + } + if (str_eq(x, y)) { + error(variables[j]->token, "Duplicate return value name `%.*s`", LIT(y)); + } + } + } + tuple->Tuple.variables = variables; - tuple->Tuple.variable_count = results.count; + tuple->Tuple.variable_count = variable_index; return tuple; } @@ -5126,16 +5179,6 @@ void check_expr_or_type(Checker *c, Operand *o, AstNode *e) { gbString write_expr_to_string(gbString str, AstNode *node); -gbString write_params_to_string(gbString str, AstNodeArray params, char *sep) { - for_array(i, params) { - if (i > 0) { - str = gb_string_appendc(str, sep); - } - str = write_expr_to_string(str, params.e[i]); - } - return str; -} - gbString write_record_fields_to_string(gbString str, AstNodeArray params) { for_array(i, params) { if (i > 0) { @@ -5301,6 +5344,13 @@ gbString write_expr_to_string(gbString str, AstNode *node) { if (f->flags&FieldFlag_using) { str = gb_string_appendc(str, "using "); } + if (f->flags&FieldFlag_immutable) { + str = gb_string_appendc(str, "immutable "); + } + if (f->flags&FieldFlag_no_alias) { + str = gb_string_appendc(str, "no_alias "); + } + for_array(i, f->names) { AstNode *name = f->names.e[i]; if (i > 0) { @@ -5308,14 +5358,24 @@ gbString write_expr_to_string(gbString str, AstNode *node) { } str = write_expr_to_string(str, name); } - - str = gb_string_appendc(str, ": "); + if (f->names.count > 0) { + str = gb_string_appendc(str, ": "); + } if (f->flags&FieldFlag_ellipsis) { str = gb_string_appendc(str, "..."); } str = write_expr_to_string(str, f->type); case_end; + case_ast_node(f, FieldList, node); + for_array(i, f->list) { + if (i > 0) { + str = gb_string_appendc(str, ", "); + } + str = write_expr_to_string(str, f->list.e[i]); + } + case_end; + case_ast_node(ce, CallExpr, node); str = write_expr_to_string(str, ce->proc); str = gb_string_appendc(str, "("); @@ -5332,7 +5392,7 @@ gbString write_expr_to_string(gbString str, AstNode *node) { case_ast_node(pt, ProcType, node); str = gb_string_appendc(str, "proc("); - str = write_params_to_string(str, pt->params, ", "); + str = write_expr_to_string(str, pt->params); str = gb_string_appendc(str, ")"); case_end; @@ -5366,7 +5426,12 @@ gbString write_expr_to_string(gbString str, AstNode *node) { str = gb_string_appendc(str, " "); } str = gb_string_appendc(str, "{"); - str = write_params_to_string(str, et->fields, ", "); + for_array(i, et->fields) { + if (i > 0) { + str = gb_string_appendc(str, ", "); + } + str = write_expr_to_string(str, et->fields.e[i]); + } str = gb_string_appendc(str, "}"); case_end; diff --git a/src/ir.c b/src/ir.c index 18832a8b6..132d3a02d 100644 --- a/src/ir.c +++ b/src/ir.c @@ -5600,12 +5600,14 @@ void ir_begin_procedure_body(irProcedure *proc) { TypeTuple *params = &proc->type->Proc.params->Tuple; for (isize i = 0; i < params->variable_count; i++) { - GB_ASSERT(pt->params.e[0]->kind == AstNode_Field); - if (q_index == pt->params.e[param_index]->Field.names.count) { + ast_node(fl, FieldList, pt->params); + GB_ASSERT(fl->list.count > 0); + GB_ASSERT(fl->list.e[0]->kind == AstNode_Field); + if (q_index == fl->list.e[param_index]->Field.names.count) { q_index = 0; param_index++; } - ast_node(field, Field, pt->params.e[param_index]); + ast_node(field, Field, fl->list.e[param_index]); AstNode *name = field->names.e[q_index++]; Entity *e = params->variables[i]; diff --git a/src/parser.c b/src/parser.c index b3c6f7b83..5e1e6bb57 100644 --- a/src/parser.c +++ b/src/parser.c @@ -302,16 +302,20 @@ AST_NODE_KIND(_DeclEnd, "", i32) \ AstNode * type; \ u32 flags; \ }) \ + AST_NODE_KIND(FieldList, "field list", struct { \ + Token token; \ + AstNodeArray list; \ + }) \ AST_NODE_KIND(_TypeBegin, "", i32) \ AST_NODE_KIND(HelperType, "type", struct { \ Token token; \ AstNode *type; \ }) \ AST_NODE_KIND(ProcType, "procedure type", struct { \ - Token token; \ - AstNodeArray params; \ - AstNodeArray results; \ - u64 tags; \ + Token token; \ + AstNode *params; \ + AstNode *results; \ + u64 tags; \ ProcCallingConvention calling_convention; \ }) \ AST_NODE_KIND(PointerType, "pointer type", struct { \ @@ -478,11 +482,14 @@ Token ast_node_token(AstNode *node) { case AstNode_ImportDecl: return node->ImportDecl.token; case AstNode_ForeignLibrary: return node->ForeignLibrary.token; + case AstNode_Field: if (node->Field.names.count > 0) { return ast_node_token(node->Field.names.e[0]); } return ast_node_token(node->Field.type); + case AstNode_FieldList: + return node->FieldList.token; case AstNode_HelperType: return node->HelperType.token; case AstNode_ProcType: return node->ProcType.token; @@ -952,6 +959,13 @@ AstNode *ast_field(AstFile *f, AstNodeArray names, AstNode *type, u32 flags) { return result; } +AstNode *ast_field_list(AstFile *f, Token token, AstNodeArray list) { + AstNode *result = make_ast_node(f, AstNode_FieldList); + result->FieldList.token = token; + result->FieldList.list = list; + return result; +} + AstNode *ast_helper_type(AstFile *f, Token token, AstNode *type) { AstNode *result = make_ast_node(f, AstNode_HelperType); @@ -961,7 +975,7 @@ AstNode *ast_helper_type(AstFile *f, Token token, AstNode *type) { } -AstNode *ast_proc_type(AstFile *f, Token token, AstNodeArray params, AstNodeArray results, u64 tags, ProcCallingConvention calling_convention) { +AstNode *ast_proc_type(AstFile *f, Token token, AstNode *params, AstNode *results, u64 tags, ProcCallingConvention calling_convention) { AstNode *result = make_ast_node(f, AstNode_ProcType); result->ProcType.token = token; result->ProcType.params = params; @@ -1308,7 +1322,6 @@ AstNode * parse_proc_type(AstFile *f, AstNode **foreign_library, String *fore AstNodeArray parse_stmt_list(AstFile *f); AstNode * parse_stmt(AstFile *f); AstNode * parse_body(AstFile *f); -void parse_proc_signature(AstFile *f, AstNodeArray *params, AstNodeArray *results); @@ -2258,15 +2271,39 @@ AstNode *parse_block_stmt(AstFile *f, b32 is_when) { return parse_body(f); } +AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind separator, TokenKind follow); +AstNode *parse_results(AstFile *f) { + if (!allow_token(f, Token_ArrowRight)) { + return NULL; + } + + if (f->curr_token.kind != Token_OpenParen) { + Token begin_token = f->curr_token; + AstNodeArray empty_names = {0}; + AstNodeArray list = make_ast_node_array(f); + AstNode *type = parse_type(f); + array_add(&list, ast_field(f, empty_names, type, 0)); + return ast_field_list(f, begin_token, list); + } + + AstNode *list = NULL; + expect_token(f, Token_OpenParen); + list = parse_field_list(f, NULL, 0, Token_Comma, Token_CloseParen); + expect_token_after(f, Token_CloseParen, "parameter list"); + return list; +} AstNode *parse_proc_type(AstFile *f, AstNode **foreign_library_, String *foreign_name_, String *link_name_) { - AstNodeArray params = {0}; - AstNodeArray results = {0}; + AstNode *params = {0}; + AstNode *results = {0}; Token proc_token = expect_token(f, Token_proc); - parse_proc_signature(f, ¶ms, &results); + expect_token(f, Token_OpenParen); + params = parse_field_list(f, NULL, FieldFlag_Signature, Token_Comma, Token_CloseParen); + expect_token_after(f, Token_CloseParen, "parameter list"); + results = parse_results(f); u64 tags = 0; String foreign_name = {0}; @@ -2404,8 +2441,9 @@ AstNodeArray convert_to_ident_list(AstFile *f, AstNodeAndFlagsArray list, bool i return idents; } -AstNodeArray parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, - TokenKind separator, TokenKind follow) { +AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind separator, TokenKind follow) { + Token start_token = f->curr_token; + AstNodeArray params = make_ast_node_array(f); AstNodeAndFlagsArray list = {0}; array_init(&list, heap_allocator()); // LEAK(bill): isize total_name_count = 0; @@ -2465,7 +2503,7 @@ AstNodeArray parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, } if (name_count_) *name_count_ = total_name_count; - return params; + return ast_field_list(f, start_token, params); } for_array(i, list) { @@ -2483,11 +2521,11 @@ AstNodeArray parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, } if (name_count_) *name_count_ = total_name_count; - return params; + return ast_field_list(f, start_token, params); } -AstNodeArray parse_record_fields(AstFile *f, isize *field_count_, u32 flags, String context) { +AstNode *parse_record_fields(AstFile *f, isize *field_count_, u32 flags, String context) { return parse_field_list(f, field_count_, flags, Token_Comma, Token_CloseBrace); } @@ -2617,9 +2655,15 @@ AstNode *parse_type_or_ident(AstFile *f) { Token open = expect_token_after(f, Token_OpenBrace, "struct"); isize decl_count = 0; - AstNodeArray decls = parse_record_fields(f, &decl_count, FieldFlag_using, str_lit("struct")); + AstNode *fields = parse_record_fields(f, &decl_count, FieldFlag_using, str_lit("struct")); Token close = expect_token(f, Token_CloseBrace); + AstNodeArray decls = {0}; + if (fields != NULL) { + GB_ASSERT(fields->kind == AstNode_FieldList); + decls = fields->FieldList.list; + } + return ast_struct_type(f, token, decls, decl_count, is_packed, is_ordered, align); } break; @@ -2627,9 +2671,15 @@ AstNode *parse_type_or_ident(AstFile *f) { Token token = expect_token(f, Token_union); Token open = expect_token_after(f, Token_OpenBrace, "union"); isize decl_count = 0; - AstNodeArray decls = parse_record_fields(f, &decl_count, 0, str_lit("union")); + AstNode *fields = parse_record_fields(f, &decl_count, 0, str_lit("union")); Token close = expect_token(f, Token_CloseBrace); + AstNodeArray decls = {0}; + if (fields != NULL) { + GB_ASSERT(fields->kind == AstNode_FieldList); + decls = fields->FieldList.list; + } + return ast_union_type(f, token, decls, decl_count); } @@ -2637,9 +2687,15 @@ AstNode *parse_type_or_ident(AstFile *f) { Token token = expect_token(f, Token_raw_union); Token open = expect_token_after(f, Token_OpenBrace, "raw_union"); isize decl_count = 0; - AstNodeArray decls = parse_record_fields(f, &decl_count, FieldFlag_using, str_lit("raw_union")); + AstNode *fields = parse_record_fields(f, &decl_count, FieldFlag_using, str_lit("raw_union")); Token close = expect_token(f, Token_CloseBrace); + AstNodeArray decls = {0}; + if (fields != NULL) { + GB_ASSERT(fields->kind == AstNode_FieldList); + decls = fields->FieldList.list; + } + return ast_raw_union_type(f, token, decls, decl_count); } @@ -2679,39 +2735,6 @@ AstNode *parse_type_or_ident(AstFile *f) { } -AstNodeArray parse_results(AstFile *f) { - AstNodeArray results = make_ast_node_array(f); - if (allow_token(f, Token_ArrowRight)) { - if (f->curr_token.kind == Token_OpenParen) { - expect_token(f, Token_OpenParen); - while (f->curr_token.kind != Token_CloseParen && - f->curr_token.kind != Token_EOF) { - array_add(&results, parse_type(f)); - if (f->curr_token.kind != Token_Comma) { - break; - } - next_token(f); - } - expect_token(f, Token_CloseParen); - - return results; - } - - array_add(&results, parse_type(f)); - return results; - } - return results; -} - -void parse_proc_signature(AstFile *f, - AstNodeArray *params, - AstNodeArray *results) { - expect_token(f, Token_OpenParen); - *params = parse_field_list(f, NULL, FieldFlag_Signature, Token_Comma, Token_CloseParen); - expect_token_after(f, Token_CloseParen, "parameter list"); - *results = parse_results(f); -} - AstNode *parse_body(AstFile *f) { AstNodeArray stmts = {0}; Token open, close; @@ -2727,48 +2750,6 @@ AstNode *parse_body(AstFile *f) { return ast_block_stmt(f, stmts, open, close); } - -/* -AstNode *parse_proc_decl(AstFile *f) { - if (look_ahead_token_kind(f, 1) == Token_OpenParen) { - // NOTE(bill): It's an anonymous procedure - // NOTE(bill): This look-ahead technically makes the grammar LALR(2) - // but is that a problem in practice? - return ast_expr_stmt(f, parse_expr(f, true)); - } - - AstNodeArray params = {0}; - AstNodeArray results = {0}; - - Token proc_token = expect_token(f, Token_proc); - AstNode *name = parse_ident(f); - parse_proc_signature(f, ¶ms, &results); - - u64 tags = 0; - String foreign_name = {0}; - String link_name = {0}; - ProcCallingConvention cc = ProcCC_Odin; - - parse_proc_tags(f, &tags, &foreign_name, &link_name, &cc); - - AstNode *proc_type = ast_proc_type(f, proc_token, params, results, tags, cc); - AstNode *body = NULL; - - if (f->curr_token.kind == Token_OpenBrace) { - if ((tags & ProcTag_foreign) != 0) { - syntax_error(proc_token, "A procedure tagged as `#foreign` cannot have a body"); - } - AstNode *curr_proc = f->curr_proc; - f->curr_proc = proc_type; - body = parse_body(f); - f->curr_proc = curr_proc; - } else if ((tags & ProcTag_foreign) == 0) { - syntax_error(proc_token, "Only a procedure tagged as `#foreign` cannot have a body"); - } - - return ast_proc_decl(f, name, proc_type, body, tags, foreign_name, link_name); -} */ - AstNode *parse_if_stmt(AstFile *f) { if (f->curr_proc == NULL) { syntax_error(f->curr_token, "You cannot use an if statement in the file scope"); From 0c37aa9ea0271ddd8c93ea65f76d2f9ef4d777c5 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sat, 18 Feb 2017 22:19:35 +0000 Subject: [PATCH 3/8] Fix overloading bug due to comparison of named types --- code/demo.odin | 1 - core/sys/windows.odin | 5 +++-- src/check_expr.c | 5 +---- src/checker.c | 3 +++ src/types.c | 36 ++++++++++++++++++++++-------------- 5 files changed, 29 insertions(+), 21 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index 34274cb22..8e5719f0f 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -7,7 +7,6 @@ #import "os.odin"; #import "halloc.odin"; - main :: proc() { /* /* diff --git a/core/sys/windows.odin b/core/sys/windows.odin index 165a0f761..d027c6072 100644 --- a/core/sys/windows.odin +++ b/core/sys/windows.odin @@ -396,8 +396,8 @@ PIXELFORMATDESCRIPTOR :: struct #ordered { damage_mask: u32, } -GetDC :: proc(h: HANDLE) -> HDC #foreign user32; -SetPixelFormat :: proc(hdc: HDC, pixel_format: i32, pfd: ^PIXELFORMATDESCRIPTOR ) -> BOOL #foreign gdi32; +GetDC :: proc(h: HWND) -> HDC #foreign user32; +SetPixelFormat :: proc(hdc: HDC, pixel_format: i32, pfd: ^PIXELFORMATDESCRIPTOR) -> BOOL #foreign gdi32; ChoosePixelFormat :: proc(hdc: HDC, pfd: ^PIXELFORMATDESCRIPTOR) -> i32 #foreign gdi32; SwapBuffers :: proc(hdc: HDC) -> BOOL #foreign gdi32; ReleaseDC :: proc(wnd: HWND, hdc: HDC) -> i32 #foreign user32; @@ -412,6 +412,7 @@ wglCreateContext :: proc(hdc: HDC) -> HGLRC #foreign opengl32; wglMakeCurrent :: proc(hdc: HDC, hglrc: HGLRC) -> BOOL #foreign opengl32; wglGetProcAddress :: proc(c_str: ^u8) -> PROC #foreign opengl32; wglDeleteContext :: proc(hglrc: HGLRC) -> BOOL #foreign opengl32; +wglChoosePixelFormatARB :: proc(hdc: HDC, attribi_list: ^i32, attribf_list: ^f32, max_formats: u32, formats: ^i32, num_formats: u32) -> BOOL #foreign opengl32; diff --git a/src/check_expr.c b/src/check_expr.c index 41f9ce08d..2f612513b 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -183,10 +183,7 @@ i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) { // TODO(bill): Should I allow this implicit conversion at all?! // rawptr <- ^T - if (is_type_rawptr(dst) && is_type_pointer(src)) { - if (dst != type) { - return -1; - } + if (are_types_identical(type, t_rawptr) && is_type_pointer(src)) { return 5; } #endif diff --git a/src/checker.c b/src/checker.c index 0b3c66166..e44fd1a77 100644 --- a/src/checker.c +++ b/src/checker.c @@ -1242,6 +1242,9 @@ void check_procedure_overloading(Checker *c, Entity *e) { ProcTypeOverloadKind kind = are_proc_types_overload_safe(p->type, q->type); switch (kind) { case ProcOverload_Identical: + error(p->token, "Overloaded procedure `%.*s` as the same type as another procedure in this scope", LIT(name)); + is_invalid = true; + break; case ProcOverload_CallingConvention: error(p->token, "Overloaded procedure `%.*s` as the same type as another procedure in this scope", LIT(name)); is_invalid = true; diff --git a/src/types.c b/src/types.c index 06a407576..72c9f19ce 100644 --- a/src/types.c +++ b/src/types.c @@ -888,7 +888,7 @@ bool are_types_identical(Type *x, Type *y) { case Type_Named: if (y->kind == Type_Named) { - return x->Named.base == y->Named.base; + return x->Named.type_name == y->Named.type_name; } break; @@ -923,7 +923,6 @@ bool are_types_identical(Type *x, Type *y) { break; } - return false; } @@ -1023,41 +1022,50 @@ typedef enum ProcTypeOverloadKind { ProcTypeOverloadKind are_proc_types_overload_safe(Type *x, Type *y) { if (!is_type_proc(x)) return ProcOverload_NotProcedure; if (!is_type_proc(y)) return ProcOverload_NotProcedure; - TypeProc *px = &base_type(x)->Proc; - TypeProc *py = &base_type(y)->Proc; + TypeProc px = base_type(x)->Proc; + TypeProc py = base_type(y)->Proc; - if (px->calling_convention != py->calling_convention) { + if (px.calling_convention != py.calling_convention) { return ProcOverload_CallingConvention; } - if (px->param_count != py->param_count) { + if (px.param_count != py.param_count) { return ProcOverload_ParamCount; } - for (isize i = 0; i < px->param_count; i++) { - Entity *ex = px->params->Tuple.variables[i]; - Entity *ey = py->params->Tuple.variables[i]; + for (isize i = 0; i < px.param_count; i++) { + Entity *ex = px.params->Tuple.variables[i]; + Entity *ey = py.params->Tuple.variables[i]; if (!are_types_identical(ex->type, ey->type)) { return ProcOverload_ParamTypes; } } // IMPORTANT TODO(bill): Determine the rules for overloading procedures with variadic parameters - if (px->variadic != py->variadic) { + if (px.variadic != py.variadic) { return ProcOverload_ParamVariadic; } - if (px->result_count != py->result_count) { + if (px.result_count != py.result_count) { return ProcOverload_ResultCount; } - for (isize i = 0; i < px->result_count; i++) { - Entity *ex = px->results->Tuple.variables[i]; - Entity *ey = py->results->Tuple.variables[i]; + for (isize i = 0; i < px.result_count; i++) { + Entity *ex = px.results->Tuple.variables[i]; + Entity *ey = py.results->Tuple.variables[i]; if (!are_types_identical(ex->type, ey->type)) { return ProcOverload_ResultTypes; } } + { + Entity *ex = px.params->Tuple.variables[0]; + Entity *ey = py.params->Tuple.variables[0]; + bool ok = are_types_identical(ex->type, ey->type); + if (ok) { + gb_printf_err("Here\n"); + } + } + return ProcOverload_Identical; } From 758dd9ba16a2dc5b01ea131e83c06bf33c33f116 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sun, 19 Feb 2017 11:35:33 +0000 Subject: [PATCH 4/8] Fix overloading bug due to `#import .`; Add sys/wgl.odin --- code/demo.odin | 2 +- core/opengl.odin | 3 +- core/sys/wgl.odin | 72 +++++++++++++++++++++++++++++++++++++++++++ core/sys/windows.odin | 20 +++--------- src/check_expr.c | 39 ++++++++++++----------- src/checker.c | 14 +++++++++ src/ir.c | 7 ----- 7 files changed, 114 insertions(+), 43 deletions(-) create mode 100644 core/sys/wgl.odin diff --git a/code/demo.odin b/code/demo.odin index 8e5719f0f..dc93ab9e9 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -13,7 +13,7 @@ main :: proc() { Version 0.1.1 Added: - * Dynamic Arrays [dynamic]Type` + * Dynamic Arrays `[dynamic]Type` * Dynamic Maps `map[Key]Value` * Dynamic array and map literals * Custom struct alignemnt `struct #align 8 { bar: i8 }` diff --git a/core/opengl.odin b/core/opengl.odin index 2821740b4..4e3ab9c39 100644 --- a/core/opengl.odin +++ b/core/opengl.odin @@ -1,5 +1,6 @@ #foreign_system_library lib "opengl32.lib" when ODIN_OS == "windows"; #import win32 "sys/windows.odin" when ODIN_OS == "windows"; +#import "sys/wgl.odin" when ODIN_OS == "windows"; #load "opengl_constants.odin"; Clear :: proc(mask: u32) #foreign lib "glClear"; @@ -35,7 +36,7 @@ _libgl := win32.LoadLibraryA(string_data("opengl32.dll\x00")); GetProcAddress :: proc(name: string) -> proc() #cc_c { assert(name[name.count-1] == 0); - res := win32.wglGetProcAddress(name.data); + res := wgl.GetProcAddress(name.data); if res == nil { res = win32.GetProcAddress(_libgl, name.data); } diff --git a/core/sys/wgl.odin b/core/sys/wgl.odin new file mode 100644 index 000000000..bf993ca58 --- /dev/null +++ b/core/sys/wgl.odin @@ -0,0 +1,72 @@ +#foreign_system_library "opengl32.lib" when ODIN_OS == "windows"; +#import . "windows.odin"; + +CONTEXT_MAJOR_VERSION_ARB :: 0x2091; +CONTEXT_MINOR_VERSION_ARB :: 0x2092; +CONTEXT_FLAGS_ARB :: 0x2094; +CONTEXT_PROFILE_MASK_ARB :: 0x9126; +CONTEXT_FORWARD_COMPATIBLE_BIT_ARB :: 0x0002; +CONTEXT_CORE_PROFILE_BIT_ARB :: 0x00000001; + +HGLRC :: HANDLE; +COLORREF :: u32; + +LAYERPLANEDESCRIPTOR :: struct #ordered { + size: u16, + version: u16, + flags: u32, + pixel_type: byte, + color_bits: byte, + red_bits: byte, + red_shift: byte, + green_bits: byte, + green_shift: byte, + blue_bits: byte, + blue_shift: byte, + alpha_bits: byte, + alpha_shift: byte, + accum_bits: byte, + accum_red_bits: byte, + accum_green_bits: byte, + accum_blue_bits: byte, + accum_alpha_bits: byte, + depth_bits: byte, + stencil_bits: byte, + aux_buffers: byte, + layer_type: byte, + reserved: byte, + transparent: COLORREF, +} + +POINTFLOAT :: struct #ordered { + x, y: f32, +} + +GLYPHMETRICSFLOAT :: struct #ordered { + black_box_x: f32, + black_box_y: f32, + glyph_origin: POINTFLOAT, + cell_inc_x: f32, + cell_inc_y: f32, +} + +CreateContextAttribsARBType :: #type proc(hdc: HDC, hshareContext: rawptr, attribList: ^i32) -> HGLRC; +ChoosePixelFormatARBType :: #type proc(hdc: HDC, attrib_i_list: ^i32, attrib_f_list: ^f32, max_formats: u32, formats: ^i32, num_formats : ^u32) -> BOOL #cc_c; + + +CreateContext :: proc(hdc: HDC) -> HGLRC #foreign opengl32 "wglCreateContext"; +MakeCurrent :: proc(hdc: HDC, hglrc: HGLRC) -> BOOL #foreign opengl32 "wglMakeCurrent"; +GetProcAddress :: proc(c_str: ^u8) -> PROC #foreign opengl32 "wglGetProcAddress"; +DeleteContext :: proc(hglrc: HGLRC) -> BOOL #foreign opengl32 "wglDeleteContext"; +CopyContext :: proc(src, dst: HGLRC, mask: u32) -> BOOL #foreign opengl32 "wglCopyContext"; +CreateLayerContext :: proc(hdc: HDC, layer_plane: i32) -> HGLRC #foreign opengl32 "wglCreateLayerContext"; +DescribeLayerPlane :: proc(hdc: HDC, pixel_format, layer_plane: i32, bytes: u32, pd: ^LAYERPLANEDESCRIPTOR) -> BOOL #foreign opengl32 "wglDescribeLayerPlane"; +GetCurrentContext :: proc() -> HGLRC #foreign opengl32 "wglGetCurrentContext"; +GetCurrentDC :: proc() -> HDC #foreign opengl32 "wglGetCurrentDC"; +GetLayerPaletteEntries :: proc(hdc: HDC, layer_plane, start, entries: i32, cr: ^COLORREF) -> i32 #foreign opengl32 "wglGetLayerPaletteEntries"; +RealizeLayerPalette :: proc(hdc: HDC, layer_plane: i32, realize: BOOL) -> BOOL #foreign opengl32 "wglRealizeLayerPalette"; +SetLayerPaletteEntries :: proc(hdc: HDC, layer_plane, start, entries: i32, cr: ^COLORREF) -> i32 #foreign opengl32 "wglSetLayerPaletteEntries"; +ShareLists :: proc(hglrc1, hglrc2: HGLRC) -> BOOL #foreign opengl32 "wglShareLists"; +SwapLayerBuffers :: proc(hdc: HDC, planes: u32) -> BOOL #foreign opengl32 "wglSwapLayerBuffers"; +UseFontBitmaps :: proc(hdc: HDC, first, count, list_base: u32) -> BOOL #foreign opengl32 "wglUseFontBitmaps"; +UseFontOutlines :: proc(hdc: HDC, first, count, list_base: u32, deviation, extrusion: f32, format: i32, gmf: ^GLYPHMETRICSFLOAT) -> BOOL #foreign opengl32 "wglUseFontOutlines"; diff --git a/core/sys/windows.odin b/core/sys/windows.odin index d027c6072..6d6059214 100644 --- a/core/sys/windows.odin +++ b/core/sys/windows.odin @@ -2,7 +2,6 @@ #foreign_system_library "user32.lib" when ODIN_OS == "windows"; #foreign_system_library "gdi32.lib" when ODIN_OS == "windows"; #foreign_system_library "winmm.lib" when ODIN_OS == "windows"; -#foreign_system_library "opengl32.lib" when ODIN_OS == "windows"; HANDLE :: rawptr; HWND :: HANDLE; @@ -168,6 +167,9 @@ DefWindowProcA :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> AdjustWindowRect :: proc(rect: ^RECT, style: u32, menu: BOOL) -> BOOL #foreign user32; GetActiveWindow :: proc() -> HWND #foreign user32; +DestroyWindow :: proc(wnd: HWND) -> BOOL #foreign user32; +DescribePixelFormat :: proc(dc: HDC, pixel_format: i32, bytes : u32, pfd: ^PIXELFORMATDESCRIPTOR) -> i32 #foreign user32; + GetQueryPerformanceFrequency :: proc() -> i64 { r: i64; @@ -360,10 +362,6 @@ PFD_DEPTH_DONTCARE :: 0x20000000; PFD_DOUBLEBUFFER_DONTCARE :: 0x40000000; PFD_STEREO_DONTCARE :: 0x80000000; -HGLRC :: HANDLE; -PROC :: #type proc() #cc_c; -wglCreateContextAttribsARBType :: #type proc(hdc: HDC, hshareContext: rawptr, attribList: ^i32) -> HGLRC; - PIXELFORMATDESCRIPTOR :: struct #ordered { size, @@ -402,18 +400,8 @@ ChoosePixelFormat :: proc(hdc: HDC, pfd: ^PIXELFORMATDESCRIPTOR) -> i32 #foreign SwapBuffers :: proc(hdc: HDC) -> BOOL #foreign gdi32; ReleaseDC :: proc(wnd: HWND, hdc: HDC) -> i32 #foreign user32; -WGL_CONTEXT_MAJOR_VERSION_ARB :: 0x2091; -WGL_CONTEXT_MINOR_VERSION_ARB :: 0x2092; -WGL_CONTEXT_PROFILE_MASK_ARB :: 0x9126; -WGL_CONTEXT_CORE_PROFILE_BIT_ARB :: 0x0001; -WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB :: 0x0002; - -wglCreateContext :: proc(hdc: HDC) -> HGLRC #foreign opengl32; -wglMakeCurrent :: proc(hdc: HDC, hglrc: HGLRC) -> BOOL #foreign opengl32; -wglGetProcAddress :: proc(c_str: ^u8) -> PROC #foreign opengl32; -wglDeleteContext :: proc(hglrc: HGLRC) -> BOOL #foreign opengl32; -wglChoosePixelFormatARB :: proc(hdc: HDC, attribi_list: ^i32, attribf_list: ^f32, max_formats: u32, formats: ^i32, num_formats: u32) -> BOOL #foreign opengl32; +PROC :: #type proc() #cc_c; GetKeyState :: proc(v_key: i32) -> i16 #foreign user32; diff --git a/src/check_expr.c b/src/check_expr.c index 2f612513b..21eb59ff3 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -2564,6 +2564,17 @@ bool check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *val return true; } +isize procedure_overload_count(Scope *s, String name) { + isize overload_count = 0; + Entity *e = scope_lookup_entity(s, name); + if (e->kind == Entity_Procedure) { + HashKey key = hash_string(e->token.string); + // NOTE(bill): Overloads are only allowed with the same scope + overload_count = map_entity_multi_count(&s->elements, key); + } + return overload_count; +} + Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_hint) { ast_node(se, SelectorExpr, node); @@ -2596,15 +2607,17 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h // It pretty much needs to be in this order and this way // If you can clean this up, please do but be really careful + Scope *import_scope = e->ImportName.scope; + String sel_name = selector->Ident.string; check_op_expr = false; - entity = scope_lookup_entity(e->ImportName.scope, sel_name); + entity = scope_lookup_entity(import_scope, sel_name); bool is_declared = entity != NULL; if (is_declared) { if (entity->kind == Entity_Builtin) { is_declared = false; - } else if (entity->scope->is_global && !e->ImportName.scope->is_global) { + } else if (entity->scope->is_global && !import_scope->is_global) { is_declared = false; } } @@ -2615,18 +2628,8 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h check_entity_decl(c, entity, NULL, NULL); GB_ASSERT(entity->type != NULL); - bool is_overloaded = false; - isize overload_count = 0; - HashKey key = {0}; - if (entity->kind == Entity_Procedure) { - key = hash_string(entity->token.string); - // NOTE(bill): Overloads are only allowed with the same scope - Scope *s = entity->scope; - overload_count = map_entity_multi_count(&s->elements, key); - if (overload_count > 1) { - is_overloaded = true; - } - } + isize overload_count = procedure_overload_count(import_scope, entity->token.string); + bool is_overloaded = overload_count > 1; bool implicit_is_found = map_bool_get(&e->ImportName.scope->implicit, hash_pointer(entity)) != NULL; bool is_not_exported = !is_entity_exported(entity); @@ -2645,12 +2648,13 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h } if (is_overloaded) { - Scope *s = entity->scope; + HashKey key = hash_string(entity->token.string); + Scope *s = import_scope; bool skip = false; Entity **procs = gb_alloc_array(heap_allocator(), Entity *, overload_count); map_entity_multi_get_all(&s->elements, key, procs); - for (isize i = 0; i < overload_count; /**/) { + for (isize i = 0; i < overload_count; i++) { Type *t = base_type(procs[i]->type); if (t == t_invalid) { continue; @@ -2660,6 +2664,7 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h if (map_bool_get(&e->ImportName.scope->implicit, hash_pointer(procs[i]))) { gb_swap(Entity *, procs[i], procs[overload_count-1]); overload_count--; + i--; // NOTE(bill): Counteract the post event continue; } @@ -2673,8 +2678,6 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h break; } } - - i++; } if (overload_count > 0 && !skip) { diff --git a/src/checker.c b/src/checker.c index e44fd1a77..4e27dbf3f 100644 --- a/src/checker.c +++ b/src/checker.c @@ -1715,6 +1715,12 @@ void check_import_entities(Checker *c, MapScope *file_scopes) { continue; } if (id->is_import) { + // String gpa = str_lit("GetProcAddress"); + // if (str_eq(e->token.string, gpa)) { + // Entity *f = scope_lookup_entity(parent_scope, gpa); + // gb_printf_err("%.*s %.*s %td\n", LIT(gpa), LIT(f->token.pos.file), entity_procedure_overload_count(f)); + // } + if (is_entity_exported(e)) { // TODO(bill): Should these entities be imported but cause an error when used? bool ok = add_entity(c, parent_scope, NULL, e); @@ -1722,6 +1728,12 @@ void check_import_entities(Checker *c, MapScope *file_scopes) { map_bool_set(&parent_scope->implicit, hash_pointer(e), true); } } + + // if (str_eq(e->token.string, gpa)) { + // Entity *f = scope_lookup_entity(parent_scope, gpa); + // gb_printf_err("%.*s %.*s %td\n", LIT(gpa), LIT(f->token.pos.file), entity_procedure_overload_count(f)); + // } + } else { add_entity(c, parent_scope, NULL, e); } @@ -1736,6 +1748,8 @@ void check_import_entities(Checker *c, MapScope *file_scopes) { Entity *e = make_entity_import_name(c->allocator, parent_scope, id->import_name, t_invalid, id->fullpath, id->import_name.string, scope); + + add_entity(c, parent_scope, NULL, e); } } diff --git a/src/ir.c b/src/ir.c index 132d3a02d..2a83a9601 100644 --- a/src/ir.c +++ b/src/ir.c @@ -1102,13 +1102,6 @@ void ir_add_block_to_proc(irProcedure *proc, irBlock *b) { b->index = proc->block_count++; } - -// irBlock *ir_add_block(irProcedure *proc, AstNode *node, char *label) { -// irBlock *block = ir_new_block(proc, node, label); -// ir_add_block_to_proc(proc, block); -// return block; -// } - void ir_start_block(irProcedure *proc, irBlock *block) { proc->curr_block = block; if (block != NULL) { From 3cec2550d9b15eb56b8dd1b42a30b9f47ace0b4c Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sun, 19 Feb 2017 11:50:42 +0000 Subject: [PATCH 5/8] `delete` for maps --- code/demo.odin | 7 +++++++ core/_preload.odin | 2 +- src/check_expr.c | 34 ++++++++++++++++++++++++++++++++-- src/checker.c | 14 ++------------ src/ir.c | 19 ++++++++++++++++++- 5 files changed, 60 insertions(+), 16 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index dc93ab9e9..b8bf9bf6d 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -8,6 +8,13 @@ #import "halloc.odin"; main :: proc() { + + m: map[int]int; + m[123] = 312; + fmt.println(m[123]); + delete(m, 123); + fmt.println(m[123]); + /* /* Version 0.1.1 diff --git a/core/_preload.odin b/core/_preload.odin index 28cd761f4..fe8288bc6 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -633,7 +633,7 @@ __dynamic_map_add_entry :: proc(using h: __Map_Header, key: __Map_Key) -> int { } -__dynamic_map_remove :: proc(using h: __Map_Header, key: __Map_Key) { +__dynamic_map_delete :: proc(using h: __Map_Header, key: __Map_Key) { fr := __dynamic_map_find(h, key); if fr.entry_index >= 0 { __dynamic_map_erase(h, fr); diff --git a/src/check_expr.c b/src/check_expr.c index 21eb59ff3..883f98865 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -2643,7 +2643,6 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h gbString sel_str = expr_to_string(selector); error_node(op_expr, "`%s` is not exported by `%.*s`", sel_str, LIT(name)); gb_string_free(sel_str); - // NOTE(bill): Not really an error so don't goto error goto error; } @@ -2966,7 +2965,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id } break; case BuiltinProc_append: { - // append :: proc([dynamic]Type, item: ...Type) { + // append :: proc([dynamic]Type, item: ...Type) Type *type = operand->type; type = base_type(type); if (!is_type_dynamic_array(type)) { @@ -2996,6 +2995,37 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id operand->type = t_int; } break; + case BuiltinProc_delete: { + // delete :: proc(map[Key]Value, key: Key) + Type *type = operand->type; + if (!is_type_map(type)) { + gbString str = type_to_string(type); + error_node(operand->expr, "Expected a map, got `%s`", str); + gb_string_free(str); + return false; + } + + Type *key = base_type(type)->Map.key; + Operand x = {Addressing_Invalid}; + AstNode *key_node = ce->args.e[1]; + Operand op = {0}; + check_expr(c, &op, key_node); + if (op.mode == Addressing_Invalid) { + return false; + } + + if (!check_is_assignable_to(c, &op, key)) { + gbString kt = type_to_string(key); + gbString ot = type_to_string(op.type); + error_node(operand->expr, "Expected a key of type `%s`, got `%s`", key, ot); + gb_string_free(ot); + gb_string_free(kt); + return false; + } + + operand->mode = Addressing_NoValue; + } break; + case BuiltinProc_size_of: { // size_of :: proc(Type) -> untyped int diff --git a/src/checker.c b/src/checker.c index 4e27dbf3f..c512e6e69 100644 --- a/src/checker.c +++ b/src/checker.c @@ -30,6 +30,7 @@ typedef enum BuiltinProcId { BuiltinProc_reserve, BuiltinProc_clear, BuiltinProc_append, + BuiltinProc_delete, BuiltinProc_size_of, BuiltinProc_size_of_val, @@ -73,6 +74,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { {STR_LIT("reserve"), 2, false, Expr_Stmt}, {STR_LIT("clear"), 1, false, Expr_Stmt}, {STR_LIT("append"), 1, true, Expr_Expr}, + {STR_LIT("delete"), 2, false, Expr_Stmt}, {STR_LIT("size_of"), 1, false, Expr_Expr}, {STR_LIT("size_of_val"), 1, false, Expr_Expr}, @@ -1715,12 +1717,6 @@ void check_import_entities(Checker *c, MapScope *file_scopes) { continue; } if (id->is_import) { - // String gpa = str_lit("GetProcAddress"); - // if (str_eq(e->token.string, gpa)) { - // Entity *f = scope_lookup_entity(parent_scope, gpa); - // gb_printf_err("%.*s %.*s %td\n", LIT(gpa), LIT(f->token.pos.file), entity_procedure_overload_count(f)); - // } - if (is_entity_exported(e)) { // TODO(bill): Should these entities be imported but cause an error when used? bool ok = add_entity(c, parent_scope, NULL, e); @@ -1728,12 +1724,6 @@ void check_import_entities(Checker *c, MapScope *file_scopes) { map_bool_set(&parent_scope->implicit, hash_pointer(e), true); } } - - // if (str_eq(e->token.string, gpa)) { - // Entity *f = scope_lookup_entity(parent_scope, gpa); - // gb_printf_err("%.*s %.*s %td\n", LIT(gpa), LIT(f->token.pos.file), entity_procedure_overload_count(f)); - // } - } else { add_entity(c, parent_scope, NULL, e); } diff --git a/src/ir.c b/src/ir.c index 2a83a9601..2f8155225 100644 --- a/src/ir.c +++ b/src/ir.c @@ -3244,7 +3244,7 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv args[3] = capacity; return ir_emit_global_call(proc, "__dynamic_array_reserve", args, 4); } else if (is_type_dynamic_map(type)) { - irValue **args = gb_alloc_array(a, irValue *, 4); + irValue **args = gb_alloc_array(a, irValue *, 2); args[0] = ir_gen_map_header(proc, ptr, type); args[1] = capacity; return ir_emit_global_call(proc, "__dynamic_map_reserve", args, 2); @@ -3360,6 +3360,23 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv return ir_emit_global_call(proc, "__dynamic_array_append", daa_args, 5); } break; + case BuiltinProc_delete: { + ir_emit_comment(proc, str_lit("delete")); + irValue *map = ir_build_expr(proc, ce->args.e[0]); + irValue *key = ir_build_expr(proc, ce->args.e[1]); + Type *map_type = ir_type(map); + GB_ASSERT(is_type_dynamic_map(map_type)); + Type *key_type = base_type(map_type)->Map.key; + + irValue *addr = ir_address_from_load_or_generate_local(proc, map); + + gbAllocator a = proc->module->allocator; + irValue **args = gb_alloc_array(a, irValue *, 2); + args[0] = ir_gen_map_header(proc, addr, map_type); + args[1] = ir_gen_map_key(proc, key, key_type); + return ir_emit_global_call(proc, "__dynamic_map_delete", args, 2); + } break; + case BuiltinProc_assert: { ir_emit_comment(proc, str_lit("assert")); From 6fdcbefe5d488deea2825d908ff36af624e170d2 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sun, 19 Feb 2017 12:38:49 +0000 Subject: [PATCH 6/8] Unexported struct fields --- code/demo.odin | 7 ----- core/fmt.odin | 2 ++ src/check_expr.c | 68 +++++++++++++++++++++++++++++++++--------------- src/checker.c | 2 ++ src/entity.c | 1 + 5 files changed, 52 insertions(+), 28 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index b8bf9bf6d..dc93ab9e9 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -8,13 +8,6 @@ #import "halloc.odin"; main :: proc() { - - m: map[int]int; - m[123] = 312; - fmt.println(m[123]); - delete(m, 123); - fmt.println(m[123]); - /* /* Version 0.1.1 diff --git a/core/fmt.odin b/core/fmt.odin index d340bbe5d..6e0856ecb 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -11,6 +11,8 @@ Buffer :: struct { length: int, } + + buffer_write :: proc(buf: ^Buffer, b: []byte) { if buf.length < buf.data.count { n := min(buf.data.count-buf.length, b.count); diff --git a/src/check_expr.c b/src/check_expr.c index 883f98865..a36838f97 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -2564,15 +2564,16 @@ bool check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *val return true; } -isize procedure_overload_count(Scope *s, String name) { - isize overload_count = 0; +isize entity_overload_count(Scope *s, String name) { Entity *e = scope_lookup_entity(s, name); - if (e->kind == Entity_Procedure) { - HashKey key = hash_string(e->token.string); - // NOTE(bill): Overloads are only allowed with the same scope - overload_count = map_entity_multi_count(&s->elements, key); + if (e == NULL) { + return 0; } - return overload_count; + if (e->kind == Entity_Procedure) { + // NOTE(bill): Overloads are only allowed with the same scope + return map_entity_multi_count(&s->elements, hash_string(e->token.string)); + } + return 1; } Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_hint) { @@ -2596,8 +2597,8 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h } if (op_expr->kind == AstNode_Ident) { - String name = op_expr->Ident.string; - Entity *e = scope_lookup_entity(c->context.scope, name); + String op_name = op_expr->Ident.string; + Entity *e = scope_lookup_entity(c->context.scope, op_name); add_entity_use(c, op_expr, e); expr_entity = e; @@ -2606,29 +2607,30 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h // IMPORTANT NOTE(bill): This is very sloppy code but it's also very fragile // It pretty much needs to be in this order and this way // If you can clean this up, please do but be really careful - + String import_name = op_name; Scope *import_scope = e->ImportName.scope; - - String sel_name = selector->Ident.string; + String entity_name = selector->Ident.string; check_op_expr = false; - entity = scope_lookup_entity(import_scope, sel_name); + entity = scope_lookup_entity(import_scope, entity_name); bool is_declared = entity != NULL; if (is_declared) { if (entity->kind == Entity_Builtin) { + // NOTE(bill): Builtin's are in the universe scope which is part of every scopes hierarchy + // This means that we should just ignore the found result through it is_declared = false; } else if (entity->scope->is_global && !import_scope->is_global) { is_declared = false; } } if (!is_declared) { - error_node(op_expr, "`%.*s` is not declared by `%.*s`", LIT(sel_name), LIT(name)); + error_node(op_expr, "`%.*s` is not declared by `%.*s`", LIT(entity_name), LIT(import_name)); goto error; } check_entity_decl(c, entity, NULL, NULL); GB_ASSERT(entity->type != NULL); - isize overload_count = procedure_overload_count(import_scope, entity->token.string); + isize overload_count = entity_overload_count(import_scope, entity_name); bool is_overloaded = overload_count > 1; bool implicit_is_found = map_bool_get(&e->ImportName.scope->implicit, hash_pointer(entity)) != NULL; @@ -2641,18 +2643,17 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h if (is_not_exported) { gbString sel_str = expr_to_string(selector); - error_node(op_expr, "`%s` is not exported by `%.*s`", sel_str, LIT(name)); + error_node(op_expr, "`%s` is not exported by `%.*s`", sel_str, LIT(import_name)); gb_string_free(sel_str); goto error; } if (is_overloaded) { - HashKey key = hash_string(entity->token.string); - Scope *s = import_scope; + HashKey key = hash_string(entity_name); bool skip = false; Entity **procs = gb_alloc_array(heap_allocator(), Entity *, overload_count); - map_entity_multi_get_all(&s->elements, key, procs); + map_entity_multi_get_all(&import_scope->elements, key, procs); for (isize i = 0; i < overload_count; i++) { Type *t = base_type(procs[i]->type); if (t == t_invalid) { @@ -2660,7 +2661,7 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h } // NOTE(bill): Check to see if it's imported - if (map_bool_get(&e->ImportName.scope->implicit, hash_pointer(procs[i]))) { + if (map_bool_get(&import_scope->implicit, hash_pointer(procs[i]))) { gb_swap(Entity *, procs[i], procs[overload_count-1]); overload_count--; i--; // NOTE(bill): Counteract the post event @@ -4464,10 +4465,23 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint String name = fv->field->Ident.string; Selection sel = lookup_field(c->allocator, type, name, o->mode == Addressing_Type); - if (sel.entity == NULL) { + bool is_unknown = sel.entity == NULL; + if (is_unknown) { error_node(elem, "Unknown field `%.*s` in structure literal", LIT(name)); continue; } + if (!is_unknown) { + Entity *f = sel.entity; + Scope *file_scope = f->scope; + while (!file_scope->is_file) { + file_scope = file_scope->parent; + } + if (!is_entity_exported(f) && file_scope != c->context.file_scope) { + error_node(elem, "Cannot assign to an unexported field `%.*s` in structure literal", LIT(name)); + continue; + } + } + if (sel.index.count > 1) { error_node(elem, "Cannot assign to an anonymous field `%.*s` in a structure literal (at the moment)", LIT(name)); @@ -4510,6 +4524,18 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint break; } + Scope *file_scope = field->scope; + while (!file_scope->is_file) { + file_scope = file_scope->parent; + } + if (!is_entity_exported(field) && file_scope != c->context.file_scope) { + gbString t = type_to_string(type); + error_node(o->expr, "Implicit assignment to an unexported field `%.*s` in `%s` literal", + LIT(field->token.string), t); + gb_string_free(t); + continue; + } + if (base_type(field->type) == t_any) { is_constant = false; } diff --git a/src/checker.c b/src/checker.c index c512e6e69..94ae0c2e0 100644 --- a/src/checker.c +++ b/src/checker.c @@ -261,6 +261,7 @@ typedef struct DelayedDecl { } DelayedDecl; typedef struct CheckerContext { + Scope * file_scope; Scope * scope; DeclInfo * decl; u32 stmt_state_flags; @@ -1018,6 +1019,7 @@ void add_curr_ast_file(Checker *c, AstFile *file) { c->curr_ast_file = file; c->context.decl = file->decl_info; c->context.scope = file->scope; + c->context.file_scope = file->scope; } } diff --git a/src/entity.c b/src/entity.c index 99acef870..32d070953 100644 --- a/src/entity.c +++ b/src/entity.c @@ -113,6 +113,7 @@ bool is_entity_kind_exported(EntityKind kind) { } bool is_entity_exported(Entity *e) { + // TODO(bill): Determine the actual exportation rules for imports of entities GB_ASSERT(e != NULL); if (!is_entity_kind_exported(e->kind)) { return false; From c0d5237b75b1ce19ee0e003dcd2dcffa66c442fd Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sun, 19 Feb 2017 12:47:02 +0000 Subject: [PATCH 7/8] Unexported struct fields on selectors --- code/demo.odin | 7 +++++++ core/fmt.odin | 2 -- src/check_expr.c | 48 ++++++++++++++++++++++++++++++++---------------- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index dc93ab9e9..5b1c82522 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -8,6 +8,13 @@ #import "halloc.odin"; main :: proc() { + m: map[int]int; + m[123] = 312; + fmt.println(m[123]); + delete(m, 123); + fmt.println(m[123]); + + /* /* Version 0.1.1 diff --git a/core/fmt.odin b/core/fmt.odin index 6e0856ecb..d340bbe5d 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -11,8 +11,6 @@ Buffer :: struct { length: int, } - - buffer_write :: proc(buf: ^Buffer, b: []byte) { if buf.length < buf.data.count { n := min(buf.data.count-buf.length, b.count); diff --git a/src/check_expr.c b/src/check_expr.c index a36838f97..4c1deb265 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -2576,6 +2576,27 @@ isize entity_overload_count(Scope *s, String name) { return 1; } +bool check_is_field_exported(Checker *c, Entity *field) { + if (field == NULL) { + // NOTE(bill): Just incase + return true; + } + if (field->kind != Entity_Variable) { + return true; + } + Scope *file_scope = field->scope; + if (file_scope == NULL) { + return true; + } + while (!file_scope->is_file) { + file_scope = file_scope->parent; + } + if (!is_entity_exported(field) && file_scope != c->context.file_scope) { + return false; + } + return true; +} + Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_hint) { ast_node(se, SelectorExpr, node); @@ -2701,7 +2722,13 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h if (entity == NULL && selector->kind == AstNode_Ident) { - sel = lookup_field(c->allocator, operand->type, selector->Ident.string, operand->mode == Addressing_Type); + String field_name = selector->Ident.string; + sel = lookup_field(c->allocator, operand->type, field_name, operand->mode == Addressing_Type); + + if (operand->mode != Addressing_Type && !check_is_field_exported(c, sel.entity)) { + error_node(op_expr, "`%.*s` is an unexported field", LIT(field_name)); + goto error; + } entity = sel.entity; // NOTE(bill): Add type info needed for fields like `names` @@ -4470,16 +4497,9 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint error_node(elem, "Unknown field `%.*s` in structure literal", LIT(name)); continue; } - if (!is_unknown) { - Entity *f = sel.entity; - Scope *file_scope = f->scope; - while (!file_scope->is_file) { - file_scope = file_scope->parent; - } - if (!is_entity_exported(f) && file_scope != c->context.file_scope) { - error_node(elem, "Cannot assign to an unexported field `%.*s` in structure literal", LIT(name)); - continue; - } + if (!is_unknown && !check_is_field_exported(c, sel.entity)) { + error_node(elem, "Cannot assign to an unexported field `%.*s` in structure literal", LIT(name)); + continue; } @@ -4524,11 +4544,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint break; } - Scope *file_scope = field->scope; - while (!file_scope->is_file) { - file_scope = file_scope->parent; - } - if (!is_entity_exported(field) && file_scope != c->context.file_scope) { + if (!check_is_field_exported(c, field)) { gbString t = type_to_string(type); error_node(o->expr, "Implicit assignment to an unexported field `%.*s` in `%s` literal", LIT(field->token.string), t); From a94dfdf21d798bc72bbee0cc04b80149f0d4b8d2 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sun, 19 Feb 2017 19:55:19 +0000 Subject: [PATCH 8/8] Begin changing `union` syntax --- code/demo.odin | 2 +- core/_preload.odin | 43 +++++++++++++++++++++++++ src/check_expr.c | 80 ++++++++++++++++++++++++++++++++++++++-------- src/gb/gb.h | 2 +- src/ir.c | 68 +++++++++++++++++++++++++++------------ src/parser.c | 42 ++++++++++++++++++------ 6 files changed, 192 insertions(+), 45 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index 5b1c82522..df740c1c2 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -5,7 +5,7 @@ #import "mem.odin"; #import "opengl.odin"; #import "os.odin"; -#import "halloc.odin"; +// #import "halloc.odin"; main :: proc() { m: map[int]int; diff --git a/core/_preload.odin b/core/_preload.odin index fe8288bc6..c61049290 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -37,6 +37,7 @@ Calling_Convention :: enum { FAST = 3, } +/* Type_Info :: union { Named: struct #ordered { name: string, @@ -96,6 +97,48 @@ Type_Info :: union { count: int, // == 0 if dynamic }, } +*/ +Type_Info :: union { + Named{name: string, base: ^Type_Info}, + Integer{size: int, signed: bool}, + Float{size: int}, + String{}, + Boolean{}, + Any{}, + Pointer{ + elem: ^Type_Info, // nil -> rawptr + }, + Procedure{ + params: ^Type_Info, // Type_Info.Tuple + results: ^Type_Info, // Type_Info.Tuple + variadic: bool, + convention: Calling_Convention, + }, + Array{ + elem: ^Type_Info, + elem_size: int, + count: int, + }, + Dynamic_Array{elem: ^Type_Info, elem_size: int}, + Slice {elem: ^Type_Info, elem_size: int}, + Vector {elem: ^Type_Info, elem_size, count, align: int}, + Tuple {using record: Type_Info_Record}, // Only really used for procedures + Struct {using record: Type_Info_Record}, + Union {using record: Type_Info_Record}, + Raw_Union {using record: Type_Info_Record}, + Enum{ + base: ^Type_Info, + names: []string, + values: []Type_Info_Enum_Value, + }, + Map{ + key: ^Type_Info, + value: ^Type_Info, + generated_struct: ^Type_Info, + count: int, // == 0 if dynamic + }, +} + // // NOTE(bill): only the ones that are needed (not all types) // // This will be set by the compiler diff --git a/src/check_expr.c b/src/check_expr.c index 4c1deb265..d85c5100a 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -355,7 +355,7 @@ void check_fields(Checker *c, AstNode *node, AstNodeArray decls, Token name_token = name->Ident; - if (str_eq(name_token.string, str_lit(""))) { + if (str_eq(name_token.string, str_lit("names"))) { error(name_token, "`names` is a reserved identifier for unions"); continue; } @@ -596,22 +596,76 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node) { GB_ASSERT(is_type_union(union_type)); ast_node(ut, UnionType, node); - isize field_count = 1; - for_array(field_index, ut->fields) { - AstNode *field = ut->fields.e[field_index]; - switch (field->kind) { - case_ast_node(f, Field, field); - field_count += f->names.count; - case_end; - } - } + isize field_count = ut->fields.count+1; + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + + MapEntity entity_map = {0}; + map_entity_init_with_reserve(&entity_map, c->tmp_allocator, 2*field_count); Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count); - check_fields(c, node, ut->fields, fields, field_count, str_lit("union")); + isize field_index = 0; + fields[field_index++] = make_entity_type_name(c->allocator, c->context.scope, empty_token, NULL); - union_type->Record.fields = fields; - union_type->Record.field_count = field_count; + for_array(i, ut->fields) { + AstNode *field = ut->fields.e[i]; + if (field->kind != AstNode_UnionField) { + continue; + } + ast_node(f, UnionField, ut->fields.e[i]); + Token name_token = f->name->Ident; + + if (str_eq(name_token.string, str_lit("names"))) { + error(name_token, "`names` is a reserved identifier for unions"); + continue; + } + + Type *base_type = make_type_struct(c->allocator); + { + ast_node(fl, FieldList, f->list); + isize field_count = 0; + for_array(j, fl->list) { + ast_node(f, Field, fl->list.e[j]); + field_count += f->names.count; + } + + Token token = name_token; + token.kind = Token_struct; + AstNode *dummy_struct = ast_struct_type(c->curr_ast_file, token, fl->list, field_count, + false, true, NULL); + + check_open_scope(c, dummy_struct); + check_struct_type(c, base_type, dummy_struct); + check_close_scope(c); + base_type->Record.node = dummy_struct; + } + + Type *type = make_type_named(c->allocator, name_token.string, base_type, NULL); + Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, type); + type->Named.type_name = e; + add_entity(c, c->context.scope, f->name, e); + + if (str_eq(name_token.string, str_lit("_"))) { + error(name_token, "`_` cannot be used a union subtype"); + continue; + } + + HashKey key = hash_string(name_token.string); + if (map_entity_get(&entity_map, key) != NULL) { + // TODO(bill): Scope checking already checks the declaration + error(name_token, "`%.*s` is already declared in this union", LIT(name_token.string)); + } else { + map_entity_set(&entity_map, key, e); + fields[field_index++] = e; + } + add_entity_use(c, f->name, e); + } + + gb_temp_arena_memory_end(tmp); + + union_type->Record.fields = fields; + union_type->Record.field_count = field_index; union_type->Record.names = make_names_field_for_record(c, c->context.scope); } diff --git a/src/gb/gb.h b/src/gb/gb.h index e45ceb050..d30e5d129 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -705,7 +705,7 @@ extern "C++" { #endif #ifndef gb_is_between -#define gb_is_between(x, lower, upper) (((x) >= (lower)) && ((x) <= (upper))) +#define gb_is_between(x, lower, upper) (((lower) <= (x)) && ((x) <= (upper))) #endif #ifndef gb_abs diff --git a/src/ir.c b/src/ir.c index 2f8155225..a2a959c1a 100644 --- a/src/ir.c +++ b/src/ir.c @@ -6467,10 +6467,11 @@ void ir_gen_tree(irGen *s) { case Type_Tuple: { ir_emit_comment(proc, str_lit("Type_Info_Tuple")); tag = ir_emit_conv(proc, ti_ptr, t_type_info_tuple_ptr); + irValue *record = ir_emit_struct_ep(proc, tag, 0); { irValue *align = ir_make_const_int(a, type_align_of(a, t)); - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 4), align); + ir_emit_store(proc, ir_emit_struct_ep(proc, record, 4), align); } irValue *memory_types = ir_type_info_member_offset(proc, type_info_member_types, t->Record.field_count, &type_info_member_types_index); @@ -6490,14 +6491,15 @@ void ir_gen_tree(irGen *s) { } } - ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 0), memory_types, ir_make_const_int(a, t->Record.field_count)); - ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 1), memory_names, ir_make_const_int(a, t->Record.field_count)); + ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 0), memory_types, ir_make_const_int(a, t->Record.field_count)); + ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 1), memory_names, ir_make_const_int(a, t->Record.field_count)); } break; case Type_Record: { switch (t->Record.kind) { case TypeRecord_Struct: { ir_emit_comment(proc, str_lit("Type_Info_Struct")); tag = ir_emit_conv(proc, ti_ptr, t_type_info_struct_ptr); + irValue *record = ir_emit_struct_ep(proc, tag, 0); { irValue *size = ir_make_const_int(a, type_size_of(a, t)); @@ -6505,11 +6507,11 @@ void ir_gen_tree(irGen *s) { irValue *packed = ir_make_const_bool(a, t->Record.struct_is_packed); irValue *ordered = ir_make_const_bool(a, t->Record.struct_is_ordered); irValue *custom_align = ir_make_const_bool(a, t->Record.custom_align); - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 3), size); - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 4), align); - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 5), packed); - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 6), ordered); - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 7), custom_align); + ir_emit_store(proc, ir_emit_struct_ep(proc, record, 3), size); + ir_emit_store(proc, ir_emit_struct_ep(proc, record, 4), align); + ir_emit_store(proc, ir_emit_struct_ep(proc, record, 5), packed); + ir_emit_store(proc, ir_emit_struct_ep(proc, record, 6), ordered); + ir_emit_store(proc, ir_emit_struct_ep(proc, record, 7), custom_align); } irValue *memory_types = ir_type_info_member_offset(proc, type_info_member_types, t->Record.field_count, &type_info_member_types_index); @@ -6536,28 +6538,52 @@ void ir_gen_tree(irGen *s) { ir_emit_store(proc, offset, ir_make_const_int(a, foffset)); } - ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 0), memory_types, ir_make_const_int(a, t->Record.field_count)); - ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 1), memory_names, ir_make_const_int(a, t->Record.field_count)); - ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 2), memory_offsets, ir_make_const_int(a, t->Record.field_count)); + ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 0), memory_types, ir_make_const_int(a, t->Record.field_count)); + ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 1), memory_names, ir_make_const_int(a, t->Record.field_count)); + ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 2), memory_offsets, ir_make_const_int(a, t->Record.field_count)); } break; - case TypeRecord_Union: + case TypeRecord_Union: { ir_emit_comment(proc, str_lit("Type_Info_Union")); tag = ir_emit_conv(proc, ti_ptr, t_type_info_union_ptr); + irValue *record = ir_emit_struct_ep(proc, tag, 0); { irValue *size = ir_make_const_int(a, type_size_of(a, t)); irValue *align = ir_make_const_int(a, type_align_of(a, t)); - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 3), size); - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 4), align); + ir_emit_store(proc, ir_emit_struct_ep(proc, record, 3), size); + ir_emit_store(proc, ir_emit_struct_ep(proc, record, 4), align); } - break; + + irValue *memory_types = ir_type_info_member_offset(proc, type_info_member_types, t->Record.field_count, &type_info_member_types_index); + irValue *memory_names = ir_type_info_member_offset(proc, type_info_member_names, t->Record.field_count, &type_info_member_names_index); + + for (isize source_index = 1; source_index < t->Record.field_count; source_index++) { + // TODO(bill): Order fields in source order not layout order + Entity *f = t->Record.fields[source_index]; + irValue *tip = ir_get_type_info_ptr(proc, type_info_data, f->type); + irValue *index = ir_make_const_int(a, source_index); + irValue *type_info = ir_emit_ptr_offset(proc, memory_types, index); + + ir_emit_store(proc, type_info, ir_type_info(proc, f->type)); + if (f->token.string.len > 0) { + irValue *name = ir_emit_ptr_offset(proc, memory_names, index); + ir_emit_store(proc, name, ir_make_const_string(a, f->token.string)); + } + } + + ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 0), memory_types, ir_make_const_int(a, t->Record.field_count)); + ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 1), memory_names, ir_make_const_int(a, t->Record.field_count)); + + } break; case TypeRecord_RawUnion: { ir_emit_comment(proc, str_lit("Type_Info_RawUnion")); tag = ir_emit_conv(proc, ti_ptr, t_type_info_raw_union_ptr); + irValue *record = ir_emit_struct_ep(proc, tag, 0); + { irValue *size = ir_make_const_int(a, type_size_of(a, t)); irValue *align = ir_make_const_int(a, type_align_of(a, t)); - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 3), size); - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 4), align); + ir_emit_store(proc, ir_emit_struct_ep(proc, record, 3), size); + ir_emit_store(proc, ir_emit_struct_ep(proc, record, 4), align); } irValue *memory_types = ir_type_info_member_offset(proc, type_info_member_types, t->Record.field_count, &type_info_member_types_index); @@ -6577,9 +6603,9 @@ void ir_gen_tree(irGen *s) { } } - ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 0), memory_types, ir_make_const_int(a, t->Record.field_count)); - ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 1), memory_names, ir_make_const_int(a, t->Record.field_count)); - ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 2), memory_offsets, ir_make_const_int(a, t->Record.field_count)); + ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 0), memory_types, ir_make_const_int(a, t->Record.field_count)); + ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 1), memory_names, ir_make_const_int(a, t->Record.field_count)); + ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 2), memory_offsets, ir_make_const_int(a, t->Record.field_count)); } break; case TypeRecord_Enum: ir_emit_comment(proc, str_lit("Type_Info_Enum")); @@ -6668,7 +6694,7 @@ void ir_gen_tree(irGen *s) { break; } } - GB_ASSERT(found); + GB_ASSERT_MSG(found, "%s", type_to_string(tag_type)); } } } diff --git a/src/parser.c b/src/parser.c index 5e1e6bb57..7c4814a68 100644 --- a/src/parser.c +++ b/src/parser.c @@ -306,6 +306,10 @@ AST_NODE_KIND(_DeclEnd, "", i32) \ Token token; \ AstNodeArray list; \ }) \ + AST_NODE_KIND(UnionField, "union field", struct { \ + AstNode *name; \ + AstNode *list; \ + }) \ AST_NODE_KIND(_TypeBegin, "", i32) \ AST_NODE_KIND(HelperType, "type", struct { \ Token token; \ @@ -490,6 +494,8 @@ Token ast_node_token(AstNode *node) { return ast_node_token(node->Field.type); case AstNode_FieldList: return node->FieldList.token; + case AstNode_UnionField: + return ast_node_token(node->UnionField.name); case AstNode_HelperType: return node->HelperType.token; case AstNode_ProcType: return node->ProcType.token; @@ -965,6 +971,12 @@ AstNode *ast_field_list(AstFile *f, Token token, AstNodeArray list) { result->FieldList.list = list; return result; } +AstNode *ast_union_field(AstFile *f, AstNode *name, AstNode *list) { + AstNode *result = make_ast_node(f, AstNode_UnionField); + result->UnionField.name = name; + result->UnionField.list = list; + return result; +} AstNode *ast_helper_type(AstFile *f, Token token, AstNode *type) { @@ -1016,7 +1028,7 @@ AstNode *ast_vector_type(AstFile *f, Token token, AstNode *count, AstNode *elem) } AstNode *ast_struct_type(AstFile *f, Token token, AstNodeArray fields, isize field_count, - bool is_packed, bool is_ordered, AstNode *align) { + bool is_packed, bool is_ordered, AstNode *align) { AstNode *result = make_ast_node(f, AstNode_StructType); result->StructType.token = token; result->StructType.fields = fields; @@ -2670,17 +2682,29 @@ AstNode *parse_type_or_ident(AstFile *f) { case Token_union: { Token token = expect_token(f, Token_union); Token open = expect_token_after(f, Token_OpenBrace, "union"); - isize decl_count = 0; - AstNode *fields = parse_record_fields(f, &decl_count, 0, str_lit("union")); - Token close = expect_token(f, Token_CloseBrace); + AstNodeArray variants = make_ast_node_array(f); - AstNodeArray decls = {0}; - if (fields != NULL) { - GB_ASSERT(fields->kind == AstNode_FieldList); - decls = fields->FieldList.list; + while (f->curr_token.kind != Token_CloseBrace && + f->curr_token.kind != Token_EOF) { + AstNode *name = parse_ident(f); + Token open = expect_token(f, Token_OpenBrace); + isize decl_count = 0; + AstNode *list = parse_record_fields(f, &decl_count, FieldFlag_using, str_lit("union")); + Token close = expect_token(f, Token_CloseBrace); + + array_add(&variants, ast_union_field(f, name, list)); + + if (f->curr_token.kind != Token_Comma) { + break; + } + next_token(f); } - return ast_union_type(f, token, decls, decl_count); + // AstNode *fields = parse_record_fields(f, &decl_count, 0, str_lit("union")); + Token close = expect_token(f, Token_CloseBrace); + + + return ast_union_type(f, token, variants, variants.count); } case Token_raw_union: {