From d2c64be85ca15117b1745b254b1806ea739aef43 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Wed, 24 Aug 2016 23:25:56 +0100 Subject: [PATCH] `using` on struct/union fields --- examples/demo.odin | 142 ++++++++++++++---- examples/file.odin | 2 +- examples/game.odin | 16 +- examples/runtime.odin | 12 +- examples/win32.odin | 36 ++--- src/checker/entity.cpp | 10 +- src/checker/expr.cpp | 328 +++++++++++++++++++++++++++++------------ src/checker/type.cpp | 2 + src/codegen/ssa.cpp | 60 +++++--- src/common.cpp | 4 +- src/parser.cpp | 38 +++-- 11 files changed, 461 insertions(+), 189 deletions(-) diff --git a/examples/demo.odin b/examples/demo.odin index 550b499fb..491a79900 100644 --- a/examples/demo.odin +++ b/examples/demo.odin @@ -1,26 +1,18 @@ // Demo 001 #load "basic.odin" -#load "math.odin" #load "game.odin" main :: proc() { - // _ = hellope(); - // procedures(); - // variables(); - // constants(); - // types(); - // data_control(); + _ = hellope(); + procedures(); + variables(); + constants(); + types(); + data_control(); + using_fields(); + // run_game(); - - Thing :: type union { - i: i32, - f: f32, - } - t: Thing; - t.f = 123; - print_int_base(t.i as int, 16); - print_nl(); } hellope :: proc() -> int { @@ -43,7 +35,7 @@ hellope :: proc() -> int { apple, banana, carrot: bool; box, carboard: bool = true, false; -hellope_value: int = hellope(); +hellope_value: int = hellope(); // The procedure is ran just before `main` variables :: proc() { i: int; // initialized with zero value @@ -293,16 +285,16 @@ types :: proc() { } BinaryNode :: type struct { - left, right: ^BinaryNode, // same format as procedure argument - data: rawptr, + left, right: ^BinaryNode; // same format as procedure argument + data: rawptr; } AddProc :: type proc(a, b: int) -> int Packed :: type struct #packed { - a: u8, - b: u16, - c: u32, + a: u8; + b: u16; + c: u32; } static_assert(size_of(Packed) == 7); // builtin procedure @@ -383,6 +375,50 @@ types :: proc() { static_assert(Certain.TOMB == 8); } + { // Untagged union + BitHack :: type union { + i: i32; + f: f32; + } + b: BitHack; + b.f = 123; + print_int(b.i as int); print_nl(); + + + + // Manually tagged union + + EntityKind :: type enum { + Invalid, + Constant, + Variable, + TypeName, + Procedure, + Builtin, + Count, + } + + Entity :: type struct { + kind: EntityKind; + guid: u64; + + // Other data + + /*using*/ + data: union { + constant: struct{}; + variable: struct{ + visited, is_field, used, anonymous: bool; + }; + procedure: struct { used: bool }; + buitlin: struct { id: i32 }; + }; + } + + + // NOTE(bill): Tagged unions are not added yet but are planned + } + { // Compound Literals @@ -478,9 +514,9 @@ void main() { { // size, align, offset Thing :: type struct { - a: u8, - b: u16, - c, d, e: u32, + a: u8; + b: u16; + c, d, e: u32; } s := size_of(Thing); @@ -619,3 +655,59 @@ data_control :: proc() { } +using_fields :: proc() { + { // Everyday stuff + Vec3 :: type struct { x, y, z: f32; } + + Entity :: type struct { + name: string; + using pos: Vec3; + vel: Vec3; + } + t: Entity; + t.y = 456; + print_f32(t.y); print_nl(); + print_f32(t.pos.y); print_nl(); + print_f32(t.vel.y); print_nl(); + + + Frog :: type struct { // Subtype (kind of) + using entity: Entity; + colour: u32; + jump_height: f32; + } + + f: Frog; + f.y = 1337; + print_f32(f.y); print_nl(); + print_f32(f.pos.y); print_nl(); + print_f32(f.vel.y); print_nl(); + + + Buffalo :: type struct { + using entity: Entity; + speed: f32; + noise_level: f32; + } + } + + + { // Crazy Shit + Vec2 :: type union { + using _xy: struct {x, y: f32}; + e: [2]f32; + v: {2}f32; + } + + Entity :: type struct { + using pos: ^Vec2; + name: string; + } + t: Entity; + t.pos = alloc(size_of(Vec2)) as ^Vec2; // TODO(bill): make an alloc type? i.e. new(Type)? + t.x = 123; + print_f32(t._xy.x); print_nl(); + print_f32(t.pos.x); print_nl(); + print_f32(t.pos._xy.x); print_nl(); + } +} diff --git a/examples/file.odin b/examples/file.odin index 3e1e76238..f007b380a 100644 --- a/examples/file.odin +++ b/examples/file.odin @@ -3,7 +3,7 @@ FileHandle :: type HANDLE; File :: type struct { - handle: FileHandle, + handle: FileHandle; } file_open :: proc(name: string) -> (File, bool) { diff --git a/examples/game.odin b/examples/game.odin index 31b6eecc0..f6afb92bf 100644 --- a/examples/game.odin +++ b/examples/game.odin @@ -34,12 +34,12 @@ to_c_string :: proc(s: string) -> ^u8 { Window :: type struct { - width, height: int, - wc: WNDCLASSEXA, - dc: HDC, - hwnd: HWND, - opengl_context, rc: HGLRC, - c_title: ^u8, + width, height: int; + wc: WNDCLASSEXA; + dc: HDC; + hwnd: HWND; + opengl_context, rc: HGLRC; + c_title: ^u8; } make_window :: proc(title: string, msg, height: int, window_proc: WNDPROC) -> (Window, bool) { @@ -123,8 +123,8 @@ display_window :: proc(w: ^Window) { Entity :: type struct { - pos: Vec2, - dim: Vec2, + pos: Vec2; + dim: Vec2; } diff --git a/examples/runtime.odin b/examples/runtime.odin index cd5f9ae31..3c2c5daf9 100644 --- a/examples/runtime.odin +++ b/examples/runtime.odin @@ -258,18 +258,18 @@ AllocatorProc :: type proc(allocator_data: rawptr, mode: AllocationMode, old_memory: rawptr, old_size: int, flags: u64) -> rawptr; Allocator :: type struct { - procedure: AllocatorProc, - data: rawptr, + procedure: AllocatorProc; + data: rawptr; } Context :: type struct { - thread_id: i32, + thread_id: i32; - user_index: i32, - user_data: rawptr, + user_index: i32; + user_data: rawptr; - allocator: Allocator, + allocator: Allocator; } #thread_local context: Context; diff --git a/examples/win32.odin b/examples/win32.odin index 5e83f5bb0..ee58367f7 100644 --- a/examples/win32.odin +++ b/examples/win32.odin @@ -40,24 +40,24 @@ INVALID_HANDLE_VALUE :: (-1 as int) as HANDLE; WNDPROC :: type proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT WNDCLASSEXA :: type struct { - size, style: u32, - wnd_proc: WNDPROC, - cls_extra, wnd_extra: i32, - instance: HINSTANCE, - icon: HICON, - cursor: HCURSOR, - background: HBRUSH, - menu_name, class_name: ^u8, - sm: HICON, + size, style: u32; + wnd_proc: WNDPROC; + cls_extra, wnd_extra: i32; + instance: HINSTANCE; + icon: HICON; + cursor: HCURSOR; + background: HBRUSH; + menu_name, class_name: ^u8; + sm: HICON; } MSG :: type struct { - hwnd: HWND, - message: u32, - wparam: WPARAM, - lparam: LPARAM, - time: u32, - pt: POINT, + hwnd: HWND; + message: u32; + wparam: WPARAM; + lparam: LPARAM; + time: u32; + pt: POINT; } @@ -189,7 +189,7 @@ wglCreateContextAttribsARBType :: type proc(hdc: HDC, hshareContext: rawptr, att PIXELFORMATDESCRIPTOR :: type struct { size, version, - flags: u32, + flags: u32; pixel_type, color_bits, @@ -210,11 +210,11 @@ PIXELFORMATDESCRIPTOR :: type struct { stencil_bits, aux_buffers, layer_type, - reserved: byte, + reserved: byte; layer_mask, visible_mask, - damage_mask: u32, + damage_mask: u32; } GetDC :: proc(h: HANDLE) -> HDC #foreign diff --git a/src/checker/entity.cpp b/src/checker/entity.cpp index 272d028d0..b59481319 100644 --- a/src/checker/entity.cpp +++ b/src/checker/entity.cpp @@ -38,9 +38,10 @@ struct Entity { union { struct { ExactValue value; } Constant; struct { - b8 visited; - b8 is_field; - b8 used; + b8 visited; // Cycle detection + b8 is_field; // Is struct field + b8 used; // Variable is used + b8 anonymous; // Variable is an anonymous struct field } Variable; struct { b8 used; } Procedure; struct { BuiltinProcId id; } Builtin; @@ -85,9 +86,10 @@ Entity *make_entity_param(gbAllocator a, Scope *parent, Token token, Type *type) return entity; } -Entity *make_entity_field(gbAllocator a, Scope *parent, Token token, Type *type) { +Entity *make_entity_field(gbAllocator a, Scope *parent, Token token, Type *type, b32 is_anonymous) { Entity *entity = make_entity_variable(a, parent, token, type); entity->Variable.is_field = true; + entity->Variable.anonymous = cast(b8)is_anonymous; return entity; } diff --git a/src/checker/expr.cpp b/src/checker/expr.cpp index 5e67af727..2b560ae8c 100644 --- a/src/checker/expr.cpp +++ b/src/checker/expr.cpp @@ -13,16 +13,91 @@ void check_entity_decl (Checker *c, Entity *e, DeclInfo *decl, T void check_proc_body (Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body); void update_expr_type (Checker *c, AstNode *e, Type *type, b32 final); +void populate_using_entity_map(Checker *c, AstNode *node, Type *t, Map *entity_map) { + t = get_base_type(type_deref(t)); + 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]; + GB_ASSERT(f->kind == Entity_Variable); + String name = f->token.string; + HashKey key = hash_string(name); + Entity **found = map_get(entity_map, key); + if (found != NULL) { + Entity *e = *found; + // TODO(bill): Better type error + error(&c->error_collector, e->token, "`%.*s` is already declared in `%s`", LIT(name), str); + } else { + map_set(entity_map, key, f); + if (f->Variable.anonymous) { + populate_using_entity_map(c, node, f->type, entity_map); + } + } + } + break; + } + +} + +void check_fields(Checker *c, AstNode *node, AstNode *field_list, Entity **fields, isize field_count, String context) { + Map entity_map = {}; + map_init(&entity_map, gb_heap_allocator()); + defer (map_destroy(&entity_map)); + + isize field_index = 0; + for (AstNode *field = field_list; field != NULL; field = field->next) { + ast_node(f, Field, field); + Type *type = check_type(c, f->type); + + if (f->is_using) { + if (f->name_count > 1) { + error(&c->error_collector, ast_node_token(f->name_list), + "Cannot apply `using` to more than one of the same type"); + } + } + + for (AstNode *name = f->name_list; name != NULL; name = name->next) { + ast_node(i, Ident, name); + Token name_token = i->token; + + Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, f->is_using); + HashKey key = hash_string(name_token.string); + if (map_get(&entity_map, key) != NULL) { + // TODO(bill): Scope checking already checks the declaration + error(&c->error_collector, name_token, "`%.*s` is already declared in this %.*s", LIT(name_token.string), LIT(context)); + } else { + map_set(&entity_map, key, e); + fields[field_index++] = e; + } + add_entity_use(&c->info, name, e); + } + + + if (f->is_using) { + Type *t = get_base_type(type_deref(type)); + if (t->kind != Type_Struct && + t->kind != Type_Union) { + Token name_token = f->name_list->Ident.token; + error(&c->error_collector, name_token, "`using` on a field `%.*s` must be a structure or union", LIT(name_token.string)); + continue; + } + + populate_using_entity_map(c, node, type, &entity_map); + } + } +} void check_struct_type(Checker *c, Type *struct_type, AstNode *node) { GB_ASSERT(node->kind == AstNode_StructType); GB_ASSERT(struct_type->kind == Type_Struct); ast_node(st, StructType, node); - Map entity_map = {}; - map_init(&entity_map, gb_heap_allocator()); - defer (map_destroy(&entity_map)); - isize field_count = 0; for (AstNode *field = st->field_list; field != NULL; field = field->next) { for (AstNode *name = field->Field.name_list; name != NULL; name = name->next) { @@ -32,26 +107,7 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) { } Entity **fields = gb_alloc_array(c->allocator, Entity *, st->field_count); - isize field_index = 0; - for (AstNode *field = st->field_list; field != NULL; field = field->next) { - ast_node(f, Field, field); - Type *type = check_type(c, f->type); - for (AstNode *name = f->name_list; name != NULL; name = name->next) { - ast_node(i, Ident, name); - Token name_token = i->token; - // TODO(bill): is the curr_scope correct? - Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type); - HashKey key = hash_string(name_token.string); - if (map_get(&entity_map, key)) { - // TODO(bill): Scope checking already checks the declaration - error(&c->error_collector, name_token, "`%.*s` is already declared in this structure", LIT(name_token.string)); - } else { - map_set(&entity_map, key, e); - fields[field_index++] = e; - } - add_entity_use(&c->info, name, e); - } - } + check_fields(c, node, st->field_list, fields, field_count, make_string("structure")); struct_type->Struct.fields = fields; struct_type->Struct.field_count = field_count; struct_type->Struct.is_packed = st->is_packed; @@ -62,10 +118,6 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node) { GB_ASSERT(union_type->kind == Type_Union); ast_node(ut, UnionType, node); - Map entity_map = {}; - map_init(&entity_map, gb_heap_allocator()); - defer (map_destroy(&entity_map)); - isize field_count = 0; for (AstNode *field = ut->field_list; field != NULL; field = field->next) { for (AstNode *name = field->Field.name_list; name != NULL; name = name->next) { @@ -75,26 +127,7 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node) { } Entity **fields = gb_alloc_array(c->allocator, Entity *, ut->field_count); - isize field_index = 0; - for (AstNode *field = ut->field_list; field != NULL; field = field->next) { - ast_node(f, Field, field); - Type *type = check_type(c, f->type); - for (AstNode *name = f->name_list; name != NULL; name = name->next) { - ast_node(i, Ident, name); - Token name_token = i->token; - // TODO(bill): is the curr_scope correct? - Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type); - HashKey key = hash_string(name_token.string); - if (map_get(&entity_map, key)) { - // TODO(bill): Scope checking already checks the declaration - error(&c->error_collector, name_token, "`%.*s` is already declared in this union", LIT(name_token.string)); - } else { - map_set(&entity_map, key, e); - fields[field_index++] = e; - } - add_entity_use(&c->info, name, e); - } - } + check_fields(c, node, ut->field_list, fields, field_count, make_string("union")); union_type->Union.fields = fields; union_type->Union.field_count = field_count; } @@ -1404,24 +1437,64 @@ b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *valu return true; } -Entity *lookup_field(Type *type, AstNode *field_node, isize *index = NULL) { - GB_ASSERT(type != NULL); - GB_ASSERT(field_node->kind == AstNode_Ident); - type = get_base_type(type); - if (type->kind == Type_Pointer) - type = get_base_type(type->Pointer.elem); +struct Selection { + Entity *entity; + gbArray(isize) index; + b32 indirect; // Set if there was a pointer deref anywhere down the line +}; +Selection empty_selection = {}; + +Selection make_selection(Entity *entity, gbArray(isize) index, b32 indirect) { + Selection s = {entity, index, indirect}; + return s; +} + +void selection_add_index(Selection *s, isize index) { + if (s->index == NULL) { + gb_array_init(s->index, gb_heap_allocator()); + } + gb_array_append(s->index, index); +} + +Selection lookup_field(Type *type_, String field_name, Selection sel = empty_selection) { + GB_ASSERT(type_ != NULL); + + if (are_strings_equal(field_name, make_string("_"))) { + return empty_selection; + } + + Type *type = type_deref(type_); + b32 is_ptr = type != type_; + type = get_base_type(type); - ast_node(i, Ident, field_node); - String field_str = i->token.string; switch (type->kind) { case Type_Struct: 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_str, str)) { - if (index) *index = i; - return f; + 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, sel); + + if (sel.entity != NULL) { + // gb_printf("%.*s, %.*s, %.*s\n", LIT(field_name), LIT(str), LIT(sel.entity->token.string)); + if (is_type_pointer(f->type)) + sel.indirect = true; + return sel; + } + gb_array_count(sel.index) = prev_count; } } break; @@ -1431,7 +1504,66 @@ Entity *lookup_field(Type *type, AstNode *field_node, isize *index = NULL) { Entity *f = type->Union.fields[i]; GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field); String str = f->token.string; - if (are_strings_equal(field_str, str)) { + if (are_strings_equal(field_name, str)) { + selection_add_index(&sel, i); + sel.entity = f; + return sel; + } + } + for (isize i = 0; i < type->Union.field_count; i++) { + Entity *f = type->Union.fields[i]; + GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field); + String str = f->token.string; + if (f->Variable.anonymous) { + selection_add_index(&sel, i); // HACK(bill): Leaky memory + sel = lookup_field(f->type, field_name, sel); + if (sel.entity != NULL && is_type_pointer(f->type)) { + sel.indirect = true; + } + return sel; + } + } + break; + + case Type_Enum: + 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 + return make_selection(f, NULL, i); + } + } + break; + } + + return sel; + +/* + switch (type->kind) { + case Type_Struct: + 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)) { + make_selection() + FoundField ff = {f, i, offset+field_offset}; + return ff; + } + if (f->Variable.anonymous) { + return lookup_field(f->type, field_node, offset+field_offset); + } + } + break; + + case Type_Union: + for (isize i = 0; i < type->Union.field_count; i++) { + Entity *f = type->Union.fields[i]; + GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field); + String str = f->token.string; + if (are_strings_equal(field_name, str)) { if (index) *index = i; return f; } @@ -1443,7 +1575,7 @@ Entity *lookup_field(Type *type, AstNode *field_node, isize *index = NULL) { Entity *f = type->Enum.fields[i]; GB_ASSERT(f->kind == Entity_Constant); String str = f->token.string; - if (are_strings_equal(field_str, str)) { + if (are_strings_equal(field_name, str)) { if (index) *index = i; return f; } @@ -1454,7 +1586,7 @@ Entity *lookup_field(Type *type, AstNode *field_node, isize *index = NULL) { // Or is this only suitable if tuples are first-class? } - return NULL; +*/ } void check_selector(Checker *c, Operand *operand, AstNode *node) { @@ -1467,10 +1599,10 @@ void check_selector(Checker *c, Operand *operand, AstNode *node) { Entity *entity = NULL; if (is_type_enum(operand->type)) { if (operand->mode == Addressing_Type) { - entity = lookup_field(operand->type, selector); + entity = lookup_field(operand->type, selector->Ident.token.string).entity; } } else { - entity = lookup_field(operand->type, selector); + entity = lookup_field(operand->type, selector->Ident.token.string).entity; } if (entity == NULL) { gbString op_str = expr_to_string(op_expr); @@ -1596,10 +1728,10 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) } } - isize index = 0; - Entity *entity = lookup_field(type, field_arg, &index); - if (entity == NULL) { - ast_node(arg, Ident, field_arg); + + ast_node(arg, Ident, field_arg); + Selection sel = lookup_field(type, arg->token.string); + 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)); @@ -1607,7 +1739,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) } operand->mode = Addressing_Constant; - operand->value = make_exact_value_integer(type_offset_of(c->sizes, c->allocator, type, index)); + // IMPORTANT TODO(bill): Fix for anonymous fields + operand->value = make_exact_value_integer(type_offset_of(c->sizes, c->allocator, type, sel.index[0])); operand->type = t_int; } break; @@ -1632,10 +1765,10 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) type = p->Pointer.elem; } - isize index = 0; - Entity *entity = lookup_field(type, s->selector, &index); - if (entity == NULL) { - ast_node(i, Ident, s->selector); + + ast_node(i, Ident, s->selector); + Selection sel = lookup_field(type, i->token.string); + 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)); @@ -1643,7 +1776,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) } operand->mode = Addressing_Constant; - operand->value = make_exact_value_integer(type_offset_of(c->sizes, c->allocator, type, index)); + // IMPORTANT TODO(bill): Fix for anonymous fields + operand->value = make_exact_value_integer(type_offset_of(c->sizes, c->allocator, type, sel.index[0])); operand->type = t_int; } break; @@ -2097,23 +2231,29 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ } String name = kv->field->Ident.token.string; - isize index = 0; - Entity *e = lookup_field(type, kv->field, &index); - if (e == NULL) { + Selection sel = lookup_field(type, kv->field->Ident.token.string); + if (sel.entity == NULL) { error(&c->error_collector, ast_node_token(elem), "Unknown field `%.*s` in structure literal", LIT(name)); continue; } - Entity *field = t->Struct.fields[index]; + + if (gb_array_count(sel.index) > 1) { + error(&c->error_collector, ast_node_token(elem), + "You cannot assign to an anonymous field `%.*s` in a structure literal (at the moment)", LIT(name)); + continue; + } + + Entity *field = t->Struct.fields[sel.index[0]]; add_entity_use(&c->info, kv->field, field); - if (fields_visited[index]) { + if (fields_visited[sel.index[0]]) { error(&c->error_collector, ast_node_token(elem), "Duplicate field `%.*s` in structure literal", LIT(name)); continue; } - fields_visited[index] = true; + fields_visited[sel.index[0]] = true; check_expr(c, o, kv->value); check_assignment(c, o, field->type, make_string("structure literal")); } @@ -2540,17 +2680,7 @@ gbString write_field_list_to_string(gbString str, AstNode *field_list, char *sep if (i > 0) str = gb_string_appendc(str, sep); - isize j = 0; - for (AstNode *name = f->name_list; name != NULL; name = name->next) { - if (j > 0) - str = gb_string_appendc(str, ", "); - str = write_expr_to_string(str, name); - j++; - } - - str = gb_string_appendc(str, ": "); - str = write_expr_to_string(str, f->type); - + str = write_expr_to_string(str, field); i++; } return str; @@ -2674,6 +2804,22 @@ gbString write_expr_to_string(gbString str, AstNode *node) { str = write_expr_to_string(str, vt->elem); case_end; + case_ast_node(f, Field, node); + if (f->is_using) { + str = gb_string_appendc(str, "using "); + } + isize i = 0; + for (AstNode *name = f->name_list; name != NULL; name = name->next) { + if (i > 0) + str = gb_string_appendc(str, ", "); + str = write_expr_to_string(str, name); + i++; + } + + str = gb_string_appendc(str, ": "); + str = write_expr_to_string(str, f->type); + case_end; + case_ast_node(ce, CallExpr, node); str = write_expr_to_string(str, ce->proc); str = gb_string_appendc(str, "("); diff --git a/src/checker/type.cpp b/src/checker/type.cpp index e7cf827d3..fe5aaf5ce 100644 --- a/src/checker/type.cpp +++ b/src/checker/type.cpp @@ -112,6 +112,8 @@ struct Type { b32 is_packed; } Struct; struct { + // IMPORTANT HACK(bill): The positions of fields and field_count + // must be same for Struct and Union Entity **fields; // Entity_Variable isize field_count; } Union; diff --git a/src/codegen/ssa.cpp b/src/codegen/ssa.cpp index c346f39b3..6c5b8f51b 100644 --- a/src/codegen/ssa.cpp +++ b/src/codegen/ssa.cpp @@ -1606,7 +1606,8 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue if (elem->kind == AstNode_FieldValue) { ast_node(kv, FieldValue, elem); - Entity *e = lookup_field(base_type, kv->field, &field_index); + Selection sel = lookup_field(base_type, kv->field->Ident.token.string); + field_index = sel.index[0]; field_expr = ssa_build_expr(proc, kv->value); } else { field_expr = ssa_build_expr(proc, elem); @@ -1874,6 +1875,36 @@ ssaValue *ssa_build_expr(ssaProcedure *proc, AstNode *expr) { return value; } +ssaValue *ssa_emit_deep_field_gep(ssaProcedure *proc, Type *type, ssaValue *e, Selection sel) { + GB_ASSERT(gb_array_count(sel.index) > 0); + + gb_for_array(i, sel.index) { + isize index = sel.index[i]; + if (is_type_pointer(type)) { + type = type_deref(type); + e = ssa_emit_load(proc, e); + e = ssa_emit_ptr_offset(proc, e, v_zero); + ssa_set_type(e, type); + } + type = get_base_type(type); + + + if (type->kind == Type_Union) { + 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; + 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; + e = ssa_emit_struct_gep(proc, e, index, type); + } + } + + return e; +} + ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) { switch (expr->kind) { @@ -1901,31 +1932,12 @@ 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)); - isize field_index = 0; - Entity *entity = lookup_field(type, unparen_expr(se->selector), &field_index); - GB_ASSERT(entity != NULL); + Selection sel = lookup_field(type, unparen_expr(se->selector)->Ident.token.string); + GB_ASSERT(sel.entity != NULL); ssaValue *e = ssa_build_addr(proc, se->expr).addr; - - if (is_type_pointer(type)) { - // NOTE(bill): Allow x^.y and x.y to be the same - e = ssa_emit_load(proc, e); - e = ssa_emit_ptr_offset(proc, e, v_zero); - ssa_set_type(e, type_deref(type)); - } - - if (type->kind == Type_Union) { - ssaValue *v = ssa_emit_ptr_offset(proc, e, v_zero); - ssa_set_type(v, make_type_pointer(proc->module->allocator, type)); - ssaValue *f = ssa_emit_conv(proc, v, make_type_pointer(proc->module->allocator, entity->type)); - f = ssa_emit_ptr_offset(proc, f, v_zero); - ssa_set_type(f, entity->type); - return ssa_make_addr(f, expr); - - } else { - ssaValue *v = ssa_emit_struct_gep(proc, e, field_index, entity->type); - return ssa_make_addr(v, expr); - } + e = ssa_emit_deep_field_gep(proc, type, e, sel); + return ssa_make_addr(e, expr); case_end; case_ast_node(ue, UnaryExpr, expr); diff --git a/src/common.cpp b/src/common.cpp index 8e23090e5..ccf165d35 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -14,8 +14,8 @@ struct HashKey { gb_inline HashKey hashing_proc(void const *data, isize len) { HashKey h = {}; - h.key = gb_murmur64(data, len); - // h.key = gb_fnv64a(data, len); + // h.key = gb_murmur64(data, len); + h.key = gb_fnv64a(data, len); return h; } diff --git a/src/parser.cpp b/src/parser.cpp index 56a90aca9..ca090a9c7 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -197,6 +197,7 @@ AST_NODE_KIND(_TypeBegin, struct{}) \ AstNode *name_list; \ isize name_count; \ AstNode *type; \ + b32 is_using; \ }) \ AST_NODE_KIND(ProcType, struct { \ Token token; \ @@ -725,11 +726,12 @@ gb_inline AstNode *make_var_decl(AstFile *f, DeclKind kind, AstNode *name_list, return result; } -gb_inline AstNode *make_field(AstFile *f, AstNode *name_list, isize name_count, AstNode *type) { +gb_inline AstNode *make_field(AstFile *f, AstNode *name_list, isize name_count, AstNode *type, b32 is_using) { AstNode *result = make_node(f, AstNode_Field); result->Field.name_list = name_list; result->Field.name_count = name_count; result->Field.type = type; + result->Field.is_using = is_using; return result; } @@ -1566,20 +1568,32 @@ AstNode *parse_type(AstFile *f) { return type; } -AstNode *parse_field_decl(AstFile *f, AstScope *scope) { +AstNode *parse_field_decl(AstFile *f, AstScope *scope, b32 allow_using) { + b32 is_using = false; AstNode *name_list = NULL; isize name_count = 0; + if (allow_using) { + if (allow_token(f, Token_using)) { + is_using = true; + } + } + name_list = parse_lhs_expr_list(f, &name_count); if (name_count == 0) ast_file_err(f, f->cursor[0], "Empty field declaration"); + if (name_count > 1 && is_using) { + ast_file_err(f, f->cursor[0], "Cannot apply `using` to more than one of the same type"); + } + + expect_token(f, Token_Colon); AstNode *type = parse_type_attempt(f); if (type == NULL) ast_file_err(f, f->cursor[0], "Expected a type for this field declaration"); - AstNode *field = make_field(f, name_list, name_count, type); + AstNode *field = make_field(f, name_list, name_count, type, is_using); add_ast_entity(f, scope, field, name_list); return field; } @@ -1602,15 +1616,19 @@ AstNode *parse_proc_type(AstFile *f, AstScope **scope_) { } -AstNode *parse_parameter_list(AstFile *f, AstScope *scope, isize *param_count_) { +AstNode *parse_parameter_list(AstFile *f, AstScope *scope, isize *param_count_, TokenKind separator, b32 allow_using) { AstNode *param_list = NULL; AstNode *param_list_curr = NULL; isize param_count = 0; - while (f->cursor[0].kind == Token_Identifier) { - AstNode *field = parse_field_decl(f, scope); + while (f->cursor[0].kind == Token_Identifier || + (allow_using && f->cursor[0].kind == Token_using)) { + 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); DLIST_APPEND(param_list, param_list_curr, field); param_count += field->Field.name_count; - if (f->cursor[0].kind != Token_Comma) + if (f->cursor[0].kind != separator) break; next_token(f); } @@ -1669,7 +1687,7 @@ AstNode *parse_identifier_or_type(AstFile *f) { } open = expect_token(f, Token_OpenBrace); - params = parse_parameter_list(f, scope, ¶m_count); + params = parse_parameter_list(f, scope, ¶m_count, Token_Semicolon, true); close = expect_token(f, Token_CloseBrace); return make_struct_type(f, token, params, param_count, is_packed); @@ -1683,7 +1701,7 @@ AstNode *parse_identifier_or_type(AstFile *f) { AstScope *scope = make_ast_scope(f, NULL); // NOTE(bill): The union needs its own scope with NO parent open = expect_token(f, Token_OpenBrace); - params = parse_parameter_list(f, scope, ¶m_count); + params = parse_parameter_list(f, scope, ¶m_count, Token_Semicolon, true); close = expect_token(f, Token_CloseBrace); return make_union_type(f, token, params, param_count); @@ -1792,7 +1810,7 @@ Token parse_procedure_signature(AstFile *f, AstScope *scope, 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); + *param_list = parse_parameter_list(f, scope, param_count, Token_Comma, false); expect_token(f, Token_CloseParen); *result_list = parse_results(f, scope, result_count); return proc_token;