Ver Fonte

A decent union type with common fields and variants

Ginger Bill há 8 anos atrás
pai
commit
047c0e4bcc
10 ficheiros alterados com 675 adições e 386 exclusões
  1. 95 5
      code/demo.odin
  2. 23 74
      core/_preload.odin
  3. 46 4
      core/fmt.odin
  4. 153 145
      src/check_expr.c
  5. 5 6
      src/check_stmt.c
  6. 24 20
      src/checker.c
  7. 206 94
      src/ir.c
  8. 1 1
      src/ir_print.c
  9. 39 12
      src/parser.c
  10. 83 25
      src/types.c

+ 95 - 5
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;
 
 
 /*

+ 23 - 74
core/_preload.odin

@@ -14,22 +14,11 @@
 
 // IMPORTANT NOTE(bill): Do not change the order of any of this data
 // The compiler relies upon this _exact_ order
-Type_Info_Record :: struct #ordered {
-	types:        []^Type_Info,
-	names:        []string,
-	offsets:      []int,    // offsets may not be used in tuples
-	size:         int, // in bytes
-	align:        int, // in bytes
-	packed:       bool,
-	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
+// NOTE(bill): This must match the compiler's
 Calling_Convention :: enum {
 	ODIN = 0,
 	C    = 1,
@@ -37,67 +26,17 @@ Calling_Convention :: enum {
 	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_Record :: struct #ordered {
+	types:        []^Type_Info,
+	names:        []string,
+	offsets:      []int,    // offsets may not be used in tuples
+	size:         int, // in bytes
+	align:        int, // in bytes
+	packed:       bool,
+	ordered:      bool,
+	custom_align: bool,
 }
-*/
+
 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 {

+ 46 - 4
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..<variant.names.count {
+				if j > 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)");
 

+ 153 - 145
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) {
-				continue;
-			}
-
-			ast_node(f, Field, decl);
-			Type *base_type = check_type_extra(c, f->type, NULL);
+	if (node != NULL) {
+		GB_ASSERT(node->kind != AstNode_UnionType);
+	}
 
-			for_array(name_index, f->names) {
-				AstNode *name = f->names.e[name_index];
-				if (!ast_node_expect(name, AstNode_Ident)) {
-					continue;
-				}
+	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);
 
-				Token name_token = name->Ident;
+		Type *type = check_type(c, f->type);
 
-				if (str_eq(name_token.string, str_lit("names"))) {
-					error(name_token, "`names` is a reserved identifier for unions");
-					continue;
-				}
+		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");
+			}
+		}
 
-				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);
+		for_array(name_index, f->names) {
+			AstNode *name = f->names.e[name_index];
+			if (!ast_node_expect(name, AstNode_Ident)) {
+				continue;
+			}
 
-				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));
-						}
+					}
+					if (ok) {
+						using_index_expr = fields[field_index-1];
 					} else {
-						error(name_token, "`using` on a field `%.*s` must be a `struct` or `raw_union`", LIT(name_token.string));
-						continue;
+						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) {
-			continue;
-		}
-		ast_node(f, UnionField, ut->fields.e[i]);
-		Token name_token = f->name->Ident;
+	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"));
 
-		if (str_eq(name_token.string, str_lit("names"))) {
-			error(name_token, "`names` is a reserved identifier for unions");
+	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, variant);
+		Token name_token = f->name->Ident;
 
 		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, "(");

+ 5 - 6
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);

+ 24 - 20
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);
 	}

+ 206 - 94
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 *data = ir_emit_conv(proc, parent, t_rawptr);
+				irValue *tag_ptr = ir_emit_union_tag_ptr(proc, parent);
+				ir_emit_store(proc, tag_ptr, ir_make_const_int(allocator, i));
 
-				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);
+
+							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);
+
+							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));
+							}
 
-						for (isize source_index = 1; source_index < t->Record.field_count; source_index++) {
-							// TODO(bill): Order fields in source order not layout order
-							Entity *f = t->Record.fields[source_index];
-							irValue *tip = ir_get_type_info_ptr(proc, type_info_data, f->type);
-							irValue *index     = ir_make_const_int(a, source_index);
-							irValue *type_info = ir_emit_ptr_offset(proc, memory_types,   index);
 
-							ir_emit_store(proc, type_info, ir_type_info(proc, f->type));
-							if (f->token.string.len > 0) {
-								irValue *name = ir_emit_ptr_offset(proc, memory_names,   index);
-								ir_emit_store(proc, name, ir_make_const_string(a, f->token.string));
-							}
+							ir_fill_slice(proc, ir_emit_struct_ep(proc, 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);
 	}

+ 1 - 1
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, "{");

+ 39 - 12
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: {

+ 83 - 25
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;