浏览代码

Start implementing Tagged Unions

Ginger Bill 9 年之前
父节点
当前提交
7f884ed251
共有 7 个文件被更改,包括 296 次插入133 次删除
  1. 10 52
      examples/demo.odin
  2. 0 6
      examples/game.odin
  3. 141 50
      src/checker/expr.cpp
  4. 74 16
      src/checker/type.cpp
  5. 7 3
      src/codegen/print_llvm.cpp
  6. 29 0
      src/codegen/ssa.cpp
  7. 35 6
      src/parser.cpp

+ 10 - 52
examples/demo.odin

@@ -3,62 +3,20 @@
 #load "game.odin"
 
 main :: proc() {
-
-	print_ints :: proc(args: ..int) {
-		for i := 0; i < len(args); i++ {
-			print_int(args[i])
-			nl()
+	Entity :: type union {
+		FROG: struct {
+			jump_height: f32
+		}
+		HELICOPTER: struct {
+			weight:     f32
+			blade_code: int
 		}
-	}
-	// print_ints()
-	// print_ints(1)
-	print_ints(1, 2, 3, 4, 5)
-
-	// print_int(min(1, 2)); nl()
-	// print_int(max(1, 2)); nl()
-	// print_int(abs(-1337)); nl()
-
-	// a, b, c := 1, 2, -1337
-
-	// print_int(min(a, b)); nl()
-	// print_int(max(a, b)); nl()
-	// print_int(abs(c) as int); nl()
-
-	// nl()
-/*
-	Vec3   :: type struct { x, y, z: f32 }
-	Entity :: type struct {
-		using pos: Vec3
-		name:      string
-	}
-
-	Amp :: type struct {
-		using entity: Entity
-		jump_height:  f32
-	}
-	Frog :: type struct {
-		using amp: Amp
-		volume: f64
-	}
-
-	f := Frog{};
-	f.name = "ribbit"
-	f.jump_height = 1337
-
-	e := ^f.entity
-	parent := e down_cast ^Frog
-
-	print_name :: proc(using e: Entity, v: Vec3) {
-		print_string(name); nl()
-		print_int(v.x as int); nl()
 	}
 
-	print_f32(f.jump_height); nl()
-	print_f32(parent.jump_height); nl()
+	e: Entity
+	f: Entity = Entity.FROG{1};
+	h: Entity = Entity.HELICOPTER{123, 4};
 
-	print_name(f, Vec3{1, 2, 3})
-	print_name(parent, Vec3{3, 2, 1})
-*/
 }
 
 nl :: proc() { print_nl() }

+ 0 - 6
examples/game.odin

@@ -122,12 +122,6 @@ display_window :: proc(w: ^Window) {
 }
 
 
-Entity :: type struct {
-	pos: Vec2
-	dim: Vec2
-}
-
-
 run_game :: proc() {
 	win32_proc :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #no_inline {
 		if msg == WM_DESTROY || msg == WM_CLOSE || msg == WM_QUIT {

+ 141 - 50
src/checker/expr.cpp

@@ -53,12 +53,16 @@ b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type, b32 is_argu
 
 	Type *s = operand->type;
 
-	if (are_types_identical(s, type))
+
+
+	if (are_types_identical(s, type)) {
 		return true;
+	}
 
 	Type *src = get_base_type(s);
 	Type *dst = get_base_type(type);
 
+
 	if (is_type_untyped(src)) {
 		switch (dst->kind) {
 		case Type_Basic:
@@ -72,8 +76,12 @@ b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type, b32 is_argu
 		}
 	}
 
-	if (are_types_identical(dst, src) && (!is_type_named(dst) || !is_type_named(src)))
+	if (are_types_identical(dst, src) && (!is_type_named(dst) || !is_type_named(src))) {
+		if (is_type_enum(dst) && is_type_enum(src))  {
+			return are_types_identical(s, type);
+		}
 		return true;
+	}
 
 	if (is_type_pointer(dst) && is_type_rawptr(src))
 	    return true;
@@ -93,6 +101,15 @@ b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type, b32 is_argu
 		}
 	}
 
+	if (is_type_union(dst)) {
+		for (isize i = 0; i < dst->Record.field_count; i++) {
+			Entity *f = dst->Record.fields[i];
+			if (are_types_identical(f->type, s)) {
+				return true;
+			}
+		}
+	}
+
 	if (is_argument) {
 		// NOTE(bill): Polymorphism for subtyping
 		if (check_is_assignable_to_using_subtype(type, src)) {
@@ -270,64 +287,87 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
 
 	}
 
-	isize field_index = 0;
-	for (AstNode *decl = decl_list; decl != NULL; decl = decl->next) {
-		if (decl->kind != AstNode_VarDecl)
-			continue;
-		ast_node(vd, VarDecl, decl);
-		if (vd->kind != Declaration_Mutable)
-			continue;
-		Type *type = check_type(c, vd->type, NULL, cycle_checker);
+	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 (AstNode *decl = decl_list; decl != NULL; decl = decl->next) {
+			if (decl->kind != AstNode_VarDecl)
+				continue;
+			ast_node(vd, VarDecl, decl);
+			if (vd->kind != Declaration_Mutable)
+				continue;
+			Type *type = check_type(c, vd->type, NULL, cycle_checker);
 
-		if (vd->is_using) {
-			if (vd->name_count > 1) {
-				error(&c->error_collector, ast_node_token(vd->name_list),
-				      "Cannot apply `using` to more than one of the same type");
+			for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
+				Token name_token = name->Ident;
+
+				Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, type);
+				HashKey key = hash_string(name_token.string);
+				if (map_get(&entity_map, key) != NULL) {
+					// TODO(bill): Scope checking already checks the declaration
+					error(&c->error_collector, name_token, "`%.*s` is already declared in this union", LIT(name_token.string));
+				} else {
+					map_set(&entity_map, key, e);
+					fields[field_index++] = e;
+					add_entity(c, c->context.scope, name, e);
+				}
+				add_entity_use(&c->info, name, e);
 			}
 		}
+	} else {
+		isize field_index = 0;
+		for (AstNode *decl = decl_list; decl != NULL; decl = decl->next) {
+			if (decl->kind != AstNode_VarDecl)
+				continue;
+			ast_node(vd, VarDecl, decl);
+			if (vd->kind != Declaration_Mutable)
+				continue;
+			Type *type = check_type(c, vd->type, NULL, cycle_checker);
 
-		for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
-			Token name_token = name->Ident;
-
-			Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, vd->is_using);
-			HashKey key = hash_string(name_token.string);
-			if (map_get(&entity_map, key) != NULL) {
-				// TODO(bill): Scope checking already checks the declaration
-				error(&c->error_collector, name_token, "`%.*s` is already declared in this structure", LIT(name_token.string));
-			} else {
-				map_set(&entity_map, key, e);
-				fields[field_index++] = e;
-				add_entity(c, c->context.scope, name, e);
+			if (vd->is_using) {
+				if (vd->name_count > 1) {
+					error(&c->error_collector, ast_node_token(vd->name_list),
+					      "Cannot apply `using` to more than one of the same type");
+				}
 			}
-			add_entity_use(&c->info, name, e);
-		}
 
+			for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
+				Token name_token = name->Ident;
 
-		if (vd->is_using) {
-			Type *t = get_base_type(type_deref(type));
-			if (!is_type_struct(t) && !is_type_raw_union(t)) {
-				Token name_token = vd->name_list->Ident;
-				error(&c->error_collector, name_token, "`using` on a field `%.*s` must be a structure or union", LIT(name_token.string));
-				continue;
+				Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, vd->is_using);
+				HashKey key = hash_string(name_token.string);
+				if (map_get(&entity_map, key) != NULL) {
+					// TODO(bill): Scope checking already checks the declaration
+					error(&c->error_collector, name_token, "`%.*s` is already declared in this type", LIT(name_token.string));
+				} else {
+					map_set(&entity_map, key, e);
+					fields[field_index++] = e;
+					add_entity(c, c->context.scope, name, e);
+				}
+				add_entity_use(&c->info, name, e);
 			}
 
-			populate_using_entity_map(c, node, type, &entity_map);
-		}
-	}
-
 
+			if (vd->is_using) {
+				Type *t = get_base_type(type_deref(type));
+				if (!is_type_struct(t) && !is_type_raw_union(t)) {
+					Token name_token = vd->name_list->Ident;
+					error(&c->error_collector, name_token, "`using` on a field `%.*s` must be a type", LIT(name_token.string));
+					continue;
+				}
 
+				populate_using_entity_map(c, node, type, &entity_map);
+			}
+		}
+	}
 }
 
 
 void check_struct_type(Checker *c, Type *struct_type, AstNode *node, CycleChecker *cycle_checker) {
-	GB_ASSERT(node->kind == AstNode_StructType);
 	GB_ASSERT(is_type_struct(struct_type));
 	ast_node(st, StructType, node);
 
-	// TODO(bill): check_struct_type and check_union_type are very similar so why not and try to merge them better
-
-isize field_count = 0;
+	isize field_count = 0;
 	isize other_field_count = 0;
 	for (AstNode *decl = st->decl_list; decl != NULL; decl = decl->next) {
 		switch (decl->kind) {
@@ -357,6 +397,39 @@ isize field_count = 0;
 	struct_type->Record.other_field_count = other_field_count;
 }
 
+void check_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker *cycle_checker) {
+	GB_ASSERT(is_type_union(union_type));
+	ast_node(ut, UnionType, node);
+
+	isize field_count = 1;
+	isize other_field_count = 0;
+	for (AstNode *decl = ut->decl_list; decl != NULL; decl = decl->next) {
+		switch (decl->kind) {
+		case_ast_node(vd, VarDecl, decl);
+			if (vd->kind == Declaration_Mutable) {
+				field_count += vd->name_count;
+			} else {
+				other_field_count += vd->name_count;
+			}
+		case_end;
+
+		case_ast_node(td, TypeDecl, decl);
+			other_field_count += 1;
+		case_end;
+		}
+	}
+
+	Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count);
+	Entity **other_fields = gb_alloc_array(c->allocator, Entity *, other_field_count);
+
+	check_fields(c, node, ut->decl_list, fields, field_count, other_fields, other_field_count, cycle_checker, make_string("union"));
+
+	union_type->Record.fields            = fields;
+	union_type->Record.field_count       = field_count;
+	union_type->Record.other_fields      = other_fields;
+	union_type->Record.other_field_count = other_field_count;
+}
+
 void check_raw_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker *cycle_checker) {
 	GB_ASSERT(node->kind == AstNode_RawUnionType);
 	GB_ASSERT(is_type_raw_union(union_type));
@@ -383,7 +456,7 @@ void check_raw_union_type(Checker *c, Type *union_type, AstNode *node, CycleChec
 	Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count);
 	Entity **other_fields = gb_alloc_array(c->allocator, Entity *, other_field_count);
 
-	check_fields(c, node, ut->decl_list, fields, field_count, other_fields, other_field_count, cycle_checker, make_string("union"));
+	check_fields(c, node, ut->decl_list, fields, field_count, other_fields, other_field_count, cycle_checker, make_string("raw union"));
 
 	union_type->Record.fields = fields;
 	union_type->Record.field_count = field_count;
@@ -392,7 +465,7 @@ void check_raw_union_type(Checker *c, Type *union_type, AstNode *node, CycleChec
 }
 
 
-void check_enum_type(Checker *c, Type *enum_type, AstNode *node) {
+void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *node) {
 	GB_ASSERT(node->kind == AstNode_EnumType);
 	GB_ASSERT(is_type_enum(enum_type));
 	ast_node(et, EnumType, node);
@@ -443,7 +516,11 @@ void check_enum_type(Checker *c, Type *enum_type, AstNode *node) {
 			iota = exact_binary_operator_value(add_token, iota, make_exact_value_integer(1));
 		}
 
-		Entity *e = make_entity_constant(c->allocator, c->context.scope, name_token, enum_type, iota);
+		Type *constant_type = enum_type;
+		if (named_type != NULL) {
+			constant_type = named_type;
+		}
+		Entity *e = make_entity_constant(c->allocator, c->context.scope, name_token, constant_type, iota);
 
 		HashKey key = hash_string(name_token.string);
 		if (map_get(&entity_map, key)) {
@@ -764,7 +841,17 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_c
 		goto end;
 	case_end;
 
-	case_ast_node(st, RawUnionType, e);
+	case_ast_node(ut, UnionType, e);
+		type = make_type_union(c->allocator);
+		set_base_type(named_type, type);
+		check_open_scope(c, e);
+		check_union_type(c, type, e, cycle_checker);
+		check_close_scope(c);
+		type->Record.node = e;
+		goto end;
+	case_end;
+
+	case_ast_node(rut, RawUnionType, e);
 		type = make_type_raw_union(c->allocator);
 		set_base_type(named_type, type);
 		check_open_scope(c, e);
@@ -777,7 +864,7 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_c
 	case_ast_node(et, EnumType, e);
 		type = make_type_enum(c->allocator);
 		set_base_type(named_type, type);
-		check_enum_type(c, type, e);
+		check_enum_type(c, type, named_type, e);
 		type->Record.node = e;
 		goto end;
 	case_end;
@@ -1096,6 +1183,7 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
 
 	if (err_str != NULL) {
 		error(&c->error_collector, op, "Cannot compare expression, %s", err_str);
+		x->type = t_untyped_bool;
 		return;
 	}
 
@@ -1215,10 +1303,13 @@ b32 check_castable_to(Checker *c, Operand *operand, Type *y) {
 		return true;
 
 	Type *x = operand->type;
-	Type *xb = get_enum_base_type(get_base_type(x));
-	Type *yb = get_enum_base_type(get_base_type(y));
-	if (are_types_identical(xb, yb))
+	Type *xb = get_base_type(x);
+	Type *yb = get_base_type(y);
+	if (are_types_identical(xb, yb)) {
 		return true;
+	}
+	xb = get_enum_base_type(x);
+	yb = get_enum_base_type(y);
 
 
 	// Cast between booleans and integers

+ 74 - 16
src/checker/type.cpp

@@ -154,6 +154,8 @@ struct Type {
 	};
 };
 
+gbString type_to_string(Type *type, gbAllocator a = gb_heap_allocator());
+
 Type *get_base_type(Type *t) {
 	for (;;) {
 		if (t == NULL || t->kind != Type_Named) {
@@ -210,6 +212,12 @@ Type *make_type_struct(gbAllocator a) {
 	return t;
 }
 
+Type *make_type_union(gbAllocator a) {
+	Type *t = alloc_type(a, Type_Record);
+	t->Record.kind = TypeRecord_Union;
+	return t;
+}
+
 Type *make_type_raw_union(gbAllocator a) {
 	Type *t = alloc_type(a, Type_Record);
 	t->Record.kind = TypeRecord_RawUnion;
@@ -453,6 +461,8 @@ Type *base_vector_type(Type *t) {
 	}
 	return t;
 }
+
+
 b32 is_type_enum(Type *t) {
 	t = get_base_type(t);
 	return (t->kind == Type_Record && t->Record.kind == TypeRecord_Enum);
@@ -461,6 +471,10 @@ b32 is_type_struct(Type *t) {
 	t = get_base_type(t);
 	return (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct);
 }
+b32 is_type_union(Type *t) {
+	t = get_base_type(t);
+	return (t->kind == Type_Record && t->Record.kind == TypeRecord_Union);
+}
 b32 is_type_raw_union(Type *t) {
 	t = get_base_type(t);
 	return (t->kind == Type_Record && t->Record.kind == TypeRecord_RawUnion);
@@ -484,7 +498,8 @@ b32 is_type_comparable(Type *t) {
 	case Type_Pointer:
 		return true;
 	case Type_Record: {
-		if (is_type_struct(t)) {
+		if (false && is_type_struct(t)) {
+			// TODO(bill): Should I even allow this?
 			for (isize i = 0; i < t->Record.field_count; i++) {
 				if (!is_type_comparable(t->Record.fields[i]->type))
 					return false;
@@ -492,7 +507,7 @@ b32 is_type_comparable(Type *t) {
 		} else if (is_type_enum(t)) {
 			return is_type_comparable(t->Record.enum_base);
 		}
-		return true;
+		return false;
 	} break;
 	case Type_Array:
 		return is_type_comparable(t->Array.elem);
@@ -547,9 +562,10 @@ b32 are_types_identical(Type *x, Type *y) {
 								return false;
 							}
 						}
-
 						return true;
 					}
+					break;
+
 				case TypeRecord_Enum:
 					return are_types_identical(x->Record.enum_base, y->Record.enum_base);
 				}
@@ -563,8 +579,9 @@ b32 are_types_identical(Type *x, Type *y) {
 		break;
 
 	case Type_Named:
-		if (y->kind == Type_Named)
+		if (y->kind == Type_Named) {
 			return x->Named.base == y->Named.base;
+		}
 		break;
 
 	case Type_Tuple:
@@ -673,24 +690,29 @@ Selection lookup_field(Type *type_, String field_name, b32 is_type, Selection se
 		return sel;
 	}
 	if (is_type) {
+
+		if (is_type_union(type)) {
+			for (isize i = 0; i < type->Record.field_count; i++) {
+				Entity *f = type->Record.fields[i];
+				GB_ASSERT(f->kind == Entity_TypeName);
+				String str = f->token.string;
+
+				if (are_strings_equal(field_name, str)) {
+					return make_selection(f, NULL, i);
+				}
+			}
+		}
+
 		for (isize i = 0; i < type->Record.other_field_count; i++) {
 			Entity *f = type->Record.other_fields[i];
 			GB_ASSERT(f->kind != Entity_Variable);
 			String str = f->token.string;
 
 			if (are_strings_equal(field_name, str)) {
-				if (is_type_enum(type)) {
-					GB_ASSERT(f->kind == Entity_Constant);
-					// NOTE(bill): Enums are constant expression
-					return make_selection(f, NULL, i);
-				} else {
-					selection_add_index(&sel, i);
-					sel.entity = f;
-					return sel;
-				}
+				return make_selection(f, NULL, i);
 			}
 		}
-	} else if (!is_type_enum(type)) {
+	} else if (!is_type_enum(type) && !is_type_union(type)) {
 		for (isize i = 0; i < type->Record.field_count; i++) {
 			Entity *f = type->Record.fields[i];
 			GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field);
@@ -761,6 +783,16 @@ i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
 				return max;
 			}
 			break;
+		case TypeRecord_Union: {
+			i64 max = s.word_size;
+			for (isize i = 1; i < t->Record.field_count; i++) {
+				// NOTE(bill): field zero is null
+				i64 align = type_align_of(s, allocator, t->Record.fields[i]->type);
+				if (max < align)
+					max = align;
+			}
+			return max;
+		} break;
 		case TypeRecord_RawUnion: {
 			i64 max = 1;
 			for (isize i = 0; i < t->Record.field_count; i++) {
@@ -868,6 +900,18 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
 			return t->Record.struct_offsets[count-1] + type_size_of(s, allocator, t->Record.fields[count-1]->type);
 		} break;
 
+		case TypeRecord_Union: {
+			i64 count = t->Record.field_count;
+			i64 max = 0;
+			// NOTE(bill): Zeroth field is invalid
+			for (isize i = 1; i < count; i++) {
+				i64 size = type_size_of(s, allocator, t->Record.fields[i]->type);
+				if (max < size)
+					max = size;
+			}
+			return type_size_of(s, allocator, t_int) + max;
+		} break;
+
 		case TypeRecord_RawUnion: {
 			i64 count = t->Record.field_count;
 			i64 max = 0;
@@ -900,7 +944,6 @@ i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, isize index)
 	return 0;
 }
 
-gbString type_to_string(Type *type, gbAllocator a = gb_heap_allocator());
 
 i64 type_offset_of_from_selection(BaseTypeSizes s, gbAllocator allocator, Type *t, Selection sel) {
 	i64 offset = 0;
@@ -952,7 +995,22 @@ gbString write_type_to_string(gbString str, Type *type) {
 				Entity *f = type->Record.fields[i];
 				GB_ASSERT(f->kind == Entity_Variable);
 				if (i > 0)
-					str = gb_string_appendc(str, ", ");
+					str = gb_string_appendc(str, "; ");
+				str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
+				str = gb_string_appendc(str, ": ");
+				str = write_type_to_string(str, f->type);
+			}
+			str = gb_string_appendc(str, "}");
+			break;
+
+		case TypeRecord_Union:
+			str = gb_string_appendc(str, "union{");
+			for (isize i = 0; i < type->Record.field_count; i++) {
+				Entity *f = type->Record.fields[i];
+				GB_ASSERT(f->kind == Entity_TypeName);
+				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);

+ 7 - 3
src/codegen/print_llvm.cpp

@@ -158,6 +158,10 @@ void ssa_print_type(gbFile *f, BaseTypeSizes s, Type *t) {
 				ssa_fprintf(f, ">");
 			}
 			break;
+		case TypeRecord_Union: {
+			i64 size_of_union = type_size_of(s, gb_heap_allocator(), t) - s.word_size;
+			ssa_fprintf(f, "{i%lld, [%lld x i8]}", word_bits, size_of_union);
+		} break;
 		case TypeRecord_RawUnion:
 			ssa_fprintf(f, "[%lld x i8]", type_size_of(s, gb_heap_allocator(), t));
 			break;
@@ -172,7 +176,7 @@ void ssa_print_type(gbFile *f, BaseTypeSizes s, Type *t) {
 		ssa_fprintf(f, "*");
 		break;
 	case Type_Named:
-		if (is_type_struct(t)) {
+		if (is_type_struct(t) || is_type_union(t)) {
 			ssa_print_encoded_local(f, t->Named.name);
 		} else {
 			ssa_print_type(f, s, get_base_type(t));
@@ -454,7 +458,6 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
 		ssa_fprintf(f, "%%%d = ", value->id);
 
 		if (gb_is_between(bo->op.kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1)) {
-
 			if (is_type_string(elem_type)) {
 				ssa_fprintf(f, "call ");
 				ssa_print_type(f, m->sizes, t_bool);
@@ -756,8 +759,9 @@ void ssa_print_proc(gbFile *f, ssaModule *m, ssaProcedure *proc) {
 void ssa_print_type_name(gbFile *f, ssaModule *m, ssaValue *v) {
 	GB_ASSERT(v->kind == ssaValue_TypeName);
 	Type *base_type = get_base_type(ssa_type(v));
-	if (!is_type_struct(base_type))
+	if (!is_type_struct(base_type) && !is_type_union(base_type)) {
 		return;
+	}
 	ssa_print_encoded_local(f, v->TypeName.name);
 	ssa_fprintf(f, " = type ");
 	ssa_print_type(f, m->sizes, get_base_type(v->TypeName.type));

+ 29 - 0
src/codegen/ssa.cpp

@@ -1432,6 +1432,34 @@ ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t, b32 is_arg
 		return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_inttoptr, value, src, dst));
 	}
 
+	if (is_type_union(dst)) {
+		for (isize i = 0; i < dst->Record.field_count; i++) {
+			Entity *f = dst->Record.fields[i];
+			if (are_types_identical(f->type, src_type)) {
+				gbAllocator allocator = proc->module->allocator;
+				ssaValue *parent = ssa_add_local_generated(proc, t);
+				ssaValue *tag = ssa_make_value_constant(allocator, t_int, make_exact_value_integer(i));
+				ssa_emit_store(proc, ssa_emit_struct_gep(proc, parent, v_zero32, t_int), tag);
+
+				i64 int_size = proc->module->sizes.word_size;
+				i64 underlying_array_size = type_size_of(proc->module->sizes, allocator, ssa_type(parent));
+				underlying_array_size -= int_size;
+				Type *array_type = make_type_array(allocator, t_u8, underlying_array_size);
+				ssaValue *data = ssa_emit_struct_gep(proc, parent, v_one32, array_type);
+				data = ssa_array_elem(proc, data);
+
+				Type *tag_type = src_type;
+				Type *t_u8_ptr = make_type_pointer(allocator, t_u8);
+				Type *tag_type_ptr = make_type_pointer(allocator, tag_type);
+				ssaValue *underlying = ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_bitcast, data, t_u8_ptr, tag_type_ptr));
+				// underlying = ssa_emit_zero_gep(proc, underlying);
+				// ssa_set_type(underlying, src);
+				// ssa_emit_store(proc, underlying, value);
+
+				return ssa_emit_load(proc, parent);
+			}
+		}
+	}
 
 	// NOTE(bill): This has to be done beofre `Pointer <-> Pointer` as it's
 	// subtype polymorphism casting
@@ -1660,6 +1688,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 		case Token_GtEq: {
 			ssaValue *left  = ssa_build_expr(proc, be->left);
 			ssaValue *right = ssa_build_expr(proc, be->right);
+
 			ssaValue *cmp = ssa_emit_comp(proc, be->op, left, right);
 			return ssa_emit_conv(proc, cmp, default_type(tv->type));
 		} break;

+ 35 - 6
src/parser.cpp

@@ -240,6 +240,11 @@ AST_NODE_KIND(_TypeBegin, "", struct{}) \
 		isize decl_count; \
 		b32 is_packed; \
 	}) \
+	AST_NODE_KIND(UnionType, "union type", struct { \
+		Token token; \
+		AstNode *decl_list; \
+		isize decl_count; \
+	}) \
 	AST_NODE_KIND(RawUnionType, "raw union type", struct { \
 		Token token; \
 		AstNode *decl_list; \
@@ -395,6 +400,8 @@ Token ast_node_token(AstNode *node) {
 		return node->VectorType.token;
 	case AstNode_StructType:
 		return node->StructType.token;
+	case AstNode_UnionType:
+		return node->UnionType.token;
 	case AstNode_RawUnionType:
 		return node->RawUnionType.token;
 	case AstNode_EnumType:
@@ -815,6 +822,15 @@ gb_inline AstNode *make_struct_type(AstFile *f, Token token, AstNode *decl_list,
 	return result;
 }
 
+
+gb_inline AstNode *make_union_type(AstFile *f, Token token, AstNode *decl_list, isize decl_count) {
+	AstNode *result = make_node(f, AstNode_UnionType);
+	result->UnionType.token = token;
+	result->UnionType.decl_list = decl_list;
+	result->UnionType.decl_count = decl_count;
+	return result;
+}
+
 gb_inline AstNode *make_raw_union_type(AstFile *f, Token token, AstNode *decl_list, isize decl_count) {
 	AstNode *result = make_node(f, AstNode_RawUnionType);
 	result->RawUnionType.token = token;
@@ -1674,8 +1690,7 @@ AstNode *parse_field_decl(AstFile *f) {
 			type = make_bad_expr(f, ellipsis, f->cursor[0]);
 		} else {
 			if (name_count > 1) {
-				ast_file_err(f, f->cursor[0], "mutliple variadic parameters, only 1 is allowed");
-				type = make_bad_expr(f, ellipsis, f->cursor[0]);
+				ast_file_err(f, f->cursor[0], "mutliple variadic parameters, only  `..`");
 			} else {
 				type = make_ellipsis(f, ellipsis, type);
 			}
@@ -1711,7 +1726,7 @@ AstNode *parse_parameter_list(AstFile *f, isize *param_count_) {
 }
 
 
-AstNode *parse_struct_params(AstFile *f, isize *decl_count_) {
+AstNode *parse_struct_params(AstFile *f, isize *decl_count_, b32 using_allowed) {
 	AstNode *decls = NULL;
 	AstNode *decls_curr = NULL;
 	isize decl_count = 0;
@@ -1728,6 +1743,10 @@ AstNode *parse_struct_params(AstFile *f, isize *decl_count_) {
 			ast_file_err(f, f->cursor[0], "Empty field declaration");
 		}
 
+		if (!using_allowed && is_using) {
+			ast_file_err(f, f->cursor[0], "Cannot apply `using` to members of a union");
+			is_using = false;
+		}
 		if (name_count > 1 && is_using) {
 			ast_file_err(f, f->cursor[0], "Cannot apply `using` to more than one of the same type");
 		}
@@ -1752,7 +1771,7 @@ AstNode *parse_struct_params(AstFile *f, isize *decl_count_) {
 			DLIST_APPEND(decls, decls_curr, decl);
 			if (decl->kind == AstNode_VarDecl) {
 				decl_count += decl->VarDecl.name_count;
-				decl->VarDecl.is_using = is_using;
+				decl->VarDecl.is_using = is_using && using_allowed;
 
 				if (decl->VarDecl.kind == Declaration_Mutable) {
 					if (decl->VarDecl.value_count > 0) {
@@ -1829,17 +1848,27 @@ AstNode *parse_identifier_or_type(AstFile *f) {
 
 		Token open = expect_token(f, Token_OpenBrace);
 		isize decl_count = 0;
-		AstNode *decls = parse_struct_params(f, &decl_count);
+		AstNode *decls = parse_struct_params(f, &decl_count, true);
 		Token close = expect_token(f, Token_CloseBrace);
 
 		return make_struct_type(f, token, decls, decl_count, is_packed);
 	} break;
 
+	case Token_union: {
+		Token token = expect_token(f, Token_union);
+		Token open = expect_token(f, Token_OpenBrace);
+		isize decl_count = 0;
+		AstNode *decls = parse_struct_params(f, &decl_count, false);
+		Token close = expect_token(f, Token_CloseBrace);
+
+		return make_union_type(f, token, decls, decl_count);
+	}
+
 	case Token_raw_union: {
 		Token token = expect_token(f, Token_raw_union);
 		Token open = expect_token(f, Token_OpenBrace);
 		isize decl_count = 0;
-		AstNode *decls = parse_struct_params(f, &decl_count);
+		AstNode *decls = parse_struct_params(f, &decl_count, true);
 		Token close = expect_token(f, Token_CloseBrace);
 
 		return make_raw_union_type(f, token, decls, decl_count);