Browse Source

`union` type allow for any types and removes common fields

Ginger Bill 8 years ago
parent
commit
fd8c4d58bb
10 changed files with 357 additions and 468 deletions
  1. 74 49
      core/_preload.odin
  2. 36 50
      core/fmt.odin
  3. 15 15
      core/mem.odin
  4. 20 20
      core/types.odin
  5. 22 110
      src/check_expr.cpp
  6. 2 2
      src/check_stmt.cpp
  7. 45 44
      src/checker.cpp
  8. 128 95
      src/ir.cpp
  9. 7 45
      src/parser.cpp
  10. 8 38
      src/types.cpp

+ 74 - 49
core/_preload.odin

@@ -34,59 +34,54 @@ CallingConvention :: enum {
 }
 // IMPORTANT NOTE(bill): Do not change the order of any of this data
 // The compiler relies upon this _exact_ order
-
-
-TypeInfoEnumValue :: raw_union {
-	f: f64;
-	i: i128;
-}
-
-TypeInfoRecord :: struct #ordered {
-	types:        []^TypeInfo;
-	names:        []string;
-	offsets:      []int;  // offsets may not be used in tuples
-	usings:       []bool; // usings may not be used in tuples
-	packed:       bool;
-	ordered:      bool;
-	custom_align: bool;
-}
-
-
-TypeInfo :: union {
-	size:  int;
-	align: int;
-
-	Named{name: string; base: ^TypeInfo};
-	Integer{signed: bool};
-	Rune{};
-	Float{};
-	Complex{};
-	String{};
-	Boolean{};
-	Any{};
-	Pointer{
+TypeInfo :: struct #ordered {
+// Core Types
+	EnumValue :: raw_union {
+		f: f64;
+		i: i128;
+	}
+	Record :: struct #ordered {
+		types:        []^TypeInfo;
+		names:        []string;
+		offsets:      []int;  // offsets may not be used in tuples
+		usings:       []bool; // usings may not be used in tuples
+		packed:       bool;
+		ordered:      bool;
+		custom_align: bool;
+	}
+
+// Variant Types
+	Named   :: struct #ordered {name: string; base: ^TypeInfo};
+	Integer :: struct #ordered {signed: bool};
+	Rune    :: struct{};
+	Float   :: struct{};
+	Complex :: struct{};
+	String  :: struct{};
+	Boolean :: struct{};
+	Any     :: struct{};
+	Pointer :: struct #ordered {
 		elem: ^TypeInfo; // nil -> rawptr
 	};
-	Atomic{elem: ^TypeInfo};
-	Procedure{
+	Atomic :: struct #ordered {elem: ^TypeInfo};
+	Procedure :: struct #ordered {
 		params:     ^TypeInfo; // TypeInfo.Tuple
 		results:    ^TypeInfo; // TypeInfo.Tuple
 		variadic:   bool;
 		convention: CallingConvention;
 	};
-	Array{
+	Array :: struct #ordered {
 		elem:      ^TypeInfo;
 		elem_size: int;
 		count:     int;
 	};
-	DynamicArray{elem: ^TypeInfo; elem_size: int};
-	Slice       {elem: ^TypeInfo; elem_size: int};
-	Vector      {elem: ^TypeInfo; elem_size, count: int};
-	Tuple       {using record: TypeInfoRecord}; // Only really used for procedures
-	Struct      {using record: TypeInfoRecord};
-	RawUnion    {using record: TypeInfoRecord};
-	Union{
-		common_fields: struct {
+	DynamicArray :: struct #ordered {elem: ^TypeInfo; elem_size: int};
+	Slice        :: struct #ordered {elem: ^TypeInfo; elem_size: int};
+	Vector       :: struct #ordered {elem: ^TypeInfo; elem_size, count: int};
+	Tuple        :: Record; // Only really used for procedures
+	Struct       :: Record;
+	RawUnion     :: Record;
+	Union :: struct #ordered {
+		common_fields: struct #ordered {
 			types:     []^TypeInfo;
 			names:     []string;
 			offsets:   []int;    // offsets may not be used in tuples
@@ -94,22 +89,52 @@ TypeInfo :: union {
 		variant_names: []string;
 		variant_types: []^TypeInfo;
 	};
-	Enum{
+	Enum :: struct #ordered {
 		base:   ^TypeInfo;
 		names:  []string;
-		values: []TypeInfoEnumValue;
+		values: []EnumValue;
 	};
-	Map{
+	Map :: struct #ordered {
 		key:              ^TypeInfo;
 		value:            ^TypeInfo;
 		generated_struct: ^TypeInfo;
 		count:            int; // == 0 if dynamic
 	};
-	BitField{
+	BitField :: struct #ordered {
 		names:   []string;
 		bits:    []i32;
 		offsets: []i32;
 	};
+
+
+// Fields
+	size:  int;
+	align: int;
+
+	variant: union {
+		Named,
+		Integer,
+		Rune,
+		Float,
+		Complex,
+		String,
+		Boolean,
+		Any,
+		Pointer,
+		Atomic,
+		Procedure,
+		Array,
+		DynamicArray,
+		Slice,
+		Vector,
+		Tuple,
+		Struct,
+		RawUnion,
+		Union,
+		Enum,
+		Map,
+		BitField,
+	};
 }
 
 // NOTE(bill): only the ones that are needed (not all types)
@@ -147,7 +172,7 @@ Context :: struct #ordered {
 
 DEFAULT_ALIGNMENT :: align_of([vector 4]f32);
 
-SourceCodeLocation :: struct {
+SourceCodeLocation :: struct #ordered {
 	fully_pathed_filename: string;
 	line, column:          i64;
 	procedure:             string;
@@ -190,7 +215,7 @@ type_info_base :: proc(info: ^TypeInfo) -> ^TypeInfo {
 	if info == nil do return nil;
 
 	base := info;
-	match i in base {
+	match i in base.variant {
 	case TypeInfo.Named: base = i.base;
 	}
 	return base;
@@ -201,7 +226,7 @@ type_info_base_without_enum :: proc(info: ^TypeInfo) -> ^TypeInfo {
 	if info == nil do return nil;
 
 	base := info;
-	match i in base {
+	match i in base.variant {
 	case TypeInfo.Named: base = i.base;
 	case TypeInfo.Enum:  base = i.base;
 	}

+ 36 - 50
core/fmt.odin

@@ -10,8 +10,8 @@ import (
 _BUFFER_SIZE :: 1<<12;
 
 StringBuffer :: union {
-	Static {buf: []u8;};
-	Dynamic{buf: [dynamic]u8;};
+	[]u8,
+	[dynamic]u8,
 }
 
 FmtInfo :: struct {
@@ -34,28 +34,18 @@ FmtInfo :: struct {
 }
 
 
-make_string_buffer_from_slice :: proc(b: []u8) -> StringBuffer {
-	return StringBuffer.Static{b};
-}
 
-make_string_dynamic_buffer :: proc() -> StringBuffer {
-	return StringBuffer.Dynamic{make([dynamic]u8)};
-}
 string_buffer_data :: proc(buf: ^StringBuffer) -> []u8 {
 	match b in buf {
-	case StringBuffer.Static:
-		return b.buf[..];
-	case StringBuffer.Dynamic:
-		return b.buf[..];
+	case []u8:        return b[..];
+	case [dynamic]u8: return b[..];
 	}
 	return nil;
 }
 string_buffer_data :: proc(buf: StringBuffer) -> []u8 {
 	match b in buf {
-	case StringBuffer.Static:
-		return b.buf[..];
-	case StringBuffer.Dynamic:
-		return b.buf[..];
+	case []u8:        return b[..];
+	case [dynamic]u8: return b[..];
 	}
 	return nil;
 }
@@ -69,18 +59,14 @@ write_string :: proc(buf: ^StringBuffer, s: string) {
 }
 write_bytes :: proc(buf: ^StringBuffer, data: []u8) {
 	match b in buf {
-	case StringBuffer.Static:
-		append(&b.buf, ...data);
-	case StringBuffer.Dynamic:
-		append(&b.buf, ...data);
+	case []u8:        append(b, ...data);
+	case [dynamic]u8: append(b, ...data);
 	}
 }
 write_byte :: proc(buf: ^StringBuffer, data: u8) {
 	match b in buf {
-	case StringBuffer.Static:
-		append(&b.buf, data);
-	case StringBuffer.Dynamic:
-		append(&b.buf, data);
+	case []u8:        append(b, data);
+	case [dynamic]u8: append(b, data);
 	}
 }
 write_rune :: proc(buf: ^StringBuffer, r: rune) {
@@ -108,7 +94,7 @@ write_int :: proc(buf: ^StringBuffer, i: i64, base: int) {
 
 fprint :: proc(fd: os.Handle, args: ...any) -> int {
 	data: [_BUFFER_SIZE]u8;
-	buf := make_string_buffer_from_slice(data[..0]);
+	buf := StringBuffer(data[..0]);
 	sbprint(&buf, ...args);
 	res := string_buffer_data(buf);
 	os.write(fd, res);
@@ -117,7 +103,7 @@ fprint :: proc(fd: os.Handle, args: ...any) -> int {
 
 fprintln :: proc(fd: os.Handle, args: ...any) -> int {
 	data: [_BUFFER_SIZE]u8;
-	buf := make_string_buffer_from_slice(data[..0]);
+	buf := StringBuffer(data[..0]);
 	sbprintln(&buf, ...args);
 	res := string_buffer_data(buf);
 	os.write(fd, res);
@@ -125,7 +111,7 @@ fprintln :: proc(fd: os.Handle, args: ...any) -> int {
 }
 fprintf :: proc(fd: os.Handle, fmt: string, args: ...any) -> int {
 	data: [_BUFFER_SIZE]u8;
-	buf := make_string_buffer_from_slice(data[..0]);
+	buf := StringBuffer(data[..0]);
 	sbprintf(&buf, fmt, ...args);
 	res := string_buffer_data(buf);
 	os.write(fd, res);
@@ -145,17 +131,17 @@ printf_err  :: proc(fmt: string, args: ...any) -> int { return fprintf(os.stderr
 // aprint* procedures return a string that was allocated with the current context
 // They must be freed accordingly
 aprint :: proc(args: ...any) -> string {
-	buf := make_string_dynamic_buffer();
+	buf := StringBuffer(make([dynamic]u8));
 	sbprint(&buf, ...args);
 	return to_string(buf);
 }
 aprintln :: proc(args: ...any) -> string {
-	buf := make_string_dynamic_buffer();
+	buf := StringBuffer(make([dynamic]u8));
 	sbprintln(&buf, ...args);
 	return to_string(buf);
 }
 aprintf :: proc(fmt: string, args: ...any) -> string {
-	buf := make_string_dynamic_buffer();
+	buf := StringBuffer(make([dynamic]u8));
 	sbprintf(&buf, fmt, ...args);
 	return to_string(buf);
 }
@@ -164,15 +150,15 @@ aprintf :: proc(fmt: string, args: ...any) -> string {
 // bprint* procedures return a string that was allocated with the current context
 // They must be freed accordingly
 bprint :: proc(buf: []u8, args: ...any) -> string {
-	sb := make_string_buffer_from_slice(buf[..0..len(buf)]);
+	sb := StringBuffer(buf[..0..len(buf)]);
 	return sbprint(&sb, ...args);
 }
 bprintln :: proc(buf: []u8, args: ...any) -> string {
-	sb := make_string_buffer_from_slice(buf[..0..len(buf)]);
+	sb := StringBuffer(buf[..0..len(buf)]);
 	return sbprintln(&sb, ...args);
 }
 bprintf :: proc(buf: []u8, fmt: string, args: ...any) -> string {
-	sb := make_string_buffer_from_slice(buf[..0..len(buf)]);
+	sb := StringBuffer(buf[..0..len(buf)]);
 	return sbprintf(&sb, fmt, ...args);
 }
 
@@ -183,7 +169,7 @@ bprintf :: proc(buf: []u8, fmt: string, args: ...any) -> string {
 
 fprint_type :: proc(fd: os.Handle, info: ^TypeInfo) {
 	data: [_BUFFER_SIZE]u8;
-	buf := make_string_buffer_from_slice(data[..0]);
+	buf := StringBuffer(data[..0]);
 	write_type(&buf, info);
 	os.write(fd, string_buffer_data(buf));
 }
@@ -192,7 +178,7 @@ write_type :: proc(buf: ^StringBuffer, ti: ^TypeInfo) {
 	if ti == nil do return;
 
 	using TypeInfo;
-	match info in ti {
+	match info in ti.variant {
 	case Named:
 		write_string(buf, info.name);
 	case Integer:
@@ -201,18 +187,18 @@ write_type :: proc(buf: ^StringBuffer, ti: ^TypeInfo) {
 		case ti == type_info(uint): write_string(buf, "uint");
 		case:
 			write_string(buf, info.signed ? "i" : "u");
-			write_int(buf, i64(8*info.size), 10);
+			write_int(buf, i64(8*ti.size), 10);
 		}
 	case Rune:
 		write_string(buf, "rune");
 	case Float:
-		match info.size {
+		match ti.size {
 		case 2: write_string(buf, "f16");
 		case 4: write_string(buf, "f32");
 		case 8: write_string(buf, "f64");
 		}
 	case Complex:
-		match info.size {
+		match ti.size {
 		case 4:  write_string(buf, "complex32");
 		case 8:  write_string(buf, "complex64");
 		case 16: write_string(buf, "complex128");
@@ -237,7 +223,7 @@ write_type :: proc(buf: ^StringBuffer, ti: ^TypeInfo) {
 		if info.params == nil {
 			write_string(buf, "()");
 		} else {
-			t := info.params.(^Tuple);
+			t := info.params.variant.(Tuple);
 			write_string(buf, "(");
 			for t, i in t.types {
 				if i > 0 do write_string(buf, ", ");
@@ -295,7 +281,7 @@ write_type :: proc(buf: ^StringBuffer, ti: ^TypeInfo) {
 		if info.ordered do write_string(buf, "#ordered ");
 		if info.custom_align {
 			write_string(buf, "#align ");
-			write_int(buf, i64(info.align), 10);
+			write_int(buf, i64(ti.align), 10);
 			write_byte(buf, ' ');
 		}
 		write_byte(buf, '{');
@@ -324,8 +310,8 @@ write_type :: proc(buf: ^StringBuffer, ti: ^TypeInfo) {
 			write_byte(buf, '{');
 			defer write_byte(buf, '}');
 
-			variant_type := type_info_base(info.variant_types[i]);
-			variant := variant_type.(^Struct);
+			variant_type := type_info_base(info.variant_types[i]).variant;
+			variant := (&variant_type).(Struct);
 
 			vc := len(variant.names)-len(cf.names);
 			for j in 0..vc {
@@ -359,9 +345,9 @@ write_type :: proc(buf: ^StringBuffer, ti: ^TypeInfo) {
 		write_string(buf, "}");
 	case BitField:
 		write_string(buf, "bit_field ");
-		if info.align != 1 {
+		if ti.align != 1 {
 			write_string(buf, "#align ");
-			write_int(buf, i64(info.align), 10);
+			write_int(buf, i64(ti.align), 10);
 			write_rune(buf, ' ');
 		}
 		write_string(buf, " {");
@@ -697,7 +683,7 @@ fmt_enum :: proc(fi: ^FmtInfo, v: any, verb: rune) {
 	}
 
 	using TypeInfo;
-	match e in v.type_info {
+	match e in v.type_info.variant {
 	case:
 		fmt_bad_verb(fi, verb);
 		return;
@@ -768,9 +754,9 @@ fmt_value :: proc(fi: ^FmtInfo, v: any, verb: rune) {
 	}
 
 	using TypeInfo;
-	match info in v.type_info {
+	match info in v.type_info.variant {
 	case Named:
-		match b in info.base {
+		match b in info.base.variant {
 		case Struct:
 			if verb != 'v' {
 				fmt_bad_verb(fi, verb);
@@ -866,9 +852,9 @@ fmt_value :: proc(fi: ^FmtInfo, v: any, verb: rune) {
 		defer write_byte(fi.buf, ']');
 
 		entries    := &(^raw.DynamicMap(v.data).entries);
-		gs         := type_info_base(info.generated_struct).(^Struct);
-		ed         := type_info_base(gs.types[1]).(^DynamicArray);
-		entry_type := ed.elem.(^Struct);
+		gs         := type_info_base(info.generated_struct).variant.(Struct);
+		ed         := type_info_base(gs.types[1]).variant.(DynamicArray);
+		entry_type := ed.elem.variant.(Struct);
 		entry_size := ed.elem_size;
 
 		for i in 0..entries.len {

+ 15 - 15
core/mem.odin

@@ -208,15 +208,15 @@ align_of_type_info :: proc(type_info: ^TypeInfo) -> int {
 	WORD_SIZE :: size_of(int);
 	MAX_ALIGN :: size_of([vector 64]f64); // TODO(bill): Should these constants be builtin constants?
 	using TypeInfo;
-	match info in type_info {
+	match info in type_info.variant {
 	case Named:
 		return align_of_type_info(info.base);
 	case Integer:
-		return info.size;
+		return type_info.align;
 	case Rune:
-		return info.size;
+		return type_info.align;
 	case Float:
-		return info.size;
+		return type_info.align;
 	case String:
 		return WORD_SIZE;
 	case Boolean:
@@ -239,13 +239,13 @@ align_of_type_info :: proc(type_info: ^TypeInfo) -> int {
 		total := size * count;
 		return clamp(total, 1, MAX_ALIGN);
 	case Tuple:
-		return info.align;
+		return type_info.align;
 	case Struct:
-		return info.align;
+		return type_info.align;
 	case Union:
-		return info.align;
+		return type_info.align;
 	case RawUnion:
-		return info.align;
+		return type_info.align;
 	case Enum:
 		return align_of_type_info(info.base);
 	case Map:
@@ -263,15 +263,15 @@ align_formula :: proc(size, align: int) -> int {
 size_of_type_info :: proc(type_info: ^TypeInfo) -> int {
 	WORD_SIZE :: size_of(int);
 	using TypeInfo;
-	match info in type_info {
+	match info in type_info.variant {
 	case Named:
 		return size_of_type_info(info.base);
 	case Integer:
-		return info.size;
+		return type_info.size;
 	case Rune:
-		return info.size;
+		return type_info.size;
 	case Float:
-		return info.size;
+		return type_info.size;
 	case String:
 		return 2*WORD_SIZE;
 	case Boolean:
@@ -301,11 +301,11 @@ size_of_type_info :: proc(type_info: ^TypeInfo) -> int {
 		alignment := align_formula(size, align);
 		return alignment*(count-1) + size;
 	case Struct:
-		return info.size;
+		return type_info.size;
 	case Union:
-		return info.size;
+		return type_info.size;
 	case RawUnion:
-		return info.size;
+		return type_info.size;
 	case Enum:
 		return size_of_type_info(info.base);
 	case Map:

+ 20 - 20
core/types.odin

@@ -1,6 +1,6 @@
 is_signed :: proc(info: ^TypeInfo) -> bool {
 	if info == nil do return false;
-	match i in type_info_base(info) {
+	match i in type_info_base(info).variant {
 	case TypeInfo.Integer: return i.signed;
 	case TypeInfo.Float:   return true;
 	}
@@ -8,96 +8,96 @@ is_signed :: proc(info: ^TypeInfo) -> bool {
 }
 is_integer :: proc(info: ^TypeInfo) -> bool {
 	if info == nil do return false;
-	_, ok := type_info_base(info).(^TypeInfo.Integer);
+	_, ok := type_info_base(info).variant.(TypeInfo.Integer);
 	return ok;
 }
 is_rune :: proc(info: ^TypeInfo) -> bool {
 	if info == nil do return false;
-	_, ok := type_info_base(info).(^TypeInfo.Rune);
+	_, ok := type_info_base(info).variant.(TypeInfo.Rune);
 	return ok;
 }
 is_float :: proc(info: ^TypeInfo) -> bool {
 	if info == nil do return false;
-	_, ok := type_info_base(info).(^TypeInfo.Float);
+	_, ok := type_info_base(info).variant.(TypeInfo.Float);
 	return ok;
 }
 is_complex :: proc(info: ^TypeInfo) -> bool {
 	if info == nil do return false;
-	_, ok := type_info_base(info).(^TypeInfo.Complex);
+	_, ok := type_info_base(info).variant.(TypeInfo.Complex);
 	return ok;
 }
 is_any :: proc(info: ^TypeInfo) -> bool {
 	if info == nil do return false;
-	_, ok := type_info_base(info).(^TypeInfo.Any);
+	_, ok := type_info_base(info).variant.(TypeInfo.Any);
 	return ok;
 }
 is_string :: proc(info: ^TypeInfo) -> bool {
 	if info == nil do return false;
-	_, ok := type_info_base(info).(^TypeInfo.String);
+	_, ok := type_info_base(info).variant.(TypeInfo.String);
 	return ok;
 }
 is_boolean :: proc(info: ^TypeInfo) -> bool {
 	if info == nil do return false;
-	_, ok := type_info_base(info).(^TypeInfo.Boolean);
+	_, ok := type_info_base(info).variant.(TypeInfo.Boolean);
 	return ok;
 }
 is_pointer :: proc(info: ^TypeInfo) -> bool {
 	if info == nil do return false;
-	_, ok := type_info_base(info).(^TypeInfo.Pointer);
+	_, ok := type_info_base(info).variant.(TypeInfo.Pointer);
 	return ok;
 }
 is_procedure :: proc(info: ^TypeInfo) -> bool {
 	if info == nil do return false;
-	_, ok := type_info_base(info).(^TypeInfo.Procedure);
+	_, ok := type_info_base(info).variant.(TypeInfo.Procedure);
 	return ok;
 }
 is_array :: proc(info: ^TypeInfo) -> bool {
 	if info == nil do return false;
-	_, ok := type_info_base(info).(^TypeInfo.Array);
+	_, ok := type_info_base(info).variant.(TypeInfo.Array);
 	return ok;
 }
 is_dynamic_array :: proc(info: ^TypeInfo) -> bool {
 	if info == nil do return false;
-	_, ok := type_info_base(info).(^TypeInfo.DynamicArray);
+	_, ok := type_info_base(info).variant.(TypeInfo.DynamicArray);
 	return ok;
 }
 is_dynamic_map :: proc(info: ^TypeInfo) -> bool {
 	if info == nil do return false;
-	_, ok := type_info_base(info).(^TypeInfo.Map);
+	_, ok := type_info_base(info).variant.(TypeInfo.Map);
 	return ok;
 }
 is_slice :: proc(info: ^TypeInfo) -> bool {
 	if info == nil do return false;
-	_, ok := type_info_base(info).(^TypeInfo.Slice);
+	_, ok := type_info_base(info).variant.(TypeInfo.Slice);
 	return ok;
 }
 is_vector :: proc(info: ^TypeInfo) -> bool {
 	if info == nil do return false;
-	_, ok := type_info_base(info).(^TypeInfo.Vector);
+	_, ok := type_info_base(info).variant.(TypeInfo.Vector);
 	return ok;
 }
 is_tuple :: proc(info: ^TypeInfo) -> bool {
 	if info == nil do return false;
-	_, ok := type_info_base(info).(^TypeInfo.Tuple);
+	_, ok := type_info_base(info).variant.(TypeInfo.Tuple);
 	return ok;
 }
 is_struct :: proc(info: ^TypeInfo) -> bool {
 	if info == nil do return false;
-	_, ok := type_info_base(info).(^TypeInfo.Struct);
+	_, ok := type_info_base(info).variant.(TypeInfo.Struct);
 	return ok;
 }
 is_union :: proc(info: ^TypeInfo) -> bool {
 	if info == nil do return false;
-	_, ok := type_info_base(info).(^TypeInfo.Union);
+	_, ok := type_info_base(info).variant.(TypeInfo.Union);
 	return ok;
 }
 is_raw_union :: proc(info: ^TypeInfo) -> bool {
 	if info == nil do return false;
-	_, ok := type_info_base(info).(^TypeInfo.RawUnion);
+	_, ok := type_info_base(info).variant.(TypeInfo.RawUnion);
 	return ok;
 }
 is_enum :: proc(info: ^TypeInfo) -> bool {
 	if info == nil do return false;
-	_, ok := type_info_base(info).(^TypeInfo.Enum);
+	_, ok := type_info_base(info).variant.(TypeInfo.Enum);
 	return ok;
 }

+ 22 - 110
src/check_expr.cpp

@@ -500,8 +500,8 @@ i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) {
 
 	if (is_type_union(dst)) {
 		for (isize i = 0; i < dst->Record.variant_count; i++) {
-			Entity *f = dst->Record.variants[i];
-			if (are_types_identical(f->type, s)) {
+			Type *vt = dst->Record.variants[i];
+			if (are_types_identical(vt, s)) {
 				return 1;
 			}
 		}
@@ -980,41 +980,17 @@ void check_union_type(Checker *c, Type *named_type, Type *union_type, AstNode *n
 	ast_node(ut, UnionType, node);
 
 	isize variant_count = ut->variants.count+1;
-	isize min_field_count = 0;
-	for_array(i, ut->fields) {
-		AstNode *field = ut->fields[i];
-		switch (field->kind) {
-		case_ast_node(f, ValueDecl, field);
-			min_field_count += f->names.count;
-		case_end;
-		}
-	}
 
 	gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
 	defer (gb_temp_arena_memory_end(tmp));
 
-	Map<Entity *> entity_map = {}; // Key: String
-	map_init_with_reserve(&entity_map, c->tmp_allocator, 2*variant_count);
-
 	Entity *using_index_expr = nullptr;
 
-	Array<Entity *> variants = {};
-	array_init(&variants, heap_allocator(), variant_count);
-
-	array_add(&variants, make_entity_type_name(c->allocator, c->context.scope, empty_token, nullptr));
-
-	auto fields = check_fields(c, nullptr, ut->fields, min_field_count, str_lit("union"));
-
-	for (isize i = 0; i < fields.count; i++) {
-		Entity *f = fields[i];
-		String name = f->token.string;
-		map_set(&entity_map, hash_string(name), f);
-	}
+	Array<Type *> variants = {};
+	array_init(&variants, c->allocator, variant_count);
+	array_add(&variants, t_invalid);
 
 	union_type->Record.scope               = c->context.scope;
-	union_type->Record.fields              = fields.data;
-	union_type->Record.fields_in_src_order = fields.data;
-	union_type->Record.field_count         = fields.count;
 	union_type->Record.are_offsets_set     = false;
 	union_type->Record.is_ordered          = true;
 	{
@@ -1023,75 +999,21 @@ void check_union_type(Checker *c, Type *named_type, Type *union_type, AstNode *n
 	}
 
 	for_array(i, ut->variants) {
-		AstNode *variant = ut->variants[i];
-		if (variant->kind != AstNode_UnionField) {
-			continue;
-		}
-		ast_node(f, UnionField, variant);
-		Token name_token = f->name->Ident.token;
-
-		Type *base_type = make_type_struct(c->allocator);
-		{
-			ast_node(fl, FieldList, f->list);
-
-			// NOTE(bill): Copy the contents for the common fields for now
-			Array<AstNode *> list = {};
-			array_init_count(&list, c->allocator, ut->fields.count+fl->list.count);
-			gb_memmove_array(list.data, ut->fields.data, ut->fields.count);
-			gb_memmove_array(list.data+ut->fields.count, fl->list.data, fl->list.count);
-
-			isize list_count = 0;
-			for_array(j, list) {
-				if (list[j]->kind == AstNode_Field) {
-					list_count += list[j]->Field.names.count;
-				} else {
-					ast_node(f, ValueDecl, list[j]);
-					list_count += f->names.count;
+		AstNode *node = ut->variants[i];
+		Type *t = check_type(c, node);
+		if (t != nullptr && t != t_invalid) {
+			bool ok = true;
+			for_array(j, variants) {
+				if (are_types_identical(t, variants[j])) {
+					ok = false;
+					gbString str = type_to_string(t);
+					error(node, "Duplicate variant type `%s`", str);
+					gb_string_free(str);
+					break;
 				}
 			}
-
-
-			Token token = name_token;
-			token.kind = Token_struct;
-			AstNode *dummy_struct = ast_struct_type(c->curr_ast_file, token, list, list_count, false, true, nullptr);
-
-			check_open_scope(c, dummy_struct);
-			base_type->Record.names = make_names_field_for_record(c, c->context.scope);
-			auto fields = check_fields(c, dummy_struct, list, list_count, str_lit("variant"));
-			base_type->Record.is_packed           = false;
-			base_type->Record.is_ordered          = true;
-			base_type->Record.fields              = fields.data;
-			base_type->Record.fields_in_src_order = fields.data;
-			base_type->Record.field_count         = fields.count;
-			base_type->Record.node                = dummy_struct;
-			base_type->Record.variant_parent      = named_type != nullptr ? named_type : union_type;
-			base_type->Record.variant_index       = variants.count;
-
-
-			type_set_offsets(c->allocator, base_type);
-
-			check_close_scope(c);
-		}
-
-		Type *type = make_type_named(c->allocator, name_token.string, base_type, nullptr);
-		Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, type);
-		type->Named.type_name = e;
-		add_entity(c, c->context.scope, f->name, e);
-
-		if (name_token.string == "_") {
-			error(name_token, "`_` cannot be used a union subtype");
-			continue;
+			if (ok) array_add(&variants, t);
 		}
-
-		HashKey key = hash_string(name_token.string);
-		if (map_get(&entity_map, key) != nullptr) {
-			// NOTE(bill): Scope checking already checks the declaration
-			error(name_token, "`%.*s` is already declared in this union", LIT(name_token.string));
-		} else {
-			map_set(&entity_map, key, e);
-			array_add(&variants, e);
-		}
-		add_entity_use(c, f->name, e);
 	}
 
 	type_set_offsets(c->allocator, union_type);
@@ -6889,28 +6811,17 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t
 
 
 		bool src_is_ptr = is_type_pointer(o->type);
-		bool dst_is_ptr = is_type_pointer(t);
 		Type *src = type_deref(o->type);
-		Type *dst = type_deref(t);
+		Type *dst = t;
 		Type *bsrc = base_type(src);
 		Type *bdst = base_type(dst);
 
-		if (src_is_ptr != dst_is_ptr) {
-			gbString src_type_str = type_to_string(o->type);
-			gbString dst_type_str = type_to_string(t);
-			error(o->expr, "Invalid type assertion types: `%s` and `%s`", src_type_str, dst_type_str);
-			gb_string_free(dst_type_str);
-			gb_string_free(src_type_str);
-			o->mode = Addressing_Invalid;
-			o->expr = node;
-			return kind;
-		}
 
 		if (is_type_union(src)) {
 			bool ok = false;
 			for (isize i = 1; i < bsrc->Record.variant_count; i++) {
-				Entity *f = bsrc->Record.variants[i];
-				if (are_types_identical(f->type, dst)) {
+				Type *vt = bsrc->Record.variants[i];
+				if (are_types_identical(vt, dst)) {
 					ok = true;
 					break;
 				}
@@ -7088,6 +6999,7 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t
 
 		case Type_Slice:
 			valid = true;
+			o->type = type_deref(o->type);
 			break;
 
 		case Type_DynamicArray:
@@ -7602,7 +7514,7 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 	case_ast_node(st, UnionType, node);
 		str = gb_string_appendc(str, "union ");
 		str = gb_string_appendc(str, "{");
-		str = write_record_fields_to_string(str, st->fields);
+		str = write_record_fields_to_string(str, st->variants);
 		str = gb_string_appendc(str, "}");
 	case_end;
 

+ 2 - 2
src/check_stmt.cpp

@@ -1504,8 +1504,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 						GB_ASSERT(is_type_union(bt));
 						bool tag_type_found = false;
 						for (isize i = 0; i < bt->Record.variant_count; i++) {
-							Entity *f = bt->Record.variants[i];
-							if (are_types_identical(f->type, y.type)) {
+							Type *vt = bt->Record.variants[i];
+							if (are_types_identical(vt, y.type)) {
 								tag_type_found = true;
 								break;
 							}

+ 45 - 44
src/checker.cpp

@@ -1156,8 +1156,7 @@ void add_type_info_type(Checker *c, Type *t) {
 		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);
+				add_type_info_type(c, bt->Record.variants[i]);
 			}
 			/* fallthrough */
 		default:
@@ -1307,30 +1306,30 @@ Entity *find_core_entity(Checker *c, String name) {
 	return e;
 }
 
+Entity *find_sub_core_entity(TypeRecord *parent, String name) {
+	GB_ASSERT(parent->scope->parent->is_global);
+	Entity *e = current_scope_lookup_entity(parent->scope, name);
+	if (e == nullptr) {
+		compiler_error("Could not find type declaration for `%.*s`\n"
+		               "Is `_preload.odin` missing from the `core` directory relative to odin.exe?", LIT(name));
+		// NOTE(bill): This will exit the program as it's cannot continue without it!
+	}
+	return e;
+}
+
+void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type);
+
 void init_preload(Checker *c) {
 	if (t_type_info == nullptr) {
 		Entity *type_info_entity = find_core_entity(c, str_lit("TypeInfo"));
 
 		t_type_info = type_info_entity->type;
 		t_type_info_ptr = make_type_pointer(c->allocator, t_type_info);
-		GB_ASSERT(is_type_union(type_info_entity->type));
+		GB_ASSERT(is_type_struct(type_info_entity->type));
 		TypeRecord *record = &base_type(type_info_entity->type)->Record;
 
-		// Entity *type_info_record = current_scope_lookup_entity(record->scope, str_lit("Record"));
-		// if (type_info_record == nullptr) {
-		// 	compiler_error("Could not find type declaration for TypeInfo.Record\n"
-		// 	               "Is `_preload.odin` missing from the `core` directory relative to the odin executable?");
-		// }
-		// Entity *type_info_enum_value = current_scope_lookup_entity(record->scope, str_lit("EnumValue"));
-		// if (type_info_record == nullptr) {
-		// 	compiler_error("Could not find type declaration for TypeInfo.EnumValue\n"
-		// 	               "Is `_preload.odin` missing from the `core` directory relative to the odin executable?");
-		// }
-
-		// GB_ASSERT(type_info_record->type != nullptr);
-		// GB_ASSERT(type_info_enum_value->type != nullptr);
-		Entity *type_info_record     = find_core_entity(c, str_lit("TypeInfoRecord"));
-		Entity *type_info_enum_value = find_core_entity(c, str_lit("TypeInfoEnumValue"));
+		Entity *type_info_record     = find_sub_core_entity(record, str_lit("Record"));
+		Entity *type_info_enum_value = find_sub_core_entity(record, str_lit("EnumValue"));
 
 
 		t_type_info_record = type_info_record->type;
@@ -1338,33 +1337,38 @@ void init_preload(Checker *c) {
 		t_type_info_enum_value = type_info_enum_value->type;
 		t_type_info_enum_value_ptr = make_type_pointer(c->allocator, t_type_info_enum_value);
 
+		GB_ASSERT(record->field_count == 3);
 
+		Entity *type_info_variant = record->fields_in_src_order[2];
+		Type *tiv_type = type_info_variant->type;
+		GB_ASSERT(is_type_union(tiv_type));
+		TypeRecord *tiv = &tiv_type->Record;
 
-		if (record->variant_count != 23) {
+		if (tiv->variant_count != 23) {
 			compiler_error("Invalid `TypeInfo` layout");
 		}
-		t_type_info_named         = record->variants[ 1]->type;
-		t_type_info_integer       = record->variants[ 2]->type;
-		t_type_info_rune          = record->variants[ 3]->type;
-		t_type_info_float         = record->variants[ 4]->type;
-		t_type_info_complex       = record->variants[ 5]->type;
-		t_type_info_string        = record->variants[ 6]->type;
-		t_type_info_boolean       = record->variants[ 7]->type;
-		t_type_info_any           = record->variants[ 8]->type;
-		t_type_info_pointer       = record->variants[ 9]->type;
-		t_type_info_atomic        = record->variants[10]->type;
-		t_type_info_procedure     = record->variants[11]->type;
-		t_type_info_array         = record->variants[12]->type;
-		t_type_info_dynamic_array = record->variants[13]->type;
-		t_type_info_slice         = record->variants[14]->type;
-		t_type_info_vector        = record->variants[15]->type;
-		t_type_info_tuple         = record->variants[16]->type;
-		t_type_info_struct        = record->variants[17]->type;
-		t_type_info_raw_union     = record->variants[18]->type;
-		t_type_info_union         = record->variants[19]->type;
-		t_type_info_enum          = record->variants[20]->type;
-		t_type_info_map           = record->variants[21]->type;
-		t_type_info_bit_field     = record->variants[22]->type;
+		t_type_info_named         = tiv->variants[ 1];
+		t_type_info_integer       = tiv->variants[ 2];
+		t_type_info_rune          = tiv->variants[ 3];
+		t_type_info_float         = tiv->variants[ 4];
+		t_type_info_complex       = tiv->variants[ 5];
+		t_type_info_string        = tiv->variants[ 6];
+		t_type_info_boolean       = tiv->variants[ 7];
+		t_type_info_any           = tiv->variants[ 8];
+		t_type_info_pointer       = tiv->variants[ 9];
+		t_type_info_atomic        = tiv->variants[10];
+		t_type_info_procedure     = tiv->variants[11];
+		t_type_info_array         = tiv->variants[12];
+		t_type_info_dynamic_array = tiv->variants[13];
+		t_type_info_slice         = tiv->variants[14];
+		t_type_info_vector        = tiv->variants[15];
+		t_type_info_tuple         = tiv->variants[16];
+		t_type_info_struct        = tiv->variants[17];
+		t_type_info_raw_union     = tiv->variants[18];
+		t_type_info_union         = tiv->variants[19];
+		t_type_info_enum          = tiv->variants[20];
+		t_type_info_map           = tiv->variants[21];
+		t_type_info_bit_field     = tiv->variants[22];
 
 		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);
@@ -1719,9 +1723,6 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
 					}
 
 					Token token = name->Ident.token;
-					if (token.string == "EnumValue") {
-						gb_printf_err("EnumValue %p\n", name);
-					}
 
 					AstNode *fl = c->context.curr_foreign_library;
 					DeclInfo *d = make_declaration_info(c->allocator, c->context.scope, c->context.decl);

+ 128 - 95
src/ir.cpp

@@ -2355,7 +2355,7 @@ irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index) {
 
 	if (is_type_struct(t)) {
 		GB_ASSERT(t->Record.field_count > 0);
-		GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
+		GB_ASSERT_MSG(gb_is_between(index, 0, t->Record.field_count-1), "0..%d..%d", index, t->Record.field_count);
 		result_type = make_type_pointer(a, t->Record.fields[index]->type);
 	} else if (is_type_union(t)) {
 		type_set_offsets(a, t);
@@ -2950,12 +2950,12 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) {
 
 	if (is_type_union(dst)) {
 		for (isize i = 1; i < dst->Record.variant_count; i++) {
-			Entity *f = dst->Record.variants[i];
-			if (are_types_identical(f->type, src_type)) {
+			Type *vt = dst->Record.variants[i];
+			if (are_types_identical(vt, src_type)) {
 				ir_emit_comment(proc, str_lit("union - child to parent"));
 				gbAllocator a = proc->module->allocator;
 				irValue *parent = ir_add_local_generated(proc, t);
-				irValue *underlying = ir_emit_conv(proc, parent, make_type_pointer(a, f->type));
+				irValue *underlying = ir_emit_conv(proc, parent, make_type_pointer(a, vt));
 				ir_emit_store(proc, underlying, value);
 
 				irValue *tag_ptr = ir_emit_union_tag_ptr(proc, parent);
@@ -3207,6 +3207,7 @@ irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *type, Token
 
 	irValue *v = ir_add_local_generated(proc, tuple);
 
+	#if 0
 	if (is_ptr) {
 		Type *src = base_type(type_deref(src_type));
 		Type *src_ptr = src_type;
@@ -3217,8 +3218,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 = nullptr;
 		for (isize i = 1; i < src->Record.variant_count; i++) {
-			Entity *f = src->Record.variants[i];
-			if (are_types_identical(f->type, dst)) {
+			Type *vt = src->Record.variants[i];
+			if (are_types_identical(vt, dst)) {
 				dst_tag = ir_const_int(a, i);
 				break;
 			}
@@ -3252,8 +3253,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 = nullptr;
 		for (isize i = 1; i < src->Record.variant_count; i++) {
-			Entity *f = src->Record.variants[i];
-			if (are_types_identical(f->type, dst)) {
+			Type *vt = src->Record.variants[i];
+			if (are_types_identical(vt, dst)) {
 				dst_tag = ir_const_int(a, i);
 				break;
 			}
@@ -3276,7 +3277,44 @@ irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *type, Token
 		ir_emit_jump(proc, end_block);
 		ir_start_block(proc, end_block);
 	}
+	#else
+	if (is_ptr) {
+		value = ir_emit_load(proc, value);
+	}
+	Type *src = base_type(type_deref(src_type));
+	GB_ASSERT(is_type_union(src));
+	Type *dst = tuple->Tuple.variables[0]->type;
+	Type *dst_ptr = make_type_pointer(a, dst);
+
+	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 = nullptr;
+	for (isize i = 1; i < src->Record.variant_count; i++) {
+		Type *vt = src->Record.variants[i];
+		if (are_types_identical(vt, dst)) {
+			dst_tag = ir_const_int(a, i);
+			break;
+		}
+	}
+	GB_ASSERT(dst_tag != nullptr);
+
+	irBlock *ok_block = ir_new_block(proc, nullptr, "union_cast.ok");
+	irBlock *end_block = ir_new_block(proc, nullptr, "union_cast.end");
+	irValue *cond = ir_emit_comp(proc, Token_CmpEq, tag, dst_tag);
+	ir_emit_if(proc, cond, ok_block, end_block);
+	ir_start_block(proc, ok_block);
+
+	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, 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);
+	#endif
 	if (!is_tuple) {
 		// NOTE(bill): Panic on invalid conversion
 		Type *dst_type = tuple->Tuple.variables[0]->type;
@@ -6789,8 +6827,8 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 					Type *ut = base_type(type_deref(parent_type));
 					GB_ASSERT(ut->Record.kind == TypeRecord_Union);
 					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)) {
+						Type *vt = ut->Record.variants[variant_index];
+						if (are_types_identical(vt, bt)) {
 							index = ir_const_int(allocator, variant_index);
 							break;
 						}
@@ -7782,6 +7820,7 @@ void ir_gen_tree(irGen *s) {
 
 				irValue *tag = nullptr;
 				irValue *ti_ptr = ir_emit_array_epi(proc, ir_global_type_info_data, entry_index);
+				irValue *variant_ptr = ir_emit_struct_ep(proc, ti_ptr, 2);
 
 				ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 0), ir_const_int(a, type_size_of(a, t)));
 				ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 1), ir_const_int(a, type_align_of(a, t)));
@@ -7790,21 +7829,21 @@ void ir_gen_tree(irGen *s) {
 				switch (t->kind) {
 				case Type_Named: {
 					ir_emit_comment(proc, str_lit("TypeInfoNamed"));
-					tag = ir_emit_conv(proc, ti_ptr, t_type_info_named_ptr);
+					tag = ir_emit_conv(proc, variant_ptr, t_type_info_named_ptr);
 
 					// TODO(bill): Which is better? The mangled name or actual name?
 					irValue *name = ir_const_string(a, t->Named.type_name->token.string);
 					irValue *gtip = ir_get_type_info_ptr(proc, t->Named.base);
 
-					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), name);
-					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 3), gtip);
+					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), name);
+					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), gtip);
 				} break;
 
 				case Type_Basic:
 					ir_emit_comment(proc, str_lit("TypeInfoBasic"));
 					switch (t->Basic.kind) {
 					case Basic_bool:
-						tag = ir_emit_conv(proc, ti_ptr, t_type_info_boolean_ptr);
+						tag = ir_emit_conv(proc, variant_ptr, t_type_info_boolean_ptr);
 						break;
 
 					case Basic_i8:
@@ -7819,106 +7858,106 @@ void ir_gen_tree(irGen *s) {
 					case Basic_u128:
 					case Basic_int:
 					case Basic_uint: {
-						tag = ir_emit_conv(proc, ti_ptr, t_type_info_integer_ptr);
+						tag = ir_emit_conv(proc, variant_ptr, t_type_info_integer_ptr);
 						irValue *is_signed = ir_const_bool(a, (t->Basic.flags & BasicFlag_Unsigned) == 0);
-						ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), is_signed);
+						ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), is_signed);
 					} break;
 
 					case Basic_rune:
-						tag = ir_emit_conv(proc, ti_ptr, t_type_info_rune_ptr);
+						tag = ir_emit_conv(proc, variant_ptr, t_type_info_rune_ptr);
 						break;
 
 					// case Basic_f16:
 					case Basic_f32:
 					case Basic_f64:
-						tag = ir_emit_conv(proc, ti_ptr, t_type_info_float_ptr);
+						tag = ir_emit_conv(proc, variant_ptr, t_type_info_float_ptr);
 						break;
 
 					// case Basic_complex32:
 					case Basic_complex64:
 					case Basic_complex128:
-						tag = ir_emit_conv(proc, ti_ptr, t_type_info_complex_ptr);
+						tag = ir_emit_conv(proc, variant_ptr, t_type_info_complex_ptr);
 						break;
 
 					case Basic_rawptr:
-						tag = ir_emit_conv(proc, ti_ptr, t_type_info_pointer_ptr);
+						tag = ir_emit_conv(proc, variant_ptr, t_type_info_pointer_ptr);
 						break;
 
 					case Basic_string:
-						tag = ir_emit_conv(proc, ti_ptr, t_type_info_string_ptr);
+						tag = ir_emit_conv(proc, variant_ptr, t_type_info_string_ptr);
 						break;
 
 					case Basic_any:
-						tag = ir_emit_conv(proc, ti_ptr, t_type_info_any_ptr);
+						tag = ir_emit_conv(proc, variant_ptr, t_type_info_any_ptr);
 						break;
 					}
 					break;
 
 				case Type_Pointer: {
 					ir_emit_comment(proc, str_lit("TypeInfoPointer"));
-					tag = ir_emit_conv(proc, ti_ptr, t_type_info_pointer_ptr);
+					tag = ir_emit_conv(proc, variant_ptr, t_type_info_pointer_ptr);
 					irValue *gep = ir_get_type_info_ptr(proc, t->Pointer.elem);
-					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), gep);
+					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), gep);
 				} break;
 				case Type_Atomic: {
 					ir_emit_comment(proc, str_lit("TypeInfoAtomic"));
-					tag = ir_emit_conv(proc, ti_ptr, t_type_info_atomic_ptr);
+					tag = ir_emit_conv(proc, variant_ptr, t_type_info_atomic_ptr);
 					irValue *gep = ir_get_type_info_ptr(proc, t->Atomic.elem);
-					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), gep);
+					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), gep);
 				} break;
 				case Type_Array: {
 					ir_emit_comment(proc, str_lit("TypeInfoArray"));
-					tag = ir_emit_conv(proc, ti_ptr, t_type_info_array_ptr);
+					tag = ir_emit_conv(proc, variant_ptr, t_type_info_array_ptr);
 					irValue *gep = ir_get_type_info_ptr(proc, t->Array.elem);
-					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), gep);
+					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), gep);
 
 					isize ez = type_size_of(a, t->Array.elem);
-					irValue *elem_size = ir_emit_struct_ep(proc, tag, 3);
+					irValue *elem_size = ir_emit_struct_ep(proc, tag, 1);
 					ir_emit_store(proc, elem_size, ir_const_int(a, ez));
 
-					irValue *count = ir_emit_struct_ep(proc, tag, 4);
+					irValue *count = ir_emit_struct_ep(proc, tag, 2);
 					ir_emit_store(proc, count, ir_const_int(a, t->Array.count));
 
 				} break;
 				case Type_DynamicArray: {
 					ir_emit_comment(proc, str_lit("TypeInfoDynamicArray"));
-					tag = ir_emit_conv(proc, ti_ptr, t_type_info_dynamic_array_ptr);
+					tag = ir_emit_conv(proc, variant_ptr, t_type_info_dynamic_array_ptr);
 					irValue *gep = ir_get_type_info_ptr(proc, t->DynamicArray.elem);
-					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), gep);
+					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), gep);
 
 					isize ez = type_size_of(a, t->DynamicArray.elem);
-					irValue *elem_size = ir_emit_struct_ep(proc, tag, 3);
+					irValue *elem_size = ir_emit_struct_ep(proc, tag, 1);
 					ir_emit_store(proc, elem_size, ir_const_int(a, ez));
 				} break;
 				case Type_Slice: {
 					ir_emit_comment(proc, str_lit("TypeInfoSlice"));
-					tag = ir_emit_conv(proc, ti_ptr, t_type_info_slice_ptr);
+					tag = ir_emit_conv(proc, variant_ptr, t_type_info_slice_ptr);
 					irValue *gep = ir_get_type_info_ptr(proc, t->Slice.elem);
-					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), gep);
+					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), gep);
 
 					isize ez = type_size_of(a, t->Slice.elem);
-					irValue *elem_size = ir_emit_struct_ep(proc, tag, 3);
+					irValue *elem_size = ir_emit_struct_ep(proc, tag, 1);
 					ir_emit_store(proc, elem_size, ir_const_int(a, ez));
 				} break;
 				case Type_Vector: {
 					ir_emit_comment(proc, str_lit("TypeInfoVector"));
-					tag = ir_emit_conv(proc, ti_ptr, t_type_info_vector_ptr);
+					tag = ir_emit_conv(proc, variant_ptr, t_type_info_vector_ptr);
 					irValue *gep = ir_get_type_info_ptr(proc, t->Vector.elem);
-					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), gep);
+					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), gep);
 
 					isize ez = type_size_of(a, t->Vector.elem);
-					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 3), ir_const_int(a, ez));
-					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 4), ir_const_int(a, t->Vector.count));
+					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), ir_const_int(a, ez));
+					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), ir_const_int(a, t->Vector.count));
 
 				} break;
 				case Type_Proc: {
 					ir_emit_comment(proc, str_lit("TypeInfoProc"));
-					tag = ir_emit_conv(proc, ti_ptr, t_type_info_procedure_ptr);
+					tag = ir_emit_conv(proc, variant_ptr, t_type_info_procedure_ptr);
 
-					irValue *params     = ir_emit_struct_ep(proc, tag, 2);
-					irValue *results    = ir_emit_struct_ep(proc, tag, 3);
-					irValue *variadic   = ir_emit_struct_ep(proc, tag, 4);
-					irValue *convention = ir_emit_struct_ep(proc, tag, 5);
+					irValue *params     = ir_emit_struct_ep(proc, tag, 0);
+					irValue *results    = ir_emit_struct_ep(proc, tag, 1);
+					irValue *variadic   = ir_emit_struct_ep(proc, tag, 2);
+					irValue *convention = ir_emit_struct_ep(proc, tag, 3);
 
 					if (t->Proc.params != nullptr) {
 						ir_emit_store(proc, params, ir_get_type_info_ptr(proc, t->Proc.params));
@@ -7933,8 +7972,7 @@ void ir_gen_tree(irGen *s) {
 				} break;
 				case Type_Tuple: {
 					ir_emit_comment(proc, str_lit("TypeInfoTuple"));
-					tag = ir_emit_conv(proc, ti_ptr, t_type_info_tuple_ptr);
-					irValue *record = ir_emit_struct_ep(proc, tag, 2);
+					tag = ir_emit_conv(proc, variant_ptr, t_type_info_tuple_ptr);
 
 					irValue *memory_types = ir_type_info_member_types_offset(proc, t->Tuple.variable_count);
 					irValue *memory_names = ir_type_info_member_names_offset(proc, t->Tuple.variable_count);
@@ -7954,23 +7992,22 @@ void ir_gen_tree(irGen *s) {
 					}
 
 					irValue *count = ir_const_int(a, t->Tuple.variable_count);
-					ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 0), memory_types, count, count);
-					ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 1), memory_names, count, count);
+					ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 0), memory_types, count, count);
+					ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 1), memory_names, count, count);
 				} break;
 				case Type_Record: {
 					switch (t->Record.kind) {
 					case TypeRecord_Struct: {
 						ir_emit_comment(proc, str_lit("TypeInfoStruct"));
-						tag = ir_emit_conv(proc, ti_ptr, t_type_info_struct_ptr);
-						irValue *record = ir_emit_struct_ep(proc, tag, 2);
+						tag = ir_emit_conv(proc, variant_ptr, t_type_info_struct_ptr);
 
 						{
 							irValue *packed       = ir_const_bool(a, t->Record.is_packed);
 							irValue *ordered      = ir_const_bool(a, t->Record.is_ordered);
 							irValue *custom_align = ir_const_bool(a, t->Record.custom_align != 0);
-							ir_emit_store(proc, ir_emit_struct_ep(proc, record, 4), packed);
-							ir_emit_store(proc, ir_emit_struct_ep(proc, record, 5), ordered);
-							ir_emit_store(proc, ir_emit_struct_ep(proc, record, 6), custom_align);
+							ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 4), packed);
+							ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 5), ordered);
+							ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 6), custom_align);
 						}
 
 						irValue *memory_types   = ir_type_info_member_types_offset(proc, t->Record.field_count);
@@ -8001,17 +8038,17 @@ void ir_gen_tree(irGen *s) {
 						}
 
 						irValue *count = ir_const_int(a, t->Record.field_count);
-						ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 0), memory_types,   count, count);
-						ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 1), memory_names,   count, count);
-						ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 2), memory_offsets, count, count);
-						ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 3), memory_usings,  count, count);
+						ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 0), memory_types,   count, count);
+						ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 1), memory_names,   count, count);
+						ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 2), memory_offsets, count, count);
+						ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 3), memory_usings,  count, count);
 					} break;
 					case TypeRecord_Union: {
 						ir_emit_comment(proc, str_lit("TypeInfoUnion"));
-						tag = ir_emit_conv(proc, ti_ptr, t_type_info_union_ptr);
+						tag = ir_emit_conv(proc, variant_ptr, t_type_info_union_ptr);
 
 						{
-							irValue *common_fields = ir_emit_struct_ep(proc, tag, 2);
+							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);
@@ -8046,8 +8083,8 @@ void ir_gen_tree(irGen *s) {
 						}
 
 						{
-							irValue *variant_names = ir_emit_struct_ep(proc, tag, 3);
-							irValue *variant_types = ir_emit_struct_ep(proc, tag, 4);
+							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);
@@ -8055,17 +8092,12 @@ void ir_gen_tree(irGen *s) {
 
 							// 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);
+								Type *vt = t->Record.variants[variant_index+1]; // Skip zeroth
+								irValue *tip = ir_get_type_info_ptr(proc, vt);
 
 								irValue *index     = ir_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_const_string(a, f->token.string));
-								}
+								ir_emit_store(proc, type_info, ir_type_info(proc, vt));
 							}
 
 							irValue *count = ir_const_int(a, variant_count);
@@ -8076,8 +8108,7 @@ void ir_gen_tree(irGen *s) {
 					} break;
 					case TypeRecord_RawUnion: {
 						ir_emit_comment(proc, str_lit("TypeInfoRawUnion"));
-						tag = ir_emit_conv(proc, ti_ptr, t_type_info_raw_union_ptr);
-						irValue *record = ir_emit_struct_ep(proc, tag, 2);
+						tag = ir_emit_conv(proc, variant_ptr, t_type_info_raw_union_ptr);
 
 						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);
@@ -8097,17 +8128,17 @@ void ir_gen_tree(irGen *s) {
 						}
 
 						irValue *count = ir_const_int(a, t->Record.field_count);
-						ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 0), memory_types,   count, count);
-						ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 1), memory_names,   count, count);
-						ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 2), memory_offsets, count, count);
+						ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 0), memory_types,   count, count);
+						ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 1), memory_names,   count, count);
+						ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 2), memory_offsets, count, count);
 					} break;
 					case TypeRecord_Enum:
 						ir_emit_comment(proc, str_lit("TypeInfoEnum"));
-						tag = ir_emit_conv(proc, ti_ptr, t_type_info_enum_ptr);
+						tag = ir_emit_conv(proc, variant_ptr, t_type_info_enum_ptr);
 						{
 							GB_ASSERT(t->Record.enum_base_type != nullptr);
 							irValue *base = ir_type_info(proc, t->Record.enum_base_type);
-							ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), base);
+							ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), base);
 
 							if (t->Record.field_count > 0) {
 								Entity **fields = t->Record.fields;
@@ -8140,11 +8171,11 @@ void ir_gen_tree(irGen *s) {
 
 								irValue *v_count = ir_const_int(a, count);
 
-								irValue *names = ir_emit_struct_ep(proc, tag, 3);
+								irValue *names = ir_emit_struct_ep(proc, tag, 1);
 								irValue *name_array_elem = ir_array_elem(proc, name_array);
 								ir_fill_slice(proc, names, name_array_elem, v_count, v_count);
 
-								irValue *values = ir_emit_struct_ep(proc, tag, 4);
+								irValue *values = ir_emit_struct_ep(proc, tag, 2);
 								irValue *value_array_elem = ir_array_elem(proc, value_array);
 								ir_fill_slice(proc, values, value_array_elem, v_count, v_count);
 							}
@@ -8154,12 +8185,12 @@ void ir_gen_tree(irGen *s) {
 				} break;
 				case Type_Map: {
 					ir_emit_comment(proc, str_lit("TypeInfoMap"));
-					tag = ir_emit_conv(proc, ti_ptr, t_type_info_map_ptr);
+					tag = ir_emit_conv(proc, variant_ptr, t_type_info_map_ptr);
 
-					irValue *key              = ir_emit_struct_ep(proc, tag, 2);
-					irValue *value            = ir_emit_struct_ep(proc, tag, 3);
-					irValue *generated_struct = ir_emit_struct_ep(proc, tag, 4);
-					irValue *count            = ir_emit_struct_ep(proc, tag, 5);
+					irValue *key              = ir_emit_struct_ep(proc, tag, 0);
+					irValue *value            = ir_emit_struct_ep(proc, tag, 1);
+					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, t->Map.key));
 					ir_emit_store(proc, value,            ir_get_type_info_ptr(proc, t->Map.value));
@@ -8169,7 +8200,7 @@ void ir_gen_tree(irGen *s) {
 
 				case Type_BitField: {
 					ir_emit_comment(proc, str_lit("TypeInfoBitField"));
-					tag = ir_emit_conv(proc, ti_ptr, t_type_info_map_ptr);
+					tag = ir_emit_conv(proc, variant_ptr, t_type_info_map_ptr);
 					// names:   []string,
 					// bits:    []u32,
 					// offsets: []u32,
@@ -8196,15 +8227,15 @@ void ir_gen_tree(irGen *s) {
 
 						irValue *v_count = ir_const_int(a, count);
 
-						irValue *names = ir_emit_struct_ep(proc, tag, 3);
+						irValue *names = ir_emit_struct_ep(proc, tag, 1);
 						irValue *name_array_elem = ir_array_elem(proc, name_array);
 						ir_fill_slice(proc, names, name_array_elem, v_count, v_count);
 
-						irValue *bits = ir_emit_struct_ep(proc, tag, 4);
+						irValue *bits = ir_emit_struct_ep(proc, tag, 2);
 						irValue *bit_array_elem = ir_array_elem(proc, bit_array);
 						ir_fill_slice(proc, bits, bit_array_elem, v_count, v_count);
 
-						irValue *offsets = ir_emit_struct_ep(proc, tag, 5);
+						irValue *offsets = ir_emit_struct_ep(proc, tag, 3);
 						irValue *offset_array_elem = ir_array_elem(proc, offset_array);
 						ir_fill_slice(proc, offsets, offset_array_elem, v_count, v_count);
 					}
@@ -8216,18 +8247,20 @@ void ir_gen_tree(irGen *s) {
 					Type *tag_type = type_deref(ir_type(tag));
 					GB_ASSERT(is_type_named(tag_type));
 					Type *ti = base_type(t_type_info);
+					Type *tiv = base_type(ti->Record.fields_in_src_order[2]->type);
+					GB_ASSERT(is_type_union(tiv));
 					bool found = false;
-					for (isize i = 1; i < ti->Record.variant_count; i++) {
-						Entity *f = ti->Record.variants[i];
-						if (are_types_identical(f->type, tag_type)) {
+					for (isize i = 1; i < tiv->Record.variant_count; i++) {
+						Type *vt = tiv->Record.variants[i];
+						if (are_types_identical(vt, tag_type)) {
 							found = true;
-							irValue *tag = ir_const_int(a, i);
-							irValue *ptr = ir_emit_union_tag_ptr(proc, ti_ptr);
-							ir_emit_store(proc, ptr, tag);
+							irValue *tag_val = ir_const_int(a, i);
+							irValue *ptr = ir_emit_union_tag_ptr(proc, variant_ptr);
+							ir_emit_store(proc, ptr, tag_val);
 							break;
 						}
 					}
-					GB_ASSERT_MSG(found, "%s", type_to_string(tag_type));
+					GB_ASSERT_MSG(found, "Tag type not found: %s", type_to_string(tag_type));
 				} else {
 					GB_PANIC("Unhandled TypeInfo type: %s", type_to_string(t));
 				}

+ 7 - 45
src/parser.cpp

@@ -425,8 +425,6 @@ AST_NODE_KIND(_TypeBegin, "", i32) \
 	}) \
 	AST_NODE_KIND(UnionType, "union type", struct { \
 		Token            token;       \
-		Array<AstNode *> fields;      \
-		isize            field_count; \
 		Array<AstNode *> variants;    \
 	}) \
 	AST_NODE_KIND(RawUnionType, "raw union type", struct { \
@@ -864,7 +862,6 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) {
 		n->StructType.align  = clone_ast_node(a, n->StructType.align);
 		break;
 	case AstNode_UnionType:
-		n->UnionType.fields   = clone_ast_node_array(a, n->UnionType.fields);
 		n->UnionType.variants = clone_ast_node_array(a, n->UnionType.variants);
 		break;
 	case AstNode_RawUnionType:
@@ -1459,11 +1456,9 @@ AstNode *ast_struct_type(AstFile *f, Token token, Array<AstNode *> fields, isize
 }
 
 
-AstNode *ast_union_type(AstFile *f, Token token, Array<AstNode *> fields, isize field_count, Array<AstNode *> variants) {
+AstNode *ast_union_type(AstFile *f, Token token, Array<AstNode *> 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;
 }
@@ -3707,7 +3702,6 @@ 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");
-		Array<AstNode *> decls    = make_ast_node_array(f);
 		Array<AstNode *> variants = make_ast_node_array(f);
 		isize total_decl_name_count = 0;
 
@@ -3717,50 +3711,18 @@ AstNode *parse_type_or_ident(AstFile *f) {
 
 		while (f->curr_token.kind != Token_CloseBrace &&
 		       f->curr_token.kind != Token_EOF) {
-			CommentGroup docs = f->lead_comment;
-			u32 flags = parse_field_prefixes(f);
-			auto names = parse_ident_list(f);
-			if (names.count == 0) {
-				break;
+			AstNode *type = parse_type(f);
+			if (type->kind != AstNode_BadExpr) {
+				array_add(&variants, type);
 			}
-			u32 set_flags = check_field_prefixes(f, names.count, FieldFlag_using, flags);
-			if (names.count == 1 && f->curr_token.kind == Token_OpenBrace) {
-				if (set_flags != 0) {
-					error(names[0], "Variants cannot have field prefixes");
-					set_flags = 0;
-				}
-				AstNode *name  = names[0];
-				Token    open  = expect_token(f, Token_OpenBrace);
-				isize decl_count = 0;
-				AstNode *list = parse_record_field_list(f, &decl_count);
-				Token    close = expect_token(f, Token_CloseBrace);
-
-				array_add(&variants, ast_union_field(f, name, list));
-				expect_semicolon(f, nullptr);
-			} else {
-				AstNode *decl = parse_value_decl(f, names, docs);
-				if (decl->kind != AstNode_ValueDecl) {
-					error(decl, "Expected a field list, got %.*s", LIT(ast_node_strings[decl->kind]));
-				} else {
-					ast_node(vd, ValueDecl, decl);
-					if (vd->is_mutable) {
-						if (set_flags&FieldFlag_using) {
-							vd->flags |= VarDeclFlag_using;
-						}
-						if (vd->flags&VarDeclFlag_thread_local) {
-							vd->flags &= ~VarDeclFlag_thread_local;
-							error(decl, "Field values cannot be #thread_local");
-						}
-					}
-					array_add(&decls, decl);
-					total_decl_name_count += vd->names.count;
-				}
+			if (!allow_token(f, Token_Comma)) {
+				break;
 			}
 		}
 
 		Token close = expect_token(f, Token_CloseBrace);
 
-		return ast_union_type(f, token, decls, total_decl_name_count, variants);
+		return ast_union_type(f, token, variants);
 	} break;
 
 	case Token_raw_union: {

+ 8 - 38
src/types.cpp

@@ -93,7 +93,7 @@ struct TypeRecord {
 	Scope *  scope;
 
 	// Entity_TypeName - union
-	Entity **variants;
+	Type **  variants;
 	i32      variant_count;
 	Entity * union__tag;
 	i64      variant_block_size; // NOTE(bill): Internal use only
@@ -1002,7 +1002,7 @@ bool is_type_polymorphic(Type *t) {
 		    }
 		}
 		for (isize i = 1; i < t->Record.variant_count; i++) {
-		    if (is_type_polymorphic(t->Record.variants[i]->type)) {
+		    if (is_type_polymorphic(t->Record.variants[i])) {
 		    	return true;
 		    }
 		}
@@ -1163,10 +1163,7 @@ bool are_types_identical(Type *x, Type *y) {
 						}
 						// NOTE(bill): zeroth variant is nullptr
 						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 (x->Record.variants[i]->token.string != y->Record.variants[i]->token.string) {
+							if (!are_types_identical(x->Record.variants[i], y->Record.variants[i])) {
 								return false;
 							}
 						}
@@ -1555,19 +1552,7 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
 			}
 		}
 
-		if (is_type_union(type)) {
-			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 == field_name) {
-					sel.entity = f;
-					// selection_add_index(&sel, i);
-					return sel;
-				}
-			}
-		} else if (is_type_enum(type)) {
+		if (is_type_enum(type)) {
 			// NOTE(bill): These may not have been added yet, so check in case
 			if (type->Record.enum_count != nullptr) {
 				if (field_name == "count") {
@@ -1880,7 +1865,7 @@ i64 type_align_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
 			}
 			// NOTE(bill): field zero is null
 			for (isize i = 1; i < t->Record.variant_count; i++) {
-				Type *variant = t->Record.variants[i]->type;
+				Type *variant = t->Record.variants[i];
 				type_path_push(path, variant);
 				if (path->failure) {
 					return FAILURE_ALIGNMENT;
@@ -2139,7 +2124,7 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
 			i64 field_size = max;
 
 			for (isize i = 1; i < variant_count; i++) {
-				Type *variant_type = t->Record.variants[i]->type;
+				Type *variant_type = t->Record.variants[i];
 				i64 size = type_size_of_internal(allocator, variant_type, path);
 				if (max < size) {
 					max = size;
@@ -2369,26 +2354,11 @@ gbString write_type_to_string(gbString str, Type *type) {
 				str = write_type_to_string(str, f->type);
 			}
 			for (isize i = 1; i < type->Record.variant_count; i++) {
-				Entity *f = type->Record.variants[i];
-				GB_ASSERT(f->kind == Entity_TypeName);
+				Type *t = type->Record.variants[i];
 				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 = write_type_to_string(str, t);
 			}
 			str = gb_string_appendc(str, "}");
 			break;