diff --git a/examples/demo.odin b/examples/demo.odin index a0441921c..492974e3c 100644 --- a/examples/demo.odin +++ b/examples/demo.odin @@ -3,6 +3,26 @@ #load "game.odin" main :: proc() { + + + match x := "1"; x { + case "1": + print_string("1!\n") + case "2": + print_string("2!\n") + if true { + break + } + case "3", "4": + print_string("3 or 4!\n") + fallthrough + default: + print_string("default!\n") + } + + nl() + +/* Vec3 :: type struct { x, y, z: f32 } Entity :: type struct { using pos: Vec3 @@ -19,11 +39,11 @@ main :: proc() { } f := Frog{}; - f.name = "ribbit"; - f.jump_height = 1337; + f.name = "ribbit" + f.jump_height = 1337 - e := ^f.entity; - parent := e down_cast ^Frog; + e := ^f.entity + parent := e down_cast ^Frog print_name :: proc(using e: Entity, v: Vec3) { print_string(name); nl() @@ -35,10 +55,8 @@ main :: proc() { print_name(f, Vec3{1, 2, 3}) print_name(parent, Vec3{3, 2, 1}) - - +*/ } - -nl :: proc() #inline { print_nl() } +nl :: proc() { print_nl() } diff --git a/examples/runtime.odin b/examples/runtime.odin index e203aaca1..6663819d4 100644 --- a/examples/runtime.odin +++ b/examples/runtime.odin @@ -365,13 +365,14 @@ __default_allocator_proc :: proc(allocator_data: rawptr, mode: AllocationMode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64) -> rawptr { using AllocationMode - if mode == ALLOC { + match mode { + case ALLOC: return heap_alloc(size) - } else if mode == RESIZE { + case RESIZE: return default_resize_align(old_memory, old_size, size, alignment) - } else if mode == DEALLOC { + case DEALLOC: heap_free(old_memory) - } else if mode == DEALLOC_ALL { + case DEALLOC_ALL: // NOTE(bill): Does nothing } diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp index d239d0995..25bef96de 100644 --- a/src/checker/checker.cpp +++ b/src/checker/checker.cpp @@ -339,6 +339,7 @@ Entity *scope_insert_entity(Scope *s, Entity *entity) { return NULL; } + void add_dependency(DeclInfo *d, Entity *e) { map_set(&d->deps, hash_pointer(e), cast(b32)true); } @@ -543,6 +544,7 @@ b32 add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) { LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column); return false; } else { + gb_printf_err("!!Here\n"); error(&c->error_collector, entity->token, "Redeclararation of `%.*s` in this scope\n" "\tat %.*s(%td:%td)", @@ -557,6 +559,78 @@ b32 add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) { return true; } + +/* +b32 add_proc_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) { + GB_ASSERT(entity->kind == Entity_Procedure); + + auto error_proc_redecl = [](Checker *c, Token token, Entity *other_entity, char *extra_msg) { + error(&c->error_collector, token, + "Redeclararation of `%.*s` in this scope %s\n" + "\tat %.*s(%td:%td)", + LIT(other_entity->token.string), + extra_msg, + LIT(other_entity->token.pos.file), other_entity->token.pos.line, other_entity->token.pos.column); + }; + + String name = entity->token.string; + HashKey key = hash_string(name); + + b32 insert_overload = false; + + if (!are_strings_equal(name, make_string("_"))) { + Entity *insert_entity = scope_insert_entity(scope, entity); + if (insert_entity != NULL) { + if (insert_entity != entity) { + isize count = multi_map_count(&scope->elements, key); + GB_ASSERT(count > 0); + Entity **entities = gb_alloc_array(gb_heap_allocator(), Entity *, count); + defer (gb_free(gb_heap_allocator(), entities)); + multi_map_get_all(&scope->elements, key, entities); + + for (isize i = 0; i < count; i++) { + Entity *e = entities[i]; + if (e == entity) { + continue; + } + if (e->kind == Entity_Procedure) { + Type *proc_type = entity->type; + Type *other_proc_type = e->type; + // gb_printf_err("%s == %s\n", type_to_string(proc_type), type_to_string(other_proc_type)); + if (are_types_identical(proc_type, other_proc_type)) { + error_proc_redecl(c, entity->token, e, "with identical types"); + return false; + } + + if (proc_type != NULL && other_proc_type != NULL) { + Type *params = proc_type->Proc.params; + Type *other_params = other_proc_type->Proc.params; + + if (are_types_identical(params, other_params)) { + error_proc_redecl(c, entity->token, e, "with 2identical parameters"); + return false; + } + } + } else { + error_proc_redecl(c, entity->token, e, ""); + return false; + } + } + insert_overload = true; + } + } + } + + if (insert_overload) { + multi_map_insert(&scope->elements, key, entity); + } + + if (identifier != NULL) + add_entity_definition(&c->info, identifier, entity); + return true; +} +*/ + void add_entity_use(CheckerInfo *i, AstNode *identifier, Entity *entity) { GB_ASSERT(identifier != NULL); GB_ASSERT(identifier->kind == AstNode_Ident); @@ -566,7 +640,7 @@ void add_entity_use(CheckerInfo *i, AstNode *identifier, Entity *entity) { void add_file_entity(Checker *c, AstNode *identifier, Entity *e, DeclInfo *d) { - GB_ASSERT(are_strings_equal(identifier->Ident.token.string, e->token.string)); + GB_ASSERT(are_strings_equal(identifier->Ident.string, e->token.string)); add_entity(c, c->global_scope, identifier, e); map_set(&c->info.entities, hash_pointer(e), d); @@ -608,7 +682,6 @@ void add_curr_ast_file(Checker *c, AstFile *file) { - #include "expr.cpp" #include "stmt.cpp" @@ -658,9 +731,8 @@ void check_parsed_files(Checker *c) { for (AstNode *name = vd->name_list, *value = vd->value_list; name != NULL && value != NULL; name = name->next, value = value->next) { - ast_node(n, Ident, name); ExactValue v = {ExactValue_Invalid}; - Entity *e = make_entity_constant(c->allocator, c->context.scope, n->token, NULL, v); + Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v); DeclInfo *di = make_declaration_info(c->allocator, c->global_scope); di->type_expr = vd->type; di->init_expr = value; @@ -692,8 +764,7 @@ void check_parsed_files(Checker *c) { AstNode *value = vd->value_list; for (AstNode *name = vd->name_list; name != NULL; name = name->next) { - ast_node(n, Ident, name); - Entity *e = make_entity_variable(c->allocator, c->global_scope, n->token, NULL); + Entity *e = make_entity_variable(c->allocator, c->global_scope, name->Ident, NULL); entities[entity_index++] = e; DeclInfo *d = di; @@ -716,7 +787,7 @@ void check_parsed_files(Checker *c) { case_ast_node(td, TypeDecl, decl); ast_node(n, Ident, td->name); - Entity *e = make_entity_type_name(c->allocator, c->global_scope, n->token, NULL); + Entity *e = make_entity_type_name(c->allocator, c->global_scope, *n, NULL); DeclInfo *d = make_declaration_info(c->allocator, e->scope); d->type_expr = td->type; add_file_entity(c, td->name, e, d); @@ -724,9 +795,8 @@ void check_parsed_files(Checker *c) { case_ast_node(pd, ProcDecl, decl); ast_node(n, Ident, pd->name); - Token token = n->token; + Token token = *n; Entity *e = make_entity_procedure(c->allocator, c->global_scope, token, NULL); - add_entity(c, c->global_scope, pd->name, e); DeclInfo *d = make_declaration_info(c->allocator, e->scope); d->proc_decl = decl; map_set(&c->info.entities, hash_pointer(e), d); @@ -747,14 +817,21 @@ void check_parsed_files(Checker *c) { } } + auto check_global_entity = [](Checker *c, EntityKind kind) { + gb_for_array(i, c->info.entities.entries) { + auto *entry = &c->info.entities.entries[i]; + Entity *e = cast(Entity *)cast(uintptr)entry->key.key; + if (e->kind == kind) { + DeclInfo *d = entry->value; + check_entity_decl(c, e, d, NULL); + } + } + }; - gb_for_array(i, c->info.entities.entries) { - auto *entry = &c->info.entities.entries[i]; - Entity *e = cast(Entity *)cast(uintptr)entry->key.key; - DeclInfo *d = entry->value; - check_entity_decl(c, e, d, NULL); - } - + check_global_entity(c, Entity_TypeName); + check_global_entity(c, Entity_Constant); + check_global_entity(c, Entity_Procedure); + check_global_entity(c, Entity_Variable); // Check procedure bodies gb_for_array(i, c->procs) { diff --git a/src/checker/entity.cpp b/src/checker/entity.cpp index 480e619a9..dd978e810 100644 --- a/src/checker/entity.cpp +++ b/src/checker/entity.cpp @@ -27,6 +27,7 @@ String const entity_strings[] = { typedef i64 EntityGuid; +typedef struct Type Type; struct Entity { EntityKind kind; diff --git a/src/checker/expr.cpp b/src/checker/expr.cpp index 81a8a3d10..39fa66e3d 100644 --- a/src/checker/expr.cpp +++ b/src/checker/expr.cpp @@ -23,9 +23,9 @@ b32 check_is_assignable_to_using_subtype(Type *dst, Type *src) { b32 src_is_ptr = src != prev_src; // b32 dst_is_ptr = dst != prev_dst; - if (src->kind == Type_Struct) { - for (isize i = 0; i < src->Struct.field_count; i++) { - Entity *f = src->Struct.fields[i]; + if (is_type_struct(src)) { + for (isize i = 0; i < src->Record.field_count; i++) { + Entity *f = src->Record.fields[i]; if (f->kind == Entity_Variable && f->Variable.anonymous) { if (are_types_identical(dst, f->type)) { return true; @@ -152,13 +152,9 @@ void populate_using_entity_map(Checker *c, AstNode *node, Type *t, Map gbString str = expr_to_string(node); defer (gb_string_free(str)); - switch (t->kind) { - // IMPORTANT HACK(bill): The positions of fields and field_count - // must be same for Struct and Union - case Type_Struct: - case Type_Union: - for (isize i = 0; i < t->Struct.field_count; i++) { - Entity *f = t->Struct.fields[i]; + if (t->kind == Type_Record) { + for (isize i = 0; i < t->Record.field_count; i++) { + Entity *f = t->Record.fields[i]; GB_ASSERT(f->kind == Entity_Variable); String name = f->token.string; HashKey key = hash_string(name); @@ -175,7 +171,6 @@ void populate_using_entity_map(Checker *c, AstNode *node, Type *t, Map } } } - break; } } @@ -215,8 +210,7 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list, name = name->next, value = value->next) { GB_ASSERT(name->kind == AstNode_Ident); ExactValue v = {ExactValue_Invalid}; - ast_node(i, Ident, name); - Token name_token = i->token; + Token name_token = name->Ident; Entity *e = make_entity_constant(c->allocator, c->context.scope, name_token, NULL, v); entities[entity_index++] = e; check_const_decl(c, e, vd->type, value); @@ -235,7 +229,7 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list, AstNode *name = vd->name_list; for (isize i = 0; i < entity_count; i++, name = name->next) { Entity *e = entities[i]; - Token name_token = name->Ident.token; + Token name_token = name->Ident; HashKey key = hash_string(name_token.string); if (map_get(&entity_map, key) != NULL) { // TODO(bill): Scope checking already checks the declaration @@ -248,10 +242,9 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list, } } else if (decl->kind == AstNode_TypeDecl) { ast_node(td, TypeDecl, decl); - ast_node(name, Ident, td->name); - Token name_token = name->token; + Token name_token = td->name->Ident; - Entity *e = make_entity_type_name(c->allocator, c->context.scope, name->token, NULL); + Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, NULL); check_type_decl(c, e, td->type, NULL, NULL); add_entity(c, c->context.scope, td->name, e); @@ -285,8 +278,7 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list, } for (AstNode *name = vd->name_list; name != NULL; name = name->next) { - ast_node(i, Ident, name); - Token name_token = i->token; + Token name_token = name->Ident; Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, vd->is_using); HashKey key = hash_string(name_token.string); @@ -304,9 +296,8 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list, if (vd->is_using) { Type *t = get_base_type(type_deref(type)); - if (t->kind != Type_Struct && - t->kind != Type_Union) { - Token name_token = vd->name_list->Ident.token; + if (!is_type_struct(t) && !is_type_raw_union(t)) { + Token name_token = vd->name_list->Ident; error(&c->error_collector, name_token, "`using` on a field `%.*s` must be a structure or union", LIT(name_token.string)); continue; } @@ -322,12 +313,12 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list, void check_struct_type(Checker *c, Type *struct_type, AstNode *node, CycleChecker *cycle_checker) { GB_ASSERT(node->kind == AstNode_StructType); - GB_ASSERT(struct_type->kind == Type_Struct); + GB_ASSERT(is_type_struct(struct_type)); ast_node(st, StructType, node); // TODO(bill): check_struct_type and check_union_type are very similar so why not and try to merge them better - isize field_count = 0; +isize field_count = 0; isize other_field_count = 0; for (AstNode *decl = st->decl_list; decl != NULL; decl = decl->next) { switch (decl->kind) { @@ -350,17 +341,17 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node, CycleChecke check_fields(c, node, st->decl_list, fields, field_count, other_fields, other_field_count, cycle_checker, make_string("struct")); - struct_type->Struct.is_packed = st->is_packed; - struct_type->Struct.fields = fields; - struct_type->Struct.field_count = field_count; - struct_type->Struct.other_fields = other_fields; - struct_type->Struct.other_field_count = other_field_count; + struct_type->Record.struct_is_packed = st->is_packed; + struct_type->Record.fields = fields; + struct_type->Record.field_count = field_count; + struct_type->Record.other_fields = other_fields; + struct_type->Record.other_field_count = other_field_count; } -void check_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker *cycle_checker) { - GB_ASSERT(node->kind == AstNode_UnionType); - GB_ASSERT(union_type->kind == Type_Union); - ast_node(ut, UnionType, node); +void check_raw_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker *cycle_checker) { + GB_ASSERT(node->kind == AstNode_RawUnionType); + GB_ASSERT(is_type_raw_union(union_type)); + ast_node(ut, RawUnionType, node); isize field_count = 0; isize other_field_count = 0; @@ -385,16 +376,16 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker check_fields(c, node, ut->decl_list, fields, field_count, other_fields, other_field_count, cycle_checker, make_string("union")); - union_type->Union.fields = fields; - union_type->Union.field_count = field_count; - union_type->Union.other_fields = other_fields; - union_type->Union.other_field_count = other_field_count; + union_type->Record.fields = fields; + union_type->Record.field_count = field_count; + union_type->Record.other_fields = other_fields; + union_type->Record.other_field_count = other_field_count; } void check_enum_type(Checker *c, Type *enum_type, AstNode *node) { GB_ASSERT(node->kind == AstNode_EnumType); - GB_ASSERT(enum_type->kind == Type_Enum); + GB_ASSERT(is_type_enum(enum_type)); ast_node(et, EnumType, node); Map entity_map = {}; @@ -413,14 +404,14 @@ void check_enum_type(Checker *c, Type *enum_type, AstNode *node) { if (base_type == NULL) { base_type = t_int; } - enum_type->Enum.base = base_type; + enum_type->Record.enum_base = base_type; Entity **fields = gb_alloc_array(c->allocator, Entity *, et->field_count); isize field_index = 0; ExactValue iota = make_exact_value_integer(-1); for (AstNode *field = et->field_list; field != NULL; field = field->next) { ast_node(f, FieldValue, field); - Token name_token = f->field->Ident.token; + Token name_token = f->field->Ident; Operand o = {}; if (f->value != NULL) { @@ -455,8 +446,8 @@ void check_enum_type(Checker *c, Type *enum_type, AstNode *node) { } add_entity_use(&c->info, f->field, e); } - enum_type->Enum.fields = fields; - enum_type->Enum.field_count = et->field_count; + enum_type->Record.other_fields = fields; + enum_type->Record.other_field_count = et->field_count; } Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize field_count) { @@ -474,8 +465,7 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize fiel Type *type = check_type(c, type_expr); for (AstNode *name = f->name_list; name != NULL; name = name->next) { if (name->kind == AstNode_Ident) { - ast_node(i, Ident, name); - Entity *param = make_entity_param(c->allocator, scope, i->token, type, f->is_using); + Entity *param = make_entity_param(c->allocator, scope, name->Ident, type, f->is_using); add_entity(c, scope, name, param); variables[variable_index++] = param; } else { @@ -536,11 +526,10 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Cycl GB_ASSERT(n->kind == AstNode_Ident); o->mode = Addressing_Invalid; o->expr = n; - ast_node(i, Ident, n); - Entity *e = scope_lookup_entity(c, c->context.scope, i->token.string); + Entity *e = scope_lookup_entity(c, c->context.scope, n->Ident.string); if (e == NULL) { - error(&c->error_collector, i->token, - "Undeclared type or identifier `%.*s`", LIT(i->token.string)); + error(&c->error_collector, n->Ident, + "Undeclared type or identifier `%.*s`", LIT(n->Ident.string)); return; } add_entity_use(&c->info, n, e); @@ -563,7 +552,7 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Cycl } if (e->type == NULL) { - GB_PANIC("Compiler error: How did this happen? type: %s; identifier: %.*s\n", type_to_string(e->type), LIT(i->token.string)); + GB_PANIC("Compiler error: How did this happen? type: %s; identifier: %.*s\n", type_to_string(e->type), LIT(n->Ident.string)); return; } @@ -739,17 +728,17 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_c check_open_scope(c, e); check_struct_type(c, type, e, cycle_checker); check_close_scope(c); - type->Struct.node = e; + type->Record.node = e; goto end; case_end; - case_ast_node(st, UnionType, e); - type = make_type_union(c->allocator); + case_ast_node(st, RawUnionType, e); + type = make_type_raw_union(c->allocator); set_base_type(named_type, type); check_open_scope(c, e); - check_union_type(c, type, e, cycle_checker); + check_raw_union_type(c, type, e, cycle_checker); check_close_scope(c); - type->Union.node = e; + type->Record.node = e; goto end; case_end; @@ -757,6 +746,7 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_c type = make_type_enum(c->allocator); set_base_type(named_type, type); check_enum_type(c, type, e); + type->Record.node = e; goto end; case_end; @@ -1249,10 +1239,9 @@ String check_down_cast_name(Type *dst_, Type *src_) { Type *dst = type_deref(dst_); Type *src = type_deref(src_); Type *dst_s = get_base_type(dst); - GB_ASSERT(dst_s->kind == Type_Struct || dst_s->kind == Type_Union); - // HACK(bill): struct/union variable overlay from unsafe tagged union - for (isize i = 0; i < dst_s->Struct.field_count; i++) { - Entity *f = dst_s->Struct.fields[i]; + GB_ASSERT(is_type_struct(dst_s) || is_type_raw_union(dst_s)); + for (isize i = 0; i < dst_s->Record.field_count; i++) { + Entity *f = dst_s->Record.fields[i]; GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field); if (f->Variable.anonymous) { if (are_types_identical(f->type, src_)) { @@ -1397,7 +1386,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) { Type *bsrc = get_base_type(src); Type *bdst = get_base_type(dst); - if (!(bsrc->kind == Type_Struct || bsrc->kind == Type_Union)) { + if (!(is_type_struct(bsrc) || is_type_raw_union(bsrc))) { gbString expr_str = expr_to_string(node); defer (gb_string_free(expr_str)); error(&c->error_collector, ast_node_token(node), "Can only `down_cast` pointer from structs or unions: `%s`", expr_str); @@ -1405,7 +1394,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) { return; } - if (!(bdst->kind == Type_Struct || bdst->kind == Type_Union)) { + if (!(is_type_struct(bdst) || is_type_struct(bdst))) { gbString expr_str = expr_to_string(node); defer (gb_string_free(expr_str)); error(&c->error_collector, ast_node_token(node), "Can only `down_cast` pointer to structs or unions: `%s`", expr_str); @@ -1740,7 +1729,7 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) { AstNode *op_expr = se->expr; AstNode *selector = se->selector; if (selector) { - Entity *entity = lookup_field(operand->type, selector->Ident.token.string, operand->mode == Addressing_Type).entity; + Entity *entity = lookup_field(operand->type, selector->Ident.string, operand->mode == Addressing_Type).entity; if (entity == NULL) { gbString op_str = expr_to_string(op_expr); gbString type_str = type_to_string(operand->type); @@ -1787,7 +1776,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) if (err) { ast_node(proc, Ident, ce->proc); error(&c->error_collector, ce->close, "`%s` arguments for `%.*s`, expected %td, got %td", - err, LIT(proc->token.string), + err, LIT(proc->string), bp->arg_count, ce->arg_list_count); return false; } @@ -1931,7 +1920,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) Type *type = get_base_type(check_type(c, ce->arg_list)); AstNode *field_arg = unparen_expr(ce->arg_list->next); if (type) { - if (type->kind != Type_Struct) { + if (!is_type_struct(type)) { error(&c->error_collector, ast_node_token(ce->arg_list), "Expected a structure type for `offset_of`"); return false; } @@ -1944,11 +1933,11 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) ast_node(arg, Ident, field_arg); - Selection sel = lookup_field(type, arg->token.string, operand->mode == Addressing_Type); + Selection sel = lookup_field(type, arg->string, operand->mode == Addressing_Type); if (sel.entity == NULL) { gbString type_str = type_to_string(type); error(&c->error_collector, ast_node_token(ce->arg_list), - "`%s` has no field named `%.*s`", type_str, LIT(arg->token.string)); + "`%s` has no field named `%.*s`", type_str, LIT(arg->string)); return false; } @@ -1975,17 +1964,18 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) Type *type = operand->type; if (get_base_type(type)->kind == Type_Pointer) { Type *p = get_base_type(type); - if (get_base_type(p)->kind == Type_Struct) + if (is_type_struct(p)) { type = p->Pointer.elem; + } } ast_node(i, Ident, s->selector); - Selection sel = lookup_field(type, i->token.string, operand->mode == Addressing_Type); + Selection sel = lookup_field(type, i->string, operand->mode == Addressing_Type); if (sel.entity == NULL) { gbString type_str = type_to_string(type); error(&c->error_collector, ast_node_token(arg), - "`%s` has no field named `%.*s`", type_str, LIT(i->token.string)); + "`%s` has no field named `%.*s`", type_str, LIT(i->string)); return false; } @@ -2357,7 +2347,6 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) return true; } - void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode *call) { GB_ASSERT(call->kind == AstNode_CallExpr); GB_ASSERT(proc_type->kind == Type_Proc); @@ -2439,8 +2428,9 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) { check_expr_or_type(c, operand, ce->proc); if (operand->mode == Addressing_Invalid) { - for (AstNode *arg = ce->arg_list; arg != NULL; arg = arg->next) + for (AstNode *arg = ce->arg_list; arg != NULL; arg = arg->next) { check_expr_base(c, operand, arg); + } operand->mode = Addressing_Invalid; operand->expr = call; return Expr_Stmt; @@ -2449,8 +2439,9 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) { if (operand->mode == Addressing_Builtin) { i32 id = operand->builtin_id; - if (!check_builtin_procedure(c, operand, call, id)) + if (!check_builtin_procedure(c, operand, call, id)) { operand->mode = Addressing_Invalid; + } operand->expr = call; return builtin_procs[id].kind; } @@ -2470,15 +2461,14 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) { check_call_arguments(c, operand, proc_type, call); - auto *proc = &proc_type->Proc; - if (proc->result_count == 0) { + if (proc_type->Proc.result_count == 0) { operand->mode = Addressing_NoValue; - } else if (proc->result_count == 1) { + } else if (proc_type->Proc.result_count == 1) { operand->mode = Addressing_Value; - operand->type = proc->results->Tuple.variables[0]->type; + operand->type = proc_type->Proc.results->Tuple.variables[0]->type; } else { operand->mode = Addressing_Value; - operand->type = proc->results; + operand->type = proc_type->Proc.results; } operand->expr = call; @@ -2580,12 +2570,14 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint Type *t = get_base_type(type); switch (t->kind) { - case Type_Struct: { + case Type_Record: { + if (!is_type_struct(t)) + break; if (cl->elem_count == 0) break; // NOTE(bill): No need to init { // Checker values AstNode *elem = cl->elem_list; - isize field_count = t->Struct.field_count; + isize field_count = t->Record.field_count; if (elem->kind == AstNode_FieldValue) { b32 *fields_visited = gb_alloc_array(c->allocator, b32, field_count); @@ -2605,9 +2597,9 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint "Invalid field name `%s` in structure literal", expr_str); continue; } - String name = kv->field->Ident.token.string; + String name = kv->field->Ident.string; - Selection sel = lookup_field(type, kv->field->Ident.token.string, o->mode == Addressing_Type); + Selection sel = lookup_field(type, name, o->mode == Addressing_Type); if (sel.entity == NULL) { error(&c->error_collector, ast_node_token(elem), "Unknown field `%.*s` in structure literal", LIT(name)); @@ -2620,7 +2612,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint continue; } - Entity *field = t->Struct.fields[sel.index[0]]; + Entity *field = t->Record.fields[sel.index[0]]; add_entity_use(&c->info, kv->field, field); if (fields_visited[sel.index[0]]) { @@ -2643,7 +2635,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint "Mixture of `field = value` and value elements in a structure literal is not allowed"); continue; } - Entity *field = t->Struct.fields[index]; + Entity *field = t->Record.fields[index]; check_expr(c, o, elem); if (index >= field_count) { @@ -2955,7 +2947,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint case AstNode_ArrayType: case AstNode_VectorType: case AstNode_StructType: - case AstNode_UnionType: + case AstNode_RawUnionType: o->mode = Addressing_Type; o->type = check_type(c, node); break; @@ -3090,7 +3082,7 @@ gbString write_expr_to_string(gbString str, AstNode *node) { break; case_ast_node(i, Ident, node); - str = string_append_token(str, i->token); + str = string_append_token(str, *i); case_end; case_ast_node(bl, BasicLit, node); @@ -3229,8 +3221,8 @@ gbString write_expr_to_string(gbString str, AstNode *node) { str = gb_string_appendc(str, "}"); case_end; - case_ast_node(st, UnionType, node); - str = gb_string_appendc(str, "union{"); + case_ast_node(st, RawUnionType, node); + str = gb_string_appendc(str, "raw_union{"); // str = write_field_list_to_string(str, st->decl_list, ", "); str = gb_string_appendc(str, "}"); case_end; diff --git a/src/checker/stmt.cpp b/src/checker/stmt.cpp index 752fc43c4..ed8b340d8 100644 --- a/src/checker/stmt.cpp +++ b/src/checker/stmt.cpp @@ -1,24 +1,35 @@ // Statements and Declarations -enum StatementFlag : u32 { - Statement_BreakAllowed = GB_BIT(0), - Statement_ContinueAllowed = GB_BIT(1), - // Statement_FallthroughAllowed = GB_BIT(2), // TODO(bill): fallthrough +enum StmtFlag : u32 { + Stmt_BreakAllowed = GB_BIT(0), + Stmt_ContinueAllowed = GB_BIT(1), + Stmt_FallthroughAllowed = GB_BIT(2), // TODO(bill): fallthrough }; + void check_stmt(Checker *c, AstNode *node, u32 flags); -void check_stmt_list(Checker *c, AstNode *node, u32 flags) { - for (; node != NULL; node = node->next) { - if (node->kind != AstNode_EmptyStmt) { - check_stmt(c, node, flags); +void check_stmt_list(Checker *c, AstNode *list, isize list_count, u32 flags) { + b32 ft_ok = (flags & Stmt_FallthroughAllowed) != 0; + u32 f = flags & (~Stmt_FallthroughAllowed); + + isize i = 0; + for (AstNode *n = list; n != NULL; n = n->next, i++) { + if (n->kind == AstNode_EmptyStmt) { + continue; } + u32 new_flags = f; + if (ft_ok && i+1 == list_count) { + new_flags |= Stmt_FallthroughAllowed; + } + check_stmt(c, n, new_flags); } } -b32 check_is_terminating(Checker *c, AstNode *node); +b32 check_is_terminating(AstNode *node); +b32 check_has_break(AstNode *stmt, b32 implicit); -b32 check_is_terminating_list(Checker *c, AstNode *list) { +b32 check_is_terminating_list(AstNode *list) { // Get to end of list for (; list != NULL; list = list->next) { if (list->next == NULL) @@ -28,43 +39,94 @@ b32 check_is_terminating_list(Checker *c, AstNode *list) { // Iterate backwards for (AstNode *n = list; n != NULL; n = n->prev) { if (n->kind != AstNode_EmptyStmt) - return check_is_terminating(c, n); + return check_is_terminating(n); } return false; } +b32 check_has_break_list(AstNode *list, b32 implicit) { + for (AstNode *stmt = list; stmt != NULL; stmt = stmt->next) { + if (check_has_break(stmt, implicit)) { + return true; + } + } + return false; +} + + +b32 check_has_break(AstNode *stmt, b32 implicit) { + switch (stmt->kind) { + case AstNode_BranchStmt: + if (stmt->BranchStmt.token.kind == Token_break) { + return implicit; + } + break; + case AstNode_BlockStmt: + return check_has_break_list(stmt->BlockStmt.list, implicit); + + case AstNode_IfStmt: + if (check_has_break(stmt->IfStmt.body, implicit) || + (stmt->IfStmt.else_stmt != NULL && check_has_break(stmt->IfStmt.else_stmt, implicit))) { + return true; + } + break; + + case AstNode_CaseClause: + return check_has_break_list(stmt->CaseClause.stmts, implicit); + } + + return false; +} + + + // NOTE(bill): The last expression has to be a `return` statement // TODO(bill): This is a mild hack and should be probably handled properly // TODO(bill): Warn/err against code after `return` that it won't be executed -b32 check_is_terminating(Checker *c, AstNode *node) { +b32 check_is_terminating(AstNode *node) { switch (node->kind) { case_ast_node(rs, ReturnStmt, node); return true; case_end; case_ast_node(bs, BlockStmt, node); - return check_is_terminating_list(c, bs->list); + return check_is_terminating_list(bs->list); case_end; case_ast_node(es, ExprStmt, node); - return check_is_terminating(c, es->expr); + return check_is_terminating(es->expr); case_end; case_ast_node(is, IfStmt, node); if (is->else_stmt != NULL) { - if (check_is_terminating(c, is->body) && - check_is_terminating(c, is->else_stmt)) { + if (check_is_terminating(is->body) && + check_is_terminating(is->else_stmt)) { return true; } } case_end; case_ast_node(fs, ForStmt, node); - if (fs->cond == NULL) { + if (fs->cond == NULL && !check_has_break(fs->body, true)) { return true; } case_end; + + case_ast_node(ms, MatchStmt, node); + b32 has_default = false; + for (AstNode *clause = ms->body->BlockStmt.list; clause != NULL; clause = clause->next) { + ast_node(cc, CaseClause, clause); + if (cc->list == NULL) { + has_default = true; + } + if (!check_is_terminating_list(cc->stmts) || + check_has_break_list(cc->stmts, true)) { + return false; + } + } + return has_default; + case_end; } return false; @@ -81,7 +143,7 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) { // NOTE(bill): Ignore assignments to `_` if (node->kind == AstNode_Ident) { ast_node(i, Ident, node); - if (are_strings_equal(i->token.string, make_string("_"))) { + if (are_strings_equal(i->string, make_string("_"))) { add_entity_definition(&c->info, node, NULL); check_assignment(c, op_a, NULL, make_string("assignment to `_` identifier")); if (op_a->mode == Addressing_Invalid) @@ -94,7 +156,7 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) { b32 used = false; if (node->kind == AstNode_Ident) { ast_node(i, Ident, node); - e = scope_lookup_entity(c, c->context.scope, i->token.string); + e = scope_lookup_entity(c, c->context.scope, i->string); if (e != NULL && e->kind == Entity_Variable) { used = e->Variable.used; // TODO(bill): Make backup just in case } @@ -306,10 +368,8 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod continue; String name = e->token.string; Type *t = get_base_type(type_deref(e->type)); - if (t->kind == Type_Struct || t->kind == Type_Union) { - // IMPORTANT HACK(bill): Entity_(Struct|Union) overlap in some memory allowing - // for some variables to accessed to same - Scope **found = map_get(&c->info.scopes, hash_pointer(t->Struct.node)); + if (is_type_struct(t) || is_type_raw_union(t)) { + Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node)); GB_ASSERT(found != NULL); gb_for_array(i, (*found)->elements.entries) { Entity *f = (*found)->elements.entries[i].value; @@ -323,7 +383,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod } } } else { - error(&c->error_collector, e->token, "`using` can only be applied to variables of type struct or union"); + error(&c->error_collector, e->token, "`using` can only be applied to variables of type struct or raw_union"); break; } } @@ -332,9 +392,9 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod push_procedure(c, type); ast_node(bs, BlockStmt, body); // TODO(bill): Check declarations first (except mutable variable declarations) - check_stmt_list(c, bs->list, 0); + check_stmt_list(c, bs->list, bs->list_count, 0); if (type->Proc.result_count > 0) { - if (!check_is_terminating(c, body)) { + if (!check_is_terminating(body)) { error(&c->error_collector, bs->close, "Missing return statement at the end of the procedure"); } } @@ -349,14 +409,15 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d, b32 check_body_later) { Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0); e->type = proc_type; ast_node(pd, ProcDecl, d->proc_decl); - check_open_scope(c, pd->type); - defer ({ - check_close_scope(c); - }); - - + defer (check_close_scope(c)); check_procedure_type(c, proc_type, pd->type); + // add_proc_entity(c, d->scope, pd->name, e); + add_entity(c, d->scope, pd->name, e); + + + + b32 is_foreign = (pd->tags & ProcTag_foreign) != 0; b32 is_inline = (pd->tags & ProcTag_inline) != 0; b32 is_no_inline = (pd->tags & ProcTag_no_inline) != 0; @@ -481,7 +542,7 @@ void check_var_decl(Checker *c, AstNode *node) { for (AstNode *name = vd->name_list; name != NULL; name = name->next) { Entity *entity = NULL; - Token token = name->Ident.token; + Token token = name->Ident; if (name->kind == AstNode_Ident) { String str = token.string; Entity *found = NULL; @@ -497,6 +558,11 @@ void check_var_decl(Checker *c, AstNode *node) { } add_entity_definition(&c->info, name, entity); } else { + TokenPos pos = found->token.pos; + error(&c->error_collector, token, + "Redeclaration of `%.*s` in this scope\n" + "\tat %.*s(%td:%td)", + LIT(str), LIT(pos.file), pos.line, pos.column); entity = found; } } else { @@ -543,9 +609,15 @@ void check_var_decl(Checker *c, AstNode *node) { GB_ASSERT(name->kind == AstNode_Ident); ExactValue v = {ExactValue_Invalid}; ast_node(i, Ident, name); - Entity *e = make_entity_constant(c->allocator, c->context.scope, i->token, NULL, v); - entities[entity_index++] = e; - check_const_decl(c, e, vd->type, value); + String str = i->string; + Entity *found = current_scope_lookup_entity(c->context.scope, str); + if (found == NULL) { + Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v); + entities[entity_index++] = e; + check_const_decl(c, e, vd->type, value); + } else { + entities[entity_index++] = found; + } } isize lhs_count = vd->name_count; @@ -572,6 +644,7 @@ void check_var_decl(Checker *c, AstNode *node) { void check_stmt(Checker *c, AstNode *node, u32 flags) { + u32 mod_flags = flags & (~Stmt_FallthroughAllowed); switch (node->kind) { case_ast_node(_, EmptyStmt, node); case_end; case_ast_node(_, BadStmt, node); case_end; @@ -711,7 +784,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { case_ast_node(bs, BlockStmt, node); check_open_scope(c, node); - check_stmt_list(c, bs->list, flags); + check_stmt_list(c, bs->list, bs->list_count, mod_flags); check_close_scope(c); case_end; @@ -730,13 +803,13 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { "Non-boolean condition in `if` statement"); } - check_stmt(c, is->body, flags); + check_stmt(c, is->body, mod_flags); if (is->else_stmt) { switch (is->else_stmt->kind) { case AstNode_IfStmt: case AstNode_BlockStmt: - check_stmt(c, is->else_stmt, flags); + check_stmt(c, is->else_stmt, mod_flags); break; default: error(&c->error_collector, ast_node_token(is->else_stmt), @@ -772,6 +845,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { case_end; case_ast_node(fs, ForStmt, node); + u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed; check_open_scope(c, node); defer (check_close_scope(c)); @@ -788,9 +862,152 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { } if (fs->post != NULL) check_stmt(c, fs->post, 0); - check_stmt(c, fs->body, flags | Statement_BreakAllowed | Statement_ContinueAllowed); + check_stmt(c, fs->body, new_flags); case_end; + case_ast_node(ms, MatchStmt, node); + Operand x = {}; + + mod_flags |= Stmt_BreakAllowed; + check_open_scope(c, node); + defer (check_close_scope(c)); + + if (ms->init != NULL) { + check_stmt(c, ms->init, 0); + } + if (ms->tag != NULL) { + check_expr(c, &x, ms->tag); + check_assignment(c, &x, NULL, make_string("match expression")); + } else { + x.mode = Addressing_Constant; + x.type = t_bool; + x.value = make_exact_value_bool(true); + + Token token = {}; + token.pos = ast_node_token(ms->body).pos; + token.string = make_string("true"); + x.expr = make_ident(c->curr_ast_file, token); + } + + // NOTE(bill): Check for multiple defaults + AstNode *first_default = NULL; + ast_node(bs, BlockStmt, ms->body); + for (AstNode *stmt = bs->list; stmt != NULL; stmt = stmt->next) { + AstNode *default_stmt = NULL; + if (stmt->kind == AstNode_CaseClause) { + ast_node(c, CaseClause, stmt); + if (c->list_count == 0) { + default_stmt = stmt; + } + } else { + error(&c->error_collector, ast_node_token(stmt), "Invalid AST - expected case clause"); + } + + if (default_stmt != NULL) { + if (first_default != NULL) { + TokenPos pos = ast_node_token(first_default).pos; + error(&c->error_collector, ast_node_token(stmt), + "multiple `default` clauses\n" + "\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column); + } else { + first_default = default_stmt; + } + } + } + + + struct TypeAndToken { + Type *type; + Token token; + }; + + Map seen = {}; // Multimap + map_init(&seen, gb_heap_allocator()); + defer (map_destroy(&seen)); + isize i = 0; + for (AstNode *stmt = bs->list; stmt != NULL; stmt = stmt->next) { + if (stmt->kind != AstNode_CaseClause) { + // NOTE(bill): error handled by above multiple default checker + continue; + } + ast_node(cc, CaseClause, stmt); + + + for (AstNode *expr = cc->list; expr != NULL; expr = expr->next) { + Operand y = {}; + Operand z = {}; + Token eq = {Token_CmpEq}; + + check_expr(c, &y, expr); + if (x.mode == Addressing_Invalid || + y.mode == Addressing_Invalid) { + continue; + } + convert_to_typed(c, &y, x.type); + if (y.mode == Addressing_Invalid) { + continue; + } + + z = y; + check_comparison(c, &z, &x, eq); + if (z.mode == Addressing_Invalid) { + continue; + } + if (y.mode != Addressing_Constant) { + continue; + } + + if (y.value.kind != ExactValue_Invalid) { + HashKey key = hash_exact_value(y.value); + auto *found = map_get(&seen, key); + if (found != NULL) { + isize count = multi_map_count(&seen, key); + TypeAndToken *taps = gb_alloc_array(gb_heap_allocator(), TypeAndToken, count); + defer (gb_free(gb_heap_allocator(), taps)); + + multi_map_get_all(&seen, key, taps); + b32 continue_outer = false; + + for (isize i = 0; i < count; i++) { + TypeAndToken tap = taps[i]; + if (are_types_identical(y.type, tap.type)) { + TokenPos pos = tap.token.pos; + gbString expr_str = expr_to_string(y.expr); + error(&c->error_collector, + ast_node_token(y.expr), + "Duplicate case `%s`\n" + "\tprevious case at %.*s(%td:%td)", + expr_str, + LIT(pos.file), pos.line, pos.column); + gb_string_free(expr_str); + continue_outer = true; + break; + } + } + + if (continue_outer) { + continue; + } + } + TypeAndToken tap = {y.type, ast_node_token(y.expr)}; + multi_map_insert(&seen, key, tap); + } + } + + check_open_scope(c, stmt); + u32 ft_flags = mod_flags; + if (i+1 < bs->list_count) { + ft_flags |= Stmt_FallthroughAllowed; + } + check_stmt_list(c, cc->stmts, cc->stmt_count, ft_flags); + check_close_scope(c); + i++; + } + + + case_end; + + case_ast_node(ds, DeferStmt, node); if (is_ast_node_decl(ds->stmt)) { error(&c->error_collector, ds->token, "You cannot defer a declaration"); @@ -806,12 +1023,16 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { Token token = bs->token; switch (token.kind) { case Token_break: - if ((flags & Statement_BreakAllowed) == 0) - error(&c->error_collector, token, "`break` only allowed in `for` statement"); + if ((flags & Stmt_BreakAllowed) == 0) + error(&c->error_collector, token, "`break` only allowed in `for` or `match` statements"); break; case Token_continue: - if ((flags & Statement_ContinueAllowed) == 0) - error(&c->error_collector, token, "`continue` only allowed in `for` statement"); + if ((flags & Stmt_ContinueAllowed) == 0) + error(&c->error_collector, token, "`continue` only allowed in `for` statements"); + break; + case Token_fallthrough: + if ((flags & Stmt_FallthroughAllowed) == 0) + error(&c->error_collector, token, "`fallthrough` statement in illegal position"); break; default: error(&c->error_collector, token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string)); @@ -828,7 +1049,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { b32 is_selector = false; AstNode *expr = unparen_expr(es->expr); if (expr->kind == AstNode_Ident) { - String name = expr->Ident.token.string; + String name = expr->Ident.string; e = scope_lookup_entity(c, c->context.scope, name); } else if (expr->kind == AstNode_SelectorExpr) { Operand o = {}; @@ -848,9 +1069,9 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { switch (e->kind) { case Entity_TypeName: { Type *t = get_base_type(e->type); - if (t->kind == Type_Enum) { - for (isize i = 0; i < t->Enum.field_count; i++) { - Entity *f = t->Enum.fields[i]; + if (is_type_enum(t)) { + for (isize i = 0; i < t->Record.other_field_count; i++) { + Entity *f = t->Record.other_fields[i]; Entity *found = scope_insert_entity(c->context.scope, f); if (found != NULL) { error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of the constant: %.*s", expr_str, LIT(found->token.string)); @@ -858,8 +1079,8 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { } f->using_parent = e; } - } else if (t->kind == Type_Struct) { - Scope **found = map_get(&c->info.scopes, hash_pointer(t->Struct.node)); + } else if (is_type_struct(t)) { + Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node)); if (found != NULL) { gb_for_array(i, (*found)->elements.entries) { Entity *f = (*found)->elements.entries[i].value; @@ -871,9 +1092,9 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { f->using_parent = e; } } else { - for (isize i = 0; i < t->Struct.other_field_count; i++) { + for (isize i = 0; i < t->Record.other_field_count; i++) { // TODO(bill): using field types too - Entity *f = t->Struct.other_fields[i]; + Entity *f = t->Record.other_fields[i]; Entity *found = scope_insert_entity(c->context.scope, f); if (found != NULL) { error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string)); @@ -897,10 +1118,8 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { case Entity_Variable: case Entity_UsingVariable: { Type *t = get_base_type(type_deref(e->type)); - if (t->kind == Type_Struct || t->kind == Type_Union) { - // IMPORTANT HACK(bill): Entity_(Struct|Union) overlap in some memory allowing - // for some variables to accessed to same - Scope **found = map_get(&c->info.scopes, hash_pointer(t->Struct.node)); + if (is_type_struct(t) || is_type_raw_union(t)) { + Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node)); GB_ASSERT(found != NULL); gb_for_array(i, (*found)->elements.entries) { Entity *f = (*found)->elements.entries[i].value; @@ -917,7 +1136,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { } } } else { - error(&c->error_collector, us->token, "`using` can only be applied to variables of type struct or union"); + error(&c->error_collector, us->token, "`using` can only be applied to variables of type struct or raw_union"); return; } } break; @@ -935,13 +1154,11 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { for (AstNode *item = vd->name_list; item != NULL; item = item->next) { ast_node(i, Ident, item); - String name = i->token.string; + String name = i->string; Entity *e = scope_lookup_entity(c, c->context.scope, name); Type *t = get_base_type(type_deref(e->type)); - if (t->kind == Type_Struct || t->kind == Type_Union) { - // IMPORTANT HACK(bill): Entity_(Struct|Union) overlap in some memory allowing - // for some variables to accessed to same - Scope **found = map_get(&c->info.scopes, hash_pointer(t->Struct.node)); + if (is_type_struct(t) || is_type_raw_union(t)) { + Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node)); GB_ASSERT(found != NULL); gb_for_array(i, (*found)->elements.entries) { Entity *f = (*found)->elements.entries[i].value; @@ -955,7 +1172,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { } } } else { - error(&c->error_collector, us->token, "`using` can only be applied to variables of type struct or union"); + error(&c->error_collector, us->token, "`using` can only be applied to variables of type struct or raw_union"); return; } } @@ -979,8 +1196,8 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { case_ast_node(pd, ProcDecl, node); ast_node(name, Ident, pd->name); - Entity *e = make_entity_procedure(c->allocator, c->context.scope, name->token, NULL); - add_entity(c, c->context.scope, pd->name, e); + Entity *e = make_entity_procedure(c->allocator, c->context.scope, *name, NULL); + // add_proc_entity(c, c->context.scope, pd->name, e); DeclInfo decl = {}; init_declaration_info(&decl, e->scope); @@ -991,7 +1208,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { case_ast_node(td, TypeDecl, node); ast_node(name, Ident, td->name); - Entity *e = make_entity_type_name(c->allocator, c->context.scope, name->token, NULL); + Entity *e = make_entity_type_name(c->allocator, c->context.scope, *name, NULL); add_entity(c, c->context.scope, td->name, e); check_type_decl(c, e, td->type, NULL, NULL); case_end; diff --git a/src/checker/type.cpp b/src/checker/type.cpp index cd11f3365..2ff004636 100644 --- a/src/checker/type.cpp +++ b/src/checker/type.cpp @@ -61,9 +61,7 @@ struct BasicType { TYPE_KIND(Array), \ TYPE_KIND(Vector), \ TYPE_KIND(Slice), \ - TYPE_KIND(Struct), \ - TYPE_KIND(Union), \ - TYPE_KIND(Enum), \ + TYPE_KIND(Record), \ TYPE_KIND(Pointer), \ TYPE_KIND(Named), \ TYPE_KIND(Tuple), \ @@ -87,6 +85,17 @@ enum TypeFlag { TypeFlag_volatile = GB_BIT(1), }; +enum TypeRecordKind { + TypeRecord_Invalid, + + TypeRecord_Struct, + TypeRecord_Enum, + TypeRecord_RawUnion, + TypeRecord_Union, // Tagged + + TypeRecord_Count, +}; + struct Type { u32 flags; TypeKind kind; @@ -104,26 +113,26 @@ struct Type { Type *elem; } Slice; struct { + TypeRecordKind kind; + + // All record types // Theses are arrays Entity **fields; // Entity_Variable isize field_count; // == offset_count - Entity **other_fields; // Entity_Constant or Entity_TypeName - isize other_field_count; AstNode *node; - i64 * offsets; - b32 are_offsets_set; - b32 is_packed; - } Struct; - struct { - // IMPORTANT HACK(bill): The positions of fields, field_count, and node - // must be same for Struct and Union - Entity **fields; // Entity_Variable - isize field_count; - Entity **other_fields; // Entity_Constant or Entity_TypeName + // enum only + Type * enum_base; // Default is `int` + + // struct only + i64 * struct_offsets; + b32 struct_are_offsets_set; + b32 struct_is_packed; + + // Entity_Constant or Entity_TypeName + Entity **other_fields; isize other_field_count; - AstNode *node; - } Union; + } Record; struct { Type *elem; } Pointer; struct { String name; @@ -141,11 +150,6 @@ struct Type { isize param_count; isize result_count; } Proc; - struct { - Type * base; // Default is `int` - Entity **fields; // Entity_Constant - isize field_count; - } Enum; }; }; @@ -200,17 +204,20 @@ Type *make_type_slice(gbAllocator a, Type *elem) { } Type *make_type_struct(gbAllocator a) { - Type *t = alloc_type(a, Type_Struct); + Type *t = alloc_type(a, Type_Record); + t->Record.kind = TypeRecord_Struct; return t; } -Type *make_type_union(gbAllocator a) { - Type *t = alloc_type(a, Type_Union); +Type *make_type_raw_union(gbAllocator a) { + Type *t = alloc_type(a, Type_Record); + t->Record.kind = TypeRecord_RawUnion; return t; } Type *make_type_enum(gbAllocator a) { - Type *t = alloc_type(a, Type_Enum); + Type *t = alloc_type(a, Type_Record); + t->Record.kind = TypeRecord_Enum; return t; } @@ -433,12 +440,21 @@ Type *base_vector_type(Type *t) { } b32 is_type_enum(Type *t) { t = get_base_type(t); - return t->kind == Type_Enum; + return (t->kind == Type_Record && t->Record.kind == TypeRecord_Enum); } +b32 is_type_struct(Type *t) { + t = get_base_type(t); + return (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct); +} +b32 is_type_raw_union(Type *t) { + t = get_base_type(t); + return (t->kind == Type_Record && t->Record.kind == TypeRecord_RawUnion); +} + Type *get_enum_base_type(Type *t) { Type *bt = get_base_type(t); if (is_type_enum(bt)) { - return bt->Enum.base; + return bt->Record.enum_base; } return t; } @@ -452,10 +468,14 @@ b32 is_type_comparable(Type *t) { return true; case Type_Pointer: return true; - case Type_Struct: { - for (isize i = 0; i < t->Struct.field_count; i++) { - if (!is_type_comparable(t->Struct.fields[i]->type)) - return false; + case Type_Record: { + if (is_type_struct(t)) { + for (isize i = 0; i < t->Record.field_count; i++) { + if (!is_type_comparable(t->Record.fields[i]->type)) + return false; + } + } else if (is_type_enum(t)) { + return is_type_comparable(t->Record.enum_base); } return true; } break; @@ -463,8 +483,6 @@ b32 is_type_comparable(Type *t) { return is_type_comparable(t->Array.elem); case Type_Vector: return is_type_comparable(t->Vector.elem); - case Type_Enum: - return is_type_comparable(t->Enum.base); case Type_Proc: return true; } @@ -501,16 +519,25 @@ b32 are_types_identical(Type *x, Type *y) { return are_types_identical(x->Slice.elem, y->Slice.elem); break; - case Type_Struct: - if (y->kind == Type_Struct) { - if (x->Struct.field_count == y->Struct.field_count) { - for (isize i = 0; i < x->Struct.field_count; i++) { - if (!are_types_identical(x->Struct.fields[i]->type, y->Struct.fields[i]->type)) { - return false; - } - } + case Type_Record: + if (y->kind == Type_Record) { + if (x->Record.kind == y->Record.kind) { + switch (x->Record.kind) { + case TypeRecord_Struct: + case TypeRecord_RawUnion: + case TypeRecord_Union: + if (x->Record.field_count == y->Record.field_count) { + for (isize i = 0; i < x->Record.field_count; i++) { + if (!are_types_identical(x->Record.fields[i]->type, y->Record.fields[i]->type)) { + return false; + } + } - return true; + return true; + } + case TypeRecord_Enum: + return are_types_identical(x->Record.enum_base, y->Record.enum_base); + } } } break; @@ -627,65 +654,55 @@ Selection lookup_field(Type *type_, String field_name, b32 is_type, Selection se b32 is_ptr = type != type_; type = get_base_type(type); - switch (type->kind) { - // HACK(bill): struct/union variable overlay from unsafe tagged union - case Type_Struct: - case Type_Union: - if (is_type) { - for (isize i = 0; i < type->Struct.other_field_count; i++) { - Entity *f = type->Struct.other_fields[i]; - GB_ASSERT(f->kind != Entity_Variable); - String str = f->token.string; - if (are_strings_equal(field_name, str)) { - selection_add_index(&sel, i); - sel.entity = f; - return sel; - } - } - } else { - for (isize i = 0; i < type->Struct.field_count; i++) { - Entity *f = type->Struct.fields[i]; - GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field); - String str = f->token.string; - if (are_strings_equal(field_name, str)) { - selection_add_index(&sel, i); - sel.entity = f; - return sel; - } + if (type->kind != Type_Record) { + return sel; + } + if (is_type) { + for (isize i = 0; i < type->Record.other_field_count; i++) { + Entity *f = type->Record.other_fields[i]; + GB_ASSERT(f->kind != Entity_Variable); + String str = f->token.string; - if (f->Variable.anonymous) { - isize prev_count = 0; - if (sel.index != NULL) { - prev_count = gb_array_count(sel.index); - } - selection_add_index(&sel, i); // HACK(bill): Leaky memory - - sel = lookup_field(f->type, field_name, is_type, sel); - - if (sel.entity != NULL) { - if (is_type_pointer(f->type)) - sel.indirect = true; - return sel; - } - gb_array_count(sel.index) = prev_count; - } - } - } - break; - - case Type_Enum: - if (is_type) { - for (isize i = 0; i < type->Enum.field_count; i++) { - Entity *f = type->Enum.fields[i]; - GB_ASSERT(f->kind == Entity_Constant); - String str = f->token.string; - if (are_strings_equal(field_name, str)) { - // Enums are constant expression + if (are_strings_equal(field_name, str)) { + if (is_type_enum(type)) { + GB_ASSERT(f->kind == Entity_Constant); + // NOTE(bill): Enums are constant expression return make_selection(f, NULL, i); + } else { + selection_add_index(&sel, i); + sel.entity = f; + return sel; } } } - break; + } else if (!is_type_enum(type)) { + for (isize i = 0; i < type->Record.field_count; i++) { + Entity *f = type->Record.fields[i]; + GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field); + String str = f->token.string; + if (are_strings_equal(field_name, str)) { + selection_add_index(&sel, i); + sel.entity = f; + return sel; + } + + if (f->Variable.anonymous) { + isize prev_count = 0; + if (sel.index != NULL) { + prev_count = gb_array_count(sel.index); + } + selection_add_index(&sel, i); // HACK(bill): Leaky memory + + sel = lookup_field(f->type, field_name, is_type, sel); + + if (sel.entity != NULL) { + if (is_type_pointer(f->type)) + sel.indirect = true; + return sel; + } + gb_array_count(sel.index) = prev_count; + } + } } return sel; @@ -716,30 +733,32 @@ i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) { return gb_clamp(size, s.max_align, 4*s.max_align); } break; - case Type_Struct: { - if (!t->Struct.is_packed) { + case Type_Record: { + switch (t->Record.kind) { + case TypeRecord_Struct: + if (!t->Record.struct_is_packed) { + i64 max = 1; + for (isize i = 0; i < t->Record.field_count; i++) { + i64 align = type_align_of(s, allocator, t->Record.fields[i]->type); + if (max < align) + max = align; + } + return max; + } + break; + case TypeRecord_RawUnion: { i64 max = 1; - for (isize i = 0; i < t->Struct.field_count; i++) { - i64 align = type_align_of(s, allocator, t->Struct.fields[i]->type); + for (isize i = 0; i < t->Record.field_count; i++) { + i64 align = type_align_of(s, allocator, t->Record.fields[i]->type); if (max < align) max = align; } return max; + } break; + case TypeRecord_Enum: + return type_align_of(s, allocator, t->Record.enum_base); } } break; - - case Type_Union: { - i64 max = 1; - for (isize i = 0; i < t->Union.field_count; i++) { - i64 align = type_align_of(s, allocator, t->Union.fields[i]->type); - if (max < align) - max = align; - } - return max; - } break; - - case Type_Enum: - return type_align_of(s, allocator, t->Enum.base); } return gb_clamp(next_pow2(type_size_of(s, allocator, t)), 1, s.max_align); @@ -767,10 +786,10 @@ i64 *type_set_offsets_of(BaseTypeSizes s, gbAllocator allocator, Entity **fields } b32 type_set_offsets(BaseTypeSizes s, gbAllocator allocator, Type *t) { - GB_ASSERT(t->kind == Type_Struct); - if (!t->Struct.are_offsets_set) { - t->Struct.offsets = type_set_offsets_of(s, allocator, t->Struct.fields, t->Struct.field_count, t->Struct.is_packed); - t->Struct.are_offsets_set = true; + GB_ASSERT(is_type_struct(t)); + if (!t->Record.struct_are_offsets_set) { + t->Record.struct_offsets = type_set_offsets_of(s, allocator, t->Record.fields, t->Record.field_count, t->Record.struct_is_packed); + t->Record.struct_are_offsets_set = true; return true; } return false; @@ -824,27 +843,32 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) { case Type_Slice: // ptr + len + cap return 3 * s.word_size; - case Type_Struct: { - i64 count = t->Struct.field_count; - if (count == 0) - return 0; - type_set_offsets(s, allocator, t); - return t->Struct.offsets[count-1] + type_size_of(s, allocator, t->Struct.fields[count-1]->type); - } break; + case Type_Record: { + switch (t->Record.kind) { + case TypeRecord_Struct: { + i64 count = t->Record.field_count; + if (count == 0) + return 0; + type_set_offsets(s, allocator, t); + return t->Record.struct_offsets[count-1] + type_size_of(s, allocator, t->Record.fields[count-1]->type); + } break; - case Type_Union: { - i64 count = t->Union.field_count; - i64 max = 0; - for (isize i = 0; i < count; i++) { - i64 size = type_size_of(s, allocator, t->Struct.fields[i]->type); - if (max < size) - max = size; + case TypeRecord_RawUnion: { + i64 count = t->Record.field_count; + i64 max = 0; + for (isize i = 0; i < count; i++) { + i64 size = type_size_of(s, allocator, t->Record.fields[i]->type); + if (max < size) + max = size; + } + return max; + } break; + + case TypeRecord_Enum: { + return type_size_of(s, allocator, t->Record.enum_base); + } break; } - return max; } break; - - case Type_Enum: - return type_size_of(s, allocator, t->Enum.base); } // Catch all @@ -852,10 +876,10 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) { } i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, isize index) { - if (t->kind == Type_Struct) { + if (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct) { type_set_offsets(s, allocator, t); - if (gb_is_between(index, 0, t->Struct.field_count-1)) { - return t->Struct.offsets[index]; + if (gb_is_between(index, 0, t->Record.field_count-1)) { + return t->Record.struct_offsets[index]; } } return 0; @@ -868,11 +892,11 @@ i64 type_offset_of_from_selection(BaseTypeSizes s, gbAllocator allocator, Type * for (isize i = 0; i < gb_array_count(sel.index); i++) { isize index = sel.index[i]; t = get_base_type(t); - if (t->kind == Type_Struct) { + if (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct) { type_set_offsets(s, allocator, t); - GB_ASSERT(gb_is_between(index, 0, t->Struct.field_count-1)); - offset += t->Struct.offsets[index]; - t = t->Struct.fields[index]->type; + GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); + offset += t->Record.struct_offsets[index]; + t = t->Record.fields[index]->type; } } return offset; @@ -905,37 +929,41 @@ gbString write_type_to_string(gbString str, Type *type) { str = write_type_to_string(str, type->Array.elem); break; - case Type_Struct: { - str = gb_string_appendc(str, "struct{"); - for (isize i = 0; i < type->Struct.field_count; i++) { - Entity *f = type->Struct.fields[i]; - GB_ASSERT(f->kind == Entity_Variable); - if (i > 0) - str = gb_string_appendc(str, ", "); - str = gb_string_append_length(str, f->token.string.text, f->token.string.len); - str = gb_string_appendc(str, ": "); - str = write_type_to_string(str, f->type); - } - str = gb_string_appendc(str, "}"); - } break; + case Type_Record: { + switch (type->Record.kind) { + case TypeRecord_Struct: + str = gb_string_appendc(str, "struct{"); + for (isize i = 0; i < type->Record.field_count; i++) { + Entity *f = type->Record.fields[i]; + GB_ASSERT(f->kind == Entity_Variable); + if (i > 0) + str = gb_string_appendc(str, ", "); + str = gb_string_append_length(str, f->token.string.text, f->token.string.len); + str = gb_string_appendc(str, ": "); + str = write_type_to_string(str, f->type); + } + str = gb_string_appendc(str, "}"); + break; - case Type_Union: { - str = gb_string_appendc(str, "union{"); - for (isize i = 0; i < type->Union.field_count; i++) { - Entity *f = type->Union.fields[i]; - GB_ASSERT(f->kind == Entity_Variable); - if (i > 0) - str = gb_string_appendc(str, ", "); - str = gb_string_append_length(str, f->token.string.text, f->token.string.len); - str = gb_string_appendc(str, ": "); - str = write_type_to_string(str, f->type); - } - str = gb_string_appendc(str, "}"); - } break; + case TypeRecord_RawUnion: + str = gb_string_appendc(str, "raw_union{"); + for (isize i = 0; i < type->Record.field_count; i++) { + Entity *f = type->Record.fields[i]; + GB_ASSERT(f->kind == Entity_Variable); + if (i > 0) + str = gb_string_appendc(str, ", "); + str = gb_string_append_length(str, f->token.string.text, f->token.string.len); + str = gb_string_appendc(str, ": "); + str = write_type_to_string(str, f->type); + } + str = gb_string_appendc(str, "}"); + break; - case Type_Enum: { - str = gb_string_appendc(str, "enum "); - str = write_type_to_string(str, type->Enum.base); + case TypeRecord_Enum: + str = gb_string_appendc(str, "enum "); + str = write_type_to_string(str, type->Record.enum_base); + break; + } } break; case Type_Pointer: diff --git a/src/codegen/codegen.cpp b/src/codegen/codegen.cpp index 88260be8c..8235fb615 100644 --- a/src/codegen/codegen.cpp +++ b/src/codegen/codegen.cpp @@ -89,11 +89,25 @@ void ssa_gen_code(ssaGen *s) { case Entity_Procedure: { auto *pd = &decl->proc_decl->ProcDecl; - String name = e->token.string; + String original_name = e->token.string; + String name = original_name; AstNode *body = pd->body; if (pd->foreign_name.len > 0) { name = pd->foreign_name; } + + if (are_strings_equal(name, original_name)) { + Scope *scope = *map_get(&info->scopes, hash_pointer(pd->type)); + isize count = multi_map_count(&scope->elements, hash_string(original_name)); + if (count > 1) { + gb_printf("%.*s\n", LIT(name)); + isize name_len = name.len + 1 + 10 + 1; + u8 *name_text = gb_alloc_array(m->allocator, u8, name_len); + name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s$%d", LIT(name), e->guid); + name = make_string(name_text, name_len-1); + } + } + ssaValue *p = ssa_make_value_procedure(a, m, e->type, decl->type_expr, body, name); p->Proc.tags = pd->tags; diff --git a/src/codegen/print_llvm.cpp b/src/codegen/print_llvm.cpp index 689b19ac8..6f63c0108 100644 --- a/src/codegen/print_llvm.cpp +++ b/src/codegen/print_llvm.cpp @@ -135,41 +135,44 @@ void ssa_print_type(gbFile *f, BaseTypeSizes s, Type *t) { ssa_print_type(f, s, t->Slice.elem); ssa_fprintf(f, "*, i%lld, i%lld}", word_bits, word_bits); break; - case Type_Struct: - if (t->Struct.is_packed) { - ssa_fprintf(f, "<"); - } - ssa_fprintf(f, "{"); - for (isize i = 0; i < t->Struct.field_count; i++) { - if (i > 0) { - ssa_fprintf(f, ", "); + case Type_Record: { + switch (t->Record.kind) { + case TypeRecord_Struct: + if (t->Record.struct_is_packed) { + ssa_fprintf(f, "<"); } - Type *ft = t->Struct.fields[i]->type; - Type *bft = get_base_type(ft); - if (bft->kind != Type_Struct) { - ft = bft; + ssa_fprintf(f, "{"); + for (isize i = 0; i < t->Record.field_count; i++) { + if (i > 0) { + ssa_fprintf(f, ", "); + } + Type *ft = t->Record.fields[i]->type; + Type *bft = get_base_type(ft); + if (!is_type_struct(bft)) { + ft = bft; + } + ssa_print_type(f, s, ft); } - ssa_print_type(f, s, ft); + ssa_fprintf(f, "}"); + if (t->Record.struct_is_packed) { + ssa_fprintf(f, ">"); + } + break; + case TypeRecord_RawUnion: + ssa_fprintf(f, "[%lld x i8]", type_size_of(s, gb_heap_allocator(), t)); + break; + case TypeRecord_Enum: + ssa_print_type(f, s, t->Record.enum_base); + break; } - ssa_fprintf(f, "}"); - if (t->Struct.is_packed) { - ssa_fprintf(f, ">"); - } - - break; - case Type_Union: { - i64 size = type_size_of(s, gb_heap_allocator(), t); - ssa_fprintf(f, "[%lld x i8]", size); } break; - case Type_Enum: - ssa_print_type(f, s, t->Enum.base); - break; + case Type_Pointer: ssa_print_type(f, s, t->Pointer.elem); ssa_fprintf(f, "*"); break; case Type_Named: - if (get_base_type(t)->kind == Type_Struct) { + if (is_type_struct(t)) { ssa_print_encoded_local(f, t->Named.name); } else { ssa_print_type(f, s, get_base_type(t)); @@ -271,7 +274,7 @@ void ssa_print_exact_value(gbFile *f, ssaModule *m, ExactValue value, Type *type void ssa_print_block_name(gbFile *f, ssaBlock *b) { ssa_print_escape_string(f, b->label, false); - ssa_fprintf(f, ".-.%d", b->id); + ssa_fprintf(f, "..%d", b->id); } void ssa_print_value(gbFile *f, ssaModule *m, ssaValue *value, Type *type_hint) { @@ -753,7 +756,7 @@ void ssa_print_proc(gbFile *f, ssaModule *m, ssaProcedure *proc) { void ssa_print_type_name(gbFile *f, ssaModule *m, ssaValue *v) { GB_ASSERT(v->kind == ssaValue_TypeName); Type *base_type = get_base_type(ssa_type(v)); - if (base_type->kind != Type_Struct) + if (!is_type_struct(base_type)) return; ssa_print_encoded_local(f, v->TypeName.name); ssa_fprintf(f, " = type "); diff --git a/src/codegen/ssa.cpp b/src/codegen/ssa.cpp index 2f7c13dd8..b2eb6c6a4 100644 --- a/src/codegen/ssa.cpp +++ b/src/codegen/ssa.cpp @@ -25,6 +25,7 @@ struct ssaBlock { isize scope_index; String label; ssaProcedure *parent; + b32 added; gbArray(ssaValue *) instrs; gbArray(ssaValue *) values; @@ -672,7 +673,7 @@ ssaValue *ssa_make_value_block(ssaProcedure *proc, AstNode *node, Scope *scope, b32 ssa_is_blank_ident(AstNode *node) { if (node->kind == AstNode_Ident) { ast_node(i, Ident, node); - return is_blank_ident(i->token.string); + return is_blank_ident(i->string); } return false; } @@ -710,7 +711,7 @@ ssaValue *ssa_emit(ssaProcedure *proc, ssaValue *instr) { GB_ASSERT(instr->kind == ssaValue_Instr); ssaBlock *b = proc->curr_block; instr->Instr.parent = b; - if (b) { + if (b != NULL) { ssaInstr *i = ssa_get_last_instr(b); if (!ssa_is_instr_terminating(i)) { gb_array_append(b->instrs, instr); @@ -742,8 +743,11 @@ ssaValue *ssa_add_local_for_identifier(ssaProcedure *proc, AstNode *name) { } ssaValue *ssa_add_local_generated(ssaProcedure *proc, Type *type) { + Scope *scope = NULL; + if (proc->curr_block) + scope = proc->curr_block->scope; Entity *entity = make_entity_variable(proc->module->allocator, - proc->curr_block->scope, + scope, empty_token, type); return ssa_emit(proc, ssa_make_instr_local(proc, entity)); @@ -1064,15 +1068,15 @@ ssaValue *ssa_emit_deep_field_gep(ssaProcedure *proc, Type *type, ssaValue *e, S type = get_base_type(type); - if (type->kind == Type_Union) { + if (is_type_raw_union(type)) { ssaValue *v = ssa_emit_ptr_offset(proc, e, v_zero); ssa_set_type(v, make_type_pointer(proc->module->allocator, type)); - type = type->Union.fields[index]->type; + type = type->Record.fields[index]->type; e = ssa_emit_conv(proc, v, make_type_pointer(proc->module->allocator, type)); e = ssa_emit_ptr_offset(proc, e, v_zero); ssa_set_type(e, type); } else { - type = type->Union.fields[index]->type; + type = type->Record.fields[index]->type; e = ssa_emit_struct_gep(proc, e, index, type); } } @@ -1095,15 +1099,15 @@ ssaValue *ssa_emit_deep_field_ev(ssaProcedure *proc, Type *type, ssaValue *e, Se type = get_base_type(type); - if (type->kind == Type_Union) { + if (is_type_raw_union(type)) { ssaValue *v = ssa_emit_ptr_offset(proc, e, v_zero); ssa_set_type(v, make_type_pointer(proc->module->allocator, type)); - type = type->Union.fields[index]->type; + type = type->Record.fields[index]->type; e = ssa_emit_conv(proc, v, make_type_pointer(proc->module->allocator, type)); e = ssa_emit_ptr_offset(proc, e, v_zero); ssa_set_type(e, type); } else { - type = type->Union.fields[index]->type; + type = type->Record.fields[index]->type; e = ssa_emit_struct_ev(proc, e, index, type); } } @@ -1296,9 +1300,9 @@ String lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) { b32 src_is_ptr = src != prev_src; // b32 dst_is_ptr = dst != prev_dst; - GB_ASSERT(src->kind == Type_Struct); - for (isize i = 0; i < src->Struct.field_count; i++) { - Entity *f = src->Struct.fields[i]; + GB_ASSERT(is_type_struct(src)); + for (isize i = 0; i < src->Record.field_count; i++) { + Entity *f = src->Record.fields[i]; if (f->kind == Entity_Variable && f->Variable.anonymous) { if (are_types_identical(dst, f->type)) { return f->token.string; @@ -1420,7 +1424,7 @@ ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t, b32 is_arg if (is_argument) { Type *sb = get_base_type(type_deref(src)); b32 src_is_ptr = src != sb; - if (sb->kind == Type_Struct) { + if (is_type_struct(sb)) { String field_name = lookup_polymorphic_field(proc->module->info, t, src); // gb_printf("field_name: %.*s\n", LIT(field_name)); if (field_name.len > 0) { @@ -1729,8 +1733,9 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue return result; } break; - case Type_Struct: { - auto *st = &base_type->Struct; + case Type_Record: { + GB_ASSERT(is_type_struct(base_type)); + auto *st = &base_type->Record; if (cl->elem_list != NULL) { isize index = 0; AstNode *elem = cl->elem_list; @@ -1743,7 +1748,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue if (elem->kind == AstNode_FieldValue) { ast_node(kv, FieldValue, elem); - Selection sel = lookup_field(base_type, kv->field->Ident.token.string, false); + Selection sel = lookup_field(base_type, kv->field->Ident.string, false); field_index = sel.index[0]; field_expr = ssa_build_expr(proc, kv->value); } else { @@ -2198,7 +2203,7 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) { case_ast_node(se, SelectorExpr, expr); Type *type = get_base_type(type_of_expr(proc->module->info, se->expr)); - Selection sel = lookup_field(type, unparen_expr(se->selector)->Ident.token.string, false); + Selection sel = lookup_field(type, unparen_expr(se->selector)->Ident.string, false); GB_ASSERT(sel.entity != NULL); ssaValue *e = ssa_build_addr(proc, se->expr).addr; @@ -2389,8 +2394,8 @@ void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name) { map_set(&m->members, hash_string(name), t); Type *bt = get_base_type(e->type); - if (bt->kind == Type_Struct) { - auto *s = &bt->Struct; + if (is_type_struct(bt)) { + auto *s = &bt->Record; for (isize j = 0; j < s->other_field_count; j++) { Entity *field = s->other_fields[j]; if (field->kind == Entity_TypeName) { @@ -2519,7 +2524,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { if (pd->body != NULL) { // NOTE(bill): Generate a new name // parent$name-guid - String pd_name = pd->name->Ident.token.string; + String pd_name = pd->name->Ident.string; isize name_len = proc->name.len + 1 + pd_name.len + 1 + 10 + 1; u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len); i32 guid = cast(i32)gb_array_count(proc->children); @@ -2527,7 +2532,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { String name = make_string(name_text, name_len-1); Entity **found = map_get(&proc->module->info->definitions, hash_pointer(pd->name)); - GB_ASSERT_MSG(found != NULL, "Unable to find: %.*s", LIT(pd->name->Ident.token.string)); + GB_ASSERT_MSG(found != NULL, "Unable to find: %.*s", LIT(pd->name->Ident.string)); Entity *e = *found; ssaValue *value = ssa_make_value_procedure(proc->module->allocator, proc->module, e->type, pd->type, pd->body, name); @@ -2538,12 +2543,14 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { gb_array_append(proc->children, &value->Proc); ssa_build_proc(value, proc); } else { - String name = pd->name->Ident.token.string; + String original_name = pd->name->Ident.string; + String name = original_name; if (pd->foreign_name.len > 0) { name = pd->foreign_name; } + auto *info = proc->module->info; - Entity **found = map_get(&proc->module->info->definitions, hash_pointer(pd->name)); + Entity **found = map_get(&info->definitions, hash_pointer(pd->name)); GB_ASSERT(found != NULL); Entity *e = *found; ssaValue *value = ssa_make_value_procedure(proc->module->allocator, @@ -2558,7 +2565,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { // NOTE(bill): Generate a new name // parent_proc.name-guid - String td_name = td->name->Ident.token.string; + String td_name = td->name->Ident.string; isize name_len = proc->name.len + 1 + td_name.len + 1 + 10 + 1; u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len); i32 guid = cast(i32)gb_array_count(proc->module->members.entries); @@ -2806,6 +2813,99 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { case_end; + case_ast_node(ms, MatchStmt, node); + if (ms->init != NULL) { + ssa_build_stmt(proc, ms->init); + } + ssaValue *tag = v_true; + if (ms->tag != NULL) { + tag = ssa_build_expr(proc, ms->tag); + } + ssaBlock *done = ssa__make_block(proc, node, make_string("match.done")); // NOTE(bill): Append later + + ast_node(body, BlockStmt, ms->body); + + + AstNode *default_stmts = NULL; + ssaBlock *default_fall = NULL; + ssaBlock *default_block = NULL; + + ssaBlock *fall = NULL; + b32 append_fall = false; + + isize case_count = body->list_count; + isize i = 0; + for (AstNode *clause = body->list; + clause != NULL; + clause = clause->next, i++) { + ssaBlock *body = fall; + b32 append_body = false; + + + ast_node(cc, CaseClause, clause); + + if (body == NULL) { + append_body = true; + if (cc->list == NULL) { + body = ssa__make_block(proc, clause, make_string("match.dflt.body")); + } else { + body = ssa__make_block(proc, clause, make_string("match.case.body")); + } + } + if (append_fall && body == fall) { + append_fall = false; + append_body = true; + } + + fall = done; + if (i+1 < case_count) { + append_fall = true; + fall = ssa__make_block(proc, clause, make_string("match.fall.body")); + } + + if (cc->list == NULL) { + // default case + default_stmts = cc->stmts; + default_fall = fall; + default_block = body; + continue; + } + + ssaBlock *next_cond = NULL; + Token eq = {Token_CmpEq}; + for (AstNode *expr = cc->list; expr != NULL; expr = expr->next) { + next_cond = ssa__make_block(proc, clause, make_string("match.case.next")); + + ssaValue *cond = ssa_emit_comp(proc, eq, tag, ssa_build_expr(proc, expr)); + ssa_emit_if(proc, cond, body, next_cond); + gb_array_append(proc->blocks, next_cond); + proc->curr_block = next_cond; + } + if (append_body) { + gb_array_append(proc->blocks, body); + } + proc->curr_block = body; + ssa_push_target_list(proc, done, NULL, fall); + ssa_build_stmt_list(proc, cc->stmts); + ssa_pop_target_list(proc); + ssa_emit_jump(proc, done); + proc->curr_block = next_cond; + } + + if (default_block != NULL) { + ssa_emit_jump(proc, default_block); + gb_array_append(proc->blocks, default_block); + proc->curr_block = default_block; + ssa_push_target_list(proc, done, NULL, default_fall); + ssa_build_stmt_list(proc, default_stmts); + ssa_pop_target_list(proc); + } + + ssa_emit_jump(proc, done); + gb_array_append(proc->blocks, done); + proc->curr_block = done; + case_end; + case_ast_node(bs, BranchStmt, node); ssaBlock *block = NULL; switch (bs->token.kind) { diff --git a/src/common.cpp b/src/common.cpp index ccf165d35..890c82373 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -110,6 +110,15 @@ template void map_clear (Map *h); template void map_grow (Map *h); template void map_rehash (Map *h, isize new_count); +template typename MapEntry *multi_map_find_first(Map *h, HashKey key); +template typename MapEntry *multi_map_find_next (Map *h, typename MapEntry *e); + +template isize multi_map_count (Map *h, HashKey key); +template void multi_map_get_all (Map *h, HashKey key, T *items); +template void multi_map_insert (Map *h, HashKey key, T value); +template void multi_map_remove (Map *h, HashKey key, typename MapEntry *e); +template void multi_map_remove_all(Map *h, HashKey key); + template @@ -150,6 +159,24 @@ gb_internal MapFindResult map__find(Map *h, HashKey key) { return fr; } +template +gb_internal MapFindResult map__find(Map *h, typename MapEntry *e) { + MapFindResult fr = {-1, -1, -1}; + if (gb_array_count(h->hashes) > 0) { + fr.hash_index = e->key.key % gb_array_count(h->hashes); + fr.entry_index = h->hashes[fr.hash_index]; + while (fr.entry_index >= 0) { + if (&h->entries[fr.entry_index] == e) { + return fr; + } + fr.entry_prev = fr.entry_index; + fr.entry_index = h->entries[fr.entry_index].next; + } + } + return fr; +} + + template gb_internal b32 map__full(Map *h) { return 0.75f * gb_array_count(h->hashes) <= gb_array_count(h->entries); @@ -221,26 +248,33 @@ void map_set(Map *h, HashKey key, T value) { map_grow(h); } + + +template +void map__erase(Map *h, MapFindResult fr) { + if (fr.entry_prev < 0) { + h->hashes[fr.hash_index] = h->entries[fr.entry_index].next; + } else { + h->entries[fr.entry_prev].next = h->entries[fr.entry_index].next; + } + if (fr.entry_index == gb_array_count(h->entries)-1) { + gb_array_pop(h->entries); + return; + } + h->entries[fr.entry_index] = h->entries[gb_array_count(h->entries)-1]; + MapFindResult last = map__find(h, h->entries[fr.entry_index].key); + if (last.entry_prev >= 0) { + h->entries[last.entry_prev].next = fr.entry_index; + } else { + h->hashes[last.hash_index] = fr.entry_index; + } +} + template void map_remove(Map *h, HashKey key) { MapFindResult fr = map__find(h, key); if (fr.entry_index >= 0) { - if (fr.entry_prev < 0) { - h->hashes[fr.hash_index] = h->entries[fr.entry_index].next; - } else { - h->entries[fr.entry_prev].next = h->entries[fr.entry_index].next; - } - if (fr.entry_index == gb_array_count(h->entries)-1) { - gb_array_pop(h->entries); - return; - } - h->entries[fr.entry_index] = h->entries[gb_array_count(h->entries)-1]; - MapFindResult last = map__find(h, h->entries[fr.entry_index].key); - if (last.entry_prev >= 0) { - h->entries[last.entry_prev].next = fr.entry_index; - } else { - h->hashes[last.hash_index] = fr.entry_index; - } + map__erase(h, fr); } } @@ -249,3 +283,82 @@ gb_inline void map_clear(Map *h) { gb_array_clear(h->hashes); gb_array_clear(h->entries); } + + + +template +typename MapEntry *multi_map_find_first(Map *h, HashKey key) { + isize i = map__find(h, key).entry_index; + if (i < 0) { + return NULL; + } + return &h->entries[i]; +} + +template +typename MapEntry *multi_map_find_next(Map *h, typename MapEntry *e) { + isize i = e->next; + while (i >= 0) { + if (hash_key_equal(h->entries[i].key, e->key)) { + return &h->entries[i]; + } + i = h->entries[i].next; + } + return NULL; +} + +template +isize multi_map_count(Map *h, HashKey key) { + isize count = 0; + auto *e = multi_map_find_first(h, key); + while (e != NULL) { + count++; + e = multi_map_find_next(h, e); + } + return count; +} + +template +void multi_map_get_all(Map *h, HashKey key, T *items) { + isize i = 0; + auto *e = multi_map_find_first(h, key); + while (e != NULL) { + items[i++] = e->value; + e = multi_map_find_next(h, e); + } +} + +template +void multi_map_insert(Map *h, HashKey key, T value) { + if (gb_array_count(h->hashes) == 0) { + map_grow(h); + } + MapFindResult fr = map__find(h, key); + isize i = map__add_entry(h, key); + if (fr.entry_prev < 0) { + h->hashes[fr.hash_index] = i; + } else { + h->entries[fr.entry_prev].next = i; + } + h->entries[i].next = fr.entry_index; + h->entries[i].value = value; + if (map__full(h)) { + map_grow(h); + } +} + +template +void multi_map_remove(Map *h, HashKey key, typename MapEntry *e) { + MapFindResult fr = map__find(h, e); + if (fr.entry_index >= 0) { + map__erase(h, fr); + } +} + +template +void multi_map_remove_all(Map *h, HashKey key) { + while (map_get(h, key) != NULL) { + map_remove(h, key); + } +} + diff --git a/src/exact_value.cpp b/src/exact_value.cpp index f428c9834..44e291af0 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -26,6 +26,10 @@ struct ExactValue { }; }; +HashKey hash_exact_value(ExactValue v) { + return hashing_proc(&v, gb_size_of(ExactValue)); +} + ExactValue make_exact_value_bool(b32 b) { ExactValue result = {ExactValue_Bool}; result.value_bool = (b != 0); diff --git a/src/main.cpp b/src/main.cpp index b37ffad48..c1caafc53 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -72,6 +72,8 @@ int main(int argc, char **argv) { defer (destroy_checker(&checker)); check_parsed_files(&checker); + +#if 1 ssaGen ssa = {}; if (!ssa_gen_init(&ssa, &checker)) return 1; @@ -90,8 +92,6 @@ int main(int argc, char **argv) { output_name, cast(int)base_name_len, output_name); if (exit_code != 0) return exit_code; -#if 1 -#endif gbString lib_str = gb_string_make(gb_heap_allocator(), "-lKernel32.lib"); char lib_str_buf[1024] = {}; @@ -119,5 +119,7 @@ int main(int argc, char **argv) { if (run_output) { win32_exec_command_line_app("%.*s.exe", cast(int)base_name_len, output_name); } +#endif + return 0; } diff --git a/src/parser.cpp b/src/parser.cpp index 2deac0b2d..4a5a8d63e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,6 +1,4 @@ struct AstNode; -struct Type; -struct AstScope; enum ParseFileError { ParseFile_None, @@ -30,8 +28,6 @@ struct AstFile { AstNode *decls; isize decl_count; - AstScope *file_scope; - AstScope *curr_scope; AstNode *curr_proc; isize scope_level; @@ -43,19 +39,6 @@ struct AstFile { TokenPos fix_prev_pos; }; -// NOTE(bill): Just used to quickly check if there is double declaration in the same scope -// No type checking actually happens -// TODO(bill): Should this be completely handled in the semantic checker or is it better here? -struct AstEntity { - Token token; - AstScope *parent; - AstNode * decl; -}; - -struct AstScope { - AstScope *parent; - Map entities; // Key: Token.string -}; struct Parser { String init_fullpath; @@ -91,12 +74,9 @@ enum CallExprKind { }; #define AST_NODE_KINDS \ - AST_NODE_KIND(Invalid, "invalid node", struct{}) \ + AST_NODE_KIND(Invalid, "invalid node", struct{}) \ AST_NODE_KIND(BasicLit, "basic literal", Token) \ - AST_NODE_KIND(Ident, "identifier", struct { \ - Token token; \ - AstEntity *entity; \ - }) \ + AST_NODE_KIND(Ident, "identifier", Token) \ AST_NODE_KIND(ProcLit, "procedure literal", struct { \ AstNode *type; \ AstNode *body; \ @@ -170,6 +150,17 @@ AST_NODE_KIND(_ComplexStmtBegin, "", struct{}) \ AstNode *init, *cond, *post; \ AstNode *body; \ }) \ + AST_NODE_KIND(CaseClause, "case clause", struct { \ + Token token; \ + AstNode *list; \ + AstNode *stmts; \ + isize list_count, stmt_count; \ + }) \ + AST_NODE_KIND(MatchStmt, "match statement", struct { \ + Token token; \ + AstNode *init, *tag; \ + AstNode *body; \ + }) \ AST_NODE_KIND(DeferStmt, "defer statement", struct { Token token; AstNode *stmt; }) \ AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; }) \ AST_NODE_KIND(UsingStmt, "using statement", struct { Token token; AstNode *node; }) \ @@ -232,7 +223,7 @@ AST_NODE_KIND(_TypeBegin, "", struct{}) \ isize decl_count; \ b32 is_packed; \ }) \ - AST_NODE_KIND(UnionType, "union type", struct { \ + AST_NODE_KIND(RawUnionType, "raw union type", struct { \ Token token; \ AstNode *decl_list; \ isize decl_count; \ @@ -276,15 +267,6 @@ struct AstNode { - -gb_inline AstScope *make_ast_scope(AstFile *f, AstScope *parent) { - AstScope *scope = gb_alloc_item(gb_arena_allocator(&f->arena), AstScope); - map_init(&scope->entities, gb_heap_allocator()); - scope->parent = parent; - return scope; -} - - gb_inline b32 is_ast_node_expr(AstNode *node) { return gb_is_between(node->kind, AstNode__ExprBegin+1, AstNode__ExprEnd-1); } @@ -307,7 +289,7 @@ Token ast_node_token(AstNode *node) { case AstNode_BasicLit: return node->BasicLit; case AstNode_Ident: - return node->Ident.token; + return node->Ident; case AstNode_ProcLit: return ast_node_token(node->ProcLit.type); case AstNode_CompoundLit: @@ -356,6 +338,10 @@ Token ast_node_token(AstNode *node) { return node->ReturnStmt.token; case AstNode_ForStmt: return node->ForStmt.token; + case AstNode_MatchStmt: + return node->MatchStmt.token; + case AstNode_CaseClause: + return node->CaseClause.token; case AstNode_DeferStmt: return node->DeferStmt.token; case AstNode_BranchStmt: @@ -367,7 +353,7 @@ Token ast_node_token(AstNode *node) { case AstNode_VarDecl: return ast_node_token(node->VarDecl.name_list); case AstNode_ProcDecl: - return node->ProcDecl.name->Ident.token; + return node->ProcDecl.name->Ident; case AstNode_TypeDecl: return node->TypeDecl.token; case AstNode_LoadDecl: @@ -390,66 +376,19 @@ Token ast_node_token(AstNode *node) { return node->VectorType.token; case AstNode_StructType: return node->StructType.token; - case AstNode_UnionType: - return node->UnionType.token; + case AstNode_RawUnionType: + return node->RawUnionType.token; case AstNode_EnumType: return node->EnumType.token; } return empty_token; -;} - -gb_inline void destroy_ast_scope(AstScope *scope) { - // NOTE(bill): No need to free the actual pointer to the AstScope - // as there should be enough room in the arena - map_destroy(&scope->entities); -} - -gb_inline AstScope *open_ast_scope(AstFile *f) { - AstScope *scope = make_ast_scope(f, f->curr_scope); - f->curr_scope = scope; - f->scope_level++; - return f->curr_scope; -} - -gb_inline void close_ast_scope(AstFile *f) { - GB_ASSERT_NOT_NULL(f->curr_scope); - GB_ASSERT(f->scope_level > 0); - { - AstScope *parent = f->curr_scope->parent; - if (f->curr_scope) { - destroy_ast_scope(f->curr_scope); - } - f->curr_scope = parent; - f->scope_level--; - } -} - -AstEntity *make_ast_entity(AstFile *f, Token token, AstNode *decl, AstScope *parent) { - AstEntity *entity = gb_alloc_item(gb_arena_allocator(&f->arena), AstEntity); - entity->token = token; - entity->decl = decl; - entity->parent = parent; - return entity; } HashKey hash_token(Token t) { return hash_string(t.string); } -AstEntity *ast_scope_lookup(AstScope *scope, Token token) { - return map_get(&scope->entities, hash_token(token)); -} - -AstEntity *ast_scope_insert(AstScope *scope, AstEntity entity) { - AstEntity *prev = ast_scope_lookup(scope, entity.token); - if (prev == NULL) { - map_set(&scope->entities, hash_token(entity.token), entity); - } - return prev; -} - - #define ast_file_err(f, token, fmt, ...) ast_file_err_(f, __FUNCTION__, token, fmt, ##__VA_ARGS__) void ast_file_err_(AstFile *file, char *function, Token token, char *fmt, ...) { // NOTE(bill): Duplicate error, skip it @@ -598,10 +537,9 @@ gb_inline AstNode *make_basic_lit(AstFile *f, Token basic_lit) { return result; } -gb_inline AstNode *make_ident(AstFile *f, Token token, AstEntity *entity = NULL) { +gb_inline AstNode *make_ident(AstFile *f, Token token) { AstNode *result = make_node(f, AstNode_Ident); - result->Ident.token = token; - result->Ident.entity = entity; + result->Ident = token; return result; } @@ -698,12 +636,34 @@ gb_inline AstNode *make_return_stmt(AstFile *f, Token token, AstNode *result_lis gb_inline AstNode *make_for_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, AstNode *post, AstNode *body) { AstNode *result = make_node(f, AstNode_ForStmt); result->ForStmt.token = token; - result->ForStmt.init = init; - result->ForStmt.cond = cond; - result->ForStmt.post = post; - result->ForStmt.body = body; + result->ForStmt.init = init; + result->ForStmt.cond = cond; + result->ForStmt.post = post; + result->ForStmt.body = body; return result; } + + +gb_inline AstNode *make_match_stmt(AstFile *f, Token token, AstNode *init, AstNode *tag, AstNode *body) { + AstNode *result = make_node(f, AstNode_MatchStmt); + result->MatchStmt.token = token; + result->MatchStmt.init = init; + result->MatchStmt.tag = tag; + result->MatchStmt.body = body; + return result; +} + +gb_inline AstNode *make_case_clause(AstFile *f, Token token, AstNode *list, isize list_count, AstNode *stmts, isize stmt_count) { + AstNode *result = make_node(f, AstNode_CaseClause); + result->CaseClause.token = token; + result->CaseClause.list = list; + result->CaseClause.list_count = list_count; + result->CaseClause.stmts = stmts; + result->CaseClause.stmt_count = stmt_count; + return result; +} + + gb_inline AstNode *make_defer_stmt(AstFile *f, Token token, AstNode *stmt) { AstNode *result = make_node(f, AstNode_DeferStmt); result->DeferStmt.token = token; @@ -804,11 +764,11 @@ gb_inline AstNode *make_struct_type(AstFile *f, Token token, AstNode *decl_list, return result; } -gb_inline AstNode *make_union_type(AstFile *f, Token token, AstNode *decl_list, isize decl_count) { - AstNode *result = make_node(f, AstNode_UnionType); - result->UnionType.token = token; - result->UnionType.decl_list = decl_list; - result->UnionType.decl_count = decl_count; +gb_inline AstNode *make_raw_union_type(AstFile *f, Token token, AstNode *decl_list, isize decl_count) { + AstNode *result = make_node(f, AstNode_RawUnionType); + result->RawUnionType.token = token; + result->RawUnionType.decl_list = decl_list; + result->RawUnionType.decl_count = decl_count; return result; } @@ -902,30 +862,6 @@ b32 is_blank_ident(String str) { return false; } -gb_internal void add_ast_entity(AstFile *f, AstScope *scope, AstNode *declaration, AstNode *name_list) { - for (AstNode *n = name_list; n != NULL; n = n->next) { - if (n->kind != AstNode_Ident) { - ast_file_err(f, ast_node_token(declaration), "Identifier is already declared or resolved"); - continue; - } - - AstEntity *entity = make_ast_entity(f, n->Ident.token, declaration, scope); - n->Ident.entity = entity; - - AstEntity *insert_entity = ast_scope_insert(scope, *entity); - if (insert_entity != NULL && !is_blank_ident(insert_entity->token.string)) { - ast_file_err(f, entity->token, - "There is already a previous declaration of `%.*s` in the current scope\n" - "at \t%.*s(%td:%td)", - LIT(insert_entity->token.string), - LIT(insert_entity->token.pos.file), - insert_entity->token.pos.line, - insert_entity->token.pos.column); - } - } -} - - void fix_advance_to_next_stmt(AstFile *f) { // TODO(bill): fix_advance_to_next_stmt @@ -994,10 +930,10 @@ b32 expect_semicolon_after_stmt(AstFile *f, AstNode *s) { AstNode *parse_expr(AstFile *f, b32 lhs); -AstNode *parse_proc_type(AstFile *f, AstScope **scope_); +AstNode *parse_proc_type(AstFile *f); AstNode *parse_stmt_list(AstFile *f, isize *list_count_); AstNode *parse_stmt(AstFile *f); -AstNode *parse_body(AstFile *f, AstScope *scope); +AstNode *parse_body(AstFile *f); AstNode *parse_identifier(AstFile *f) { Token token = f->cursor[0]; @@ -1218,9 +1154,8 @@ AstNode *parse_operand(AstFile *f, b32 lhs) { // Parse Procedure Type or Literal case Token_proc: { - AstScope *scope = NULL; AstNode *curr_proc = f->curr_proc; - AstNode *type = parse_proc_type(f, &scope); + AstNode *type = parse_proc_type(f); f->curr_proc = type; defer (f->curr_proc = curr_proc); @@ -1235,13 +1170,10 @@ AstNode *parse_operand(AstFile *f, b32 lhs) { return type; } else { AstNode *body; - AstScope *curr_scope = f->curr_scope; - f->curr_scope = scope; f->expr_level++; - body = parse_body(f, scope); + body = parse_body(f); f->expr_level--; - f->curr_scope = curr_scope; return make_proc_lit(f, type, body, tags); } @@ -1538,7 +1470,7 @@ AstNode *parse_simple_stmt(AstFile *f) { case Token_CmpAndEq: case Token_CmpOrEq: { - if (f->curr_scope == f->file_scope) { + if (f->curr_proc == NULL) { ast_file_err(f, f->cursor[0], "You cannot use a simple statement in the file scope"); return make_bad_stmt(f, f->cursor[0], f->cursor[0]); } @@ -1566,7 +1498,7 @@ AstNode *parse_simple_stmt(AstFile *f) { switch (token.kind) { case Token_Increment: case Token_Decrement: - if (f->curr_scope == f->file_scope) { + if (f->curr_proc == NULL) { ast_file_err(f, f->cursor[0], "You cannot use a simple statement in the file scope"); return make_bad_stmt(f, f->cursor[0], f->cursor[0]); } @@ -1581,15 +1513,11 @@ AstNode *parse_simple_stmt(AstFile *f) { AstNode *parse_block_stmt(AstFile *f) { - if (f->curr_scope == f->file_scope) { + if (f->curr_proc == NULL) { ast_file_err(f, f->cursor[0], "You cannot use a block statement in the file scope"); return make_bad_stmt(f, f->cursor[0], f->cursor[0]); } - AstNode *block_stmt; - - open_ast_scope(f); - block_stmt = parse_body(f, f->curr_scope); - close_ast_scope(f); + AstNode *block_stmt = parse_body(f); return block_stmt; } @@ -1644,7 +1572,7 @@ AstNode *parse_type(AstFile *f) { return type; } -AstNode *parse_field_decl(AstFile *f, AstScope *scope, b32 allow_using) { +AstNode *parse_field_decl(AstFile *f, b32 allow_using) { b32 is_using = false; AstNode *name_list = NULL; isize name_count = 0; @@ -1670,29 +1598,26 @@ AstNode *parse_field_decl(AstFile *f, AstScope *scope, b32 allow_using) { ast_file_err(f, f->cursor[0], "Expected a type for this field declaration"); AstNode *field = make_field(f, name_list, name_count, type, is_using); - add_ast_entity(f, scope, field, name_list); return field; } -Token parse_procedure_signature(AstFile *f, AstScope *scope, +Token parse_procedure_signature(AstFile *f, AstNode **param_list, isize *param_count, AstNode **result_list, isize *result_count); -AstNode *parse_proc_type(AstFile *f, AstScope **scope_) { - AstScope *scope = make_ast_scope(f, f->file_scope); // Procedure's scope +AstNode *parse_proc_type(AstFile *f) { AstNode *params = NULL; AstNode *results = NULL; isize param_count = 0; isize result_count = 0; - Token proc_token = parse_procedure_signature(f, scope, ¶ms, ¶m_count, &results, &result_count); + Token proc_token = parse_procedure_signature(f, ¶ms, ¶m_count, &results, &result_count); - if (scope_) *scope_ = scope; return make_proc_type(f, proc_token, params, param_count, results, result_count); } -AstNode *parse_parameter_list(AstFile *f, AstScope *scope, isize *param_count_, TokenKind separator, b32 allow_using) { +AstNode *parse_parameter_list(AstFile *f, isize *param_count_, TokenKind separator, b32 allow_using) { AstNode *param_list = NULL; AstNode *param_list_curr = NULL; isize param_count = 0; @@ -1701,7 +1626,7 @@ AstNode *parse_parameter_list(AstFile *f, AstScope *scope, isize *param_count_, if (!allow_using && allow_token(f, Token_using)) { ast_file_err(f, f->cursor[-1], "`using` is only allowed within structures (at the moment)"); } - AstNode *field = parse_field_decl(f, scope, allow_using); + AstNode *field = parse_field_decl(f, allow_using); DLIST_APPEND(param_list, param_list_curr, field); param_count += field->Field.name_count; if (f->cursor[0].kind != separator) @@ -1826,12 +1751,6 @@ AstNode *parse_identifier_or_type(AstFile *f) { } } - AstScope *scope = make_ast_scope(f, NULL); // NOTE(bill): The struct needs its own scope with NO parent - AstScope *curr_scope = f->curr_scope; - f->curr_scope = scope; - defer (f->curr_scope = curr_scope); - - Token open = expect_token(f, Token_OpenBrace); isize decl_count = 0; AstNode *decls = parse_struct_params(f, &decl_count); @@ -1840,20 +1759,14 @@ AstNode *parse_identifier_or_type(AstFile *f) { return make_struct_type(f, token, decls, decl_count, is_packed); } break; - case Token_union: { - Token token = expect_token(f, Token_union); - AstScope *scope = make_ast_scope(f, NULL); // NOTE(bill): The struct needs its own scope with NO parent - AstScope *curr_scope = f->curr_scope; - f->curr_scope = scope; - defer (f->curr_scope = curr_scope); - - + case Token_raw_union: { + Token token = expect_token(f, Token_raw_union); Token open = expect_token(f, Token_OpenBrace); isize decl_count = 0; AstNode *decls = parse_struct_params(f, &decl_count); Token close = expect_token(f, Token_CloseBrace); - return make_union_type(f, token, decls, decl_count); + return make_raw_union_type(f, token, decls, decl_count); } case Token_enum: { @@ -1895,7 +1808,7 @@ AstNode *parse_identifier_or_type(AstFile *f) { case Token_proc: { AstNode *curr_proc = f->curr_proc; - AstNode *type = parse_proc_type(f, NULL); + AstNode *type = parse_proc_type(f); f->curr_proc = type; f->curr_proc = curr_proc; return type; @@ -1930,7 +1843,7 @@ AstNode *parse_identifier_or_type(AstFile *f) { } -AstNode *parse_results(AstFile *f, AstScope *scope, isize *result_count) { +AstNode *parse_results(AstFile *f, isize *result_count) { if (allow_token(f, Token_ArrowRight)) { if (f->cursor[0].kind == Token_OpenParen) { expect_token(f, Token_OpenParen); @@ -1959,18 +1872,18 @@ AstNode *parse_results(AstFile *f, AstScope *scope, isize *result_count) { return NULL; } -Token parse_procedure_signature(AstFile *f, AstScope *scope, +Token parse_procedure_signature(AstFile *f, AstNode **param_list, isize *param_count, AstNode **result_list, isize *result_count) { Token proc_token = expect_token(f, Token_proc); expect_token(f, Token_OpenParen); - *param_list = parse_parameter_list(f, scope, param_count, Token_Comma, true); + *param_list = parse_parameter_list(f, param_count, Token_Comma, true); expect_token(f, Token_CloseParen); - *result_list = parse_results(f, scope, result_count); + *result_list = parse_results(f, result_count); return proc_token; } -AstNode *parse_body(AstFile *f, AstScope *scope) { +AstNode *parse_body(AstFile *f) { AstNode *statement_list = NULL; isize statement_list_count = 0; Token open, close; @@ -1989,9 +1902,8 @@ AstNode *parse_proc_decl(AstFile *f, Token proc_token, AstNode *name) { isize param_count = 0; isize result_count = 0; - AstScope *scope = open_ast_scope(f); - - parse_procedure_signature(f, scope, ¶m_list, ¶m_count, &result_list, &result_count); + parse_procedure_signature(f, ¶m_list, ¶m_count, &result_list, &result_count); + AstNode *proc_type = make_proc_type(f, proc_token, param_list, param_count, result_list, result_count); AstNode *body = NULL; u64 tags = 0; @@ -1999,16 +1911,17 @@ AstNode *parse_proc_decl(AstFile *f, Token proc_token, AstNode *name) { parse_proc_tags(f, &tags, &foreign_name); + AstNode *curr_proc = f->curr_proc; + f->curr_proc = proc_type; + defer (f->curr_proc = curr_proc); + if (f->cursor[0].kind == Token_OpenBrace) { if ((tags & ProcTag_foreign) != 0) { ast_file_err(f, f->cursor[0], "A procedure tagged as `#foreign` cannot have a body"); } - body = parse_body(f, scope); + body = parse_body(f); } - close_ast_scope(f); - - AstNode *proc_type = make_proc_type(f, proc_token, param_list, param_count, result_list, result_count); return make_proc_decl(f, name, proc_type, body, tags, foreign_name); } @@ -2036,7 +1949,7 @@ AstNode *parse_decl(AstFile *f, AstNode *name_list, isize name_count) { Token token = expect_token(f, Token_type); if (name_count != 1) { ast_file_err(f, ast_node_token(name_list), "You can only declare one type at a time"); - return make_bad_decl(f, name_list->Ident.token, token); + return make_bad_decl(f, name_list->Ident, token); } if (type != NULL) { @@ -2053,11 +1966,10 @@ AstNode *parse_decl(AstFile *f, AstNode *name_list, isize name_count) { AstNode *name = name_list; if (name_count != 1) { ast_file_err(f, proc_token, "You can only declare one procedure at a time"); - return make_bad_decl(f, name->Ident.token, proc_token); + return make_bad_decl(f, name->Ident, proc_token); } AstNode *proc_decl = parse_proc_decl(f, proc_token, name); - add_ast_entity(f, f->curr_scope, proc_decl, name_list); return proc_decl; } else { @@ -2091,13 +2003,12 @@ AstNode *parse_decl(AstFile *f, AstNode *name_list, isize name_count) { } AstNode *var_decl = make_var_decl(f, declaration_kind, name_list, name_count, type, value_list, value_count); - add_ast_entity(f, f->curr_scope, var_decl, name_list); return var_decl; } AstNode *parse_if_stmt(AstFile *f) { - if (f->curr_scope == f->file_scope) { + if (f->curr_proc == NULL) { ast_file_err(f, f->cursor[0], "You cannot use an if statement in the file scope"); return make_bad_stmt(f, f->cursor[0], f->cursor[0]); } @@ -2108,9 +2019,6 @@ AstNode *parse_if_stmt(AstFile *f) { AstNode *body = NULL; AstNode *else_stmt = NULL; - open_ast_scope(f); - defer (close_ast_scope(f)); - isize prev_level = f->expr_level; f->expr_level = -1; @@ -2154,7 +2062,7 @@ AstNode *parse_if_stmt(AstFile *f) { } AstNode *parse_return_stmt(AstFile *f) { - if (f->curr_scope == f->file_scope) { + if (f->curr_proc == NULL) { ast_file_err(f, f->cursor[0], "You cannot use a return statement in the file scope"); return make_bad_stmt(f, f->cursor[0], f->cursor[0]); } @@ -2175,14 +2083,12 @@ AstNode *parse_return_stmt(AstFile *f) { } AstNode *parse_for_stmt(AstFile *f) { - if (f->curr_scope == f->file_scope) { + if (f->curr_proc == NULL) { ast_file_err(f, f->cursor[0], "You cannot use a for statement in the file scope"); return make_bad_stmt(f, f->cursor[0], f->cursor[0]); } Token token = expect_token(f, Token_for); - open_ast_scope(f); - defer (close_ast_scope(f)); AstNode *init = NULL; AstNode *cond = NULL; @@ -2220,8 +2126,78 @@ AstNode *parse_for_stmt(AstFile *f) { return make_for_stmt(f, token, init, cond, end, body); } +AstNode *parse_case_clause(AstFile *f) { + Token token = f->cursor[0]; + AstNode *list = NULL; + isize list_count = 0; + if (allow_token(f, Token_case)) { + list = parse_rhs_expr_list(f, &list_count); + } else { + expect_token(f, Token_default); + } + expect_token(f, Token_Colon); // TODO(bill): Is this the best syntax? + isize stmt_count = 0; + AstNode *stmts = parse_stmt_list(f, &stmt_count); + + return make_case_clause(f, token, list, list_count, stmts, stmt_count); +} + +AstNode *parse_match_stmt(AstFile *f) { + if (f->curr_proc == NULL) { + ast_file_err(f, f->cursor[0], "You cannot use a match statement in the file scope"); + return make_bad_stmt(f, f->cursor[0], f->cursor[0]); + } + + Token token = expect_token(f, Token_match); + + AstNode *init = NULL; + AstNode *tag = NULL; + if (f->cursor[0].kind != Token_OpenBrace) { + isize prev_level = f->expr_level; + f->expr_level = -1; + if (f->cursor[0].kind != Token_Semicolon) { + tag = parse_simple_stmt(f); + } + if (allow_token(f, Token_Semicolon)) { + init = tag; + tag = NULL; + if (f->cursor[0].kind != Token_OpenBrace) { + tag = parse_simple_stmt(f); + } + } + + f->expr_level = prev_level; + } + + Token open = expect_token(f, Token_OpenBrace); + AstNode *list = NULL; + AstNode *list_curr = NULL; + isize list_count = 0; +#if 1 + while (f->cursor[0].kind == Token_case || + f->cursor[0].kind == Token_default) { + DLIST_APPEND(list, list_curr, parse_case_clause(f)); + list_count++; + } +#else + + while (f->cursor[0].kind == Token_ArrowRight) { + DLIST_APPEND(list, list_curr, parse_case_clause(f)); + list_count++; + } + +#endif + Token close = expect_token(f, Token_CloseBrace); + + AstNode *body = make_block_stmt(f, list, list_count, open, close); + + tag = convert_stmt_to_expr(f, tag, make_string("match expression")); + return make_match_stmt(f, token, init, tag, body); +} + + AstNode *parse_defer_stmt(AstFile *f) { - if (f->curr_scope == f->file_scope) { + if (f->curr_proc == NULL) { ast_file_err(f, f->cursor[0], "You cannot use a defer statement in the file scope"); return make_bad_stmt(f, f->cursor[0], f->cursor[0]); } @@ -2268,16 +2244,16 @@ AstNode *parse_stmt(AstFile *f) { case Token_if: return parse_if_stmt(f); case Token_return: return parse_return_stmt(f); case Token_for: return parse_for_stmt(f); + case Token_match: return parse_match_stmt(f); case Token_defer: return parse_defer_stmt(f); - // case Token_match: return NULL; // TODO(bill): Token_match - // case Token_case: return NULL; // TODO(bill): Token_case case Token_break: case Token_continue: case Token_fallthrough: next_token(f); - expect_token(f, Token_Semicolon); - return make_branch_stmt(f, token); + s = make_branch_stmt(f, token); + expect_semicolon_after_stmt(f, s); + return s; case Token_using: { AstNode *node = NULL; @@ -2318,14 +2294,14 @@ AstNode *parse_stmt(AstFile *f) { if (are_strings_equal(s->TagStmt.name.string, make_string("load"))) { Token file_path = expect_token(f, Token_String); - if (f->curr_scope == f->file_scope) { + if (f->curr_proc == NULL) { return make_load_decl(f, s->TagStmt.token, file_path); } ast_file_err(f, token, "You cannot `load` within a procedure. This must be done at the file scope."); return make_bad_decl(f, token, file_path); } else if (are_strings_equal(s->TagStmt.name.string, make_string("foreign_system_library"))) { Token file_path = expect_token(f, Token_String); - if (f->curr_scope == f->file_scope) { + if (f->curr_proc == NULL) { return make_foreign_system_library(f, s->TagStmt.token, file_path); } ast_file_err(f, token, "You cannot using `foreign_system_library` within a procedure. This must be done at the file scope."); @@ -2337,7 +2313,7 @@ AstNode *parse_stmt(AstFile *f) { ast_file_err(f, token, "#thread_local may only be applied to variable declarations"); return make_bad_decl(f, token, ast_node_token(var_decl)); } - if (f->curr_scope != f->file_scope) { + if (f->curr_proc != NULL) { ast_file_err(f, token, "#thread_local is only allowed at the file scope."); return make_bad_decl(f, token, ast_node_token(var_decl)); } @@ -2371,10 +2347,14 @@ AstNode *parse_stmt_list(AstFile *f, isize *list_count_) { isize list_count = 0; while (f->cursor[0].kind != Token_case && + f->cursor[0].kind != Token_default && f->cursor[0].kind != Token_CloseBrace && f->cursor[0].kind != Token_EOF) { - DLIST_APPEND(list_root, list_curr, parse_stmt(f)); - list_count++; + AstNode *stmt = parse_stmt(f); + if (stmt && stmt->kind != AstNode_EmptyStmt) { + DLIST_APPEND(list_root, list_curr, stmt); + list_count++; + } } if (list_count_) *list_count_ = list_count; @@ -2405,12 +2385,11 @@ ParseFileError init_ast_file(AstFile *f, String fullpath) { f->cursor = &f->tokens[0]; // NOTE(bill): Is this big enough or too small? - isize arena_size = gb_max(gb_size_of(AstNode), gb_size_of(AstScope)); + isize arena_size = gb_size_of(AstNode); arena_size *= 2*gb_array_count(f->tokens); gb_arena_init_from_allocator(&f->arena, gb_heap_allocator(), arena_size); - open_ast_scope(f); - f->file_scope = f->curr_scope; + f->curr_proc = NULL; return ParseFile_None; } @@ -2428,7 +2407,6 @@ ParseFileError init_ast_file(AstFile *f, String fullpath) { } void destroy_ast_file(AstFile *f) { - close_ast_scope(f); gb_arena_free(&f->arena); gb_array_free(f->tokens); gb_free(gb_heap_allocator(), f->tokenizer.fullpath.text); diff --git a/src/printer.cpp b/src/printer.cpp index df93a92db..63127acd4 100644 --- a/src/printer.cpp +++ b/src/printer.cpp @@ -16,7 +16,7 @@ void print_ast(AstNode *node, isize indent) { break; case AstNode_Ident: print_indent(indent); - print_token(node->Ident.token); + print_token(node->Ident); break; case AstNode_ProcLit: print_indent(indent); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 7581771e4..9d9351923 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -88,6 +88,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \ TOKEN_KIND(Token_continue, "continue"), \ TOKEN_KIND(Token_fallthrough, "fallthrough"), \ TOKEN_KIND(Token_case, "case"), \ + TOKEN_KIND(Token_default, "default"), \ TOKEN_KIND(Token_then, "then"), \ TOKEN_KIND(Token_if, "if"), \ TOKEN_KIND(Token_else, "else"), \ @@ -96,6 +97,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \ TOKEN_KIND(Token_return, "return"), \ TOKEN_KIND(Token_struct, "struct"), \ TOKEN_KIND(Token_union, "union"), \ + TOKEN_KIND(Token_raw_union, "raw_union"), \ TOKEN_KIND(Token_enum, "enum"), \ TOKEN_KIND(Token_using, "using"), \ TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \