Browse Source

Match statements; Type System change (Type_Record for all sum and product types)

Ginger Bill 9 years ago
parent
commit
fa09d805e2

+ 26 - 8
examples/demo.odin

@@ -3,6 +3,26 @@
 #load "game.odin"
 
 main :: proc() {
+
+
+	match x := "1"; x {
+	case "1":
+		print_string("1!\n")
+	case "2":
+		print_string("2!\n")
+		if true {
+			break
+		}
+	case "3", "4":
+		print_string("3 or 4!\n")
+		fallthrough
+	default:
+		print_string("default!\n")
+	}
+
+	nl()
+
+/*
 	Vec3   :: type struct { x, y, z: f32 }
 	Entity :: type struct {
 		using pos: Vec3
@@ -19,11 +39,11 @@ main :: proc() {
 	}
 
 	f := Frog{};
-	f.name = "ribbit";
-	f.jump_height = 1337;
+	f.name = "ribbit"
+	f.jump_height = 1337
 
-	e := ^f.entity;
-	parent := e down_cast ^Frog;
+	e := ^f.entity
+	parent := e down_cast ^Frog
 
 	print_name :: proc(using e: Entity, v: Vec3) {
 		print_string(name); nl()
@@ -35,10 +55,8 @@ main :: proc() {
 
 	print_name(f, Vec3{1, 2, 3})
 	print_name(parent, Vec3{3, 2, 1})
-
-
+*/
 }
 
-
-nl :: proc() #inline { print_nl() }
+nl :: proc() { print_nl() }
 

+ 5 - 4
examples/runtime.odin

@@ -365,13 +365,14 @@ __default_allocator_proc :: proc(allocator_data: rawptr, mode: AllocationMode,
                                  size, alignment: int,
                                  old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
 	using AllocationMode
-	if mode == ALLOC {
+	match mode {
+	case ALLOC:
 		return heap_alloc(size)
-	} else if mode == RESIZE {
+	case RESIZE:
 		return default_resize_align(old_memory, old_size, size, alignment)
-	} else if mode == DEALLOC {
+	case DEALLOC:
 		heap_free(old_memory)
-	} else if mode == DEALLOC_ALL {
+	case DEALLOC_ALL:
 		// NOTE(bill): Does nothing
 	}
 

+ 93 - 16
src/checker/checker.cpp

@@ -339,6 +339,7 @@ Entity *scope_insert_entity(Scope *s, Entity *entity) {
 	return NULL;
 }
 
+
 void add_dependency(DeclInfo *d, Entity *e) {
 	map_set(&d->deps, hash_pointer(e), cast(b32)true);
 }
@@ -543,6 +544,7 @@ b32 add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
 				      LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column);
 				return false;
 			} else {
+				gb_printf_err("!!Here\n");
 				error(&c->error_collector, entity->token,
 				      "Redeclararation of `%.*s` in this scope\n"
 				      "\tat %.*s(%td:%td)",
@@ -557,6 +559,78 @@ b32 add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
 	return true;
 }
 
+
+/*
+b32 add_proc_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
+	GB_ASSERT(entity->kind == Entity_Procedure);
+
+	auto error_proc_redecl = [](Checker *c, Token token, Entity *other_entity, char *extra_msg) {
+		error(&c->error_collector, token,
+		      "Redeclararation of `%.*s` in this scope %s\n"
+		      "\tat %.*s(%td:%td)",
+		      LIT(other_entity->token.string),
+		      extra_msg,
+		      LIT(other_entity->token.pos.file), other_entity->token.pos.line, other_entity->token.pos.column);
+	};
+
+	String name = entity->token.string;
+	HashKey key = hash_string(name);
+
+	b32 insert_overload = false;
+
+	if (!are_strings_equal(name, make_string("_"))) {
+		Entity *insert_entity = scope_insert_entity(scope, entity);
+		if (insert_entity != NULL) {
+			if (insert_entity != entity) {
+				isize count = multi_map_count(&scope->elements, key);
+				GB_ASSERT(count > 0);
+				Entity **entities = gb_alloc_array(gb_heap_allocator(), Entity *, count);
+				defer (gb_free(gb_heap_allocator(), entities));
+				multi_map_get_all(&scope->elements, key, entities);
+
+				for (isize i = 0; i < count; i++) {
+					Entity *e = entities[i];
+					if (e == entity) {
+						continue;
+					}
+					if (e->kind == Entity_Procedure) {
+						Type *proc_type = entity->type;
+						Type *other_proc_type = e->type;
+						// gb_printf_err("%s == %s\n", type_to_string(proc_type), type_to_string(other_proc_type));
+						if (are_types_identical(proc_type, other_proc_type)) {
+							error_proc_redecl(c, entity->token, e, "with identical types");
+							return false;
+						}
+
+						if (proc_type != NULL && other_proc_type != NULL) {
+							Type *params = proc_type->Proc.params;
+							Type *other_params = other_proc_type->Proc.params;
+
+							if (are_types_identical(params, other_params)) {
+								error_proc_redecl(c, entity->token, e, "with 2identical parameters");
+								return false;
+							}
+						}
+					} else {
+						error_proc_redecl(c, entity->token, e, "");
+						return false;
+					}
+				}
+				insert_overload = true;
+			}
+		}
+	}
+
+	if (insert_overload) {
+		multi_map_insert(&scope->elements, key, entity);
+	}
+
+	if (identifier != NULL)
+		add_entity_definition(&c->info, identifier, entity);
+	return true;
+}
+*/
+
 void add_entity_use(CheckerInfo *i, AstNode *identifier, Entity *entity) {
 	GB_ASSERT(identifier != NULL);
 	GB_ASSERT(identifier->kind == AstNode_Ident);
@@ -566,7 +640,7 @@ void add_entity_use(CheckerInfo *i, AstNode *identifier, Entity *entity) {
 
 
 void add_file_entity(Checker *c, AstNode *identifier, Entity *e, DeclInfo *d) {
-	GB_ASSERT(are_strings_equal(identifier->Ident.token.string, e->token.string));
+	GB_ASSERT(are_strings_equal(identifier->Ident.string, e->token.string));
 
 	add_entity(c, c->global_scope, identifier, e);
 	map_set(&c->info.entities, hash_pointer(e), d);
@@ -608,7 +682,6 @@ void add_curr_ast_file(Checker *c, AstFile *file) {
 
 
 
-
 #include "expr.cpp"
 #include "stmt.cpp"
 
@@ -658,9 +731,8 @@ void check_parsed_files(Checker *c) {
 					for (AstNode *name = vd->name_list, *value = vd->value_list;
 					     name != NULL && value != NULL;
 					     name = name->next, value = value->next) {
-						ast_node(n, Ident, name);
 						ExactValue v = {ExactValue_Invalid};
-						Entity *e = make_entity_constant(c->allocator, c->context.scope, n->token, NULL, v);
+						Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v);
 						DeclInfo *di = make_declaration_info(c->allocator, c->global_scope);
 						di->type_expr = vd->type;
 						di->init_expr = value;
@@ -692,8 +764,7 @@ void check_parsed_files(Checker *c) {
 
 					AstNode *value = vd->value_list;
 					for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
-						ast_node(n, Ident, name);
-						Entity *e = make_entity_variable(c->allocator, c->global_scope, n->token, NULL);
+						Entity *e = make_entity_variable(c->allocator, c->global_scope, name->Ident, NULL);
 						entities[entity_index++] = e;
 
 						DeclInfo *d = di;
@@ -716,7 +787,7 @@ void check_parsed_files(Checker *c) {
 
 			case_ast_node(td, TypeDecl, decl);
 				ast_node(n, Ident, td->name);
-				Entity *e = make_entity_type_name(c->allocator, c->global_scope, n->token, NULL);
+				Entity *e = make_entity_type_name(c->allocator, c->global_scope, *n, NULL);
 				DeclInfo *d = make_declaration_info(c->allocator, e->scope);
 				d->type_expr = td->type;
 				add_file_entity(c, td->name, e, d);
@@ -724,9 +795,8 @@ void check_parsed_files(Checker *c) {
 
 			case_ast_node(pd, ProcDecl, decl);
 				ast_node(n, Ident, pd->name);
-				Token token = n->token;
+				Token token = *n;
 				Entity *e = make_entity_procedure(c->allocator, c->global_scope, token, NULL);
-				add_entity(c, c->global_scope, pd->name, e);
 				DeclInfo *d = make_declaration_info(c->allocator, e->scope);
 				d->proc_decl = decl;
 				map_set(&c->info.entities, hash_pointer(e), d);
@@ -747,14 +817,21 @@ void check_parsed_files(Checker *c) {
 		}
 	}
 
+	auto check_global_entity = [](Checker *c, EntityKind kind) {
+		gb_for_array(i, c->info.entities.entries) {
+			auto *entry = &c->info.entities.entries[i];
+			Entity *e = cast(Entity *)cast(uintptr)entry->key.key;
+			if (e->kind == kind) {
+				DeclInfo *d = entry->value;
+				check_entity_decl(c, e, d, NULL);
+			}
+		}
+	};
 
-	gb_for_array(i, c->info.entities.entries) {
-		auto *entry = &c->info.entities.entries[i];
-		Entity *e = cast(Entity *)cast(uintptr)entry->key.key;
-		DeclInfo *d = entry->value;
-		check_entity_decl(c, e, d, NULL);
-	}
-
+	check_global_entity(c, Entity_TypeName);
+	check_global_entity(c, Entity_Constant);
+	check_global_entity(c, Entity_Procedure);
+	check_global_entity(c, Entity_Variable);
 
 	// Check procedure bodies
 	gb_for_array(i, c->procs) {

+ 1 - 0
src/checker/entity.cpp

@@ -27,6 +27,7 @@ String const entity_strings[] = {
 
 
 typedef i64 EntityGuid;
+typedef struct Type Type;
 
 struct Entity {
 	EntityKind kind;

+ 78 - 86
src/checker/expr.cpp

@@ -23,9 +23,9 @@ b32 check_is_assignable_to_using_subtype(Type *dst, Type *src) {
 	b32 src_is_ptr = src != prev_src;
 	// b32 dst_is_ptr = dst != prev_dst;
 
-	if (src->kind == Type_Struct) {
-		for (isize i = 0; i < src->Struct.field_count; i++) {
-			Entity *f = src->Struct.fields[i];
+	if (is_type_struct(src)) {
+		for (isize i = 0; i < src->Record.field_count; i++) {
+			Entity *f = src->Record.fields[i];
 			if (f->kind == Entity_Variable && f->Variable.anonymous) {
 				if (are_types_identical(dst, f->type)) {
 					return true;
@@ -152,13 +152,9 @@ void populate_using_entity_map(Checker *c, AstNode *node, Type *t, Map<Entity *>
 	gbString str = expr_to_string(node);
 	defer (gb_string_free(str));
 
-	switch (t->kind) {
-	// IMPORTANT HACK(bill): The positions of fields and field_count
-	// must be same for Struct and Union
-	case Type_Struct:
-	case Type_Union:
-		for (isize i = 0; i < t->Struct.field_count; i++) {
-			Entity *f = t->Struct.fields[i];
+	if (t->kind == Type_Record) {
+		for (isize i = 0; i < t->Record.field_count; i++) {
+			Entity *f = t->Record.fields[i];
 			GB_ASSERT(f->kind == Entity_Variable);
 			String name = f->token.string;
 			HashKey key = hash_string(name);
@@ -175,7 +171,6 @@ void populate_using_entity_map(Checker *c, AstNode *node, Type *t, Map<Entity *>
 				}
 			}
 		}
-		break;
 	}
 
 }
@@ -215,8 +210,7 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
 			     name = name->next, value = value->next) {
 				GB_ASSERT(name->kind == AstNode_Ident);
 				ExactValue v = {ExactValue_Invalid};
-				ast_node(i, Ident, name);
-				Token name_token = i->token;
+				Token name_token = name->Ident;
 				Entity *e = make_entity_constant(c->allocator, c->context.scope, name_token, NULL, v);
 				entities[entity_index++] = e;
 				check_const_decl(c, e, vd->type, value);
@@ -235,7 +229,7 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
 			AstNode *name = vd->name_list;
 			for (isize i = 0; i < entity_count; i++, name = name->next) {
 				Entity *e = entities[i];
-				Token name_token = name->Ident.token;
+				Token name_token = name->Ident;
 				HashKey key = hash_string(name_token.string);
 				if (map_get(&entity_map, key) != NULL) {
 					// TODO(bill): Scope checking already checks the declaration
@@ -248,10 +242,9 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
 			}
 		} else if (decl->kind == AstNode_TypeDecl) {
 			ast_node(td, TypeDecl, decl);
-			ast_node(name, Ident, td->name);
-			Token name_token = name->token;
+			Token name_token = td->name->Ident;
 
-			Entity *e = make_entity_type_name(c->allocator, c->context.scope, name->token, NULL);
+			Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, NULL);
 			check_type_decl(c, e, td->type, NULL, NULL);
 			add_entity(c, c->context.scope, td->name, e);
 
@@ -285,8 +278,7 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
 		}
 
 		for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
-			ast_node(i, Ident, name);
-			Token name_token = i->token;
+			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);
@@ -304,9 +296,8 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
 
 		if (vd->is_using) {
 			Type *t = get_base_type(type_deref(type));
-			if (t->kind != Type_Struct &&
-			    t->kind != Type_Union) {
-				Token name_token = vd->name_list->Ident.token;
+			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;
 			}
@@ -322,12 +313,12 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
 
 void check_struct_type(Checker *c, Type *struct_type, AstNode *node, CycleChecker *cycle_checker) {
 	GB_ASSERT(node->kind == AstNode_StructType);
-	GB_ASSERT(struct_type->kind == Type_Struct);
+	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) {
@@ -350,17 +341,17 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node, CycleChecke
 
 	check_fields(c, node, st->decl_list, fields, field_count, other_fields, other_field_count, cycle_checker, make_string("struct"));
 
-	struct_type->Struct.is_packed         = st->is_packed;
-	struct_type->Struct.fields            = fields;
-	struct_type->Struct.field_count       = field_count;
-	struct_type->Struct.other_fields      = other_fields;
-	struct_type->Struct.other_field_count = other_field_count;
+	struct_type->Record.struct_is_packed  = st->is_packed;
+	struct_type->Record.fields            = fields;
+	struct_type->Record.field_count       = field_count;
+	struct_type->Record.other_fields      = other_fields;
+	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(node->kind == AstNode_UnionType);
-	GB_ASSERT(union_type->kind == Type_Union);
-	ast_node(ut, UnionType, node);
+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));
+	ast_node(ut, RawUnionType, node);
 
 	isize field_count = 0;
 	isize other_field_count = 0;
@@ -385,16 +376,16 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker
 
 	check_fields(c, node, ut->decl_list, fields, field_count, other_fields, other_field_count, cycle_checker, make_string("union"));
 
-	union_type->Union.fields = fields;
-	union_type->Union.field_count = field_count;
-	union_type->Union.other_fields = other_fields;
-	union_type->Union.other_field_count = other_field_count;
+	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_enum_type(Checker *c, Type *enum_type, AstNode *node) {
 	GB_ASSERT(node->kind == AstNode_EnumType);
-	GB_ASSERT(enum_type->kind == Type_Enum);
+	GB_ASSERT(is_type_enum(enum_type));
 	ast_node(et, EnumType, node);
 
 	Map<Entity *> entity_map = {};
@@ -413,14 +404,14 @@ void check_enum_type(Checker *c, Type *enum_type, AstNode *node) {
 	if (base_type == NULL) {
 		base_type = t_int;
 	}
-	enum_type->Enum.base = base_type;
+	enum_type->Record.enum_base = base_type;
 
 	Entity **fields = gb_alloc_array(c->allocator, Entity *, et->field_count);
 	isize field_index = 0;
 	ExactValue iota = make_exact_value_integer(-1);
 	for (AstNode *field = et->field_list; field != NULL; field = field->next) {
 		ast_node(f, FieldValue, field);
-		Token name_token = f->field->Ident.token;
+		Token name_token = f->field->Ident;
 
 		Operand o = {};
 		if (f->value != NULL) {
@@ -455,8 +446,8 @@ void check_enum_type(Checker *c, Type *enum_type, AstNode *node) {
 		}
 		add_entity_use(&c->info, f->field, e);
 	}
-	enum_type->Enum.fields = fields;
-	enum_type->Enum.field_count = et->field_count;
+	enum_type->Record.other_fields = fields;
+	enum_type->Record.other_field_count = et->field_count;
 }
 
 Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize field_count) {
@@ -474,8 +465,7 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize fiel
 			Type *type = check_type(c, type_expr);
 			for (AstNode *name = f->name_list; name != NULL; name = name->next) {
 				if (name->kind == AstNode_Ident) {
-					ast_node(i, Ident, name);
-					Entity *param = make_entity_param(c->allocator, scope, i->token, type, f->is_using);
+					Entity *param = make_entity_param(c->allocator, scope, name->Ident, type, f->is_using);
 					add_entity(c, scope, name, param);
 					variables[variable_index++] = param;
 				} else {
@@ -536,11 +526,10 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Cycl
 	GB_ASSERT(n->kind == AstNode_Ident);
 	o->mode = Addressing_Invalid;
 	o->expr = n;
-	ast_node(i, Ident, n);
-	Entity *e = scope_lookup_entity(c, c->context.scope, i->token.string);
+	Entity *e = scope_lookup_entity(c, c->context.scope, n->Ident.string);
 	if (e == NULL) {
-		error(&c->error_collector, i->token,
-		    "Undeclared type or identifier `%.*s`", LIT(i->token.string));
+		error(&c->error_collector, n->Ident,
+		    "Undeclared type or identifier `%.*s`", LIT(n->Ident.string));
 		return;
 	}
 	add_entity_use(&c->info, n, e);
@@ -563,7 +552,7 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Cycl
 	}
 
 	if (e->type == NULL) {
-		GB_PANIC("Compiler error: How did this happen? type: %s; identifier: %.*s\n", type_to_string(e->type), LIT(i->token.string));
+		GB_PANIC("Compiler error: How did this happen? type: %s; identifier: %.*s\n", type_to_string(e->type), LIT(n->Ident.string));
 		return;
 	}
 
@@ -739,17 +728,17 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_c
 		check_open_scope(c, e);
 		check_struct_type(c, type, e, cycle_checker);
 		check_close_scope(c);
-		type->Struct.node = e;
+		type->Record.node = e;
 		goto end;
 	case_end;
 
-	case_ast_node(st, UnionType, e);
-		type = make_type_union(c->allocator);
+	case_ast_node(st, RawUnionType, e);
+		type = make_type_raw_union(c->allocator);
 		set_base_type(named_type, type);
 		check_open_scope(c, e);
-		check_union_type(c, type, e, cycle_checker);
+		check_raw_union_type(c, type, e, cycle_checker);
 		check_close_scope(c);
-		type->Union.node = e;
+		type->Record.node = e;
 		goto end;
 	case_end;
 
@@ -757,6 +746,7 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_c
 		type = make_type_enum(c->allocator);
 		set_base_type(named_type, type);
 		check_enum_type(c, type, e);
+		type->Record.node = e;
 		goto end;
 	case_end;
 
@@ -1249,10 +1239,9 @@ String check_down_cast_name(Type *dst_, Type *src_) {
 	Type *dst = type_deref(dst_);
 	Type *src = type_deref(src_);
 	Type *dst_s = get_base_type(dst);
-	GB_ASSERT(dst_s->kind == Type_Struct || dst_s->kind == Type_Union);
-	// HACK(bill): struct/union variable overlay from unsafe tagged union
-	for (isize i = 0; i < dst_s->Struct.field_count; i++) {
-		Entity *f = dst_s->Struct.fields[i];
+	GB_ASSERT(is_type_struct(dst_s) || is_type_raw_union(dst_s));
+	for (isize i = 0; i < dst_s->Record.field_count; i++) {
+		Entity *f = dst_s->Record.fields[i];
 		GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field);
 		if (f->Variable.anonymous) {
 			if (are_types_identical(f->type, src_)) {
@@ -1397,7 +1386,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 		Type *bsrc = get_base_type(src);
 		Type *bdst = get_base_type(dst);
 
-		if (!(bsrc->kind == Type_Struct || bsrc->kind == Type_Union)) {
+		if (!(is_type_struct(bsrc) || is_type_raw_union(bsrc))) {
 			gbString expr_str = expr_to_string(node);
 			defer (gb_string_free(expr_str));
 			error(&c->error_collector, ast_node_token(node), "Can only `down_cast` pointer from structs or unions: `%s`", expr_str);
@@ -1405,7 +1394,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 			return;
 		}
 
-		if (!(bdst->kind == Type_Struct || bdst->kind == Type_Union)) {
+		if (!(is_type_struct(bdst) || is_type_struct(bdst))) {
 			gbString expr_str = expr_to_string(node);
 			defer (gb_string_free(expr_str));
 			error(&c->error_collector, ast_node_token(node), "Can only `down_cast` pointer to structs or unions: `%s`", expr_str);
@@ -1740,7 +1729,7 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) {
 	AstNode *op_expr  = se->expr;
 	AstNode *selector = se->selector;
 	if (selector) {
-		Entity *entity = lookup_field(operand->type, selector->Ident.token.string, operand->mode == Addressing_Type).entity;
+		Entity *entity = lookup_field(operand->type, selector->Ident.string, operand->mode == Addressing_Type).entity;
 		if (entity == NULL) {
 			gbString op_str   = expr_to_string(op_expr);
 			gbString type_str = type_to_string(operand->type);
@@ -1787,7 +1776,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		if (err) {
 			ast_node(proc, Ident, ce->proc);
 			error(&c->error_collector, ce->close, "`%s` arguments for `%.*s`, expected %td, got %td",
-			      err, LIT(proc->token.string),
+			      err, LIT(proc->string),
 			      bp->arg_count, ce->arg_list_count);
 			return false;
 		}
@@ -1931,7 +1920,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		Type *type = get_base_type(check_type(c, ce->arg_list));
 		AstNode *field_arg = unparen_expr(ce->arg_list->next);
 		if (type) {
-			if (type->kind != Type_Struct) {
+			if (!is_type_struct(type)) {
 				error(&c->error_collector, ast_node_token(ce->arg_list), "Expected a structure type for `offset_of`");
 				return false;
 			}
@@ -1944,11 +1933,11 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 
 
 		ast_node(arg, Ident, field_arg);
-		Selection sel = lookup_field(type, arg->token.string, operand->mode == Addressing_Type);
+		Selection sel = lookup_field(type, arg->string, operand->mode == Addressing_Type);
 		if (sel.entity == NULL) {
 			gbString type_str = type_to_string(type);
 			error(&c->error_collector, ast_node_token(ce->arg_list),
-			      "`%s` has no field named `%.*s`", type_str, LIT(arg->token.string));
+			      "`%s` has no field named `%.*s`", type_str, LIT(arg->string));
 			return false;
 		}
 
@@ -1975,17 +1964,18 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		Type *type = operand->type;
 		if (get_base_type(type)->kind == Type_Pointer) {
 			Type *p = get_base_type(type);
-			if (get_base_type(p)->kind == Type_Struct)
+			if (is_type_struct(p)) {
 				type = p->Pointer.elem;
+			}
 		}
 
 
 		ast_node(i, Ident, s->selector);
-		Selection sel = lookup_field(type, i->token.string, operand->mode == Addressing_Type);
+		Selection sel = lookup_field(type, i->string, operand->mode == Addressing_Type);
 		if (sel.entity == NULL) {
 			gbString type_str = type_to_string(type);
 			error(&c->error_collector, ast_node_token(arg),
-			      "`%s` has no field named `%.*s`", type_str, LIT(i->token.string));
+			      "`%s` has no field named `%.*s`", type_str, LIT(i->string));
 			return false;
 		}
 
@@ -2357,7 +2347,6 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 	return true;
 }
 
-
 void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode *call) {
 	GB_ASSERT(call->kind == AstNode_CallExpr);
 	GB_ASSERT(proc_type->kind == Type_Proc);
@@ -2439,8 +2428,9 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
 	check_expr_or_type(c, operand, ce->proc);
 
 	if (operand->mode == Addressing_Invalid) {
-		for (AstNode *arg = ce->arg_list; arg != NULL; arg = arg->next)
+		for (AstNode *arg = ce->arg_list; arg != NULL; arg = arg->next) {
 			check_expr_base(c, operand, arg);
+		}
 		operand->mode = Addressing_Invalid;
 		operand->expr = call;
 		return Expr_Stmt;
@@ -2449,8 +2439,9 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
 
 	if (operand->mode == Addressing_Builtin) {
 		i32 id = operand->builtin_id;
-		if (!check_builtin_procedure(c, operand, call, id))
+		if (!check_builtin_procedure(c, operand, call, id)) {
 			operand->mode = Addressing_Invalid;
+		}
 		operand->expr = call;
 		return builtin_procs[id].kind;
 	}
@@ -2470,15 +2461,14 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
 
 	check_call_arguments(c, operand, proc_type, call);
 
-	auto *proc = &proc_type->Proc;
-	if (proc->result_count == 0) {
+	if (proc_type->Proc.result_count == 0) {
 		operand->mode = Addressing_NoValue;
-	} else if (proc->result_count == 1) {
+	} else if (proc_type->Proc.result_count == 1) {
 		operand->mode = Addressing_Value;
-		operand->type = proc->results->Tuple.variables[0]->type;
+		operand->type = proc_type->Proc.results->Tuple.variables[0]->type;
 	} else {
 		operand->mode = Addressing_Value;
-		operand->type = proc->results;
+		operand->type = proc_type->Proc.results;
 	}
 
 	operand->expr = call;
@@ -2580,12 +2570,14 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 
 		Type *t = get_base_type(type);
 		switch (t->kind) {
-		case Type_Struct: {
+		case Type_Record: {
+			if (!is_type_struct(t))
+				break;
 			if (cl->elem_count == 0)
 				break; // NOTE(bill): No need to init
 			{ // Checker values
 				AstNode *elem = cl->elem_list;
-				isize field_count = t->Struct.field_count;
+				isize field_count = t->Record.field_count;
 				if (elem->kind == AstNode_FieldValue) {
 					b32 *fields_visited = gb_alloc_array(c->allocator, b32, field_count);
 
@@ -2605,9 +2597,9 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 							      "Invalid field name `%s` in structure literal", expr_str);
 							continue;
 						}
-						String name = kv->field->Ident.token.string;
+						String name = kv->field->Ident.string;
 
-						Selection sel = lookup_field(type, kv->field->Ident.token.string, o->mode == Addressing_Type);
+						Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
 						if (sel.entity == NULL) {
 							error(&c->error_collector, ast_node_token(elem),
 							      "Unknown field `%.*s` in structure literal", LIT(name));
@@ -2620,7 +2612,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 							continue;
 						}
 
-						Entity *field = t->Struct.fields[sel.index[0]];
+						Entity *field = t->Record.fields[sel.index[0]];
 						add_entity_use(&c->info, kv->field, field);
 
 						if (fields_visited[sel.index[0]]) {
@@ -2643,7 +2635,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 							      "Mixture of `field = value` and value elements in a structure literal is not allowed");
 							continue;
 						}
-						Entity *field = t->Struct.fields[index];
+						Entity *field = t->Record.fields[index];
 
 						check_expr(c, o, elem);
 						if (index >= field_count) {
@@ -2955,7 +2947,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 	case AstNode_ArrayType:
 	case AstNode_VectorType:
 	case AstNode_StructType:
-	case AstNode_UnionType:
+	case AstNode_RawUnionType:
 		o->mode = Addressing_Type;
 		o->type = check_type(c, node);
 		break;
@@ -3090,7 +3082,7 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 		break;
 
 	case_ast_node(i, Ident, node);
-		str = string_append_token(str, i->token);
+		str = string_append_token(str, *i);
 	case_end;
 
 	case_ast_node(bl, BasicLit, node);
@@ -3229,8 +3221,8 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 		str = gb_string_appendc(str, "}");
 	case_end;
 
-	case_ast_node(st, UnionType, node);
-		str = gb_string_appendc(str, "union{");
+	case_ast_node(st, RawUnionType, node);
+		str = gb_string_appendc(str, "raw_union{");
 		// str = write_field_list_to_string(str, st->decl_list, ", ");
 		str = gb_string_appendc(str, "}");
 	case_end;

+ 282 - 65
src/checker/stmt.cpp

@@ -1,24 +1,35 @@
 // Statements and Declarations
 
-enum StatementFlag : u32 {
-	Statement_BreakAllowed       = GB_BIT(0),
-	Statement_ContinueAllowed    = GB_BIT(1),
-	// Statement_FallthroughAllowed = GB_BIT(2), // TODO(bill): fallthrough
+enum StmtFlag : u32 {
+	Stmt_BreakAllowed       = GB_BIT(0),
+	Stmt_ContinueAllowed    = GB_BIT(1),
+	Stmt_FallthroughAllowed = GB_BIT(2), // TODO(bill): fallthrough
 };
 
+
 void check_stmt(Checker *c, AstNode *node, u32 flags);
 
-void check_stmt_list(Checker *c, AstNode *node, u32 flags) {
-	for (; node != NULL; node = node->next) {
-		if (node->kind != AstNode_EmptyStmt) {
-			check_stmt(c, node, flags);
+void check_stmt_list(Checker *c, AstNode *list, isize list_count, u32 flags) {
+	b32 ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
+	u32 f = flags & (~Stmt_FallthroughAllowed);
+
+	isize i = 0;
+	for (AstNode *n = list; n != NULL; n = n->next, i++) {
+		if (n->kind == AstNode_EmptyStmt) {
+			continue;
+		}
+		u32 new_flags = f;
+		if (ft_ok && i+1 == list_count) {
+			new_flags |= Stmt_FallthroughAllowed;
 		}
+		check_stmt(c, n, new_flags);
 	}
 }
 
-b32 check_is_terminating(Checker *c, AstNode *node);
+b32 check_is_terminating(AstNode *node);
+b32 check_has_break(AstNode *stmt, b32 implicit);
 
-b32 check_is_terminating_list(Checker *c, AstNode *list) {
+b32 check_is_terminating_list(AstNode *list) {
 	// Get to end of list
 	for (; list != NULL; list = list->next) {
 		if (list->next == NULL)
@@ -28,43 +39,94 @@ b32 check_is_terminating_list(Checker *c, AstNode *list) {
 	// Iterate backwards
 	for (AstNode *n = list; n != NULL; n = n->prev) {
 		if (n->kind != AstNode_EmptyStmt)
-			return check_is_terminating(c, n);
+			return check_is_terminating(n);
+	}
+
+	return false;
+}
+
+b32 check_has_break_list(AstNode *list, b32 implicit) {
+	for (AstNode *stmt = list; stmt != NULL; stmt = stmt->next) {
+		if (check_has_break(stmt, implicit)) {
+			return true;
+		}
+	}
+	return false;
+}
+
+
+b32 check_has_break(AstNode *stmt, b32 implicit) {
+	switch (stmt->kind) {
+	case AstNode_BranchStmt:
+		if (stmt->BranchStmt.token.kind == Token_break) {
+			return implicit;
+		}
+		break;
+	case AstNode_BlockStmt:
+		return check_has_break_list(stmt->BlockStmt.list, implicit);
+
+	case AstNode_IfStmt:
+		if (check_has_break(stmt->IfStmt.body, implicit) ||
+		    (stmt->IfStmt.else_stmt != NULL && check_has_break(stmt->IfStmt.else_stmt, implicit))) {
+			return true;
+		}
+		break;
+
+	case AstNode_CaseClause:
+		return check_has_break_list(stmt->CaseClause.stmts, implicit);
 	}
 
 	return false;
 }
 
+
+
 // NOTE(bill): The last expression has to be a `return` statement
 // TODO(bill): This is a mild hack and should be probably handled properly
 // TODO(bill): Warn/err against code after `return` that it won't be executed
-b32 check_is_terminating(Checker *c, AstNode *node) {
+b32 check_is_terminating(AstNode *node) {
 	switch (node->kind) {
 	case_ast_node(rs, ReturnStmt, node);
 		return true;
 	case_end;
 
 	case_ast_node(bs, BlockStmt, node);
-		return check_is_terminating_list(c, bs->list);
+		return check_is_terminating_list(bs->list);
 	case_end;
 
 	case_ast_node(es, ExprStmt, node);
-		return check_is_terminating(c, es->expr);
+		return check_is_terminating(es->expr);
 	case_end;
 
 	case_ast_node(is, IfStmt, node);
 		if (is->else_stmt != NULL) {
-			if (check_is_terminating(c, is->body) &&
-			    check_is_terminating(c, is->else_stmt)) {
+			if (check_is_terminating(is->body) &&
+			    check_is_terminating(is->else_stmt)) {
 			    return true;
 		    }
 		}
 	case_end;
 
 	case_ast_node(fs, ForStmt, node);
-		if (fs->cond == NULL) {
+		if (fs->cond == NULL && !check_has_break(fs->body, true)) {
 			return true;
 		}
 	case_end;
+
+	case_ast_node(ms, MatchStmt, node);
+		b32 has_default = false;
+		for (AstNode *clause = ms->body->BlockStmt.list; clause != NULL; clause = clause->next) {
+			ast_node(cc, CaseClause, clause);
+			if (cc->list == NULL) {
+				has_default = true;
+			}
+			if (!check_is_terminating_list(cc->stmts) ||
+			    check_has_break_list(cc->stmts, true)) {
+				return false;
+			}
+		}
+		return has_default;
+	case_end;
 	}
 
 	return false;
@@ -81,7 +143,7 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
 	// NOTE(bill): Ignore assignments to `_`
 	if (node->kind == AstNode_Ident) {
 		ast_node(i, Ident, node);
-		if (are_strings_equal(i->token.string, make_string("_"))) {
+		if (are_strings_equal(i->string, make_string("_"))) {
 			add_entity_definition(&c->info, node, NULL);
 			check_assignment(c, op_a, NULL, make_string("assignment to `_` identifier"));
 			if (op_a->mode == Addressing_Invalid)
@@ -94,7 +156,7 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
 	b32 used = false;
 	if (node->kind == AstNode_Ident) {
 		ast_node(i, Ident, node);
-		e = scope_lookup_entity(c, c->context.scope, i->token.string);
+		e = scope_lookup_entity(c, c->context.scope, i->string);
 		if (e != NULL && e->kind == Entity_Variable) {
 			used = e->Variable.used; // TODO(bill): Make backup just in case
 		}
@@ -306,10 +368,8 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
 				continue;
 			String name = e->token.string;
 			Type *t = get_base_type(type_deref(e->type));
-			if (t->kind == Type_Struct || t->kind == Type_Union) {
-				// IMPORTANT HACK(bill): Entity_(Struct|Union) overlap in some memory allowing
-				// for some variables to accessed to same
-				Scope **found = map_get(&c->info.scopes, hash_pointer(t->Struct.node));
+			if (is_type_struct(t) || is_type_raw_union(t)) {
+				Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node));
 				GB_ASSERT(found != NULL);
 				gb_for_array(i, (*found)->elements.entries) {
 					Entity *f = (*found)->elements.entries[i].value;
@@ -323,7 +383,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
 					}
 				}
 			} else {
-				error(&c->error_collector, e->token, "`using` can only be applied to variables of type struct or union");
+				error(&c->error_collector, e->token, "`using` can only be applied to variables of type struct or raw_union");
 				break;
 			}
 		}
@@ -332,9 +392,9 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
 	push_procedure(c, type);
 	ast_node(bs, BlockStmt, body);
 	// TODO(bill): Check declarations first (except mutable variable declarations)
-	check_stmt_list(c, bs->list, 0);
+	check_stmt_list(c, bs->list, bs->list_count, 0);
 	if (type->Proc.result_count > 0) {
-		if (!check_is_terminating(c, body)) {
+		if (!check_is_terminating(body)) {
 			error(&c->error_collector, bs->close, "Missing return statement at the end of the procedure");
 		}
 	}
@@ -349,14 +409,15 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d, b32 check_body_later) {
 	Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0);
 	e->type = proc_type;
 	ast_node(pd, ProcDecl, d->proc_decl);
-
 	check_open_scope(c, pd->type);
-	defer ({
-		check_close_scope(c);
-	});
+	defer (check_close_scope(c));
+	check_procedure_type(c, proc_type, pd->type);
+	// add_proc_entity(c, d->scope, pd->name, e);
+	add_entity(c, d->scope, pd->name, e);
+
+
 
 
-	check_procedure_type(c, proc_type, pd->type);
 	b32 is_foreign   = (pd->tags & ProcTag_foreign)   != 0;
 	b32 is_inline    = (pd->tags & ProcTag_inline)    != 0;
 	b32 is_no_inline = (pd->tags & ProcTag_no_inline) != 0;
@@ -481,7 +542,7 @@ void check_var_decl(Checker *c, AstNode *node) {
 
 		for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
 			Entity *entity = NULL;
-			Token token = name->Ident.token;
+			Token token = name->Ident;
 			if (name->kind == AstNode_Ident) {
 				String str = token.string;
 				Entity *found = NULL;
@@ -497,6 +558,11 @@ void check_var_decl(Checker *c, AstNode *node) {
 					}
 					add_entity_definition(&c->info, name, entity);
 				} else {
+					TokenPos pos = found->token.pos;
+					error(&c->error_collector, token,
+					      "Redeclaration of `%.*s` in this scope\n"
+					      "\tat %.*s(%td:%td)",
+					      LIT(str), LIT(pos.file), pos.line, pos.column);
 					entity = found;
 				}
 			} else {
@@ -543,9 +609,15 @@ void check_var_decl(Checker *c, AstNode *node) {
 			GB_ASSERT(name->kind == AstNode_Ident);
 			ExactValue v = {ExactValue_Invalid};
 			ast_node(i, Ident, name);
-			Entity *e = make_entity_constant(c->allocator, c->context.scope, i->token, NULL, v);
-			entities[entity_index++] = e;
-			check_const_decl(c, e, vd->type, value);
+			String str = i->string;
+			Entity *found = current_scope_lookup_entity(c->context.scope, str);
+			if (found == NULL) {
+				Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v);
+				entities[entity_index++] = e;
+				check_const_decl(c, e, vd->type, value);
+			} else {
+				entities[entity_index++] = found;
+			}
 		}
 
 		isize lhs_count = vd->name_count;
@@ -572,6 +644,7 @@ void check_var_decl(Checker *c, AstNode *node) {
 
 
 void check_stmt(Checker *c, AstNode *node, u32 flags) {
+	u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
 	switch (node->kind) {
 	case_ast_node(_, EmptyStmt, node); case_end;
 	case_ast_node(_, BadStmt,   node); case_end;
@@ -711,7 +784,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 
 	case_ast_node(bs, BlockStmt, node);
 		check_open_scope(c, node);
-		check_stmt_list(c, bs->list, flags);
+		check_stmt_list(c, bs->list, bs->list_count, mod_flags);
 		check_close_scope(c);
 	case_end;
 
@@ -730,13 +803,13 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 			            "Non-boolean condition in `if` statement");
 		}
 
-		check_stmt(c, is->body, flags);
+		check_stmt(c, is->body, mod_flags);
 
 		if (is->else_stmt) {
 			switch (is->else_stmt->kind) {
 			case AstNode_IfStmt:
 			case AstNode_BlockStmt:
-				check_stmt(c, is->else_stmt, flags);
+				check_stmt(c, is->else_stmt, mod_flags);
 				break;
 			default:
 				error(&c->error_collector, ast_node_token(is->else_stmt),
@@ -772,6 +845,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 	case_end;
 
 	case_ast_node(fs, ForStmt, node);
+		u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
 		check_open_scope(c, node);
 		defer (check_close_scope(c));
 
@@ -788,9 +862,152 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 		}
 		if (fs->post != NULL)
 			check_stmt(c, fs->post, 0);
-		check_stmt(c, fs->body, flags | Statement_BreakAllowed | Statement_ContinueAllowed);
+		check_stmt(c, fs->body, new_flags);
 	case_end;
 
+	case_ast_node(ms, MatchStmt, node);
+		Operand x = {};
+
+		mod_flags |= Stmt_BreakAllowed;
+		check_open_scope(c, node);
+		defer (check_close_scope(c));
+
+		if (ms->init != NULL) {
+			check_stmt(c, ms->init, 0);
+		}
+		if (ms->tag != NULL) {
+			check_expr(c, &x, ms->tag);
+			check_assignment(c, &x, NULL, make_string("match expression"));
+		} else {
+			x.mode  = Addressing_Constant;
+			x.type  = t_bool;
+			x.value = make_exact_value_bool(true);
+
+			Token token = {};
+			token.pos = ast_node_token(ms->body).pos;
+			token.string = make_string("true");
+			x.expr  = make_ident(c->curr_ast_file, token);
+		}
+
+		// NOTE(bill): Check for multiple defaults
+		AstNode *first_default = NULL;
+		ast_node(bs, BlockStmt, ms->body);
+		for (AstNode *stmt = bs->list; stmt != NULL; stmt = stmt->next) {
+			AstNode *default_stmt = NULL;
+			if (stmt->kind == AstNode_CaseClause) {
+				ast_node(c, CaseClause, stmt);
+				if (c->list_count == 0) {
+					default_stmt = stmt;
+				}
+			} else {
+				error(&c->error_collector, ast_node_token(stmt), "Invalid AST - expected case clause");
+			}
+
+			if (default_stmt != NULL) {
+				if (first_default != NULL) {
+					TokenPos pos = ast_node_token(first_default).pos;
+					error(&c->error_collector, ast_node_token(stmt),
+					      "multiple `default` clauses\n"
+					      "\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column);
+				} else {
+					first_default = default_stmt;
+				}
+			}
+		}
+
+
+		struct TypeAndToken {
+			Type *type;
+			Token token;
+		};
+
+		Map<TypeAndToken> seen = {}; // Multimap
+		map_init(&seen, gb_heap_allocator());
+		defer (map_destroy(&seen));
+		isize i = 0;
+		for (AstNode *stmt = bs->list; stmt != NULL; stmt = stmt->next) {
+			if (stmt->kind != AstNode_CaseClause) {
+				// NOTE(bill): error handled by above multiple default checker
+				continue;
+			}
+			ast_node(cc, CaseClause, stmt);
+
+
+			for (AstNode *expr = cc->list; expr != NULL; expr = expr->next) {
+				Operand y = {};
+				Operand z = {};
+				Token eq = {Token_CmpEq};
+
+				check_expr(c, &y, expr);
+				if (x.mode == Addressing_Invalid ||
+				    y.mode == Addressing_Invalid) {
+					continue;
+				}
+				convert_to_typed(c, &y, x.type);
+				if (y.mode == Addressing_Invalid) {
+					continue;
+				}
+
+				z = y;
+				check_comparison(c, &z, &x, eq);
+				if (z.mode == Addressing_Invalid) {
+					continue;
+				}
+				if (y.mode != Addressing_Constant) {
+					continue;
+				}
+
+				if (y.value.kind != ExactValue_Invalid) {
+					HashKey key = hash_exact_value(y.value);
+					auto *found = map_get(&seen, key);
+					if (found != NULL) {
+						isize count = multi_map_count(&seen, key);
+						TypeAndToken *taps = gb_alloc_array(gb_heap_allocator(), TypeAndToken, count);
+						defer (gb_free(gb_heap_allocator(), taps));
+
+						multi_map_get_all(&seen, key, taps);
+						b32 continue_outer = false;
+
+						for (isize i = 0; i < count; i++) {
+							TypeAndToken tap = taps[i];
+							if (are_types_identical(y.type, tap.type)) {
+								TokenPos pos = tap.token.pos;
+								gbString expr_str = expr_to_string(y.expr);
+								error(&c->error_collector,
+								      ast_node_token(y.expr),
+								      "Duplicate case `%s`\n"
+								      "\tprevious case at %.*s(%td:%td)",
+								      expr_str,
+								      LIT(pos.file), pos.line, pos.column);
+								gb_string_free(expr_str);
+								continue_outer = true;
+								break;
+							}
+						}
+
+						if (continue_outer) {
+							continue;
+						}
+					}
+					TypeAndToken tap = {y.type, ast_node_token(y.expr)};
+					multi_map_insert(&seen, key, tap);
+				}
+			}
+
+			check_open_scope(c, stmt);
+			u32 ft_flags = mod_flags;
+			if (i+1 < bs->list_count) {
+				ft_flags |= Stmt_FallthroughAllowed;
+			}
+			check_stmt_list(c, cc->stmts, cc->stmt_count, ft_flags);
+			check_close_scope(c);
+			i++;
+		}
+
+
+	case_end;
+
+
 	case_ast_node(ds, DeferStmt, node);
 		if (is_ast_node_decl(ds->stmt)) {
 			error(&c->error_collector, ds->token, "You cannot defer a declaration");
@@ -806,12 +1023,16 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 		Token token = bs->token;
 		switch (token.kind) {
 		case Token_break:
-			if ((flags & Statement_BreakAllowed) == 0)
-				error(&c->error_collector, token, "`break` only allowed in `for` statement");
+			if ((flags & Stmt_BreakAllowed) == 0)
+				error(&c->error_collector, token, "`break` only allowed in `for` or `match` statements");
 			break;
 		case Token_continue:
-			if ((flags & Statement_ContinueAllowed) == 0)
-				error(&c->error_collector, token, "`continue` only allowed in `for` statement");
+			if ((flags & Stmt_ContinueAllowed) == 0)
+				error(&c->error_collector, token, "`continue` only allowed in `for` statements");
+			break;
+		case Token_fallthrough:
+			if ((flags & Stmt_FallthroughAllowed) == 0)
+				error(&c->error_collector, token, "`fallthrough` statement in illegal position");
 			break;
 		default:
 			error(&c->error_collector, token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string));
@@ -828,7 +1049,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 			b32 is_selector = false;
 			AstNode *expr = unparen_expr(es->expr);
 			if (expr->kind == AstNode_Ident) {
-				String name = expr->Ident.token.string;
+				String name = expr->Ident.string;
 				e = scope_lookup_entity(c, c->context.scope, name);
 			} else if (expr->kind == AstNode_SelectorExpr) {
 				Operand o = {};
@@ -848,9 +1069,9 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 			switch (e->kind) {
 			case Entity_TypeName: {
 				Type *t = get_base_type(e->type);
-				if (t->kind == Type_Enum) {
-					for (isize i = 0; i < t->Enum.field_count; i++) {
-						Entity *f = t->Enum.fields[i];
+				if (is_type_enum(t)) {
+					for (isize i = 0; i < t->Record.other_field_count; i++) {
+						Entity *f = t->Record.other_fields[i];
 						Entity *found = scope_insert_entity(c->context.scope, f);
 						if (found != NULL) {
 							error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of the constant: %.*s", expr_str, LIT(found->token.string));
@@ -858,8 +1079,8 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 						}
 						f->using_parent = e;
 					}
-				} else if (t->kind == Type_Struct) {
-					Scope **found = map_get(&c->info.scopes, hash_pointer(t->Struct.node));
+				} else if (is_type_struct(t)) {
+					Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node));
 					if (found != NULL) {
 						gb_for_array(i, (*found)->elements.entries) {
 							Entity *f = (*found)->elements.entries[i].value;
@@ -871,9 +1092,9 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 							f->using_parent = e;
 						}
 					} else {
-						for (isize i = 0; i < t->Struct.other_field_count; i++) {
+						for (isize i = 0; i < t->Record.other_field_count; i++) {
 							// TODO(bill): using field types too
-							Entity *f = t->Struct.other_fields[i];
+							Entity *f = t->Record.other_fields[i];
 							Entity *found = scope_insert_entity(c->context.scope, f);
 							if (found != NULL) {
 								error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
@@ -897,10 +1118,8 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 			case Entity_Variable:
 			case Entity_UsingVariable: {
 				Type *t = get_base_type(type_deref(e->type));
-				if (t->kind == Type_Struct || t->kind == Type_Union) {
-					// IMPORTANT HACK(bill): Entity_(Struct|Union) overlap in some memory allowing
-					// for some variables to accessed to same
-					Scope **found = map_get(&c->info.scopes, hash_pointer(t->Struct.node));
+				if (is_type_struct(t) || is_type_raw_union(t)) {
+					Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node));
 					GB_ASSERT(found != NULL);
 					gb_for_array(i, (*found)->elements.entries) {
 						Entity *f = (*found)->elements.entries[i].value;
@@ -917,7 +1136,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 						}
 					}
 				} else {
-					error(&c->error_collector, us->token, "`using` can only be applied to variables of type struct or union");
+					error(&c->error_collector, us->token, "`using` can only be applied to variables of type struct or raw_union");
 					return;
 				}
 			} break;
@@ -935,13 +1154,11 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 
 			for (AstNode *item = vd->name_list; item != NULL; item = item->next) {
 				ast_node(i, Ident, item);
-				String name = i->token.string;
+				String name = i->string;
 				Entity *e = scope_lookup_entity(c, c->context.scope, name);
 				Type *t = get_base_type(type_deref(e->type));
-				if (t->kind == Type_Struct || t->kind == Type_Union) {
-					// IMPORTANT HACK(bill): Entity_(Struct|Union) overlap in some memory allowing
-					// for some variables to accessed to same
-					Scope **found = map_get(&c->info.scopes, hash_pointer(t->Struct.node));
+				if (is_type_struct(t) || is_type_raw_union(t)) {
+					Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node));
 					GB_ASSERT(found != NULL);
 					gb_for_array(i, (*found)->elements.entries) {
 						Entity *f = (*found)->elements.entries[i].value;
@@ -955,7 +1172,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 						}
 					}
 				} else {
-					error(&c->error_collector, us->token, "`using` can only be applied to variables of type struct or union");
+					error(&c->error_collector, us->token, "`using` can only be applied to variables of type struct or raw_union");
 					return;
 				}
 			}
@@ -979,8 +1196,8 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 
 	case_ast_node(pd, ProcDecl, node);
 		ast_node(name, Ident, pd->name);
-		Entity *e = make_entity_procedure(c->allocator, c->context.scope, name->token, NULL);
-		add_entity(c, c->context.scope, pd->name, e);
+		Entity *e = make_entity_procedure(c->allocator, c->context.scope, *name, NULL);
+		// add_proc_entity(c, c->context.scope, pd->name, e);
 
 		DeclInfo decl = {};
 		init_declaration_info(&decl, e->scope);
@@ -991,7 +1208,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 
 	case_ast_node(td, TypeDecl, node);
 		ast_node(name, Ident, td->name);
-		Entity *e = make_entity_type_name(c->allocator, c->context.scope, name->token, NULL);
+		Entity *e = make_entity_type_name(c->allocator, c->context.scope, *name, NULL);
 		add_entity(c, c->context.scope, td->name, e);
 		check_type_decl(c, e, td->type, NULL, NULL);
 	case_end;

+ 193 - 165
src/checker/type.cpp

@@ -61,9 +61,7 @@ struct BasicType {
 	TYPE_KIND(Array), \
 	TYPE_KIND(Vector), \
 	TYPE_KIND(Slice), \
-	TYPE_KIND(Struct), \
-	TYPE_KIND(Union), \
-	TYPE_KIND(Enum), \
+	TYPE_KIND(Record), \
 	TYPE_KIND(Pointer), \
 	TYPE_KIND(Named), \
 	TYPE_KIND(Tuple), \
@@ -87,6 +85,17 @@ enum TypeFlag {
 	TypeFlag_volatile     = GB_BIT(1),
 };
 
+enum TypeRecordKind {
+	TypeRecord_Invalid,
+
+	TypeRecord_Struct,
+	TypeRecord_Enum,
+	TypeRecord_RawUnion,
+	TypeRecord_Union, // Tagged
+
+	TypeRecord_Count,
+};
+
 struct Type {
 	u32 flags;
 	TypeKind kind;
@@ -104,26 +113,26 @@ struct Type {
 			Type *elem;
 		} Slice;
 		struct {
+			TypeRecordKind kind;
+
+			// All record types
 			// Theses are arrays
 			Entity **fields; // Entity_Variable
 			isize    field_count; // == offset_count
-			Entity **other_fields; // Entity_Constant or Entity_TypeName
-			isize    other_field_count;
 			AstNode *node;
 
-			i64 *    offsets;
-			b32      are_offsets_set;
-			b32      is_packed;
-		} Struct;
-		struct {
-			// IMPORTANT HACK(bill): The positions of fields, field_count, and node
-			// must be same for Struct and Union
-			Entity **fields; // Entity_Variable
-			isize    field_count;
-			Entity **other_fields; // Entity_Constant or Entity_TypeName
+			// enum only
+			Type *   enum_base; // Default is `int`
+
+			// struct only
+			i64 *    struct_offsets;
+			b32      struct_are_offsets_set;
+			b32      struct_is_packed;
+
+			// Entity_Constant or Entity_TypeName
+			Entity **other_fields;
 			isize    other_field_count;
-			AstNode *node;
-		} Union;
+		} Record;
 		struct { Type *elem; } Pointer;
 		struct {
 			String  name;
@@ -141,11 +150,6 @@ struct Type {
 			isize  param_count;
 			isize  result_count;
 		} Proc;
-		struct {
-			Type *   base; // Default is `int`
-			Entity **fields; // Entity_Constant
-			isize    field_count;
-		} Enum;
 	};
 };
 
@@ -200,17 +204,20 @@ Type *make_type_slice(gbAllocator a, Type *elem) {
 }
 
 Type *make_type_struct(gbAllocator a) {
-	Type *t = alloc_type(a, Type_Struct);
+	Type *t = alloc_type(a, Type_Record);
+	t->Record.kind = TypeRecord_Struct;
 	return t;
 }
 
-Type *make_type_union(gbAllocator a) {
-	Type *t = alloc_type(a, Type_Union);
+Type *make_type_raw_union(gbAllocator a) {
+	Type *t = alloc_type(a, Type_Record);
+	t->Record.kind = TypeRecord_RawUnion;
 	return t;
 }
 
 Type *make_type_enum(gbAllocator a) {
-	Type *t = alloc_type(a, Type_Enum);
+	Type *t = alloc_type(a, Type_Record);
+	t->Record.kind = TypeRecord_Enum;
 	return t;
 }
 
@@ -433,12 +440,21 @@ Type *base_vector_type(Type *t) {
 }
 b32 is_type_enum(Type *t) {
 	t = get_base_type(t);
-	return t->kind == Type_Enum;
+	return (t->kind == Type_Record && t->Record.kind == TypeRecord_Enum);
+}
+b32 is_type_struct(Type *t) {
+	t = get_base_type(t);
+	return (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct);
+}
+b32 is_type_raw_union(Type *t) {
+	t = get_base_type(t);
+	return (t->kind == Type_Record && t->Record.kind == TypeRecord_RawUnion);
 }
+
 Type *get_enum_base_type(Type *t) {
 	Type *bt = get_base_type(t);
 	if (is_type_enum(bt)) {
-		return bt->Enum.base;
+		return bt->Record.enum_base;
 	}
 	return t;
 }
@@ -452,10 +468,14 @@ b32 is_type_comparable(Type *t) {
 		return true;
 	case Type_Pointer:
 		return true;
-	case Type_Struct: {
-		for (isize i = 0; i < t->Struct.field_count; i++) {
-			if (!is_type_comparable(t->Struct.fields[i]->type))
-				return false;
+	case Type_Record: {
+		if (is_type_struct(t)) {
+			for (isize i = 0; i < t->Record.field_count; i++) {
+				if (!is_type_comparable(t->Record.fields[i]->type))
+					return false;
+			}
+		} else if (is_type_enum(t)) {
+			return is_type_comparable(t->Record.enum_base);
 		}
 		return true;
 	} break;
@@ -463,8 +483,6 @@ b32 is_type_comparable(Type *t) {
 		return is_type_comparable(t->Array.elem);
 	case Type_Vector:
 		return is_type_comparable(t->Vector.elem);
-	case Type_Enum:
-		return is_type_comparable(t->Enum.base);
 	case Type_Proc:
 		return true;
 	}
@@ -501,16 +519,25 @@ b32 are_types_identical(Type *x, Type *y) {
 			return are_types_identical(x->Slice.elem, y->Slice.elem);
 		break;
 
-	case Type_Struct:
-		if (y->kind == Type_Struct) {
-			if (x->Struct.field_count == y->Struct.field_count) {
-				for (isize i = 0; i < x->Struct.field_count; i++) {
-					if (!are_types_identical(x->Struct.fields[i]->type, y->Struct.fields[i]->type)) {
-						return false;
+	case Type_Record:
+		if (y->kind == Type_Record) {
+			if (x->Record.kind == y->Record.kind) {
+				switch (x->Record.kind) {
+				case TypeRecord_Struct:
+				case TypeRecord_RawUnion:
+				case TypeRecord_Union:
+					if (x->Record.field_count == y->Record.field_count) {
+						for (isize i = 0; i < x->Record.field_count; i++) {
+							if (!are_types_identical(x->Record.fields[i]->type, y->Record.fields[i]->type)) {
+								return false;
+							}
+						}
+
+						return true;
 					}
+				case TypeRecord_Enum:
+					return are_types_identical(x->Record.enum_base, y->Record.enum_base);
 				}
-
-				return true;
 			}
 		}
 		break;
@@ -627,65 +654,55 @@ Selection lookup_field(Type *type_, String field_name, b32 is_type, Selection se
 	b32 is_ptr = type != type_;
 	type = get_base_type(type);
 
-	switch (type->kind) {
-	// HACK(bill): struct/union variable overlay from unsafe tagged union
-	case Type_Struct:
-	case Type_Union:
-		if (is_type) {
-			for (isize i = 0; i < type->Struct.other_field_count; i++) {
-				Entity *f = type->Struct.other_fields[i];
-				GB_ASSERT(f->kind != Entity_Variable);
-				String str = f->token.string;
-				if (are_strings_equal(field_name, str)) {
+	if (type->kind != Type_Record) {
+		return sel;
+	}
+	if (is_type) {
+		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;
 				}
 			}
-		} else {
-			for (isize i = 0; i < type->Struct.field_count; i++) {
-				Entity *f = type->Struct.fields[i];
-				GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field);
-				String str = f->token.string;
-				if (are_strings_equal(field_name, str)) {
-					selection_add_index(&sel, i);
-					sel.entity = f;
-					return sel;
-				}
-
-				if (f->Variable.anonymous) {
-					isize prev_count = 0;
-					if (sel.index != NULL) {
-						prev_count = gb_array_count(sel.index);
-					}
-					selection_add_index(&sel, i); // HACK(bill): Leaky memory
-
-					sel = lookup_field(f->type, field_name, is_type, sel);
+		}
+	} else if (!is_type_enum(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);
+			String str = f->token.string;
+			if (are_strings_equal(field_name, str)) {
+				selection_add_index(&sel, i);
+				sel.entity = f;
+				return sel;
+			}
 
-					if (sel.entity != NULL) {
-						if (is_type_pointer(f->type))
-							sel.indirect = true;
-						return sel;
-					}
-					gb_array_count(sel.index) = prev_count;
+			if (f->Variable.anonymous) {
+				isize prev_count = 0;
+				if (sel.index != NULL) {
+					prev_count = gb_array_count(sel.index);
 				}
-			}
-		}
-		break;
+				selection_add_index(&sel, i); // HACK(bill): Leaky memory
 
-	case Type_Enum:
-		if (is_type) {
-			for (isize i = 0; i < type->Enum.field_count; i++) {
-				Entity *f = type->Enum.fields[i];
-				GB_ASSERT(f->kind == Entity_Constant);
-				String str = f->token.string;
-				if (are_strings_equal(field_name, str)) {
-					// Enums are constant expression
-					return make_selection(f, NULL, i);
+				sel = lookup_field(f->type, field_name, is_type, sel);
+
+				if (sel.entity != NULL) {
+					if (is_type_pointer(f->type))
+						sel.indirect = true;
+					return sel;
 				}
+				gb_array_count(sel.index) = prev_count;
 			}
 		}
-		break;
 	}
 
 	return sel;
@@ -716,30 +733,32 @@ i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
 		return gb_clamp(size, s.max_align, 4*s.max_align);
 	} break;
 
-	case Type_Struct: {
-		if (!t->Struct.is_packed) {
+	case Type_Record: {
+		switch (t->Record.kind) {
+		case TypeRecord_Struct:
+			if (!t->Record.struct_is_packed) {
+				i64 max = 1;
+				for (isize i = 0; i < t->Record.field_count; i++) {
+					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->Struct.field_count; i++) {
-				i64 align = type_align_of(s, allocator, t->Struct.fields[i]->type);
+			for (isize i = 0; i < t->Record.field_count; i++) {
+				i64 align = type_align_of(s, allocator, t->Record.fields[i]->type);
 				if (max < align)
 					max = align;
 			}
 			return max;
+		} break;
+		case TypeRecord_Enum:
+			return type_align_of(s, allocator, t->Record.enum_base);
 		}
 	} break;
-
-	case Type_Union: {
-		i64 max = 1;
-		for (isize i = 0; i < t->Union.field_count; i++) {
-			i64 align = type_align_of(s, allocator, t->Union.fields[i]->type);
-			if (max < align)
-				max = align;
-		}
-		return max;
-	} break;
-
-	case Type_Enum:
-		return type_align_of(s, allocator, t->Enum.base);
 	}
 
 	return gb_clamp(next_pow2(type_size_of(s, allocator, t)), 1, s.max_align);
@@ -767,10 +786,10 @@ i64 *type_set_offsets_of(BaseTypeSizes s, gbAllocator allocator, Entity **fields
 }
 
 b32 type_set_offsets(BaseTypeSizes s, gbAllocator allocator, Type *t) {
-	GB_ASSERT(t->kind == Type_Struct);
-	if (!t->Struct.are_offsets_set) {
-		t->Struct.offsets = type_set_offsets_of(s, allocator, t->Struct.fields, t->Struct.field_count, t->Struct.is_packed);
-		t->Struct.are_offsets_set = true;
+	GB_ASSERT(is_type_struct(t));
+	if (!t->Record.struct_are_offsets_set) {
+		t->Record.struct_offsets = type_set_offsets_of(s, allocator, t->Record.fields, t->Record.field_count, t->Record.struct_is_packed);
+		t->Record.struct_are_offsets_set = true;
 		return true;
 	}
 	return false;
@@ -824,27 +843,32 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
 	case Type_Slice: // ptr + len + cap
 		return 3 * s.word_size;
 
-	case Type_Struct: {
-		i64 count = t->Struct.field_count;
-		if (count == 0)
-			return 0;
-		type_set_offsets(s, allocator, t);
-		return t->Struct.offsets[count-1] + type_size_of(s, allocator, t->Struct.fields[count-1]->type);
-	} break;
+	case Type_Record: {
+		switch (t->Record.kind) {
+		case TypeRecord_Struct: {
+			i64 count = t->Record.field_count;
+			if (count == 0)
+				return 0;
+			type_set_offsets(s, allocator, t);
+			return t->Record.struct_offsets[count-1] + type_size_of(s, allocator, t->Record.fields[count-1]->type);
+		} break;
+
+		case TypeRecord_RawUnion: {
+			i64 count = t->Record.field_count;
+			i64 max = 0;
+			for (isize i = 0; i < count; i++) {
+				i64 size = type_size_of(s, allocator, t->Record.fields[i]->type);
+				if (max < size)
+					max = size;
+			}
+			return max;
+		} break;
 
-	case Type_Union: {
-		i64 count = t->Union.field_count;
-		i64 max = 0;
-		for (isize i = 0; i < count; i++) {
-			i64 size = type_size_of(s, allocator, t->Struct.fields[i]->type);
-			if (max < size)
-				max = size;
+		case TypeRecord_Enum: {
+			return type_size_of(s, allocator, t->Record.enum_base);
+		} break;
 		}
-		return max;
 	} break;
-
-	case Type_Enum:
-		return type_size_of(s, allocator, t->Enum.base);
 	}
 
 	// Catch all
@@ -852,10 +876,10 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
 }
 
 i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, isize index) {
-	if (t->kind == Type_Struct) {
+	if (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct) {
 		type_set_offsets(s, allocator, t);
-		if (gb_is_between(index, 0, t->Struct.field_count-1)) {
-			return t->Struct.offsets[index];
+		if (gb_is_between(index, 0, t->Record.field_count-1)) {
+			return t->Record.struct_offsets[index];
 		}
 	}
 	return 0;
@@ -868,11 +892,11 @@ i64 type_offset_of_from_selection(BaseTypeSizes s, gbAllocator allocator, Type *
 	for (isize i = 0; i < gb_array_count(sel.index); i++) {
 		isize index = sel.index[i];
 		t = get_base_type(t);
-		if (t->kind == Type_Struct) {
+		if (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct) {
 			type_set_offsets(s, allocator, t);
-			GB_ASSERT(gb_is_between(index, 0, t->Struct.field_count-1));
-			offset += t->Struct.offsets[index];
-			t = t->Struct.fields[index]->type;
+			GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
+			offset += t->Record.struct_offsets[index];
+			t = t->Record.fields[index]->type;
 		}
 	}
 	return offset;
@@ -905,37 +929,41 @@ gbString write_type_to_string(gbString str, Type *type) {
 		str = write_type_to_string(str, type->Array.elem);
 		break;
 
-	case Type_Struct: {
-		str = gb_string_appendc(str, "struct{");
-		for (isize i = 0; i < type->Struct.field_count; i++) {
-			Entity *f = type->Struct.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, "}");
-	} break;
+	case Type_Record: {
+		switch (type->Record.kind) {
+		case TypeRecord_Struct:
+			str = gb_string_appendc(str, "struct{");
+			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, ", ");
+				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 Type_Union: {
-		str = gb_string_appendc(str, "union{");
-		for (isize i = 0; i < type->Union.field_count; i++) {
-			Entity *f = type->Union.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, "}");
-	} break;
+		case TypeRecord_RawUnion:
+			str = gb_string_appendc(str, "raw_union{");
+			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, ", ");
+				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 Type_Enum: {
-		str = gb_string_appendc(str, "enum ");
-		str = write_type_to_string(str, type->Enum.base);
+		case TypeRecord_Enum:
+			str = gb_string_appendc(str, "enum ");
+			str = write_type_to_string(str, type->Record.enum_base);
+			break;
+		}
 	} break;
 
 	case Type_Pointer:

+ 15 - 1
src/codegen/codegen.cpp

@@ -89,11 +89,25 @@ void ssa_gen_code(ssaGen *s) {
 
 		case Entity_Procedure: {
 			auto *pd = &decl->proc_decl->ProcDecl;
-			String name = e->token.string;
+			String original_name = e->token.string;
+			String name = original_name;
 			AstNode *body = pd->body;
 			if (pd->foreign_name.len > 0) {
 				name = pd->foreign_name;
 			}
+
+			if (are_strings_equal(name, original_name)) {
+				Scope *scope = *map_get(&info->scopes, hash_pointer(pd->type));
+				isize count = multi_map_count(&scope->elements, hash_string(original_name));
+				if (count > 1) {
+					gb_printf("%.*s\n", LIT(name));
+					isize name_len = name.len + 1 + 10 + 1;
+					u8 *name_text = gb_alloc_array(m->allocator, u8, name_len);
+					name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s$%d", LIT(name), e->guid);
+					name = make_string(name_text, name_len-1);
+				}
+			}
+
 			ssaValue *p = ssa_make_value_procedure(a, m, e->type, decl->type_expr, body, name);
 			p->Proc.tags = pd->tags;
 

+ 31 - 28
src/codegen/print_llvm.cpp

@@ -135,41 +135,44 @@ void ssa_print_type(gbFile *f, BaseTypeSizes s, Type *t) {
 		ssa_print_type(f, s, t->Slice.elem);
 		ssa_fprintf(f, "*, i%lld, i%lld}", word_bits, word_bits);
 		break;
-	case Type_Struct:
-		if (t->Struct.is_packed) {
-			ssa_fprintf(f, "<");
-		}
-		ssa_fprintf(f, "{");
-		for (isize i = 0; i < t->Struct.field_count; i++) {
-			if (i > 0) {
-				ssa_fprintf(f, ", ");
+	case Type_Record: {
+		switch (t->Record.kind) {
+		case TypeRecord_Struct:
+			if (t->Record.struct_is_packed) {
+				ssa_fprintf(f, "<");
 			}
-			Type *ft = t->Struct.fields[i]->type;
-			Type *bft = get_base_type(ft);
-			if (bft->kind != Type_Struct) {
-				ft = bft;
+			ssa_fprintf(f, "{");
+			for (isize i = 0; i < t->Record.field_count; i++) {
+				if (i > 0) {
+					ssa_fprintf(f, ", ");
+				}
+				Type *ft = t->Record.fields[i]->type;
+				Type *bft = get_base_type(ft);
+				if (!is_type_struct(bft)) {
+					ft = bft;
+				}
+				ssa_print_type(f, s, ft);
 			}
-			ssa_print_type(f, s, ft);
-		}
-		ssa_fprintf(f, "}");
-		if (t->Struct.is_packed) {
-			ssa_fprintf(f, ">");
+			ssa_fprintf(f, "}");
+			if (t->Record.struct_is_packed) {
+				ssa_fprintf(f, ">");
+			}
+			break;
+		case TypeRecord_RawUnion:
+			ssa_fprintf(f, "[%lld x i8]", type_size_of(s, gb_heap_allocator(), t));
+			break;
+		case TypeRecord_Enum:
+			ssa_print_type(f, s, t->Record.enum_base);
+			break;
 		}
-
-		break;
-	case Type_Union: {
-		i64 size = type_size_of(s, gb_heap_allocator(), t);
-		ssa_fprintf(f, "[%lld x i8]", size);
 	} break;
-	case Type_Enum:
-		ssa_print_type(f, s, t->Enum.base);
-		break;
+
 	case Type_Pointer:
 		ssa_print_type(f, s, t->Pointer.elem);
 		ssa_fprintf(f, "*");
 		break;
 	case Type_Named:
-		if (get_base_type(t)->kind == Type_Struct) {
+		if (is_type_struct(t)) {
 			ssa_print_encoded_local(f, t->Named.name);
 		} else {
 			ssa_print_type(f, s, get_base_type(t));
@@ -271,7 +274,7 @@ void ssa_print_exact_value(gbFile *f, ssaModule *m, ExactValue value, Type *type
 
 void ssa_print_block_name(gbFile *f, ssaBlock *b) {
 	ssa_print_escape_string(f, b->label, false);
-	ssa_fprintf(f, ".-.%d", b->id);
+	ssa_fprintf(f, "..%d", b->id);
 }
 
 void ssa_print_value(gbFile *f, ssaModule *m, ssaValue *value, Type *type_hint) {
@@ -753,7 +756,7 @@ 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 (base_type->kind != Type_Struct)
+	if (!is_type_struct(base_type))
 		return;
 	ssa_print_encoded_local(f, v->TypeName.name);
 	ssa_fprintf(f, " = type ");

+ 124 - 24
src/codegen/ssa.cpp

@@ -25,6 +25,7 @@ struct ssaBlock {
 	isize scope_index;
 	String label;
 	ssaProcedure *parent;
+	b32 added;
 
 	gbArray(ssaValue *) instrs;
 	gbArray(ssaValue *) values;
@@ -672,7 +673,7 @@ ssaValue *ssa_make_value_block(ssaProcedure *proc, AstNode *node, Scope *scope,
 b32 ssa_is_blank_ident(AstNode *node) {
 	if (node->kind == AstNode_Ident) {
 		ast_node(i, Ident, node);
-		return is_blank_ident(i->token.string);
+		return is_blank_ident(i->string);
 	}
 	return false;
 }
@@ -710,7 +711,7 @@ ssaValue *ssa_emit(ssaProcedure *proc, ssaValue *instr) {
 	GB_ASSERT(instr->kind == ssaValue_Instr);
 	ssaBlock *b = proc->curr_block;
 	instr->Instr.parent = b;
-	if (b) {
+	if (b != NULL) {
 		ssaInstr *i = ssa_get_last_instr(b);
 		if (!ssa_is_instr_terminating(i)) {
 			gb_array_append(b->instrs, instr);
@@ -742,8 +743,11 @@ ssaValue *ssa_add_local_for_identifier(ssaProcedure *proc, AstNode *name) {
 }
 
 ssaValue *ssa_add_local_generated(ssaProcedure *proc, Type *type) {
+	Scope *scope = NULL;
+	if (proc->curr_block)
+		scope = proc->curr_block->scope;
 	Entity *entity = make_entity_variable(proc->module->allocator,
-	                                      proc->curr_block->scope,
+	                                      scope,
 	                                      empty_token,
 	                                      type);
 	return ssa_emit(proc, ssa_make_instr_local(proc, entity));
@@ -1064,15 +1068,15 @@ ssaValue *ssa_emit_deep_field_gep(ssaProcedure *proc, Type *type, ssaValue *e, S
 		type = get_base_type(type);
 
 
-		if (type->kind == Type_Union) {
+		if (is_type_raw_union(type)) {
 			ssaValue *v = ssa_emit_ptr_offset(proc, e, v_zero);
 			ssa_set_type(v, make_type_pointer(proc->module->allocator, type));
-			type = type->Union.fields[index]->type;
+			type = type->Record.fields[index]->type;
 			e = ssa_emit_conv(proc, v, make_type_pointer(proc->module->allocator, type));
 			e = ssa_emit_ptr_offset(proc, e, v_zero);
 			ssa_set_type(e, type);
 		} else {
-			type = type->Union.fields[index]->type;
+			type = type->Record.fields[index]->type;
 			e = ssa_emit_struct_gep(proc, e, index, type);
 		}
 	}
@@ -1095,15 +1099,15 @@ ssaValue *ssa_emit_deep_field_ev(ssaProcedure *proc, Type *type, ssaValue *e, Se
 		type = get_base_type(type);
 
 
-		if (type->kind == Type_Union) {
+		if (is_type_raw_union(type)) {
 			ssaValue *v = ssa_emit_ptr_offset(proc, e, v_zero);
 			ssa_set_type(v, make_type_pointer(proc->module->allocator, type));
-			type = type->Union.fields[index]->type;
+			type = type->Record.fields[index]->type;
 			e = ssa_emit_conv(proc, v, make_type_pointer(proc->module->allocator, type));
 			e = ssa_emit_ptr_offset(proc, e, v_zero);
 			ssa_set_type(e, type);
 		} else {
-			type = type->Union.fields[index]->type;
+			type = type->Record.fields[index]->type;
 			e = ssa_emit_struct_ev(proc, e, index, type);
 		}
 	}
@@ -1296,9 +1300,9 @@ String lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) {
 	b32 src_is_ptr = src != prev_src;
 	// b32 dst_is_ptr = dst != prev_dst;
 
-	GB_ASSERT(src->kind == Type_Struct);
-	for (isize i = 0; i < src->Struct.field_count; i++) {
-		Entity *f = src->Struct.fields[i];
+	GB_ASSERT(is_type_struct(src));
+	for (isize i = 0; i < src->Record.field_count; i++) {
+		Entity *f = src->Record.fields[i];
 		if (f->kind == Entity_Variable && f->Variable.anonymous) {
 			if (are_types_identical(dst, f->type)) {
 				return f->token.string;
@@ -1420,7 +1424,7 @@ ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t, b32 is_arg
 	if (is_argument) {
 		Type *sb = get_base_type(type_deref(src));
 		b32 src_is_ptr = src != sb;
-		if (sb->kind == Type_Struct) {
+		if (is_type_struct(sb)) {
 			String field_name = lookup_polymorphic_field(proc->module->info, t, src);
 			// gb_printf("field_name: %.*s\n", LIT(field_name));
 			if (field_name.len > 0) {
@@ -1729,8 +1733,9 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 			return result;
 		} break;
 
-		case Type_Struct: {
-			auto *st = &base_type->Struct;
+		case Type_Record: {
+			GB_ASSERT(is_type_struct(base_type));
+			auto *st = &base_type->Record;
 			if (cl->elem_list != NULL) {
 				isize index = 0;
 				AstNode *elem = cl->elem_list;
@@ -1743,7 +1748,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 
 					if (elem->kind == AstNode_FieldValue) {
 						ast_node(kv, FieldValue, elem);
-						Selection sel = lookup_field(base_type, kv->field->Ident.token.string, false);
+						Selection sel = lookup_field(base_type, kv->field->Ident.string, false);
 						field_index = sel.index[0];
 						field_expr = ssa_build_expr(proc, kv->value);
 					} else {
@@ -2198,7 +2203,7 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
 	case_ast_node(se, SelectorExpr, expr);
 		Type *type = get_base_type(type_of_expr(proc->module->info, se->expr));
 
-		Selection sel = lookup_field(type, unparen_expr(se->selector)->Ident.token.string, false);
+		Selection sel = lookup_field(type, unparen_expr(se->selector)->Ident.string, false);
 		GB_ASSERT(sel.entity != NULL);
 
 		ssaValue *e = ssa_build_addr(proc, se->expr).addr;
@@ -2389,8 +2394,8 @@ void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name) {
 	map_set(&m->members, hash_string(name), t);
 
 	Type *bt = get_base_type(e->type);
-	if (bt->kind == Type_Struct) {
-		auto *s = &bt->Struct;
+	if (is_type_struct(bt)) {
+		auto *s = &bt->Record;
 		for (isize j = 0; j < s->other_field_count; j++) {
 			Entity *field = s->other_fields[j];
 			if (field->kind == Entity_TypeName) {
@@ -2519,7 +2524,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 		if (pd->body != NULL) {
 			// NOTE(bill): Generate a new name
 			// parent$name-guid
-			String pd_name = pd->name->Ident.token.string;
+			String pd_name = pd->name->Ident.string;
 			isize name_len = proc->name.len + 1 + pd_name.len + 1 + 10 + 1;
 			u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len);
 			i32 guid = cast(i32)gb_array_count(proc->children);
@@ -2527,7 +2532,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 			String name = make_string(name_text, name_len-1);
 
 			Entity **found = map_get(&proc->module->info->definitions, hash_pointer(pd->name));
-			GB_ASSERT_MSG(found != NULL, "Unable to find: %.*s", LIT(pd->name->Ident.token.string));
+			GB_ASSERT_MSG(found != NULL, "Unable to find: %.*s", LIT(pd->name->Ident.string));
 			Entity *e = *found;
 			ssaValue *value = ssa_make_value_procedure(proc->module->allocator,
 			                                           proc->module, e->type, pd->type, pd->body, name);
@@ -2538,12 +2543,14 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 			gb_array_append(proc->children, &value->Proc);
 			ssa_build_proc(value, proc);
 		} else {
-			String name = pd->name->Ident.token.string;
+			String original_name = pd->name->Ident.string;
+			String name = original_name;
 			if (pd->foreign_name.len > 0) {
 				name = pd->foreign_name;
 			}
+			auto *info = proc->module->info;
 
-			Entity **found = map_get(&proc->module->info->definitions, hash_pointer(pd->name));
+			Entity **found = map_get(&info->definitions, hash_pointer(pd->name));
 			GB_ASSERT(found != NULL);
 			Entity *e = *found;
 			ssaValue *value = ssa_make_value_procedure(proc->module->allocator,
@@ -2558,7 +2565,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 		// NOTE(bill): Generate a new name
 		// parent_proc.name-guid
-		String td_name = td->name->Ident.token.string;
+		String td_name = td->name->Ident.string;
 		isize name_len = proc->name.len + 1 + td_name.len + 1 + 10 + 1;
 		u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len);
 		i32 guid = cast(i32)gb_array_count(proc->module->members.entries);
@@ -2806,6 +2813,99 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 	case_end;
 
+	case_ast_node(ms, MatchStmt, node);
+		if (ms->init != NULL) {
+			ssa_build_stmt(proc, ms->init);
+		}
+		ssaValue *tag = v_true;
+		if (ms->tag != NULL) {
+			tag = ssa_build_expr(proc, ms->tag);
+		}
+		ssaBlock *done = ssa__make_block(proc, node, make_string("match.done")); // NOTE(bill): Append later
+
+		ast_node(body, BlockStmt, ms->body);
+
+
+		AstNode *default_stmts = NULL;
+		ssaBlock *default_fall = NULL;
+		ssaBlock *default_block = NULL;
+
+		ssaBlock *fall = NULL;
+		b32 append_fall = false;
+
+		isize case_count = body->list_count;
+		isize i = 0;
+		for (AstNode *clause = body->list;
+		     clause != NULL;
+		     clause = clause->next, i++) {
+			ssaBlock *body = fall;
+			b32 append_body = false;
+
+
+			ast_node(cc, CaseClause, clause);
+
+			if (body == NULL) {
+				append_body = true;
+				if (cc->list == NULL) {
+					body = ssa__make_block(proc, clause, make_string("match.dflt.body"));
+				} else {
+					body = ssa__make_block(proc, clause, make_string("match.case.body"));
+				}
+			}
+			if (append_fall && body == fall) {
+				append_fall = false;
+				append_body = true;
+			}
+
+			fall = done;
+			if (i+1 < case_count) {
+				append_fall = true;
+				fall = ssa__make_block(proc, clause, make_string("match.fall.body"));
+			}
+
+			if (cc->list == NULL) {
+				// default case
+				default_stmts  = cc->stmts;
+				default_fall  = fall;
+				default_block = body;
+				continue;
+			}
+
+			ssaBlock *next_cond = NULL;
+			Token eq = {Token_CmpEq};
+			for (AstNode *expr = cc->list; expr != NULL; expr = expr->next) {
+				next_cond = ssa__make_block(proc, clause, make_string("match.case.next"));
+
+				ssaValue *cond = ssa_emit_comp(proc, eq, tag, ssa_build_expr(proc, expr));
+				ssa_emit_if(proc, cond, body, next_cond);
+				gb_array_append(proc->blocks, next_cond);
+				proc->curr_block = next_cond;
+			}
+			if (append_body) {
+				gb_array_append(proc->blocks, body);
+			}
+			proc->curr_block = body;
+			ssa_push_target_list(proc, done, NULL, fall);
+			ssa_build_stmt_list(proc, cc->stmts);
+			ssa_pop_target_list(proc);
+			ssa_emit_jump(proc, done);
+			proc->curr_block = next_cond;
+		}
+
+		if (default_block != NULL) {
+			ssa_emit_jump(proc, default_block);
+			gb_array_append(proc->blocks, default_block);
+			proc->curr_block = default_block;
+			ssa_push_target_list(proc, done, NULL, default_fall);
+			ssa_build_stmt_list(proc, default_stmts);
+			ssa_pop_target_list(proc);
+		}
+
+		ssa_emit_jump(proc, done);
+		gb_array_append(proc->blocks, done);
+		proc->curr_block = done;
+	case_end;
+
 	case_ast_node(bs, BranchStmt, node);
 		ssaBlock *block = NULL;
 		switch (bs->token.kind) {

+ 129 - 16
src/common.cpp

@@ -110,6 +110,15 @@ template <typename T> void map_clear  (Map<T> *h);
 template <typename T> void map_grow   (Map<T> *h);
 template <typename T> void map_rehash (Map<T> *h, isize new_count);
 
+template <typename T> typename MapEntry<T> *multi_map_find_first(Map<T> *h, HashKey key);
+template <typename T> typename MapEntry<T> *multi_map_find_next (Map<T> *h, typename MapEntry<T> *e);
+
+template <typename T> isize multi_map_count     (Map<T> *h, HashKey key);
+template <typename T> void  multi_map_get_all   (Map<T> *h, HashKey key, T *items);
+template <typename T> void  multi_map_insert    (Map<T> *h, HashKey key, T value);
+template <typename T> void  multi_map_remove    (Map<T> *h, HashKey key, typename MapEntry<T> *e);
+template <typename T> void  multi_map_remove_all(Map<T> *h, HashKey key);
+
 
 
 template <typename T>
@@ -150,6 +159,24 @@ gb_internal MapFindResult map__find(Map<T> *h, HashKey key) {
 	return fr;
 }
 
+template <typename T>
+gb_internal MapFindResult map__find(Map<T> *h, typename MapEntry<T> *e) {
+	MapFindResult fr = {-1, -1, -1};
+	if (gb_array_count(h->hashes) > 0) {
+		fr.hash_index  = e->key.key % gb_array_count(h->hashes);
+		fr.entry_index = h->hashes[fr.hash_index];
+		while (fr.entry_index >= 0) {
+			if (&h->entries[fr.entry_index] == e) {
+				return fr;
+			}
+			fr.entry_prev = fr.entry_index;
+			fr.entry_index = h->entries[fr.entry_index].next;
+		}
+	}
+	return fr;
+}
+
+
 template <typename T>
 gb_internal b32 map__full(Map<T> *h) {
 	return 0.75f * gb_array_count(h->hashes) <= gb_array_count(h->entries);
@@ -221,26 +248,33 @@ void map_set(Map<T> *h, HashKey key, T value) {
 		map_grow(h);
 }
 
+
+
+template <typename T>
+void map__erase(Map<T> *h, MapFindResult fr) {
+	if (fr.entry_prev < 0) {
+		h->hashes[fr.hash_index] = h->entries[fr.entry_index].next;
+	} else {
+		h->entries[fr.entry_prev].next = h->entries[fr.entry_index].next;
+	}
+	if (fr.entry_index == gb_array_count(h->entries)-1) {
+		gb_array_pop(h->entries);
+		return;
+	}
+	h->entries[fr.entry_index] = h->entries[gb_array_count(h->entries)-1];
+	MapFindResult last = map__find(h, h->entries[fr.entry_index].key);
+	if (last.entry_prev >= 0) {
+		h->entries[last.entry_prev].next = fr.entry_index;
+	} else {
+		h->hashes[last.hash_index] = fr.entry_index;
+	}
+}
+
 template <typename T>
 void map_remove(Map<T> *h, HashKey key) {
 	MapFindResult fr = map__find(h, key);
 	if (fr.entry_index >= 0) {
-		if (fr.entry_prev < 0) {
-			h->hashes[fr.hash_index] = h->entries[fr.entry_index].next;
-		} else {
-			h->entries[fr.entry_prev].next = h->entries[fr.entry_index].next;
-		}
-		if (fr.entry_index == gb_array_count(h->entries)-1) {
-			gb_array_pop(h->entries);
-			return;
-		}
-		h->entries[fr.entry_index] = h->entries[gb_array_count(h->entries)-1];
-		MapFindResult last = map__find(h, h->entries[fr.entry_index].key);
-		if (last.entry_prev >= 0) {
-			h->entries[last.entry_prev].next = fr.entry_index;
-		} else {
-			h->hashes[last.hash_index] = fr.entry_index;
-		}
+		map__erase(h, fr);
 	}
 }
 
@@ -249,3 +283,82 @@ gb_inline void map_clear(Map<T> *h) {
 	gb_array_clear(h->hashes);
 	gb_array_clear(h->entries);
 }
+
+
+
+template <typename T>
+typename MapEntry<T> *multi_map_find_first(Map<T> *h, HashKey key) {
+	isize i = map__find(h, key).entry_index;
+	if (i < 0) {
+		return NULL;
+	}
+	return &h->entries[i];
+}
+
+template <typename T>
+typename MapEntry<T> *multi_map_find_next(Map<T> *h, typename MapEntry<T> *e) {
+	isize i = e->next;
+	while (i >= 0) {
+		if (hash_key_equal(h->entries[i].key, e->key)) {
+			return &h->entries[i];
+		}
+		i = h->entries[i].next;
+	}
+	return NULL;
+}
+
+template <typename T>
+isize multi_map_count(Map<T> *h, HashKey key) {
+	isize count = 0;
+	auto *e = multi_map_find_first(h, key);
+	while (e != NULL) {
+		count++;
+		e = multi_map_find_next(h, e);
+	}
+	return count;
+}
+
+template <typename T>
+void multi_map_get_all(Map<T> *h, HashKey key, T *items) {
+	isize i = 0;
+	auto *e = multi_map_find_first(h, key);
+	while (e != NULL) {
+		items[i++] = e->value;
+		e = multi_map_find_next(h, e);
+	}
+}
+
+template <typename T>
+void multi_map_insert(Map<T> *h, HashKey key, T value) {
+	if (gb_array_count(h->hashes) == 0) {
+		map_grow(h);
+	}
+	MapFindResult fr = map__find(h, key);
+	isize i = map__add_entry(h, key);
+	if (fr.entry_prev < 0) {
+		h->hashes[fr.hash_index] = i;
+	} else {
+		h->entries[fr.entry_prev].next = i;
+	}
+	h->entries[i].next = fr.entry_index;
+	h->entries[i].value = value;
+	if (map__full(h)) {
+		map_grow(h);
+	}
+}
+
+template <typename T>
+void multi_map_remove(Map<T> *h, HashKey key, typename MapEntry<T> *e) {
+	MapFindResult fr = map__find(h, e);
+	if (fr.entry_index >= 0) {
+		map__erase(h, fr);
+	}
+}
+
+template <typename T>
+void multi_map_remove_all(Map<T> *h, HashKey key) {
+	while (map_get(h, key) != NULL) {
+		map_remove(h, key);
+	}
+}
+

+ 4 - 0
src/exact_value.cpp

@@ -26,6 +26,10 @@ struct ExactValue {
 	};
 };
 
+HashKey hash_exact_value(ExactValue v) {
+	return hashing_proc(&v, gb_size_of(ExactValue));
+}
+
 ExactValue make_exact_value_bool(b32 b) {
 	ExactValue result = {ExactValue_Bool};
 	result.value_bool = (b != 0);

+ 4 - 2
src/main.cpp

@@ -72,6 +72,8 @@ int main(int argc, char **argv) {
 	defer (destroy_checker(&checker));
 
 	check_parsed_files(&checker);
+
+#if 1
 	ssaGen ssa = {};
 	if (!ssa_gen_init(&ssa, &checker))
 		return 1;
@@ -90,8 +92,6 @@ int main(int argc, char **argv) {
 		output_name, cast(int)base_name_len, output_name);
 	if (exit_code != 0)
 		return exit_code;
-#if 1
-#endif
 
 	gbString lib_str = gb_string_make(gb_heap_allocator(), "-lKernel32.lib");
 	char lib_str_buf[1024] = {};
@@ -119,5 +119,7 @@ int main(int argc, char **argv) {
 	if (run_output) {
 		win32_exec_command_line_app("%.*s.exe", cast(int)base_name_len, output_name);
 	}
+#endif
+
 	return 0;
 }

+ 176 - 198
src/parser.cpp

@@ -1,6 +1,4 @@
 struct AstNode;
-struct Type;
-struct AstScope;
 
 enum ParseFileError {
 	ParseFile_None,
@@ -30,8 +28,6 @@ struct AstFile {
 	AstNode *decls;
 	isize decl_count;
 
-	AstScope *file_scope;
-	AstScope *curr_scope;
 	AstNode *curr_proc;
 	isize scope_level;
 
@@ -43,19 +39,6 @@ struct AstFile {
 	TokenPos fix_prev_pos;
 };
 
-// NOTE(bill): Just used to quickly check if there is double declaration in the same scope
-// No type checking actually happens
-// TODO(bill): Should this be completely handled in the semantic checker or is it better here?
-struct AstEntity {
-	Token     token;
-	AstScope *parent;
-	AstNode * decl;
-};
-
-struct AstScope {
-	AstScope *parent;
-	Map<AstEntity> entities; // Key: Token.string
-};
 
 struct Parser {
 	String init_fullpath;
@@ -91,12 +74,9 @@ enum CallExprKind {
 };
 
 #define AST_NODE_KINDS \
-	AST_NODE_KIND(Invalid,  "invalid node", struct{}) \
+	AST_NODE_KIND(Invalid,  "invalid node",  struct{}) \
 	AST_NODE_KIND(BasicLit, "basic literal", Token) \
-	AST_NODE_KIND(Ident,    "identifier", struct { \
-		Token token; \
-		AstEntity *entity; \
-	}) \
+	AST_NODE_KIND(Ident,    "identifier",    Token) \
 	AST_NODE_KIND(ProcLit, "procedure literal", struct { \
 		AstNode *type; \
 		AstNode *body; \
@@ -170,6 +150,17 @@ AST_NODE_KIND(_ComplexStmtBegin, "", struct{}) \
 		AstNode *init, *cond, *post; \
 		AstNode *body; \
 	}) \
+	AST_NODE_KIND(CaseClause, "case clause", struct { \
+		Token token; \
+		AstNode *list; \
+		AstNode *stmts; \
+		isize list_count, stmt_count; \
+	}) \
+	AST_NODE_KIND(MatchStmt, "match statement", struct { \
+		Token token; \
+		AstNode *init, *tag; \
+		AstNode *body; \
+	}) \
 	AST_NODE_KIND(DeferStmt,  "defer statement",  struct { Token token; AstNode *stmt; }) \
 	AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; }) \
 	AST_NODE_KIND(UsingStmt,  "using statement",  struct { Token token; AstNode *node; }) \
@@ -232,7 +223,7 @@ AST_NODE_KIND(_TypeBegin, "", struct{}) \
 		isize decl_count; \
 		b32 is_packed; \
 	}) \
-	AST_NODE_KIND(UnionType, "union type", struct { \
+	AST_NODE_KIND(RawUnionType, "raw union type", struct { \
 		Token token; \
 		AstNode *decl_list; \
 		isize decl_count; \
@@ -276,15 +267,6 @@ struct AstNode {
 
 
 
-
-gb_inline AstScope *make_ast_scope(AstFile *f, AstScope *parent) {
-	AstScope *scope = gb_alloc_item(gb_arena_allocator(&f->arena), AstScope);
-	map_init(&scope->entities, gb_heap_allocator());
-	scope->parent = parent;
-	return scope;
-}
-
-
 gb_inline b32 is_ast_node_expr(AstNode *node) {
 	return gb_is_between(node->kind, AstNode__ExprBegin+1, AstNode__ExprEnd-1);
 }
@@ -307,7 +289,7 @@ Token ast_node_token(AstNode *node) {
 	case AstNode_BasicLit:
 		return node->BasicLit;
 	case AstNode_Ident:
-		return node->Ident.token;
+		return node->Ident;
 	case AstNode_ProcLit:
 		return ast_node_token(node->ProcLit.type);
 	case AstNode_CompoundLit:
@@ -356,6 +338,10 @@ Token ast_node_token(AstNode *node) {
 		return node->ReturnStmt.token;
 	case AstNode_ForStmt:
 		return node->ForStmt.token;
+	case AstNode_MatchStmt:
+		return node->MatchStmt.token;
+	case AstNode_CaseClause:
+		return node->CaseClause.token;
 	case AstNode_DeferStmt:
 		return node->DeferStmt.token;
 	case AstNode_BranchStmt:
@@ -367,7 +353,7 @@ Token ast_node_token(AstNode *node) {
 	case AstNode_VarDecl:
 		return ast_node_token(node->VarDecl.name_list);
 	case AstNode_ProcDecl:
-		return node->ProcDecl.name->Ident.token;
+		return node->ProcDecl.name->Ident;
 	case AstNode_TypeDecl:
 		return node->TypeDecl.token;
 	case AstNode_LoadDecl:
@@ -390,66 +376,19 @@ 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:
 		return node->EnumType.token;
 	}
 
 	return empty_token;
-;}
-
-gb_inline void destroy_ast_scope(AstScope *scope) {
-	// NOTE(bill): No need to free the actual pointer to the AstScope
-	// as there should be enough room in the arena
-	map_destroy(&scope->entities);
-}
-
-gb_inline AstScope *open_ast_scope(AstFile *f) {
-	AstScope *scope = make_ast_scope(f, f->curr_scope);
-	f->curr_scope = scope;
-	f->scope_level++;
-	return f->curr_scope;
-}
-
-gb_inline void close_ast_scope(AstFile *f) {
-	GB_ASSERT_NOT_NULL(f->curr_scope);
-	GB_ASSERT(f->scope_level > 0);
-	{
-		AstScope *parent = f->curr_scope->parent;
-		if (f->curr_scope) {
-			destroy_ast_scope(f->curr_scope);
-		}
-		f->curr_scope = parent;
-		f->scope_level--;
-	}
-}
-
-AstEntity *make_ast_entity(AstFile *f, Token token, AstNode *decl, AstScope *parent) {
-	AstEntity *entity = gb_alloc_item(gb_arena_allocator(&f->arena), AstEntity);
-	entity->token = token;
-	entity->decl = decl;
-	entity->parent = parent;
-	return entity;
 }
 
 HashKey hash_token(Token t) {
 	return hash_string(t.string);
 }
 
-AstEntity *ast_scope_lookup(AstScope *scope, Token token) {
-	return map_get(&scope->entities, hash_token(token));
-}
-
-AstEntity *ast_scope_insert(AstScope *scope, AstEntity entity) {
-	AstEntity *prev = ast_scope_lookup(scope, entity.token);
-	if (prev == NULL) {
-		map_set(&scope->entities, hash_token(entity.token), entity);
-	}
-	return prev;
-}
-
-
 #define ast_file_err(f, token, fmt, ...) ast_file_err_(f, __FUNCTION__, token, fmt, ##__VA_ARGS__)
 void ast_file_err_(AstFile *file, char *function, Token token, char *fmt, ...) {
 	// NOTE(bill): Duplicate error, skip it
@@ -598,10 +537,9 @@ gb_inline AstNode *make_basic_lit(AstFile *f, Token basic_lit) {
 	return result;
 }
 
-gb_inline AstNode *make_ident(AstFile *f, Token token, AstEntity *entity = NULL) {
+gb_inline AstNode *make_ident(AstFile *f, Token token) {
 	AstNode *result = make_node(f, AstNode_Ident);
-	result->Ident.token = token;
-	result->Ident.entity   = entity;
+	result->Ident = token;
 	return result;
 }
 
@@ -698,12 +636,34 @@ gb_inline AstNode *make_return_stmt(AstFile *f, Token token, AstNode *result_lis
 gb_inline AstNode *make_for_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, AstNode *post, AstNode *body) {
 	AstNode *result = make_node(f, AstNode_ForStmt);
 	result->ForStmt.token = token;
-	result->ForStmt.init = init;
-	result->ForStmt.cond = cond;
-	result->ForStmt.post = post;
-	result->ForStmt.body = body;
+	result->ForStmt.init  = init;
+	result->ForStmt.cond  = cond;
+	result->ForStmt.post  = post;
+	result->ForStmt.body  = body;
 	return result;
 }
+
+
+gb_inline AstNode *make_match_stmt(AstFile *f, Token token, AstNode *init, AstNode *tag, AstNode *body) {
+	AstNode *result = make_node(f, AstNode_MatchStmt);
+	result->MatchStmt.token = token;
+	result->MatchStmt.init  = init;
+	result->MatchStmt.tag   = tag;
+	result->MatchStmt.body  = body;
+	return result;
+}
+
+gb_inline AstNode *make_case_clause(AstFile *f, Token token, AstNode *list, isize list_count, AstNode *stmts, isize stmt_count) {
+	AstNode *result = make_node(f, AstNode_CaseClause);
+	result->CaseClause.token = token;
+	result->CaseClause.list  = list;
+	result->CaseClause.list_count = list_count;
+	result->CaseClause.stmts = stmts;
+	result->CaseClause.stmt_count = stmt_count;
+	return result;
+}
+
+
 gb_inline AstNode *make_defer_stmt(AstFile *f, Token token, AstNode *stmt) {
 	AstNode *result = make_node(f, AstNode_DeferStmt);
 	result->DeferStmt.token = token;
@@ -804,11 +764,11 @@ 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;
+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;
+	result->RawUnionType.decl_list = decl_list;
+	result->RawUnionType.decl_count = decl_count;
 	return result;
 }
 
@@ -902,30 +862,6 @@ b32 is_blank_ident(String str) {
 	return false;
 }
 
-gb_internal void add_ast_entity(AstFile *f, AstScope *scope, AstNode *declaration, AstNode *name_list) {
-	for (AstNode *n = name_list; n != NULL; n = n->next) {
-		if (n->kind != AstNode_Ident) {
-			ast_file_err(f, ast_node_token(declaration), "Identifier is already declared or resolved");
-			continue;
-		}
-
-		AstEntity *entity = make_ast_entity(f, n->Ident.token, declaration, scope);
-		n->Ident.entity = entity;
-
-		AstEntity *insert_entity = ast_scope_insert(scope, *entity);
-		if (insert_entity != NULL && !is_blank_ident(insert_entity->token.string)) {
-			ast_file_err(f, entity->token,
-			             "There is already a previous declaration of `%.*s` in the current scope\n"
-			             "at \t%.*s(%td:%td)",
-			             LIT(insert_entity->token.string),
-			             LIT(insert_entity->token.pos.file),
-			             insert_entity->token.pos.line,
-			             insert_entity->token.pos.column);
-		}
-	}
-}
-
-
 
 void fix_advance_to_next_stmt(AstFile *f) {
 	// TODO(bill): fix_advance_to_next_stmt
@@ -994,10 +930,10 @@ b32 expect_semicolon_after_stmt(AstFile *f, AstNode *s) {
 
 
 AstNode *parse_expr(AstFile *f, b32 lhs);
-AstNode *parse_proc_type(AstFile *f, AstScope **scope_);
+AstNode *parse_proc_type(AstFile *f);
 AstNode *parse_stmt_list(AstFile *f, isize *list_count_);
 AstNode *parse_stmt(AstFile *f);
-AstNode *parse_body(AstFile *f, AstScope *scope);
+AstNode *parse_body(AstFile *f);
 
 AstNode *parse_identifier(AstFile *f) {
 	Token token = f->cursor[0];
@@ -1218,9 +1154,8 @@ AstNode *parse_operand(AstFile *f, b32 lhs) {
 
 	// Parse Procedure Type or Literal
 	case Token_proc: {
-		AstScope *scope = NULL;
 		AstNode *curr_proc = f->curr_proc;
-		AstNode *type = parse_proc_type(f, &scope);
+		AstNode *type = parse_proc_type(f);
 		f->curr_proc = type;
 		defer (f->curr_proc = curr_proc);
 
@@ -1235,13 +1170,10 @@ AstNode *parse_operand(AstFile *f, b32 lhs) {
 			return type;
 		} else {
 			AstNode *body;
-			AstScope *curr_scope = f->curr_scope;
 
-			f->curr_scope = scope;
 			f->expr_level++;
-			body = parse_body(f, scope);
+			body = parse_body(f);
 			f->expr_level--;
-			f->curr_scope = curr_scope;
 
 			return make_proc_lit(f, type, body, tags);
 		}
@@ -1538,7 +1470,7 @@ AstNode *parse_simple_stmt(AstFile *f) {
 	case Token_CmpAndEq:
 	case Token_CmpOrEq:
 	{
-		if (f->curr_scope == f->file_scope) {
+		if (f->curr_proc == NULL) {
 			ast_file_err(f, f->cursor[0], "You cannot use a simple statement in the file scope");
 			return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 		}
@@ -1566,7 +1498,7 @@ AstNode *parse_simple_stmt(AstFile *f) {
 	switch (token.kind) {
 	case Token_Increment:
 	case Token_Decrement:
-		if (f->curr_scope == f->file_scope) {
+		if (f->curr_proc == NULL) {
 			ast_file_err(f, f->cursor[0], "You cannot use a simple statement in the file scope");
 			return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 		}
@@ -1581,15 +1513,11 @@ AstNode *parse_simple_stmt(AstFile *f) {
 
 
 AstNode *parse_block_stmt(AstFile *f) {
-	if (f->curr_scope == f->file_scope) {
+	if (f->curr_proc == NULL) {
 		ast_file_err(f, f->cursor[0], "You cannot use a block statement in the file scope");
 		return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 	}
-	AstNode *block_stmt;
-
-	open_ast_scope(f);
-	block_stmt = parse_body(f, f->curr_scope);
-	close_ast_scope(f);
+	AstNode *block_stmt = parse_body(f);
 	return block_stmt;
 }
 
@@ -1644,7 +1572,7 @@ AstNode *parse_type(AstFile *f) {
 	return type;
 }
 
-AstNode *parse_field_decl(AstFile *f, AstScope *scope, b32 allow_using) {
+AstNode *parse_field_decl(AstFile *f, b32 allow_using) {
 	b32 is_using = false;
 	AstNode *name_list = NULL;
 	isize name_count = 0;
@@ -1670,29 +1598,26 @@ AstNode *parse_field_decl(AstFile *f, AstScope *scope, b32 allow_using) {
 		ast_file_err(f, f->cursor[0], "Expected a type for this field declaration");
 
 	AstNode *field = make_field(f, name_list, name_count, type, is_using);
-	add_ast_entity(f, scope, field, name_list);
 	return field;
 }
 
-Token parse_procedure_signature(AstFile *f, AstScope *scope,
+Token parse_procedure_signature(AstFile *f,
                                 AstNode **param_list, isize *param_count,
                                 AstNode **result_list, isize *result_count);
 
-AstNode *parse_proc_type(AstFile *f, AstScope **scope_) {
-	AstScope *scope = make_ast_scope(f, f->file_scope); // Procedure's scope
+AstNode *parse_proc_type(AstFile *f) {
 	AstNode *params = NULL;
 	AstNode *results = NULL;
 	isize param_count = 0;
 	isize result_count = 0;
 
-	Token proc_token = parse_procedure_signature(f, scope, &params, &param_count, &results, &result_count);
+	Token proc_token = parse_procedure_signature(f, &params, &param_count, &results, &result_count);
 
-	if (scope_) *scope_ = scope;
 	return make_proc_type(f, proc_token, params, param_count, results, result_count);
 }
 
 
-AstNode *parse_parameter_list(AstFile *f, AstScope *scope, isize *param_count_, TokenKind separator, b32 allow_using) {
+AstNode *parse_parameter_list(AstFile *f, isize *param_count_, TokenKind separator, b32 allow_using) {
 	AstNode *param_list = NULL;
 	AstNode *param_list_curr = NULL;
 	isize param_count = 0;
@@ -1701,7 +1626,7 @@ AstNode *parse_parameter_list(AstFile *f, AstScope *scope, isize *param_count_,
 		if (!allow_using && allow_token(f, Token_using)) {
 			ast_file_err(f, f->cursor[-1], "`using` is only allowed within structures (at the moment)");
 		}
-		AstNode *field = parse_field_decl(f, scope, allow_using);
+		AstNode *field = parse_field_decl(f, allow_using);
 		DLIST_APPEND(param_list, param_list_curr, field);
 		param_count += field->Field.name_count;
 		if (f->cursor[0].kind != separator)
@@ -1826,12 +1751,6 @@ AstNode *parse_identifier_or_type(AstFile *f) {
 			}
 		}
 
-		AstScope *scope = make_ast_scope(f, NULL); // NOTE(bill): The struct needs its own scope with NO parent
-		AstScope *curr_scope = f->curr_scope;
-		f->curr_scope = scope;
-		defer (f->curr_scope = curr_scope);
-
-
 		Token open = expect_token(f, Token_OpenBrace);
 		isize decl_count = 0;
 		AstNode *decls = parse_struct_params(f, &decl_count);
@@ -1840,20 +1759,14 @@ AstNode *parse_identifier_or_type(AstFile *f) {
 		return make_struct_type(f, token, decls, decl_count, is_packed);
 	} break;
 
-	case Token_union: {
-		Token token = expect_token(f, Token_union);
-		AstScope *scope = make_ast_scope(f, NULL); // NOTE(bill): The struct needs its own scope with NO parent
-		AstScope *curr_scope = f->curr_scope;
-		f->curr_scope = scope;
-		defer (f->curr_scope = curr_scope);
-
-
+	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);
 		Token close = expect_token(f, Token_CloseBrace);
 
-		return make_union_type(f, token, decls, decl_count);
+		return make_raw_union_type(f, token, decls, decl_count);
 	}
 
 	case Token_enum: {
@@ -1895,7 +1808,7 @@ AstNode *parse_identifier_or_type(AstFile *f) {
 
 	case Token_proc: {
 		AstNode *curr_proc = f->curr_proc;
-		AstNode *type = parse_proc_type(f, NULL);
+		AstNode *type = parse_proc_type(f);
 		f->curr_proc = type;
 		f->curr_proc = curr_proc;
 		return type;
@@ -1930,7 +1843,7 @@ AstNode *parse_identifier_or_type(AstFile *f) {
 }
 
 
-AstNode *parse_results(AstFile *f, AstScope *scope, isize *result_count) {
+AstNode *parse_results(AstFile *f, isize *result_count) {
 	if (allow_token(f, Token_ArrowRight)) {
 		if (f->cursor[0].kind == Token_OpenParen) {
 			expect_token(f, Token_OpenParen);
@@ -1959,18 +1872,18 @@ AstNode *parse_results(AstFile *f, AstScope *scope, isize *result_count) {
 	return NULL;
 }
 
-Token parse_procedure_signature(AstFile *f, AstScope *scope,
+Token parse_procedure_signature(AstFile *f,
                                AstNode **param_list, isize *param_count,
                                AstNode **result_list, isize *result_count) {
 	Token proc_token = expect_token(f, Token_proc);
 	expect_token(f, Token_OpenParen);
-	*param_list = parse_parameter_list(f, scope, param_count, Token_Comma, true);
+	*param_list = parse_parameter_list(f, param_count, Token_Comma, true);
 	expect_token(f, Token_CloseParen);
-	*result_list = parse_results(f, scope, result_count);
+	*result_list = parse_results(f, result_count);
 	return proc_token;
 }
 
-AstNode *parse_body(AstFile *f, AstScope *scope) {
+AstNode *parse_body(AstFile *f) {
 	AstNode *statement_list = NULL;
 	isize statement_list_count = 0;
 	Token open, close;
@@ -1989,9 +1902,8 @@ AstNode *parse_proc_decl(AstFile *f, Token proc_token, AstNode *name) {
 	isize param_count = 0;
 	isize result_count = 0;
 
-	AstScope *scope = open_ast_scope(f);
-
-	parse_procedure_signature(f, scope, &param_list, &param_count, &result_list, &result_count);
+	parse_procedure_signature(f, &param_list, &param_count, &result_list, &result_count);
+	AstNode *proc_type = make_proc_type(f, proc_token, param_list, param_count, result_list, result_count);
 
 	AstNode *body = NULL;
 	u64 tags = 0;
@@ -1999,16 +1911,17 @@ AstNode *parse_proc_decl(AstFile *f, Token proc_token, AstNode *name) {
 
 	parse_proc_tags(f, &tags, &foreign_name);
 
+	AstNode *curr_proc = f->curr_proc;
+	f->curr_proc = proc_type;
+	defer (f->curr_proc = curr_proc);
+
 	if (f->cursor[0].kind == Token_OpenBrace) {
 		if ((tags & ProcTag_foreign) != 0) {
 			ast_file_err(f, f->cursor[0], "A procedure tagged as `#foreign` cannot have a body");
 		}
-		body = parse_body(f, scope);
+		body = parse_body(f);
 	}
 
-	close_ast_scope(f);
-
-	AstNode *proc_type = make_proc_type(f, proc_token, param_list, param_count, result_list, result_count);
 	return make_proc_decl(f, name, proc_type, body, tags, foreign_name);
 }
 
@@ -2036,7 +1949,7 @@ AstNode *parse_decl(AstFile *f, AstNode *name_list, isize name_count) {
 			Token token = expect_token(f, Token_type);
 			if (name_count != 1) {
 				ast_file_err(f, ast_node_token(name_list), "You can only declare one type at a time");
-				return make_bad_decl(f, name_list->Ident.token, token);
+				return make_bad_decl(f, name_list->Ident, token);
 			}
 
 			if (type != NULL) {
@@ -2053,11 +1966,10 @@ AstNode *parse_decl(AstFile *f, AstNode *name_list, isize name_count) {
 			AstNode *name = name_list;
 			if (name_count != 1) {
 				ast_file_err(f, proc_token, "You can only declare one procedure at a time");
-				return make_bad_decl(f, name->Ident.token, proc_token);
+				return make_bad_decl(f, name->Ident, proc_token);
 			}
 
 			AstNode *proc_decl = parse_proc_decl(f, proc_token, name);
-			add_ast_entity(f, f->curr_scope, proc_decl, name_list);
 			return proc_decl;
 
 		} else {
@@ -2091,13 +2003,12 @@ AstNode *parse_decl(AstFile *f, AstNode *name_list, isize name_count) {
 	}
 
 	AstNode *var_decl = make_var_decl(f, declaration_kind, name_list, name_count, type, value_list, value_count);
-	add_ast_entity(f, f->curr_scope, var_decl, name_list);
 	return var_decl;
 }
 
 
 AstNode *parse_if_stmt(AstFile *f) {
-	if (f->curr_scope == f->file_scope) {
+	if (f->curr_proc == NULL) {
 		ast_file_err(f, f->cursor[0], "You cannot use an if statement in the file scope");
 		return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 	}
@@ -2108,9 +2019,6 @@ AstNode *parse_if_stmt(AstFile *f) {
 	AstNode *body = NULL;
 	AstNode *else_stmt = NULL;
 
-	open_ast_scope(f);
-	defer (close_ast_scope(f));
-
 	isize prev_level = f->expr_level;
 	f->expr_level = -1;
 
@@ -2154,7 +2062,7 @@ AstNode *parse_if_stmt(AstFile *f) {
 }
 
 AstNode *parse_return_stmt(AstFile *f) {
-	if (f->curr_scope == f->file_scope) {
+	if (f->curr_proc == NULL) {
 		ast_file_err(f, f->cursor[0], "You cannot use a return statement in the file scope");
 		return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 	}
@@ -2175,14 +2083,12 @@ AstNode *parse_return_stmt(AstFile *f) {
 }
 
 AstNode *parse_for_stmt(AstFile *f) {
-	if (f->curr_scope == f->file_scope) {
+	if (f->curr_proc == NULL) {
 		ast_file_err(f, f->cursor[0], "You cannot use a for statement in the file scope");
 		return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 	}
 
 	Token token = expect_token(f, Token_for);
-	open_ast_scope(f);
-	defer (close_ast_scope(f));
 
 	AstNode *init = NULL;
 	AstNode *cond = NULL;
@@ -2220,8 +2126,78 @@ AstNode *parse_for_stmt(AstFile *f) {
 	return make_for_stmt(f, token, init, cond, end, body);
 }
 
+AstNode *parse_case_clause(AstFile *f) {
+	Token token = f->cursor[0];
+	AstNode *list = NULL;
+	isize list_count = 0;
+	if (allow_token(f, Token_case)) {
+		list = parse_rhs_expr_list(f, &list_count);
+	} else {
+		expect_token(f, Token_default);
+	}
+	expect_token(f, Token_Colon); // TODO(bill): Is this the best syntax?
+	isize stmt_count = 0;
+	AstNode *stmts = parse_stmt_list(f, &stmt_count);
+
+	return make_case_clause(f, token, list, list_count, stmts, stmt_count);
+}
+
+AstNode *parse_match_stmt(AstFile *f) {
+	if (f->curr_proc == NULL) {
+		ast_file_err(f, f->cursor[0], "You cannot use a match statement in the file scope");
+		return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
+	}
+
+	Token token = expect_token(f, Token_match);
+
+	AstNode *init = NULL;
+	AstNode *tag = NULL;
+	if (f->cursor[0].kind != Token_OpenBrace) {
+		isize prev_level = f->expr_level;
+		f->expr_level = -1;
+		if (f->cursor[0].kind != Token_Semicolon) {
+			tag = parse_simple_stmt(f);
+		}
+		if (allow_token(f, Token_Semicolon)) {
+			init = tag;
+			tag = NULL;
+			if (f->cursor[0].kind != Token_OpenBrace) {
+				tag = parse_simple_stmt(f);
+			}
+		}
+
+		f->expr_level = prev_level;
+	}
+
+	Token open = expect_token(f, Token_OpenBrace);
+	AstNode *list = NULL;
+	AstNode *list_curr = NULL;
+	isize list_count = 0;
+#if 1
+	while (f->cursor[0].kind == Token_case ||
+	       f->cursor[0].kind == Token_default) {
+		DLIST_APPEND(list, list_curr, parse_case_clause(f));
+		list_count++;
+	}
+#else
+
+	while (f->cursor[0].kind == Token_ArrowRight) {
+		DLIST_APPEND(list, list_curr, parse_case_clause(f));
+		list_count++;
+	}
+
+#endif
+	Token close = expect_token(f, Token_CloseBrace);
+
+	AstNode *body = make_block_stmt(f, list, list_count, open, close);
+
+	tag = convert_stmt_to_expr(f, tag, make_string("match expression"));
+	return make_match_stmt(f, token, init, tag, body);
+}
+
+
 AstNode *parse_defer_stmt(AstFile *f) {
-	if (f->curr_scope == f->file_scope) {
+	if (f->curr_proc == NULL) {
 		ast_file_err(f, f->cursor[0], "You cannot use a defer statement in the file scope");
 		return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 	}
@@ -2268,16 +2244,16 @@ AstNode *parse_stmt(AstFile *f) {
 	case Token_if:     return parse_if_stmt(f);
 	case Token_return: return parse_return_stmt(f);
 	case Token_for:    return parse_for_stmt(f);
+	case Token_match:  return parse_match_stmt(f);
 	case Token_defer:  return parse_defer_stmt(f);
-	// case Token_match: return NULL; // TODO(bill): Token_match
-	// case Token_case: return NULL; // TODO(bill): Token_case
 
 	case Token_break:
 	case Token_continue:
 	case Token_fallthrough:
 		next_token(f);
-		expect_token(f, Token_Semicolon);
-		return make_branch_stmt(f, token);
+		s = make_branch_stmt(f, token);
+		expect_semicolon_after_stmt(f, s);
+		return s;
 
 	case Token_using: {
 		AstNode *node = NULL;
@@ -2318,14 +2294,14 @@ AstNode *parse_stmt(AstFile *f) {
 
 		if (are_strings_equal(s->TagStmt.name.string, make_string("load"))) {
 			Token file_path = expect_token(f, Token_String);
-			if (f->curr_scope == f->file_scope) {
+			if (f->curr_proc == NULL) {
 				return make_load_decl(f, s->TagStmt.token, file_path);
 			}
 			ast_file_err(f, token, "You cannot `load` within a procedure. This must be done at the file scope.");
 			return make_bad_decl(f, token, file_path);
 		} else if (are_strings_equal(s->TagStmt.name.string, make_string("foreign_system_library"))) {
 			Token file_path = expect_token(f, Token_String);
-			if (f->curr_scope == f->file_scope) {
+			if (f->curr_proc == NULL) {
 				return make_foreign_system_library(f, s->TagStmt.token, file_path);
 			}
 			ast_file_err(f, token, "You cannot using `foreign_system_library` within a procedure. This must be done at the file scope.");
@@ -2337,7 +2313,7 @@ AstNode *parse_stmt(AstFile *f) {
 				ast_file_err(f, token, "#thread_local may only be applied to variable declarations");
 				return make_bad_decl(f, token, ast_node_token(var_decl));
 			}
-			if (f->curr_scope != f->file_scope) {
+			if (f->curr_proc != NULL) {
 				ast_file_err(f, token, "#thread_local is only allowed at the file scope.");
 				return make_bad_decl(f, token, ast_node_token(var_decl));
 			}
@@ -2371,10 +2347,14 @@ AstNode *parse_stmt_list(AstFile *f, isize *list_count_) {
 	isize list_count = 0;
 
 	while (f->cursor[0].kind != Token_case &&
+	       f->cursor[0].kind != Token_default &&
 	       f->cursor[0].kind != Token_CloseBrace &&
 	       f->cursor[0].kind != Token_EOF) {
-		DLIST_APPEND(list_root, list_curr, parse_stmt(f));
-		list_count++;
+		AstNode *stmt = parse_stmt(f);
+		if (stmt && stmt->kind != AstNode_EmptyStmt) {
+			DLIST_APPEND(list_root, list_curr, stmt);
+			list_count++;
+		}
 	}
 
 	if (list_count_) *list_count_ = list_count;
@@ -2405,12 +2385,11 @@ ParseFileError init_ast_file(AstFile *f, String fullpath) {
 		f->cursor = &f->tokens[0];
 
 		// NOTE(bill): Is this big enough or too small?
-		isize arena_size = gb_max(gb_size_of(AstNode), gb_size_of(AstScope));
+		isize arena_size = gb_size_of(AstNode);
 		arena_size *= 2*gb_array_count(f->tokens);
 		gb_arena_init_from_allocator(&f->arena, gb_heap_allocator(), arena_size);
 
-		open_ast_scope(f);
-		f->file_scope = f->curr_scope;
+		f->curr_proc = NULL;
 
 		return ParseFile_None;
 	}
@@ -2428,7 +2407,6 @@ ParseFileError init_ast_file(AstFile *f, String fullpath) {
 }
 
 void destroy_ast_file(AstFile *f) {
-	close_ast_scope(f);
 	gb_arena_free(&f->arena);
 	gb_array_free(f->tokens);
 	gb_free(gb_heap_allocator(), f->tokenizer.fullpath.text);

+ 1 - 1
src/printer.cpp

@@ -16,7 +16,7 @@ void print_ast(AstNode *node, isize indent) {
 		break;
 	case AstNode_Ident:
 		print_indent(indent);
-		print_token(node->Ident.token);
+		print_token(node->Ident);
 		break;
 	case AstNode_ProcLit:
 		print_indent(indent);

+ 2 - 0
src/tokenizer.cpp

@@ -88,6 +88,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
 	TOKEN_KIND(Token_continue,    "continue"), \
 	TOKEN_KIND(Token_fallthrough, "fallthrough"), \
 	TOKEN_KIND(Token_case,        "case"), \
+	TOKEN_KIND(Token_default,     "default"), \
 	TOKEN_KIND(Token_then,        "then"), \
 	TOKEN_KIND(Token_if,          "if"), \
 	TOKEN_KIND(Token_else,        "else"), \
@@ -96,6 +97,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
 	TOKEN_KIND(Token_return,      "return"), \
 	TOKEN_KIND(Token_struct,      "struct"), \
 	TOKEN_KIND(Token_union,       "union"), \
+	TOKEN_KIND(Token_raw_union,   "raw_union"), \
 	TOKEN_KIND(Token_enum,        "enum"), \
 	TOKEN_KIND(Token_using,       "using"), \
 TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \