From 047c0e4bcc415a1f0e7b55afd57900319e43dbef Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Tue, 21 Feb 2017 21:21:54 +0000 Subject: [PATCH] A decent union type with common fields and variants --- code/demo.odin | 100 ++++++++++++++- core/_preload.odin | 99 ++++----------- core/fmt.odin | 50 +++++++- src/check_expr.c | 304 +++++++++++++++++++++++---------------------- src/check_stmt.c | 11 +- src/checker.c | 44 ++++--- src/ir.c | 298 ++++++++++++++++++++++++++++++-------------- src/ir_print.c | 2 +- src/parser.c | 51 ++++++-- src/types.c | 108 ++++++++++++---- 10 files changed, 678 insertions(+), 389 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index df740c1c2..c88e5477a 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -7,12 +7,102 @@ #import "os.odin"; // #import "halloc.odin"; +Token_Kind :: enum { +} +Token_Pos :: struct { + file: string, + line: int, + column: int, +} +Token :: struct { + kind: Token_Kind, + name: string, + using pos: Token_Pos, +} + +Exact_Value :: union { + Boolean {b: bool}, + String {s: string}, + Integer {i: i64}, + Float {f: f64}, + Pointer {p: i64}, + Compound{c: rawptr}, +} +Overload_Kind :: enum { + UNKNOWN, + NO, + YES, +} +Scope :: struct { + parent: ^Scope, + prev, next: ^Scope, + first_child, last_child: ^Scope, + elements: map[string]^Entity, + implicit: map[^Entity]bool, + + shared: [dynamic]^Scope, + imported: [dynamic]^Scope, + is_proc: bool, + is_global: bool, + is_file: bool, + is_init: bool, + has_been_imported: bool, // This is only applicable to file scopes + file: rawptr, +} + +Type :: struct { +} + +Entity :: union { +// Common Fields + flags: u32, + using token: Token, + scope: ^Scope, // Parent's scope + type: ^Type, + // identifier: ^ast.Node, + + using_parent: ^Entity, + // using_expr: ^ast.Node, + +// Variants + Constant{value: Exact_Value}, + Variable{ + field_index, field_src_index: int, + is_immutable, is_thread_local: bool, + }, + Type_Name{}, + Procedure{ + is_foreign: bool, + foreign_name: string, + foreign_library: ^Entity, + link_name: string, + tags: u64, + overload_kind: Overload_Kind, + }, + Builtin{id: int}, + Import_Name{ + import_path: string, + import_name: string, + import_scope: ^Scope, + used: bool, + }, + Library_Name{ + library_path: string, + library_name: string, + used: bool, + }, + Nil{}, +} + main :: proc() { - m: map[int]int; - m[123] = 312; - fmt.println(m[123]); - delete(m, 123); - fmt.println(m[123]); + e: Entity; + u := union_cast(^Type_Info.Union)type_info_base(type_info_of_val(e)); + + + fmt.println(type_info_base(type_info(Entity))); + + + // e.flags = 123; /* diff --git a/core/_preload.odin b/core/_preload.odin index c61049290..c1014fa95 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -14,6 +14,18 @@ // IMPORTANT NOTE(bill): Do not change the order of any of this data // The compiler relies upon this _exact_ order +Type_Info_Enum_Value :: raw_union { + f: f64, + i: i64, +} +// NOTE(bill): This must match the compiler's +Calling_Convention :: enum { + ODIN = 0, + C = 1, + STD = 2, + FAST = 3, +} + Type_Info_Record :: struct #ordered { types: []^Type_Info, names: []string, @@ -24,80 +36,7 @@ Type_Info_Record :: struct #ordered { ordered: bool, custom_align: bool, } -Type_Info_Enum_Value :: raw_union { - f: f64, - i: i64, -} -// NOTE(bill): This much the same as the compiler's -Calling_Convention :: enum { - ODIN = 0, - C = 1, - STD = 2, - FAST = 3, -} - -/* -Type_Info :: union { - Named: struct #ordered { - name: string, - base: ^Type_Info, // This will _not_ be a Type_Info.Named - }, - Integer: struct #ordered { - size: int, // in bytes - signed: bool, - }, - Float: struct #ordered { - size: int, // in bytes - }, - String: struct #ordered {}, - Boolean: struct #ordered {}, - Any: struct #ordered {}, - Pointer: struct #ordered { - elem: ^Type_Info, // nil -> rawptr - }, - Procedure: struct #ordered { - params: ^Type_Info, // Type_Info.Tuple - results: ^Type_Info, // Type_Info.Tuple - variadic: bool, - convention: Calling_Convention, - }, - Array: struct #ordered { - elem: ^Type_Info, - elem_size: int, - count: int, - }, - Dynamic_Array: struct #ordered { - elem: ^Type_Info, - elem_size: int, - }, - Slice: struct #ordered { - elem: ^Type_Info, - elem_size: int, - }, - Vector: struct #ordered { - elem: ^Type_Info, - elem_size: int, - count: int, - align: int, - }, - Tuple: Type_Info_Record, // Only really used for procedures - Struct: Type_Info_Record, - Union: Type_Info_Record, - Raw_Union: Type_Info_Record, - Enum: struct #ordered { - base: ^Type_Info, - names: []string, - values: []Type_Info_Enum_Value, - }, - Map: struct #ordered { - key: ^Type_Info, - value: ^Type_Info, - generated_struct: ^Type_Info, - count: int, // == 0 if dynamic - }, -} -*/ Type_Info :: union { Named{name: string, base: ^Type_Info}, Integer{size: int, signed: bool}, @@ -124,8 +63,18 @@ Type_Info :: union { Vector {elem: ^Type_Info, elem_size, count, align: int}, Tuple {using record: Type_Info_Record}, // Only really used for procedures Struct {using record: Type_Info_Record}, - Union {using record: Type_Info_Record}, Raw_Union {using record: Type_Info_Record}, + Union{ + common_fields: struct { + types: []^Type_Info, + names: []string, + offsets: []int, // offsets may not be used in tuples + }, + variant_names: []string, + variant_types: []^Type_Info, + size: int, + align: int, + }, Enum{ base: ^Type_Info, names: []string, @@ -142,7 +91,7 @@ Type_Info :: union { // // NOTE(bill): only the ones that are needed (not all types) // // This will be set by the compiler -// immutable __type_infos: []Type_Info; +__type_infos: []Type_Info; type_info_base :: proc(info: ^Type_Info) -> ^Type_Info { if info == nil { diff --git a/core/fmt.odin b/core/fmt.odin index d340bbe5d..54fbfa7f3 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -215,11 +215,36 @@ buffer_write_type :: proc(buf: ^Buffer, ti: ^Type_Info) { case Union: buffer_write_string(buf, "union {"); - for name, i in info.names { + cf := info.common_fields; + total_count := 0; + for name, i in cf.names { + if i > 0 { + buffer_write_string(buf, ", "); + } buffer_write_string(buf, name); buffer_write_string(buf, ": "); - buffer_write_type(buf, info.types[i]); - buffer_write_byte(buf, ','); + buffer_write_type(buf, cf.types[i]); + total_count += 1; + } + for name, i in info.variant_names { + if i > 0 || total_count > 0 { + buffer_write_string(buf, ", "); + } + buffer_write_string(buf, name); + buffer_write_byte(buf, '{'); + defer buffer_write_byte(buf, '}'); + + variant_type := type_info_base(info.variant_types[i]); + variant := union_cast(^Struct)variant_type; + + for j in cf.names.count.. 0 { + buffer_write_byte(buf, ','); + } + buffer_write_string(buf, variant.names[j]); + buffer_write_string(buf, ": "); + buffer_write_type(buf, variant.types[j]); + } } buffer_write_string(buf, "}"); @@ -687,6 +712,9 @@ fmt_enum :: proc(fi: ^Fmt_Info, v: any, verb: rune) { break; } } + } else if e.values.count == 0 { + buffer_write_string(fi.buf, ""); + ok = true; } else { for it, idx in e.values { if it.f == f { @@ -864,7 +892,21 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) { } case Union: - buffer_write_string(fi.buf, "(union)"); + buffer_write_byte(fi.buf, '{'); + defer buffer_write_byte(fi.buf, '}'); + + cf := info.common_fields; + + for _, i in cf.names { + if i > 0 { + buffer_write_string(fi.buf, ", "); + } + buffer_write_string(fi.buf, cf.names[i]); + buffer_write_string(fi.buf, " = "); + data := cast(^byte)v.data + cf.offsets[i]; + fmt_value(fi, any{cf.types[i], cast(rawptr)data}, 'v'); + } + case Raw_Union: buffer_write_string(fi.buf, "(raw_union)"); diff --git a/src/check_expr.c b/src/check_expr.c index d85c5100a..6adb3e701 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -189,8 +189,8 @@ i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) { #endif if (is_type_union(dst)) { - for (isize i = 0; i < dst->Record.field_count; i++) { - Entity *f = dst->Record.fields[i]; + for (isize i = 0; i < dst->Record.variant_count; i++) { + Entity *f = dst->Record.variants[i]; if (are_types_identical(f->type, s)) { return 1; } @@ -297,7 +297,10 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n void populate_using_entity_map(Checker *c, AstNode *node, Type *t, MapEntity *entity_map) { t = base_type(type_deref(t)); - gbString str = expr_to_string(node); + gbString str = NULL; + if (node != NULL) { + expr_to_string(node); + } if (t->kind == Type_Record) { for (isize i = 0; i < t->Record.field_count; i++) { @@ -309,7 +312,11 @@ void populate_using_entity_map(Checker *c, AstNode *node, Type *t, MapEntity *en if (found != NULL) { Entity *e = *found; // TODO(bill): Better type error - error(e->token, "`%.*s` is already declared in `%s`", LIT(name), str); + if (str != NULL) { + error(e->token, "`%.*s` is already declared in `%s`", LIT(name), str); + } else { + error(e->token, "`%.*s` is already declared`", LIT(name)); + } } else { map_entity_set(entity_map, key, f); add_entity(c, c->context.scope, NULL, f); @@ -324,145 +331,106 @@ void populate_using_entity_map(Checker *c, AstNode *node, Type *t, MapEntity *en } -void check_fields(Checker *c, AstNode *node, AstNodeArray decls, - Entity **fields, isize field_count, - String context) { +// Returns filled field_count +isize check_fields(Checker *c, AstNode *node, AstNodeArray decls, + Entity **fields, isize field_count, + String context) { gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); MapEntity entity_map = {0}; map_entity_init_with_reserve(&entity_map, c->tmp_allocator, 2*field_count); - isize other_field_index = 0; Entity *using_index_expr = NULL; - if (node->kind == AstNode_UnionType) { - isize field_index = 0; - fields[field_index++] = make_entity_type_name(c->allocator, c->context.scope, empty_token, NULL); - for_array(decl_index, decls) { - AstNode *decl = decls.e[decl_index]; - if (decl->kind != AstNode_Field) { + if (node != NULL) { + GB_ASSERT(node->kind != AstNode_UnionType); + } + + isize field_index = 0; + for_array(decl_index, decls) { + AstNode *decl = decls.e[decl_index]; + if (decl->kind != AstNode_Field) { + continue; + } + ast_node(f, Field, decl); + + Type *type = check_type(c, f->type); + + if (f->flags&FieldFlag_using) { + if (f->names.count > 1) { + error_node(f->names.e[0], "Cannot apply `using` to more than one of the same type"); + } + } + + for_array(name_index, f->names) { + AstNode *name = f->names.e[name_index]; + if (!ast_node_expect(name, AstNode_Ident)) { continue; } - ast_node(f, Field, decl); - Type *base_type = check_type_extra(c, f->type, NULL); - - for_array(name_index, f->names) { - AstNode *name = f->names.e[name_index]; - if (!ast_node_expect(name, AstNode_Ident)) { - continue; - } - - Token name_token = name->Ident; - - if (str_eq(name_token.string, str_lit("names"))) { - error(name_token, "`names` is a reserved identifier for unions"); - continue; - } - - Type *type = make_type_named(c->allocator, name_token.string, base_type, NULL); - Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, type); - type->Named.type_name = e; - add_entity(c, c->context.scope, name, e); - - if (str_eq(name_token.string, str_lit("_"))) { - error(name_token, "`_` cannot be used a union subtype"); - continue; - } + Token name_token = name->Ident; + Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, f->flags&FieldFlag_using, cast(i32)field_index); + e->identifier = name; + if (str_eq(name_token.string, str_lit("_"))) { + fields[field_index++] = e; + } else { HashKey key = hash_string(name_token.string); - if (map_entity_get(&entity_map, key) != NULL) { + Entity **found = map_entity_get(&entity_map, key); + if (found != NULL) { + Entity *e = *found; // TODO(bill): Scope checking already checks the declaration - error(name_token, "`%.*s` is already declared in this union", LIT(name_token.string)); + error(name_token, "`%.*s` is already declared in this type", LIT(name_token.string)); + error(e->token, "\tpreviously declared"); } else { map_entity_set(&entity_map, key, e); fields[field_index++] = e; + add_entity(c, c->context.scope, name, e); } add_entity_use(c, name, e); } } - } else { - isize field_index = 0; - for_array(decl_index, decls) { - AstNode *decl = decls.e[decl_index]; - if (decl->kind != AstNode_Field) { - continue; - } - ast_node(f, Field, decl); - - Type *type = check_type_extra(c, f->type, NULL); - - if (f->flags&FieldFlag_using) { - if (f->names.count > 1) { - error_node(f->names.e[0], "Cannot apply `using` to more than one of the same type"); - } - } - - for_array(name_index, f->names) { - AstNode *name = f->names.e[name_index]; - if (!ast_node_expect(name, AstNode_Ident)) { - continue; - } - - Token name_token = name->Ident; - - Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, f->flags&FieldFlag_using, cast(i32)field_index); - e->identifier = name; - if (str_eq(name_token.string, str_lit("_"))) { - fields[field_index++] = e; - } else { - HashKey key = hash_string(name_token.string); - if (map_entity_get(&entity_map, key) != NULL) { - // TODO(bill): Scope checking already checks the declaration - error(name_token, "`%.*s` is already declared in this type", LIT(name_token.string)); - } else { - map_entity_set(&entity_map, key, e); - fields[field_index++] = e; - add_entity(c, c->context.scope, name, e); - } - add_entity_use(c, name, e); - } - } - if (f->flags&FieldFlag_using) { - Type *t = base_type(type_deref(type)); - if (!is_type_struct(t) && !is_type_raw_union(t) && - f->names.count >= 1 && - f->names.e[0]->kind == AstNode_Ident) { - Token name_token = f->names.e[0]->Ident; - if (is_type_indexable(t)) { - bool ok = true; - for_array(emi, entity_map.entries) { - Entity *e = entity_map.entries.e[emi].value; - if (e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous) { - if (is_type_indexable(e->type)) { - if (e->identifier != f->names.e[0]) { - ok = false; - using_index_expr = e; - break; - } + if (f->flags&FieldFlag_using) { + Type *t = base_type(type_deref(type)); + if (!is_type_struct(t) && !is_type_raw_union(t) && + f->names.count >= 1 && + f->names.e[0]->kind == AstNode_Ident) { + Token name_token = f->names.e[0]->Ident; + if (is_type_indexable(t)) { + bool ok = true; + for_array(emi, entity_map.entries) { + Entity *e = entity_map.entries.e[emi].value; + if (e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous) { + if (is_type_indexable(e->type)) { + if (e->identifier != f->names.e[0]) { + ok = false; + using_index_expr = e; + break; } } } - if (ok) { - using_index_expr = fields[field_index-1]; - } else { - fields[field_index-1]->flags &= ~EntityFlag_Anonymous; - error(name_token, "Previous `using` for an index expression `%.*s`", LIT(name_token.string)); - } - } else { - error(name_token, "`using` on a field `%.*s` must be a `struct` or `raw_union`", LIT(name_token.string)); - continue; } + if (ok) { + using_index_expr = fields[field_index-1]; + } else { + fields[field_index-1]->flags &= ~EntityFlag_Anonymous; + error(name_token, "Previous `using` for an index expression `%.*s`", LIT(name_token.string)); + } + } else { + error(name_token, "`using` on a field `%.*s` must be a `struct` or `raw_union`", LIT(name_token.string)); + continue; } - - populate_using_entity_map(c, node, type, &entity_map); } + + populate_using_entity_map(c, node, type, &entity_map); } } gb_temp_arena_memory_end(tmp); + + return field_index; } @@ -520,7 +488,7 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) { Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count); - check_fields(c, node, st->fields, fields, field_count, str_lit("struct")); + field_count = check_fields(c, node, st->fields, fields, field_count, str_lit("struct")); struct_type->Record.struct_is_packed = st->is_packed; struct_type->Record.struct_is_ordered = st->is_ordered; @@ -590,55 +558,88 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) { return; } -} +} void check_union_type(Checker *c, Type *union_type, AstNode *node) { GB_ASSERT(is_type_union(union_type)); ast_node(ut, UnionType, node); - isize field_count = ut->fields.count+1; + isize variant_count = ut->variants.count+1; + isize field_count = 0; + for_array(i, ut->fields) { + AstNode *field = ut->fields.e[i]; + if (field->kind == AstNode_Field) { + ast_node(f, Field, field); + field_count += f->names.count; + } + } gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); MapEntity entity_map = {0}; - map_entity_init_with_reserve(&entity_map, c->tmp_allocator, 2*field_count); + map_entity_init_with_reserve(&entity_map, c->tmp_allocator, 2*variant_count); - Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count); + Entity *using_index_expr = NULL; - isize field_index = 0; - fields[field_index++] = make_entity_type_name(c->allocator, c->context.scope, empty_token, NULL); + Entity **variants = gb_alloc_array(c->allocator, Entity *, variant_count); + Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count); - for_array(i, ut->fields) { - AstNode *field = ut->fields.e[i]; - if (field->kind != AstNode_UnionField) { + isize variant_index = 0; + variants[variant_index++] = make_entity_type_name(c->allocator, c->context.scope, empty_token, NULL); + + field_count = check_fields(c, NULL, ut->fields, fields, field_count, str_lit("union")); + + union_type->Record.fields = fields; + union_type->Record.field_count = field_count; + + for_array(i, ut->variants) { + AstNode *variant = ut->variants.e[i]; + if (variant->kind != AstNode_UnionField) { continue; } - ast_node(f, UnionField, ut->fields.e[i]); + ast_node(f, UnionField, variant); Token name_token = f->name->Ident; - if (str_eq(name_token.string, str_lit("names"))) { - error(name_token, "`names` is a reserved identifier for unions"); - continue; - } - Type *base_type = make_type_struct(c->allocator); { ast_node(fl, FieldList, f->list); - isize field_count = 0; - for_array(j, fl->list) { - ast_node(f, Field, fl->list.e[j]); - field_count += f->names.count; + + // TODO(bill): Just do a gb_memcopy here + // NOTE(bill): Copy the contents for the common fields for now + AstNodeArray list = {0}; + array_init_count(&list, c->allocator, ut->fields.count+fl->list.count); + for (isize j = 0; j < ut->fields.count; j++) { + list.e[j] = ut->fields.e[j]; } + for (isize j = 0; j < fl->list.count; j++) { + list.e[j+ut->fields.count] = fl->list.e[j]; + } + + isize list_count = 0; + for_array(j, list) { + ast_node(f, Field, list.e[j]); + list_count += f->names.count; + } + Token token = name_token; token.kind = Token_struct; - AstNode *dummy_struct = ast_struct_type(c->curr_ast_file, token, fl->list, field_count, - false, true, NULL); + AstNode *dummy_struct = ast_struct_type(c->curr_ast_file, token, list, list_count, false, true, NULL); check_open_scope(c, dummy_struct); - check_struct_type(c, base_type, dummy_struct); - check_close_scope(c); + Entity **fields = gb_alloc_array(c->allocator, Entity *, list_count); + isize field_count = check_fields(c, dummy_struct, list, fields, list_count, str_lit("variant")); + base_type->Record.struct_is_packed = false; + base_type->Record.struct_is_ordered = true; + base_type->Record.fields = fields; + base_type->Record.fields_in_src_order = fields; + base_type->Record.field_count = field_count; + base_type->Record.names = make_names_field_for_record(c, c->context.scope); base_type->Record.node = dummy_struct; + + type_set_offsets(c->allocator, base_type); + + check_close_scope(c); } Type *type = make_type_named(c->allocator, name_token.string, base_type, NULL); @@ -657,16 +658,15 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node) { error(name_token, "`%.*s` is already declared in this union", LIT(name_token.string)); } else { map_entity_set(&entity_map, key, e); - fields[field_index++] = e; + variants[variant_index++] = e; } add_entity_use(c, f->name, e); } gb_temp_arena_memory_end(tmp); - union_type->Record.fields = fields; - union_type->Record.field_count = field_index; - union_type->Record.names = make_names_field_for_record(c, c->context.scope); + union_type->Record.variants = variants; + union_type->Record.variant_count = variant_index; } void check_raw_union_type(Checker *c, Type *union_type, AstNode *node) { @@ -686,7 +686,7 @@ void check_raw_union_type(Checker *c, Type *union_type, AstNode *node) { Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count); - check_fields(c, node, ut->fields, fields, field_count, str_lit("raw_union")); + field_count = check_fields(c, node, ut->fields, fields, field_count, str_lit("raw_union")); union_type->Record.fields = fields; union_type->Record.field_count = field_count; @@ -865,7 +865,7 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari AstNode *field = params.e[i]; if (ast_node_expect(field, AstNode_Field)) { ast_node(f, Field, field); - variable_count += max(f->names.count, 1); + variable_count += gb_max(f->names.count, 1); } } @@ -951,7 +951,7 @@ Type *check_get_results(Checker *c, Scope *scope, AstNode *_results) { AstNode *field = results.e[i]; if (ast_node_expect(field, AstNode_Field)) { ast_node(f, Field, field); - variable_count += max(f->names.count, 1); + variable_count += gb_max(f->names.count, 1); } } @@ -4516,12 +4516,15 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint Type *t = base_type(type); switch (t->kind) { case Type_Record: { - if (!is_type_struct(t)) { + if (!is_type_struct(t) && !is_type_union(t)) { if (cl->elems.count != 0) { error_node(node, "Illegal compound literal"); } break; } + if (is_type_union(t)) { + is_constant = false; + } if (cl->elems.count == 0) { break; // NOTE(bill): No need to init } @@ -4917,8 +4920,8 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint } bool ok = false; - for (isize i = 1; i < bsrc->Record.field_count; i++) { - Entity *f = bsrc->Record.fields[i]; + for (isize i = 1; i < bsrc->Record.variant_count; i++) { + Entity *f = bsrc->Record.variants[i]; if (are_types_identical(f->type, dst)) { ok = true; break; @@ -5308,11 +5311,9 @@ gbString write_expr_to_string(gbString str, AstNode *node); gbString write_record_fields_to_string(gbString str, AstNodeArray params) { for_array(i, params) { if (i > 0) { - str = gb_string_appendc(str, " "); + str = gb_string_appendc(str, ", "); } str = write_expr_to_string(str, params.e[i]); - str = gb_string_appendc(str, ";"); - } return str; } @@ -5502,6 +5503,13 @@ gbString write_expr_to_string(gbString str, AstNode *node) { } case_end; + case_ast_node(f, UnionField, node); + str = write_expr_to_string(str, f->name); + str = gb_string_appendc(str, "{"); + str = write_expr_to_string(str, f->list); + str = gb_string_appendc(str, "}"); + 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/check_stmt.c b/src/check_stmt.c index 7315f80ee..90cb6ff61 100644 --- a/src/check_stmt.c +++ b/src/check_stmt.c @@ -1029,8 +1029,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { if (is_union_ptr) { GB_ASSERT(is_type_union(bt)); bool tag_type_found = false; - for (isize i = 0; i < bt->Record.field_count; i++) { - Entity *f = bt->Record.fields[i]; + for (isize i = 0; i < bt->Record.variant_count; i++) { + Entity *f = bt->Record.variants[i]; if (are_types_identical(f->type, y.type)) { tag_type_found = true; break; @@ -1038,8 +1038,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { } if (!tag_type_found) { gbString type_str = type_to_string(y.type); - error_node(y.expr, - "Unknown tag type, got `%s`", type_str); + error_node(y.expr, "Unknown tag type, got `%s`", type_str); gb_string_free(type_str); continue; } @@ -1163,8 +1162,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { case Entity_TypeName: { Type *t = base_type(e->type); if (is_type_union(t)) { - for (isize i = 0; i < t->Record.field_count; i++) { - Entity *f = t->Record.fields[i]; + for (isize i = 0; i < t->Record.variant_count; i++) { + Entity *f = t->Record.variants[i]; Entity *found = scope_insert_entity(c->context.scope, f); if (found != NULL) { gbString expr_str = expr_to_string(expr); diff --git a/src/checker.c b/src/checker.c index 94ae0c2e0..f2addf241 100644 --- a/src/checker.c +++ b/src/checker.c @@ -954,6 +954,10 @@ void add_type_info_type(Checker *c, Type *t) { break; case TypeRecord_Union: add_type_info_type(c, t_int); + for (isize i = 0; i < bt->Record.variant_count; i++) { + Entity *f = bt->Record.variants[i]; + add_type_info_type(c, f->type); + } /* fallthrough */ default: for (isize i = 0; i < bt->Record.field_count; i++) { @@ -1103,27 +1107,27 @@ void init_preload(Checker *c) { - if (record->field_count != 19) { + if (record->variant_count != 19) { compiler_error("Invalid `Type_Info` layout"); } - t_type_info_named = record->fields[ 1]->type; - t_type_info_integer = record->fields[ 2]->type; - t_type_info_float = record->fields[ 3]->type; - t_type_info_string = record->fields[ 4]->type; - t_type_info_boolean = record->fields[ 5]->type; - t_type_info_any = record->fields[ 6]->type; - t_type_info_pointer = record->fields[ 7]->type; - t_type_info_procedure = record->fields[ 8]->type; - t_type_info_array = record->fields[ 9]->type; - t_type_info_dynamic_array = record->fields[10]->type; - t_type_info_slice = record->fields[11]->type; - t_type_info_vector = record->fields[12]->type; - t_type_info_tuple = record->fields[13]->type; - t_type_info_struct = record->fields[14]->type; - t_type_info_union = record->fields[15]->type; - t_type_info_raw_union = record->fields[16]->type; - t_type_info_enum = record->fields[17]->type; - t_type_info_map = record->fields[18]->type; + t_type_info_named = record->variants[ 1]->type; + t_type_info_integer = record->variants[ 2]->type; + t_type_info_float = record->variants[ 3]->type; + t_type_info_string = record->variants[ 4]->type; + t_type_info_boolean = record->variants[ 5]->type; + t_type_info_any = record->variants[ 6]->type; + t_type_info_pointer = record->variants[ 7]->type; + t_type_info_procedure = record->variants[ 8]->type; + t_type_info_array = record->variants[ 9]->type; + t_type_info_dynamic_array = record->variants[10]->type; + t_type_info_slice = record->variants[11]->type; + t_type_info_vector = record->variants[12]->type; + t_type_info_tuple = record->variants[13]->type; + t_type_info_struct = record->variants[14]->type; + t_type_info_raw_union = record->variants[15]->type; + t_type_info_union = record->variants[16]->type; + t_type_info_enum = record->variants[17]->type; + t_type_info_map = record->variants[18]->type; t_type_info_named_ptr = make_type_pointer(c->allocator, t_type_info_named); t_type_info_integer_ptr = make_type_pointer(c->allocator, t_type_info_integer); @@ -1139,8 +1143,8 @@ void init_preload(Checker *c) { t_type_info_vector_ptr = make_type_pointer(c->allocator, t_type_info_vector); t_type_info_tuple_ptr = make_type_pointer(c->allocator, t_type_info_tuple); t_type_info_struct_ptr = make_type_pointer(c->allocator, t_type_info_struct); - t_type_info_union_ptr = make_type_pointer(c->allocator, t_type_info_union); t_type_info_raw_union_ptr = make_type_pointer(c->allocator, t_type_info_raw_union); + t_type_info_union_ptr = make_type_pointer(c->allocator, t_type_info_union); t_type_info_enum_ptr = make_type_pointer(c->allocator, t_type_info_enum); t_type_info_map_ptr = make_type_pointer(c->allocator, t_type_info_map); } diff --git a/src/ir.c b/src/ir.c index a2a959c1a..d19006455 100644 --- a/src/ir.c +++ b/src/ir.c @@ -1827,6 +1827,15 @@ irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index) { GB_ASSERT(t->Record.field_count > 0); GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); result_type = make_type_pointer(a, t->Record.fields[index]->type); + } else if (is_type_union(t)) { + type_set_offsets(a, t); + GB_ASSERT(t->Record.field_count > 0); + GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); + result_type = make_type_pointer(a, t->Record.fields[index]->type); + i64 offset = t->Record.struct_offsets[index]; + irValue *ptr = ir_emit_conv(proc, s, t_u8_ptr); + ptr = ir_emit_ptr_offset(proc, ptr, ir_make_const_int(a, offset)); + return ir_emit_conv(proc, ptr, result_type); } else if (is_type_tuple(t)) { GB_ASSERT(t->Tuple.variable_count > 0); GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1)); @@ -1881,6 +1890,17 @@ irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index) { GB_ASSERT(t->Record.field_count > 0); GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); result_type = t->Record.fields[index]->type; + } else if (is_type_union(t)) { + type_set_offsets(a, t); + GB_ASSERT(t->Record.field_count > 0); + GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); + Type *ptr_type = make_type_pointer(a, t->Record.fields[index]->type); + i64 offset = t->Record.struct_offsets[index]; + irValue *ptr = ir_address_from_load_or_generate_local(proc, s); + ptr = ir_emit_conv(proc, s, t_u8_ptr); + ptr = ir_emit_ptr_offset(proc, ptr, ir_make_const_int(a, offset)); + ptr = ir_emit_conv(proc, ptr, ptr_type); + return ir_emit_load(proc, ptr); } else if (is_type_tuple(t)) { GB_ASSERT(t->Tuple.variable_count > 0); GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1)); @@ -2278,20 +2298,16 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { } if (is_type_union(dst)) { - for (isize i = 0; i < dst->Record.field_count; i++) { - Entity *f = dst->Record.fields[i]; + for (isize i = 1; i < dst->Record.variant_count; i++) { + Entity *f = dst->Record.variants[i]; if (are_types_identical(f->type, src_type)) { ir_emit_comment(proc, str_lit("union - child to parent")); gbAllocator allocator = proc->module->allocator; irValue *parent = ir_add_local_generated(proc, t); - irValue *tag = ir_make_const_int(allocator, i); - ir_emit_store(proc, ir_emit_union_tag_ptr(proc, parent), tag); + irValue *tag_ptr = ir_emit_union_tag_ptr(proc, parent); + ir_emit_store(proc, tag_ptr, ir_make_const_int(allocator, i)); - irValue *data = ir_emit_conv(proc, parent, t_rawptr); - - Type *tag_type = src_type; - Type *tag_type_ptr = make_type_pointer(allocator, tag_type); - irValue *underlying = ir_emit_bitcast(proc, data, tag_type_ptr); + irValue *underlying = ir_emit_conv(proc, parent, make_type_pointer(allocator, src_type)); ir_emit_store(proc, underlying, value); return ir_emit_load(proc, parent); @@ -2515,8 +2531,8 @@ irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *type, Token irValue *tag = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, value)); irValue *dst_tag = NULL; - for (isize i = 1; i < src->Record.field_count; i++) { - Entity *f = src->Record.fields[i]; + for (isize i = 1; i < src->Record.variant_count; i++) { + Entity *f = src->Record.variants[i]; if (are_types_identical(f->type, dst)) { dst_tag = ir_make_const_int(a, i); break; @@ -2546,10 +2562,12 @@ irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *type, Token Type *dst = tuple->Tuple.variables[0]->type; Type *dst_ptr = make_type_pointer(a, dst); - irValue *tag = ir_emit_union_tag_value(proc, value); + irValue *value_ = ir_address_from_load_or_generate_local(proc, value); + + irValue *tag = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, value_)); irValue *dst_tag = NULL; - for (isize i = 1; i < src->Record.field_count; i++) { - Entity *f = src->Record.fields[i]; + for (isize i = 1; i < src->Record.variant_count; i++) { + Entity *f = src->Record.variants[i]; if (are_types_identical(f->type, dst)) { dst_tag = ir_make_const_int(a, i); break; @@ -2557,8 +2575,6 @@ irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *type, Token } GB_ASSERT(dst_tag != NULL); - irValue *union_ptr = ir_address_from_load_or_generate_local(proc, value); - irBlock *ok_block = ir_new_block(proc, NULL, "union_cast.ok"); irBlock *end_block = ir_new_block(proc, NULL, "union_cast.end"); irValue *cond = ir_emit_comp(proc, Token_CmpEq, tag, dst_tag); @@ -2568,13 +2584,12 @@ irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *type, Token irValue *gep0 = ir_emit_struct_ep(proc, v, 0); irValue *gep1 = ir_emit_struct_ep(proc, v, 1); - irValue *data = ir_emit_load(proc, ir_emit_conv(proc, union_ptr, dst_ptr)); + irValue *data = ir_emit_load(proc, ir_emit_conv(proc, value_, ir_type(gep0))); ir_emit_store(proc, gep0, data); ir_emit_store(proc, gep1, v_true); ir_emit_jump(proc, end_block); ir_start_block(proc, end_block); - } if (!is_tuple) { @@ -2629,10 +2644,20 @@ isize ir_type_info_index(CheckerInfo *info, Type *type) { return entry_index; } + +// TODO(bill): Try and make a lot of this constant aggregate literals in LLVM IR +gb_global irValue *ir_global_type_info_data = NULL; +gb_global irValue *ir_global_type_info_member_types = NULL; +gb_global irValue *ir_global_type_info_member_names = NULL; +gb_global irValue *ir_global_type_info_member_offsets = NULL; + +gb_global i32 ir_global_type_info_data_index = 0; +gb_global i32 ir_global_type_info_member_types_index = 0; +gb_global i32 ir_global_type_info_member_names_index = 0; +gb_global i32 ir_global_type_info_member_offsets_index = 0; + + irValue *ir_type_info(irProcedure *proc, Type *type) { - irValue **found = map_ir_value_get(&proc->module->members, hash_string(str_lit(IR_TYPE_INFO_DATA_NAME))); - GB_ASSERT(found != NULL); - irValue *type_info_data = *found; CheckerInfo *info = proc->module->info; type = default_type(type); @@ -2641,7 +2666,7 @@ irValue *ir_type_info(irProcedure *proc, Type *type) { // gb_printf_err("%d %s\n", entry_index, type_to_string(type)); - return ir_emit_array_ep(proc, type_info_data, ir_make_const_i32(proc->module->allocator, entry_index)); + return ir_emit_array_ep(proc, ir_global_type_info_data, ir_make_const_i32(proc->module->allocator, entry_index)); } @@ -2780,10 +2805,9 @@ void ir_gen_global_type_name(irModule *m, Entity *e, String name) { if (is_type_union(e->type)) { Type *bt = base_type(e->type); - TypeRecord *s = &bt->Record; // NOTE(bill): Zeroth entry is null (for `match type` stmts) - for (isize j = 1; j < s->field_count; j++) { - ir_mangle_sub_type_name(m, s->fields[j], name); + for (isize j = 1; j < bt->Record.variant_count; j++) { + ir_mangle_sub_type_name(m, bt->Record.variants[j], name); } } } @@ -4191,7 +4215,10 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) { } break; case Type_Record: { - GB_ASSERT(is_type_struct(bt)); + // TODO(bill): "constant" unions are not initialized constantly at the moment. + // NOTE(bill): This is due to the layout of the unions when printed to LLVM-IR + bool is_union = is_type_union(bt); + GB_ASSERT(is_type_struct(bt) || is_type_union(bt)); TypeRecord *st = &bt->Record; if (cl->elems.count > 0) { ir_emit_store(proc, v, ir_add_module_constant(proc->module, type, make_exact_value_compound(expr))); @@ -4214,7 +4241,7 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) { } field = st->fields[index]; - if (ir_is_elem_const(proc->module, elem, field->type)) { + if (!is_union && ir_is_elem_const(proc->module, elem, field->type)) { continue; } @@ -5420,10 +5447,10 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { irValue *index = NULL; Type *ut = base_type(type_deref(ir_type(parent))); GB_ASSERT(ut->Record.kind == TypeRecord_Union); - for (isize field_index = 1; field_index < ut->Record.field_count; field_index++) { - Entity *f = ut->Record.fields[field_index]; + for (isize variant_index = 1; variant_index < ut->Record.variant_count; variant_index++) { + Entity *f = ut->Record.variants[variant_index]; if (are_types_identical(f->type, bt)) { - index = ir_make_const_int(allocator, field_index); + index = ir_make_const_int(allocator, variant_index); break; } } @@ -5751,13 +5778,25 @@ void ir_init_module(irModule *m, Checker *c) { { // Add type info data { + isize max_index = -1; + for_array(type_info_map_index, m->info->type_info_map.entries) { + MapIsizeEntry *entry = &m->info->type_info_map.entries.e[type_info_map_index]; + Type *t = cast(Type *)cast(uintptr)entry->key.key; + t = default_type(t); + isize entry_index = ir_type_info_index(m->info, t); + if (max_index < entry_index) { + max_index = entry_index; + } + } + isize max_type_info_count = max_index+1; + String name = str_lit(IR_TYPE_INFO_DATA_NAME); - isize count = c->info.type_info_map.entries.count; - Entity *e = make_entity_variable(m->allocator, NULL, make_token_ident(name), make_type_array(m->allocator, t_type_info, count), false); + Entity *e = make_entity_variable(m->allocator, NULL, make_token_ident(name), make_type_array(m->allocator, t_type_info, max_type_info_count), false); irValue *g = ir_make_value_global(m->allocator, e, NULL); g->Global.is_private = true; ir_module_add_value(m, e, g); map_ir_value_set(&m->members, hash_string(name), g); + ir_global_type_info_data = g; } // Type info member buffer @@ -5775,6 +5814,11 @@ void ir_init_module(irModule *m, Checker *c) { case TypeRecord_Struct: case TypeRecord_RawUnion: count += t->Record.field_count; + break; + case TypeRecord_Union: + count += t->Record.field_count; + count += t->Record.variant_count; + break; } break; case Type_Tuple: @@ -5790,6 +5834,7 @@ void ir_init_module(irModule *m, Checker *c) { irValue *g = ir_make_value_global(m->allocator, e, NULL); ir_module_add_value(m, e, g); map_ir_value_set(&m->members, hash_string(name), g); + ir_global_type_info_member_types = g; } { String name = str_lit(IR_TYPE_INFO_NAMES_NAME); @@ -5798,6 +5843,7 @@ void ir_init_module(irModule *m, Checker *c) { irValue *g = ir_make_value_global(m->allocator, e, NULL); ir_module_add_value(m, e, g); map_ir_value_set(&m->members, hash_string(name), g); + ir_global_type_info_member_names = g; } { String name = str_lit(IR_TYPE_INFO_OFFSETS_NAME); @@ -5806,6 +5852,7 @@ void ir_init_module(irModule *m, Checker *c) { irValue *g = ir_make_value_global(m->allocator, e, NULL); ir_module_add_value(m, e, g); map_ir_value_set(&m->members, hash_string(name), g); + ir_global_type_info_member_offsets = g; } } } @@ -5912,18 +5959,36 @@ String ir_mangle_name(irGen *s, String path, Entity *e) { return make_string(new_name, new_name_len-1); } -irValue *ir_get_type_info_ptr(irProcedure *proc, irValue *type_info_data, Type *type) { + +// +// Type Info stuff +// +irValue *ir_get_type_info_ptr(irProcedure *proc, Type *type) { i32 index = cast(i32)ir_type_info_index(proc->module->info, type); // gb_printf_err("%d %s\n", index, type_to_string(type)); - irValue *ptr = ir_emit_array_epi(proc, type_info_data, index); + irValue *ptr = ir_emit_array_epi(proc, ir_global_type_info_data, index); return ir_emit_bitcast(proc, ptr, t_type_info_ptr); } -irValue *ir_type_info_member_offset(irProcedure *proc, irValue *data, isize count, i32 *index) { - irValue *offset = ir_emit_array_epi(proc, data, *index); - *index += count; +irValue *ir_type_info_member_types_offset(irProcedure *proc, isize count) { + irValue *offset = ir_emit_array_epi(proc, ir_global_type_info_member_types, ir_global_type_info_member_types_index); + ir_global_type_info_member_types_index += count; return offset; } +irValue *ir_type_info_member_names_offset(irProcedure *proc, isize count) { + irValue *offset = ir_emit_array_epi(proc, ir_global_type_info_member_names, ir_global_type_info_member_names_index); + ir_global_type_info_member_names_index += count; + return offset; +} +irValue *ir_type_info_member_offsets_offset(irProcedure *proc, isize count) { + irValue *offset = ir_emit_array_epi(proc, ir_global_type_info_member_offsets, ir_global_type_info_member_offsets_index); + ir_global_type_info_member_offsets_index += count; + return offset; +} + + + + void ir_add_foreign_library_path(irModule *m, Entity *e) { GB_ASSERT(e != NULL); @@ -6285,25 +6350,13 @@ void ir_gen_tree(irGen *s) { } { // NOTE(bill): Setup type_info data - // TODO(bill): Try and make a lot of this constant aggregate literals in LLVM IR - irValue *type_info_data = NULL; - irValue *type_info_member_types = NULL; - irValue *type_info_member_names = NULL; - irValue *type_info_member_offsets = NULL; - - irValue **found = NULL; - type_info_data = *map_ir_value_get(&proc->module->members, hash_string(str_lit(IR_TYPE_INFO_DATA_NAME))); - type_info_member_types = *map_ir_value_get(&proc->module->members, hash_string(str_lit(IR_TYPE_INFO_TYPES_NAME))); - type_info_member_names = *map_ir_value_get(&proc->module->members, hash_string(str_lit(IR_TYPE_INFO_NAMES_NAME))); - type_info_member_offsets = *map_ir_value_get(&proc->module->members, hash_string(str_lit(IR_TYPE_INFO_OFFSETS_NAME))); - CheckerInfo *info = proc->module->info; - if (false) { + if (true) { irValue *global_type_infos = ir_find_global_variable(proc, str_lit("__type_infos")); - Type *type = base_type(type_deref(ir_type(type_info_data))); + Type *type = base_type(type_deref(ir_type(ir_global_type_info_data))); GB_ASSERT(is_type_array(type)); - irValue *array_data = ir_emit_array_epi(proc, type_info_data, 0); + irValue *array_data = ir_emit_array_epi(proc, ir_global_type_info_data, 0); irValue *array_count = ir_make_const_int(proc->module->allocator, type->Array.count); ir_emit_store(proc, ir_emit_struct_ep(proc, global_type_infos, 0), array_data); @@ -6323,10 +6376,10 @@ void ir_gen_tree(irGen *s) { MapIsizeEntry *entry = &info->type_info_map.entries.e[type_info_map_index]; Type *t = cast(Type *)cast(uintptr)entry->key.key; t = default_type(t); - isize entry_index = entry->value; + isize entry_index = ir_type_info_index(info, t); irValue *tag = NULL; - irValue *ti_ptr = ir_emit_array_epi(proc, type_info_data, entry_index); + irValue *ti_ptr = ir_emit_array_epi(proc, ir_global_type_info_data, entry_index); switch (t->kind) { @@ -6336,7 +6389,7 @@ void ir_gen_tree(irGen *s) { // TODO(bill): Which is better? The mangled name or actual name? irValue *name = ir_make_const_string(a, t->Named.type_name->token.string); - irValue *gtip = ir_get_type_info_ptr(proc, type_info_data, t->Named.base); + irValue *gtip = ir_get_type_info_ptr(proc, t->Named.base); ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), name); ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), gtip); @@ -6395,13 +6448,13 @@ void ir_gen_tree(irGen *s) { case Type_Pointer: { ir_emit_comment(proc, str_lit("Type_Info_Pointer")); tag = ir_emit_conv(proc, ti_ptr, t_type_info_pointer_ptr); - irValue *gep = ir_get_type_info_ptr(proc, type_info_data, t->Pointer.elem); + irValue *gep = ir_get_type_info_ptr(proc, t->Pointer.elem); ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), gep); } break; case Type_Array: { ir_emit_comment(proc, str_lit("Type_Info_Array")); tag = ir_emit_conv(proc, ti_ptr, t_type_info_array_ptr); - irValue *gep = ir_get_type_info_ptr(proc, type_info_data, t->Array.elem); + irValue *gep = ir_get_type_info_ptr(proc, t->Array.elem); ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), gep); isize ez = type_size_of(a, t->Array.elem); @@ -6415,7 +6468,7 @@ void ir_gen_tree(irGen *s) { case Type_DynamicArray: { ir_emit_comment(proc, str_lit("Type_Info_DynamicArray")); tag = ir_emit_conv(proc, ti_ptr, t_type_info_dynamic_array_ptr); - irValue *gep = ir_get_type_info_ptr(proc, type_info_data, t->DynamicArray.elem); + irValue *gep = ir_get_type_info_ptr(proc, t->DynamicArray.elem); ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), gep); isize ez = type_size_of(a, t->DynamicArray.elem); @@ -6425,7 +6478,7 @@ void ir_gen_tree(irGen *s) { case Type_Slice: { ir_emit_comment(proc, str_lit("Type_Info_Slice")); tag = ir_emit_conv(proc, ti_ptr, t_type_info_slice_ptr); - irValue *gep = ir_get_type_info_ptr(proc, type_info_data, t->Slice.elem); + irValue *gep = ir_get_type_info_ptr(proc, t->Slice.elem); ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), gep); isize ez = type_size_of(a, t->Slice.elem); @@ -6435,7 +6488,7 @@ void ir_gen_tree(irGen *s) { case Type_Vector: { ir_emit_comment(proc, str_lit("Type_Info_Vector")); tag = ir_emit_conv(proc, ti_ptr, t_type_info_vector_ptr); - irValue *gep = ir_get_type_info_ptr(proc, type_info_data, t->Vector.elem); + irValue *gep = ir_get_type_info_ptr(proc, t->Vector.elem); ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), gep); isize ez = type_size_of(a, t->Vector.elem); @@ -6454,10 +6507,10 @@ void ir_gen_tree(irGen *s) { irValue *convention = ir_emit_struct_ep(proc, tag, 3); if (t->Proc.params) { - ir_emit_store(proc, params, ir_get_type_info_ptr(proc, type_info_data, t->Proc.params)); + ir_emit_store(proc, params, ir_get_type_info_ptr(proc, t->Proc.params)); } if (t->Proc.results) { - ir_emit_store(proc, results, ir_get_type_info_ptr(proc, type_info_data, t->Proc.results)); + ir_emit_store(proc, results, ir_get_type_info_ptr(proc, t->Proc.results)); } ir_emit_store(proc, variadic, ir_make_const_bool(a, t->Proc.variadic)); ir_emit_store(proc, convention, ir_make_const_int(a, t->Proc.calling_convention)); @@ -6474,8 +6527,8 @@ void ir_gen_tree(irGen *s) { ir_emit_store(proc, ir_emit_struct_ep(proc, record, 4), align); } - irValue *memory_types = ir_type_info_member_offset(proc, type_info_member_types, t->Record.field_count, &type_info_member_types_index); - irValue *memory_names = ir_type_info_member_offset(proc, type_info_member_names, t->Record.field_count, &type_info_member_names_index); + irValue *memory_types = ir_type_info_member_types_offset(proc, t->Record.field_count); + irValue *memory_names = ir_type_info_member_names_offset(proc, t->Record.field_count); for (isize i = 0; i < t->Tuple.variable_count; i++) { // NOTE(bill): offset is not used for tuples @@ -6514,15 +6567,15 @@ void ir_gen_tree(irGen *s) { ir_emit_store(proc, ir_emit_struct_ep(proc, record, 7), custom_align); } - irValue *memory_types = ir_type_info_member_offset(proc, type_info_member_types, t->Record.field_count, &type_info_member_types_index); - irValue *memory_names = ir_type_info_member_offset(proc, type_info_member_names, t->Record.field_count, &type_info_member_names_index); - irValue *memory_offsets = ir_type_info_member_offset(proc, type_info_member_offsets, t->Record.field_count, &type_info_member_offsets_index); + irValue *memory_types = ir_type_info_member_types_offset(proc, t->Record.field_count); + irValue *memory_names = ir_type_info_member_names_offset(proc, t->Record.field_count); + irValue *memory_offsets = ir_type_info_member_offsets_offset(proc, t->Record.field_count); type_set_offsets(a, t); // NOTE(bill): Just incase the offsets have not been set yet for (isize source_index = 0; source_index < t->Record.field_count; source_index++) { // TODO(bill): Order fields in source order not layout order Entity *f = t->Record.fields_in_src_order[source_index]; - irValue *tip = ir_get_type_info_ptr(proc, type_info_data, f->type); + irValue *tip = ir_get_type_info_ptr(proc, f->type); i64 foffset = t->Record.struct_offsets[f->Variable.field_index]; GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field); @@ -6545,33 +6598,75 @@ void ir_gen_tree(irGen *s) { case TypeRecord_Union: { ir_emit_comment(proc, str_lit("Type_Info_Union")); tag = ir_emit_conv(proc, ti_ptr, t_type_info_union_ptr); - irValue *record = ir_emit_struct_ep(proc, tag, 0); { irValue *size = ir_make_const_int(a, type_size_of(a, t)); irValue *align = ir_make_const_int(a, type_align_of(a, t)); - ir_emit_store(proc, ir_emit_struct_ep(proc, record, 3), size); - ir_emit_store(proc, ir_emit_struct_ep(proc, record, 4), align); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 3), size); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 4), align); } - irValue *memory_types = ir_type_info_member_offset(proc, type_info_member_types, t->Record.field_count, &type_info_member_types_index); - irValue *memory_names = ir_type_info_member_offset(proc, type_info_member_names, t->Record.field_count, &type_info_member_names_index); + { + irValue *common_fields = ir_emit_struct_ep(proc, tag, 0); - for (isize source_index = 1; source_index < t->Record.field_count; source_index++) { - // TODO(bill): Order fields in source order not layout order - Entity *f = t->Record.fields[source_index]; - irValue *tip = ir_get_type_info_ptr(proc, type_info_data, f->type); - irValue *index = ir_make_const_int(a, source_index); - irValue *type_info = ir_emit_ptr_offset(proc, memory_types, index); + isize field_count = t->Record.field_count; + irValue *memory_types = ir_type_info_member_types_offset(proc, field_count); + irValue *memory_names = ir_type_info_member_names_offset(proc, field_count); + irValue *memory_offsets = ir_type_info_member_offsets_offset(proc, field_count); - ir_emit_store(proc, type_info, ir_type_info(proc, f->type)); - if (f->token.string.len > 0) { - irValue *name = ir_emit_ptr_offset(proc, memory_names, index); - ir_emit_store(proc, name, ir_make_const_string(a, f->token.string)); + type_set_offsets(a, t); // NOTE(bill): Just incase the offsets have not been set yet + for (isize field_index = 0; field_index < field_count; field_index++) { + // TODO(bill): Order fields in source order not layout order + Entity *f = t->Record.fields[field_index]; + irValue *tip = ir_get_type_info_ptr(proc, f->type); + i64 foffset = t->Record.struct_offsets[f->Variable.field_index]; + GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field); + + irValue *index = ir_make_const_int(a, field_index); + irValue *type_info = ir_emit_ptr_offset(proc, memory_types, index); + irValue *offset = ir_emit_ptr_offset(proc, memory_offsets, index); + + ir_emit_store(proc, type_info, ir_type_info(proc, f->type)); + if (f->token.string.len > 0) { + irValue *name = ir_emit_ptr_offset(proc, memory_names, index); + ir_emit_store(proc, name, ir_make_const_string(a, f->token.string)); + } + ir_emit_store(proc, offset, ir_make_const_int(a, foffset)); } + + + ir_fill_slice(proc, ir_emit_struct_ep(proc, common_fields, 0), memory_types, ir_make_const_int(a, field_count)); + ir_fill_slice(proc, ir_emit_struct_ep(proc, common_fields, 1), memory_names, ir_make_const_int(a, field_count)); + ir_fill_slice(proc, ir_emit_struct_ep(proc, common_fields, 2), memory_offsets, ir_make_const_int(a, field_count)); } - ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 0), memory_types, ir_make_const_int(a, t->Record.field_count)); - ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 1), memory_names, ir_make_const_int(a, t->Record.field_count)); + { + irValue *variant_names = ir_emit_struct_ep(proc, tag, 1); + irValue *variant_types = ir_emit_struct_ep(proc, tag, 2); + + isize variant_count = gb_max(0, t->Record.variant_count-1); + irValue *memory_names = ir_type_info_member_names_offset(proc, variant_count); + irValue *memory_types = ir_type_info_member_types_offset(proc, variant_count); + + // NOTE(bill): Zeroth is nil so ignore it + for (isize variant_index = 0; variant_index < variant_count; variant_index++) { + Entity *f = t->Record.variants[variant_index+1]; // Skip zeroth + irValue *tip = ir_get_type_info_ptr(proc, f->type); + + irValue *index = ir_make_const_int(a, variant_index); + irValue *type_info = ir_emit_ptr_offset(proc, memory_types, index); + ir_emit_store(proc, type_info, ir_type_info(proc, f->type)); + + if (f->token.string.len > 0) { + irValue *name = ir_emit_ptr_offset(proc, memory_names, index); + ir_emit_store(proc, name, ir_make_const_string(a, f->token.string)); + } + } + + irValue *count = ir_make_const_int(a, variant_count); + + ir_fill_slice(proc, variant_names, memory_names, count); + ir_fill_slice(proc, variant_types, memory_types, count); + } } break; case TypeRecord_RawUnion: { @@ -6586,9 +6681,9 @@ void ir_gen_tree(irGen *s) { ir_emit_store(proc, ir_emit_struct_ep(proc, record, 4), align); } - irValue *memory_types = ir_type_info_member_offset(proc, type_info_member_types, t->Record.field_count, &type_info_member_types_index); - irValue *memory_names = ir_type_info_member_offset(proc, type_info_member_names, t->Record.field_count, &type_info_member_names_index); - irValue *memory_offsets = ir_type_info_member_offset(proc, type_info_member_offsets, t->Record.field_count, &type_info_member_offsets_index); + irValue *memory_types = ir_type_info_member_types_offset(proc, t->Record.field_count); + irValue *memory_names = ir_type_info_member_names_offset(proc, t->Record.field_count); + irValue *memory_offsets = ir_type_info_member_offsets_offset(proc, t->Record.field_count); for (isize i = 0; i < t->Record.field_count; i++) { Entity *f = t->Record.fields[i]; @@ -6672,9 +6767,9 @@ void ir_gen_tree(irGen *s) { irValue *generated_struct = ir_emit_struct_ep(proc, tag, 2); irValue *count = ir_emit_struct_ep(proc, tag, 3); - ir_emit_store(proc, key, ir_get_type_info_ptr(proc, type_info_data, t->Map.key)); - ir_emit_store(proc, value, ir_get_type_info_ptr(proc, type_info_data, t->Map.value)); - ir_emit_store(proc, generated_struct, ir_get_type_info_ptr(proc, type_info_data, t->Map.generated_struct_type)); + ir_emit_store(proc, key, ir_get_type_info_ptr(proc, t->Map.key)); + ir_emit_store(proc, value, ir_get_type_info_ptr(proc, t->Map.value)); + ir_emit_store(proc, generated_struct, ir_get_type_info_ptr(proc, t->Map.generated_struct_type)); ir_emit_store(proc, count, ir_make_const_int(a, t->Map.count)); } break; } @@ -6682,10 +6777,11 @@ void ir_gen_tree(irGen *s) { if (tag != NULL) { Type *tag_type = type_deref(ir_type(tag)); + GB_ASSERT(is_type_named(tag_type)); Type *ti = base_type(t_type_info); bool found = false; - for (isize i = 1; i < ti->Record.field_count; i++) { - Entity *f = ti->Record.fields[i]; + for (isize i = 1; i < ti->Record.variant_count; i++) { + Entity *f = ti->Record.variants[i]; if (are_types_identical(f->type, tag_type)) { found = true; irValue *tag = ir_make_const_int(a, i); @@ -6695,6 +6791,8 @@ void ir_gen_tree(irGen *s) { } } GB_ASSERT_MSG(found, "%s", type_to_string(tag_type)); + } else { + GB_PANIC("Unhandled Type_Info type: %s", type_to_string(t)); } } } @@ -6702,6 +6800,20 @@ void ir_gen_tree(irGen *s) { ir_end_procedure_body(proc); } + for_array(type_info_map_index, info->type_info_map.entries) { + MapIsizeEntry *entry = &info->type_info_map.entries.e[type_info_map_index]; + Type *t = cast(Type *)cast(uintptr)entry->key.key; + t = default_type(t); + isize entry_index = entry->value; + + gbString s = type_to_string(t); + GB_ASSERT(s[0] != 0); + // gb_printf_err("%s\n", s); + } + + + + for_array(i, m->procs_to_generate) { ir_build_proc(m->procs_to_generate.e[i], m->procs_to_generate.e[i]->Proc.parent); } diff --git a/src/ir_print.c b/src/ir_print.c index a35c4eb36..a7888ce5a 100644 --- a/src/ir_print.c +++ b/src/ir_print.c @@ -191,7 +191,7 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) { case Type_Slice: ir_fprintf(f, "{"); ir_print_type(f, m, t->Slice.elem); - ir_fprintf(f, "*, i%lld, i%lld}", word_bits, word_bits); + ir_fprintf(f, "*, i%lld}", word_bits); return; case Type_DynamicArray: ir_fprintf(f, "{"); diff --git a/src/parser.c b/src/parser.c index 7c4814a68..289e80ba4 100644 --- a/src/parser.c +++ b/src/parser.c @@ -349,9 +349,10 @@ AST_NODE_KIND(_TypeBegin, "", i32) \ AstNode *align; \ }) \ AST_NODE_KIND(UnionType, "union type", struct { \ - Token token; \ + Token token; \ AstNodeArray fields; \ - isize field_count; \ + isize field_count; \ + AstNodeArray variants; \ }) \ AST_NODE_KIND(RawUnionType, "raw union type", struct { \ Token token; \ @@ -1040,11 +1041,12 @@ AstNode *ast_struct_type(AstFile *f, Token token, AstNodeArray fields, isize fie } -AstNode *ast_union_type(AstFile *f, Token token, AstNodeArray fields, isize field_count) { +AstNode *ast_union_type(AstFile *f, Token token, AstNodeArray fields, isize field_count, AstNodeArray variants) { AstNode *result = make_ast_node(f, AstNode_UnionType); result->UnionType.token = token; result->UnionType.fields = fields; result->UnionType.field_count = field_count; + result->UnionType.variants = variants; return result; } @@ -2682,29 +2684,54 @@ AstNode *parse_type_or_ident(AstFile *f) { case Token_union: { Token token = expect_token(f, Token_union); Token open = expect_token_after(f, Token_OpenBrace, "union"); + AstNodeArray decls = make_ast_node_array(f); AstNodeArray variants = make_ast_node_array(f); + isize total_decl_name_count = 0; while (f->curr_token.kind != Token_CloseBrace && f->curr_token.kind != Token_EOF) { - AstNode *name = parse_ident(f); - Token open = expect_token(f, Token_OpenBrace); - isize decl_count = 0; - AstNode *list = parse_record_fields(f, &decl_count, FieldFlag_using, str_lit("union")); - Token close = expect_token(f, Token_CloseBrace); - - array_add(&variants, ast_union_field(f, name, list)); + u32 decl_flags = parse_field_prefixes(f); + if (decl_flags != 0) { + AstNodeArray names = parse_ident_list(f); + if (names.count == 0) { + syntax_error(f->curr_token, "Empty field declaration"); + } + u32 set_flags = check_field_prefixes(f, names.count, FieldFlag_using, decl_flags); + total_decl_name_count += names.count; + expect_token_after(f, Token_Colon, "field list"); + AstNode *type = parse_var_type(f, false); + array_add(&decls, ast_field(f, names, type, set_flags)); + } else { + AstNodeArray names = parse_ident_list(f); + if (names.count == 0) { + break; + } + if (names.count > 1 || f->curr_token.kind == Token_Colon) { + u32 set_flags = check_field_prefixes(f, names.count, FieldFlag_using, decl_flags); + total_decl_name_count += names.count; + expect_token_after(f, Token_Colon, "field list"); + AstNode *type = parse_var_type(f, false); + array_add(&decls, ast_field(f, names, type, set_flags)); + } else { + AstNode *name = names.e[0]; + Token open = expect_token(f, Token_OpenBrace); + isize decl_count = 0; + AstNode *list = parse_record_fields(f, &decl_count, FieldFlag_using, str_lit("union")); + Token close = expect_token(f, Token_CloseBrace); + array_add(&variants, ast_union_field(f, name, list)); + } + } if (f->curr_token.kind != Token_Comma) { break; } next_token(f); } - // AstNode *fields = parse_record_fields(f, &decl_count, 0, str_lit("union")); Token close = expect_token(f, Token_CloseBrace); - return ast_union_type(f, token, variants, variants.count); + return ast_union_type(f, token, decls, total_decl_name_count, variants); } case Token_raw_union: { diff --git a/src/types.c b/src/types.c index 72c9f19ce..5b83bba46 100644 --- a/src/types.c +++ b/src/types.c @@ -89,12 +89,15 @@ typedef struct TypeRecord { // All record types // Theses are arrays // Entity_Variable - struct/raw_union - // Entity_TypeName - union // Entity_Constant - enum Entity **fields; i32 field_count; // == struct_offsets count AstNode *node; + // Entity_TypeName - union + Entity **variants; + i32 variant_count; + i64 * struct_offsets; bool struct_are_offsets_set; bool struct_is_packed; @@ -301,8 +304,8 @@ gb_global Type *t_type_info_slice = NULL; gb_global Type *t_type_info_vector = NULL; gb_global Type *t_type_info_tuple = NULL; gb_global Type *t_type_info_struct = NULL; -gb_global Type *t_type_info_union = NULL; gb_global Type *t_type_info_raw_union = NULL; +gb_global Type *t_type_info_union = NULL; gb_global Type *t_type_info_enum = NULL; gb_global Type *t_type_info_map = NULL; @@ -320,8 +323,8 @@ gb_global Type *t_type_info_slice_ptr = NULL; gb_global Type *t_type_info_vector_ptr = NULL; gb_global Type *t_type_info_tuple_ptr = NULL; gb_global Type *t_type_info_struct_ptr = NULL; -gb_global Type *t_type_info_union_ptr = NULL; gb_global Type *t_type_info_raw_union_ptr = NULL; +gb_global Type *t_type_info_union_ptr = NULL; gb_global Type *t_type_info_enum_ptr = NULL; gb_global Type *t_type_info_map_ptr = NULL; @@ -858,6 +861,7 @@ bool are_types_identical(Type *x, Type *y) { case TypeRecord_RawUnion: case TypeRecord_Union: if (x->Record.field_count == y->Record.field_count && + x->Record.variant_count == y->Record.variant_count && x->Record.struct_is_packed == y->Record.struct_is_packed && x->Record.struct_is_ordered == y->Record.struct_is_ordered && x->Record.custom_align == y->Record.custom_align) { @@ -870,9 +874,18 @@ bool are_types_identical(Type *x, Type *y) { return false; } } + for (isize i = 1; i < x->Record.variant_count; i++) { + if (!are_types_identical(x->Record.variants[i]->type, y->Record.variants[i]->type)) { + return false; + } + if (str_ne(x->Record.variants[i]->token.string, y->Record.variants[i]->token.string)) { + return false; + } + } return true; } break; + case TypeRecord_Enum: return x == y; // NOTE(bill): All enums are unique } @@ -1331,14 +1344,12 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n } if (is_type_union(type)) { - // NOTE(bill): The subtype for a union are stored in the fields - // as they are "kind of" like variables but not - for (isize i = 0; i < type->Record.field_count; i++) { - Entity *f = type->Record.fields[i]; + for (isize i = 1; i < type->Record.variant_count; i++) { + Entity *f = type->Record.variants[i]; GB_ASSERT(f->kind == Entity_TypeName); String str = f->token.string; - if (str_eq(field_name, str)) { + if (str_eq(str, field_name)) { sel.entity = f; // selection_add_index(&sel, i); return sel; @@ -1373,7 +1384,7 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n } } } - } else if (!is_type_union(type)) { + } else { for (isize i = 0; i < type->Record.field_count; i++) { Entity *f = type->Record.fields[i]; if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) { @@ -1605,13 +1616,13 @@ i64 type_align_of_internal(gbAllocator allocator, Type *t, TypePath *path) { case TypeRecord_Union: { i64 max = 1; // NOTE(bill): field zero is null - for (isize i = 1; i < t->Record.field_count; i++) { - Type *field_type = t->Record.fields[i]->type; - type_path_push(path, field_type); + for (isize i = 1; i < t->Record.variant_count; i++) { + Type *variant = t->Record.variants[i]->type; + type_path_push(path, variant); if (path->failure) { return FAILURE_ALIGNMENT; } - i64 align = type_align_of_internal(allocator, field_type, path); + i64 align = type_align_of_internal(allocator, variant, path); type_path_pop(path); if (max < align) { max = align; @@ -1620,7 +1631,7 @@ i64 type_align_of_internal(gbAllocator allocator, Type *t, TypePath *path) { return max; } break; case TypeRecord_RawUnion: { - i64 max = 1; + i64 max = build_context.word_size; for (isize i = 0; i < t->Record.field_count; i++) { Type *field_type = t->Record.fields[i]->type; type_path_push(path, field_type); @@ -1653,7 +1664,6 @@ i64 *type_set_offsets_of(gbAllocator allocator, Entity **fields, isize field_cou offsets[i] = curr_offset; curr_offset += type_size_of(allocator, fields[i]->type); } - } else { for (isize i = 0; i < field_count; i++) { i64 align = type_align_of(allocator, fields[i]->type); @@ -1673,7 +1683,13 @@ bool type_set_offsets(gbAllocator allocator, Type *t) { t->Record.struct_are_offsets_set = true; return true; } - } else if (is_type_tuple(t)) { + } else if (is_type_union(t)) { + if (!t->Record.struct_are_offsets_set) { + t->Record.struct_offsets = type_set_offsets_of(allocator, t->Record.fields, t->Record.field_count, false); + t->Record.struct_are_offsets_set = true; + return true; + } + } else if (is_type_tuple(t)) { if (!t->Tuple.are_offsets_set) { t->Tuple.offsets = type_set_offsets_of(allocator, t->Tuple.variables, t->Tuple.variable_count, false); t->Tuple.are_offsets_set = true; @@ -1801,7 +1817,7 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) { } break; case TypeRecord_Union: { - i64 count = t->Record.field_count; + i64 count = t->Record.variant_count; i64 align = type_align_of_internal(allocator, t, path); if (path->failure) { return FAILURE_SIZE; @@ -1809,13 +1825,13 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) { i64 max = 0; // NOTE(bill): Zeroth field is invalid for (isize i = 1; i < count; i++) { - i64 size = type_size_of_internal(allocator, t->Record.fields[i]->type, path); + i64 size = type_size_of_internal(allocator, t->Record.variants[i]->type, path); if (max < size) { max = size; } } // NOTE(bill): Align to int - isize size = align_formula(max, build_context.word_size); + i64 size = align_formula(max, build_context.word_size); size += type_size_of_internal(allocator, t_int, path); return align_formula(size, align); } break; @@ -1983,8 +1999,9 @@ gbString write_type_to_string(gbString str, Type *type) { 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, "; "); + 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); @@ -1994,16 +2011,38 @@ gbString write_type_to_string(gbString str, Type *type) { case TypeRecord_Union: str = gb_string_appendc(str, "union{"); - for (isize i = 1; i < type->Record.field_count; i++) { + for (isize i = 0; i < type->Record.field_count; i++) { Entity *f = type->Record.fields[i]; - GB_ASSERT(f->kind == Entity_TypeName); - if (i > 1) { - str = gb_string_appendc(str, "; "); + 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, base_type(f->type)); } + for (isize i = 1; i < type->Record.variant_count; i++) { + Entity *f = type->Record.variants[i]; + GB_ASSERT(f->kind == Entity_TypeName); + if (i > 1 || type->Record.field_count > 1) { + str = gb_string_appendc(str, ", "); + } + str = gb_string_append_length(str, f->token.string.text, f->token.string.len); + str = gb_string_appendc(str, "{"); + Type *variant = base_type(f->type); + for (isize i = 0; i < variant->Record.field_count; i++) { + Entity *f = variant->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, "{"); + + } str = gb_string_appendc(str, "}"); break; @@ -2021,6 +2060,25 @@ gbString write_type_to_string(gbString str, Type *type) { } str = gb_string_appendc(str, "}"); break; + + case TypeRecord_Enum: + str = gb_string_appendc(str, "enum"); + if (type->Record.enum_base_type != NULL) { + str = gb_string_appendc(str, " "); + str = write_type_to_string(str, type->Record.enum_base_type); + } + str = gb_string_appendc(str, " {"); + for (isize i = 0; i < type->Record.field_count; i++) { + Entity *f = type->Record.fields[i]; + GB_ASSERT(f->kind == Entity_Constant); + 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 = gb_string_appendc(str, "}"); + break; } } break;